use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::io::{self, prelude::*};
use std::path::{Path, PathBuf};
use std::process::exit;
const SYS_PATHS: [&str; 2] = ["/sys/class/backlight", "/sys/class/leds"];
pub trait Controller {
fn get_brightness(&self) -> u32;
fn get_max_brightness(&self) -> u32;
fn set_brightness(&self, value: u32);
fn check_brightness_value(&self, value: u32) {
let max = self.get_max_brightness();
if value > max {
eprintln!("brightness value too high: {value} > {max}",);
exit(exitcode::DATAERR);
}
}
}
pub struct RawController {
path: PathBuf,
}
impl RawController {
pub fn new(path: PathBuf) -> Self {
Self { path }
}
}
impl Controller for RawController {
fn get_brightness(&self) -> u32 {
read_file_to_int(self.path.join("brightness"))
}
fn get_max_brightness(&self) -> u32 {
read_file_to_int(self.path.join("max_brightness"))
}
fn set_brightness(&self, value: u32) {
self.check_brightness_value(value);
let path = self.path.join("brightness");
let mut file = match OpenOptions::new().write(true).read(true).open(&path) {
Err(why) => {
eprintln!("couldn't open '{}': {:?}", &path.display(), why.kind());
exit(exitcode::OSFILE);
}
Ok(file) => file,
};
match write!(file, "{}", value) {
Ok(_) => {}
Err(err) => {
eprintln!(
"could not write '{value}' to file '{}': {:?}",
&path.display(),
err.kind()
);
exit(exitcode::OSFILE);
}
};
}
}
pub struct LinController {
parent_controller: RawController,
}
impl LinController {
pub fn new(path: PathBuf) -> Self {
Self {
parent_controller: RawController::new(path),
}
}
}
impl Controller for LinController {
fn get_brightness(&self) -> u32 {
((self.parent_controller.get_brightness() as f64
/ self.parent_controller.get_max_brightness() as f64)
* self.get_max_brightness() as f64) as u32
}
fn get_max_brightness(&self) -> u32 {
100
}
fn set_brightness(&self, value: u32) {
self.check_brightness_value(value);
if value > self.get_max_brightness() {
eprintln!(
"brightness value too high! {value} > {}",
self.get_max_brightness()
);
exit(exitcode::DATAERR);
}
self.parent_controller.set_brightness(
(value * self.parent_controller.get_max_brightness()) / self.get_max_brightness(),
)
}
}
pub struct LogController {
parent_controller: RawController,
}
impl LogController {
pub fn new(path: PathBuf) -> Self {
Self {
parent_controller: RawController::new(path),
}
}
}
impl Controller for LogController {
fn get_brightness(&self) -> u32 {
((self.parent_controller.get_brightness() as f64).log10()
/ (self.parent_controller.get_max_brightness() as f64).log10()
* self.get_max_brightness() as f64) as u32
}
fn get_max_brightness(&self) -> u32 {
100
}
fn set_brightness(&self, value: u32) {
self.check_brightness_value(value);
if value > self.get_max_brightness() {
eprintln!(
"brightness value too high! {value} > {}",
self.get_max_brightness()
);
exit(exitcode::DATAERR);
}
self.parent_controller.set_brightness(10f64.powf(
(value as f64 / self.get_max_brightness() as f64)
* (self.parent_controller.get_max_brightness() as f64).log10(),
) as u32)
}
}
fn read_file_to_int(path: PathBuf) -> u32 {
let mut file = match File::open(&path) {
Err(why) => {
eprintln!("couldn't open {}: {:?}", path.display(), why.kind());
exit(exitcode::OSFILE);
}
Ok(file) => file,
};
let mut s = String::new();
match file.read_to_string(&mut s) {
Err(why) => {
eprintln!("couldn't read {}: {:?}", path.display(), why.kind());
exit(exitcode::OSFILE);
}
Ok(_) => return s.trim().parse().unwrap(),
}
}
pub fn get_controllers() -> Result<(String, HashMap<String, PathBuf>), io::Error> {
let mut controllers: HashMap<String, PathBuf> = HashMap::new();
let mut default = None;
for path in SYS_PATHS {
if Path::new(path).exists() {
for name in Path::new(path).read_dir()? {
let name = name?.path();
let key = String::from(name.file_name().unwrap().to_str().unwrap());
if default.is_none() {
default = Some(key.clone());
}
controllers.insert(key, name);
}
}
}
Ok((
default.unwrap_or_else(|| {
eprintln!("no devices found");
exit(exitcode::OSFILE)
}),
controllers,
))
}