diff --git a/Cargo.lock b/Cargo.lock index 0476910f..b764e0ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -666,6 +666,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", + "toml", ] [[package]] @@ -816,6 +817,8 @@ version = "4.5.8" dependencies = [ "config-traits", "dirs", + "env_logger", + "log", "rog_anime", "rog_aura", "rog_dbus", diff --git a/MANUAL.md b/MANUAL.md index cc2ae35c..371da285 100644 --- a/MANUAL.md +++ b/MANUAL.md @@ -117,60 +117,67 @@ I'm unsure of how many laptops this works on, so please try it. An Aura config itself is a file with contents: -```json -{ - "name": "aura-default", - "aura": [ - { - "Breathe": { - "led_type": { - "Key": "W" - }, - "start_colour1": [ - 255, - 0, - 20 +```ron +( + name: "aura-default", + aura: ( + effects: [ + Breathe(( + led: W, + start_colour1: (255, 0, 20), + start_colour2: (20, 255, 0), + speed: Low, + )), + Breathe(( + led: A, + start_colour1: (255, 0, 20), + start_colour2: (20, 255, 0), + speed: Low, + )), + Breathe(( + led: S, + start_colour1: (255, 0, 20), + start_colour2: (20, 255, 0), + speed: Low, + )), + Breathe(( + led: D, + start_colour1: (255, 0, 20), + start_colour2: (20, 255, 0), + speed: Low, + )), + Breathe(( + led: F, + start_colour1: (255, 0, 0), + start_colour2: (255, 0, 0), + speed: High, + )), + Static(( + led: RCtrl, + colour: (0, 0, 255), + )), + Static(( + led: LCtrl, + colour: (0, 0, 255), + )), + Static(( + led: Esc, + colour: (0, 0, 255), + )), + DoomFlicker(( + led: N9, + start_colour: (0, 0, 255), + max_percentage: 80, + min_percentage: 40, + )), ], - "start_colour2": [ - 20, - 255, - 0 - ], - "speed": "Low" - } - }, - { - "Static": { - "led_type": { - "Key": "Esc" - }, - "colour": [ - 0, - 0, - 255 - ] - } - }, - { - "Flicker": { - "led_type": { - "Key": "N9" - }, - "start_colour": [ - 0, - 0, - 255 - ], - "max_percentage": 80, - "min_percentage": 40 - } - } - ] -} + zoned: false, + ), +) ``` -If your laptop supports multizone, `"led_type"` can also be `"Zone": ` -- `"None"` +If your laptop supports multizone, `"led"` can also be `"Zone": ` +- `SingleZone` // Keyboards with only one zone - `ZonedKbLeft` // keyboard left - `ZonedKbLeftMid` // keyboard left-middle - `ZonedKbRightMid` // etc @@ -182,6 +189,25 @@ If your laptop supports multizone, `"led_type"` can also be `"Zone": = tmp.split('.').collect(); + cfg_old.push(format!("{}.cfg", parts[0])); + } + if do_rename && cfg_old.exists() { + // Now we gotta rename it + warn!("Renaming {cfg_old:?} to {config:?}"); + std::fs::rename(&cfg_old, &config).unwrap_or_else(|err| { + error!( + "Could not rename. Please remove {} then restart service: Error {}", + self.file_name(), + err + ) + }); + do_rename = false; + } + if do_rename && !cfg_old.exists() { + warn!("Config {cfg_old:?} does not exist, looking for .conf next"); + cfg_old.pop(); + let tmp = self.file_name(); + let parts: Vec<_> = tmp.split('.').collect(); + cfg_old.push(format!("{}.conf", parts[0])); + } + if do_rename && cfg_old.exists() { + // Now we gotta rename it + warn!("Renaming {cfg_old:?} to {config:?}"); + std::fs::rename(&cfg_old, &config).unwrap_or_else(|err| { + error!( + "Could not rename. Please remove {} then restart service: Error {}", + self.file_name(), + err + ) + }); + } config } @@ -146,6 +186,8 @@ where self = data; } else if let Ok(data) = serde_json::from_str(&buf) { self = data; + } else if let Ok(data) = toml::from_str(&buf) { + self = data; } else { self.rename_file_old(); self = Self::new(); @@ -205,8 +247,12 @@ where self = data; } else if let Ok(data) = serde_json::from_str(&buf) { self = data; + } else if let Ok(data) = toml::from_str(&buf) { + self = data; } else if let Ok(data) = serde_json::from_str::(&buf) { self = data.into(); + } else if let Ok(data) = toml::from_str::(&buf) { + self = data.into(); } else { self.rename_file_old(); self = Self::new(); @@ -274,10 +320,16 @@ where self = data; } else if let Ok(data) = serde_json::from_str(&buf) { self = data; + } else if let Ok(data) = toml::from_str(&buf) { + self = data; } else if let Ok(data) = serde_json::from_str::(&buf) { self = data.into(); + } else if let Ok(data) = toml::from_str::(&buf) { + self = data.into(); } else if let Ok(data) = serde_json::from_str::(&buf) { self = data.into(); + } else if let Ok(data) = toml::from_str::(&buf) { + self = data.into(); } else { self.rename_file_old(); self = Self::new(); diff --git a/daemon-user/Cargo.toml b/daemon-user/Cargo.toml index 405f111a..1d00f785 100644 --- a/daemon-user/Cargo.toml +++ b/daemon-user/Cargo.toml @@ -30,3 +30,7 @@ rog_platform = { path = "../rog-platform" } config-traits = { path = "../config-traits" } zbus.workspace = true + +# cli and logging +log.workspace = true +env_logger.workspace = true \ No newline at end of file diff --git a/daemon-user/src/user_config.rs b/daemon-user/src/config.rs similarity index 52% rename from daemon-user/src/user_config.rs rename to daemon-user/src/config.rs index 70984ca4..fef850f8 100644 --- a/daemon-user/src/user_config.rs +++ b/daemon-user/src/config.rs @@ -1,92 +1,30 @@ -use std::fs::{create_dir, OpenOptions}; -use std::io::{Read, Write}; +use std::path::PathBuf; use std::time::Duration; +use config_traits::{StdConfig, StdConfigLoad1}; use rog_anime::{ActionLoader, AnimTime, AnimeType, Fade, Sequences as AnimeSequences, Vec2}; use rog_aura::advanced::LedCode; use rog_aura::effects::{AdvancedEffects as AuraSequences, Breathe, DoomFlicker, Effect, Static}; use rog_aura::{Colour, Speed}; -use serde::de::DeserializeOwned; use serde_derive::{Deserialize, Serialize}; use crate::error::Error; -pub trait ConfigLoadSave { - fn name(&self) -> String; +const ROOT_CONF_DIR: &str = "rog"; - fn default_with_name(name: String) -> T; - - fn write(&self) -> Result<(), Error> - where - Self: serde::Serialize, - { - let mut path = if let Some(dir) = dirs::config_dir() { - dir - } else { - return Err(Error::XdgVars); - }; - - path.push("rog"); - if !path.exists() { - create_dir(path.clone())?; - } - let name = self.name(); - path.push(name + ".cfg"); - - let mut file = OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(&path)?; - - let json = serde_json::to_string_pretty(&self).unwrap(); - file.write_all(json.as_bytes())?; - Ok(()) - } - - fn load(name: String) -> Result { - let mut path = if let Some(dir) = dirs::config_dir() { - dir - } else { - return Err(Error::XdgVars); - }; - - path.push("rog"); - if !path.exists() { - create_dir(path.clone())?; - } - - path.push(name.clone() + ".cfg"); - - let mut file = OpenOptions::new() - .read(true) - .write(true) - .create(true) - .open(&path)?; - - let mut buf = String::new(); - - if let Ok(read_len) = file.read_to_string(&mut buf) { - if read_len == 0 { - let default = Self::default_with_name(name); - let json = serde_json::to_string_pretty(&default).unwrap(); - file.write_all(json.as_bytes())?; - return Ok(default); - } else if let Ok(data) = serde_json::from_str::(&buf) { - return Ok(data); - } - } - Err(Error::ConfigLoadFail) - } +fn root_conf_dir() -> PathBuf { + let mut dir = dirs::config_dir().unwrap_or_else(|| PathBuf::from("/tmp")); + dir.push(ROOT_CONF_DIR); + dir } #[derive(Debug, Deserialize, Serialize)] -pub struct UserAnimeConfig { +pub struct ConfigAnime { pub name: String, pub anime: Vec, } -impl UserAnimeConfig { +impl ConfigAnime { pub fn create(&self, anime_type: AnimeType) -> Result { let mut seq = AnimeSequences::new(anime_type); @@ -96,25 +34,17 @@ impl UserAnimeConfig { Ok(seq) } -} -impl ConfigLoadSave for UserAnimeConfig { - fn name(&self) -> String { - self.name.clone() - } - - fn default_with_name(name: String) -> Self { - UserAnimeConfig { - name, - ..Default::default() - } + pub fn set_name(mut self, name: String) -> Self { + self.name = name; + self } } -impl Default for UserAnimeConfig { +impl Default for ConfigAnime { fn default() -> Self { Self { - name: "default".to_owned(), + name: "anime-default".to_owned(), anime: vec![ ActionLoader::AsusImage { file: "/usr/share/asusd/anime/custom/diagonal-template.png".into(), @@ -172,26 +102,36 @@ impl Default for UserAnimeConfig { } } +impl StdConfig for ConfigAnime { + fn new() -> Self { + Self::default() + } + + fn file_name(&self) -> String { + format!("{}.ron", self.name) + } + + fn config_dir() -> std::path::PathBuf { + root_conf_dir() + } +} + +impl StdConfigLoad1 for ConfigAnime {} + #[derive(Debug, Deserialize, Serialize)] -pub struct UserAuraConfig { +pub struct ConfigAura { pub name: String, pub aura: AuraSequences, } -impl ConfigLoadSave for UserAuraConfig { - fn name(&self) -> String { - self.name.clone() - } - - fn default_with_name(name: String) -> Self { - UserAuraConfig { - name, - ..Default::default() - } +impl ConfigAura { + pub fn set_name(mut self, name: String) -> Self { + self.name = name; + self } } -impl Default for UserAuraConfig { +impl Default for ConfigAura { fn default() -> Self { let mut seq = AuraSequences::new(false); let mut key = Effect::Breathe(Breathe::new( @@ -228,86 +168,52 @@ impl Default for UserAuraConfig { seq.push(key); Self { - name: "default".to_owned(), + name: "aura-default".to_owned(), aura: seq, } } } +impl StdConfig for ConfigAura { + fn new() -> Self { + Self::default() + } + + fn file_name(&self) -> String { + format!("{}.ron", self.name) + } + + fn config_dir() -> std::path::PathBuf { + root_conf_dir() + } +} + +impl StdConfigLoad1 for ConfigAura {} + #[derive(Debug, Default, Deserialize, Serialize)] #[serde(default)] -pub struct UserConfig { +pub struct ConfigBase { /// Name of active anime config file in the user config directory pub active_anime: Option, /// Name of active aura config file in the user config directory pub active_aura: Option, } -impl UserConfig { - pub fn new() -> Self { +impl StdConfig for ConfigBase { + fn new() -> Self { Self { active_anime: Some("anime-default".to_owned()), active_aura: Some("aura-default".to_owned()), } } - pub fn load(&mut self) -> Result<(), Error> { - let mut path = if let Some(dir) = dirs::config_dir() { - dir - } else { - return Err(Error::XdgVars); - }; - - path.push("rog"); - if !path.exists() { - create_dir(path.clone())?; - } - - path.push("rog-user.cfg"); - - let mut file = OpenOptions::new() - .read(true) - .write(true) - .create(true) - .open(&path)?; - - let mut buf = String::new(); - - if let Ok(read_len) = file.read_to_string(&mut buf) { - if read_len == 0 { - let json = serde_json::to_string_pretty(&self).unwrap(); - file.write_all(json.as_bytes())?; - } else if let Ok(data) = serde_json::from_str::(&buf) { - self.active_anime = data.active_anime; - self.active_aura = data.active_aura; - return Ok(()); - } - } - Ok(()) + fn file_name(&self) -> String { + "rog-user.ron".to_owned() } - pub fn write(&self) -> Result<(), Error> { - let mut path = if let Some(dir) = dirs::config_dir() { - dir - } else { - return Err(Error::XdgVars); - }; - - path.push("rog"); - if !path.exists() { - create_dir(path.clone())?; - } - - path.push("rog-user.cfg"); - - let mut file = OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(&path)?; - - let json = serde_json::to_string_pretty(&self).unwrap(); - file.write_all(json.as_bytes())?; - Ok(()) + fn config_dir() -> std::path::PathBuf { + root_conf_dir() } } + +impl StdConfigLoad1 for ConfigBase {} diff --git a/daemon-user/src/ctrl_anime.rs b/daemon-user/src/ctrl_anime.rs index 25f66747..7961dd5c 100644 --- a/daemon-user/src/ctrl_anime.rs +++ b/daemon-user/src/ctrl_anime.rs @@ -4,6 +4,7 @@ use std::sync::{Arc, Mutex}; use std::thread::sleep; use std::time::{Duration, Instant}; +use config_traits::StdConfig; use rog_anime::error::AnimeError; use rog_anime::{ActionData, ActionLoader, AnimTime, Fade, Sequences, Vec2}; use rog_dbus::RogDbusClientBlocking; @@ -11,8 +12,8 @@ use serde_derive::{Deserialize, Serialize}; use zbus::dbus_interface; use zbus::zvariant::{ObjectPath, Type}; +use crate::config::ConfigAnime; use crate::error::Error; -use crate::user_config::{ConfigLoadSave, UserAnimeConfig}; #[derive(Debug, Clone, Deserialize, Serialize, Type)] pub struct Timer { @@ -134,7 +135,7 @@ impl<'a> CtrlAnimeInner<'static> { } pub struct CtrlAnime<'a> { - config: Arc>, + config: Arc>, client: RogDbusClientBlocking<'a>, inner: Arc>>, /// Must be the same Atomic as in CtrlAnimeInner @@ -143,7 +144,7 @@ pub struct CtrlAnime<'a> { impl CtrlAnime<'static> { pub fn new( - config: Arc>, + config: Arc>, inner: Arc>>, client: RogDbusClientBlocking<'static>, inner_early_return: Arc, @@ -206,7 +207,7 @@ impl CtrlAnime<'static> { .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; } config.anime.push(action); - config.write()?; + config.write(); let json = serde_json::to_string_pretty(&*config).expect("Parse config to JSON failed"); @@ -251,7 +252,7 @@ impl CtrlAnime<'static> { .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; } config.anime.push(action); - config.write()?; + config.write(); let json = serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed"); @@ -296,7 +297,7 @@ impl CtrlAnime<'static> { .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; } config.anime.push(action); - config.write()?; + config.write(); let json = serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed"); @@ -321,7 +322,7 @@ impl CtrlAnime<'static> { .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; } config.anime.push(action); - config.write()?; + config.write(); let json = serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed"); @@ -344,7 +345,7 @@ impl CtrlAnime<'static> { if (index as usize) < config.anime.len() { config.anime.remove(index as usize); } - config.write()?; + config.write(); let json = serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed"); diff --git a/daemon-user/src/daemon.rs b/daemon-user/src/daemon.rs index c6962d60..a3a56c9e 100644 --- a/daemon-user/src/daemon.rs +++ b/daemon-user/src/daemon.rs @@ -1,15 +1,16 @@ use std::fs::OpenOptions; -use std::io::Read; +use std::io::{Read, Write}; use std::path::PathBuf; use std::sync::atomic::AtomicBool; use std::sync::{Arc, Mutex}; +use config_traits::{StdConfig, StdConfigLoad1}; use rog_anime::usb::get_anime_type; use rog_aura::aura_detection::LaptopLedData; use rog_aura::layouts::KeyLayout; use rog_dbus::RogDbusClientBlocking; +use rog_user::config::*; use rog_user::ctrl_anime::{CtrlAnime, CtrlAnimeInner}; -use rog_user::user_config::*; use rog_user::DBUS_NAME; use smol::Executor; use zbus::Connection; @@ -21,6 +22,13 @@ const DATA_DIR: &str = env!("CARGO_MANIFEST_DIR"); const BOARD_NAME: &str = "/sys/class/dmi/id/board_name"; fn main() -> Result<(), Box> { + let mut logger = env_logger::Builder::new(); + logger + .parse_default_env() + .target(env_logger::Target::Stdout) + .format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args())) + .init(); + println!(" user daemon v{}", rog_user::VERSION); println!(" rog-anime v{}", rog_anime::VERSION); println!(" rog-dbus v{}", rog_dbus::VERSION); @@ -29,8 +37,7 @@ fn main() -> Result<(), Box> { let (client, _) = RogDbusClientBlocking::new()?; let supported = client.proxies().supported().supported_functions()?; - let mut config = UserConfig::new(); - config.load()?; + let config = ConfigBase::new().load(); let executor = Executor::new(); @@ -39,7 +46,7 @@ fn main() -> Result<(), Box> { if supported.anime_ctrl.0 { if let Some(cfg) = config.active_anime { let anime_type = get_anime_type()?; - let anime_config = UserAnimeConfig::load(cfg)?; + let anime_config = ConfigAnime::new().set_name(cfg).load(); let anime = anime_config.create(anime_type)?; let anime_config = Arc::new(Mutex::new(anime_config)); @@ -70,7 +77,7 @@ fn main() -> Result<(), Box> { // if supported.keyboard_led.per_key_led_mode { if let Some(cfg) = config.active_aura { - let mut aura_config = UserAuraConfig::load(cfg)?; + let mut aura_config = ConfigAura::new().set_name(cfg).load(); // Find and load a matching layout for laptop let mut file = OpenOptions::new() diff --git a/daemon-user/src/lib.rs b/daemon-user/src/lib.rs index 1a3d3d24..25a75409 100644 --- a/daemon-user/src/lib.rs +++ b/daemon-user/src/lib.rs @@ -1,4 +1,4 @@ -pub mod user_config; +pub mod config; pub mod error; diff --git a/daemon/src/config.rs b/daemon/src/config.rs index 9861a929..5ce70570 100644 --- a/daemon/src/config.rs +++ b/daemon/src/config.rs @@ -1,7 +1,7 @@ use config_traits::{StdConfig, StdConfigLoad3}; use serde_derive::{Deserialize, Serialize}; -const CONFIG_FILE: &str = "asusd.conf"; +const CONFIG_FILE: &str = "asusd.ron"; #[derive(Deserialize, Serialize, Default)] pub struct Config { diff --git a/daemon/src/ctrl_anime/config.rs b/daemon/src/ctrl_anime/config.rs index c00ecc5f..20dfa5aa 100644 --- a/daemon/src/ctrl_anime/config.rs +++ b/daemon/src/ctrl_anime/config.rs @@ -5,7 +5,7 @@ use rog_anime::error::AnimeError; use rog_anime::{ActionData, ActionLoader, AnimTime, AnimeType, Fade, Vec2}; use serde_derive::{Deserialize, Serialize}; -const CONFIG_FILE: &str = "anime.conf"; +const CONFIG_FILE: &str = "anime.ron"; #[derive(Deserialize, Serialize)] pub struct AnimeConfigV341 { diff --git a/daemon/src/ctrl_aura/config.rs b/daemon/src/ctrl_aura/config.rs index 9333821a..f49fd3c9 100644 --- a/daemon/src/ctrl_aura/config.rs +++ b/daemon/src/ctrl_aura/config.rs @@ -8,7 +8,7 @@ use rog_platform::hid_raw::HidRaw; use rog_platform::keyboard_led::KeyboardLed; use serde_derive::{Deserialize, Serialize}; -const CONFIG_FILE: &str = "aura.conf"; +const CONFIG_FILE: &str = "aura.ron"; /// Enable/disable LED control in various states such as /// when the device is awake, suspended, shutting down or diff --git a/daemon/src/ctrl_profiles/config.rs b/daemon/src/ctrl_profiles/config.rs index 7b360549..f21bae12 100644 --- a/daemon/src/ctrl_profiles/config.rs +++ b/daemon/src/ctrl_profiles/config.rs @@ -7,8 +7,8 @@ use serde_derive::{Deserialize, Serialize}; use crate::CONFIG_PATH_BASE; -const CONFIG_FILE: &str = "profile.conf"; -const CONFIG_FAN_FILE: &str = "fan_curves.conf"; +const CONFIG_FILE: &str = "profile.ron"; +const CONFIG_FAN_FILE: &str = "fan_curves.ron"; #[derive(Deserialize, Serialize, Debug)] pub struct ProfileConfig {