Files
asusctl/asusd/src/aura_anime/trait_impls.rs
Denis Benato 180566e5f1 Fix: share a single HID device
Avoid opening multiple handles to the same device whenever possible.
2025-10-22 22:05:17 +02:00

508 lines
17 KiB
Rust

use std::sync::atomic::Ordering;
use config_traits::StdConfig;
use log::{debug, error, 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, Brightness,
};
use rog_anime::{Animations, AnimeDataBuffer, DeviceState};
use zbus::object_server::SignalEmitter;
use zbus::proxy::CacheProperties;
use zbus::zvariant::OwnedObjectPath;
use zbus::{interface, Connection};
use super::config::AniMeConfig;
use super::AniMe;
use crate::error::RogError;
use crate::Reloadable;
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 AniMeZbus(AniMe);
impl AniMeZbus {
pub fn new(anime: AniMe) -> Self {
Self(anime)
}
pub async fn start_tasks(
mut self,
connection: &Connection,
path: OwnedObjectPath,
) -> Result<(), RogError> {
// let task = zbus.clone();
self.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
connection
.object_server()
.at(path.clone(), self)
.await
.map_err(|e| {
error!("Couldn't add server at path: {path}, {e:?}");
e
})?;
debug!("start_tasks was successful");
Ok(())
}
}
// 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.
#[interface(name = "xyz.ljones.Anime")]
impl AniMeZbus {
/// 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 bright = self.0.config.lock().await.display_brightness;
if self.0.config.lock().await.builtin_anims_enabled {
// This clears the display, causing flickers if done indiscriminately on every
// write. Therefore, we guard it behind a config check.
self.0.set_builtins_enabled(false, bright).await?;
}
self.0.thread_exit.store(true, Ordering::SeqCst);
self.0.write_data_buffer(input).await.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
err
})?;
Ok(())
}
/// Set base brightness level
#[zbus(property)]
async fn brightness(&self) -> Brightness {
if let Ok(config) = self.0.config.try_lock() {
return config.display_brightness;
}
Brightness::Off
}
/// Set base brightness level
#[zbus(property)]
async fn set_brightness(&self, brightness: Brightness) {
self.0
.write_bytes(&pkt_set_brightness(brightness))
.await
.map_err(|err| {
warn!("ctrl_anime::set_brightness {}", err);
})
.ok();
self.0
.write_bytes(&pkt_set_enable_display(brightness != Brightness::Off))
.await
.map_err(|err| {
warn!("ctrl_anime::set_brightness {}", err);
})
.ok();
let mut config = self.0.config.lock().await;
config.display_enabled = brightness != Brightness::Off;
config.display_brightness = brightness;
config.write();
}
#[zbus(property)]
async fn builtins_enabled(&self) -> bool {
if let Ok(config) = self.0.config.try_lock() {
return config.builtin_anims_enabled;
}
false
}
/// Enable the builtin animations or not. This is quivalent to "Powersave
/// animations" in Armory crate
#[zbus(property)]
async fn set_builtins_enabled(&self, enabled: bool) {
let mut config = self.0.config.lock().await;
let brightness = config.display_brightness;
self.0
.set_builtins_enabled(enabled, brightness)
.await
.map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err);
})
.ok();
if !enabled {
let anime_type = config.anime_type;
let data = vec![255u8; anime_type.data_length()];
if let Ok(tmp) = AnimeDataBuffer::from_vec(anime_type, data).map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err);
}) {
self.0
.write_bytes(tmp.data())
.await
.map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err);
})
.ok();
}
}
config.builtin_anims_enabled = enabled;
config.write();
if enabled {
self.0.thread_exit.store(true, Ordering::Release);
}
}
#[zbus(property)]
async fn builtin_animations(&self) -> Animations {
if let Ok(config) = self.0.config.try_lock() {
return config.builtin_anims;
}
Animations::default()
}
/// Set which builtin animation is used for each stage
#[zbus(property)]
async fn set_builtin_animations(&self, settings: Animations) {
self.0
.write_bytes(&pkt_set_builtin_animations(
settings.boot, settings.awake, settings.sleep, settings.shutdown,
))
.await
.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
})
.ok();
self.0
.write_bytes(&pkt_set_enable_powersave_anim(true))
.await
.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
})
.ok();
let mut config = self.0.config.lock().await;
config.display_enabled = true;
config.builtin_anims = settings;
config.write();
}
#[zbus(property)]
async fn enable_display(&self) -> bool {
if let Ok(config) = self.0.config.try_lock() {
return config.display_enabled;
}
false
}
/// Set whether the AniMe is enabled at all
#[zbus(property)]
async fn set_enable_display(&self, enabled: bool) {
self.0
.write_bytes(&pkt_set_enable_display(enabled))
.await
.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
})
.ok();
let mut config = self.0.config.lock().await;
config.display_enabled = enabled;
config.write();
}
#[zbus(property)]
async fn off_when_unplugged(&self) -> bool {
if let Ok(config) = self.0.config.try_lock() {
return config.off_when_unplugged;
}
false
}
/// Set if to turn the AniMe Matrix off when external power is unplugged
#[zbus(property)]
async fn set_off_when_unplugged(&self, enabled: bool) {
let manager = get_logind_manager().await;
let pow = manager.on_external_power().await.unwrap_or_default();
self.0
.write_bytes(&pkt_set_enable_display(!pow && !enabled))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
})
.ok();
let mut config = self.0.config.lock().await;
config.off_when_unplugged = enabled;
config.write();
}
#[zbus(property)]
async fn off_when_suspended(&self) -> bool {
if let Ok(config) = self.0.config.try_lock() {
return config.off_when_suspended;
}
false
}
/// Set if to turn the AniMe Matrix off when the laptop is suspended
#[zbus(property)]
async fn set_off_when_suspended(&self, enabled: bool) {
let mut config = self.0.config.lock().await;
config.off_when_suspended = enabled;
config.write();
}
#[zbus(property)]
async fn off_when_lid_closed(&self) -> bool {
if let Ok(config) = self.0.config.try_lock() {
return config.off_when_lid_closed;
}
false
}
/// Set if to turn the AniMe Matrix off when the lid is closed
#[zbus(property)]
async fn set_off_when_lid_closed(&self, enabled: bool) {
let manager = get_logind_manager().await;
let lid = manager.lid_closed().await.unwrap_or_default();
self.0
.write_bytes(&pkt_set_enable_display(lid && !enabled))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
})
.ok();
let mut config = self.0.config.lock().await;
config.off_when_lid_closed = enabled;
config.write();
}
/// 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 {
self.0.thread_exit.store(true, Ordering::SeqCst);
self.0.run_thread(self.0.cache.system.clone(), false).await;
}
}
/// Get the device state as stored by asusd
// #[zbus(property)]
async fn device_state(&self) -> DeviceState {
DeviceState::from(&*self.0.config.lock().await)
}
}
impl crate::CtrlTask for AniMeZbus {
fn zbus_path() -> &'static str {
"ANIME_ZBUS_PATH"
}
async fn create_tasks(&self, _: SignalEmitter<'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 config = inner.config.lock().await.clone();
if config.display_enabled {
inner.thread_exit.store(true, Ordering::Release); // ensure clean slate
inner
.write_bytes(&pkt_set_enable_display(
!(sleeping && config.off_when_suspended),
))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err);
})
.ok();
if config.builtin_anims_enabled {
inner
.write_bytes(&pkt_set_enable_powersave_anim(
!(sleeping && config.off_when_suspended),
))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err);
})
.ok();
} else if !sleeping && !config.builtin_anims_enabled {
// Run custom wake animation
inner
.write_bytes(&pkt_set_enable_powersave_anim(false))
.await
.ok(); // ensure builtins are disabled
inner.run_thread(inner.cache.wake.clone(), true).await;
}
}
}
},
move |shutting_down| {
// on_shutdown
let inner = inner2.clone();
async move {
let AniMeConfig {
display_enabled,
builtin_anims_enabled,
..
} = *inner.config.lock().await;
if display_enabled && !builtin_anims_enabled {
if shutting_down {
inner.run_thread(inner.cache.shutdown.clone(), true).await;
} else {
inner.run_thread(inner.cache.boot.clone(), true).await;
}
}
}
},
move |lid_closed| {
let inner = inner3.clone();
// on lid change
async move {
let AniMeConfig {
off_when_lid_closed,
builtin_anims_enabled,
..
} = *inner.config.lock().await;
if off_when_lid_closed {
if builtin_anims_enabled {
inner
.write_bytes(&pkt_set_enable_powersave_anim(!lid_closed))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err);
})
.ok();
}
inner
.write_bytes(&pkt_set_enable_display(!lid_closed))
.await
.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 AniMeConfig {
off_when_unplugged,
builtin_anims_enabled,
brightness_on_battery,
..
} = *inner.config.lock().await;
if off_when_unplugged {
if builtin_anims_enabled {
inner
.write_bytes(&pkt_set_enable_powersave_anim(power_plugged))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err);
})
.ok();
}
inner
.write_bytes(&pkt_set_enable_display(power_plugged))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_unplugged {}", err);
})
.ok();
} else {
inner
.write_bytes(&pkt_set_brightness(brightness_on_battery))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_unplugged {}", err);
})
.ok();
}
}
},
)
.await;
Ok(())
}
}
impl crate::Reloadable for AniMeZbus {
async fn reload(&mut self) -> Result<(), RogError> {
let AniMeConfig {
builtin_anims_enabled,
builtin_anims,
display_enabled,
display_brightness,
off_when_lid_closed,
off_when_unplugged,
..
} = *self.0.config.lock().await;
// Set builtins
if builtin_anims_enabled {
self.0
.write_bytes(&pkt_set_builtin_animations(
builtin_anims.boot,
builtin_anims.awake,
builtin_anims.sleep,
builtin_anims.shutdown,
))
.await?;
}
// Builtins enabled or na?
self.0
.set_builtins_enabled(builtin_anims_enabled, display_brightness)
.await?;
let manager = get_logind_manager().await;
let lid_closed = manager.lid_closed().await.unwrap_or_default();
let power_plugged = manager.on_external_power().await.unwrap_or_default();
let turn_off =
(lid_closed && off_when_lid_closed) || (!power_plugged && off_when_unplugged);
self.0
.write_bytes(&pkt_set_enable_display(!turn_off))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::reload {}", err);
})
.ok();
if turn_off || !display_enabled {
self.0.write_bytes(&pkt_set_enable_display(false)).await?;
// early return so we don't run animation thread
return Ok(());
}
if !builtin_anims_enabled && !self.0.cache.boot.is_empty() {
self.0
.write_bytes(&pkt_set_enable_powersave_anim(false))
.await
.ok();
let action = self.0.cache.boot.clone();
self.0.run_thread(action, true).await;
}
Ok(())
}
}