From 47675194f0371e463963a964421e2ec5b3244cc3 Mon Sep 17 00:00:00 2001 From: Maxim Baz Date: Mon, 7 Aug 2023 11:41:25 +0200 Subject: [PATCH] Refactor skipping disconnected outputs --- src/brightness/mod.rs | 2 +- src/config/app.rs | 16 +++ src/frame/capturer/mod.rs | 2 +- src/frame/capturer/wlroots.rs | 8 +- src/main.rs | 192 +++++++++++++++++----------------- 5 files changed, 116 insertions(+), 104 deletions(-) diff --git a/src/brightness/mod.rs b/src/brightness/mod.rs index 4c8bc07..63f3d70 100644 --- a/src/brightness/mod.rs +++ b/src/brightness/mod.rs @@ -12,7 +12,7 @@ pub use controller::Controller; pub use ddcutil::DdcUtil; #[cfg_attr(test, automock)] -pub trait Brightness { +pub trait Brightness: Send { fn get(&mut self) -> Result>; fn set(&mut self, value: u64) -> Result>; } diff --git a/src/config/app.rs b/src/config/app.rs index ce61290..0923140 100644 --- a/src/config/app.rs +++ b/src/config/app.rs @@ -48,3 +48,19 @@ pub struct Config { pub als: Als, pub output: Vec, } + +impl Output { + pub fn name(&self) -> &str { + match self { + self::Output::Backlight(cfg) => &cfg.name, + self::Output::DdcUtil(cfg) => &cfg.name, + } + } + + pub fn capturer(&self) -> &Capturer { + match self { + self::Output::Backlight(cfg) => &cfg.capturer, + self::Output::DdcUtil(cfg) => &cfg.capturer, + } + } +} diff --git a/src/frame/capturer/mod.rs b/src/frame/capturer/mod.rs index a84f588..a817e5a 100644 --- a/src/frame/capturer/mod.rs +++ b/src/frame/capturer/mod.rs @@ -1,6 +1,6 @@ pub mod none; pub mod wlroots; -pub trait Capturer { +pub trait Capturer: Send { fn run(&mut self); } diff --git a/src/frame/capturer/wlroots.rs b/src/frame/capturer/wlroots.rs index 1f94bbd..6a80d35 100644 --- a/src/frame/capturer/wlroots.rs +++ b/src/frame/capturer/wlroots.rs @@ -36,16 +36,16 @@ pub struct Capturer { } impl Capturer { - pub fn new(output_name: &str, controller: Controller) -> Self { + pub fn new(output_name: &str, controller: Controller) -> Result> { let connection = Connection::connect_to_env().expect("Unable to connect to Wayland"); - Self { + Ok(Self { vulkan: Vulkan::new().expect("Unable to initialize Vulkan"), - output: find_output(&connection, output_name).expect("Unable to find output"), + output: find_output(&connection, output_name)?, connection, controller, pending_frame: None, - } + }) } } diff --git a/src/main.rs b/src/main.rs index 67a15d3..f031ad1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,6 @@ +use config::Output; use itertools::Itertools; +use std::error::Error; use std::sync::mpsc; mod als; @@ -30,109 +32,103 @@ fn main() { let als_txs = config .output .iter() - .filter_map(|output| { - let output = output.clone(); - - let (als_tx, als_rx) = mpsc::channel(); - let (user_tx, user_rx) = mpsc::channel(); - let (prediction_tx, prediction_rx) = mpsc::channel(); - - let (output_name, output_capturer) = match output.clone() { - config::Output::Backlight(cfg) => (cfg.name, cfg.capturer), - config::Output::DdcUtil(cfg) => (cfg.name, cfg.capturer), - }; - - let brightness = match output { - config::Output::Backlight(cfg) => { - brightness::Backlight::new(&cfg.path, cfg.min_brightness) - .map(|b| Box::new(b) as Box) - } - config::Output::DdcUtil(cfg) => { - brightness::DdcUtil::new(&cfg.name, cfg.min_brightness) - .map(|b| Box::new(b) as Box) - } - }; - - match brightness { - Ok(b) => { - let thread_name = format!("backlight-{}", output_name); - std::thread::Builder::new() - .name(thread_name.clone()) - .spawn(move || { - brightness::Controller::new(b, user_tx, prediction_rx).run(); - }) - .unwrap_or_else(|_| panic!("Unable to start thread: {}", thread_name)); - - let thread_name = format!("predictor-{}", output_name); - std::thread::Builder::new() - .name(thread_name.clone()) - .spawn(move || { - let controller = predictor::Controller::new( - prediction_tx, - user_rx, - als_rx, - true, - &output_name, - ); - - let mut frame_capturer: Box = - match output_capturer { - config::Capturer::Wlroots => { - Box::new(frame::capturer::wlroots::Capturer::new( - &output_name, - controller, - )) - } - config::Capturer::None => { - Box::new(frame::capturer::none::Capturer::new(controller)) - } - }; - - frame_capturer.run(); - }) - .unwrap_or_else(|_| panic!("Unable to start thread: {}", thread_name)); - - Some(als_tx) - } - Err(err) => { - log::warn!( - "Skipping '{}' as it might be disconnected: {}", - output_name, - err - ); - - None - } + .filter_map(|output| match init_output(output) { + Ok((mut brightness_controller, mut frame_capturer, als_tx)) => { + spawn(format!("backlight-{}", output.name()), move || { + brightness_controller.run() + }); + spawn(format!("predictor-{}", output.name()), move || { + frame_capturer.run(); + }); + + Some(als_tx) + } + Err(err) => { + log::warn!( + "Skipping '{}' as it might be disconnected: {}", + output.name(), + err + ); + + None } }) .collect_vec(); - std::thread::Builder::new() - .name("als".to_string()) - .spawn(move || { - let als: Box = match config.als { - config::Als::Iio { path, thresholds } => Box::new( - als::iio::Als::new(&path, thresholds) - .expect("Unable to initialize ALS IIO sensor"), - ), - config::Als::Time { thresholds } => Box::new(als::time::Als::new(thresholds)), - config::Als::Webcam { video, thresholds } => Box::new({ - let (webcam_tx, webcam_rx) = mpsc::channel(); - std::thread::Builder::new() - .name("als-webcam".to_string()) - .spawn(move || { - als::webcam::Webcam::new(webcam_tx, video).run(); - }) - .expect("Unable to start thread: als-webcam"); - als::webcam::Als::new(webcam_rx, thresholds) - }), - config::Als::None => Box::::default(), - }; - - als::controller::Controller::new(als, als_txs).run(); - }) - .expect("Unable to start thread: als"); + spawn("als".to_string(), move || { + let als: Box = match config.als { + config::Als::Iio { path, thresholds } => Box::new( + als::iio::Als::new(&path, thresholds).expect("Unable to initialize ALS IIO sensor"), + ), + config::Als::Time { thresholds } => Box::new(als::time::Als::new(thresholds)), + config::Als::Webcam { video, thresholds } => Box::new({ + let (webcam_tx, webcam_rx) = mpsc::channel(); + std::thread::Builder::new() + .name("als-webcam".to_string()) + .spawn(move || { + als::webcam::Webcam::new(webcam_tx, video).run(); + }) + .expect("Unable to start thread: als-webcam"); + als::webcam::Als::new(webcam_rx, thresholds) + }), + config::Als::None => Box::::default(), + }; + + als::controller::Controller::new(als, als_txs).run(); + }); log::info!("Continue adjusting brightness and wluma will learn your preference over time."); std::thread::park(); } + +fn spawn(thread_name: String, handler: F) +where + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, +{ + std::thread::Builder::new() + .name(thread_name.clone()) + .spawn(handler) + .unwrap_or_else(|_| panic!("Unable to start thread: {}", thread_name)); +} + +type OutputHandler = ( + brightness::Controller, + Box, + std::sync::mpsc::Sender, +); + +fn init_output(output: &Output) -> Result> { + let output = output.clone(); + + let (als_tx, als_rx) = mpsc::channel(); + let (user_tx, user_rx) = mpsc::channel(); + let (prediction_tx, prediction_rx) = mpsc::channel(); + + let brightness = match &output { + config::Output::Backlight(cfg) => { + brightness::Backlight::new(&cfg.path, cfg.min_brightness).map(|b| Box::new(b) as Box<_>) + } + config::Output::DdcUtil(cfg) => { + brightness::DdcUtil::new(&cfg.name, cfg.min_brightness).map(|b| Box::new(b) as Box<_>) + } + }?; + + let brightness_controller = brightness::Controller::new(brightness, user_tx, prediction_rx); + + let predictor_controller = + predictor::Controller::new(prediction_tx, user_rx, als_rx, true, output.name()); + + let frame_capturer: Box = match output.capturer() { + config::Capturer::Wlroots => { + frame::capturer::wlroots::Capturer::new(output.name(), predictor_controller) + .map(|b| Box::new(b) as Box<_>)? + } + config::Capturer::None => { + Box::new(frame::capturer::none::Capturer::new(predictor_controller)) + } + }; + + Ok((brightness_controller, frame_capturer, als_tx)) +}