Skip to content
Snippets Groups Projects
Unverified Commit 96eef0ae authored by Pavel Kirilin's avatar Pavel Kirilin :alien:
Browse files

Added new run mods.

Description:
- Added test mode to check processors on random image.
- Added completion mode to generate completion for your shell.
- Fixed scaling.

Signed-off-by: default avatarPavel Kirilin <win10@list.ru>
parent 6cc2a279
No related branches found
No related tags found
No related merge requests found
use std::io::Read;
use std::process::Command; use std::process::Command;
use dbus::blocking::Connection; use dbus::blocking::Connection;
...@@ -5,6 +6,7 @@ use dbus::Message; ...@@ -5,6 +6,7 @@ use dbus::Message;
use crate::img_processors::process_image; use crate::img_processors::process_image;
use crate::result::MBGResult; use crate::result::MBGResult;
use crate::TestingPic;
pub fn reset_background_handler(_: (), _: &Connection, message: &Message) -> bool { pub fn reset_background_handler(_: (), _: &Connection, message: &Message) -> bool {
let member_name = message.read1::<String>(); let member_name = message.read1::<String>();
...@@ -34,6 +36,18 @@ pub fn process_image_url(image_url: String) -> MBGResult<()> { ...@@ -34,6 +36,18 @@ pub fn process_image_url(image_url: String) -> MBGResult<()> {
Ok(()) 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) { pub fn set_wallpaper(path: &str) {
debug!("Setting background"); debug!("Setting background");
let out = Command::new("feh").arg("--bg-fill").arg(path).output(); let out = Command::new("feh").arg("--bg-fill").arg(path).output();
......
#[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
use std::cmp::min; use std::cmp::min;
use image::imageops::{overlay, FilterType};
use image::{DynamicImage, GenericImageView, Rgba, RgbaImage}; use image::{DynamicImage, GenericImageView, Rgba, RgbaImage};
use image::imageops::{FilterType, overlay}; use imageproc::drawing::{draw_filled_circle_mut, Blend, Canvas};
use imageproc::drawing::{Blend, Canvas, draw_filled_circle_mut};
use crate::config::image_processors::ProcessorParams; use crate::config::image_processors::ProcessorParams;
use crate::img_processors::ImageProcessor; use crate::img_processors::ImageProcessor;
...@@ -15,18 +15,22 @@ pub struct Border { ...@@ -15,18 +15,22 @@ pub struct Border {
pub width: u32, pub width: u32,
} }
impl Border { impl Border {
fn crop_circle(&self, img: &DynamicImage) -> MBGResult<DynamicImage> { fn crop_circle(&self, img: &DynamicImage) -> MBGResult<DynamicImage> {
let img_height = image::GenericImageView::height(img); let img_height = image::GenericImageView::height(img);
let img_width = image::GenericImageView::width(img); let img_width = image::GenericImageView::width(img);
let transparent_pixel = Rgba([0u8, 0u8, 0u8, 0u8]); 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 center = ((mask.width() / 2) as i32, (mask.height() / 2) as i32);
let radius = min(center.0, center.1); let radius = min(center.0, center.1);
let black_pixel = Rgba([0u8, 0u8, 0u8, 255u8]); let black_pixel = Rgba([0u8, 0u8, 0u8, 255u8]);
draw_filled_circle_mut(&mut mask, center, radius, black_pixel); draw_filled_circle_mut(&mut mask, center, radius, black_pixel);
let mut mask = DynamicImage::ImageRgba8(mask.0); let mut mask = DynamicImage::ImageRgba8(mask.0);
debug!("{}x{}", img_width, img_height);
mask = mask.resize(img_width, img_height, FilterType::Nearest); mask = mask.resize(img_width, img_height, FilterType::Nearest);
let mut res = mask.clone(); let mut res = mask.clone();
for (x, y, pix) in mask.pixels() { for (x, y, pix) in mask.pixels() {
...@@ -39,17 +43,20 @@ impl Border { ...@@ -39,17 +43,20 @@ impl Border {
fn draw_border(&self, img: &DynamicImage) -> MBGResult<DynamicImage> { fn draw_border(&self, img: &DynamicImage) -> MBGResult<DynamicImage> {
let img_width = image::GenericImageView::width(img); let img_width = image::GenericImageView::width(img);
let img_height = image::GenericImageView::height(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()); let target_color = read_color::rgb_maybe_a(&mut self.color.chars());
if target_color.is_none() { if target_color.is_none() {
return Err( return Err(MBGError::ConfigErr(String::from(
MBGError::ConfigErr(String::from("Can't parse border color. Please use hex value.")) "Can't parse border color. Please use hex value.",
); )));
} }
let target_color = target_color.unwrap(); let target_color = target_color.unwrap();
let target_color = match target_color { let target_color = match target_color {
([r, g, b, ], Some(a)) => Rgba([r, g, b, a]), ([r, g, b], Some(a)) => Rgba([r, g, b, a]),
([r, g, b, ], None) => Rgba([r, g, b, 255u8]), ([r, g, b], None) => Rgba([r, g, b, 255u8]),
}; };
if self.circle { if self.circle {
let mut mask = DynamicImage::ImageRgba8(RgbaImage::from_pixel( let mut mask = DynamicImage::ImageRgba8(RgbaImage::from_pixel(
...@@ -76,6 +83,7 @@ impl Border { ...@@ -76,6 +83,7 @@ impl Border {
impl ImageProcessor for Border { impl ImageProcessor for Border {
fn process(&self, img: &DynamicImage) -> MBGResult<DynamicImage> { fn process(&self, img: &DynamicImage) -> MBGResult<DynamicImage> {
debug!("Setting border");
let mut res = img.clone(); let mut res = img.clone();
if self.circle { if self.circle {
res = self.crop_circle(&res)?; res = self.crop_circle(&res)?;
......
...@@ -142,4 +142,4 @@ fn mapper( ...@@ -142,4 +142,4 @@ fn mapper(
width, width,
}), }),
} }
} }
\ No newline at end of file
use image::imageops::FilterType;
use image::{DynamicImage, GenericImageView}; use image::{DynamicImage, GenericImageView};
use image::imageops::FilterType;
use crate::config::image_processors::ProcessorParams; use crate::config::image_processors::ProcessorParams;
use crate::result::MBGResult; use crate::result::MBGResult;
...@@ -13,12 +13,11 @@ pub struct ScaleProcessor { ...@@ -13,12 +13,11 @@ pub struct ScaleProcessor {
impl super::ImageProcessor for ScaleProcessor { impl super::ImageProcessor for ScaleProcessor {
fn process(&self, img: &DynamicImage) -> MBGResult<DynamicImage> { fn process(&self, img: &DynamicImage) -> MBGResult<DynamicImage> {
let abs_width = self.screen_width.abs() as u32; let abs_width = self.screen_width.abs() as u32;
let scale_factor = ((abs_width / img.width()) as f32) * self.params.strength; let scale_factor = (abs_width as f32 / img.width() as f32) * self.params.strength;
let scale_factor = scale_factor.ceil() as u32;
debug!("Scaling image by {} times", scale_factor); debug!("Scaling image by {} times", scale_factor);
Ok(img.resize( Ok(img.resize(
img.width() * scale_factor, (img.width() as f32 * scale_factor) as u32,
img.height() * scale_factor, (img.height() as f32 * scale_factor) as u32,
FilterType::Nearest, FilterType::Nearest,
)) ))
} }
......
...@@ -9,10 +9,12 @@ extern crate serde_derive; ...@@ -9,10 +9,12 @@ extern crate serde_derive;
use structopt::StructOpt; use structopt::StructOpt;
use crate::background::test_processors;
use crate::config::Config; use crate::config::Config;
use crate::logging::setup_logger; use crate::logging::setup_logger;
use crate::player_dbus::start_listen; use crate::player_dbus::start_listen;
use crate::result::{MBGError, MBGResult}; use crate::result::{MBGError, MBGResult};
use structopt::clap::Shell;
pub mod background; pub mod background;
pub mod config; pub mod config;
...@@ -23,30 +25,10 @@ pub mod logging; ...@@ -23,30 +25,10 @@ pub mod logging;
pub mod player_dbus; pub mod player_dbus;
pub mod result; pub mod result;
#[derive(Debug, StructOpt)] include!("cli.rs");
#[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,
}
fn main() -> MBGResult<()> { fn main() -> MBGResult<()> {
let opt: Opt = Opt::from_args(); let opt: Opt = Opt::from_args();
let mode = opt.cmd.unwrap_or_else(|| RunMode::Run);
let logger = setup_logger(); let logger = setup_logger();
if let Err(error) = logger { if let Err(error) = logger {
error!("Failed to create pretty logger"); error!("Failed to create pretty logger");
...@@ -57,9 +39,5 @@ fn main() -> MBGResult<()> { ...@@ -57,9 +39,5 @@ fn main() -> MBGResult<()> {
if res.is_err() { if res.is_err() {
return Err(MBGError::X11Error(String::from("Could not connect to GTK"))); return Err(MBGError::X11Error(String::from("Could not connect to GTK")));
} }
run(opt)
match mode {
RunMode::Run => start_listen(),
RunMode::GenConf => Config::generate_config(),
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment