From 0a008a653aa75e22ef6530f6b15ccf34c15e9719 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Sat, 24 Jun 2023 12:55:45 +1200 Subject: [PATCH] Animatrix: Default to GA402 style if Unknown, use default-workspace. Also rename daemon crates to the bin names they use to be less confusing. Signed-off-by: Luke D. Jones --- .gitlab-ci.yml | 4 +- CHANGELOG.md | 3 +- Cargo.lock | 92 ++-- Cargo.toml | 3 +- asusctl/Cargo.toml | 2 +- asusctl/src/main.rs | 2 +- daemon-user/Cargo.toml | 39 -- daemon-user/README.md | 14 - daemon-user/src/config.rs | 219 ---------- daemon-user/src/ctrl_anime.rs | 373 ---------------- daemon-user/src/daemon.rs | 116 ----- daemon-user/src/error.rs | 45 -- daemon-user/src/lib.rs | 11 - daemon-user/src/zbus_anime.rs | 70 --- daemon/Cargo.toml | 50 --- daemon/src/config.rs | 77 ---- daemon/src/ctrl_anime/config.rs | 214 --------- daemon/src/ctrl_anime/mod.rs | 233 ---------- daemon/src/ctrl_anime/trait_impls.rs | 299 ------------- daemon/src/ctrl_aura/config.rs | 357 --------------- daemon/src/ctrl_aura/controller.rs | 558 ------------------------ daemon/src/ctrl_aura/mod.rs | 4 - daemon/src/ctrl_aura/trait_impls.rs | 331 -------------- daemon/src/ctrl_platform.rs | 382 ---------------- daemon/src/ctrl_power.rs | 287 ------------ daemon/src/ctrl_profiles/config.rs | 60 --- daemon/src/ctrl_profiles/controller.rs | 191 -------- daemon/src/ctrl_profiles/mod.rs | 4 - daemon/src/ctrl_profiles/trait_impls.rs | 311 ------------- daemon/src/ctrl_supported.rs | 42 -- daemon/src/daemon.rs | 165 ------- daemon/src/error.rs | 135 ------ daemon/src/lib.rs | 229 ---------- rog-anime/src/data.rs | 9 +- rog-anime/src/diagonal.rs | 8 +- rog-anime/src/image.rs | 53 +-- rog-aura/src/effects/doom.rs | 8 - rog-control-center/Cargo.toml | 2 +- rog-control-center/src/lib.rs | 2 +- rog-platform/src/hid_raw.rs | 1 - simulators/src/simulator.rs | 57 +-- 41 files changed, 112 insertions(+), 4950 deletions(-) delete mode 100644 daemon-user/Cargo.toml delete mode 100644 daemon-user/README.md delete mode 100644 daemon-user/src/config.rs delete mode 100644 daemon-user/src/ctrl_anime.rs delete mode 100644 daemon-user/src/daemon.rs delete mode 100644 daemon-user/src/error.rs delete mode 100644 daemon-user/src/lib.rs delete mode 100644 daemon-user/src/zbus_anime.rs delete mode 100644 daemon/Cargo.toml delete mode 100644 daemon/src/config.rs delete mode 100644 daemon/src/ctrl_anime/config.rs delete mode 100644 daemon/src/ctrl_anime/mod.rs delete mode 100644 daemon/src/ctrl_anime/trait_impls.rs delete mode 100644 daemon/src/ctrl_aura/config.rs delete mode 100644 daemon/src/ctrl_aura/controller.rs delete mode 100644 daemon/src/ctrl_aura/mod.rs delete mode 100644 daemon/src/ctrl_aura/trait_impls.rs delete mode 100644 daemon/src/ctrl_platform.rs delete mode 100644 daemon/src/ctrl_power.rs delete mode 100644 daemon/src/ctrl_profiles/config.rs delete mode 100644 daemon/src/ctrl_profiles/controller.rs delete mode 100644 daemon/src/ctrl_profiles/mod.rs delete mode 100644 daemon/src/ctrl_profiles/trait_impls.rs delete mode 100644 daemon/src/ctrl_supported.rs delete mode 100644 daemon/src/daemon.rs delete mode 100644 daemon/src/error.rs delete mode 100644 daemon/src/lib.rs diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f5679f71..23147584 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -52,7 +52,7 @@ test: <<: *rust_cache script: - mkdir -p .git/hooks > /dev/null - - cargo test + - cargo test --all release: only: @@ -79,6 +79,6 @@ pages: artifacts: paths: - public - + variables: GIT_SUBMODULE_STRATEGY: normal diff --git a/CHANGELOG.md b/CHANGELOG.md index 7eace445..a18476f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Move FX506HC to FX506H in arua DB to catch full series of this range - Move FX506LH to FX506L in arua DB to catch full series of this range -- Rmeove notification handle tracking limit, fixes KDE issue with profile notif +- Remove notification handle tracking limit, fixes KDE issue with profile notif +- Rename daemon and daemon-user crates to asusd and asusd-user to not be confusing in workspace naming ### BREAKING - All Anime related DBUS methods/notifs are changed diff --git a/Cargo.lock b/Cargo.lock index 6dd9ebc2..72bdb7da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -177,8 +177,8 @@ checksum = "8868f09ff8cea88b079da74ae569d9b8c62a23c68c746240b704ee6f7525c89c" name = "asusctl" version = "4.6.2" dependencies = [ + "asusd", "cargo-husky", - "daemon", "gif", "glam", "gumdrop", @@ -192,6 +192,50 @@ dependencies = [ "toml 0.5.11", ] +[[package]] +name = "asusd" +version = "4.6.2" +dependencies = [ + "async-trait", + "cargo-husky", + "concat-idents", + "config-traits", + "env_logger", + "log", + "logind-zbus", + "rog_anime", + "rog_aura", + "rog_dbus", + "rog_platform", + "rog_profiles", + "serde", + "serde_derive", + "sysfs-class", + "systemd-zbus", + "tokio", + "zbus", +] + +[[package]] +name = "asusd-user" +version = "4.6.2" +dependencies = [ + "cargo-husky", + "config-traits", + "dirs", + "env_logger", + "log", + "rog_anime", + "rog_aura", + "rog_dbus", + "rog_platform", + "serde", + "serde_derive", + "serde_json", + "smol", + "zbus", +] + [[package]] name = "async-broadcast" version = "0.5.1" @@ -773,50 +817,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "daemon" -version = "4.6.2" -dependencies = [ - "async-trait", - "cargo-husky", - "concat-idents", - "config-traits", - "env_logger", - "log", - "logind-zbus", - "rog_anime", - "rog_aura", - "rog_dbus", - "rog_platform", - "rog_profiles", - "serde", - "serde_derive", - "sysfs-class", - "systemd-zbus", - "tokio", - "zbus", -] - -[[package]] -name = "daemon-user" -version = "4.6.2" -dependencies = [ - "cargo-husky", - "config-traits", - "dirs", - "env_logger", - "log", - "rog_anime", - "rog_aura", - "rog_dbus", - "rog_platform", - "serde", - "serde_derive", - "serde_json", - "smol", - "zbus", -] - [[package]] name = "derivative" version = "2.2.0" @@ -2560,8 +2560,8 @@ checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" name = "rog-control-center" version = "4.6.2" dependencies = [ + "asusd", "cargo-husky", - "daemon", "dirs", "eframe", "egui", diff --git a/Cargo.toml b/Cargo.toml index 8fd155f0..6c38f137 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] -members = ["asusctl", "config-traits", "daemon", "daemon-user", "rog-platform", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "rog-control-center", "simulators"] +members = ["asusctl", "asusd", "asusd-user", "config-traits", "rog-platform", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "rog-control-center", "simulators"] +default-members = ["asusctl", "asusd", "asusd-user", "rog-control-center"] [workspace.package] version = "4.6.2" diff --git a/asusctl/Cargo.toml b/asusctl/Cargo.toml index 1f554ed7..ad74ea53 100644 --- a/asusctl/Cargo.toml +++ b/asusctl/Cargo.toml @@ -11,7 +11,7 @@ rog_aura = { path = "../rog-aura" } rog_dbus = { path = "../rog-dbus" } rog_profiles = { path = "../rog-profiles" } rog_platform = { path = "../rog-platform" } -daemon = { path = "../daemon" } +asusd = { path = "../asusd" } gumdrop.workspace = true toml.workspace = true diff --git a/asusctl/src/main.rs b/asusctl/src/main.rs index 417db51e..f56ffe06 100644 --- a/asusctl/src/main.rs +++ b/asusctl/src/main.rs @@ -83,7 +83,7 @@ fn print_error_help(err: &dyn std::error::Error, supported: Option<&SupportedFun fn print_versions() { println!("App and daemon versions:"); println!(" asusctl v{}", env!("CARGO_PKG_VERSION")); - println!(" asusd v{}", daemon::VERSION); + println!(" asusd v{}", asusd::VERSION); println!("\nComponent crate versions:"); println!(" rog-anime v{}", rog_anime::VERSION); println!(" rog-aura v{}", rog_aura::VERSION); diff --git a/daemon-user/Cargo.toml b/daemon-user/Cargo.toml deleted file mode 100644 index 0a51f551..00000000 --- a/daemon-user/Cargo.toml +++ /dev/null @@ -1,39 +0,0 @@ -[package] -name = "daemon-user" -license = "MPL-2.0" -version.workspace = true -authors = ["Luke D Jones "] -edition = "2021" -description = "Usermode daemon for user settings, anime, per-key lighting" - -[lib] -name = "rog_user" -path = "src/lib.rs" - -[[bin]] -name = "asusd-user" -path = "src/daemon.rs" - -[dependencies] -dirs.workspace = true -smol.workspace = true - -# serialisation -serde.workspace = true -serde_json.workspace = true -serde_derive.workspace = true - -rog_anime = { path = "../rog-anime" } -rog_aura = { path = "../rog-aura" } -rog_dbus = { path = "../rog-dbus" } -rog_platform = { path = "../rog-platform" } -config-traits = { path = "../config-traits" } - -zbus.workspace = true - -# cli and logging -log.workspace = true -env_logger.workspace = true - -[dev-dependencies] -cargo-husky.workspace = true \ No newline at end of file diff --git a/daemon-user/README.md b/daemon-user/README.md deleted file mode 100644 index be3b4e23..00000000 --- a/daemon-user/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# daemon-user - -This crate is for the binary of `asusd-user` and its helper lib. - -The purpose of `asusd-user` is to run in userland and provide the user + third-party apps an interface for such things as creating AniMe sequences (and more in future, see todo list). - -`asusd-user` should try to be as simple as possible while allowing a decent degree of control. - -## TODO - -- [ ] CLI for basic settings/interaction -- [ ] RGB keyboard per-key programs -- [ ] User profiles (fan, cpu etc). These would be replacing the system-daemon profiles only when the user is active, otherwise system-daemon defaults to system settings. -- [ ] Audio EQ visualiser - for use with anime + keyboard lighting \ No newline at end of file diff --git a/daemon-user/src/config.rs b/daemon-user/src/config.rs deleted file mode 100644 index 785bfbf1..00000000 --- a/daemon-user/src/config.rs +++ /dev/null @@ -1,219 +0,0 @@ -use std::path::PathBuf; -use std::time::Duration; - -use config_traits::{StdConfig, StdConfigLoad}; -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_derive::{Deserialize, Serialize}; - -use crate::error::Error; - -const ROOT_CONF_DIR: &str = "rog"; - -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 ConfigAnime { - pub name: String, - pub anime: Vec, -} - -impl ConfigAnime { - pub fn create(&self, anime_type: AnimeType) -> Result { - let mut seq = AnimeSequences::new(anime_type); - - for (idx, action) in self.anime.iter().enumerate() { - seq.insert(idx, action)?; - } - - Ok(seq) - } - - pub fn set_name(mut self, name: String) -> Self { - self.name = name; - self - } -} - -impl Default for ConfigAnime { - fn default() -> Self { - Self { - name: "anime-default".to_owned(), - anime: vec![ - ActionLoader::AsusImage { - file: "/usr/share/asusd/anime/custom/diagonal-template.png".into(), - brightness: 1.0, - time: AnimTime::Fade(Fade::new( - Duration::from_secs(2), - None, - Duration::from_secs(2), - )), - }, - ActionLoader::AsusAnimation { - file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(), - brightness: 0.5, - time: AnimTime::Fade(Fade::new( - Duration::from_secs(6), - None, - Duration::from_secs(3), - )), - }, - ActionLoader::ImageAnimation { - file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(), - scale: 0.9, - angle: 0.65, - translation: Vec2::default(), - brightness: 0.5, - time: AnimTime::Fade(Fade::new( - Duration::from_secs(2), - Some(Duration::from_secs(2)), - Duration::from_secs(2), - )), - }, - ActionLoader::Image { - file: "/usr/share/asusd/anime/custom/rust.png".into(), - scale: 1.0, - angle: 0.0, - translation: Vec2::default(), - time: AnimTime::Fade(Fade::new( - Duration::from_secs(2), - Some(Duration::from_secs(1)), - Duration::from_secs(2), - )), - brightness: 0.6, - }, - ActionLoader::Pause(Duration::from_secs(1)), - ActionLoader::ImageAnimation { - file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(), - scale: 0.9, - angle: 0.0, - translation: Vec2::new(3.0, 2.0), - brightness: 0.5, - time: AnimTime::Count(2), - }, - ], - } - } -} - -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 StdConfigLoad for ConfigAnime {} - -#[derive(Debug, Deserialize, Serialize)] -pub struct ConfigAura { - pub name: String, - pub aura: AuraSequences, -} - -impl ConfigAura { - pub fn set_name(mut self, name: String) -> Self { - self.name = name; - self - } -} - -impl Default for ConfigAura { - fn default() -> Self { - let mut seq = AuraSequences::new(false); - let mut key = Effect::Breathe(Breathe::new( - LedCode::W, - Colour(255, 0, 20), - Colour(20, 255, 0), - Speed::Low, - )); - - seq.push(key.clone()); - key.set_led(LedCode::A); - seq.push(key.clone()); - key.set_led(LedCode::S); - seq.push(key.clone()); - key.set_led(LedCode::D); - seq.push(key); - - let key = Effect::Breathe(Breathe::new( - LedCode::F, - Colour(255, 0, 0), - Colour(255, 0, 0), - Speed::High, - )); - seq.push(key); - - let mut key = Effect::Static(Static::new(LedCode::RCtrl, Colour(0, 0, 255))); - seq.push(key.clone()); - key.set_led(LedCode::LCtrl); - seq.push(key.clone()); - key.set_led(LedCode::Esc); - seq.push(key); - - let key = Effect::DoomFlicker(DoomFlicker::new(LedCode::N9, Colour(0, 0, 255), 80, 40)); - seq.push(key); - - Self { - 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 StdConfigLoad for ConfigAura {} - -#[derive(Debug, Default, Deserialize, Serialize)] -#[serde(default)] -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 StdConfig for ConfigBase { - fn new() -> Self { - Self { - active_anime: Some("anime-default".to_owned()), - active_aura: Some("aura-default".to_owned()), - } - } - - fn file_name(&self) -> String { - "rog-user.ron".to_owned() - } - - fn config_dir() -> std::path::PathBuf { - root_conf_dir() - } -} - -impl StdConfigLoad for ConfigBase {} diff --git a/daemon-user/src/ctrl_anime.rs b/daemon-user/src/ctrl_anime.rs deleted file mode 100644 index a87898d2..00000000 --- a/daemon-user/src/ctrl_anime.rs +++ /dev/null @@ -1,373 +0,0 @@ -use std::path::Path; -use std::sync::atomic::{AtomicBool, Ordering}; -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; -use serde_derive::{Deserialize, Serialize}; -use zbus::dbus_interface; -use zbus::zvariant::{ObjectPath, Type}; - -use crate::config::ConfigAnime; -use crate::error::Error; - -#[derive(Debug, Clone, Deserialize, Serialize, Type)] -pub struct Timer { - type_of: TimeType, - /// If time type is Timer then this is milliseonds, otherwise it is - /// animation loop count - count: u64, - /// Used only for `TimeType::Timer`, milliseonds to fade the image in for - fade_in: Option, - /// Used only for `TimeType::Timer`, milliseonds to fade the image out for - fade_out: Option, -} - -impl From for AnimTime { - fn from(time: Timer) -> Self { - match time.type_of { - TimeType::Timer => { - if time.fade_in.is_some() || time.fade_out.is_some() { - let fade_in = time - .fade_in - .map_or(Duration::from_secs(0), Duration::from_millis); - let fade_out = time - .fade_out - .map_or(Duration::from_secs(0), Duration::from_millis); - let show_for = if time.count != 0 { - Some(Duration::from_millis(time.count)) - } else { - None - }; - AnimTime::Fade(Fade::new(fade_in, show_for, fade_out)) - } else { - AnimTime::Time(Duration::from_millis(time.count)) - } - } - TimeType::Count => AnimTime::Count(time.count as u32), - TimeType::Infinite => AnimTime::Infinite, - } - } -} - -#[derive(Debug, Clone, Deserialize, Serialize, Type)] -pub enum TimeType { - Timer, - Count, - Infinite, -} - -/// The inner object exists to allow the zbus proxy to share it with a runner -/// thread and a zbus server behind `Arc>` -pub struct CtrlAnimeInner<'a> { - sequences: Sequences, - client: RogDbusClientBlocking<'a>, - do_early_return: Arc, -} - -impl<'a> CtrlAnimeInner<'static> { - pub fn new( - sequences: Sequences, - client: RogDbusClientBlocking<'static>, - do_early_return: Arc, - ) -> Result { - Ok(Self { - sequences, - client, - do_early_return, - }) - } - - /// To be called on each main loop iteration to pump out commands to the - /// anime - pub fn run(&'a self) -> Result<(), Error> { - if self.do_early_return.load(Ordering::SeqCst) { - return Ok(()); - } - - for action in self.sequences.iter() { - match action { - ActionData::Animation(frames) => { - rog_anime::run_animation(frames, &|output| { - if self.do_early_return.load(Ordering::Acquire) { - return Ok(true); // Do safe exit - } - self.client - .proxies() - .anime() - .write(output) - .map_err(|e| AnimeError::Dbus(format!("{}", e))) - .map(|_| false) - }); - } - ActionData::Image(image) => { - self.client - .proxies() - .anime() - .write(image.as_ref().clone()) - .ok(); - } - ActionData::Pause(duration) => { - let start = Instant::now(); - 'pause: loop { - if self.do_early_return.load(Ordering::SeqCst) { - return Ok(()); - } - if Instant::now().duration_since(start) > *duration { - break 'pause; - } - sleep(Duration::from_millis(1)); - } - } - ActionData::AudioEq - | ActionData::SystemInfo - | ActionData::TimeDate - | ActionData::Matrix => {} - } - } - - Ok(()) - } -} - -pub struct CtrlAnime<'a> { - config: Arc>, - client: RogDbusClientBlocking<'a>, - inner: Arc>>, - /// Must be the same Atomic as in CtrlAnimeInner - inner_early_return: Arc, -} - -impl CtrlAnime<'static> { - pub fn new( - config: Arc>, - inner: Arc>>, - client: RogDbusClientBlocking<'static>, - inner_early_return: Arc, - ) -> Result { - Ok(CtrlAnime { - config, - client, - inner, - inner_early_return, - }) - } - - pub async fn add_to_server(self, server: &mut zbus::Connection) { - server - .object_server() - .at( - &ObjectPath::from_str_unchecked("/org/asuslinux/Anime"), - self, - ) - .await - .map_err(|err| { - println!("CtrlAnime: add_to_server {}", err); - err - }) - .ok(); - } -} - -// The pattern for a zbus method is: -// - Get config lock if required -// - Set inner_early_return to stop the inner run loop temporarily -// - Do actions -// - Write config if required -// - Unset inner_early_return -#[dbus_interface(name = "org.asuslinux.Daemon")] -impl CtrlAnime<'static> { - pub fn insert_asus_gif( - &mut self, - index: u32, - file: &str, - time: Timer, - brightness: f32, - ) -> zbus::fdo::Result { - if let Ok(mut config) = self.config.try_lock() { - let time: AnimTime = time.into(); - let file = Path::new(&file); - let action = ActionLoader::AsusAnimation { - file: file.into(), - brightness, - time, - }; - - // Must make the inner run loop return early - self.inner_early_return.store(true, Ordering::SeqCst); - - if let Ok(mut controller) = self.inner.lock() { - controller - .sequences - .insert(index as usize, &action) - .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; - } - config.anime.push(action); - config.write(); - - let json = serde_json::to_string_pretty(&*config).expect("Parse config to JSON failed"); - - // Release the inner run loop again - self.inner_early_return.store(false, Ordering::SeqCst); - return Ok(json); - } - Err(zbus::fdo::Error::Failed("UserConfig lock fail".into())) - } - - #[allow(clippy::too_many_arguments)] - pub fn insert_image_gif( - &mut self, - index: u32, - file: &str, - scale: f32, - angle: f32, - xy: (f32, f32), - time: Timer, - brightness: f32, - ) -> zbus::fdo::Result { - if let Ok(mut config) = self.config.try_lock() { - let time: AnimTime = time.into(); - let file = Path::new(&file); - let translation = Vec2::new(xy.0, xy.1); - let action = ActionLoader::ImageAnimation { - file: file.into(), - scale, - angle, - translation, - brightness, - time, - }; - - // Must make the inner run loop return early - self.inner_early_return.store(true, Ordering::SeqCst); - - if let Ok(mut controller) = self.inner.lock() { - controller - .sequences - .insert(index as usize, &action) - .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; - } - config.anime.push(action); - config.write(); - - let json = - serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed"); - - // Release the inner run loop again - self.inner_early_return.store(false, Ordering::SeqCst); - return Ok(json); - } - Err(zbus::fdo::Error::Failed("UserConfig lock fail".into())) - } - - #[allow(clippy::too_many_arguments)] - pub fn insert_image( - &mut self, - index: u32, - file: &str, - scale: f32, - angle: f32, - xy: (f32, f32), - time: Timer, - brightness: f32, - ) -> zbus::fdo::Result { - if let Ok(mut config) = self.config.try_lock() { - let file = Path::new(&file); - let time = time.into(); - let action = ActionLoader::Image { - file: file.into(), - scale, - angle, - translation: Vec2::new(xy.0, xy.1), - brightness, - time, - }; - - // Must make the inner run loop return early - self.inner_early_return.store(true, Ordering::SeqCst); - - if let Ok(mut controller) = self.inner.lock() { - controller - .sequences - .insert(index as usize, &action) - .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; - } - config.anime.push(action); - config.write(); - - let json = - serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed"); - - // Release the inner run loop again - self.inner_early_return.store(false, Ordering::SeqCst); - return Ok(json); - } - Err(zbus::fdo::Error::Failed("UserConfig lock fail".into())) - } - - pub fn insert_pause(&mut self, index: u32, millis: u64) -> zbus::fdo::Result { - if let Ok(mut config) = self.config.try_lock() { - let action = ActionLoader::Pause(Duration::from_millis(millis)); - // Must make the inner run loop return early - self.inner_early_return.store(true, Ordering::SeqCst); - - if let Ok(mut controller) = self.inner.lock() { - controller - .sequences - .insert(index as usize, &action) - .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; - } - config.anime.push(action); - config.write(); - - let json = - serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed"); - - // Release the inner run loop again - self.inner_early_return.store(false, Ordering::SeqCst); - return Ok(json); - } - Err(zbus::fdo::Error::Failed("UserConfig lock fail".into())) - } - - pub fn remove_item(&mut self, index: u32) -> zbus::fdo::Result { - if let Ok(mut config) = self.config.try_lock() { - // Must make the inner run loop return early - self.inner_early_return.store(true, Ordering::SeqCst); - - if let Ok(mut controller) = self.inner.lock() { - controller.sequences.remove_item(index as usize); - } - if (index as usize) < config.anime.len() { - config.anime.remove(index as usize); - } - config.write(); - - let json = - serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed"); - - // Release the inner run loop again - self.inner_early_return.store(false, Ordering::SeqCst); - return Ok(json); - } - Err(zbus::fdo::Error::Failed("UserConfig lock fail".into())) - } - - pub fn set_state(&mut self, on: bool) -> zbus::fdo::Result<()> { - // Operations here need to be in specific order - if on { - self.client.proxies().anime().set_enable_display(on).ok(); - // Let the inner loop run - self.inner_early_return.store(false, Ordering::SeqCst); - } else { - // Must make the inner run loop return early - self.inner_early_return.store(true, Ordering::SeqCst); - self.client.proxies().anime().set_enable_display(on).ok(); - } - Ok(()) - } -} diff --git a/daemon-user/src/daemon.rs b/daemon-user/src/daemon.rs deleted file mode 100644 index ca7fed28..00000000 --- a/daemon-user/src/daemon.rs +++ /dev/null @@ -1,116 +0,0 @@ -use std::io::Write; -use std::path::PathBuf; -use std::sync::atomic::AtomicBool; -use std::sync::{Arc, Mutex}; - -use config_traits::{StdConfig, StdConfigLoad}; -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::DBUS_NAME; -use smol::Executor; -use zbus::Connection; - -#[cfg(not(feature = "local_data"))] -const DATA_DIR: &str = "/usr/share/rog-gui/"; -#[cfg(feature = "local_data")] -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); - println!("rog-platform v{}", rog_platform::VERSION); - - let (client, _) = RogDbusClientBlocking::new()?; - let supported = client.proxies().supported().supported_functions()?; - - let config = ConfigBase::new().load(); - - let executor = Executor::new(); - - let early_return = Arc::new(AtomicBool::new(false)); - // Set up the anime data and run loop/thread - if supported.anime_ctrl.0 { - if let Some(cfg) = config.active_anime { - let anime_type = get_anime_type()?; - 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)); - - executor - .spawn(async move { - // Create server - let mut connection = Connection::session().await.unwrap(); - connection.request_name(DBUS_NAME).await.unwrap(); - - // Inner behind mutex required for thread safety - let inner = Arc::new(Mutex::new( - CtrlAnimeInner::new(anime, client, early_return.clone()).unwrap(), - )); - // Need new client object for dbus control part - let (client, _) = RogDbusClientBlocking::new().unwrap(); - let anime_control = - CtrlAnime::new(anime_config, inner.clone(), client, early_return).unwrap(); - anime_control.add_to_server(&mut connection).await; - loop { - if let Ok(inner) = inner.clone().try_lock() { - inner.run().ok(); - } - } - }) - .detach(); - } - } - - // if supported.keyboard_led.per_key_led_mode { - if let Some(cfg) = config.active_aura { - let mut aura_config = ConfigAura::new().set_name(cfg).load(); - // let baord_name = std::fs::read_to_string(BOARD_NAME)?; - - let led_support = LaptopLedData::get_data(); - - let layout = KeyLayout::find_layout(led_support, PathBuf::from(DATA_DIR)) - .map_err(|e| { - println!("{BOARD_NAME}, {e}"); - }) - .unwrap_or_else(|_| KeyLayout::default_layout()); - - executor - .spawn(async move { - // Create server - let (client, _) = RogDbusClientBlocking::new().unwrap(); - // let connection = Connection::session().await.unwrap(); - // connection.request_name(DBUS_NAME).await.unwrap(); - - loop { - aura_config.aura.next_state(&layout); - let packets = aura_config.aura.create_packets(); - - client - .proxies() - .led() - .direct_addressing_raw(packets) - .unwrap(); - std::thread::sleep(std::time::Duration::from_millis(33)); - } - }) - .detach(); - } - // } - - loop { - smol::block_on(executor.tick()); - } -} diff --git a/daemon-user/src/error.rs b/daemon-user/src/error.rs deleted file mode 100644 index e6c0e33e..00000000 --- a/daemon-user/src/error.rs +++ /dev/null @@ -1,45 +0,0 @@ -use std::fmt; - -use rog_anime::error::AnimeError; - -#[derive(Debug)] -pub enum Error { - Io(std::io::Error), - ConfigLoadFail, - ConfigLockFail, - XdgVars, - Anime(AnimeError), -} - -impl fmt::Display for Error { - // This trait requires `fmt` with this exact signature. - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Error::Io(err) => write!(f, "Failed to open: {}", err), - Error::ConfigLoadFail => write!(f, "Failed to load user config"), - Error::ConfigLockFail => write!(f, "Failed to lock user config"), - Error::XdgVars => write!(f, "XDG environment vars appear unset"), - Error::Anime(err) => write!(f, "Anime error: {}", err), - } - } -} - -impl std::error::Error for Error {} - -impl From for Error { - fn from(err: std::io::Error) -> Self { - Error::Io(err) - } -} - -impl From for Error { - fn from(err: AnimeError) -> Self { - Error::Anime(err) - } -} - -impl From for zbus::fdo::Error { - fn from(err: Error) -> Self { - zbus::fdo::Error::Failed(format!("Anime zbus error: {}", err)) - } -} diff --git a/daemon-user/src/lib.rs b/daemon-user/src/lib.rs deleted file mode 100644 index 25a75409..00000000 --- a/daemon-user/src/lib.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub mod config; - -pub mod error; - -pub mod ctrl_anime; - -pub mod zbus_anime; - -pub static DBUS_NAME: &str = "org.asuslinux.Daemon"; - -pub static VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/daemon-user/src/zbus_anime.rs b/daemon-user/src/zbus_anime.rs deleted file mode 100644 index 793f373b..00000000 --- a/daemon-user/src/zbus_anime.rs +++ /dev/null @@ -1,70 +0,0 @@ -//! # `DBus` interface proxy for: `org.asuslinux.Daemon` -//! -//! This code was generated by `zbus-xmlgen` `1.0.0` from `DBus` introspection -//! data. Source: `Interface '/org/asuslinux/Anime' from service -//! 'org.asuslinux.Daemon' on session bus`. -//! -//! You may prefer to adapt it, instead of using it verbatim. -//! -//! More information can be found in the -//! [Writing a client proxy](https://dbus.pages.freedesktop.org/zbus/client.html) -//! section of the zbus documentation. -//! -//! This `DBus` object implements -//! [standard `DBus` interfaces](https://dbus.freedesktop.org/doc/dbus-specification.html), -//! (`org.freedesktop.DBus.*`) for which the following zbus proxies can be used: -//! -//! * [`zbus::fdo::PeerProxy`] -//! * [`zbus::fdo::IntrospectableProxy`] -//! * [`zbus::fdo::PropertiesProxy`] -//! -//! …consequently `zbus-xmlgen` did not generate code for the above interfaces. -#![allow(clippy::too_many_arguments)] - -use zbus::dbus_proxy; - -#[dbus_proxy(interface = "org.asuslinux.Daemon")] -trait Daemon { - /// InsertAsusGif method - fn insert_asus_gif( - &self, - index: u32, - file: &str, - time: u32, - count: u32, - brightness: f64, - ) -> zbus::Result; - - /// InsertImage method - fn insert_image( - &self, - index: u32, - file: &str, - scale: f64, - angle: f64, - xy: &(f64, f64), - brightness: f64, - ) -> zbus::Result; - - /// InsertImageGif method - fn insert_image_gif( - &self, - index: u32, - file: &str, - scale: f64, - angle: f64, - xy: &(f64, f64), - time: u32, - count: u32, - brightness: f64, - ) -> zbus::Result; - - /// InsertPause method - fn insert_pause(&self, index: u32, millis: u64) -> zbus::Result; - - /// RemoveItem method - fn remove_item(&self, index: u32) -> zbus::Result; - - /// SetState method - fn set_state(&self, on: bool) -> zbus::Result<()>; -} diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml deleted file mode 100644 index 1f64c790..00000000 --- a/daemon/Cargo.toml +++ /dev/null @@ -1,50 +0,0 @@ -[package] -name = "daemon" -license = "MPL-2.0" -version.workspace = true -readme = "README.md" -authors = ["Luke "] -repository = "https://gitlab.com/asus-linux/asus-nb-ctrl" -homepage = "https://gitlab.com/asus-linux/asus-nb-ctrl" -description = "A daemon app for ASUS GX502 and similar laptops to control missing features" -edition = "2021" - -[lib] -name = "daemon" -path = "src/lib.rs" - -[[bin]] -name = "asusd" -path = "src/daemon.rs" - -[dependencies] -config-traits = { path = "../config-traits" } -rog_anime = { path = "../rog-anime", features = ["dbus"] } -rog_aura = { path = "../rog-aura", features = ["dbus"] } -rog_platform = { path = "../rog-platform" } -rog_profiles = { path = "../rog-profiles" } -rog_dbus = { path = "../rog-dbus" } - -async-trait.workspace = true -tokio.workspace = true - -# cli and logging -log.workspace = true -env_logger.workspace = true - -zbus.workspace = true -logind-zbus.workspace = true - -# serialisation -serde.workspace = true -serde_derive.workspace = true - -# Device control -sysfs-class.workspace = true # used for backlight control and baord ID - -concat-idents.workspace = true - -systemd-zbus = "*" - -[dev-dependencies] -cargo-husky.workspace = true \ No newline at end of file diff --git a/daemon/src/config.rs b/daemon/src/config.rs deleted file mode 100644 index a1669651..00000000 --- a/daemon/src/config.rs +++ /dev/null @@ -1,77 +0,0 @@ -use config_traits::{StdConfig, StdConfigLoad2}; -use serde_derive::{Deserialize, Serialize}; - -const CONFIG_FILE: &str = "asusd.ron"; - -#[derive(Deserialize, Serialize, Default, Debug)] -pub struct Config { - /// Save charge limit for restoring on boot - pub bat_charge_limit: u8, - pub panel_od: bool, - pub disable_nvidia_powerd_on_battery: bool, - pub ac_command: String, - pub bat_command: String, -} - -impl StdConfig for Config { - fn new() -> Self { - Config { - bat_charge_limit: 100, - panel_od: false, - disable_nvidia_powerd_on_battery: true, - ac_command: String::new(), - bat_command: String::new(), - } - } - - fn config_dir() -> std::path::PathBuf { - std::path::PathBuf::from(crate::CONFIG_PATH_BASE) - } - - fn file_name(&self) -> String { - CONFIG_FILE.to_owned() - } -} - -impl StdConfigLoad2 for Config {} - -#[derive(Deserialize, Serialize, Default)] -#[serde(default)] -pub struct Config455 { - /// Save charge limit for restoring on boot - pub bat_charge_limit: u8, - pub panel_od: bool, -} - -impl From for Config { - fn from(c: Config455) -> Self { - Self { - bat_charge_limit: c.bat_charge_limit, - panel_od: c.panel_od, - disable_nvidia_powerd_on_battery: true, - ac_command: String::new(), - bat_command: String::new(), - } - } -} - -#[derive(Deserialize, Serialize, Default)] -pub struct Config458 { - /// Save charge limit for restoring on boot - pub bat_charge_limit: u8, - pub panel_od: bool, - pub ac_command: String, - pub bat_command: String, -} - -impl From for Config { - fn from(c: Config458) -> Self { - Self { - bat_charge_limit: c.bat_charge_limit, - panel_od: c.panel_od, - disable_nvidia_powerd_on_battery: true, - ac_command: c.ac_command, - bat_command: c.bat_command, - } - } -} diff --git a/daemon/src/ctrl_anime/config.rs b/daemon/src/ctrl_anime/config.rs deleted file mode 100644 index 72c77783..00000000 --- a/daemon/src/ctrl_anime/config.rs +++ /dev/null @@ -1,214 +0,0 @@ -use std::time::Duration; - -use config_traits::{StdConfig, StdConfigLoad2}; -use rog_anime::error::AnimeError; -use rog_anime::usb::Brightness; -use rog_anime::{ActionData, ActionLoader, AnimTime, Animations, AnimeType, Fade, Vec2}; -use serde_derive::{Deserialize, Serialize}; - -const CONFIG_FILE: &str = "anime.ron"; - -#[derive(Deserialize, Serialize)] -pub struct AnimeConfigV460 { - pub system: Vec, - pub boot: Vec, - pub wake: Vec, - pub sleep: Vec, - pub shutdown: Vec, - pub brightness: f32, -} - -impl From for AnimeConfig { - fn from(c: AnimeConfigV460) -> AnimeConfig { - AnimeConfig { - system: c.system, - boot: c.boot, - wake: c.wake, - sleep: c.sleep, - shutdown: c.shutdown, - ..Default::default() - } - } -} - -#[derive(Deserialize, Serialize, Debug)] -pub struct AnimeConfigV5 { - pub system: Vec, - pub boot: Vec, - pub wake: Vec, - pub sleep: Vec, - pub shutdown: Vec, - pub brightness: f32, - pub awake_enabled: bool, - pub boot_anim_enabled: bool, -} - -impl From for AnimeConfig { - fn from(c: AnimeConfigV5) -> AnimeConfig { - AnimeConfig { - system: c.system, - boot: c.boot, - wake: c.wake, - sleep: c.sleep, - shutdown: c.shutdown, - ..Default::default() - } - } -} - -#[derive(Deserialize, Serialize, Default)] -pub struct AnimeConfigCached { - pub system: Vec, - pub boot: Vec, - pub wake: Vec, - pub sleep: Vec, - pub shutdown: Vec, -} - -impl AnimeConfigCached { - pub fn init_from_config( - &mut self, - config: &AnimeConfig, - anime_type: AnimeType, - ) -> Result<(), AnimeError> { - let mut sys = Vec::with_capacity(config.system.len()); - for ani in &config.system { - sys.push(ActionData::from_anime_action(anime_type, ani)?); - } - self.system = sys; - - let mut boot = Vec::with_capacity(config.boot.len()); - for ani in &config.boot { - boot.push(ActionData::from_anime_action(anime_type, ani)?); - } - self.boot = boot; - - let mut wake = Vec::with_capacity(config.wake.len()); - for ani in &config.wake { - wake.push(ActionData::from_anime_action(anime_type, ani)?); - } - self.wake = wake; - - let mut sleep = Vec::with_capacity(config.sleep.len()); - for ani in &config.sleep { - sleep.push(ActionData::from_anime_action(anime_type, ani)?); - } - self.sleep = sleep; - - let mut shutdown = Vec::with_capacity(config.shutdown.len()); - for ani in &config.shutdown { - shutdown.push(ActionData::from_anime_action(anime_type, ani)?); - } - self.shutdown = shutdown; - Ok(()) - } -} - -/// Config for base system actions for the anime display -#[derive(Deserialize, Serialize, Debug)] -pub struct AnimeConfig { - pub system: Vec, - pub boot: Vec, - pub wake: Vec, - pub sleep: Vec, - pub shutdown: Vec, - pub brightness: f32, - pub display_enabled: bool, - pub display_brightness: Brightness, - pub builtin_anims_enabled: bool, - pub builtin_anims: Animations, -} - -impl Default for AnimeConfig { - fn default() -> Self { - AnimeConfig { - system: Vec::new(), - boot: Vec::new(), - wake: Vec::new(), - sleep: Vec::new(), - shutdown: Vec::new(), - brightness: 1.0, - display_enabled: true, - display_brightness: Brightness::Med, - builtin_anims_enabled: true, - builtin_anims: Animations::default(), - } - } -} - -impl StdConfig for AnimeConfig { - fn new() -> Self { - Self::create_default() - } - - fn config_dir() -> std::path::PathBuf { - std::path::PathBuf::from(crate::CONFIG_PATH_BASE) - } - - fn file_name(&self) -> String { - CONFIG_FILE.to_owned() - } -} - -impl StdConfigLoad2 for AnimeConfig {} - -impl AnimeConfig { - // fn clamp_config_brightness(mut config: &mut AnimeConfig) { - // if config.brightness < 0.0 || config.brightness > 1.0 { - // warn!( - // "Clamped brightness to [0.0 ; 1.0], was {}", - // config.brightness - // ); - // config.brightness = f32::max(0.0, f32::min(1.0, config.brightness)); - // } - // } - - fn create_default() -> Self { - // create a default config here - AnimeConfig { - system: vec![], - boot: vec![ActionLoader::ImageAnimation { - file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(), - scale: 0.9, - angle: 0.65, - translation: Vec2::default(), - brightness: 1.0, - time: AnimTime::Fade(Fade::new( - Duration::from_secs(2), - Some(Duration::from_secs(2)), - Duration::from_secs(2), - )), - }], - wake: vec![ActionLoader::ImageAnimation { - file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(), - scale: 0.9, - angle: 0.65, - translation: Vec2::default(), - brightness: 1.0, - time: AnimTime::Fade(Fade::new( - Duration::from_secs(2), - Some(Duration::from_secs(2)), - Duration::from_secs(2), - )), - }], - sleep: vec![ActionLoader::ImageAnimation { - file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(), - scale: 0.9, - angle: 0.0, - translation: Vec2::new(3.0, 2.0), - brightness: 1.0, - time: AnimTime::Infinite, - }], - shutdown: vec![ActionLoader::ImageAnimation { - file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(), - scale: 0.9, - angle: 0.0, - translation: Vec2::new(3.0, 2.0), - brightness: 1.0, - time: AnimTime::Infinite, - }], - brightness: 1.0, - ..Default::default() - } - } -} diff --git a/daemon/src/ctrl_anime/mod.rs b/daemon/src/ctrl_anime/mod.rs deleted file mode 100644 index 0be653b2..00000000 --- a/daemon/src/ctrl_anime/mod.rs +++ /dev/null @@ -1,233 +0,0 @@ -pub mod config; -/// Implements `CtrlTask`, Reloadable, `ZbusRun` -pub mod trait_impls; - -use std::convert::TryFrom; -use std::error::Error; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; -use std::thread::sleep; - -use ::zbus::export::futures_util::lock::Mutex; -use log::{error, info, warn}; -use rog_anime::error::AnimeError; -use rog_anime::usb::{get_anime_type, pkt_flush, pkt_set_enable_powersave_anim, pkts_for_init}; -use rog_anime::{ActionData, AnimeDataBuffer, AnimePacketType, AnimeType}; -use rog_platform::hid_raw::HidRaw; -use rog_platform::supported::AnimeSupportedFunctions; -use rog_platform::usb_raw::USBRaw; - -use self::config::{AnimeConfig, AnimeConfigCached}; -use crate::error::RogError; -use crate::GetSupported; - -impl GetSupported for CtrlAnime { - type A = AnimeSupportedFunctions; - - fn get_supported() -> Self::A { - AnimeSupportedFunctions(HidRaw::new("193b").is_ok()) - } -} - -pub struct CtrlAnime { - // node: HidRaw, - node: USBRaw, - anime_type: AnimeType, - cache: AnimeConfigCached, - config: AnimeConfig, - // set to force thread to exit - thread_exit: Arc, - // Set to false when the thread exits - thread_running: Arc, -} - -impl CtrlAnime { - #[inline] - pub fn new(config: AnimeConfig) -> Result> { - // let node = HidRaw::new("193b")?; - let node = USBRaw::new(0x193b)?; - let anime_type = get_anime_type().unwrap_or(AnimeType::GA401); - - info!("Device has an AniMe Matrix display: {anime_type:?}"); - let mut cache = AnimeConfigCached::default(); - cache.init_from_config(&config, anime_type)?; - - let ctrl = CtrlAnime { - node, - anime_type, - cache, - config, - thread_exit: Arc::new(AtomicBool::new(false)), - thread_running: Arc::new(AtomicBool::new(false)), - }; - ctrl.do_initialization()?; - - Ok(ctrl) - } - - // let device = CtrlAnime::get_device(0x0b05, 0x193b)?; - - /// Start an action thread. This is classed as a singleton and there should - /// be only one running - so the thread uses atomics to signal run/exit. - /// - /// Because this also writes to the usb device, other write tries (display - /// only) *must* get the mutex lock and set the `thread_exit` atomic. - async fn run_thread(inner: Arc>, actions: Vec, mut once: bool) { - if actions.is_empty() { - warn!("AniMe system actions was empty"); - return; - } - - if let Some(lock) = inner.try_lock() { - lock.node - .write_bytes(&pkt_set_enable_powersave_anim(false)) - .map_err(|err| { - warn!("rog_anime::run_animation:callback {}", err); - }) - .ok(); - } - - // Loop rules: - // - Lock the mutex **only when required**. That is, the lock must be held for - // the shortest duration possible. - // - An AtomicBool used for thread exit should be checked in every loop, - // including nested - - // The only reason for this outer thread is to prevent blocking while waiting - // for the next spawned thread to exit - // TODO: turn this in to async task (maybe? COuld still risk blocking main - // thread) - std::thread::Builder::new() - .name("AniMe system thread start".into()) - .spawn(move || { - info!("AniMe new system thread started"); - // Getting copies of these Atomics is done *in* the thread to ensure - // we don't block other threads/main - let thread_exit; - let thread_running; - let anime_type; - loop { - if let Some(lock) = inner.try_lock() { - thread_exit = lock.thread_exit.clone(); - thread_running = lock.thread_running.clone(); - anime_type = lock.anime_type; - break; - } - } - // First two loops are to ensure we *do* aquire a lock on the mutex - // The reason the loop is required is because the USB writes can block - // for up to 10ms. We can't fail to get the atomics. - while thread_running.load(Ordering::SeqCst) { - // Make any running loop exit first - thread_exit.store(true, Ordering::SeqCst); - } - - info!("AniMe no previous system thread running (now)"); - thread_exit.store(false, Ordering::SeqCst); - thread_running.store(true, Ordering::SeqCst); - 'main: loop { - for action in &actions { - if thread_exit.load(Ordering::SeqCst) { - break 'main; - } - match action { - ActionData::Animation(frames) => { - rog_anime::run_animation(frames, &|frame| { - if thread_exit.load(Ordering::Acquire) { - info!("rog-anime: animation sub-loop was asked to exit"); - return Ok(true); // Do safe exit - } - inner - .try_lock() - .map(|lock| { - lock.write_data_buffer(frame) - .map_err(|err| { - warn!( - "rog_anime::run_animation:callback {}", - err - ); - }) - .ok(); - false // Don't exit yet - }) - .map_or_else( - || { - warn!("rog_anime::run_animation:callback failed"); - Err(AnimeError::NoFrames) - }, - Ok, - ) - }); - if thread_exit.load(Ordering::Acquire) { - info!("rog-anime: sub-loop exited and main loop exiting now"); - break 'main; - } - } - ActionData::Image(image) => { - once = false; - if let Some(lock) = inner.try_lock() { - lock.write_data_buffer(image.as_ref().clone()) - .map_err(|e| error!("{}", e)) - .ok(); - } - } - ActionData::Pause(duration) => sleep(*duration), - ActionData::AudioEq - | ActionData::SystemInfo - | ActionData::TimeDate - | ActionData::Matrix => {} - } - } - if thread_exit.load(Ordering::SeqCst) { - break 'main; - } - if once || actions.is_empty() { - break 'main; - } - } - // Clear the display on exit - if let Some(lock) = inner.try_lock() { - if let Ok(data) = - AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()]) - .map_err(|e| error!("{}", e)) - { - lock.write_data_buffer(data) - .map_err(|err| { - warn!("rog_anime::run_animation:callback {}", err); - }) - .ok(); - } - } - // Loop ended, set the atmonics - thread_running.store(false, Ordering::SeqCst); - info!("AniMe system thread exited"); - }) - .map(|err| info!("AniMe system thread: {:?}", err)) - .ok(); - } - - /// Write only a data packet. This will modify the leds brightness using the - /// global brightness set in config. - fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) -> Result<(), RogError> { - for led in buffer.data_mut().iter_mut() { - let mut bright = *led as f32 * self.config.brightness; - if bright > 254.0 { - bright = 254.0; - } - *led = bright as u8; - } - let data = AnimePacketType::try_from(buffer)?; - for row in &data { - self.node.write_bytes(row)?; - } - self.node.write_bytes(&pkt_flush())?; - Ok(()) - } - - fn do_initialization(&self) -> Result<(), RogError> { - let pkts = pkts_for_init(); - self.node.write_bytes(&pkts[0])?; - self.node.write_bytes(&pkts[1])?; - Ok(()) - } -} diff --git a/daemon/src/ctrl_anime/trait_impls.rs b/daemon/src/ctrl_anime/trait_impls.rs deleted file mode 100644 index b52d6e30..00000000 --- a/daemon/src/ctrl_anime/trait_impls.rs +++ /dev/null @@ -1,299 +0,0 @@ -use std::sync::atomic::Ordering; -use std::sync::Arc; - -use async_trait::async_trait; -use config_traits::StdConfig; -use log::warn; -use rog_anime::usb::{ - pkt_set_brightness, pkt_set_builtin_animations, pkt_set_enable_display, - pkt_set_enable_powersave_anim, AnimAwake, AnimBooting, AnimShutdown, AnimSleeping, Brightness, -}; -use rog_anime::{AnimeDataBuffer, DeviceState}; -use zbus::export::futures_util::lock::Mutex; -use zbus::{dbus_interface, Connection, SignalContext}; - -use super::CtrlAnime; -use crate::error::RogError; - -pub(super) const ZBUS_PATH: &str = "/org/asuslinux/Anime"; - -#[derive(Clone)] -pub struct CtrlAnimeZbus(pub Arc>); - -/// The struct with the main dbus methods requires this trait -#[async_trait] -impl crate::ZbusRun for CtrlAnimeZbus { - async fn add_to_server(self, server: &mut Connection) { - Self::add_to_server_helper(self, ZBUS_PATH, server).await; - } -} - -// None of these calls can be guarnateed to succeed unless we loop until okay -// If the try_lock *does* succeed then any other thread trying to lock will not -// grab it until we finish. -#[dbus_interface(name = "org.asuslinux.Daemon")] -impl CtrlAnimeZbus { - /// Writes a data stream of length. Will force system thread to exit until - /// it is restarted - async fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> { - let lock = self.0.lock().await; - lock.thread_exit.store(true, Ordering::SeqCst); - lock.write_data_buffer(input).map_err(|err| { - warn!("rog_anime::run_animation:callback {}", err); - err - })?; - Ok(()) - } - - /// Set the global AniMe brightness - async fn set_image_brightness(&self, bright: f32) { - let mut lock = self.0.lock().await; - let mut bright = bright; - if bright < 0.0 { - bright = 0.0; - } else if bright > 1.0 { - bright = 1.0; - } - lock.config.brightness = bright; - lock.config.write(); - } - - /// Set base brightness level - // TODO: enum for brightness - async fn set_brightness( - &self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - brightness: Brightness, - ) { - let mut lock = self.0.lock().await; - lock.node - .write_bytes(&pkt_set_brightness(brightness)) - .map_err(|err| { - warn!("rog_anime::run_animation:callback {}", err); - }) - .ok(); - lock.config.display_brightness = brightness; - lock.config.write(); - - Self::notify_device_state( - &ctxt, - DeviceState { - display_enabled: lock.config.display_enabled, - display_brightness: lock.config.display_brightness, - builtin_anims_enabled: lock.config.builtin_anims_enabled, - builtin_anims: lock.config.builtin_anims, - }, - ) - .await - .ok(); - } - - /// Enable the builtin animations or not - async fn set_builtins_enabled( - &self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - enabled: bool, - ) { - let mut lock = self.0.lock().await; - lock.node - .write_bytes(&pkt_set_enable_powersave_anim(enabled)) - .map_err(|err| { - warn!("rog_anime::run_animation:callback {}", err); - }) - .ok(); - lock.config.builtin_anims_enabled = enabled; - lock.config.write(); - if enabled { - lock.thread_exit.store(true, Ordering::Release); - } - - Self::notify_device_state( - &ctxt, - DeviceState { - display_enabled: lock.config.display_enabled, - display_brightness: lock.config.display_brightness, - builtin_anims_enabled: lock.config.builtin_anims_enabled, - builtin_anims: lock.config.builtin_anims, - }, - ) - .await - .ok(); - } - - /// Set which builtin animation is used for each stage - async fn set_builtin_animations( - &self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - boot: AnimBooting, - awake: AnimAwake, - sleep: AnimSleeping, - shutdown: AnimShutdown, - ) { - let mut lock = self.0.lock().await; - lock.node - .write_bytes(&pkt_set_enable_powersave_anim(true)) - .map_err(|err| { - warn!("rog_anime::run_animation:callback {}", err); - }) - .ok(); - lock.node - .write_bytes(&pkt_set_builtin_animations(boot, awake, sleep, shutdown)) - .map_err(|err| { - warn!("rog_anime::run_animation:callback {}", err); - }) - .ok(); - lock.config.builtin_anims.boot = boot; - lock.config.builtin_anims.sleep = sleep; - lock.config.builtin_anims.awake = awake; - lock.config.builtin_anims.shutdown = shutdown; - lock.config.write(); - - Self::notify_device_state( - &ctxt, - DeviceState { - display_enabled: lock.config.display_enabled, - display_brightness: lock.config.display_brightness, - builtin_anims_enabled: lock.config.builtin_anims_enabled, - builtin_anims: lock.config.builtin_anims, - }, - ) - .await - .ok(); - } - - /// Set whether the AniMe is enabled at all - async fn set_enable_display( - &self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - enabled: bool, - ) { - let mut lock = self.0.lock().await; - lock.node - .write_bytes(&pkt_set_enable_display(enabled)) - .map_err(|err| { - warn!("rog_anime::run_animation:callback {}", err); - }) - .ok(); - lock.config.display_enabled = enabled; - lock.config.write(); - - Self::notify_device_state( - &ctxt, - DeviceState { - display_enabled: lock.config.display_enabled, - display_brightness: lock.config.display_brightness, - builtin_anims_enabled: lock.config.builtin_anims_enabled, - builtin_anims: lock.config.builtin_anims, - }, - ) - .await - .ok(); - } - - /// The main loop is the base system set action if the user isn't running - /// the user daemon - async fn run_main_loop(&self, start: bool) { - if start { - let lock = self.0.lock().await; - lock.thread_exit.store(true, Ordering::SeqCst); - CtrlAnime::run_thread(self.0.clone(), lock.cache.system.clone(), false).await; - } - } - - /// Get the device state as stored by asusd - // #[dbus_interface(property)] - async fn device_state(&self) -> DeviceState { - let lock = self.0.lock().await; - DeviceState { - display_enabled: lock.config.display_enabled, - display_brightness: lock.config.display_brightness, - builtin_anims_enabled: lock.config.builtin_anims_enabled, - builtin_anims: lock.config.builtin_anims, - } - } - - /// Notify listeners of the status of AniMe LED power and factory - /// system-status animations - #[dbus_interface(signal)] - async fn notify_device_state(ctxt: &SignalContext<'_>, data: DeviceState) -> zbus::Result<()>; -} - -#[async_trait] -impl crate::CtrlTask for CtrlAnimeZbus { - fn zbus_path() -> &'static str { - ZBUS_PATH - } - - async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> { - let inner1 = self.0.clone(); - let inner2 = self.0.clone(); - let inner3 = self.0.clone(); - let inner4 = self.0.clone(); - self.create_sys_event_tasks( - move || { - // on_sleep - let inner1 = inner1.clone(); - async move { - let lock = inner1.lock().await; - CtrlAnime::run_thread(inner1.clone(), lock.cache.sleep.clone(), true).await; - } - }, - move || { - // on_wake - let inner2 = inner2.clone(); - async move { - let lock = inner2.lock().await; - CtrlAnime::run_thread(inner2.clone(), lock.cache.wake.clone(), true).await; - } - }, - move || { - // on_shutdown - let inner3 = inner3.clone(); - async move { - let lock = inner3.lock().await; - CtrlAnime::run_thread(inner3.clone(), lock.cache.shutdown.clone(), true).await; - } - }, - move || { - // on_boot - let inner4 = inner4.clone(); - async move { - let lock = inner4.lock().await; - CtrlAnime::run_thread(inner4.clone(), lock.cache.boot.clone(), true).await; - } - }, - ) - .await; - - Ok(()) - } -} - -#[async_trait] -impl crate::Reloadable for CtrlAnimeZbus { - async fn reload(&mut self) -> Result<(), RogError> { - if let Some(lock) = self.0.try_lock() { - let anim = &lock.config.builtin_anims; - lock.node - .write_bytes(&pkt_set_enable_display(lock.config.display_enabled))?; - lock.node.write_bytes(&pkt_set_enable_powersave_anim( - lock.config.builtin_anims_enabled, - ))?; - lock.node.write_bytes(&pkt_set_builtin_animations( - anim.boot, - anim.awake, - anim.sleep, - anim.shutdown, - ))?; - - if lock.config.builtin_anims_enabled && !lock.cache.boot.is_empty() { - lock.node - .write_bytes(&pkt_set_enable_powersave_anim(false)) - .ok(); - } - let action = lock.cache.boot.clone(); - CtrlAnime::run_thread(self.0.clone(), action, true).await; - } - Ok(()) - } -} diff --git a/daemon/src/ctrl_aura/config.rs b/daemon/src/ctrl_aura/config.rs deleted file mode 100644 index 566c9e39..00000000 --- a/daemon/src/ctrl_aura/config.rs +++ /dev/null @@ -1,357 +0,0 @@ -use std::collections::{BTreeMap, HashSet}; - -use config_traits::{StdConfig, StdConfigLoad}; -use rog_aura::aura_detection::LaptopLedData; -use rog_aura::usb::{AuraDevRog1, AuraDevRog2, AuraDevTuf, AuraDevice, AuraPowerDev}; -use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Direction, LedBrightness, Speed, GRADIENT}; -use serde_derive::{Deserialize, Serialize}; - -const CONFIG_FILE: &str = "aura.ron"; - -/// Enable/disable LED control in various states such as -/// when the device is awake, suspended, shutting down or -/// booting. -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum AuraPowerConfig { - AuraDevTuf(HashSet), - AuraDevRog1(HashSet), - AuraDevRog2(HashSet), -} - -impl AuraPowerConfig { - /// Invalid for TUF laptops - pub fn to_bytes(control: &Self) -> [u8; 4] { - match control { - AuraPowerConfig::AuraDevTuf(_) => [0, 0, 0, 0], - AuraPowerConfig::AuraDevRog1(c) => { - let c: Vec = c.iter().copied().collect(); - AuraDevRog1::to_bytes(&c) - } - AuraPowerConfig::AuraDevRog2(c) => { - let c: Vec = c.iter().copied().collect(); - AuraDevRog2::to_bytes(&c) - } - } - } - - pub fn to_tuf_bool_array(control: &Self) -> Option<[bool; 5]> { - if let Self::AuraDevTuf(c) = control { - return Some([ - true, - c.contains(&AuraDevTuf::Boot), - c.contains(&AuraDevTuf::Awake), - c.contains(&AuraDevTuf::Sleep), - c.contains(&AuraDevTuf::Keyboard), - ]); - } - - if let Self::AuraDevRog1(c) = control { - return Some([ - true, - c.contains(&AuraDevRog1::Boot), - c.contains(&AuraDevRog1::Awake), - c.contains(&AuraDevRog1::Sleep), - c.contains(&AuraDevRog1::Keyboard), - ]); - } - - None - } - - pub fn set_tuf(&mut self, power: AuraDevTuf, on: bool) { - if let Self::AuraDevTuf(p) = self { - if on { - p.insert(power); - } else { - p.remove(&power); - } - } - } - - pub fn set_0x1866(&mut self, power: AuraDevRog1, on: bool) { - if let Self::AuraDevRog1(p) = self { - if on { - p.insert(power); - } else { - p.remove(&power); - } - } - } - - pub fn set_0x19b6(&mut self, power: AuraDevRog2, on: bool) { - if let Self::AuraDevRog2(p) = self { - if on { - p.insert(power); - } else { - p.remove(&power); - } - } - } -} - -impl From<&AuraPowerConfig> for AuraPowerDev { - fn from(config: &AuraPowerConfig) -> Self { - match config { - AuraPowerConfig::AuraDevTuf(d) => AuraPowerDev { - tuf: d.iter().copied().collect(), - x1866: vec![], - x19b6: vec![], - }, - AuraPowerConfig::AuraDevRog1(d) => AuraPowerDev { - tuf: vec![], - x1866: d.iter().copied().collect(), - x19b6: vec![], - }, - AuraPowerConfig::AuraDevRog2(d) => AuraPowerDev { - tuf: vec![], - x1866: vec![], - x19b6: d.iter().copied().collect(), - }, - } - } -} - -#[derive(Deserialize, Serialize, Debug, Clone)] -// #[serde(default)] -pub struct AuraConfig { - pub brightness: LedBrightness, - pub current_mode: AuraModeNum, - pub builtins: BTreeMap, - pub multizone: Option>>, - pub multizone_on: bool, - pub enabled: AuraPowerConfig, -} - -impl StdConfig for AuraConfig { - fn new() -> Self { - // Self::create_default(AuraDevice::X19b6, &LaptopLedData::get_data()) - panic!("AuraConfig::new() should not be used, use AuraConfig::create_default() instead"); - } - - fn config_dir() -> std::path::PathBuf { - std::path::PathBuf::from(crate::CONFIG_PATH_BASE) - } - - fn file_name(&self) -> String { - CONFIG_FILE.to_owned() - } -} - -impl StdConfigLoad for AuraConfig {} - -impl AuraConfig { - pub fn create_default(prod_id: AuraDevice, support_data: &LaptopLedData) -> Self { - // create a default config here - let enabled = if prod_id == AuraDevice::X19b6 { - AuraPowerConfig::AuraDevRog2(HashSet::from([ - AuraDevRog2::BootLogo, - AuraDevRog2::BootKeyb, - AuraDevRog2::SleepLogo, - AuraDevRog2::SleepKeyb, - AuraDevRog2::AwakeLogo, - AuraDevRog2::AwakeKeyb, - AuraDevRog2::ShutdownLogo, - AuraDevRog2::ShutdownKeyb, - AuraDevRog2::BootBar, - AuraDevRog2::AwakeBar, - AuraDevRog2::SleepBar, - AuraDevRog2::ShutdownBar, - AuraDevRog2::BootRearGlow, - AuraDevRog2::AwakeRearGlow, - AuraDevRog2::SleepRearGlow, - AuraDevRog2::ShutdownRearGlow, - ])) - } else if prod_id == AuraDevice::Tuf { - AuraPowerConfig::AuraDevTuf(HashSet::from([ - AuraDevTuf::Awake, - AuraDevTuf::Boot, - AuraDevTuf::Sleep, - AuraDevTuf::Keyboard, - ])) - } else { - AuraPowerConfig::AuraDevRog1(HashSet::from([ - AuraDevRog1::Awake, - AuraDevRog1::Boot, - AuraDevRog1::Sleep, - AuraDevRog1::Keyboard, - AuraDevRog1::Lightbar, - ])) - }; - let mut config = AuraConfig { - brightness: LedBrightness::Med, - current_mode: AuraModeNum::Static, - builtins: BTreeMap::new(), - multizone: None, - multizone_on: false, - enabled, - }; - - for n in &support_data.basic_modes { - config - .builtins - .insert(*n, AuraEffect::default_with_mode(*n)); - - if !support_data.basic_zones.is_empty() { - let mut default = vec![]; - for (i, tmp) in support_data.basic_zones.iter().enumerate() { - default.push(AuraEffect { - mode: *n, - zone: *tmp, - colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]), - colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]), - speed: Speed::Med, - direction: Direction::Left, - }); - } - if let Some(m) = config.multizone.as_mut() { - m.insert(*n, default); - } else { - let mut tmp = BTreeMap::new(); - tmp.insert(*n, default); - config.multizone = Some(tmp); - } - } - } - config - } - - /// Set the mode data, current mode, and if multizone enabled. - /// - /// Multipurpose, will accept `AuraEffect` with zones and put in the correct - /// store. - pub fn set_builtin(&mut self, effect: AuraEffect) { - self.current_mode = effect.mode; - if effect.zone() == AuraZone::None { - self.builtins.insert(*effect.mode(), effect); - self.multizone_on = false; - } else { - if let Some(multi) = self.multizone.as_mut() { - if let Some(fx) = multi.get_mut(effect.mode()) { - for fx in fx.iter_mut() { - if fx.zone == effect.zone { - *fx = effect; - return; - } - } - fx.push(effect); - } else { - multi.insert(*effect.mode(), vec![effect]); - } - } else { - let mut tmp = BTreeMap::new(); - tmp.insert(*effect.mode(), vec![effect]); - self.multizone = Some(tmp); - } - self.multizone_on = true; - } - } - - pub fn get_multizone(&self, aura_type: AuraModeNum) -> Option<&[AuraEffect]> { - if let Some(multi) = &self.multizone { - return multi.get(&aura_type).map(|v| v.as_slice()); - } - None - } -} - -#[cfg(test)] -mod tests { - use rog_aura::aura_detection::LaptopLedData; - use rog_aura::usb::AuraDevice; - use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour}; - - use super::AuraConfig; - - #[test] - fn set_multizone_4key_config() { - let mut config = AuraConfig::create_default(AuraDevice::X19b6, &LaptopLedData::default()); - - let effect = AuraEffect { - colour1: Colour(0xff, 0x00, 0xff), - zone: AuraZone::Key1, - ..Default::default() - }; - config.set_builtin(effect); - - assert!(config.multizone.is_some()); - - let effect = AuraEffect { - colour1: Colour(0x00, 0xff, 0xff), - zone: AuraZone::Key2, - ..Default::default() - }; - config.set_builtin(effect); - - let effect = AuraEffect { - colour1: Colour(0xff, 0xff, 0x00), - zone: AuraZone::Key3, - ..Default::default() - }; - config.set_builtin(effect); - - let effect = AuraEffect { - colour1: Colour(0x00, 0xff, 0x00), - zone: AuraZone::Key4, - ..Default::default() - }; - let effect_clone = effect.clone(); - config.set_builtin(effect); - // This should replace existing - config.set_builtin(effect_clone); - - let res = config.multizone.unwrap(); - let sta = res.get(&AuraModeNum::Static).unwrap(); - assert_eq!(sta.len(), 4); - assert_eq!(sta[0].colour1, Colour(0xff, 0x00, 0xff)); - assert_eq!(sta[1].colour1, Colour(0x00, 0xff, 0xff)); - assert_eq!(sta[2].colour1, Colour(0xff, 0xff, 0x00)); - assert_eq!(sta[3].colour1, Colour(0x00, 0xff, 0x00)); - } - - #[test] - fn set_multizone_multimode_config() { - let mut config = AuraConfig::create_default(AuraDevice::X19b6, &LaptopLedData::default()); - - let effect = AuraEffect { - zone: AuraZone::Key1, - ..Default::default() - }; - config.set_builtin(effect); - - assert!(config.multizone.is_some()); - - let effect = AuraEffect { - zone: AuraZone::Key2, - mode: AuraModeNum::Breathe, - ..Default::default() - }; - config.set_builtin(effect); - - let effect = AuraEffect { - zone: AuraZone::Key3, - mode: AuraModeNum::Comet, - ..Default::default() - }; - config.set_builtin(effect); - - let effect = AuraEffect { - zone: AuraZone::Key4, - mode: AuraModeNum::Pulse, - ..Default::default() - }; - config.set_builtin(effect); - - let res = config.multizone.unwrap(); - let sta = res.get(&AuraModeNum::Static).unwrap(); - assert_eq!(sta.len(), 1); - - let sta = res.get(&AuraModeNum::Breathe).unwrap(); - assert_eq!(sta.len(), 1); - - let sta = res.get(&AuraModeNum::Comet).unwrap(); - assert_eq!(sta.len(), 1); - - let sta = res.get(&AuraModeNum::Pulse).unwrap(); - assert_eq!(sta.len(), 1); - } -} diff --git a/daemon/src/ctrl_aura/controller.rs b/daemon/src/ctrl_aura/controller.rs deleted file mode 100644 index 041b8e61..00000000 --- a/daemon/src/ctrl_aura/controller.rs +++ /dev/null @@ -1,558 +0,0 @@ -use std::collections::BTreeMap; - -use config_traits::{StdConfig, StdConfigLoad}; -use log::{info, warn}; -use rog_aura::advanced::{LedUsbPackets, UsbPackets}; -use rog_aura::aura_detection::{LaptopLedData, ASUS_KEYBOARD_DEVICES}; -use rog_aura::usb::{AuraDevice, LED_APPLY, LED_SET}; -use rog_aura::{AuraEffect, AuraZone, Direction, LedBrightness, Speed, GRADIENT, LED_MSG_LEN}; -use rog_platform::hid_raw::HidRaw; -use rog_platform::keyboard_led::KeyboardLed; -use rog_platform::supported::LedSupportedFunctions; - -use super::config::{AuraConfig, AuraPowerConfig}; -use crate::error::RogError; -use crate::GetSupported; - -impl GetSupported for CtrlKbdLed { - type A = LedSupportedFunctions; - - fn get_supported() -> Self::A { - // let mode = <&str>::from(&::from(*mode)); - let laptop = LaptopLedData::get_data(); - let stock_led_modes = laptop.basic_modes; - let multizone_led_mode = laptop.basic_zones; - let advanced_type = laptop.advanced_type; - - let mut prod_id = AuraDevice::Unknown; - for prod in ASUS_KEYBOARD_DEVICES { - if HidRaw::new(prod.into()).is_ok() { - prod_id = prod; - break; - } - } - - let rgb = KeyboardLed::new(); - if let Ok(p) = rgb.as_ref() { - if p.has_kbd_rgb_mode() { - prod_id = AuraDevice::Tuf; - } - } - - LedSupportedFunctions { - dev_id: prod_id, - brightness: rgb.is_ok(), - basic_modes: stock_led_modes, - basic_zones: multizone_led_mode, - advanced_type: advanced_type.into(), - } - } -} - -#[derive(Debug, PartialEq, Eq, PartialOrd)] -pub enum LEDNode { - KbdLed(KeyboardLed), - Rog(HidRaw), - None, -} - -pub struct CtrlKbdLed { - // TODO: config stores the keyboard type as an AuraPower, use or update this - pub led_prod: AuraDevice, - pub led_node: LEDNode, - pub kd_brightness: KeyboardLed, - pub supported_modes: LaptopLedData, - pub flip_effect_write: bool, - pub per_key_mode_active: bool, - pub config: AuraConfig, -} - -impl CtrlKbdLed { - pub fn new(supported_modes: LaptopLedData) -> Result { - let mut led_prod = AuraDevice::Unknown; - let mut usb_node = None; - for prod in ASUS_KEYBOARD_DEVICES { - match HidRaw::new(prod.into()) { - Ok(node) => { - led_prod = prod; - usb_node = Some(node); - info!( - "Looked for keyboard controller 0x{}: Found", - <&str>::from(prod) - ); - break; - } - Err(err) => info!( - "Looked for keyboard controller 0x{}: {err}", - <&str>::from(prod) - ), - } - } - - let rgb_led = KeyboardLed::new()?; - - if usb_node.is_none() && !rgb_led.has_kbd_rgb_mode() { - let dmi = sysfs_class::DmiId::default(); - if let Ok(prod_family) = dmi.product_family() { - if prod_family.contains("TUF") { - warn!( - "kbd_rgb_mode was not found in the /sys/. You require a minimum 6.1 \ - kernel and a supported TUF laptop" - ); - } - } - return Err(RogError::NoAuraKeyboard); - } - - let led_node = if let Some(rog) = usb_node { - info!("Found ROG USB keyboard"); - LEDNode::Rog(rog) - } else if rgb_led.has_kbd_rgb_mode() { - info!("Found TUF keyboard"); - LEDNode::KbdLed(rgb_led.clone()) - } else { - LEDNode::None - }; - - let mut config_init = AuraConfig::create_default(led_prod, &supported_modes); - let mut config_loaded = config_init.clone().load(); - - for mode in &mut config_init.builtins { - // update init values from loaded values if they exist - if let Some(loaded) = config_loaded.builtins.get(mode.0) { - *mode.1 = loaded.clone(); - } - } - config_loaded.builtins = config_init.builtins; - - if let (Some(mut multizone_init), Some(multizone_loaded)) = - (config_init.multizone, config_loaded.multizone.as_mut()) - { - for mode in multizone_init.iter_mut() { - // update init values from loaded values if they exist - if let Some(loaded) = multizone_loaded.get(mode.0) { - let mut new_set = Vec::new(); - // only reuse a zone mode if the mode is supported - for mode in loaded { - if supported_modes.basic_modes.contains(&mode.mode) { - new_set.push(mode.clone()); - } - } - *mode.1 = new_set; - } - } - *multizone_loaded = multizone_init; - } - - let ctrl = CtrlKbdLed { - led_prod, - led_node, // on TUF this is the same as rgb_led / kd_brightness - kd_brightness: rgb_led, // If was none then we already returned above - supported_modes, - flip_effect_write: false, - per_key_mode_active: false, - config: config_loaded, - }; - Ok(ctrl) - } - - pub(super) fn get_brightness(&self) -> Result { - self.kd_brightness - .get_brightness() - .map_err(RogError::Platform) - } - - pub(super) fn set_brightness(&self, brightness: LedBrightness) -> Result<(), RogError> { - self.kd_brightness - .set_brightness(brightness as u8) - .map_err(RogError::Platform) - } - - pub fn next_brightness(&mut self) -> Result<(), RogError> { - let mut bright = (self.config.brightness as u32) + 1; - if bright > 3 { - bright = 0; - } - self.config.brightness = ::from(bright); - self.config.write(); - self.set_brightness(self.config.brightness) - } - - pub fn prev_brightness(&mut self) -> Result<(), RogError> { - let mut bright = self.config.brightness as u32; - if bright == 0 { - bright = 3; - } else { - bright -= 1; - } - self.config.brightness = ::from(bright); - self.config.write(); - self.set_brightness(self.config.brightness) - } - - /// Set combination state for boot animation/sleep animation/all leds/keys - /// leds/side leds LED active - pub(super) fn set_power_states(&mut self) -> Result<(), RogError> { - if let LEDNode::KbdLed(platform) = &mut self.led_node { - if let Some(pwr) = AuraPowerConfig::to_tuf_bool_array(&self.config.enabled) { - let buf = [1, pwr[1] as u8, pwr[2] as u8, pwr[3] as u8, pwr[4] as u8]; - platform.set_kbd_rgb_state(&buf)?; - } - } else if let LEDNode::Rog(hid_raw) = &self.led_node { - let bytes = AuraPowerConfig::to_bytes(&self.config.enabled); - let message = [0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2], bytes[3]]; - - hid_raw.write_bytes(&message)?; - hid_raw.write_bytes(&LED_SET)?; - // Changes won't persist unless apply is set - hid_raw.write_bytes(&LED_APPLY)?; - } - Ok(()) - } - - /// Set an Aura effect if the effect mode or zone is supported. - /// - /// On success the aura config file is read to refresh cached values, then - /// the effect is stored and config written to disk. - pub(crate) fn set_effect(&mut self, effect: AuraEffect) -> Result<(), RogError> { - if !self.supported_modes.basic_modes.contains(&effect.mode) - || effect.zone != AuraZone::None - && !self.supported_modes.basic_zones.contains(&effect.zone) - { - return Err(RogError::AuraEffectNotSupported); - } - - self.write_mode(&effect)?; - self.config.read(); // refresh config if successful - self.config.set_builtin(effect); - if self.config.brightness == LedBrightness::Off { - self.config.brightness = LedBrightness::Med; - } - self.config.write(); - self.set_brightness(self.config.brightness)?; - Ok(()) - } - - /// Write an effect block. This is for per-key, but can be repurposed to - /// write the raw factory mode packets - when doing this it is expected that - /// only the first `Vec` (`effect[0]`) is valid. - pub fn write_effect_block(&mut self, effect: &UsbPackets) -> Result<(), RogError> { - if self.config.brightness == LedBrightness::Off { - self.config.brightness = LedBrightness::Med; - self.config.write(); - } - - let pkt_type = effect[0][1]; - const PER_KEY_TYPE: u8 = 0xbc; - - if pkt_type != PER_KEY_TYPE { - self.per_key_mode_active = false; - if let LEDNode::Rog(hid_raw) = &self.led_node { - hid_raw.write_bytes(&effect[0])?; - hid_raw.write_bytes(&LED_SET)?; - // hid_raw.write_bytes(&LED_APPLY)?; - } - } else { - if !self.per_key_mode_active { - if let LEDNode::Rog(hid_raw) = &self.led_node { - let init = LedUsbPackets::get_init_msg(); - hid_raw.write_bytes(&init)?; - } - self.per_key_mode_active = true; - } - if let LEDNode::Rog(hid_raw) = &self.led_node { - for row in effect.iter() { - hid_raw.write_bytes(row)?; - } - } else if let LEDNode::KbdLed(tuf) = &self.led_node { - for row in effect.iter() { - let r = row[9]; - let g = row[10]; - let b = row[11]; - tuf.set_kbd_rgb_mode(&[0, 0, r, g, b, 0])?; - } - } - self.flip_effect_write = !self.flip_effect_write; - } - Ok(()) - } - - pub(super) fn toggle_mode(&mut self, reverse: bool) -> Result<(), RogError> { - let current = self.config.current_mode; - if let Some(idx) = self - .supported_modes - .basic_modes - .iter() - .position(|v| *v == current) - { - let mut idx = idx; - // goes past end of array - if reverse { - if idx == 0 { - idx = self.supported_modes.basic_modes.len() - 1; - } else { - idx -= 1; - } - } else { - idx += 1; - if idx == self.supported_modes.basic_modes.len() { - idx = 0; - } - } - let next = self.supported_modes.basic_modes[idx]; - - self.config.read(); - // if self.config.builtins.contains_key(&next) { - self.config.current_mode = next; - self.write_current_config_mode()?; - // } - self.config.write(); - } - - Ok(()) - } - - fn write_mode(&mut self, mode: &AuraEffect) -> Result<(), RogError> { - if let LEDNode::KbdLed(platform) = &self.led_node { - let buf = [ - 1, - mode.mode as u8, - mode.colour1.0, - mode.colour1.1, - mode.colour1.2, - mode.speed as u8, - ]; - platform.set_kbd_rgb_mode(&buf)?; - } else if let LEDNode::Rog(hid_raw) = &self.led_node { - let bytes: [u8; LED_MSG_LEN] = mode.into(); - hid_raw.write_bytes(&bytes)?; - hid_raw.write_bytes(&LED_SET)?; - // Changes won't persist unless apply is set - hid_raw.write_bytes(&LED_APPLY)?; - } else { - return Err(RogError::NoAuraKeyboard); - } - self.per_key_mode_active = false; - Ok(()) - } - - pub(super) fn write_current_config_mode(&mut self) -> Result<(), RogError> { - if self.config.multizone_on { - let mode = self.config.current_mode; - let mut create = false; - // There is no multizone config for this mode so create one here - // using the colours of rainbow if it exists, or first available - // mode, or random - if self.config.multizone.is_none() { - create = true; - } else if let Some(multizones) = self.config.multizone.as_ref() { - if !multizones.contains_key(&mode) { - create = true; - } - } - if create { - info!("No user-set config for zone founding, attempting a default"); - self.create_multizone_default()?; - } - - if let Some(multizones) = self.config.multizone.as_mut() { - if let Some(set) = multizones.get(&mode) { - for mode in set.clone() { - self.write_mode(&mode)?; - } - } - } - } else { - let mode = self.config.current_mode; - if let Some(effect) = self.config.builtins.get(&mode).cloned() { - self.write_mode(&effect)?; - } - } - - Ok(()) - } - - /// Create a default for the `current_mode` if multizone and no config - /// exists. - fn create_multizone_default(&mut self) -> Result<(), RogError> { - let mut default = vec![]; - for (i, tmp) in self.supported_modes.basic_zones.iter().enumerate() { - default.push(AuraEffect { - mode: self.config.current_mode, - zone: *tmp, - colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]), - colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]), - speed: Speed::Med, - direction: Direction::Left, - }); - } - if default.is_empty() { - return Err(RogError::AuraEffectNotSupported); - } - - if let Some(multizones) = self.config.multizone.as_mut() { - multizones.insert(self.config.current_mode, default); - } else { - let mut tmp = BTreeMap::new(); - tmp.insert(self.config.current_mode, default); - self.config.multizone = Some(tmp); - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use rog_aura::aura_detection::LaptopLedData; - use rog_aura::usb::AuraDevice; - use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour}; - use rog_platform::keyboard_led::KeyboardLed; - - use super::CtrlKbdLed; - use crate::ctrl_aura::config::AuraConfig; - use crate::ctrl_aura::controller::LEDNode; - - #[test] - // #[ignore = "Must be manually run due to detection stage"] - fn check_set_mode_errors() { - // Checking to ensure set_mode errors when unsupported modes are tried - let config = AuraConfig::create_default(AuraDevice::X19b6, &LaptopLedData::default()); - let supported_modes = LaptopLedData { - board_name: String::new(), - layout_name: "ga401".to_owned(), - basic_modes: vec![AuraModeNum::Static], - basic_zones: vec![], - advanced_type: rog_aura::AdvancedAuraType::None, - }; - let mut controller = CtrlKbdLed { - led_prod: AuraDevice::X19b6, - led_node: LEDNode::None, - kd_brightness: KeyboardLed::default(), - supported_modes, - flip_effect_write: false, - per_key_mode_active: false, - config, - }; - - let mut effect = AuraEffect { - colour1: Colour(0xff, 0x00, 0xff), - zone: AuraZone::None, - ..Default::default() - }; - - // This error comes from write_bytes because we don't have a keyboard node - // stored - assert_eq!( - controller - .set_effect(effect.clone()) - .unwrap_err() - .to_string(), - "No supported Aura keyboard" - ); - - effect.mode = AuraModeNum::Laser; - assert_eq!( - controller - .set_effect(effect.clone()) - .unwrap_err() - .to_string(), - "Aura effect not supported" - ); - - effect.mode = AuraModeNum::Static; - effect.zone = AuraZone::Key2; - assert_eq!( - controller - .set_effect(effect.clone()) - .unwrap_err() - .to_string(), - "Aura effect not supported" - ); - - controller.supported_modes.basic_zones.push(AuraZone::Key2); - assert_eq!( - controller.set_effect(effect).unwrap_err().to_string(), - "No supported Aura keyboard" - ); - } - - #[test] - fn create_multizone_if_no_config() { - // Checking to ensure set_mode errors when unsupported modes are tried - let config = AuraConfig::create_default(AuraDevice::X19b6, &LaptopLedData::default()); - let supported_modes = LaptopLedData { - board_name: String::new(), - layout_name: "ga401".to_owned(), - basic_modes: vec![AuraModeNum::Static], - basic_zones: vec![], - advanced_type: rog_aura::AdvancedAuraType::None, - }; - let mut controller = CtrlKbdLed { - led_prod: AuraDevice::X19b6, - led_node: LEDNode::None, - kd_brightness: KeyboardLed::default(), - supported_modes, - flip_effect_write: false, - per_key_mode_active: false, - config, - }; - - assert!(controller.config.multizone.is_none()); - assert!(controller.create_multizone_default().is_err()); - assert!(controller.config.multizone.is_none()); - - controller.supported_modes.basic_zones.push(AuraZone::Key1); - controller.supported_modes.basic_zones.push(AuraZone::Key2); - assert!(controller.create_multizone_default().is_ok()); - assert!(controller.config.multizone.is_some()); - - let m = controller.config.multizone.unwrap(); - assert!(m.contains_key(&AuraModeNum::Static)); - let e = m.get(&AuraModeNum::Static).unwrap(); - assert_eq!(e.len(), 2); - assert_eq!(e[0].zone, AuraZone::Key1); - assert_eq!(e[1].zone, AuraZone::Key2); - } - - #[test] - fn next_mode_create_multizone_if_no_config() { - // Checking to ensure set_mode errors when unsupported modes are tried - let config = AuraConfig::create_default(AuraDevice::X19b6, &LaptopLedData::default()); - let supported_modes = LaptopLedData { - board_name: String::new(), - layout_name: "ga401".to_owned(), - basic_modes: vec![AuraModeNum::Static], - basic_zones: vec![AuraZone::Key1, AuraZone::Key2], - advanced_type: rog_aura::AdvancedAuraType::None, - }; - let mut controller = CtrlKbdLed { - led_prod: AuraDevice::X19b6, - led_node: LEDNode::None, - kd_brightness: KeyboardLed::default(), - supported_modes, - flip_effect_write: false, - per_key_mode_active: false, - config, - }; - - assert!(controller.config.multizone.is_none()); - controller.config.multizone_on = true; - // This is called in toggle_mode. It will error here because we have no - // keyboard node in tests. - assert_eq!( - controller - .write_current_config_mode() - .unwrap_err() - .to_string(), - "No supported Aura keyboard" - ); - assert!(controller.config.multizone.is_some()); - - let m = controller.config.multizone.unwrap(); - assert!(m.contains_key(&AuraModeNum::Static)); - let e = m.get(&AuraModeNum::Static).unwrap(); - assert_eq!(e.len(), 2); - assert_eq!(e[0].zone, AuraZone::Key1); - assert_eq!(e[1].zone, AuraZone::Key2); - } -} diff --git a/daemon/src/ctrl_aura/mod.rs b/daemon/src/ctrl_aura/mod.rs deleted file mode 100644 index 2da39c6e..00000000 --- a/daemon/src/ctrl_aura/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod config; -pub mod controller; -/// Implements `CtrlTask`, `Reloadable`, `ZbusRun` -pub mod trait_impls; diff --git a/daemon/src/ctrl_aura/trait_impls.rs b/daemon/src/ctrl_aura/trait_impls.rs deleted file mode 100644 index 3411aed6..00000000 --- a/daemon/src/ctrl_aura/trait_impls.rs +++ /dev/null @@ -1,331 +0,0 @@ -use std::collections::BTreeMap; -use std::sync::Arc; - -use async_trait::async_trait; -use config_traits::StdConfig; -use log::{error, info, warn}; -use rog_aura::advanced::UsbPackets; -use rog_aura::usb::AuraPowerDev; -use rog_aura::{AuraEffect, AuraModeNum, LedBrightness}; -use zbus::export::futures_util::lock::{Mutex, MutexGuard}; -use zbus::export::futures_util::StreamExt; -use zbus::{dbus_interface, Connection, SignalContext}; - -use super::controller::CtrlKbdLed; -use crate::error::RogError; -use crate::CtrlTask; - -pub(super) const ZBUS_PATH: &str = "/org/asuslinux/Aura"; - -#[derive(Clone)] -pub struct CtrlKbdLedZbus(pub Arc>); - -impl CtrlKbdLedZbus { - fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> { - let bright = lock.kd_brightness.get_brightness()?; - lock.config.read(); - lock.config.brightness = (bright as u32).into(); - lock.config.write(); - Ok(()) - } -} - -#[async_trait] -impl crate::ZbusRun for CtrlKbdLedZbus { - async fn add_to_server(self, server: &mut Connection) { - Self::add_to_server_helper(self, ZBUS_PATH, server).await; - } -} - -/// The main interface for changing, reading, or notfying signals -/// -/// LED commands are split between Brightness, Modes, Per-Key -#[dbus_interface(name = "org.asuslinux.Daemon")] -impl CtrlKbdLedZbus { - /// Set the keyboard brightness level (0-3) - async fn set_brightness(&mut self, brightness: LedBrightness) { - let ctrl = self.0.lock().await; - ctrl.set_brightness(brightness) - .map_err(|err| warn!("{}", err)) - .ok(); - } - - /// Set a variety of states, input is array of enum. - /// `enabled` sets if the sent array should be disabled or enabled - /// - /// ```text - /// pub struct AuraPowerDev { - /// pub x1866: Vec, - /// pub x19b6: Vec, - /// } - /// pub enum AuraDev1866 { - /// Awake, - /// Keyboard, - /// Lightbar, - /// Boot, - /// Sleep, - /// } - /// enum AuraDev19b6 { - /// BootLogo, - /// BootKeyb, - /// AwakeLogo, - /// AwakeKeyb, - /// SleepLogo, - /// SleepKeyb, - /// ShutdownLogo, - /// ShutdownKeyb, - /// AwakeBar, - /// BootBar, - /// SleepBar, - /// ShutdownBar, - /// BootRearBar, - /// AwakeRearBar, - /// SleepRearBar, - /// ShutdownRearBar, - /// } - /// ``` - async fn set_leds_power( - &mut self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - options: AuraPowerDev, - enabled: bool, - ) -> zbus::fdo::Result<()> { - let mut ctrl = self.0.lock().await; - for p in options.tuf { - ctrl.config.enabled.set_tuf(p, enabled); - } - for p in options.x1866 { - ctrl.config.enabled.set_0x1866(p, enabled); - } - for p in options.x19b6 { - ctrl.config.enabled.set_0x19b6(p, enabled); - } - - ctrl.config.write(); - - ctrl.set_power_states().map_err(|e| { - warn!("{}", e); - e - })?; - - Self::notify_power_states(&ctxt, &AuraPowerDev::from(&ctrl.config.enabled)) - .await - .unwrap_or_else(|err| warn!("{}", err)); - Ok(()) - } - - async fn set_led_mode( - &mut self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - effect: AuraEffect, - ) -> zbus::fdo::Result<()> { - let mut ctrl = self.0.lock().await; - - ctrl.set_effect(effect).map_err(|e| { - warn!("{}", e); - e - })?; - - if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { - Self::notify_led(&ctxt, mode.clone()) - .await - .unwrap_or_else(|err| warn!("{}", err)); - } - Ok(()) - } - - async fn next_led_mode( - &self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - ) -> zbus::fdo::Result<()> { - let mut ctrl = self.0.lock().await; - - ctrl.toggle_mode(false).map_err(|e| { - warn!("{}", e); - e - })?; - - if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { - Self::notify_led(&ctxt, mode.clone()) - .await - .unwrap_or_else(|err| warn!("{}", err)); - } - - Ok(()) - } - - async fn prev_led_mode( - &self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - ) -> zbus::fdo::Result<()> { - let mut ctrl = self.0.lock().await; - - ctrl.toggle_mode(true).map_err(|e| { - warn!("{}", e); - e - })?; - - if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { - Self::notify_led(&ctxt, mode.clone()) - .await - .unwrap_or_else(|err| warn!("{}", err)); - } - - Ok(()) - } - - async fn next_led_brightness(&self) -> zbus::fdo::Result<()> { - let mut ctrl = self.0.lock().await; - ctrl.next_brightness().map_err(|e| { - warn!("{}", e); - e - })?; - Ok(()) - } - - async fn prev_led_brightness(&self) -> zbus::fdo::Result<()> { - let mut ctrl = self.0.lock().await; - ctrl.prev_brightness().map_err(|e| { - warn!("{}", e); - e - })?; - Ok(()) - } - - // As property doesn't work for AuraPowerDev (complexity of serialization?) - // #[dbus_interface(property)] - async fn leds_enabled(&self) -> AuraPowerDev { - let ctrl = self.0.lock().await; - AuraPowerDev::from(&ctrl.config.enabled) - } - - /// Return the current mode data - async fn led_mode(&self) -> AuraModeNum { - let ctrl = self.0.lock().await; - ctrl.config.current_mode - } - - /// Return a list of available modes - async fn led_modes(&self) -> BTreeMap { - let ctrl = self.0.lock().await; - ctrl.config.builtins.clone() - } - - /// On machine that have some form of either per-key keyboard or per-zone - /// this can be used to write custom effects over dbus. The input is a - /// nested `Vec>` where `Vec` is a raw USB packet - async fn direct_addressing_raw(&self, data: UsbPackets) -> zbus::fdo::Result<()> { - let mut ctrl = self.0.lock().await; - ctrl.write_effect_block(&data)?; - Ok(()) - } - - /// Return the current LED brightness - #[dbus_interface(property)] - async fn led_brightness(&self) -> i8 { - let ctrl = self.0.lock().await; - ctrl.get_brightness().map(|n| n as i8).unwrap_or(-1) - } - - #[dbus_interface(signal)] - async fn notify_led(signal_ctxt: &SignalContext<'_>, data: AuraEffect) -> zbus::Result<()>; - - #[dbus_interface(signal)] - async fn notify_power_states( - signal_ctxt: &SignalContext<'_>, - data: &AuraPowerDev, - ) -> zbus::Result<()>; -} - -#[async_trait] -impl CtrlTask for CtrlKbdLedZbus { - fn zbus_path() -> &'static str { - ZBUS_PATH - } - - async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> { - let load_save = |start: bool, mut lock: MutexGuard<'_, CtrlKbdLed>| { - // If waking up - if !start { - info!("CtrlKbdLedTask reloading brightness and modes"); - lock.set_brightness(lock.config.brightness) - .map_err(|e| error!("CtrlKbdLedTask: {e}")) - .ok(); - lock.write_current_config_mode() - .map_err(|e| error!("CtrlKbdLedTask: {e}")) - .ok(); - } else if start { - info!("CtrlKbdLedTask saving last brightness"); - Self::update_config(&mut lock) - .map_err(|e| error!("CtrlKbdLedTask: {e}")) - .ok(); - } - }; - - let inner1 = self.0.clone(); - let inner2 = self.0.clone(); - let inner3 = self.0.clone(); - let inner4 = self.0.clone(); - self.create_sys_event_tasks( - // Loop so that we do aquire the lock but also don't block other - // threads (prevents potential deadlocks) - move || { - let inner1 = inner1.clone(); - async move { - let lock = inner1.lock().await; - load_save(true, lock); - } - }, - move || { - let inner2 = inner2.clone(); - async move { - let lock = inner2.lock().await; - load_save(false, lock); - } - }, - move || { - let inner3 = inner3.clone(); - async move { - let lock = inner3.lock().await; - load_save(false, lock); - } - }, - move || { - let inner4 = inner4.clone(); - async move { - let lock = inner4.lock().await; - load_save(false, lock); - } - }, - ) - .await; - - let ctrl2 = self.0.clone(); - let ctrl = self.0.lock().await; - let watch = ctrl.kd_brightness.monitor_brightness()?; - tokio::spawn(async move { - let mut buffer = [0; 32]; - watch - .into_event_stream(&mut buffer) - .unwrap() - .for_each(|_| async { - if let Some(lock) = ctrl2.try_lock() { - load_save(true, lock); - } - }) - .await; - }); - - Ok(()) - } -} - -#[async_trait] -impl crate::Reloadable for CtrlKbdLedZbus { - async fn reload(&mut self) -> Result<(), RogError> { - let mut ctrl = self.0.lock().await; - ctrl.write_current_config_mode()?; - ctrl.set_power_states().map_err(|err| warn!("{err}")).ok(); - Ok(()) - } -} diff --git a/daemon/src/ctrl_platform.rs b/daemon/src/ctrl_platform.rs deleted file mode 100644 index 29b342cc..00000000 --- a/daemon/src/ctrl_platform.rs +++ /dev/null @@ -1,382 +0,0 @@ -use std::fs::OpenOptions; -use std::io::{Read, Write}; -use std::path::Path; -use std::process::Command; -use std::sync::Arc; - -use async_trait::async_trait; -use config_traits::StdConfig; -use log::{info, warn}; -use rog_platform::platform::{AsusPlatform, GpuMode}; -use rog_platform::supported::RogBiosSupportedFunctions; -use zbus::export::futures_util::lock::Mutex; -use zbus::{dbus_interface, Connection, SignalContext}; - -use crate::config::Config; -use crate::error::RogError; -use crate::{task_watch_item, CtrlTask, GetSupported}; - -const ZBUS_PATH: &str = "/org/asuslinux/Platform"; -const ASUS_POST_LOGO_SOUND: &str = - "/sys/firmware/efi/efivars/AsusPostLogoSound-607005d5-3f75-4b2e-98f0-85ba66797a3e"; - -#[derive(Clone)] -pub struct CtrlPlatform { - platform: AsusPlatform, - config: Arc>, -} - -impl GetSupported for CtrlPlatform { - type A = RogBiosSupportedFunctions; - - fn get_supported() -> Self::A { - let mut panel_overdrive = false; - let mut dgpu_disable = false; - let mut egpu_enable = false; - let mut gpu_mux = false; - - if let Ok(platform) = AsusPlatform::new() { - panel_overdrive = platform.has_panel_od(); - dgpu_disable = platform.has_dgpu_disable(); - egpu_enable = platform.has_egpu_enable(); - gpu_mux = platform.has_gpu_mux_mode(); - } - - RogBiosSupportedFunctions { - post_sound: Path::new(ASUS_POST_LOGO_SOUND).exists(), - gpu_mux, - panel_overdrive, - dgpu_disable, - egpu_enable, - } - } -} - -impl CtrlPlatform { - pub fn new(config: Arc>) -> Result { - let platform = AsusPlatform::new()?; - - if !platform.has_gpu_mux_mode() { - info!("G-Sync Switchable Graphics or GPU MUX not detected"); - info!("Standard graphics switching will still work."); - } - - if Path::new(ASUS_POST_LOGO_SOUND).exists() { - CtrlPlatform::set_path_mutable(ASUS_POST_LOGO_SOUND)?; - } else { - info!("Switch for POST boot sound not detected"); - } - - Ok(CtrlPlatform { platform, config }) - } - - fn set_path_mutable(path: &str) -> Result<(), RogError> { - let output = Command::new("/usr/bin/chattr") - .arg("-i") - .arg(path) - .output() - .map_err(|err| RogError::Path(path.into(), err))?; - info!("Set {} writeable: status: {}", path, output.status); - Ok(()) - } - - fn set_gfx_mode(&self, mode: GpuMode) -> Result<(), RogError> { - self.platform.set_gpu_mux_mode(mode.to_mux_attr())?; - // self.update_initramfs(enable)?; - if mode == GpuMode::Discrete { - info!("Set system-level graphics mode: Dedicated Nvidia"); - } else { - info!("Set system-level graphics mode: Optimus"); - } - Ok(()) - } - - pub fn get_boot_sound() -> Result { - let data = std::fs::read(ASUS_POST_LOGO_SOUND) - .map_err(|err| RogError::Read(ASUS_POST_LOGO_SOUND.into(), err))?; - - let idx = data.len() - 1; - Ok(data[idx] as i8) - } - - pub(super) fn set_boot_sound(on: bool) -> Result<(), RogError> { - let path = ASUS_POST_LOGO_SOUND; - let mut file = OpenOptions::new() - .read(true) - .write(true) - .open(path) - .map_err(|err| RogError::Path(path.into(), err))?; - - let mut data = Vec::new(); - #[allow(clippy::verbose_file_reads)] - file.read_to_end(&mut data) - .map_err(|err| RogError::Read(path.into(), err))?; - - let idx = data.len() - 1; - if on { - data[idx] = 1; - info!("Set boot POST sound on"); - } else { - data[idx] = 0; - info!("Set boot POST sound off"); - } - file.write_all(&data) - .map_err(|err| RogError::Path(path.into(), err))?; - - Ok(()) - } - - fn set_panel_overdrive(&self, enable: bool) -> Result<(), RogError> { - self.platform.set_panel_od(enable).map_err(|err| { - warn!("CtrlRogBios: set_panel_overdrive {}", err); - err - })?; - Ok(()) - } -} - -#[dbus_interface(name = "org.asuslinux.Daemon")] -impl CtrlPlatform { - async fn set_gpu_mux_mode( - &mut self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - mode: GpuMode, - ) { - self.set_gfx_mode(mode) - .map_err(|err| { - warn!("CtrlRogBios: set_gpu_mux_mode {}", err); - err - }) - .ok(); - Self::notify_gpu_mux_mode(&ctxt, mode).await.ok(); - } - - fn gpu_mux_mode(&self) -> GpuMode { - match self.platform.get_gpu_mux_mode() { - Ok(m) => GpuMode::from_mux(m as u8), - Err(e) => { - warn!("CtrlRogBios: get_gfx_mode {}", e); - GpuMode::Error - } - } - } - - #[dbus_interface(signal)] - async fn notify_gpu_mux_mode( - signal_ctxt: &SignalContext<'_>, - mode: GpuMode, - ) -> zbus::Result<()> { - } - - async fn set_post_boot_sound( - &mut self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - on: bool, - ) { - Self::set_boot_sound(on) - .map_err(|err| { - warn!("CtrlRogBios: set_post_boot_sound {}", err); - err - }) - .ok(); - Self::notify_post_boot_sound(&ctxt, on).await.ok(); - } - - fn post_boot_sound(&self) -> i8 { - Self::get_boot_sound() - .map_err(|err| { - warn!("CtrlRogBios: get_boot_sound {}", err); - err - }) - .unwrap_or(-1) - } - - #[dbus_interface(signal)] - async fn notify_post_boot_sound(ctxt: &SignalContext<'_>, on: bool) -> zbus::Result<()> {} - - async fn set_panel_od( - &mut self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - overdrive: bool, - ) { - match self.platform.set_panel_od(overdrive) { - Ok(_) => { - if let Some(mut lock) = self.config.try_lock() { - lock.panel_od = overdrive; - lock.write(); - } - Self::notify_panel_od(&ctxt, overdrive).await.ok(); - } - Err(err) => warn!("CtrlRogBios: set_panel_overdrive {}", err), - }; - } - - /// Get the `panel_od` value from platform. Updates the stored value in - /// internal config also. - fn panel_od(&self) -> bool { - let od = self - .platform - .get_panel_od() - .map_err(|err| { - warn!("CtrlRogBios: get_panel_od {}", err); - err - }) - .unwrap_or(false); - if let Some(mut lock) = self.config.try_lock() { - lock.panel_od = od; - lock.write(); - } - od - } - - #[dbus_interface(signal)] - async fn notify_panel_od(signal_ctxt: &SignalContext<'_>, overdrive: bool) -> zbus::Result<()> { - } - - async fn set_dgpu_disable( - &mut self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - disable: bool, - ) { - match self.platform.set_dgpu_disable(disable) { - Ok(_) => { - Self::notify_dgpu_disable(&ctxt, disable).await.ok(); - } - Err(err) => warn!("CtrlRogBios: set_dgpu_disable {}", err), - }; - } - - fn dgpu_disable(&self) -> bool { - self.platform - .get_dgpu_disable() - .map_err(|err| { - warn!("CtrlRogBios: get_dgpu_disable {}", err); - err - }) - .unwrap_or(false) - } - - #[dbus_interface(signal)] - async fn notify_dgpu_disable( - signal_ctxt: &SignalContext<'_>, - disable: bool, - ) -> zbus::Result<()> { - } - - async fn set_egpu_enable( - &mut self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - enable: bool, - ) { - match self.platform.set_egpu_enable(enable) { - Ok(_) => { - Self::notify_egpu_enable(&ctxt, enable).await.ok(); - } - Err(err) => warn!("CtrlRogBios: set_egpu_enable {}", err), - }; - } - - fn egpu_enable(&self) -> bool { - self.platform - .get_egpu_enable() - .map_err(|err| { - warn!("CtrlRogBios: get_egpu_enable {}", err); - err - }) - .unwrap_or(false) - } - - #[dbus_interface(signal)] - async fn notify_egpu_enable(signal_ctxt: &SignalContext<'_>, enable: bool) -> zbus::Result<()> { - } -} - -#[async_trait] -impl crate::ZbusRun for CtrlPlatform { - async fn add_to_server(self, server: &mut Connection) { - Self::add_to_server_helper(self, "/org/asuslinux/Platform", server).await; - } -} - -#[async_trait] -impl crate::Reloadable for CtrlPlatform { - async fn reload(&mut self) -> Result<(), RogError> { - if self.platform.has_panel_od() { - let p = if let Some(lock) = self.config.try_lock() { - lock.panel_od - } else { - false - }; - self.set_panel_overdrive(p)?; - } - Ok(()) - } -} - -impl CtrlPlatform { - task_watch_item!(panel_od platform); - - task_watch_item!(dgpu_disable platform); - - task_watch_item!(egpu_enable platform); - // NOTE: see note further below - // task_watch_item!(gpu_mux_mode platform); -} - -#[async_trait] -impl CtrlTask for CtrlPlatform { - fn zbus_path() -> &'static str { - ZBUS_PATH - } - - async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> { - let platform1 = self.clone(); - let platform2 = self.clone(); - self.create_sys_event_tasks( - move || async { {} }, - move || { - let platform1 = platform1.clone(); - async move { - info!("CtrlRogBios reloading panel_od"); - let lock = platform1.config.lock().await; - if platform1.platform.has_panel_od() { - platform1 - .set_panel_overdrive(lock.panel_od) - .map_err(|err| { - warn!("CtrlCharge: set_limit {}", err); - err - }) - .ok(); - } - } - }, - move || async { {} }, - move || { - let platform2 = platform2.clone(); - async move { - info!("CtrlRogBios reloading panel_od"); - let lock = platform2.config.lock().await; - if platform2.platform.has_panel_od() { - platform2 - .set_panel_overdrive(lock.panel_od) - .map_err(|err| { - warn!("CtrlCharge: set_limit {}", err); - err - }) - .ok(); - } - } - }, - ) - .await; - - self.watch_panel_od(signal_ctxt.clone()).await?; - self.watch_dgpu_disable(signal_ctxt.clone()).await?; - self.watch_egpu_enable(signal_ctxt.clone()).await?; - // NOTE: Can't have this as a watch because on a write to it, it reverts back to - // booted-with value as it does not actually change until reboot. - // self.watch_gpu_mux_mode(signal_ctxt.clone()).await?; - - Ok(()) - } -} diff --git a/daemon/src/ctrl_power.rs b/daemon/src/ctrl_power.rs deleted file mode 100644 index df8b6814..00000000 --- a/daemon/src/ctrl_power.rs +++ /dev/null @@ -1,287 +0,0 @@ -use std::process::Command; -use std::sync::Arc; -use std::time::Duration; - -use async_trait::async_trait; -use config_traits::StdConfig; -use log::{error, info, warn}; -use rog_platform::power::AsusPower; -use rog_platform::supported::ChargeSupportedFunctions; -use systemd_zbus::{ManagerProxy as SystemdProxy, Mode, UnitFileState}; -use tokio::time::sleep; -use zbus::export::futures_util::lock::Mutex; -use zbus::{dbus_interface, Connection, SignalContext}; - -use crate::config::Config; -use crate::error::RogError; -use crate::{task_watch_item, CtrlTask, GetSupported}; - -const ZBUS_PATH: &str = "/org/asuslinux/Power"; -const NVIDIA_POWERD: &str = "nvidia-powerd.service"; - -impl GetSupported for CtrlPower { - type A = ChargeSupportedFunctions; - - fn get_supported() -> Self::A { - ChargeSupportedFunctions { - charge_level_set: if let Ok(power) = AsusPower::new() { - power.has_charge_control_end_threshold() - } else { - false - }, - } - } -} - -#[derive(Clone)] -pub struct CtrlPower { - power: AsusPower, - config: Arc>, -} - -#[dbus_interface(name = "org.asuslinux.Daemon")] -impl CtrlPower { - async fn set_charge_control_end_threshold( - &mut self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - limit: u8, - ) -> zbus::fdo::Result<()> { - if !(20..=100).contains(&limit) { - return Err(RogError::ChargeLimit(limit))?; - } - self.set(limit) - .map_err(|err| { - warn!("CtrlCharge: set_limit {}", err); - err - }) - .ok(); - Self::notify_charge_control_end_threshold(&ctxt, limit) - .await - .ok(); - Ok(()) - } - - fn charge_control_end_threshold(&self) -> u8 { - loop { - if let Some(mut config) = self.config.try_lock() { - let limit = self - .power - .get_charge_control_end_threshold() - .map_err(|err| { - warn!("CtrlCharge: get_charge_control_end_threshold {}", err); - err - }) - .unwrap_or(100); - - config.read(); - config.bat_charge_limit = limit; - config.write(); - - return config.bat_charge_limit; - } - } - } - - fn mains_online(&self) -> bool { - if self.power.has_online() { - if let Ok(v) = self.power.get_online() { - return v == 1; - } - } - false - } - - #[dbus_interface(signal)] - async fn notify_charge_control_end_threshold( - ctxt: &SignalContext<'_>, - limit: u8, - ) -> zbus::Result<()>; - - #[dbus_interface(signal)] - async fn notify_mains_online(ctxt: &SignalContext<'_>, on: bool) -> zbus::Result<()>; -} - -#[async_trait] -impl crate::ZbusRun for CtrlPower { - async fn add_to_server(self, server: &mut Connection) { - Self::add_to_server_helper(self, ZBUS_PATH, server).await; - } -} - -#[async_trait] -impl crate::Reloadable for CtrlPower { - async fn reload(&mut self) -> Result<(), RogError> { - if let Some(mut config) = self.config.try_lock() { - config.read(); - self.set(config.bat_charge_limit)?; - } - Ok(()) - } -} - -impl CtrlPower { - task_watch_item!(charge_control_end_threshold power); - - pub fn new(config: Arc>) -> Result { - Ok(CtrlPower { - power: AsusPower::new()?, - config, - }) - } - - pub(super) fn set(&self, limit: u8) -> Result<(), RogError> { - if !(20..=100).contains(&limit) { - return Err(RogError::ChargeLimit(limit)); - } - - self.power.set_charge_control_end_threshold(limit)?; - - info!("Battery charge limit: {}", limit); - - if let Some(mut config) = self.config.try_lock() { - config.read(); - config.bat_charge_limit = limit; - config.write(); - } - - Ok(()) - } -} - -#[async_trait] -impl CtrlTask for CtrlPower { - fn zbus_path() -> &'static str { - ZBUS_PATH - } - - async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> { - let conn = zbus::Connection::system().await?; - let sysd1 = SystemdProxy::new(&conn).await?; - let sysd2 = sysd1.clone(); - let sysd3 = sysd1.clone(); - - let power1 = self.clone(); - let power2 = self.clone(); - self.create_sys_event_tasks( - move || async {}, - move || { - let power = power1.clone(); - let sysd = sysd1.clone(); - async move { - info!("CtrlCharge reloading charge limit"); - let lock = power.config.lock().await; - power - .set(lock.bat_charge_limit) - .map_err(|err| { - warn!("CtrlCharge: set_limit {}", err); - err - }) - .ok(); - - if lock.disable_nvidia_powerd_on_battery { - if let Ok(value) = power.power.get_online() { - do_nvidia_powerd_action(&sysd, value == 1).await; - } - } - } - }, - move || async {}, - move || { - let power = power2.clone(); - let sysd = sysd2.clone(); - async move { - info!("CtrlCharge reloading charge limit"); - let lock = power.config.lock().await; - power - .set(lock.bat_charge_limit) - .map_err(|err| { - warn!("CtrlCharge: set_limit {}", err); - err - }) - .ok(); - - if lock.disable_nvidia_powerd_on_battery { - if let Ok(value) = power.power.get_online() { - do_nvidia_powerd_action(&sysd, value == 1).await; - } - } - } - }, - ) - .await; - - let config = self.config.clone(); - self.watch_charge_control_end_threshold(signal_ctxt.clone()) - .await?; - - let ctrl = self.clone(); - tokio::spawn(async move { - let mut online = 10; - loop { - if let Ok(value) = ctrl.power.get_online() { - if online != value { - online = value; - let mut config = config.lock().await; - config.read(); - - if config.disable_nvidia_powerd_on_battery { - do_nvidia_powerd_action(&sysd3, value == 1).await; - } - - Self::notify_mains_online(&signal_ctxt, value == 1) - .await - .unwrap(); - - let mut prog: Vec<&str> = Vec::new(); - if value == 1 { - // AC ONLINE - prog = config.ac_command.split_whitespace().collect(); - } else if value == 0 { - // BATTERY - prog = config.bat_command.split_whitespace().collect(); - } - - if prog.len() > 1 { - let mut cmd = Command::new(prog[0]); - for arg in prog.iter().skip(1) { - cmd.arg(*arg); - } - if let Err(e) = cmd.spawn() { - if value == 1 { - error!("AC power command error: {e}"); - } else { - error!("Battery power command error: {e}"); - } - } - } - } - } - // The inotify doesn't pick up events when the kernel changes internal value - // so we need to watch it with a thread and sleep unfortunately - sleep(Duration::from_secs(1)).await; - } - }); - - Ok(()) - } -} - -async fn do_nvidia_powerd_action(proxy: &SystemdProxy<'_>, ac_on: bool) { - if let Ok(res) = proxy.get_unit_file_state(NVIDIA_POWERD).await { - if res == UnitFileState::Enabled { - if ac_on { - proxy - .start_unit(NVIDIA_POWERD, Mode::Replace) - .await - .map_err(|e| error!("Error stopping {NVIDIA_POWERD}, {e:?}")) - .ok(); - } else { - proxy - .stop_unit(NVIDIA_POWERD, Mode::Replace) - .await - .map_err(|e| error!("Error stopping {NVIDIA_POWERD}, {e:?}")) - .ok(); - } - } - } -} diff --git a/daemon/src/ctrl_profiles/config.rs b/daemon/src/ctrl_profiles/config.rs deleted file mode 100644 index a66f8a75..00000000 --- a/daemon/src/ctrl_profiles/config.rs +++ /dev/null @@ -1,60 +0,0 @@ -use std::path::PathBuf; - -use config_traits::{StdConfig, StdConfigLoad}; -use rog_profiles::fan_curve_set::FanCurveSet; -use rog_profiles::Profile; -use serde_derive::{Deserialize, Serialize}; - -use crate::CONFIG_PATH_BASE; - -const CONFIG_FILE: &str = "profile.ron"; -const CONFIG_FAN_FILE: &str = "fan_curves.ron"; - -#[derive(Deserialize, Serialize, Debug)] -pub struct ProfileConfig { - /// For restore on boot - pub active_profile: Profile, -} - -impl StdConfig for ProfileConfig { - fn new() -> Self { - Self { - active_profile: Profile::Balanced, - } - } - - fn config_dir() -> std::path::PathBuf { - PathBuf::from(CONFIG_PATH_BASE) - } - - fn file_name(&self) -> String { - CONFIG_FILE.to_owned() - } -} - -impl StdConfigLoad for ProfileConfig {} - -#[derive(Deserialize, Serialize, Debug, Default)] -pub struct FanCurveConfig { - pub balanced: FanCurveSet, - pub performance: FanCurveSet, - pub quiet: FanCurveSet, -} - -impl StdConfig for FanCurveConfig { - /// Create a new config. The defaults are zeroed so the device must be read - /// to get the actual device defaults. - fn new() -> Self { - Self::default() - } - - fn config_dir() -> std::path::PathBuf { - PathBuf::from(CONFIG_PATH_BASE) - } - - fn file_name(&self) -> String { - CONFIG_FAN_FILE.to_owned() - } -} - -impl StdConfigLoad for FanCurveConfig {} diff --git a/daemon/src/ctrl_profiles/controller.rs b/daemon/src/ctrl_profiles/controller.rs deleted file mode 100644 index b6752bc1..00000000 --- a/daemon/src/ctrl_profiles/controller.rs +++ /dev/null @@ -1,191 +0,0 @@ -use config_traits::{StdConfig, StdConfigLoad}; -use log::{info, warn}; -use rog_platform::platform::AsusPlatform; -use rog_platform::supported::PlatformProfileFunctions; -use rog_profiles::error::ProfileError; -use rog_profiles::{FanCurveProfiles, Profile}; - -use super::config::{FanCurveConfig, ProfileConfig}; -use crate::error::RogError; -use crate::GetSupported; - -// TODO: macro wrapper for warn/info/error log macros to add module name -const MOD_NAME: &str = "CtrlPlatformProfile"; - -pub struct FanCurves { - config_file: FanCurveConfig, - profiles: FanCurveProfiles, -} - -impl FanCurves { - pub fn update_profiles_from_config(&mut self) { - self.profiles.balanced = self.config_file.balanced.clone(); - self.profiles.performance = self.config_file.performance.clone(); - self.profiles.quiet = self.config_file.quiet.clone(); - } - - pub fn update_config_from_profiles(&mut self) { - self.config_file.balanced = self.profiles.balanced.clone(); - self.config_file.performance = self.profiles.performance.clone(); - self.config_file.quiet = self.profiles.quiet.clone(); - } - - pub fn profiles(&self) -> &FanCurveProfiles { - &self.profiles - } - - pub fn profiles_mut(&mut self) -> &mut FanCurveProfiles { - &mut self.profiles - } -} - -pub struct CtrlPlatformProfile { - pub profile_config: ProfileConfig, - pub fan_curves: Option, - pub platform: AsusPlatform, -} - -impl GetSupported for CtrlPlatformProfile { - type A = PlatformProfileFunctions; - - fn get_supported() -> Self::A { - if !Profile::is_platform_profile_supported() { - warn!( - "platform_profile kernel interface not found, your laptop does not support this, \ - or the interface is missing." - ); - } - - let res = FanCurveProfiles::is_supported(); - let mut fan_curve_supported = res.is_err(); - if let Ok(r) = res { - fan_curve_supported = r; - }; - - if !fan_curve_supported { - info!( - "fan curves kernel interface not found, your laptop does not support this, or the \ - interface is missing." - ); - } - - PlatformProfileFunctions { - platform_profile: Profile::is_platform_profile_supported(), - fan_curves: fan_curve_supported, - } - } -} - -impl CtrlPlatformProfile { - pub fn new(config: ProfileConfig) -> Result { - let platform = AsusPlatform::new()?; - if platform.has_platform_profile() || platform.has_throttle_thermal_policy() { - info!("{MOD_NAME}: Device has profile control available"); - - let mut controller = CtrlPlatformProfile { - profile_config: config, - fan_curves: None, - platform, - }; - if FanCurveProfiles::get_device().is_ok() { - info!("{MOD_NAME}: Device has fan curves available"); - let fan_config = FanCurveConfig::new(); - // Only do defaults if the config doesn't already exist - if !fan_config.file_path().exists() { - info!("{MOD_NAME}: Fetching default fan curves"); - controller.fan_curves = Some(FanCurves { - config_file: fan_config, - profiles: FanCurveProfiles::default(), - }); - for _ in [Profile::Balanced, Profile::Performance, Profile::Quiet] { - // For each profile we need to switch to it before we - // can read the existing values from hardware. The ACPI method used - // for this is what limits us. - controller.set_next_profile()?; - // Make sure to set the baseline to default - controller.set_active_curve_to_defaults()?; - let active = Profile::get_active_profile().unwrap_or(Profile::Balanced); - - if let Some(curves) = controller.fan_curves.as_ref() { - info!( - "{MOD_NAME}: {active:?}: {}", - String::from(curves.profiles().get_fan_curves_for(active)) - ); - } - } - if let Some(curves) = controller.fan_curves.as_ref() { - curves.config_file.write(); - } - } else { - info!("{MOD_NAME}: Fan curves previously stored, loading..."); - let mut fan_curves = FanCurves { - config_file: fan_config.load(), - profiles: FanCurveProfiles::default(), - }; - fan_curves.update_profiles_from_config(); - controller.fan_curves = Some(fan_curves); - } - } - - return Ok(controller); - } - - Err(ProfileError::NotSupported.into()) - } - - pub fn save_config(&mut self) { - self.profile_config.write(); - if let Some(fans) = self.fan_curves.as_mut() { - fans.update_config_from_profiles(); - fans.config_file.write(); // config write - } - } - - /// Toggle to next profile in list. This will first read the config, switch, - /// then write out - pub(super) fn set_next_profile(&mut self) -> Result<(), RogError> { - // Read first just incase the user has modified the config before calling this - match self.profile_config.active_profile { - Profile::Balanced => { - Profile::set_profile(Profile::Performance)?; - self.profile_config.active_profile = Profile::Performance; - } - Profile::Performance => { - Profile::set_profile(Profile::Quiet)?; - self.profile_config.active_profile = Profile::Quiet; - } - Profile::Quiet => { - Profile::set_profile(Profile::Balanced)?; - self.profile_config.active_profile = Profile::Balanced; - } - } - self.write_profile_curve_to_platform()?; - Ok(()) - } - - /// Set the curve for the active profile active - pub(super) fn write_profile_curve_to_platform(&mut self) -> Result<(), RogError> { - if let Some(curves) = &mut self.fan_curves { - if let Ok(mut device) = FanCurveProfiles::get_device() { - curves.profiles_mut().write_profile_curve_to_platform( - self.profile_config.active_profile, - &mut device, - )?; - } - } - Ok(()) - } - - pub(super) fn set_active_curve_to_defaults(&mut self) -> Result<(), RogError> { - if let Some(curves) = self.fan_curves.as_mut() { - if let Ok(mut device) = FanCurveProfiles::get_device() { - curves.profiles_mut().set_active_curve_to_defaults( - self.profile_config.active_profile, - &mut device, - )?; - curves.update_config_from_profiles(); - } - } - Ok(()) - } -} diff --git a/daemon/src/ctrl_profiles/mod.rs b/daemon/src/ctrl_profiles/mod.rs deleted file mode 100644 index d4212ff9..00000000 --- a/daemon/src/ctrl_profiles/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod config; -pub mod controller; -/// Implements `CtrlTask`, Reloadable, `ZbusRun` -pub mod trait_impls; diff --git a/daemon/src/ctrl_profiles/trait_impls.rs b/daemon/src/ctrl_profiles/trait_impls.rs deleted file mode 100644 index 76b5c41c..00000000 --- a/daemon/src/ctrl_profiles/trait_impls.rs +++ /dev/null @@ -1,311 +0,0 @@ -use std::str::FromStr; -use std::sync::Arc; - -use async_trait::async_trait; -use config_traits::StdConfig; -use log::{error, info, warn}; -use rog_profiles::fan_curve_set::{CurveData, FanCurveSet}; -use rog_profiles::{FanCurveProfiles, Profile}; -use zbus::export::futures_util::lock::Mutex; -use zbus::export::futures_util::StreamExt; -use zbus::fdo::Error; -use zbus::{dbus_interface, Connection, SignalContext}; - -use super::controller::CtrlPlatformProfile; -use crate::error::RogError; -use crate::CtrlTask; - -const MOD_NAME: &str = "ProfileZbus"; - -const ZBUS_PATH: &str = "/org/asuslinux/Profile"; -const UNSUPPORTED_MSG: &str = - "Fan curves are not supported on this laptop or you require a patched kernel"; - -#[derive(Clone)] -pub struct ProfileZbus(pub Arc>); - -#[dbus_interface(name = "org.asuslinux.Daemon")] -impl ProfileZbus { - /// Fetch profile names - fn profiles(&mut self) -> zbus::fdo::Result> { - if let Ok(profiles) = Profile::get_profile_names() { - return Ok(profiles); - } - Err(Error::Failed( - "Failed to get all profile details".to_owned(), - )) - } - - /// Toggle to next platform_profile. Names provided by `Profiles`. - /// If fan-curves are supported will also activate a fan curve for profile. - async fn next_profile(&mut self, #[zbus(signal_context)] ctxt: SignalContext<'_>) { - let mut ctrl = self.0.lock().await; - ctrl.set_next_profile() - .unwrap_or_else(|err| warn!("{MOD_NAME}: {}", err)); - ctrl.save_config(); - - Self::notify_profile(&ctxt, ctrl.profile_config.active_profile) - .await - .ok(); - } - - /// Fetch the active profile name - async fn active_profile(&mut self) -> zbus::fdo::Result { - let mut ctrl = self.0.lock().await; - ctrl.profile_config.read(); - Ok(ctrl.profile_config.active_profile) - } - - /// Set this platform_profile name as active - async fn set_active_profile( - &self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - profile: Profile, - ) { - let mut ctrl = self.0.lock().await; - // Read first just incase the user has modified the config before calling this - ctrl.profile_config.read(); - Profile::set_profile(profile) - .map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e)) - .ok(); - ctrl.profile_config.active_profile = profile; - ctrl.write_profile_curve_to_platform() - .map_err(|e| warn!("{MOD_NAME}: write_profile_curve_to_platform, {}", e)) - .ok(); - - ctrl.save_config(); - - Self::notify_profile(&ctxt, ctrl.profile_config.active_profile) - .await - .ok(); - } - - /// Get a list of profiles that have fan-curves enabled. - async fn enabled_fan_profiles(&mut self) -> zbus::fdo::Result> { - let mut ctrl = self.0.lock().await; - ctrl.profile_config.read(); - if let Some(curves) = &mut ctrl.fan_curves { - return Ok(curves.profiles().get_enabled_curve_profiles()); - } - Err(Error::Failed(UNSUPPORTED_MSG.to_owned())) - } - - /// Set a profile fan curve enabled status. Will also activate a fan curve - /// if in the same profile mode - async fn set_fan_curve_enabled( - &mut self, - profile: Profile, - enabled: bool, - ) -> zbus::fdo::Result<()> { - let mut ctrl = self.0.lock().await; - ctrl.profile_config.read(); - if let Some(curves) = &mut ctrl.fan_curves { - curves - .profiles_mut() - .set_profile_curve_enabled(profile, enabled); - - ctrl.write_profile_curve_to_platform() - .map_err(|e| warn!("{MOD_NAME}: write_profile_curve_to_platform, {}", e)) - .ok(); - - ctrl.save_config(); - Ok(()) - } else { - Err(Error::Failed(UNSUPPORTED_MSG.to_owned())) - } - } - - /// Get the fan-curve data for the currently active Profile - async fn fan_curve_data(&mut self, profile: Profile) -> zbus::fdo::Result { - let mut ctrl = self.0.lock().await; - ctrl.profile_config.read(); - if let Some(curves) = &mut ctrl.fan_curves { - let curve = curves.profiles().get_fan_curves_for(profile); - return Ok(curve.clone()); - } - Err(Error::Failed(UNSUPPORTED_MSG.to_owned())) - } - - /// Set the fan curve for the specified profile. - /// Will also activate the fan curve if the user is in the same mode. - async fn set_fan_curve(&self, profile: Profile, curve: CurveData) -> zbus::fdo::Result<()> { - let mut ctrl = self.0.lock().await; - ctrl.profile_config.read(); - if let Some(curves) = &mut ctrl.fan_curves { - curves - .profiles_mut() - .save_fan_curve(curve, profile) - .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; - } else { - return Err(Error::Failed(UNSUPPORTED_MSG.to_owned())); - } - ctrl.write_profile_curve_to_platform() - .map_err(|e| warn!("{MOD_NAME}: Profile::set_profile, {}", e)) - .ok(); - ctrl.save_config(); - - Ok(()) - } - - /// Reset the stored (self) and device curve to the defaults of the - /// platform. - /// - /// Each platform_profile has a different default and the defualt can be - /// read only for the currently active profile. - async fn set_active_curve_to_defaults(&self) -> zbus::fdo::Result<()> { - let mut ctrl = self.0.lock().await; - ctrl.profile_config.read(); - ctrl.set_active_curve_to_defaults() - .map_err(|e| warn!("{MOD_NAME}: Profile::set_active_curve_to_defaults, {}", e)) - .ok(); - ctrl.save_config(); - Ok(()) - } - - /// Reset the stored (self) and device curve to the defaults of the - /// platform. - /// - /// Each platform_profile has a different default and the defualt can be - /// read only for the currently active profile. - async fn reset_profile_curves(&self, profile: Profile) -> zbus::fdo::Result<()> { - let mut ctrl = self.0.lock().await; - ctrl.profile_config.read(); - let active = Profile::get_active_profile().unwrap_or(Profile::Balanced); - - Profile::set_profile(profile) - .map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e)) - .ok(); - ctrl.set_active_curve_to_defaults() - .map_err(|e| warn!("{MOD_NAME}: Profile::set_active_curve_to_defaults, {}", e)) - .ok(); - - Profile::set_profile(active) - .map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e)) - .ok(); - ctrl.save_config(); - Ok(()) - } - - #[dbus_interface(signal)] - async fn notify_profile(signal_ctxt: &SignalContext<'_>, profile: Profile) -> zbus::Result<()> { - } -} - -#[async_trait] -impl crate::ZbusRun for ProfileZbus { - async fn add_to_server(self, server: &mut Connection) { - Self::add_to_server_helper(self, ZBUS_PATH, server).await; - } -} - -#[async_trait] -impl CtrlTask for ProfileZbus { - fn zbus_path() -> &'static str { - ZBUS_PATH - } - - async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> { - let ctrl = self.0.clone(); - let sig_ctx = signal_ctxt.clone(); - let watch = self - .0 - .lock() - .await - .platform - .monitor_throttle_thermal_policy()?; - - tokio::spawn(async move { - let mut buffer = [0; 32]; - if let Ok(stream) = watch.into_event_stream(&mut buffer) { - stream - .for_each(|_| async { - let mut lock = ctrl.lock().await; - if let Ok(profile) = - lock.platform.get_throttle_thermal_policy().map_err(|e| { - error!("{MOD_NAME}: get_throttle_thermal_policy error: {e}"); - }) - { - let new_profile = Profile::from_throttle_thermal_policy(profile); - if new_profile != lock.profile_config.active_profile { - info!("{MOD_NAME}: platform_profile changed to {new_profile}"); - lock.profile_config.active_profile = new_profile; - lock.write_profile_curve_to_platform().unwrap(); - lock.save_config(); - Profile::set_profile(lock.profile_config.active_profile) - .map_err(|e| { - error!("Profile::set_profile() error: {e}"); - }) - .ok(); - } - Self::notify_profile(&sig_ctx, lock.profile_config.active_profile) - .await - .ok(); - } - }) - .await; - } - }); - - let ctrl = self.0.clone(); - let watch = self.0.lock().await.platform.monitor_platform_profile()?; - - tokio::spawn(async move { - let mut buffer = [0; 32]; - if let Ok(stream) = watch.into_event_stream(&mut buffer) { - stream - .for_each(|_| async { - let mut lock = ctrl.lock().await; - if let Ok(profile) = lock.platform.get_platform_profile().map_err(|e| { - error!("get_platform_profile error: {e}"); - }) { - if let Ok(new_profile) = Profile::from_str(&profile).map_err(|e| { - error!("Profile::from_str(&profile) error: {e}"); - }) { - if new_profile != lock.profile_config.active_profile { - info!("{MOD_NAME}: platform_profile changed to {new_profile}"); - lock.profile_config.active_profile = new_profile; - lock.write_profile_curve_to_platform().unwrap(); - lock.save_config(); - Profile::set_profile(lock.profile_config.active_profile) - .map_err(|e| { - error!("Profile::set_profile() error: {e}"); - }) - .ok(); - } - Self::notify_profile( - &signal_ctxt, - lock.profile_config.active_profile, - ) - .await - .ok(); - } - } - }) - .await; - } - }); - - Ok(()) - } -} - -#[async_trait] -impl crate::Reloadable for ProfileZbus { - /// Fetch the active profile and use that to set all related components up - async fn reload(&mut self) -> Result<(), RogError> { - let mut ctrl = self.0.lock().await; - let active = ctrl.profile_config.active_profile; - if let Some(curves) = &mut ctrl.fan_curves { - if let Ok(mut device) = FanCurveProfiles::get_device() { - // There is a possibility that the curve was default zeroed, so this call - // initialises the data from system read and we need to save it - // after - curves - .profiles_mut() - .write_profile_curve_to_platform(active, &mut device)?; - ctrl.profile_config.write(); - } - } - Ok(()) - } -} diff --git a/daemon/src/ctrl_supported.rs b/daemon/src/ctrl_supported.rs deleted file mode 100644 index 3a94db75..00000000 --- a/daemon/src/ctrl_supported.rs +++ /dev/null @@ -1,42 +0,0 @@ -use async_trait::async_trait; -use serde_derive::{Deserialize, Serialize}; -use zbus::zvariant::Type; -use zbus::{dbus_interface, Connection}; - -use crate::ctrl_anime::CtrlAnime; -use crate::ctrl_aura::controller::CtrlKbdLed; -use crate::ctrl_platform::CtrlPlatform; -use crate::ctrl_power::CtrlPower; -use crate::ctrl_profiles::controller::CtrlPlatformProfile; -use crate::GetSupported; - -#[derive(Serialize, Deserialize, Debug, Type)] -pub struct SupportedFunctions(rog_platform::supported::SupportedFunctions); - -#[dbus_interface(name = "org.asuslinux.Daemon")] -impl SupportedFunctions { - pub fn supported_functions(&self) -> &rog_platform::supported::SupportedFunctions { - &self.0 - } -} - -#[async_trait] -impl crate::ZbusRun for SupportedFunctions { - async fn add_to_server(self, server: &mut Connection) { - Self::add_to_server_helper(self, "/org/asuslinux/Supported", server).await; - } -} - -impl GetSupported for SupportedFunctions { - type A = SupportedFunctions; - - fn get_supported() -> Self::A { - Self(rog_platform::supported::SupportedFunctions { - anime_ctrl: CtrlAnime::get_supported(), - keyboard_led: CtrlKbdLed::get_supported(), - charge_ctrl: CtrlPower::get_supported(), - platform_profile: CtrlPlatformProfile::get_supported(), - rog_bios_ctrl: CtrlPlatform::get_supported(), - }) - } -} diff --git a/daemon/src/daemon.rs b/daemon/src/daemon.rs deleted file mode 100644 index d7888251..00000000 --- a/daemon/src/daemon.rs +++ /dev/null @@ -1,165 +0,0 @@ -use std::env; -use std::error::Error; -use std::io::Write; -use std::sync::Arc; -use std::time::Duration; - -use ::zbus::export::futures_util::lock::Mutex; -use ::zbus::Connection; -use config_traits::{StdConfig, StdConfigLoad, StdConfigLoad2}; -use daemon::config::Config; -use daemon::ctrl_anime::config::AnimeConfig; -use daemon::ctrl_anime::trait_impls::CtrlAnimeZbus; -use daemon::ctrl_anime::CtrlAnime; -use daemon::ctrl_aura::controller::CtrlKbdLed; -use daemon::ctrl_aura::trait_impls::CtrlKbdLedZbus; -use daemon::ctrl_platform::CtrlPlatform; -use daemon::ctrl_power::CtrlPower; -use daemon::ctrl_profiles::config::ProfileConfig; -use daemon::ctrl_profiles::controller::CtrlPlatformProfile; -use daemon::ctrl_profiles::trait_impls::ProfileZbus; -use daemon::ctrl_supported::SupportedFunctions; -use daemon::{print_board_info, CtrlTask, GetSupported, Reloadable, ZbusRun}; -use log::{error, info, warn}; -use rog_aura::aura_detection::LaptopLedData; -use rog_dbus::DBUS_NAME; -use rog_profiles::Profile; -use tokio::time::sleep; -use zbus::SignalContext; - -#[tokio::main] -async 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(); - - let is_service = match env::var_os("IS_SERVICE") { - Some(val) => val == "1", - None => false, - }; - - if !is_service { - println!("asusd schould be only run from the right systemd service"); - println!( - "do not run in your terminal, if you need an logs please use journalctl -b -u asusd" - ); - println!("asusd will now exit"); - return Ok(()); - } - - info!(" daemon v{}", daemon::VERSION); - info!(" rog-anime v{}", rog_anime::VERSION); - info!(" rog-aura v{}", rog_aura::VERSION); - info!(" rog-dbus v{}", rog_dbus::VERSION); - info!(" rog-profiles v{}", rog_profiles::VERSION); - info!("rog-platform v{}", rog_platform::VERSION); - - start_daemon().await?; - Ok(()) -} - -/// The actual main loop for the daemon -async fn start_daemon() -> Result<(), Box> { - let supported = SupportedFunctions::get_supported(); - print_board_info(); - println!("{}", supported.supported_functions()); - - // Start zbus server - let mut connection = Connection::system().await?; - - let config = Config::new().load(); - let config = Arc::new(Mutex::new(config)); - - supported.add_to_server(&mut connection).await; - - match CtrlPlatform::new(config.clone()) { - Ok(ctrl) => { - let sig_ctx = CtrlPlatform::signal_context(&connection)?; - start_tasks(ctrl, &mut connection, sig_ctx).await?; - } - Err(err) => { - error!("CtrlPlatform: {}", err); - } - } - - match CtrlPower::new(config.clone()) { - Ok(ctrl) => { - let sig_ctx = CtrlPower::signal_context(&connection)?; - start_tasks(ctrl, &mut connection, sig_ctx).await?; - } - Err(err) => { - error!("CtrlPower: {}", err); - } - } - - if Profile::is_platform_profile_supported() { - let profile_config = ProfileConfig::new().load(); - match CtrlPlatformProfile::new(profile_config) { - Ok(ctrl) => { - let zbus = ProfileZbus(Arc::new(Mutex::new(ctrl))); - let sig_ctx = ProfileZbus::signal_context(&connection)?; - start_tasks(zbus, &mut connection, sig_ctx).await?; - } - Err(err) => { - error!("Profile control: {}", err); - } - } - } else { - warn!("platform_profile support not found"); - } - - match CtrlAnime::new(AnimeConfig::new().load()) { - Ok(ctrl) => { - let zbus = CtrlAnimeZbus(Arc::new(Mutex::new(ctrl))); - let sig_ctx = CtrlAnimeZbus::signal_context(&connection)?; - start_tasks(zbus, &mut connection, sig_ctx).await?; - } - Err(err) => { - info!("AniMe control: {}", err); - } - } - - let laptop = LaptopLedData::get_data(); - // CtrlKbdLed deviates from the config pattern above due to requiring a keyboard - // detection first - match CtrlKbdLed::new(laptop) { - Ok(ctrl) => { - let zbus = CtrlKbdLedZbus(Arc::new(Mutex::new(ctrl))); - let sig_ctx = CtrlKbdLedZbus::signal_context(&connection)?; - start_tasks(zbus, &mut connection, sig_ctx).await?; - } - Err(err) => { - error!("Keyboard control: {}", err); - } - } - - // Request dbus name after finishing initalizing all functions - connection.request_name(DBUS_NAME).await?; - - loop { - // This is just a blocker to idle and ensure the reator reacts - sleep(Duration::from_millis(1000)).await; - } -} - -async fn start_tasks( - mut zbus: T, - connection: &mut Connection, - signal_ctx: SignalContext<'static>, -) -> Result<(), Box> -where - T: ZbusRun + Reloadable + CtrlTask + Clone, -{ - let task = zbus.clone(); - - zbus.reload() - .await - .unwrap_or_else(|err| warn!("Controller error: {}", err)); - zbus.add_to_server(connection).await; - - task.create_tasks(signal_ctx).await.ok(); - Ok(()) -} diff --git a/daemon/src/error.rs b/daemon/src/error.rs deleted file mode 100644 index 64ef8a0f..00000000 --- a/daemon/src/error.rs +++ /dev/null @@ -1,135 +0,0 @@ -use std::convert::From; -use std::fmt; - -use config_traits::ron; -use rog_anime::error::AnimeError; -use rog_platform::error::PlatformError; -use rog_profiles::error::ProfileError; - -#[derive(Debug)] -pub enum RogError { - ParseVendor, - ParseLed, - MissingProfile(String), - Udev(String, std::io::Error), - Path(String, std::io::Error), - Read(String, std::io::Error), - Write(String, std::io::Error), - NotSupported, - NotFound(String), - DoTask(String), - MissingFunction(String), - MissingLedBrightNode(String, std::io::Error), - ReloadFail(String), - Profiles(ProfileError), - Initramfs(String), - Modprobe(String), - Io(std::io::Error), - Zbus(zbus::Error), - ChargeLimit(u8), - AuraEffectNotSupported, - NoAuraKeyboard, - NoAuraNode, - Anime(AnimeError), - Platform(PlatformError), - SystemdUnitAction(String), - SystemdUnitWaitTimeout(String), - Command(String, std::io::Error), - ParseRon(ron::Error), -} - -impl fmt::Display for RogError { - // This trait requires `fmt` with this exact signature. - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - RogError::ParseVendor => write!(f, "Parse gfx vendor error"), - RogError::ParseLed => write!(f, "Parse LED error"), - RogError::MissingProfile(profile) => write!(f, "Profile does not exist {}", profile), - RogError::Udev(deets, error) => write!(f, "udev {}: {}", deets, error), - RogError::Path(path, error) => write!(f, "Path {}: {}", path, error), - RogError::Read(path, error) => write!(f, "Read {}: {}", path, error), - RogError::Write(path, error) => write!(f, "Write {}: {}", path, error), - RogError::NotSupported => write!(f, "Not supported"), - RogError::NotFound(deets) => write!(f, "Not found: {}", deets), - RogError::DoTask(deets) => write!(f, "Task error: {}", deets), - RogError::MissingFunction(deets) => write!(f, "Missing functionality: {}", deets), - RogError::MissingLedBrightNode(path, error) => write!( - f, - "Led node at {} is missing, please check you have the required patch or dkms \ - module installed: {}", - path, error - ), - RogError::ReloadFail(deets) => write!(f, "Reload error: {}", deets), - RogError::Profiles(deets) => write!(f, "Profile error: {}", deets), - RogError::Initramfs(detail) => write!(f, "Initiramfs error: {}", detail), - RogError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail), - RogError::Io(detail) => write!(f, "std::io error: {}", detail), - RogError::Zbus(detail) => write!(f, "Zbus error: {}", detail), - RogError::ChargeLimit(value) => { - write!(f, "Invalid charging limit, not in range 20-100%: {}", value) - } - RogError::AuraEffectNotSupported => write!(f, "Aura effect not supported"), - RogError::NoAuraKeyboard => write!(f, "No supported Aura keyboard"), - RogError::NoAuraNode => write!(f, "No Aura keyboard node found"), - RogError::Anime(deets) => write!(f, "AniMe Matrix error: {}", deets), - RogError::Platform(deets) => write!(f, "Asus Platform error: {}", deets), - RogError::SystemdUnitAction(action) => { - write!(f, "systemd unit action {} failed", action) - } - RogError::SystemdUnitWaitTimeout(state) => { - write!( - f, - "Timed out waiting for systemd unit change {} state", - state - ) - } - RogError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error), - RogError::ParseRon(error) => write!(f, "Parse config error: {}", error), - } - } -} - -impl std::error::Error for RogError {} - -impl From for RogError { - fn from(err: ProfileError) -> Self { - RogError::Profiles(err) - } -} - -impl From for RogError { - fn from(err: AnimeError) -> Self { - RogError::Anime(err) - } -} - -impl From for RogError { - fn from(err: PlatformError) -> Self { - RogError::Platform(err) - } -} - -impl From for RogError { - fn from(err: zbus::Error) -> Self { - RogError::Zbus(err) - } -} - -impl From for RogError { - fn from(err: std::io::Error) -> Self { - RogError::Io(err) - } -} - -impl From for RogError { - fn from(err: ron::Error) -> Self { - RogError::ParseRon(err) - } -} - -impl From for zbus::fdo::Error { - #[inline] - fn from(err: RogError) -> Self { - zbus::fdo::Error::Failed(format!("{}", err)) - } -} diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs deleted file mode 100644 index 1292e11f..00000000 --- a/daemon/src/lib.rs +++ /dev/null @@ -1,229 +0,0 @@ -#![deny(unused_must_use)] -/// Configuration loading, saving -pub mod config; -/// Control of anime matrix display -pub mod ctrl_anime; -/// Keyboard LED brightness control, RGB, and LED display modes -pub mod ctrl_aura; -/// Control ASUS bios function such as boot sound, Optimus/Dedicated gfx mode -pub mod ctrl_platform; -/// Control of battery charge level -pub mod ctrl_power; -/// Control platform profiles + fan-curves if available -pub mod ctrl_profiles; - -/// Fetch all supported functions for the laptop -pub mod ctrl_supported; - -pub mod error; - -use std::future::Future; - -use async_trait::async_trait; -use log::{debug, info, warn}; -use logind_zbus::manager::ManagerProxy; -use zbus::export::futures_util::StreamExt; -use zbus::zvariant::ObjectPath; -use zbus::{Connection, SignalContext}; - -use crate::error::RogError; - -const CONFIG_PATH_BASE: &str = "/etc/asusd/"; - -/// This macro adds a function which spawns an `inotify` task on the passed in -/// `Executor`. -/// -/// The generated function is `watch_()`. Self requires the following -/// methods to be available: -/// - `() -> SomeValue`, functionally is a getter, but is allowed to have -/// side effects. -/// - `notify_(SignalContext, SomeValue)` -/// -/// In most cases if `SomeValue` is stored in a config then `()` getter is -/// expected to update it. The getter should *never* write back to the path or -/// attribute that is being watched or an infinite loop will occur. -/// -/// # Example -/// -/// ```ignore -/// impl CtrlRogBios { -/// task_watch_item!(panel_od platform); -/// task_watch_item!(gpu_mux_mode platform); -/// } -/// ``` -#[macro_export] -macro_rules! task_watch_item { - ($name:ident $self_inner:ident) => { - concat_idents::concat_idents!(fn_name = watch_, $name { - async fn fn_name( - &self, - signal_ctxt: SignalContext<'static>, - ) -> Result<(), RogError> { - use zbus::export::futures_util::StreamExt; - - let ctrl = self.clone(); - concat_idents::concat_idents!(watch_fn = monitor_, $name { - match self.$self_inner.watch_fn() { - Ok(watch) => { - tokio::spawn(async move { - let mut buffer = [0; 32]; - watch.into_event_stream(&mut buffer).unwrap().for_each(|_| async { - let value = ctrl.$name(); - concat_idents::concat_idents!(notif_fn = notify_, $name { - Self::notif_fn(&signal_ctxt, value).await.ok(); - }); - }).await; - }); - } - Err(e) => info!("inotify watch failed: {}. You can ignore this if your device does not support the feature", e), - } - }); - Ok(()) - } - }); - }; -} - -pub const VERSION: &str = env!("CARGO_PKG_VERSION"); - -pub fn print_board_info() { - let dmi = sysfs_class::DmiId::default(); - let board_name = dmi.board_name().expect("Could not get board_name"); - let prod_family = dmi.product_family().expect("Could not get product_family"); - - info!("Product family: {}", prod_family.trim()); - info!("Board name: {}", board_name.trim()); -} - -#[async_trait] -pub trait Reloadable { - async fn reload(&mut self) -> Result<(), RogError>; -} - -#[async_trait] -pub trait ZbusRun { - async fn add_to_server(self, server: &mut Connection); - - async fn add_to_server_helper( - iface: impl zbus::Interface, - path: &str, - server: &mut Connection, - ) { - server - .object_server() - .at(&ObjectPath::from_str_unchecked(path), iface) - .await - .map_err(|err| { - warn!("{}: add_to_server {}", path, err); - err - }) - .ok(); - } -} - -/// Set up a task to run on the async executor -#[async_trait] -pub trait CtrlTask { - fn zbus_path() -> &'static str; - - fn signal_context(connection: &Connection) -> Result, zbus::Error> { - SignalContext::new(connection, Self::zbus_path()) - } - - /// Implement to set up various tasks that may be required, using the - /// `Executor`. No blocking loops are allowed, or they must be run on a - /// separate thread. - async fn create_tasks(&self, signal: SignalContext<'static>) -> Result<(), RogError>; - - // /// Create a timed repeating task - // async fn repeating_task(&self, millis: u64, mut task: impl FnMut() + Send + - // 'static) { use std::time::Duration; - // use tokio::time; - // let mut timer = time::interval(Duration::from_millis(millis)); - // tokio::spawn(async move { - // timer.tick().await; - // task(); - // }); - // } - - /// Free helper method to create tasks to run on: sleep, wake, shutdown, - /// boot - /// - /// The closures can potentially block, so execution time should be the - /// minimal possible such as save a variable. - async fn create_sys_event_tasks< - Fut1, - Fut2, - Fut3, - Fut4, - F1: Send + 'static, - F2: Send + 'static, - F3: Send + 'static, - F4: Send + 'static, - >( - &self, - mut on_sleep: F1, - mut on_wake: F2, - mut on_shutdown: F3, - mut on_boot: F4, - ) where - F1: FnMut() -> Fut1, - F2: FnMut() -> Fut2, - F3: FnMut() -> Fut3, - F4: FnMut() -> Fut4, - Fut1: Future + Send, - Fut2: Future + Send, - Fut3: Future + Send, - Fut4: Future + Send, - { - let connection = Connection::system() - .await - .expect("Controller could not create dbus connection"); - - let manager = ManagerProxy::new(&connection) - .await - .expect("Controller could not create ManagerProxy"); - - tokio::spawn(async move { - if let Ok(mut notif) = manager.receive_prepare_for_sleep().await { - while let Some(event) = notif.next().await { - if let Ok(args) = event.args() { - if args.start { - debug!("Doing on_sleep()"); - on_sleep().await; - } else if !args.start() { - debug!("Doing on_wake()"); - on_wake().await; - } - } - } - } - }); - - let manager = ManagerProxy::new(&connection) - .await - .expect("Controller could not create ManagerProxy"); - - tokio::spawn(async move { - if let Ok(mut notif) = manager.receive_prepare_for_shutdown().await { - while let Some(event) = notif.next().await { - if let Ok(args) = event.args() { - if args.start { - debug!("Doing on_shutdown()"); - on_shutdown().await; - } else if !args.start() { - debug!("Doing on_boot()"); - on_boot().await; - } - } - } - } - }); - } -} - -pub trait GetSupported { - type A; - - fn get_supported() -> Self::A; -} diff --git a/rog-anime/src/data.rs b/rog-anime/src/data.rs index dfdc8bf8..588c995e 100644 --- a/rog-anime/src/data.rs +++ b/rog-anime/src/data.rs @@ -57,9 +57,8 @@ impl AnimeType { /// The width of diagonal images pub fn width(&self) -> usize { match self { - AnimeType::GA401 | AnimeType::GA402 => 74, AnimeType::GU604 => 70, - AnimeType::Unknown => 0, + _ => 74, } } @@ -67,9 +66,8 @@ impl AnimeType { pub fn height(&self) -> usize { match self { AnimeType::GA401 => 36, - AnimeType::GA402 => 39, AnimeType::GU604 => 43, - AnimeType::Unknown => 0, + _ => 39, } } @@ -77,9 +75,8 @@ impl AnimeType { pub fn data_length(&self) -> usize { match self { AnimeType::GA401 => PANE_LEN * 2, - AnimeType::GA402 => PANE_LEN * 3, AnimeType::GU604 => PANE_LEN * 3, - AnimeType::Unknown => 0, + _ => PANE_LEN * 3, } } } diff --git a/rog-anime/src/diagonal.rs b/rog-anime/src/diagonal.rs index 1123ff58..2e6527b0 100644 --- a/rog-anime/src/diagonal.rs +++ b/rog-anime/src/diagonal.rs @@ -140,9 +140,8 @@ impl AnimeDiagonal { pub fn into_data_buffer(&self, anime_type: AnimeType) -> Result { match anime_type { AnimeType::GA401 => self.to_ga401_packets(), - AnimeType::GA402 => self.to_ga402_packets(), AnimeType::GU604 => self.to_gu604_packets(), - AnimeType::Unknown => self.to_unknown_packets(), + _ => self.to_ga402_packets(), } } @@ -377,9 +376,4 @@ impl AnimeDiagonal { AnimeDataBuffer::from_vec(crate::AnimeType::GA402, buf) } - - fn to_unknown_packets(&self) -> Result { - let buf = vec![0u8; AnimeType::Unknown.data_length()]; - AnimeDataBuffer::from_vec(crate::AnimeType::Unknown, buf) - } } diff --git a/rog-anime/src/image.rs b/rog-anime/src/image.rs index 82cf5ddb..ddfa1ee0 100644 --- a/rog-anime/src/image.rs +++ b/rog-anime/src/image.rs @@ -119,9 +119,8 @@ impl AnimeImage { fn scale_x(anime_type: AnimeType) -> f32 { match anime_type { AnimeType::GA401 => 0.8, - AnimeType::GA402 => 0.77, AnimeType::GU604 => 0.78, - AnimeType::Unknown => 0.0, + _ => 0.77, } } @@ -137,9 +136,8 @@ impl AnimeImage { fn scale_y(anime_type: AnimeType) -> f32 { match anime_type { AnimeType::GA401 => 0.3, - AnimeType::GA402 => 0.283, AnimeType::GU604 => 0.28, - AnimeType::Unknown => 0.0, + _ => 0.283, } } @@ -172,14 +170,6 @@ impl AnimeImage { } (y + 1) / 2 - 3 } - AnimeType::GA402 => { - // first 11 rows start at zero - if y <= 11 { - return 0; - } - // and then their offset grows by one every two rows - (y + 1) / 2 - 5 - } AnimeType::GU604 => { // first 9 rows start at zero if y <= 9 { @@ -188,7 +178,14 @@ impl AnimeImage { // and then their offset grows by one every two rows (y - 9) / 2 } - AnimeType::Unknown => 0, + _ => { + // first 11 rows start at zero + if y <= 11 { + return 0; + } + // and then their offset grows by one every two rows + (y + 1) / 2 - 5 + } } } @@ -217,19 +214,18 @@ impl AnimeImage { } 36 - (y + 1) / 2 } - AnimeType::GA402 => { - if y <= 11 { - return 34; - } - 39 - y / 2 - } AnimeType::GU604 => { if y <= 9 { return 38 + y % 2; } 38 - Self::first_x(anime_type, y) + y % 2 } - AnimeType::Unknown => 0, + _ => { + if y <= 11 { + return 34; + } + 39 - y / 2 + } } } @@ -238,9 +234,9 @@ impl AnimeImage { match anime_type { // 33.0 = Longest row LED count (physical) plus half-pixel offset AnimeType::GA401 => (33.0 + 0.5) * Self::scale_x(anime_type), - AnimeType::GA402 => (35.0 + 0.5) * Self::scale_x(anime_type), + AnimeType::GU604 => (38.0 + 0.5) * Self::scale_x(anime_type), - AnimeType::Unknown => 0.0, + _ => (35.0 + 0.5) * Self::scale_x(anime_type), } } @@ -248,9 +244,8 @@ impl AnimeImage { fn height(anime_type: AnimeType) -> u32 { match anime_type { AnimeType::GA401 => 55, - AnimeType::GA402 => 61, AnimeType::GU604 => 62, - AnimeType::Unknown => 0, + _ => 61, } } @@ -259,10 +254,9 @@ impl AnimeImage { match anime_type { // 54.0 = End column LED count (physical) plus one dead pixel AnimeType::GA401 => (54.0 + 1.0) * Self::scale_y(anime_type), - // GA402 may not have dead pixels and require only the physical LED count - AnimeType::GA402 => 61.0 * Self::scale_y(anime_type), AnimeType::GU604 => 62.0 * Self::scale_y(anime_type), - AnimeType::Unknown => 0.0, + // GA402 may not have dead pixels and require only the physical LED count + _ => 61.0 * Self::scale_y(anime_type), } } @@ -274,10 +268,9 @@ impl AnimeImage { 1 | 3 => 35, // Some rows are padded _ => 36 - y / 2, }, - // GA402 does not have padding, equivalent to width - AnimeType::GA402 => AnimeImage::width(anime_type, y), AnimeType::GU604 => AnimeImage::width(anime_type, y), - AnimeType::Unknown => 0, + // GA402 does not have padding, equivalent to width + _ => AnimeImage::width(anime_type, y), } } diff --git a/rog-aura/src/effects/doom.rs b/rog-aura/src/effects/doom.rs index e0bf73a7..e1b613e4 100644 --- a/rog-aura/src/effects/doom.rs +++ b/rog-aura/src/effects/doom.rs @@ -84,14 +84,6 @@ impl EffectState for DoomFlicker { } } -pub struct LightFlash { - pub count: i32, - pub max_light: i32, - pub min_light: i32, - pub max_time: i32, - pub min_time: i32, -} - #[derive(Debug, Clone, Deserialize, Serialize)] pub struct DoomLightFlash { led: LedCode, diff --git a/rog-control-center/Cargo.toml b/rog-control-center/Cargo.toml index 33b2f7a5..c4778c0f 100644 --- a/rog-control-center/Cargo.toml +++ b/rog-control-center/Cargo.toml @@ -18,7 +18,7 @@ eframe = { git = "https://github.com/emilk/egui", rev = "b8e798777de519de3a18787 libappindicator = "0.8" # Tray icon gtk = "0.16" -daemon = { path = "../daemon" } +asusd = { path = "../asusd" } rog_anime = { path = "../rog-anime" } rog_dbus = { path = "../rog-dbus" } rog_aura = { path = "../rog-aura" } diff --git a/rog-control-center/src/lib.rs b/rog-control-center/src/lib.rs index ccaae778..dba2601c 100644 --- a/rog-control-center/src/lib.rs +++ b/rog-control-center/src/lib.rs @@ -33,7 +33,7 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub fn print_versions() { println!("App and daemon versions:"); println!(" rog-gui v{}", VERSION); - println!(" asusd v{}", daemon::VERSION); + println!(" asusd v{}", asusd::VERSION); println!("\nComponent crate versions:"); println!(" rog-anime v{}", rog_anime::VERSION); println!(" rog-aura v{}", rog_aura::VERSION); diff --git a/rog-platform/src/hid_raw.rs b/rog-platform/src/hid_raw.rs index 1fc4ee12..1ca2bb02 100644 --- a/rog-platform/src/hid_raw.rs +++ b/rog-platform/src/hid_raw.rs @@ -42,7 +42,6 @@ impl HidRaw { } else { // Try to see if there is a virtual device created with uhid for testing let dev_path = device.devpath().to_string_lossy(); - dbg!(&dev_path); if dev_path.contains("virtual") && dev_path.contains(&id_product.to_uppercase()) { if let Some(dev_node) = device.devnode() { info!("Using device at: {:?} for asdfgsadfgh control", dev_node); diff --git a/simulators/src/simulator.rs b/simulators/src/simulator.rs index d7ddf4ce..e04c0595 100644 --- a/simulators/src/simulator.rs +++ b/simulators/src/simulator.rs @@ -10,23 +10,22 @@ use sdl2::pixels::Color; use sdl2::rect::Rect; use uhid_virt::{Bus, CreateParams, UHIDDevice}; +mod animatrix; +use animatrix::*; + pub struct VirtAnimeMatrix { device: UHIDDevice, buffer: [u8; 640], time: Instant, -} - -impl Default for VirtAnimeMatrix { - fn default() -> Self { - Self::new() - } + animatrix: AniMatrix, } impl VirtAnimeMatrix { - pub fn new() -> Self { + pub fn new(model: Model) -> Self { VirtAnimeMatrix { time: Instant::now(), buffer: [0; 640], + animatrix: AniMatrix::new(model), device: UHIDDevice::create(CreateParams { name: String::from("ROG_Virtual Anime Matrix"), phys: String::from(""), @@ -146,33 +145,35 @@ fn main() -> Result<(), Box> { let mut canvas = window.into_canvas().build().unwrap(); - let mut dev = VirtAnimeMatrix::new(); + let mut dev = VirtAnimeMatrix::new(Model::GA402); + canvas.set_draw_color(Color::RGB(0, 0, 0)); + canvas.clear(); let mut event_pump = sdl_context.event_pump().unwrap(); 'running: loop { - canvas.set_draw_color(Color::RGB(0, 0, 0)); - canvas.clear(); - dev.read(); // it's blocking, and damned hard to sync with arc/mutex - let one = dev.buffer[0..7] != USB_PREFIX2; + // let one = dev.buffer[0..7] != USB_PREFIX2; + let index = dev.buffer[3]; + println!("{:02x}", index); - for (mut i, b) in dev.buffer.iter().skip(7).enumerate() { - if !one { - i += 640; - }; - if *b == 0 { - continue; + let w = dev.animatrix.led_shape().horizontal * 6; + let h = dev.animatrix.led_shape().vertical * 6; + for (y_count, row) in dev.animatrix.rows().iter().enumerate() { + if row.0 == index { + let start = row.1; + let end = start + row.2; + for (x_count, b) in dev.buffer[start..end].iter().enumerate() { + print!("{b},"); + canvas.set_draw_color(Color::RGB(*b as u8, *b as u8, *b as u8)); + + let x = x_count as i32 * w - if y_count % 2 == 0 { 0 } else { w / 2 }; + let y = y_count as i32 * h; + canvas + .fill_rect(Rect::new(x, y, w as u32, h as u32)) + .unwrap(); + } + println!(); } - canvas.set_draw_color(Color::RGB(*b as u8, *b as u8, *b as u8)); - let y = (i / 33) % 55; - let x = (i % 33) as i32 * 20; - // if i % 2 == 0 { - // x /= 2; - // } - dbg!(i, i / 33); - canvas - .fill_rect(Rect::new(x, y as i32 * 20, 20, 20)) - .unwrap(); } for event in event_pump.poll_iter() {