From cd5daa17d0852941806fde2298cd779375961e69 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Wed, 21 Jun 2023 13:34:08 +1200 Subject: [PATCH] Anime: Enabled setting builtin animations --- CHANGELOG.md | 8 +- asusctl/src/anime_cli.rs | 34 ++-- asusctl/src/main.rs | 29 +++- daemon-user/src/ctrl_anime.rs | 4 +- daemon/src/ctrl_anime/config.rs | 116 ++++--------- daemon/src/ctrl_anime/mod.rs | 15 +- daemon/src/ctrl_anime/trait_impls.rs | 163 ++++++++++++------ rog-anime/src/data.rs | 19 +- rog-anime/src/error.rs | 2 + rog-anime/src/usb.rs | 142 ++++++++++++--- rog-control-center/src/system_state.rs | 43 ++--- rog-control-center/src/update_and_notify.rs | 2 +- rog-control-center/src/widgets/anime_power.rs | 54 +++--- rog-dbus/Cargo.toml | 2 +- rog-dbus/src/zbus_anime.rs | 34 ++-- 15 files changed, 420 insertions(+), 247 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81194e82..7eace445 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Support for GL503V LED modes - Support the Rear Glow on some laptops - Support for GV601VI LED modes -- Support most of M16 AniMe Matrix +- Support M16 matrix display + - Custom images + - Pixel gifs + - Power options + - Builtin animations ### 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 +### BREAKING +- All Anime related DBUS methods/notifs are changed ## [v4.6.2] - Fix rog-control-center not reopening if `startup_in_background` is set diff --git a/asusctl/src/anime_cli.rs b/asusctl/src/anime_cli.rs index 3b2cf5c6..940dcbe0 100644 --- a/asusctl/src/anime_cli.rs +++ b/asusctl/src/anime_cli.rs @@ -1,20 +1,14 @@ use gumdrop::Options; -use rog_anime::usb::Brightness; +use rog_anime::usb::{AnimAwake, AnimBooting, AnimShutdown, AnimSleeping, Brightness}; #[derive(Options)] pub struct AnimeCommand { #[options(help = "print help message")] pub help: bool, - #[options( - meta = "", - help = "enable/disable the panel LEDs (does not erase last image)" - )] - pub enable: Option, - #[options( - meta = "", - help = "enable/disable system animations (boot/sleep/shutdown)" - )] - pub boot_enable: Option, + #[options(meta = "", help = "enable/disable the display")] + pub enable_display: Option, + #[options(meta = "", help = "enable/disable the builtin run/powersave animation")] + pub enable_powersave_anim: Option, #[options( meta = "", help = "set global base brightness value " @@ -38,6 +32,24 @@ pub enum AnimeActions { Gif(AnimeGif), #[options(help = "display an animated diagonal/pixel-perfect GIF")] PixelGif(AnimeGifDiagonal), + #[options(help = "change which builtin animations are shown")] + SetBuiltins(Builtins), +} + +#[derive(Options)] +pub struct Builtins { + #[options(help = "print help message")] + pub help: bool, + #[options(meta = "", help = " ")] + pub boot: AnimBooting, + #[options(meta = "", help = "")] + pub awake: AnimAwake, + #[options(meta = "", help = "")] + pub sleep: AnimSleeping, + #[options(meta = "", help = "")] + pub shutdown: AnimShutdown, + #[options(meta = "", help = "set/apply the animations")] + pub set: Option, } #[derive(Options)] diff --git a/asusctl/src/main.rs b/asusctl/src/main.rs index 689538bf..417db51e 100644 --- a/asusctl/src/main.rs +++ b/asusctl/src/main.rs @@ -223,8 +223,8 @@ fn handle_anime( cmd: &AnimeCommand, ) -> Result<(), Box> { if (cmd.command.is_none() - && cmd.enable.is_none() - && cmd.boot_enable.is_none() + && cmd.enable_display.is_none() + && cmd.enable_powersave_anim.is_none() && cmd.brightness.is_none() && cmd.image_brightness.is_none() && !cmd.clear) @@ -235,11 +235,11 @@ fn handle_anime( println!("\n{}", lst); } } - if let Some(anime_turn) = cmd.enable { - dbus.proxies().anime().set_awake_enabled(anime_turn)?; + if let Some(enable) = cmd.enable_display { + dbus.proxies().anime().set_enable_display(enable)?; } - if let Some(anime_boot) = cmd.boot_enable { - dbus.proxies().anime().set_animation_enabled(anime_boot)?; + if let Some(enable) = cmd.enable_powersave_anim { + dbus.proxies().anime().set_builtins_enabled(enable)?; } if let Some(bright) = cmd.brightness { dbus.proxies().anime().set_brightness(bright)?; @@ -367,6 +367,23 @@ fn handle_anime( } } } + AnimeActions::SetBuiltins(builtins) => { + if builtins.help_requested() || builtins.set.is_none() { + println!("\nAny unspecified args will be set to default (first shown var)\n"); + println!("\n{}", builtins.self_usage()); + if let Some(lst) = builtins.self_command_list() { + println!("\n{}", lst); + } + return Ok(()); + } + + dbus.proxies().anime().set_builtin_animations( + builtins.boot, + builtins.awake, + builtins.sleep, + builtins.shutdown, + )?; + } } } Ok(()) diff --git a/daemon-user/src/ctrl_anime.rs b/daemon-user/src/ctrl_anime.rs index 6954711b..a87898d2 100644 --- a/daemon-user/src/ctrl_anime.rs +++ b/daemon-user/src/ctrl_anime.rs @@ -360,13 +360,13 @@ impl CtrlAnime<'static> { 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_awake_enabled(on).ok(); + 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_awake_enabled(on).ok(); + self.client.proxies().anime().set_enable_display(on).ok(); } Ok(()) } diff --git a/daemon/src/ctrl_anime/config.rs b/daemon/src/ctrl_anime/config.rs index ce4a5693..72c77783 100644 --- a/daemon/src/ctrl_anime/config.rs +++ b/daemon/src/ctrl_anime/config.rs @@ -2,78 +2,12 @@ use std::time::Duration; use config_traits::{StdConfig, StdConfigLoad2}; use rog_anime::error::AnimeError; -use rog_anime::{ActionData, ActionLoader, AnimTime, AnimeType, Fade, Vec2}; +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 AnimeConfigV341 { - pub system: Option, - pub boot: Option, - pub suspend: Option, - pub shutdown: Option, -} - -impl From for AnimeConfig { - fn from(c: AnimeConfigV341) -> AnimeConfig { - AnimeConfig { - system: if let Some(ani) = c.system { - vec![ani] - } else { - vec![] - }, - boot: if let Some(ani) = c.boot { - vec![ani] - } else { - vec![] - }, - wake: if let Some(ani) = c.suspend { - vec![ani] - } else { - vec![] - }, - shutdown: if let Some(ani) = c.shutdown.clone() { - vec![ani] - } else { - vec![] - }, - sleep: if let Some(ani) = c.shutdown.clone() { - vec![ani] - } else { - vec![] - }, - brightness: 1.0, - awake_enabled: true, - boot_anim_enabled: true, - } - } -} - -#[derive(Deserialize, Serialize)] -pub struct AnimeConfigV352 { - pub system: Vec, - pub boot: Vec, - pub wake: Vec, - pub shutdown: Vec, - pub brightness: f32, -} - -impl From for AnimeConfig { - fn from(c: AnimeConfigV352) -> AnimeConfig { - AnimeConfig { - system: c.system, - boot: c.boot, - wake: c.wake, - sleep: c.shutdown.clone(), - shutdown: c.shutdown, - brightness: 1.0, - awake_enabled: true, - boot_anim_enabled: true, - } - } -} - #[derive(Deserialize, Serialize)] pub struct AnimeConfigV460 { pub system: Vec, @@ -92,9 +26,32 @@ impl From for AnimeConfig { wake: c.wake, sleep: c.sleep, shutdown: c.shutdown, - brightness: 1.0, - awake_enabled: true, - boot_anim_enabled: true, + ..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() } } } @@ -156,8 +113,10 @@ pub struct AnimeConfig { pub sleep: Vec, pub shutdown: Vec, pub brightness: f32, - pub awake_enabled: bool, - pub boot_anim_enabled: bool, + pub display_enabled: bool, + pub display_brightness: Brightness, + pub builtin_anims_enabled: bool, + pub builtin_anims: Animations, } impl Default for AnimeConfig { @@ -169,8 +128,10 @@ impl Default for AnimeConfig { sleep: Vec::new(), shutdown: Vec::new(), brightness: 1.0, - awake_enabled: true, - boot_anim_enabled: true, + display_enabled: true, + display_brightness: Brightness::Med, + builtin_anims_enabled: true, + builtin_anims: Animations::default(), } } } @@ -189,7 +150,7 @@ impl StdConfig for AnimeConfig { } } -impl StdConfigLoad2 for AnimeConfig {} +impl StdConfigLoad2 for AnimeConfig {} impl AnimeConfig { // fn clamp_config_brightness(mut config: &mut AnimeConfig) { @@ -247,8 +208,7 @@ impl AnimeConfig { time: AnimTime::Infinite, }], brightness: 1.0, - awake_enabled: true, - boot_anim_enabled: true, + ..Default::default() } } } diff --git a/daemon/src/ctrl_anime/mod.rs b/daemon/src/ctrl_anime/mod.rs index c7973c78..0be653b2 100644 --- a/daemon/src/ctrl_anime/mod.rs +++ b/daemon/src/ctrl_anime/mod.rs @@ -11,7 +11,7 @@ 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_for_flush, pkts_for_init}; +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; @@ -72,12 +72,21 @@ impl CtrlAnime { /// /// Because this also writes to the usb device, other write tries (display /// only) *must* get the mutex lock and set the `thread_exit` atomic. - fn run_thread(inner: Arc>, actions: Vec, mut once: bool) { + 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. @@ -211,7 +220,7 @@ impl CtrlAnime { for row in &data { self.node.write_bytes(row)?; } - self.node.write_bytes(&pkt_for_flush())?; + self.node.write_bytes(&pkt_flush())?; Ok(()) } diff --git a/daemon/src/ctrl_anime/trait_impls.rs b/daemon/src/ctrl_anime/trait_impls.rs index d11f69ce..b52d6e30 100644 --- a/daemon/src/ctrl_anime/trait_impls.rs +++ b/daemon/src/ctrl_anime/trait_impls.rs @@ -5,10 +5,10 @@ use async_trait::async_trait; use config_traits::StdConfig; use log::warn; use rog_anime::usb::{ - pkt_for_enable_animation, pkt_for_set_awake_enabled, pkt_for_set_boot, pkt_for_set_brightness, - Brightness, + 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, AnimePowerStates}; +use rog_anime::{AnimeDataBuffer, DeviceState}; use zbus::export::futures_util::lock::Mutex; use zbus::{dbus_interface, Connection, SignalContext}; @@ -65,83 +65,125 @@ impl CtrlAnimeZbus { #[zbus(signal_context)] ctxt: SignalContext<'_>, brightness: Brightness, ) { - let lock = self.0.lock().await; + let mut lock = self.0.lock().await; lock.node - .write_bytes(&pkt_for_set_brightness(brightness)) + .write_bytes(&pkt_set_brightness(brightness)) .map_err(|err| { warn!("rog_anime::run_animation:callback {}", err); }) .ok(); - // lock.config.write(); + lock.config.display_brightness = brightness; + lock.config.write(); - Self::notify_power_states( + Self::notify_device_state( &ctxt, - AnimePowerStates { - brightness: lock.config.brightness.floor() as u8, - enabled: lock.config.awake_enabled, - boot_anim_enabled: lock.config.boot_anim_enabled, + 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 displaying images/data - async fn set_awake_enabled( + /// Enable the builtin animations or not + async fn set_builtins_enabled( &self, #[zbus(signal_context)] ctxt: SignalContext<'_>, - status: bool, + enabled: bool, ) { let mut lock = self.0.lock().await; lock.node - .write_bytes(&pkt_for_set_awake_enabled(status)) + .write_bytes(&pkt_set_enable_powersave_anim(enabled)) .map_err(|err| { warn!("rog_anime::run_animation:callback {}", err); }) .ok(); - lock.config.awake_enabled = status; + lock.config.builtin_anims_enabled = enabled; lock.config.write(); + if enabled { + lock.thread_exit.store(true, Ordering::Release); + } - Self::notify_power_states( + Self::notify_device_state( &ctxt, - AnimePowerStates { - brightness: lock.config.brightness.floor() as u8, - enabled: lock.config.awake_enabled, - boot_anim_enabled: lock.config.boot_anim_enabled, + 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 will show boot, suspend, or off animations - async fn set_animation_enabled( + /// Set which builtin animation is used for each stage + async fn set_builtin_animations( &self, #[zbus(signal_context)] ctxt: SignalContext<'_>, - on: bool, + boot: AnimBooting, + awake: AnimAwake, + sleep: AnimSleeping, + shutdown: AnimShutdown, ) { let mut lock = self.0.lock().await; lock.node - .write_bytes(&pkt_for_set_boot(on)) + .write_bytes(&pkt_set_enable_powersave_anim(true)) .map_err(|err| { warn!("rog_anime::run_animation:callback {}", err); }) .ok(); lock.node - .write_bytes(&pkt_for_enable_animation()) + .write_bytes(&pkt_set_builtin_animations(boot, awake, sleep, shutdown)) .map_err(|err| { warn!("rog_anime::run_animation:callback {}", err); }) .ok(); - lock.config.boot_anim_enabled = on; + 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_power_states( + Self::notify_device_state( &ctxt, - AnimePowerStates { - brightness: lock.config.brightness.floor() as u8, - enabled: lock.config.awake_enabled, - boot_anim_enabled: lock.config.boot_anim_enabled, + 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 @@ -154,31 +196,26 @@ impl CtrlAnimeZbus { 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); + CtrlAnime::run_thread(self.0.clone(), lock.cache.system.clone(), false).await; } } - /// Get status of if the AniMe LEDs are on/displaying while system is awake - #[dbus_interface(property)] - async fn awake_enabled(&self) -> bool { + /// Get the device state as stored by asusd + // #[dbus_interface(property)] + async fn device_state(&self) -> DeviceState { let lock = self.0.lock().await; - lock.config.awake_enabled - } - - /// Get the status of if factory system-status animations are enabled - #[dbus_interface(property)] - async fn animation_enabled(&self) -> bool { - let lock = self.0.lock().await; - lock.config.boot_anim_enabled + 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_power_states( - ctxt: &SignalContext<'_>, - data: AnimePowerStates, - ) -> zbus::Result<()>; + async fn notify_device_state(ctxt: &SignalContext<'_>, data: DeviceState) -> zbus::Result<()>; } #[async_trait] @@ -198,7 +235,7 @@ impl crate::CtrlTask for CtrlAnimeZbus { let inner1 = inner1.clone(); async move { let lock = inner1.lock().await; - CtrlAnime::run_thread(inner1.clone(), lock.cache.sleep.clone(), true); + CtrlAnime::run_thread(inner1.clone(), lock.cache.sleep.clone(), true).await; } }, move || { @@ -206,7 +243,7 @@ impl crate::CtrlTask for CtrlAnimeZbus { let inner2 = inner2.clone(); async move { let lock = inner2.lock().await; - CtrlAnime::run_thread(inner2.clone(), lock.cache.wake.clone(), true); + CtrlAnime::run_thread(inner2.clone(), lock.cache.wake.clone(), true).await; } }, move || { @@ -214,7 +251,7 @@ impl crate::CtrlTask for CtrlAnimeZbus { let inner3 = inner3.clone(); async move { let lock = inner3.lock().await; - CtrlAnime::run_thread(inner3.clone(), lock.cache.shutdown.clone(), true); + CtrlAnime::run_thread(inner3.clone(), lock.cache.shutdown.clone(), true).await; } }, move || { @@ -222,7 +259,7 @@ impl crate::CtrlTask for CtrlAnimeZbus { let inner4 = inner4.clone(); async move { let lock = inner4.lock().await; - CtrlAnime::run_thread(inner4.clone(), lock.cache.boot.clone(), true); + CtrlAnime::run_thread(inner4.clone(), lock.cache.boot.clone(), true).await; } }, ) @@ -236,14 +273,26 @@ impl crate::CtrlTask for CtrlAnimeZbus { impl crate::Reloadable for CtrlAnimeZbus { async fn reload(&mut self) -> Result<(), RogError> { if let Some(lock) = self.0.try_lock() { - // TODO: restore new settings - // lock.node - // .write_bytes(&pkt_for_set_brightness(lock.config.awake_enabled))?; + let anim = &lock.config.builtin_anims; lock.node - .write_bytes(&pkt_for_set_boot(lock.config.boot_anim_enabled))?; + .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); + CtrlAnime::run_thread(self.0.clone(), action, true).await; } Ok(()) } diff --git a/rog-anime/src/data.rs b/rog-anime/src/data.rs index 2b630d44..dfdc8bf8 100644 --- a/rog-anime/src/data.rs +++ b/rog-anime/src/data.rs @@ -8,6 +8,7 @@ use serde_derive::{Deserialize, Serialize}; use zbus::zvariant::Type; use crate::error::{AnimeError, Result}; +use crate::usb::{AnimAwake, AnimBooting, AnimShutdown, AnimSleeping, Brightness}; use crate::{AnimTime, AnimeGif}; /// The first 7 bytes of a USB packet are accounted for by `USB_PREFIX1` and @@ -25,12 +26,22 @@ pub const USB_PREFIX2: [u8; 7] = [0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02]; /// Third packet is for GA402 matrix pub const USB_PREFIX3: [u8; 7] = [0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02]; +#[cfg_attr(feature = "dbus", derive(Type))] +#[derive(Default, Deserialize, PartialEq, Eq, Clone, Copy, Serialize, Debug)] +pub struct Animations { + pub boot: AnimBooting, + pub awake: AnimAwake, + pub sleep: AnimSleeping, + pub shutdown: AnimShutdown, +} + #[cfg_attr(feature = "dbus", derive(Type))] #[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] -pub struct AnimePowerStates { - pub brightness: u8, - pub enabled: bool, - pub boot_anim_enabled: bool, +pub struct DeviceState { + pub display_enabled: bool, + pub display_brightness: Brightness, + pub builtin_anims_enabled: bool, + pub builtin_anims: Animations, } #[cfg_attr(feature = "dbus", derive(Type))] diff --git a/rog-anime/src/error.rs b/rog-anime/src/error.rs index 687862c9..49f17c86 100644 --- a/rog-anime/src/error.rs +++ b/rog-anime/src/error.rs @@ -24,12 +24,14 @@ pub enum AnimeError { DataBufferLength, PixelGifWidth(usize), PixelGifHeight(usize), + ParseError(String), } impl fmt::Display for AnimeError { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + AnimeError::ParseError(e) => write!(f, "Could not parse {e}"), AnimeError::NoFrames => write!(f, "No frames in PNG"), AnimeError::Io(e) => write!(f, "Could not open: {}", e), AnimeError::Png(e) => write!(f, "PNG error: {}", e), diff --git a/rog-anime/src/usb.rs b/rog-anime/src/usb.rs index 57a26763..598b903b 100644 --- a/rog-anime/src/usb.rs +++ b/rog-anime/src/usb.rs @@ -23,11 +23,12 @@ pub const VENDOR_ID: u16 = 0x0b05; pub const PROD_ID: u16 = 0x193b; #[cfg_attr(feature = "dbus", derive(Type))] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] +#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] /// Base LED brightness of the display pub enum Brightness { Off, Low, + #[default] Med, High, } @@ -46,6 +47,97 @@ impl FromStr for Brightness { } } +impl From for Brightness { + fn from(v: u8) -> Brightness { + match v { + 0 => Brightness::Off, + 2 => Brightness::Low, + 3 => Brightness::High, + _ => Brightness::Med, + } + } +} + +#[cfg_attr(feature = "dbus", derive(Type))] +#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] +pub enum AnimBooting { + #[default] + GlitchConstruction, + StaticEmergence, +} + +impl FromStr for AnimBooting { + type Err = AnimeError; + + fn from_str(s: &str) -> Result { + match s { + "GlitchConstruction" => Ok(Self::GlitchConstruction), + "StaticEmergence" => Ok(Self::StaticEmergence), + _ => Err(AnimeError::ParseError(s.to_owned())), + } + } +} + +#[cfg_attr(feature = "dbus", derive(Type))] +#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] +pub enum AnimAwake { + #[default] + BinaryBannerScroll, + RogLogoGlitch, +} + +impl FromStr for AnimAwake { + type Err = AnimeError; + + fn from_str(s: &str) -> Result { + match s { + "BinaryBannerScroll" => Ok(Self::BinaryBannerScroll), + "RogLogoGlitch" => Ok(Self::RogLogoGlitch), + _ => Err(AnimeError::ParseError(s.to_owned())), + } + } +} + +#[cfg_attr(feature = "dbus", derive(Type))] +#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] +pub enum AnimSleeping { + #[default] + BannerSwipe, + Starfield, +} + +impl FromStr for AnimSleeping { + type Err = AnimeError; + + fn from_str(s: &str) -> Result { + match s { + "BannerSwipe" => Ok(Self::BannerSwipe), + "Starfield" => Ok(Self::Starfield), + _ => Err(AnimeError::ParseError(s.to_owned())), + } + } +} + +#[cfg_attr(feature = "dbus", derive(Type))] +#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] +pub enum AnimShutdown { + #[default] + GlitchOut, + SeeYa, +} + +impl FromStr for AnimShutdown { + type Err = AnimeError; + + fn from_str(s: &str) -> Result { + match s { + "GlitchOut" => Ok(Self::GlitchOut), + "SeeYa" => Ok(Self::SeeYa), + _ => Err(AnimeError::ParseError(s.to_owned())), + } + } +} + /// `get_anime_type` is very broad, matching on part of the laptop board name /// only. For this reason `find_node()` must be used also to verify if the USB /// device is available. @@ -89,7 +181,7 @@ pub const fn pkts_for_init() -> [[u8; PACKET_SIZE]; 2] { /// Should be written to the device after writing the two main data packets that /// make up the display data packet #[inline] -pub const fn pkt_for_flush() -> [u8; PACKET_SIZE] { +pub const fn pkt_flush() -> [u8; PACKET_SIZE] { let mut pkt = [0; PACKET_SIZE]; pkt[0] = DEV_PAGE; pkt[1] = 0xc0; @@ -97,23 +189,10 @@ pub const fn pkt_for_flush() -> [u8; PACKET_SIZE] { pkt } -/// Get the packet required for setting the device to on, on boot. Requires +/// Packet for setting the brightness (0-3). Requires /// `pkt_for_apply()` to be written after. #[inline] -pub const fn pkt_for_set_boot(status: bool) -> [u8; PACKET_SIZE] { - let mut pkt = [0; PACKET_SIZE]; - pkt[0] = DEV_PAGE; - pkt[1] = 0xc3; - pkt[2] = 0x01; - pkt[3] = if status { 0x00 } else { 0x80 }; - pkt -} - -/// Get the packet required for setting the device to on. Requires -/// `pkt_for_apply()` to be written after. -// TODO: change the users of this method -#[inline] -pub const fn pkt_for_set_brightness(brightness: Brightness) -> [u8; PACKET_SIZE] { +pub const fn pkt_set_brightness(brightness: Brightness) -> [u8; PACKET_SIZE] { let mut pkt = [0; PACKET_SIZE]; pkt[0] = DEV_PAGE; pkt[1] = 0xc0; @@ -122,23 +201,42 @@ pub const fn pkt_for_set_brightness(brightness: Brightness) -> [u8; PACKET_SIZE] pkt } +/// Enable the display? #[inline] -pub const fn pkt_for_set_awake_enabled(enable: bool) -> [u8; PACKET_SIZE] { +pub const fn pkt_set_enable_display(status: bool) -> [u8; PACKET_SIZE] { let mut pkt = [0; PACKET_SIZE]; pkt[0] = DEV_PAGE; pkt[1] = 0xc3; pkt[2] = 0x01; - pkt[3] = if !enable { 0x80 } else { 0x00 }; + pkt[3] = if status { 0x00 } else { 0x80 }; pkt } -/// Packet required to apply a device setting +/// Enable builtin animations? #[inline] -pub const fn pkt_for_enable_animation() -> [u8; PACKET_SIZE] { +pub const fn pkt_set_enable_powersave_anim(status: bool) -> [u8; PACKET_SIZE] { let mut pkt = [0; PACKET_SIZE]; pkt[0] = DEV_PAGE; pkt[1] = 0xc4; pkt[2] = 0x01; - pkt[3] = 0x80; + pkt[3] = if status { 0x00 } else { 0x80 }; + pkt +} + +/// Set which animations are shown for each stage +#[inline] +pub const fn pkt_set_builtin_animations( + boot: AnimBooting, + awake: AnimAwake, + sleep: AnimSleeping, + shutdown: AnimShutdown, +) -> [u8; PACKET_SIZE] { + let mut pkt = [0; PACKET_SIZE]; + pkt[0] = DEV_PAGE; + pkt[1] = 0xc5; + pkt[2] = (awake as u8) + | ((sleep as u8) << 0x01) + | ((shutdown as u8) << 0x02) + | ((boot as u8) << 0x03); pkt } diff --git a/rog-control-center/src/system_state.rs b/rog-control-center/src/system_state.rs index 6ef348a8..a2ccfd6d 100644 --- a/rog-control-center/src/system_state.rs +++ b/rog-control-center/src/system_state.rs @@ -5,6 +5,8 @@ use std::time::SystemTime; use egui::Vec2; use log::error; +use rog_anime::usb::Brightness; +use rog_anime::Animations; use rog_aura::layouts::KeyLayout; use rog_aura::usb::AuraPowerDev; use rog_aura::{AuraEffect, AuraModeNum}; @@ -194,29 +196,25 @@ impl AuraState { #[derive(Clone, Debug, Default)] pub struct AnimeState { - pub bright: u8, - pub boot: bool, - pub awake: bool, - pub sleep: bool, + pub display_enabled: bool, + pub display_brightness: Brightness, + pub builtin_anims_enabled: bool, + pub builtin_anims: Animations, } impl AnimeState { pub fn new(supported: &SupportedFunctions, dbus: &RogDbusClientBlocking<'_>) -> Result { - Ok(Self { - boot: if supported.anime_ctrl.0 { - dbus.proxies().anime().animation_enabled()? - } else { - false - }, - awake: if supported.anime_ctrl.0 { - dbus.proxies().anime().awake_enabled()? - } else { - false - }, - // TODO: - sleep: false, - bright: 200, - }) + if supported.anime_ctrl.0 { + let device_state = dbus.proxies().anime().device_state()?; + Ok(Self { + display_enabled: device_state.display_enabled, + display_brightness: device_state.display_brightness, + builtin_anims_enabled: device_state.builtin_anims_enabled, + builtin_anims: device_state.builtin_anims, + }) + } else { + Ok(Default::default()) + } } } @@ -429,12 +427,7 @@ impl Default for SystemState { wave_green: Default::default(), wave_blue: Default::default(), }, - anime: AnimeState { - bright: Default::default(), - boot: Default::default(), - awake: Default::default(), - sleep: Default::default(), - }, + anime: AnimeState::default(), profiles: ProfilesState { list: Default::default(), current: Default::default(), diff --git a/rog-control-center/src/update_and_notify.rs b/rog-control-center/src/update_and_notify.rs index 57ff6751..d76afa01 100644 --- a/rog-control-center/src/update_and_notify.rs +++ b/rog-control-center/src/update_and_notify.rs @@ -269,7 +269,7 @@ pub fn start_notifications( e }) .unwrap(); - if let Ok(p) = proxy.receive_power_states().await { + if let Ok(p) = proxy.receive_device_state().await { info!("Started zbus signal thread: receive_power_states"); p.for_each(|_| { if let Ok(_lock) = page_states1.lock() { diff --git a/rog-control-center/src/widgets/anime_power.rs b/rog-control-center/src/widgets/anime_power.rs index 4b258f8d..6f1b0f27 100644 --- a/rog-control-center/src/widgets/anime_power.rs +++ b/rog-control-center/src/widgets/anime_power.rs @@ -1,4 +1,5 @@ use egui::{RichText, Ui}; +use rog_anime::usb::Brightness; use rog_platform::supported::SupportedFunctions; use crate::system_state::SystemState; @@ -7,42 +8,50 @@ pub fn anime_power_group(_supported: &SupportedFunctions, states: &mut SystemSta ui.heading("AniMe Matrix Settings"); ui.label("Options are incomplete. Awake + Boot should work"); - let mut changed = false; + let mut brightness = states.anime.display_brightness as u8; ui.horizontal_wrapped(|ui| { ui.vertical(|ui| { let h = 16.0; ui.set_row_height(22.0); ui.horizontal_wrapped(|ui| { - ui.label(RichText::new("Brightness").size(h)); + ui.label(RichText::new("Display brightness").size(h)); }); ui.horizontal_wrapped(|ui| { - ui.label(RichText::new("Boot").size(h)); + ui.label(RichText::new("Display enabled").size(h)); }); ui.horizontal_wrapped(|ui| { - ui.label(RichText::new("Awake").size(h)); - }); - ui.horizontal_wrapped(|ui| { - ui.label(RichText::new("Sleep").size(h)); + ui.label(RichText::new("Animations enabled").size(h)); }); + // ui.horizontal_wrapped(|ui| { + // ui.label(RichText::new("Sleep").size(h)); + // }); }); ui.vertical(|ui| { ui.set_row_height(22.0); + ui.horizontal_wrapped(|ui| { + if ui.add(egui::Slider::new(&mut brightness, 0..=3)).changed() { + states + .asus_dbus + .proxies() + .anime() + .set_brightness(Brightness::from(brightness)) + .map_err(|err| { + states.error = Some(err.to_string()); + }) + .ok(); + } + }); ui.horizontal_wrapped(|ui| { if ui - .add(egui::Slider::new(&mut states.anime.bright, 0..=254)) + .checkbox(&mut states.anime.display_enabled, "Enable") .changed() { - changed = true; - } - }); - ui.horizontal_wrapped(|ui| { - if ui.checkbox(&mut states.anime.boot, "Enable").changed() { states .asus_dbus .proxies() .anime() - .set_animation_enabled(states.anime.boot) + .set_enable_display(states.anime.display_enabled) .map_err(|err| { states.error = Some(err.to_string()); }) @@ -50,23 +59,26 @@ pub fn anime_power_group(_supported: &SupportedFunctions, states: &mut SystemSta } }); ui.horizontal_wrapped(|ui| { - if ui.checkbox(&mut states.anime.awake, "Enable").changed() { + if ui + .checkbox(&mut states.anime.builtin_anims_enabled, "Enable") + .changed() + { states .asus_dbus .proxies() .anime() - .set_awake_enabled(states.anime.awake) + .set_builtins_enabled(states.anime.builtin_anims_enabled) .map_err(|err| { states.error = Some(err.to_string()); }) .ok(); } }); - ui.horizontal_wrapped(|ui| { - if ui.checkbox(&mut states.anime.sleep, "Enable").changed() { - changed = true; - } - }); + // ui.horizontal_wrapped(|ui| { + // if ui.checkbox(&mut states.anime.sleep, "Enable").changed() { + // changed = true; + // } + // }); }); }); } diff --git a/rog-dbus/Cargo.toml b/rog-dbus/Cargo.toml index b3118d92..a0b98241 100644 --- a/rog-dbus/Cargo.toml +++ b/rog-dbus/Cargo.toml @@ -10,7 +10,7 @@ description = "dbus interface methods for asusctl" edition = "2021" [dependencies] -rog_anime = { path = "../rog-anime" } +rog_anime = { path = "../rog-anime", features = ["dbus"] } rog_aura = { path = "../rog-aura" } rog_profiles = { path = "../rog-profiles" } rog_platform = { path = "../rog-platform" } diff --git a/rog-dbus/src/zbus_anime.rs b/rog-dbus/src/zbus_anime.rs index 43eadcd2..46ab143d 100644 --- a/rog-dbus/src/zbus_anime.rs +++ b/rog-dbus/src/zbus_anime.rs @@ -1,5 +1,5 @@ -use rog_anime::usb::Brightness; -use rog_anime::{AnimeDataBuffer, AnimePowerStates}; +use rog_anime::usb::{AnimAwake, AnimBooting, AnimShutdown, AnimSleeping, Brightness}; +use rog_anime::{AnimeDataBuffer, DeviceState as AnimeDeviceState}; use zbus::dbus_proxy; #[dbus_proxy( @@ -7,32 +7,36 @@ use zbus::dbus_proxy; default_path = "/org/asuslinux/Anime" )] trait Anime { - /// Set whether the AniMe will show boot, suspend, or off animations - fn set_animation_enabled(&self, status: bool) -> zbus::Result<()>; + /// Set the global (image) brightness + fn set_image_brightness(&self, bright: f32) -> zbus::Result<()>; /// Set the global base brightness fn set_brightness(&self, bright: Brightness) -> zbus::Result<()>; - /// Set the global (image) brightness - fn set_image_brightness(&self, bright: f32) -> zbus::Result<()>; + /// Set whether the AniMe will show boot, suspend, or off animations + fn set_builtins_enabled(&self, enabled: bool) -> zbus::Result<()>; + + /// Set which builtin animation is used for each stage + fn set_builtin_animations( + &self, + boot: AnimBooting, + awake: AnimAwake, + sleep: AnimSleeping, + shutdown: AnimShutdown, + ) -> zbus::Result<()>; /// Set whether the AniMe is displaying images/data - fn set_awake_enabled(&self, status: bool) -> zbus::Result<()>; + fn set_enable_display(&self, status: bool) -> zbus::Result<()>; /// Writes a data stream of length. Will force system thread to exit until /// it is restarted fn write(&self, input: AnimeDataBuffer) -> zbus::Result<()>; - /// Get status of if the AniMe LEDs are on - #[dbus_proxy(property)] - fn awake_enabled(&self) -> zbus::Result; - - /// Get the status of if factory system-status animations are enabled - #[dbus_proxy(property)] - fn animation_enabled(&self) -> zbus::Result; + // #[dbus_proxy(property)] + fn device_state(&self) -> zbus::Result; /// Notify listeners of the status of AniMe LED power and factory /// system-status animations #[dbus_proxy(signal)] - fn power_states(&self, data: AnimePowerStates) -> zbus::Result<()>; + fn device_state(&self, data: AnimeDeviceState) -> zbus::Result<()>; }