use itertools::Itertools;
use std::sync::mpsc;
mod als;
mod brightness;
mod config;
mod device_file;
mod frame;
mod predictor;
fn main() {
let panic_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic_info| {
panic_hook(panic_info);
std::process::exit(1);
}));
env_logger::builder()
.filter_level(log::LevelFilter::Info)
.parse_default_env()
.init();
let config = match config::load() {
Ok(config) => config,
Err(err) => panic!("Unable to load config: {}", err),
};
log::debug!("Using {:#?}", config);
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<dyn brightness::Brightness + Send>)
}
config::Output::DdcUtil(cfg) => {
brightness::DdcUtil::new(&cfg.name, cfg.min_brightness)
.map(|b| Box::new(b) as Box<dyn brightness::Brightness + Send>)
}
};
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 frame_capturer: Box<dyn frame::capturer::Capturer> =
match output_capturer {
config::Capturer::Wlroots => {
Box::<frame::capturer::wlroots::Capturer>::default()
}
config::Capturer::None => {
Box::<frame::capturer::none::Capturer>::default()
}
};
let controller = predictor::Controller::new(
prediction_tx,
user_rx,
als_rx,
true,
&output_name,
);
frame_capturer.run(&output_name, controller)
})
.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
}
}
})
.collect_vec();
std::thread::Builder::new()
.name("als".to_string())
.spawn(move || {
let als: Box<dyn als::Als> = 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::<als::none::Als>::default(),
};
als::controller::Controller::new(als, als_txs).run();
})
.expect("Unable to start thread: als");
log::info!("Continue adjusting brightness and wluma will learn your preference over time.");
std::thread::park();
}