mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
350 lines
12 KiB
Rust
350 lines
12 KiB
Rust
use std::collections::BTreeMap;
|
|
|
|
use config_traits::StdConfig;
|
|
use log::{debug, error, info, warn};
|
|
use rog_aura::keyboard::{AuraLaptopUsbPackets, LaptopAuraPower};
|
|
use rog_aura::{AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, LedBrightness, PowerZones};
|
|
use zbus::fdo::Error as ZbErr;
|
|
use zbus::object_server::SignalEmitter;
|
|
use zbus::zvariant::OwnedObjectPath;
|
|
use zbus::{interface, Connection};
|
|
|
|
use super::Aura;
|
|
use crate::error::RogError;
|
|
use crate::{CtrlTask, Reloadable};
|
|
|
|
pub const AURA_ZBUS_NAME: &str = "Aura";
|
|
pub const AURA_ZBUS_PATH: &str = "/xyz/ljones";
|
|
|
|
#[derive(Clone)]
|
|
pub struct AuraZbus(Aura);
|
|
|
|
impl AuraZbus {
|
|
pub fn new(aura: Aura) -> Self {
|
|
Self(aura)
|
|
}
|
|
|
|
pub async fn start_tasks(
|
|
mut self,
|
|
connection: &Connection,
|
|
// _signal_ctx: SignalEmitter<'static>,
|
|
path: OwnedObjectPath,
|
|
) -> Result<(), RogError> {
|
|
// let task = zbus.clone();
|
|
// let signal_ctx = signal_ctx.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:?}"))
|
|
.ok();
|
|
// TODO: skip this until we keep handles to tasks so they can be killed
|
|
// task.create_tasks(signal_ctx).await
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// The main interface for changing, reading, or notfying
|
|
///
|
|
/// LED commands are split between Brightness, Modes, Per-Key
|
|
#[interface(name = "xyz.ljones.Aura")]
|
|
impl AuraZbus {
|
|
/// Return the device type for this Aura keyboard
|
|
#[zbus(property)]
|
|
async fn device_type(&self) -> AuraDeviceType {
|
|
self.0.config.lock().await.led_type
|
|
}
|
|
|
|
/// Return the current LED brightness
|
|
#[zbus(property)]
|
|
async fn brightness(&self) -> Result<LedBrightness, ZbErr> {
|
|
if let Some(bl) = self.0.backlight.as_ref() {
|
|
return Ok(bl.lock().await.get_brightness().map(|n| n.into())?);
|
|
}
|
|
Err(ZbErr::Failed("No sysfs brightness control".to_string()))
|
|
}
|
|
|
|
/// Set the keyboard brightness level (0-3)
|
|
#[zbus(property)]
|
|
async fn set_brightness(&mut self, brightness: LedBrightness) -> Result<(), ZbErr> {
|
|
if let Some(bl) = self.0.backlight.as_ref() {
|
|
let res = bl.lock().await.set_brightness(brightness.into());
|
|
if res.is_ok() {
|
|
let mut config = self.0.config.lock().await;
|
|
config.brightness = brightness;
|
|
config.write();
|
|
}
|
|
return Ok(res?);
|
|
}
|
|
Err(ZbErr::Failed("No sysfs brightness control".to_string()))
|
|
}
|
|
|
|
/// Total levels of brightness available
|
|
#[zbus(property)]
|
|
async fn supported_brightness(&self) -> Vec<LedBrightness> {
|
|
vec![
|
|
LedBrightness::Off,
|
|
LedBrightness::Low,
|
|
LedBrightness::Med,
|
|
LedBrightness::High,
|
|
]
|
|
}
|
|
|
|
/// The total available modes
|
|
#[zbus(property)]
|
|
async fn supported_basic_modes(&self) -> Result<Vec<AuraModeNum>, ZbErr> {
|
|
let config = self.0.config.lock().await;
|
|
Ok(config.builtins.keys().cloned().collect())
|
|
}
|
|
|
|
#[zbus(property)]
|
|
async fn supported_basic_zones(&self) -> Result<Vec<AuraZone>, ZbErr> {
|
|
let config = self.0.config.lock().await;
|
|
Ok(config.support_data.basic_zones.clone())
|
|
}
|
|
|
|
#[zbus(property)]
|
|
async fn supported_power_zones(&self) -> Result<Vec<PowerZones>, ZbErr> {
|
|
let config = self.0.config.lock().await;
|
|
Ok(config.support_data.power_zones.clone())
|
|
}
|
|
|
|
/// The current mode data
|
|
#[zbus(property)]
|
|
async fn led_mode(&self) -> Result<AuraModeNum, ZbErr> {
|
|
// entirely possible to deadlock here, so use try instead of lock()
|
|
// let ctrl = self.0.lock().await;
|
|
// Ok(config.current_mode)
|
|
if let Ok(config) = self.0.config.try_lock() {
|
|
Ok(config.current_mode)
|
|
} else {
|
|
Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
|
|
}
|
|
}
|
|
|
|
/// Set an Aura effect if the effect mode or zone is supported.
|
|
///
|
|
/// On success the aura config file is read to refresh cached values, then
|
|
/// the effect is stored and config written to disk.
|
|
#[zbus(property)]
|
|
async fn set_led_mode(&mut self, num: AuraModeNum) -> Result<(), ZbErr> {
|
|
let mut config = self.0.config.lock().await;
|
|
config.current_mode = num;
|
|
self.0.write_current_config_mode(&mut config).await?;
|
|
if config.brightness == LedBrightness::Off {
|
|
config.brightness = LedBrightness::Med;
|
|
}
|
|
self.0.set_brightness(config.brightness.into()).await?;
|
|
config.write();
|
|
Ok(())
|
|
}
|
|
|
|
/// The current mode data
|
|
#[zbus(property)]
|
|
async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> {
|
|
// entirely possible to deadlock here, so use try instead of lock()
|
|
if let Ok(config) = self.0.config.try_lock() {
|
|
let mode = config.current_mode;
|
|
match config.builtins.get(&mode) {
|
|
Some(effect) => Ok(effect.clone()),
|
|
None => Err(ZbErr::Failed("Could not get the current effect".into())),
|
|
}
|
|
} else {
|
|
Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
|
|
}
|
|
}
|
|
|
|
/// Set an Aura effect if the effect mode or zone is supported.
|
|
///
|
|
/// On success the aura config file is read to refresh cached values, then
|
|
/// the effect is stored and config written to disk.
|
|
#[zbus(property)]
|
|
async fn set_led_mode_data(&mut self, effect: AuraEffect) -> Result<(), ZbErr> {
|
|
let mut config = self.0.config.lock().await;
|
|
if !config.support_data.basic_modes.contains(&effect.mode)
|
|
|| effect.zone != AuraZone::None
|
|
&& !config.support_data.basic_zones.contains(&effect.zone)
|
|
{
|
|
return Err(ZbErr::NotSupported(format!(
|
|
"The Aura effect is not supported: {effect:?}"
|
|
)));
|
|
}
|
|
|
|
self.0
|
|
.write_effect_and_apply(config.led_type, &effect)
|
|
.await?;
|
|
if config.brightness == LedBrightness::Off {
|
|
config.brightness = LedBrightness::Med;
|
|
}
|
|
self.0.set_brightness(config.brightness.into()).await?;
|
|
config.set_builtin(effect);
|
|
config.write();
|
|
Ok(())
|
|
}
|
|
|
|
/// Get the data set for every mode available
|
|
async fn all_mode_data(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
|
|
let config = self.0.config.lock().await;
|
|
config.builtins.clone()
|
|
}
|
|
|
|
// As property doesn't work for AuraPowerDev (complexity of serialization?)
|
|
#[zbus(property)]
|
|
async fn led_power(&self) -> LaptopAuraPower {
|
|
let config = self.0.config.lock().await;
|
|
config.enabled.clone()
|
|
}
|
|
|
|
/// Set a variety of states, input is array of enum.
|
|
/// `enabled` sets if the sent array should be disabled or enabled
|
|
///
|
|
/// For Modern ROG devices the "enabled" flag is ignored.
|
|
#[zbus(property)]
|
|
async fn set_led_power(&mut self, options: LaptopAuraPower) -> Result<(), ZbErr> {
|
|
let mut config = self.0.config.lock().await;
|
|
for opt in options.states {
|
|
let zone = opt.zone;
|
|
for state in config.enabled.states.iter_mut() {
|
|
if state.zone == zone {
|
|
*state = opt;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
config.write();
|
|
Ok(self.0.set_power_states(&config).await.map_err(|e| {
|
|
warn!("{}", e);
|
|
e
|
|
})?)
|
|
}
|
|
|
|
/// On machine that have some form of either per-key keyboard or per-zone
|
|
/// this can be used to write custom effects over dbus. The input is a
|
|
/// nested `Vec<Vec<8>>` where `Vec<u8>` is a raw USB packet
|
|
async fn direct_addressing_raw(&self, data: AuraLaptopUsbPackets) -> Result<(), ZbErr> {
|
|
let mut config = self.0.config.lock().await;
|
|
self.0.write_effect_block(&mut config, &data).await?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl CtrlTask for AuraZbus {
|
|
fn zbus_path() -> &'static str {
|
|
"/xyz/ljones"
|
|
}
|
|
|
|
async fn create_tasks(&self, _: SignalEmitter<'static>) -> Result<(), RogError> {
|
|
let inner1 = self.0.clone();
|
|
let inner3 = self.0.clone();
|
|
self.create_sys_event_tasks(
|
|
move |sleeping| {
|
|
let inner1 = inner1.clone();
|
|
// unwrap as we want to bomb out of the task
|
|
async move {
|
|
if !sleeping {
|
|
info!("CtrlKbdLedTask reloading brightness and modes");
|
|
if let Some(backlight) = &inner1.backlight {
|
|
backlight
|
|
.lock()
|
|
.await
|
|
.set_brightness(inner1.config.lock().await.brightness.into())
|
|
.map_err(|e| {
|
|
error!("CtrlKbdLedTask: {e}");
|
|
e
|
|
})
|
|
.unwrap();
|
|
}
|
|
let mut config = inner1.config.lock().await;
|
|
inner1
|
|
.write_current_config_mode(&mut config)
|
|
.await
|
|
.map_err(|e| {
|
|
error!("CtrlKbdLedTask: {e}");
|
|
e
|
|
})
|
|
.unwrap();
|
|
} else if sleeping {
|
|
inner1
|
|
.update_config()
|
|
.await
|
|
.map_err(|e| {
|
|
error!("CtrlKbdLedTask: {e}");
|
|
e
|
|
})
|
|
.unwrap();
|
|
}
|
|
}
|
|
},
|
|
move |_shutting_down| {
|
|
let inner3 = inner3.clone();
|
|
async move {
|
|
info!("CtrlKbdLedTask reloading brightness and modes");
|
|
if let Some(backlight) = &inner3.backlight {
|
|
// unwrap as we want to bomb out of the task
|
|
backlight
|
|
.lock()
|
|
.await
|
|
.set_brightness(inner3.config.lock().await.brightness.into())
|
|
.map_err(|e| {
|
|
error!("CtrlKbdLedTask: {e}");
|
|
e
|
|
})
|
|
.unwrap();
|
|
}
|
|
}
|
|
},
|
|
move |_lid_closed| {
|
|
// on lid change
|
|
async move {}
|
|
},
|
|
move |_power_plugged| {
|
|
// power change
|
|
async move {}
|
|
},
|
|
)
|
|
.await;
|
|
|
|
// let ctrl2 = self.0.clone();
|
|
// let ctrl = self.0.lock().await;
|
|
// if ctrl.led_node.has_brightness_control() {
|
|
// let watch = ctrl.led_node.monitor_brightness()?;
|
|
// tokio::spawn(async move {
|
|
// let mut buffer = [0; 32];
|
|
// watch
|
|
// .into_event_stream(&mut buffer)
|
|
// .unwrap()
|
|
// .for_each(|_| async {
|
|
// if let Some(lock) = ctrl2.try_lock() {
|
|
// load_save(true, lock).unwrap(); // unwrap as we want
|
|
// // to
|
|
// // bomb out of the
|
|
// // task
|
|
// }
|
|
// })
|
|
// .await;
|
|
// });
|
|
// }
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Reloadable for AuraZbus {
|
|
async fn reload(&mut self) -> Result<(), RogError> {
|
|
self.0.fix_ally_power().await?;
|
|
debug!("reloading keyboard mode");
|
|
let mut config = self.0.lock_config().await;
|
|
self.0.write_current_config_mode(&mut config).await?;
|
|
debug!("reloading power states");
|
|
self.0
|
|
.set_power_states(&config)
|
|
.await
|
|
.map_err(|err| warn!("{err}"))
|
|
.ok();
|
|
Ok(())
|
|
}
|
|
}
|