From e515741efae6c7b81b332330ab0ff0d7c6445113 Mon Sep 17 00:00:00 2001 From: Luke D Jones Date: Sat, 10 Apr 2021 14:21:22 +1200 Subject: [PATCH] anime: add zbus methods --- CHANGELOG.md | 1 + Cargo.lock | 5 + asus-notify/src/main.rs | 18 +- asusctl/examples/animatrix-gif.rs | 14 +- asusctl/examples/animatrix-png-gif.rs | 97 -------- asusctl/src/main.rs | 8 +- daemon-user/Cargo.toml | 9 +- daemon-user/src/ctrl_anime.rs | 342 ++++++++++++++++++++++++++ daemon-user/src/daemon.rs | 67 +++++ daemon-user/src/error.rs | 8 + daemon-user/src/lib.rs | 6 + daemon-user/src/main.rs | 66 ----- daemon-user/src/user_config.rs | 84 ++----- daemon-user/src/zbus_anime.rs | 68 +++++ daemon/src/ctrl_anime.rs | 5 +- daemon/src/ctrl_charge.rs | 7 +- daemon/src/ctrl_fan_cpu.rs | 10 +- daemon/src/ctrl_leds.rs | 14 +- daemon/src/ctrl_rog_bios.rs | 8 +- daemon/src/ctrl_supported.rs | 13 +- rog-anime/Cargo.toml | 5 +- rog-anime/src/data.rs | 4 +- rog-anime/src/error.rs | 14 ++ rog-anime/src/gif.rs | 2 +- rog-anime/src/sequencer.rs | 118 +++++++-- rog-types/Cargo.toml | 4 +- rog-types/src/lib.rs | 2 + rog-types/src/supported.rs | 41 +++ 28 files changed, 741 insertions(+), 299 deletions(-) delete mode 100644 asusctl/examples/animatrix-png-gif.rs create mode 100644 daemon-user/src/ctrl_anime.rs create mode 100644 daemon-user/src/daemon.rs delete mode 100644 daemon-user/src/main.rs create mode 100644 daemon-user/src/zbus_anime.rs create mode 100644 rog-types/src/supported.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d5cb5b0..afd77842 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Revert zbus to 1.9.1 - Use enum to show power states, and catch missing pci path for nvidia. - Partial user-daemon for anime/per-key done, asusd-user. Includes asusd-user systemd unit. +- user-daemon provides dbus emthods to insert anime actions, remove from index, set leds on/off # [3.3.0] - 2021-04-3 ### Changed diff --git a/Cargo.lock b/Cargo.lock index afb346f2..a8860b48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -233,9 +233,13 @@ dependencies = [ "dirs 3.0.1", "rog_anime", "rog_dbus", + "rog_types", "serde", "serde_derive", "serde_json", + "zbus", + "zvariant", + "zvariant_derive", ] [[package]] @@ -909,6 +913,7 @@ dependencies = [ "png_pong", "serde", "serde_derive", + "zbus", "zvariant", "zvariant_derive", ] diff --git a/asus-notify/src/main.rs b/asus-notify/src/main.rs index 0d0c71aa..974131b5 100644 --- a/asus-notify/src/main.rs +++ b/asus-notify/src/main.rs @@ -2,6 +2,7 @@ use notify_rust::{Hint, Notification, NotificationHandle}; use rog_dbus::{DbusProxies, Signals}; use rog_types::profile::Profile; use std::error::Error; +use std::thread::sleep; use std::time::Duration; fn main() -> Result<(), Box> { @@ -18,9 +19,22 @@ fn main() -> Result<(), Box> { let mut last_chrg_notif: Option = None; let recv = proxies.setup_recv(conn); + let mut err_count = 0; loop { - std::thread::sleep(Duration::from_millis(100)); - recv.next_signal().unwrap(); + sleep(Duration::from_millis(100)); + if let Err(err) = recv.next_signal() { + if err_count < 3 { + err_count += 1; + println!("{}", err); + } + if err_count == 3 { + err_count += 1; + println!("Max error count reached. Spooling silently."); + } + sleep(Duration::from_millis(2000)); + continue; + } + err_count = 0; if let Ok(mut lock) = signals.gfx_vendor.lock() { if let Some(vendor) = lock.take() { diff --git a/asusctl/examples/animatrix-gif.rs b/asusctl/examples/animatrix-gif.rs index 6bc94aad..1e6ef6a5 100644 --- a/asusctl/examples/animatrix-gif.rs +++ b/asusctl/examples/animatrix-gif.rs @@ -1,6 +1,6 @@ use std::{env, path::Path, thread::sleep}; -use rog_anime::{Action, Sequences}; +use rog_anime::{ActionData, AnimeAction, Sequences}; use rog_dbus::AuraDbusClient; fn main() { @@ -15,11 +15,19 @@ fn main() { let path = Path::new(&args[1]); let brightness = args[2].parse::().unwrap(); let mut seq = Sequences::new(); - seq.add_asus_gif(path, rog_anime::AnimTime::Infinite, brightness).unwrap(); + seq.insert( + 0, + &AnimeAction::AsusAnimation { + file: path.into(), + time: rog_anime::AnimTime::Infinite, + brightness, + }, + ) + .unwrap(); loop { for action in seq.iter() { - if let Action::Animation(frames) = action { + if let ActionData::Animation(frames) = action { for frame in frames.frames() { client .proxies() diff --git a/asusctl/examples/animatrix-png-gif.rs b/asusctl/examples/animatrix-png-gif.rs deleted file mode 100644 index ecfa6c19..00000000 --- a/asusctl/examples/animatrix-png-gif.rs +++ /dev/null @@ -1,97 +0,0 @@ -use std::{ - env, - path::Path, - thread::sleep, - time::{Duration, Instant}, -}; - -use glam::Vec2; -use rog_anime::{Action, AnimTime, Sequences}; -use rog_dbus::AuraDbusClient; - -fn main() { - let (client, _) = AuraDbusClient::new().unwrap(); - - let args: Vec = env::args().into_iter().collect(); - if args.len() < 7 { - println!( - "Usage: " - ); - println!("e.g, asusctl/examples/file.gif 0.9 0.4 0.0 0.0 0.8 0"); - return; - } - - let mut seq = Sequences::new(); - seq.add_image_gif( - Path::new(&args[1]), - args[2].parse::().unwrap(), - args[3].parse::().unwrap(), - Vec2::new( - args[4].parse::().unwrap(), - args[5].parse::().unwrap(), - ), - if let Ok(time) = args[7].parse::() { - if time != 0 { - AnimTime::Time(Duration::from_secs(time)) - } else { - AnimTime::Infinite - } - } else { - AnimTime::Infinite - }, - args[6].parse::().unwrap(), - ) - .unwrap(); - - if args.len() == 9 { - seq.add_image_gif( - Path::new(&args[8]), - args[2].parse::().unwrap(), - args[3].parse::().unwrap(), - Vec2::new( - args[4].parse::().unwrap(), - args[5].parse::().unwrap(), - ), - if let Ok(time) = args[7].parse::() { - if time != 0 { - AnimTime::Time(Duration::from_secs(time)) - } else { - AnimTime::Infinite - } - } else { - AnimTime::Infinite - }, - args[6].parse::().unwrap(), - ) - .unwrap(); - } - - loop { - for action in seq.iter() { - if let Action::Animation(frames) = action { - let mut count = 0; - let start = Instant::now(); - 'outer: loop { - for frame in frames.frames() { - client - .proxies() - .anime() - .write(frame.frame().clone()) - .unwrap(); - if let AnimTime::Time(time) = frames.duration() { - if Instant::now().duration_since(start) > time { - break 'outer; - } - } else if let AnimTime::Cycles(times) = frames.duration() { - if count == times { - break 'outer; - } - } - sleep(frame.delay()); - } - count +=1; - } - } - } - } -} diff --git a/asusctl/src/main.rs b/asusctl/src/main.rs index e3be97bb..25ab2219 100644 --- a/asusctl/src/main.rs +++ b/asusctl/src/main.rs @@ -3,10 +3,6 @@ mod aura_cli; use crate::aura_cli::{LedBrightness, SetAuraBuiltin}; use anime_cli::{AniMeActions, AniMeCommand}; -use daemon::{ - ctrl_fan_cpu::FanCpuSupportedFunctions, ctrl_leds::LedSupportedFunctions, - ctrl_rog_bios::RogBiosSupportedFunctions, ctrl_supported::SupportedFunctions, -}; use gumdrop::{Opt, Options}; use rog_anime::{AniMeDataBuffer, AniMeImage, Vec2, ANIME_DATA_LEN}; use rog_dbus::AuraDbusClient; @@ -14,6 +10,10 @@ use rog_types::{ aura_modes::{self, AuraEffect, AuraModeNum}, gfx_vendors::GfxVendors, profile::{FanLevel, ProfileCommand, ProfileEvent}, + supported::{ + FanCpuSupportedFunctions, LedSupportedFunctions, RogBiosSupportedFunctions, + SupportedFunctions, + }, }; use std::{env::args, path::Path}; use yansi_term::Colour::Green; diff --git a/daemon-user/Cargo.toml b/daemon-user/Cargo.toml index 85426905..b491607b 100644 --- a/daemon-user/Cargo.toml +++ b/daemon-user/Cargo.toml @@ -11,7 +11,7 @@ path = "src/lib.rs" [[bin]] name = "asusd-user" -path = "src/main.rs" +path = "src/daemon.rs" [dependencies] # serialisation @@ -21,5 +21,10 @@ serde_derive = "^1.0" rog_anime = { path = "../rog-anime" } rog_dbus = { path = "../rog-dbus" } +rog_types = { path = "../rog-types" } -dirs = "3.0.1" \ No newline at end of file +dirs = "3.0.1" + +zbus = "^1.9.1" +zvariant = "^2.6" +zvariant_derive = "^2.6" \ No newline at end of file diff --git a/daemon-user/src/ctrl_anime.rs b/daemon-user/src/ctrl_anime.rs new file mode 100644 index 00000000..d67e6601 --- /dev/null +++ b/daemon-user/src/ctrl_anime.rs @@ -0,0 +1,342 @@ +use rog_anime::{ActionData, AnimTime, AnimeAction, Sequences, Vec2}; +use rog_dbus::AuraDbusClient; +//use crate::dbus::DbusEvents; +use serde_derive::{Deserialize, Serialize}; +use std::time::Duration; +use std::{ + path::Path, + sync::{ + atomic::{AtomicBool, Ordering}, + Mutex, + }, +}; +use std::{sync::Arc, thread::sleep, time::Instant}; +use zbus::dbus_interface; +use zvariant::ObjectPath; +use zvariant_derive::Type; + +use crate::{error::Error, user_config::UserConfig}; + +#[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: AuraDbusClient<'a>, + do_early_return: &'a AtomicBool, +} + +impl<'a> CtrlAnimeInner<'static> { + pub fn new( + sequences: Sequences, + client: AuraDbusClient<'static>, + do_early_return: &'static AtomicBool, + ) -> 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(&self) -> Result<(), Error> { + if self.do_early_return.load(Ordering::SeqCst) { + return Ok(()); + } + + for action in self.sequences.iter() { + match action { + ActionData::Animation(frames) => { + let mut count = 0; + let start = Instant::now(); + 'animation: loop { + for frame in frames.frames() { + if self.do_early_return.load(Ordering::SeqCst) { + return Ok(()); + } + self.client + .proxies() + .anime() + .write(frame.frame().clone()) + .unwrap(); + if let AnimTime::Time(time) = frames.duration() { + if Instant::now().duration_since(start) > time { + break 'animation; + } + } + sleep(frame.delay()); + } + if let AnimTime::Cycles(times) = frames.duration() { + count += 1; + if count >= times { + break 'animation; + } + } + } + } + ActionData::Image(image) => { + self.client + .proxies() + .anime() + .write(image.as_ref().clone()) + .unwrap(); + } + 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: AuraDbusClient<'a>, + inner: Arc>>, + /// Must be the same Atomic as in CtrlAnimeInner + inner_early_return: &'a AtomicBool, +} + +impl<'a> CtrlAnime<'static> { + pub fn new( + config: Arc>, + inner: Arc>>, + client: AuraDbusClient<'static>, + inner_early_return: &'static AtomicBool, + ) -> Result { + Ok(CtrlAnime { + config, + inner, + client, + inner_early_return, + }) + } + + pub fn add_to_server(self, server: &mut zbus::ObjectServer) { + server + .at( + &ObjectPath::from_str_unchecked("/org/asuslinux/Anime"), + self, + ) + .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: String, + time: TimeType, + count: u32, + brightness: f32, + ) -> zbus::fdo::Result { + if let Ok(mut config) = self.config.try_lock() { + let time: AnimTime = match time { + TimeType::Timer => AnimTime::Time(Duration::from_millis(count as u64)), + TimeType::Count => AnimTime::Cycles(count), + TimeType::Infinite => AnimTime::Infinite, + }; + let file = Path::new(&file); + let action = AnimeAction::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)?; + } + 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: String, + scale: f32, + angle: f32, + xy: (f32, f32), + time: TimeType, + count: u32, + brightness: f32, + ) -> zbus::fdo::Result { + if let Ok(mut config) = self.config.try_lock() { + let time: AnimTime = match time { + TimeType::Timer => AnimTime::Time(Duration::from_millis(count as u64)), + TimeType::Count => AnimTime::Cycles(count), + TimeType::Infinite => AnimTime::Infinite, + }; + let file = Path::new(&file); + let translation = Vec2::new(xy.0, xy.1); + let action = AnimeAction::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)?; + } + 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_image( + &mut self, + index: u32, + file: String, + scale: f32, + angle: f32, + xy: (f32, f32), + brightness: f32, + ) -> zbus::fdo::Result { + if let Ok(mut config) = self.config.try_lock() { + let file = Path::new(&file); + let action = AnimeAction::Image { + file: file.into(), + scale, + angle, + translation: Vec2::new(xy.0, xy.1), + brightness, + }; + + // 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)?; + } + 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 = AnimeAction::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)?; + } + 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().toggle_on(on)?; + // 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().toggle_on(on)?; + } + Ok(()) + } +} diff --git a/daemon-user/src/daemon.rs b/daemon-user/src/daemon.rs new file mode 100644 index 00000000..c76b7868 --- /dev/null +++ b/daemon-user/src/daemon.rs @@ -0,0 +1,67 @@ +use rog_dbus::AuraDbusClient; +use rog_types::supported::SupportedFunctions; +use rog_user::{ + ctrl_anime::{CtrlAnime, CtrlAnimeInner}, + user_config::*, + DBUS_NAME, +}; +use std::sync::Arc; +use std::sync::Mutex; +use std::thread; +use zbus::{fdo, Connection}; + +use std::sync::atomic::AtomicBool; + +/// The anime loop needs an atomic to make it exit early if required +static ANIME_INNER_EARLY_RETURN: AtomicBool = AtomicBool::new(false); + +fn main() -> Result<(), Box> { + println!(" rog-dbus version {}", rog_dbus::VERSION); + + let (client, _) = AuraDbusClient::new().unwrap(); + let supported = client.proxies().supported().get_supported_functions()?; + let supported = serde_json::from_str::(&&supported).unwrap(); + + let mut config = UserConfig::new(); + config.load_config()?; + let anime = config.create_anime()?; + + let config = Arc::new(Mutex::new(config)); + + // Create server + let connection = Connection::new_session()?; + fdo::DBusProxy::new(&connection)? + .request_name(DBUS_NAME, fdo::RequestNameFlags::ReplaceExisting.into())?; + let mut server = zbus::ObjectServer::new(&connection); + + // Set up the anime data and run loop/thread + if supported.anime_ctrl.0 { + // Inner behind mutex required for thread safety + let inner = Arc::new(Mutex::new(CtrlAnimeInner::new( + anime, + client, + &ANIME_INNER_EARLY_RETURN, + )?)); + // Need new client object for dbus control part + let (client, _) = AuraDbusClient::new().unwrap(); + let anime_control = + CtrlAnime::new(config, inner.clone(), client, &ANIME_INNER_EARLY_RETURN)?; + anime_control.add_to_server(&mut server); + // Thread using inner + let _anime_thread = thread::Builder::new() + .name("Anime User".into()) + .spawn(move || loop { + if let Ok(inner) = inner.try_lock() { + inner.run().unwrap(); + } + })?; + } + + if supported.keyboard_led.per_key_led_mode {} + + loop { + if let Err(err) = server.try_handle_next() { + println!("{}", err); + } + } +} diff --git a/daemon-user/src/error.rs b/daemon-user/src/error.rs index 86496894..2ed8f90c 100644 --- a/daemon-user/src/error.rs +++ b/daemon-user/src/error.rs @@ -6,6 +6,7 @@ use rog_anime::error::AnimeError; pub enum Error { Io(std::io::Error), ConfigLoadFail, + ConfigLockFail, XdgVars, Anime(AnimeError), } @@ -16,6 +17,7 @@ impl fmt::Display for Error { 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), } @@ -35,3 +37,9 @@ impl From for Error { 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 index 3a8034bb..331ae57a 100644 --- a/daemon-user/src/lib.rs +++ b/daemon-user/src/lib.rs @@ -1,3 +1,9 @@ pub mod user_config; pub mod error; + +pub mod ctrl_anime; + +pub mod zbus_anime; + +pub static DBUS_NAME: &str = "org.asuslinux.Daemon"; \ No newline at end of file diff --git a/daemon-user/src/main.rs b/daemon-user/src/main.rs deleted file mode 100644 index cf0eae41..00000000 --- a/daemon-user/src/main.rs +++ /dev/null @@ -1,66 +0,0 @@ -use rog_anime::{Action, AnimTime}; -use rog_dbus::AuraDbusClient; -use rog_user::user_config::*; - -use std::{ - thread::sleep, - time::{Duration, Instant}, -}; - -fn main() -> Result<(), Box> { - println!(" rog-dbus version {}", rog_dbus::VERSION); - - let (client, _) = AuraDbusClient::new().unwrap(); - - let mut config = UserConfig::new(); - config.load_config()?; - let anime = config.create_anime()?; - - // TODO: - // - find user config dir with xdg - // - load user config - // - start anime - // A way to reload when the config changes - - loop { - for action in anime.iter() { - let start = Instant::now(); - - match action { - Action::Animation(frames) => { - let mut count = 0; - 'animation: loop { - for frame in frames.frames() { - client.proxies().anime().write(frame.frame().clone())?; - if let AnimTime::Time(time) = frames.duration() { - if Instant::now().duration_since(start) > time { - break 'animation; - } - } - sleep(frame.delay()); - } - if let AnimTime::Cycles(times) = frames.duration() { - count += 1; - if count >= times { - break 'animation; - } - } - } - } - Action::Image(image) => { - client.proxies().anime().write(image.as_ref().clone())?; - } - Action::Pause(duration) => 'pause: loop { - if Instant::now().duration_since(start) > *duration { - break 'pause; - } - sleep(Duration::from_millis(10)); - }, - Action::AudioEq => {} - Action::SystemInfo => {} - Action::TimeDate => {} - Action::Matrix => {} - } - } - } -} diff --git a/daemon-user/src/user_config.rs b/daemon-user/src/user_config.rs index dd886f93..d1464448 100644 --- a/daemon-user/src/user_config.rs +++ b/daemon-user/src/user_config.rs @@ -1,46 +1,17 @@ use std::{ fs::{create_dir, OpenOptions}, io::{Read, Write}, - path::PathBuf, time::Duration, }; -use rog_anime::{AnimTime, Sequences, Vec2}; +use rog_anime::{AnimTime, AnimeAction, Sequences, Vec2}; use serde_derive::{Deserialize, Serialize}; use crate::error::Error; #[derive(Debug, Default, Deserialize, Serialize)] pub struct UserConfig { - anime: Vec, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub enum AnimeAction { - /// Full gif sequence. Immutable. - AsusAnimation { - file: PathBuf, - time: AnimTime, - brightness: f32, - }, - /// Basic image, can have properties changed - ImageAnimation { - file: PathBuf, - scale: f32, - angle: f32, - translation: Vec2, - time: AnimTime, - brightness: f32, - }, - Image { - file: PathBuf, - scale: f32, - angle: f32, - translation: Vec2, - brightness: f32, - }, - /// A pause to be used between sequences - Pause(Duration), + pub anime: Vec, } impl UserConfig { @@ -114,38 +85,35 @@ impl UserConfig { } } Ok(()) - //Err(Error::ConfigLoadFail) + } + + pub fn write(&self) -> Result<(), Error> { + let mut path = if let Some(dir) = dirs::config_dir() { + dir + } else { + return Err(Error::XdgVars); + }; + + path.push("rog"); + if !path.exists() { + create_dir(path.clone())?; + } + + path.push("rog-user.cfg"); + + let mut file = OpenOptions::new().write(true).create(true).truncate(true).open(&path)?; + + let json = serde_json::to_string_pretty(&self).unwrap(); + dbg!(&json); + file.write_all(json.as_bytes())?; + Ok(()) } pub fn create_anime(&self) -> Result { let mut seq = Sequences::new(); - for anime in self.anime.iter() { - match anime { - AnimeAction::AsusAnimation { - file, - time: duration, - brightness, - } => seq.add_asus_gif(&file, *duration, *brightness)?, - AnimeAction::ImageAnimation { - file, - scale, - angle, - translation, - time: duration, - brightness, - } => { - seq.add_image_gif(&file, *scale, *angle, *translation, *duration, *brightness)? - } - AnimeAction::Image { - file, - scale, - angle, - translation, - brightness, - } => seq.add_png(&file, *scale, *angle, *translation, *brightness)?, - AnimeAction::Pause(duration) => seq.add_pause(*duration)?, - } + for (idx, action) in self.anime.iter().enumerate() { + seq.insert(idx, action)?; } Ok(seq) diff --git a/daemon-user/src/zbus_anime.rs b/daemon-user/src/zbus_anime.rs new file mode 100644 index 00000000..ec497f79 --- /dev/null +++ b/daemon-user/src/zbus_anime.rs @@ -0,0 +1,68 @@ +//! # 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. + +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/src/ctrl_anime.rs b/daemon/src/ctrl_anime.rs index b84ac5ab..d29006bd 100644 --- a/daemon/src/ctrl_anime.rs +++ b/daemon/src/ctrl_anime.rs @@ -15,6 +15,7 @@ const ON_OFF: u8 = 0x04; use log::{error, info, warn}; use rog_anime::{AniMeDataBuffer, AniMePacketType}; +use rog_types::supported::AnimeSupportedFunctions; use rusb::{Device, DeviceHandle}; use std::error::Error; use std::time::Duration; @@ -23,10 +24,6 @@ use zvariant::ObjectPath; use crate::GetSupported; -use serde_derive::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize)] -pub struct AnimeSupportedFunctions(pub bool); - impl GetSupported for CtrlAnimeDisplay { type A = AnimeSupportedFunctions; diff --git a/daemon/src/ctrl_charge.rs b/daemon/src/ctrl_charge.rs index e583f584..dbd7202d 100644 --- a/daemon/src/ctrl_charge.rs +++ b/daemon/src/ctrl_charge.rs @@ -1,7 +1,7 @@ use crate::{config::Config, error::RogError, GetSupported}; //use crate::dbus::DbusEvents; use log::{info, warn}; -use serde_derive::{Deserialize, Serialize}; +use rog_types::supported::ChargeSupportedFunctions; use std::fs::OpenOptions; use std::io::Write; use std::path::Path; @@ -12,11 +12,6 @@ use zvariant::ObjectPath; static BAT_CHARGE_PATH: &str = "/sys/class/power_supply/BAT0/charge_control_end_threshold"; -#[derive(Serialize, Deserialize)] -pub struct ChargeSupportedFunctions { - pub charge_level_set: bool, -} - impl GetSupported for CtrlCharge { type A = ChargeSupportedFunctions; diff --git a/daemon/src/ctrl_fan_cpu.rs b/daemon/src/ctrl_fan_cpu.rs index f01d67ed..54e1d157 100644 --- a/daemon/src/ctrl_fan_cpu.rs +++ b/daemon/src/ctrl_fan_cpu.rs @@ -1,8 +1,7 @@ use crate::error::RogError; use crate::{config::Config, GetSupported}; use log::{info, warn}; -use rog_types::profile::{FanLevel, Profile, ProfileEvent}; -use serde_derive::{Deserialize, Serialize}; +use rog_types::{profile::{FanLevel, Profile, ProfileEvent}, supported::FanCpuSupportedFunctions}; use std::fs::OpenOptions; use std::io::Write; use std::path::Path; @@ -20,13 +19,6 @@ pub struct CtrlFanAndCpu { config: Arc>, } -#[derive(Serialize, Deserialize)] -pub struct FanCpuSupportedFunctions { - pub stock_fan_modes: bool, - pub min_max_freq: bool, - pub fan_curve_set: bool, -} - impl GetSupported for CtrlFanAndCpu { type A = FanCpuSupportedFunctions; diff --git a/daemon/src/ctrl_leds.rs b/daemon/src/ctrl_leds.rs index cb5d0502..6ce6c8d6 100644 --- a/daemon/src/ctrl_leds.rs +++ b/daemon/src/ctrl_leds.rs @@ -10,10 +10,7 @@ use crate::{ laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES}, }; use log::{error, info, warn}; -use rog_types::{ - aura_modes::{AuraEffect, AuraModeNum, LedBrightness}, - LED_MSG_LEN, -}; +use rog_types::{LED_MSG_LEN, aura_modes::{AuraEffect, AuraModeNum, LedBrightness}, supported::LedSupportedFunctions}; use std::fs::OpenOptions; use std::io::{Read, Write}; use std::path::Path; @@ -24,15 +21,6 @@ use zvariant::ObjectPath; use crate::GetSupported; -use serde_derive::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize)] -pub struct LedSupportedFunctions { - pub brightness_set: bool, - pub stock_led_modes: Option>, - pub multizone_led_mode: bool, - pub per_key_led_mode: bool, -} - impl GetSupported for CtrlKbdBacklight { type A = LedSupportedFunctions; diff --git a/daemon/src/ctrl_rog_bios.rs b/daemon/src/ctrl_rog_bios.rs index fce680fa..ab181353 100644 --- a/daemon/src/ctrl_rog_bios.rs +++ b/daemon/src/ctrl_rog_bios.rs @@ -1,6 +1,6 @@ use crate::{config::Config, error::RogError, GetSupported}; use log::{error, info, warn}; -use serde_derive::{Deserialize, Serialize}; +use rog_types::supported::RogBiosSupportedFunctions; use std::fs::OpenOptions; use std::io::BufRead; use std::io::{Read, Write}; @@ -23,12 +23,6 @@ pub struct CtrlRogBios { _config: Arc>, } -#[derive(Serialize, Deserialize)] -pub struct RogBiosSupportedFunctions { - pub post_sound_toggle: bool, - pub dedicated_gfx_toggle: bool, -} - impl GetSupported for CtrlRogBios { type A = RogBiosSupportedFunctions; diff --git a/daemon/src/ctrl_supported.rs b/daemon/src/ctrl_supported.rs index 335827a1..230d727a 100644 --- a/daemon/src/ctrl_supported.rs +++ b/daemon/src/ctrl_supported.rs @@ -4,12 +4,13 @@ use zbus::dbus_interface; use zvariant::ObjectPath; use crate::{ - ctrl_anime::{AnimeSupportedFunctions, CtrlAnimeDisplay}, - ctrl_charge::{ChargeSupportedFunctions, CtrlCharge}, - ctrl_fan_cpu::{CtrlFanAndCpu, FanCpuSupportedFunctions}, - ctrl_leds::{CtrlKbdBacklight, LedSupportedFunctions}, - ctrl_rog_bios::{CtrlRogBios, RogBiosSupportedFunctions}, - GetSupported, + ctrl_anime::CtrlAnimeDisplay, ctrl_charge::CtrlCharge, ctrl_fan_cpu::CtrlFanAndCpu, + ctrl_leds::CtrlKbdBacklight, ctrl_rog_bios::CtrlRogBios, GetSupported, +}; + +use rog_types::supported::{ + AnimeSupportedFunctions, ChargeSupportedFunctions, FanCpuSupportedFunctions, + LedSupportedFunctions, RogBiosSupportedFunctions, }; #[derive(Serialize, Deserialize)] diff --git a/rog-anime/Cargo.toml b/rog-anime/Cargo.toml index 2ced82eb..978a5adb 100644 --- a/rog-anime/Cargo.toml +++ b/rog-anime/Cargo.toml @@ -17,11 +17,12 @@ gif = "^0.11.2" serde = "^1.0" serde_derive = "^1.0" +zbus = "^1.9.1" zvariant = "^2.5" zvariant_derive = "^2.5" glam = { version = "*", features = ["serde"] } [features] -default = ["zbus"] -zbus = [] \ No newline at end of file +default = ["z"] +z = [] \ No newline at end of file diff --git a/rog-anime/src/data.rs b/rog-anime/src/data.rs index df360049..5dfe9da4 100644 --- a/rog-anime/src/data.rs +++ b/rog-anime/src/data.rs @@ -1,5 +1,5 @@ use serde_derive::{Deserialize, Serialize}; -#[cfg(feature = "zbus")] +#[cfg(feature = "z")] use zvariant_derive::Type; /// The first 7 bytes of a USB packet are accounted for by `USB_PREFIX1` and `USB_PREFIX2` @@ -16,7 +16,7 @@ const USB_PREFIX2: [u8; 7] = [0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02]; /// The minimal serializable data that can be transferred over wire types. /// Other data structures in `rog_anime` will convert to this. -#[cfg_attr(feature = "zbus", derive(Type))] +#[cfg_attr(feature = "z", derive(Type))] #[derive(Debug, Clone, Deserialize, Serialize)] pub struct AniMeDataBuffer(Vec); diff --git a/rog-anime/src/error.rs b/rog-anime/src/error.rs index 3033eea4..8e4d3b49 100644 --- a/rog-anime/src/error.rs +++ b/rog-anime/src/error.rs @@ -3,6 +3,9 @@ use png_pong::decode::Error as PngError; use std::error::Error; use std::fmt; +#[cfg(feature = "z")] +use zbus::fdo; + #[derive(Debug)] pub enum AnimeError { NoFrames, @@ -12,6 +15,8 @@ pub enum AnimeError { Format, /// The input was incorrect size, expected size is `IncorrectSize(width, height)` IncorrectSize(u32, u32), + #[cfg(feature = "z")] + Zbus(fdo::Error) } impl fmt::Display for AnimeError { @@ -28,6 +33,8 @@ impl fmt::Display for AnimeError { "The input image size is incorrect, expected {}x{}", width, height ), + #[cfg(feature = "z")] + AnimeError::Zbus(e) => write!(f, "ZBUS error: {}", e), } } } @@ -51,3 +58,10 @@ impl From for AnimeError { AnimeError::Gif(err) } } + +#[cfg(feature = "z")] +impl From for fdo::Error { + fn from(err: AnimeError) -> Self { + fdo::Error::Failed(format!("{}", err)) + } +} \ No newline at end of file diff --git a/rog-anime/src/gif.rs b/rog-anime/src/gif.rs index 28d879bc..5540d6d8 100644 --- a/rog-anime/src/gif.rs +++ b/rog-anime/src/gif.rs @@ -24,6 +24,7 @@ impl AniMeFrame { #[derive(Debug, Copy, Clone, Deserialize, Serialize)] pub enum AnimTime { + /// Time in milliseconds for animation to run Time(Duration), Cycles(u32), Infinite, @@ -35,7 +36,6 @@ impl Default for AnimTime { } } - /// A gif animation. This is a collection of frames from the gif, and a duration /// that the animation should be shown for. #[derive(Debug, Clone, Deserialize, Serialize)] diff --git a/rog-anime/src/sequencer.rs b/rog-anime/src/sequencer.rs index 9948567a..3aa22981 100644 --- a/rog-anime/src/sequencer.rs +++ b/rog-anime/src/sequencer.rs @@ -1,13 +1,44 @@ -use std::{path::Path, time::Duration}; +use std::{ + path::{Path, PathBuf}, + time::Duration, +}; use glam::Vec2; use serde_derive::{Deserialize, Serialize}; -use crate::{AniMeDataBuffer, AniMeGif, AniMeImage, AnimTime, error::AnimeError}; +use crate::{error::AnimeError, AniMeDataBuffer, AniMeGif, AniMeImage, AnimTime}; + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub enum AnimeAction { + /// Full gif sequence. Immutable. + AsusAnimation { + file: PathBuf, + time: AnimTime, + brightness: f32, + }, + /// Basic image, can have properties changed + ImageAnimation { + file: PathBuf, + scale: f32, + angle: f32, + translation: Vec2, + time: AnimTime, + brightness: f32, + }, + Image { + file: PathBuf, + scale: f32, + angle: f32, + translation: Vec2, + brightness: f32, + }, + /// A pause to be used between sequences + Pause(Duration), +} /// #[derive(Debug, Deserialize, Serialize)] -pub enum Action { +pub enum ActionData { /// Full gif sequence. Immutable. Animation(AniMeGif), /// Basic image, can have properties changed and image updated via those properties @@ -26,40 +57,92 @@ pub enum Action { /// An optimised precomputed set of actions #[derive(Debug, Deserialize, Serialize, Default)] -pub struct Sequences(Vec); +pub struct Sequences(Vec); impl Sequences { pub fn new() -> Self { Self(Vec::new()) } - pub fn add_asus_gif( + pub fn insert(&mut self, index: usize, action: &AnimeAction) -> Result<(), AnimeError> { + match action { + AnimeAction::AsusAnimation { + file, + time: duration, + brightness, + } => self.insert_asus_gif(index, &file, *duration, *brightness)?, + AnimeAction::ImageAnimation { + file, + scale, + angle, + translation, + time: duration, + brightness, + } => self.insert_image_gif( + index, + &file, + *scale, + *angle, + *translation, + *duration, + *brightness, + )?, + AnimeAction::Image { + file, + scale, + angle, + translation, + brightness, + } => self.insert_png(index, &file, *scale, *angle, *translation, *brightness)?, + AnimeAction::Pause(duration) => self.insert_pause(index, *duration), + }; + Ok(()) + } + + pub fn remove_item(&mut self, index: usize) -> Option { + if index < self.0.len() { + return Some(self.0.remove(index)); + } + None + } + + fn insert_asus_gif( &mut self, + mut index: usize, file: &Path, duration: AnimTime, brightness: f32, ) -> Result<(), AnimeError> { + if index > self.0.len() { + index = self.0.len() - 1; + } let frames = AniMeGif::create_diagonal_gif(file, duration, brightness)?; - self.0.push(Action::Animation(frames)); + self.0.insert(index, ActionData::Animation(frames)); Ok(()) } - pub fn add_png( + fn insert_png( &mut self, + mut index: usize, file: &Path, scale: f32, angle: f32, translation: Vec2, brightness: f32, ) -> Result<(), AnimeError> { + if index > self.0.len() { + index = self.0.len() - 1; + } let image = AniMeImage::from_png(file, scale, angle, translation, brightness)?; let data = ::from(&image); - self.0.push(Action::Image(Box::new(data))); + self.0.insert(index, ActionData::Image(Box::new(data))); Ok(()) } - pub fn add_image_gif( + #[allow(clippy::too_many_arguments)] + fn insert_image_gif( &mut self, + mut index: usize, file: &Path, scale: f32, angle: f32, @@ -67,15 +150,20 @@ impl Sequences { duration: AnimTime, brightness: f32, ) -> Result<(), AnimeError> { + if index > self.0.len() { + index = self.0.len() - 1; + } let frames = AniMeGif::create_png_gif(file, scale, angle, translation, duration, brightness)?; - self.0.push(Action::Animation(frames)); + self.0.insert(index, ActionData::Animation(frames)); Ok(()) } - pub fn add_pause(&mut self, duration: Duration) -> Result<(), AnimeError> { - self.0.push(Action::Pause(duration)); - Ok(()) + fn insert_pause(&mut self, mut index: usize, duration: Duration) { + if index > self.0.len() { + index = self.0.len() - 1; + } + self.0.insert(index, ActionData::Pause(duration)); } pub fn iter(&self) -> ActionIterator { @@ -92,9 +180,9 @@ pub struct ActionIterator<'a> { } impl<'a> Iterator for ActionIterator<'a> { - type Item = &'a Action; + type Item = &'a ActionData; - fn next(&mut self) -> Option<&'a Action> { + fn next(&mut self) -> Option<&'a ActionData> { if self.next_idx == self.actions.0.len() { self.next_idx = 0; return None; diff --git a/rog-types/Cargo.toml b/rog-types/Cargo.toml index 7c2241e5..110812da 100644 --- a/rog-types/Cargo.toml +++ b/rog-types/Cargo.toml @@ -14,5 +14,5 @@ gumdrop = "^0.8" rog_fan_curve = { version = "^0.1", features = ["serde"] } serde = "^1.0" serde_derive = "^1.0" -zvariant = "^2.5" -zvariant_derive = "^2.5" \ No newline at end of file +zvariant = "^2.6" +zvariant_derive = "^2.6" \ No newline at end of file diff --git a/rog-types/src/lib.rs b/rog-types/src/lib.rs index 93dcf9a6..8caa4fb1 100644 --- a/rog-types/src/lib.rs +++ b/rog-types/src/lib.rs @@ -16,6 +16,8 @@ pub mod aura_perkey; pub mod gfx_vendors; +pub mod supported; + pub mod error; pub static VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/rog-types/src/supported.rs b/rog-types/src/supported.rs new file mode 100644 index 00000000..2d7e0681 --- /dev/null +++ b/rog-types/src/supported.rs @@ -0,0 +1,41 @@ +use serde_derive::{Deserialize, Serialize}; + +use crate::aura_modes::AuraModeNum; + +#[derive(Serialize, Deserialize)] +pub struct SupportedFunctions { + pub anime_ctrl: AnimeSupportedFunctions, + pub charge_ctrl: ChargeSupportedFunctions, + pub fan_cpu_ctrl: FanCpuSupportedFunctions, + pub keyboard_led: LedSupportedFunctions, + pub rog_bios_ctrl: RogBiosSupportedFunctions, +} + +#[derive(Serialize, Deserialize)] +pub struct AnimeSupportedFunctions(pub bool); + +#[derive(Serialize, Deserialize)] +pub struct ChargeSupportedFunctions { + pub charge_level_set: bool, +} + +#[derive(Serialize, Deserialize)] +pub struct FanCpuSupportedFunctions { + pub stock_fan_modes: bool, + pub min_max_freq: bool, + pub fan_curve_set: bool, +} + +#[derive(Serialize, Deserialize)] +pub struct LedSupportedFunctions { + pub brightness_set: bool, + pub stock_led_modes: Option>, + pub multizone_led_mode: bool, + pub per_key_led_mode: bool, +} + +#[derive(Serialize, Deserialize)] +pub struct RogBiosSupportedFunctions { + pub post_sound_toggle: bool, + pub dedicated_gfx_toggle: bool, +}