use crate::cli_options::LedBrightness; use super::*; use crate::fancy::KeyColourArray; use crate::profile::ProfileEvent; use ctrl_gfx::vendors::GfxVendors; use dbus::{blocking::Connection, Message}; use std::error::Error; use std::sync::{ atomic::{AtomicBool, Ordering}, Arc, Mutex, }; use std::{thread, time::Duration}; use crate::dbus_charge::{OrgAsuslinuxDaemonNotifyCharge, OrgAsuslinuxDaemon as OrgAsuslinuxDaemonCharge}; use crate::dbus_gfx::{ OrgAsuslinuxDaemon as OrgAsuslinuxDaemonGfx, OrgAsuslinuxDaemonNotifyAction, OrgAsuslinuxDaemonNotifyGfx, }; use crate::dbus_ledmode::{ OrgAsuslinuxDaemon as OrgAsuslinuxDaemonLed, OrgAsuslinuxDaemonNotifyLed, }; use crate::dbus_profile::{ OrgAsuslinuxDaemon as OrgAsuslinuxDaemonProfile, OrgAsuslinuxDaemonNotifyProfile, }; // Signals separated out pub struct CtrlSignals { pub gfx_vendor_signal: Arc>>, pub gfx_action_signal: Arc>>, pub profile_signal: Arc>>, pub ledmode_signal: Arc>>, pub charge_signal: Arc>>, } impl CtrlSignals { #[inline] pub fn new(connection: &Connection) -> Result> { let proxy = connection.with_proxy( "org.asuslinux.Daemon", "/org/asuslinux/Gfx", Duration::from_secs(2), ); let gfx_vendor_signal = Arc::new(Mutex::new(None)); let gfx_res1 = gfx_vendor_signal.clone(); let _x = proxy.match_signal( move |sig: OrgAsuslinuxDaemonNotifyGfx, _: &Connection, _: &Message| { if let Ok(mut lock) = gfx_res1.lock() { *lock = Some(sig.vendor); } true }, )?; let gfx_action_signal = Arc::new(Mutex::new(None)); let gfx_res1 = gfx_action_signal.clone(); let _x = proxy.match_signal( move |sig: OrgAsuslinuxDaemonNotifyAction, _: &Connection, _: &Message| { if let Ok(mut lock) = gfx_res1.lock() { *lock = Some(sig.action); } true }, )?; // let proxy = connection.with_proxy( "org.asuslinux.Daemon", "/org/asuslinux/Profile", Duration::from_secs(2), ); let profile_signal = Arc::new(Mutex::new(None)); let prof_res1 = profile_signal.clone(); let _x = proxy.match_signal( move |sig: OrgAsuslinuxDaemonNotifyProfile, _: &Connection, _: &Message| { if let Ok(mut lock) = prof_res1.lock() { *lock = Some(sig.profile); } true }, )?; // let proxy = connection.with_proxy( "org.asuslinux.Daemon", "/org/asuslinux/Led", Duration::from_secs(2), ); let ledmode_signal = Arc::new(Mutex::new(None)); let led_res1 = ledmode_signal.clone(); let _x = proxy.match_signal( move |sig: OrgAsuslinuxDaemonNotifyLed, _: &Connection, _: &Message| { if let Ok(mut lock) = led_res1.lock() { if let Ok(dat) = serde_json::from_str(&sig.data) { *lock = Some(dat); } } true }, )?; // let proxy = connection.with_proxy( "org.asuslinux.Daemon", "/org/asuslinux/Charge", Duration::from_secs(2), ); let charge_signal = Arc::new(Mutex::new(None)); let charge_res1 = charge_signal.clone(); let _x = proxy.match_signal( move |sig: OrgAsuslinuxDaemonNotifyCharge, _: &Connection, _: &Message| { if let Ok(mut lock) = charge_res1.lock() { *lock = Some(sig.limit); } true }, )?; Ok(CtrlSignals { gfx_vendor_signal, gfx_action_signal, profile_signal, ledmode_signal, charge_signal, }) } } /// Simplified way to write a effect block pub struct AuraDbusClient { connection: Box, block_time: u64, stop: Arc, signals: CtrlSignals, } impl AuraDbusClient { #[inline] pub fn new() -> Result> { let connection = Connection::new_system()?; let stop = Arc::new(AtomicBool::new(false)); let match_rule = dbus::message::MatchRule::new_signal(DBUS_IFACE, "NotifyLed"); let stop1 = stop.clone(); connection.add_match(match_rule, move |_: (), _, msg| { if msg.read1::<&str>().is_ok() { stop1.clone().store(true, Ordering::Relaxed); } true })?; let signals = CtrlSignals::new(&connection)?; Ok(AuraDbusClient { connection: Box::new(connection), block_time: 33333, stop, signals, }) } pub fn wait_gfx_changed(&self) -> Result> { loop { self.connection.process(Duration::from_millis(1))?; if let Ok(lock) = self.signals.gfx_action_signal.lock() { if let Some(stuff) = lock.as_ref() { return Ok(stuff.to_string()); } } } } /// This method must always be called before the very first write to initialise /// the keyboard LED EC in the correct mode #[inline] pub fn init_effect(&self) -> Result<(), Box> { let mode = AuraModes::PerKey(vec![vec![]]); let proxy = self.connection.with_proxy( "org.asuslinux.Daemon", "/org/asuslinux/Led", Duration::from_secs(2), ); proxy.set_led_mode(&serde_json::to_string(&mode)?)?; Ok(()) } /// Write a single colour block. /// /// Intentionally blocks for 10ms after sending to allow the block to /// be written to the keyboard EC. This should not be async. #[inline] pub fn write_colour_block( &mut self, key_colour_array: &KeyColourArray, ) -> Result<(), Box> { let group = key_colour_array.get(); let mut vecs = Vec::with_capacity(group.len()); for v in group { vecs.push(v.to_vec()); } let mode = AuraModes::PerKey(vecs); self.write_keyboard_leds(&mode)?; thread::sleep(Duration::from_micros(self.block_time)); self.connection.process(Duration::from_micros(500))?; if self.stop.load(Ordering::Relaxed) { println!("Keyboard backlight was changed, exiting"); std::process::exit(1) } Ok(()) } #[inline] pub fn write_keyboard_leds(&self, mode: &AuraModes) -> Result<(), Box> { let proxy = self.connection.with_proxy( "org.asuslinux.Daemon", "/org/asuslinux/Led", Duration::from_secs(2), ); proxy.set_led_mode(&serde_json::to_string(mode)?)?; Ok(()) } #[inline] pub fn get_gfx_pwr(&self) -> Result> { let proxy = self.connection.with_proxy( "org.asuslinux.Daemon", "/org/asuslinux/Gfx", Duration::from_secs(2), ); let x = proxy.power()?; Ok(x) } #[inline] pub fn get_gfx_mode(&self) -> Result> { let proxy = self.connection.with_proxy( "org.asuslinux.Daemon", "/org/asuslinux/Gfx", Duration::from_secs(2), ); let x = proxy.vendor()?; Ok(x) } #[inline] pub fn write_gfx_mode(&self, vendor: GfxVendors) -> Result<(), Box> { let proxy = self.connection.with_proxy( "org.asuslinux.Daemon", "/org/asuslinux/Gfx", Duration::from_secs(30), ); proxy.set_vendor(<&str>::from(&vendor))?; Ok(()) } #[inline] pub fn write_fan_mode(&self, level: u8) -> Result<(), Box> { let proxy = self.connection.with_proxy( "org.asuslinux.Daemon", "/org/asuslinux/Profile", Duration::from_secs(2), ); proxy.set_profile(&serde_json::to_string(&ProfileEvent::ChangeMode(level))?)?; Ok(()) } #[inline] pub fn write_profile_command( &self, cmd: &ProfileEvent, ) -> Result<(), Box> { let proxy = self.connection.with_proxy( "org.asuslinux.Daemon", "/org/asuslinux/Profile", Duration::from_secs(2), ); proxy.set_profile(&serde_json::to_string(cmd)?)?; Ok(()) } #[inline] pub fn write_charge_limit(&self, level: u8) -> Result<(), Box> { let proxy = self.connection.with_proxy( "org.asuslinux.Daemon", "/org/asuslinux/Charge", Duration::from_secs(2), ); proxy.set_limit(level)?; Ok(()) } #[inline] pub fn write_builtin_mode(&self, mode: &AuraModes) -> Result<(), Box> { self.write_keyboard_leds(mode) } #[inline] pub fn get_led_brightness(&self) -> Result> { let proxy = self.connection.with_proxy( "org.asuslinux.Daemon", "/org/asuslinux/Led", Duration::from_secs(2), ); match proxy.led_bright()? { -1 => Ok(LedBrightness::new(None)), level => Ok(LedBrightness::new(Some(level as u8))), } } #[inline] pub fn write_brightness(&self, level: u8) -> Result<(), Box> { self.write_keyboard_leds(&AuraModes::LedBrightness(level))?; Ok(()) } }