diff --git a/Cargo.lock b/Cargo.lock index 4f128cc7..7833e055 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,17 @@ dependencies = [ "memchr", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.0.1" @@ -22,10 +33,15 @@ name = "autoclockspeed" version = "0.1.6" dependencies = [ "chrono", + "env_logger", + "home", + "log", "nix", "regex", + "serde", "structopt", "termion", + "toml", ] [[package]] @@ -46,6 +62,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "chrono" version = "0.4.19" @@ -70,6 +92,19 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "heck" version = "0.3.3" @@ -79,6 +114,30 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "home" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654" +dependencies = [ + "winapi", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "lazy_static" version = "1.4.0" @@ -91,6 +150,15 @@ version = "0.2.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "memchr" version = "2.4.0" @@ -105,7 +173,7 @@ checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" dependencies = [ "bitflags", "cc", - "cfg-if", + "cfg-if 0.1.10", "libc", "void", ] @@ -212,6 +280,26 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +[[package]] +name = "serde" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "structopt" version = "0.3.22" @@ -247,6 +335,15 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + [[package]] name = "termion" version = "1.5.6" @@ -279,6 +376,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + [[package]] name = "unicode-segmentation" version = "1.8.0" @@ -331,6 +437,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 513758ee..67823252 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,11 @@ regex = "1.5.4" termion = "1.5.3" nix = "0.15.0" chrono = "0.4" +log = "0.4.14" +home = "0.5.3" +env_logger = "0.9.0" +toml = "0.5.8" +serde = { version = "1.0", features = ["derive"] } [[bin]] name = "acs" diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 00000000..4560a10d --- /dev/null +++ b/src/config.rs @@ -0,0 +1,42 @@ +use super::local::config_path; +use super::warn_user; +use serde::{Deserialize, Serialize}; +use std::fs::File; +use std::io::Read; + +#[derive(Debug, Deserialize, Serialize)] +pub struct Config { + pub powersave_under: i8, + // Future variables + // pub charging_powersave_under: i32, +} + +pub fn default_config() -> Config { + Config { + powersave_under: 20, + } +} + +pub fn open_config() -> Result { + // Open config file + let mut config_file = match File::open(config_path().as_str()) { + Ok(a) => a, + Err(e) => return Err(e), + }; + + // Read it to new string + let mut config: String = String::new(); + config_file.read_to_string(&mut config).unwrap(); + + // Try parsing as string, warn user if broken + // e.g. WARN: missing field `charging_powersave_under` at line 1 column 1 + let config_toml: Config = match toml::from_str(config.as_str()) { + Ok(a) => a, + Err(e) => { + warn_user!(format!("{}", e)); + panic!("{}", e); + } + }; + + Ok(config_toml) +} diff --git a/src/daemon.rs b/src/daemon.rs index 2ce578b0..0c125211 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -1,3 +1,4 @@ +use super::config::Config; use super::cpu::{Speed, CPU}; use super::logger; use super::logger::Interface; @@ -30,6 +31,7 @@ pub struct Daemon { pub charging: bool, pub charge: i8, pub logger: logger::Logger, + pub config: Config, } fn make_gov_powersave(cpu: &mut CPU) -> Result<(), Error> { @@ -69,7 +71,7 @@ impl Checker for Daemon { let timeout = time::Duration::from_millis(self.delay); // The state for rules - let mut already_under_20_percent: bool = false; + let mut already_under_powersave_under_percent: bool = false; let mut already_charging: bool = false; let mut already_closed: bool = false; let mut first_run: bool = true; @@ -89,7 +91,8 @@ impl Checker for Daemon { // but only if the previous states were incorrect if first_run == true { already_charging = !self.charging; - already_under_20_percent = !(self.charge < 20); + already_under_powersave_under_percent = + !(self.charge < self.config.powersave_under); already_closed = self.lid_state == LidState::Closed; first_run = false; } @@ -108,7 +111,7 @@ impl Checker for Daemon { if self.lid_state == LidState::Open && already_closed { // A few checks inorder to insure the computer should actually be in performance - if !(self.charge < 20) && self.charging { + if !(self.charge < self.config.powersave_under) && self.charging { self.logger.log( "Governor set to performance because lid opened", logger::Severity::Log, @@ -124,27 +127,34 @@ impl Checker for Daemon { already_closed = false; } - // Under 20% rule -> gov powersave - // If the battery life is below 20%, set gov to powersave + // Under self.config.powersave_under% rule -> gov powersave + // If the battery life is below self.config.powersave_under%, set gov to powersave - if self.charge < 20 && !already_under_20_percent { + if self.charge < self.config.powersave_under + && !already_under_powersave_under_percent + { self.logger.log( - "Governor set to powersave because battery was less than 20", + &format!( + "Governor set to powersave because battery was less than {}", + self.config.powersave_under + ), logger::Severity::Log, ); self.apply_to_cpus(&make_gov_powersave)?; - already_under_20_percent = true; + already_under_powersave_under_percent = true; // Make sure to reset state } - if self.charge >= 20 { - already_under_20_percent = false; + if self.charge >= self.config.powersave_under { + already_under_powersave_under_percent = false; } // Charging rule -> gov performance // If the battery is charging, set to performance if self.charging && !already_charging { - if self.lid_state == LidState::Closed || self.charge < 20 { + if self.lid_state == LidState::Closed + || self.charge < self.config.powersave_under + { self.logger.log( "Battery is charging however the governor remains unchanged", logger::Severity::Log, @@ -270,7 +280,12 @@ fn format_message(edit: bool, started_as_edit: bool, forced_reason: String, dela ) } -pub fn daemon_init(verbose: bool, delay: u64, mut edit: bool) -> Result { +pub fn daemon_init( + verbose: bool, + delay: u64, + mut edit: bool, + config: Config, +) -> Result { let started_as_edit = edit; let mut forced_reason: String = String::new(); // Check if the device has a battery, otherwise force it to monitor mode @@ -318,6 +333,7 @@ pub fn daemon_init(verbose: bool, delay: u64, mut edit: bool) -> Result::new(), }, + config, }; // Make a cpu struct for each cpu listed diff --git a/src/display.rs b/src/display.rs index 055d3af3..7e04a7b4 100644 --- a/src/display.rs +++ b/src/display.rs @@ -3,6 +3,34 @@ use super::power::LidState; use std::fmt::Display; use termion::{color, style}; +#[macro_export] +macro_rules! bold_color_text { + ($a:expr, $b:expr) => {{ + format!( + "{}{}{}{}{}", + termion::style::Bold, + termion::color::Fg($b), + $a, + termion::color::Fg(termion::color::Reset), + termion::style::Reset, + ) + }}; +} + +#[macro_export] +macro_rules! warn_user { + ($a:expr) => {{ + println!( + "{}{}WARN:{}{} {}", + termion::style::Bold, + termion::color::Fg(termion::color::Yellow), + termion::color::Fg(termion::color::Reset), + termion::style::Reset, + $a, + ); + }}; +} + pub fn print_freq(f: i32, raw: bool) { if raw { println!("{}", f); diff --git a/src/error.rs b/src/error.rs index 986909c7..75eaec7b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -17,10 +17,7 @@ impl From for Error { impl fmt::Debug for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "Something went wrong. Try running with sudo.\n" - ) + write!(f, "Something went wrong. Try running with sudo.\n") } } diff --git a/src/local.rs b/src/local.rs new file mode 100644 index 00000000..7e3b3c69 --- /dev/null +++ b/src/local.rs @@ -0,0 +1,60 @@ +use super::debug; +use std::fs; +use std::path::Path; + +pub fn check_for_local(path_name: &str) -> bool { + match home::home_dir() { + Some(path) => { + let local_path = + Path::new(format!("{}/{}/", path.display(), &path_name).as_str()).to_owned(); + + if local_path.exists() { + return true; + } else { + debug!("Could not find ~/{}", path_name); + } + } + None => println!("Could not get home directory"), + } + + return false; +} + +pub fn create_local(path_name: &str) { + match home::home_dir() { + Some(path) => { + let local_path = + Path::new(&format!("{}/{}", path.display(), &path_name).as_str()).to_owned(); + + match fs::create_dir_all(local_path.clone()) { + Ok(_) => debug!("Created {:?}", local_path), + Err(e) => eprintln!("{}", e), + } + } + None => println!("Could not get home directory"), + } +} + +pub fn config_path() -> String { + match home::home_dir() { + Some(path) => { + format!("{}/{}", path.display(), ".config/acs/acs.toml") + } + None => { + println!("Could not get home directory"); + String::new() + } + } +} + +pub fn local_config_file_exists() -> bool { + Path::new(&config_path()).exists() +} + +pub fn local_config_dir_exists() -> bool { + check_for_local(".config/acs/") +} + +pub fn create_local_config_dir() { + create_local(".config/acs/") +} diff --git a/src/main.rs b/src/main.rs index 886d4118..2ed01d3b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,12 @@ +use config::{default_config, open_config}; use daemon::{daemon_init, Checker}; use display::{ print_available_governors, print_cpu_governors, print_cpu_speeds, print_cpu_temp, print_cpus, print_freq, print_power, print_turbo, }; use error::Error; +use local::{create_local_config_dir, local_config_dir_exists}; +use log::debug; use power::{read_battery_charge, read_lid_state, read_power_source}; use std::process::exit; use structopt::StructOpt; @@ -12,10 +15,12 @@ use system::{ list_cpu_governors, list_cpu_speeds, list_cpu_temp, list_cpus, }; +pub mod config; pub mod cpu; pub mod daemon; pub mod display; pub mod error; +pub mod local; pub mod logger; pub mod power; pub mod system; @@ -130,8 +135,25 @@ enum Command { } fn main() { + env_logger::init(); let mut main_daemon: daemon::Daemon; + // Create config directory if it doesn't exist + if !local_config_dir_exists() { + create_local_config_dir(); + } + + // Config will always exist, default or otherwise + let config: config::Config = match open_config() { + Ok(a) => a, + Err(_) => { + warn_user!( + "Using default config. Create file ~/.config/acs/acs.toml for custom config." + ); + default_config() + } + }; + match Command::from_args() { // Everything starting with "get" Command::Get { get } => match get { @@ -182,9 +204,9 @@ fn main() { // Everything starting with "set" Command::Set { set } => match set { - SetType::Gov { value } => match daemon_init(true, 0, false) { + SetType::Gov { value } => match daemon_init(true, 0, false, config) { Ok(mut d) => match d.set_govs(value.clone()) { - Ok(_) => {}, + Ok(_) => {} Err(e) => eprint!("Could not set gov, {:?}", e), }, Err(_) => eprint!("Could not run daemon in edit mode"), @@ -192,7 +214,7 @@ fn main() { }, // Run command - Command::Run { quiet, delay } => match daemon_init(!quiet, delay, true) { + Command::Run { quiet, delay } => match daemon_init(!quiet, delay, true, config) { Ok(d) => { main_daemon = d; main_daemon.run().unwrap_err(); @@ -201,7 +223,7 @@ fn main() { }, // Monitor command - Command::Monitor { delay } => match daemon_init(true, delay, false) { + Command::Monitor { delay } => match daemon_init(true, delay, false, config) { Ok(d) => { main_daemon = d; main_daemon.run().unwrap_err(); diff --git a/src/power.rs b/src/power.rs index 5e9619f5..e0e26064 100644 --- a/src/power.rs +++ b/src/power.rs @@ -40,13 +40,12 @@ pub fn read_lid_state() -> Result { let lid_status_path: Vec<&str> = vec![ "/proc/acpi/button/lid/LID/state", "/proc/acpi/button/lid/LID0/state", - "/proc/acpi/button/lid/LID1/state" + "/proc/acpi/button/lid/LID1/state", + "/proc/acpi/button/lid/LID2/state", ]; let path: &str = match get_best_path(lid_status_path) { - Ok(path) => { - path - }, + Ok(path) => path, Err(error) => { if error.type_id() == Error::IO.type_id() { // Make sure to return IO error if one occurs @@ -73,13 +72,12 @@ pub fn read_battery_charge() -> Result { let battery_charge_path: Vec<&str> = vec![ "/sys/class/power_supply/BAT/capacity", "/sys/class/power_supply/BAT0/capacity", - "/sys/class/power_supply/BAT1/capacity" + "/sys/class/power_supply/BAT1/capacity", + "/sys/class/power_supply/BAT2/capacity", ]; let path: &str = match get_best_path(battery_charge_path) { - Ok(path) => { - path - }, + Ok(path) => path, Err(error) => { if error.type_id() == Error::IO.type_id() { // Make sure to return IO error if one occurs @@ -101,18 +99,15 @@ pub fn read_battery_charge() -> Result { } pub fn read_power_source() -> Result { - - let power_source_path: Vec<&str> = vec![ + let power_source_path: Vec<&str> = vec![ "/sys/class/power_supply/AC/online", "/sys/class/power_supply/AC0/online", "/sys/class/power_supply/AC1/online", - "/sys/class/power_supply/ACAD/online" + "/sys/class/power_supply/ACAD/online", ]; let path: &str = match get_best_path(power_source_path) { - Ok(path) => { - path - }, + Ok(path) => path, Err(error) => { if error.type_id() == Error::IO.type_id() { // Make sure to return IO error if one occurs @@ -132,8 +127,7 @@ pub fn read_power_source() -> Result { return Ok(pwr_str == "1"); } -pub fn get_best_path(paths: Vec::<&str>) -> Result<&str, Error> { - +pub fn get_best_path(paths: Vec<&str>) -> Result<&str, Error> { for path in paths.iter() { if Path::new(path).exists() { return Ok(path); @@ -141,4 +135,4 @@ pub fn get_best_path(paths: Vec::<&str>) -> Result<&str, Error> { } return Err(Error::Unknown); -} \ No newline at end of file +} diff --git a/src/system.rs b/src/system.rs index 2056bd9e..49bccf96 100644 --- a/src/system.rs +++ b/src/system.rs @@ -197,22 +197,18 @@ mod tests { } #[test] - fn acs_list_cpus_test() -> Result<(), Error>{ - assert_eq!( - type_of(list_cpus()?), - type_of(Vec::::new()) - ); + fn acs_list_cpus_test() -> Result<(), Error> { + assert_eq!(type_of(list_cpus()?), type_of(Vec::::new())); for x in list_cpus()? { assert!(x.name.len() > 0); assert!(x.max_freq > 0); assert!(x.min_freq > 0); - assert!(x.cur_freq> 0); + assert!(x.cur_freq > 0); assert!(x.cur_temp > 0); assert!(x.gov == "powersave" || x.gov == "performance"); - } Ok(()) }