diff --git a/Cargo.lock b/Cargo.lock
index d58cf39880839b697745fdfd8ce2649c82521d5f..e8c82f96ca6abfd7089a3fc7671872bbff92daad 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1201,6 +1201,7 @@ dependencies = [
  "imageproc",
  "log",
  "mpris",
+ "read_color",
  "reqwest",
  "serde",
  "serde_derive",
@@ -1649,6 +1650,12 @@ dependencies = [
  "num_cpus",
 ]
 
+[[package]]
+name = "read_color"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f4c8858baa4ad3c8bcc156ae91a0ffe22b76a3975c40c49b4f04c15c6bce0da"
+
 [[package]]
 name = "redox_syscall"
 version = "0.1.56"
diff --git a/Cargo.toml b/Cargo.toml
index 521b176dad4504b891865915c672158fb9ded913..846f25c54608df1a36441a66a6e6d286eac29cc1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,6 +9,7 @@ version = "0.1.7"
 
 [dependencies.log]              # A lightweight logging facade for Rust.
  version = "0.4"
+
 [dependencies.mpris]            # Idiomatic MPRIS D-Bus interface library.
 version = "2.0.0-rc2"
 
@@ -35,6 +36,7 @@ version = "0.5"
 features = [
     "colored"
 ]
+
 [dependencies.dbus]             # Used to listen to D-bus daemon.
 version = "0.8.1"
 
@@ -44,12 +46,6 @@ features = [
     "blocking"
 ]
 
-#[dependencies.xcb]
-#version = "0.9.0"
-#features = [
-#    "randr"
-#]
-
 [dependencies.gtk]
 version = "0.8.1"
 
@@ -72,4 +68,7 @@ version = "0.3"
 version = "0.23.1"
 
 [dependencies.imageproc]
-version = "0.20.0"
\ No newline at end of file
+version = "0.20.0"
+
+[dependencies.read_color]
+version = "1.0.0"
\ No newline at end of file
diff --git a/PKGBUILD b/PKGBUILD
index 43c55549f83cf316107e11c07191610a7049d352..7e57c068746b97f17c021de757b30120037a5ec0 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -11,12 +11,13 @@ depends=(
   'rust'
   'systemd'
   'git'
+  'feh'
+  'gtk3'
 )
 makedepends=(
   'rust'
 )
-# source=("")
-# sha1sums=('')
+
 
 build() {
   cd "$startdir/"
diff --git a/src/background.rs b/src/background.rs
index bc0016f670fe7bb286db7fff02f75482d46083d1..a0bb337fc8c3a01e298ebac3a993fdf44f3b93de 100644
--- a/src/background.rs
+++ b/src/background.rs
@@ -40,4 +40,4 @@ pub fn set_wallpaper(path: &str) {
     if let Err(set_error) = out {
         error!("Can't set background: {}", set_error.to_string());
     }
-}
\ No newline at end of file
+}
diff --git a/src/config/image_processors.rs b/src/config/image_processors.rs
index 14f7bfc3d167bae7b5c32dd898b2b5247c4a74c2..0645adbb211e919f43263ab6cc7869e34abddb0a 100644
--- a/src/config/image_processors.rs
+++ b/src/config/image_processors.rs
@@ -1,6 +1,6 @@
 use crate::img_processors::Processors;
 
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Clone, Deserialize, Debug)]
 pub struct ProcessorParams {
     pub effect: Processors,
     #[serde(default = "default_strength")]
@@ -21,4 +21,4 @@ impl Default for ProcessorParams {
             layer: 0,
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/config/mod.rs b/src/config/mod.rs
index 3d4ac635421345de754c929046a0c76824fa9943..b1cde04cc79bc99da9660cab7565d841cf93a4fb 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -5,8 +5,8 @@ use serde_derive::Deserialize;
 
 use crate::result::{MBGError, MBGResult};
 
-pub mod logger;
 pub mod image_processors;
+pub mod logger;
 
 #[derive(Serialize, Default, Deserialize, Debug)]
 pub struct Config {
@@ -64,7 +64,9 @@ impl Config {
         config_path.push(".mbg.toml");
         let mut conf_file = File::create(config_path)?;
         let mut default_conf = Self::default();
-        default_conf.processors.push(image_processors::ProcessorParams::default());
+        default_conf
+            .processors
+            .push(image_processors::ProcessorParams::default());
         default_conf.blender.push(0);
         let contents = toml::to_string_pretty(&default_conf)?;
         conf_file.write_all(contents.as_bytes())?;
diff --git a/src/display.rs b/src/display.rs
index 124f5baf86f5d1ac530ca62f95f3ccda6c0b8299..e3c9f3e7ae84b2df4040488f5681b97d158c6d0f 100644
--- a/src/display.rs
+++ b/src/display.rs
@@ -29,7 +29,6 @@ pub fn get_max_resolution() -> MBGResult<(i32, i32)> {
     Ok((height, width))
 }
 
-
 // pub fn update_bg(img_bytes: Vec<u8>) -> MBGResult<()> {
 //     debug!("TODO: Change background.");
 //     file.write_all(img_bytes.as_slice())?;
@@ -52,4 +51,4 @@ pub fn get_max_resolution() -> MBGResult<(i32, i32)> {
 //     // debug!("{}", count);
 //     // debug!("{}x{}", screen.width_in_pixels(), screen.height_in_pixels());
 //     Ok(())
-// }
\ No newline at end of file
+// }
diff --git a/src/img_processors/blur.rs b/src/img_processors/blur.rs
index 7456e5786492c8815e14c0966a23497a91a9cc69..2432acfb52b85863b36f6b2193286adf7e979dc4 100644
--- a/src/img_processors/blur.rs
+++ b/src/img_processors/blur.rs
@@ -1,21 +1,21 @@
-use image::{DynamicImage};
+use image::DynamicImage;
 use imageproc::filter::gaussian_blur_f32;
 
 use crate::config::image_processors::ProcessorParams;
-use crate::result::{MBGResult};
+use crate::result::MBGResult;
 
 use super::ImageProcessor;
 
 pub struct BlurProcessor {
     pub params: ProcessorParams,
-    pub screen_height: i32,
-    pub screen_width: i32,
 }
 
 impl ImageProcessor for BlurProcessor {
     fn process(&self, img: &DynamicImage) -> MBGResult<DynamicImage> {
         debug!("Blurring image with sigma: {}", self.params.strength);
-        Ok(DynamicImage::ImageRgba8(gaussian_blur_f32(&img.to_rgba(), self.params.strength)))
+        Ok(DynamicImage::ImageRgba8(gaussian_blur_f32(
+            &img.to_rgba(),
+            self.params.strength,
+        )))
     }
 }
-
diff --git a/src/img_processors/border.rs b/src/img_processors/border.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b9b41c925bfc11ea5512503f478170aaf6a0d657
--- /dev/null
+++ b/src/img_processors/border.rs
@@ -0,0 +1,88 @@
+use std::cmp::min;
+
+use image::{DynamicImage, GenericImageView, Rgba, RgbaImage};
+use image::imageops::{FilterType, overlay};
+use imageproc::drawing::{Blend, Canvas, draw_filled_circle_mut};
+
+use crate::config::image_processors::ProcessorParams;
+use crate::img_processors::ImageProcessor;
+use crate::result::{MBGError, MBGResult};
+
+pub struct Border {
+    pub params: ProcessorParams,
+    pub circle: bool,
+    pub color: String,
+    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 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);
+        mask = mask.resize(img_width, img_height, FilterType::Nearest);
+        let mut res = mask.clone();
+        for (x, y, pix) in mask.pixels() {
+            let pixel = image::GenericImageView::get_pixel(img, x, y).0;
+            res.draw_pixel(x, y, Rgba([pixel[0], pixel[1], pixel[2], pix.0[3]]));
+        }
+        Ok(res)
+    }
+
+    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 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."))
+            );
+        }
+        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]),
+        };
+        if self.circle {
+            let mut mask = DynamicImage::ImageRgba8(RgbaImage::from_pixel(
+                img_width + self.width * 2,
+                img_height + self.width * 2,
+                Rgba([0u8, 0u8, 0u8, 0u8]),
+            ));
+            let radius = min(center.0, center.1);
+            let cir_center = (center.0 as i32, center.1 as i32);
+            draw_filled_circle_mut(&mut mask, cir_center, radius as i32, target_color);
+            overlay(&mut mask, img, self.width, self.width);
+            Ok(mask)
+        } else {
+            let mut mask = DynamicImage::ImageRgba8(RgbaImage::from_pixel(
+                img_width + self.width * 2,
+                img_height + self.width * 2,
+                target_color,
+            ));
+            overlay(&mut mask, img, self.width, self.width);
+            Ok(mask)
+        }
+    }
+}
+
+impl ImageProcessor for Border {
+    fn process(&self, img: &DynamicImage) -> MBGResult<DynamicImage> {
+        let mut res = img.clone();
+        if self.circle {
+            res = self.crop_circle(&res)?;
+        }
+        if self.width > 0 {
+            res = self.draw_border(&res)?;
+        }
+        Ok(res)
+    }
+}
diff --git a/src/img_processors/crop.rs b/src/img_processors/crop.rs
index 72a5794d256f71a60a5e87f0ba5bcde45d28e129..498b47eb56958a41f9d1e21ccc99b8ead6511f05 100644
--- a/src/img_processors/crop.rs
+++ b/src/img_processors/crop.rs
@@ -17,11 +17,26 @@ pub struct CropProcessor {
 impl ImageProcessor for CropProcessor {
     fn process(&self, img: &DynamicImage) -> MBGResult<DynamicImage> {
         let mut res = img.clone();
-        let requested_width = if self.fit { self.screen_width as u32 } else { self.width };
-        let requested_height = if self.fit { self.screen_height as u32 } else { self.height };
-        debug!("Cropping image to be {}x{}", requested_width, requested_height);
-        let start_x = (res.width() / 2).checked_sub(requested_width / 2).unwrap_or_else(|| 0);
-        let start_y = (res.height() / 2).checked_sub(requested_height / 2).unwrap_or_else(|| 0);
+        let requested_width = if self.fit {
+            self.screen_width as u32
+        } else {
+            self.width
+        };
+        let requested_height = if self.fit {
+            self.screen_height as u32
+        } else {
+            self.height
+        };
+        debug!(
+            "Cropping image to be {}x{}",
+            requested_width, requested_height
+        );
+        let start_x = (res.width() / 2)
+            .checked_sub(requested_width / 2)
+            .unwrap_or_else(|| 0);
+        let start_y = (res.height() / 2)
+            .checked_sub(requested_height / 2)
+            .unwrap_or_else(|| 0);
         Ok(res.crop(start_x, start_y, requested_width, requested_height))
     }
-}
\ No newline at end of file
+}
diff --git a/src/img_processors/mod.rs b/src/img_processors/mod.rs
index 4a2c523aeabe323871f9caa20a2dbce7be44a62d..d9d13e38de41a5a7d41e30c39120a11cb1e84d7d 100644
--- a/src/img_processors/mod.rs
+++ b/src/img_processors/mod.rs
@@ -1,17 +1,18 @@
-use image::{DynamicImage, GenericImageView};
 use image::imageops::overlay;
+use image::{DynamicImage, GenericImageView};
 
-use crate::config::Config;
 use crate::config::image_processors::ProcessorParams;
+use crate::config::Config;
 use crate::display::get_max_resolution;
 use crate::result::{MBGError, MBGResult};
 
-pub mod scale;
-pub mod none;
-pub mod crop;
 pub mod blur;
+pub mod border;
+pub mod crop;
+pub mod none;
+pub mod scale;
 
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Clone, Deserialize, Debug)]
 pub enum Processors {
     None,
     Blur,
@@ -24,6 +25,14 @@ pub enum Processors {
         #[serde(default)]
         fit: bool,
     },
+    Border {
+        #[serde(default)]
+        circle: bool,
+        #[serde(default)]
+        color: String,
+        #[serde(default)]
+        width: u32,
+    },
 }
 
 pub trait ImageProcessor {
@@ -70,7 +79,9 @@ pub fn process_image(img_bytes: Vec<u8>) -> MBGResult<DynamicImage> {
         debug!("Filter applied");
     }
     if layers.is_empty() {
-        return Err(MBGError::ImageError(String::from("No processors was found.")));
+        return Err(MBGError::ImageError(String::from(
+            "No processors was found.",
+        )));
     }
     if conf.blender.is_empty() {
         Ok(layers[0].clone())
@@ -79,7 +90,6 @@ pub fn process_image(img_bytes: Vec<u8>) -> MBGResult<DynamicImage> {
     }
 }
 
-
 pub fn blend_layers(layers: Vec<DynamicImage>, blender: Vec<u8>) -> MBGResult<DynamicImage> {
     let mut res = layers[0].clone();
     for index in blender.into_iter() {
@@ -96,14 +106,40 @@ impl_params_getter!(
     none::NoneProcessor,
     blur::BlurProcessor,
     scale::ScaleProcessor,
-    crop::CropProcessor
+    crop::CropProcessor,
+    border::Border
 );
 
-fn mapper(params: ProcessorParams, screen_height: i32, screen_width: i32) -> Box<dyn ImgFilterTrait> {
-    match params.effect {
-        Processors::None => { Box::new(none::NoneProcessor { params, screen_height, screen_width }) }
-        Processors::Blur => { Box::new(blur::BlurProcessor { params, screen_height, screen_width }) }
-        Processors::Scale => { Box::new(scale::ScaleProcessor { params, screen_height, screen_width }) }
-        Processors::Crop { width, height, fit } => { Box::new(crop::CropProcessor { params, screen_height, screen_width, height, width, fit }) }
+fn mapper(
+    params: ProcessorParams,
+    screen_height: i32,
+    screen_width: i32,
+) -> Box<dyn ImgFilterTrait> {
+    match params.clone().effect {
+        Processors::None => Box::new(none::NoneProcessor { params }),
+        Processors::Blur => Box::new(blur::BlurProcessor { params }),
+        Processors::Scale => Box::new(scale::ScaleProcessor {
+            params,
+            screen_height,
+            screen_width,
+        }),
+        Processors::Crop { width, height, fit } => Box::new(crop::CropProcessor {
+            params,
+            screen_height,
+            screen_width,
+            height,
+            width,
+            fit,
+        }),
+        Processors::Border {
+            circle,
+            color,
+            width,
+        } => Box::new(border::Border {
+            params,
+            circle,
+            color,
+            width,
+        }),
     }
 }
\ No newline at end of file
diff --git a/src/img_processors/none.rs b/src/img_processors/none.rs
index 6cdee0cdb8f67670cb0f6dd8cc7f8f55c8491613..f19725371c383e3cabcc74082a48b98598290d45 100644
--- a/src/img_processors/none.rs
+++ b/src/img_processors/none.rs
@@ -6,8 +6,6 @@ use crate::result::MBGResult;
 
 pub struct NoneProcessor {
     pub params: ProcessorParams,
-    pub screen_height: i32,
-    pub screen_width: i32,
 }
 
 impl ImageProcessor for NoneProcessor {
@@ -15,4 +13,4 @@ impl ImageProcessor for NoneProcessor {
         debug!("Do nothing with image");
         Ok(img.clone())
     }
-}
\ No newline at end of file
+}
diff --git a/src/img_processors/scale.rs b/src/img_processors/scale.rs
index 3b8320de3053bdb4ec86b6b37e9ec725f1e9a7e9..e1c20e3d6748ec21593d9d098110689e79c557ae 100644
--- a/src/img_processors/scale.rs
+++ b/src/img_processors/scale.rs
@@ -1,5 +1,5 @@
-use image::{DynamicImage, GenericImageView};
 use image::imageops::FilterType;
+use image::{DynamicImage, GenericImageView};
 
 use crate::config::image_processors::ProcessorParams;
 use crate::result::MBGResult;
@@ -16,6 +16,10 @@ impl super::ImageProcessor for ScaleProcessor {
         let scale_factor = ((abs_width / img.width()) as f32) * self.params.strength;
         let scale_factor = scale_factor.ceil() as u32;
         debug!("Scaling image by {} times", scale_factor);
-        Ok(img.resize(img.width() * scale_factor, img.height() * scale_factor, FilterType::Nearest))
+        Ok(img.resize(
+            img.width() * scale_factor,
+            img.height() * scale_factor,
+            FilterType::Nearest,
+        ))
     }
 }
diff --git a/src/main.rs b/src/main.rs
index bfeeb16da6a8a1f16c9cf8a669d30cb927e2be8d..af98e4d430e4141194fe76588a6e77980a462d54 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -17,16 +17,16 @@ use crate::result::{MBGError, MBGResult};
 pub mod background;
 pub mod config;
 pub mod dbus_interface;
+pub mod display;
 pub mod img_processors;
 pub mod logging;
 pub mod player_dbus;
-pub mod display;
 pub mod result;
 
 #[derive(Debug, StructOpt)]
 #[structopt(
-name = "music_bg",
-about = "Listens to your mpris interface and sets you a cool background."
+    name = "music_bg",
+    about = "Listens to your mpris interface and sets you a cool background."
 )]
 struct Opt {
     #[structopt(subcommand)]
@@ -38,8 +38,8 @@ enum RunMode {
     #[structopt(name = "run", about = "Run d-bus listener")]
     Run,
     #[structopt(
-    name = "config",
-    about = "Create or replace default configuration file."
+        name = "config",
+        about = "Create or replace default configuration file."
     )]
     GenConf,
 }
diff --git a/src/player_dbus.rs b/src/player_dbus.rs
index dabcdf41e0abfc9b21ed13f1ddb68dfa94b0857b..5ece33b29ec71289e3be9e837493371dc2568a44 100644
--- a/src/player_dbus.rs
+++ b/src/player_dbus.rs
@@ -1,10 +1,10 @@
 use std::collections::HashMap;
 use std::time::Duration;
 
-use dbus::{arg, Message};
 use dbus::arg::RefArg;
 use dbus::blocking::Connection;
 use dbus::message::{MatchRule, SignalArgs};
+use dbus::{arg, Message};
 
 use crate::background::{nitrogen_restore, process_image_url, reset_background_handler};
 use crate::config::Config;
diff --git a/src/result.rs b/src/result.rs
index ead167a1995afd4bc086199285f1bd1677afd3d9..be9d7f83086a97d5901935a05c370dfa7b76bd43 100644
--- a/src/result.rs
+++ b/src/result.rs
@@ -1,5 +1,3 @@
-
-
 pub type MBGResult<T> = Result<T, MBGError>;
 
 #[derive(Debug, Fail)]
diff --git a/systemd/music_bg.service b/systemd/music_bg.service
index 738fedf4ab6e38860df321166dee7fd727511f7c..c21e9b7ea06eeb6cb7d82f9d4d6d202996ff291b 100644
--- a/systemd/music_bg.service
+++ b/systemd/music_bg.service
@@ -1,6 +1,6 @@
 [Unit]
 Description=Dynamic music background
-After=network.target
+After=graphical-session.target
 
 [Service]
 Type=simple