Files
asusctl/asusd/src/ctrl_anime/trait_impls.rs
T
Luke D. Jones fa043adc99 rog-platform: add CPU and GPU tunings
rog-platform: add tunables to supported dat

Anime: fixes to how some power options work
2023-11-17 17:16:03 +13:00

397 lines
14 KiB
Rust

use std::sync::atomic::Ordering;
use std::sync::Arc;
use async_trait::async_trait;
use config_traits::StdConfig;
use log::warn;
use logind_zbus::manager::ManagerProxy;
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, CacheProperties, Connection, SignalContext};
use super::CtrlAnime;
use crate::error::RogError;
pub(super) const ZBUS_PATH: &str = "/org/asuslinux/Anime";
async fn get_logind_manager<'a>() -> ManagerProxy<'a> {
let connection = Connection::system()
.await
.expect("Controller could not create dbus connection");
ManagerProxy::builder(&connection)
.cache_properties(CacheProperties::No)
.build()
.await
.expect("Controller could not create ManagerProxy")
}
#[derive(Clone)]
pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>);
/// 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!("ctrl_anime::run_animation:callback {}", err);
err
})?;
Ok(())
}
/// Set base brightness level
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!("ctrl_anime::set_brightness {}", err);
})
.ok();
lock.node
.write_bytes(&pkt_set_enable_display(brightness != Brightness::Off))
.map_err(|err| {
warn!("ctrl_anime::set_brightness {}", err);
})
.ok();
lock.config.display_enabled = brightness != Brightness::Off;
lock.config.display_brightness = brightness;
lock.config.write();
Self::notify_device_state(&ctxt, DeviceState::from(&lock.config))
.await
.ok();
}
/// Enable the builtin animations or not. This is quivalent to "Powersave
/// animations" in Armory crate
async fn set_builtins_enabled(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
enabled: bool,
) {
let mut lock = self.0.lock().await;
lock.node
.set_builtins_enabled(enabled, lock.config.display_brightness)
.map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err);
})
.ok();
if !enabled {
let data = vec![255u8; lock.anime_type.data_length()];
if let Ok(tmp) = AnimeDataBuffer::from_vec(lock.anime_type, data).map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err);
}) {
lock.node
.write_bytes(tmp.data())
.map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", 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::from(&lock.config))
.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_builtin_animations(boot, awake, sleep, shutdown))
.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
})
.ok();
lock.node
.write_bytes(&pkt_set_enable_powersave_anim(true))
.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
})
.ok();
lock.config.display_enabled = true;
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::from(&lock.config))
.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!("ctrl_anime::run_animation:callback {}", err);
})
.ok();
lock.config.display_enabled = enabled;
lock.config.write();
Self::notify_device_state(&ctxt, DeviceState::from(&lock.config))
.await
.ok();
}
/// Set if to turn the AniMe Matrix off when external power is unplugged
async fn set_off_when_unplugged(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
enabled: bool,
) {
let mut lock = self.0.lock().await;
let manager = get_logind_manager().await;
let pow = manager.on_external_power().await.unwrap_or_default();
lock.node
.write_bytes(&pkt_set_enable_display(!pow && !enabled))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
})
.ok();
lock.config.off_when_unplugged = enabled;
lock.config.write();
Self::notify_device_state(&ctxt, DeviceState::from(&lock.config))
.await
.ok();
}
/// Set if to turn the AniMe Matrix off when the laptop is suspended
async fn set_off_when_suspended(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
enabled: bool,
) {
let mut lock = self.0.lock().await;
lock.config.off_when_suspended = enabled;
lock.config.write();
Self::notify_device_state(&ctxt, DeviceState::from(&lock.config))
.await
.ok();
}
/// Set if to turn the AniMe Matrix off when the lid is closed
async fn set_off_when_lid_closed(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
enabled: bool,
) {
let mut lock = self.0.lock().await;
let manager = get_logind_manager().await;
let lid = manager.lid_closed().await.unwrap_or_default();
lock.node
.write_bytes(&pkt_set_enable_display(lid && !enabled))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
})
.ok();
lock.config.off_when_lid_closed = enabled;
lock.config.write();
Self::notify_device_state(&ctxt, DeviceState::from(&lock.config))
.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::from(&lock.config)
}
/// 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 |sleeping| {
// on_sleep
let inner = inner1.clone();
async move {
let lock = inner.lock().await;
if lock.config.display_enabled {
lock.thread_exit.store(true, Ordering::Release); // ensure clean slate
lock.node
.write_bytes(&pkt_set_enable_display(
!(sleeping && lock.config.off_when_suspended),
))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err);
})
.ok();
if !sleeping && !lock.config.builtin_anims_enabled {
CtrlAnime::run_thread(inner.clone(), lock.cache.wake.clone(), true)
.await;
}
}
}
},
move |shutting_down| {
// on_shutdown
let inner = inner2.clone();
async move {
let lock = inner.lock().await;
if lock.config.display_enabled && !lock.config.builtin_anims_enabled {
if shutting_down {
CtrlAnime::run_thread(inner.clone(), lock.cache.shutdown.clone(), true)
.await;
} else {
CtrlAnime::run_thread(inner.clone(), lock.cache.boot.clone(), true)
.await;
}
}
}
},
move |lid_closed| {
let inner = inner3.clone();
// on lid change
async move {
let lock = inner.lock().await;
if lock.config.off_when_lid_closed {
lock.node
.write_bytes(&pkt_set_enable_display(lid_closed))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
})
.ok();
}
}
},
move |power_plugged| {
let inner = inner4.clone();
// on power change
async move {
let lock = inner.lock().await;
if lock.config.off_when_unplugged {
lock.node
.write_bytes(&pkt_set_enable_display(power_plugged))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_unplugged {}", err);
})
.ok();
} else {
lock.node
.write_bytes(&pkt_set_brightness(lock.config.brightness_on_battery))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_unplugged {}", err);
})
.ok();
}
}
},
)
.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;
// Set builtins
if lock.config.builtin_anims_enabled {
lock.node.write_bytes(&pkt_set_builtin_animations(
anim.boot,
anim.awake,
anim.sleep,
anim.shutdown,
))?;
}
// Builtins enabled or na?
lock.node.set_builtins_enabled(
lock.config.builtin_anims_enabled,
lock.config.display_brightness,
)?;
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(())
}
}