use ctrl_gfx::ctrl_gfx::CtrlGraphics; use daemon::config::Config; use daemon::ctrl_anime::CtrlAnimeDisplay; use daemon::ctrl_charge::CtrlCharge; use daemon::ctrl_fan_cpu::{CtrlFanAndCPU, DbusFanAndCpu}; use daemon::ctrl_leds::{CtrlKbdBacklight, DbusKbdBacklight}; use daemon::laptops::match_laptop; use asus_nb::DBUS_NAME; use daemon::{CtrlTask, Reloadable, ZbusAdd}; use log::LevelFilter; use log::{error, info, warn}; use std::error::Error; use std::io::Write; use std::sync::Arc; use std::sync::Mutex; use zbus::fdo; use zbus::Connection; pub fn main() -> Result<(), Box> { let mut logger = env_logger::Builder::new(); logger .target(env_logger::Target::Stdout) .format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args())) .filter(None, LevelFilter::Info) .init(); info!("Version: {}", daemon::VERSION); start_daemon()?; Ok(()) } // Timing is such that: // - interrupt write is minimum 1ms (sometimes lower) // - read interrupt must timeout, minimum of 1ms // - for a single usb packet, 2ms total. // - to maintain constant times of 1ms, per-key colours should use // the effect endpoint so that the complete colour block is written // as fast as 1ms per row of the matrix inside it. (10ms total time) // // DBUS processing takes 6ms if not tokiod fn start_daemon() -> Result<(), Box> { let laptop = match_laptop(); let config = if let Some(laptop) = laptop.as_ref() { Config::default().load(laptop.supported_modes()) } else { Config::default().load(&[]) }; let connection = Connection::new_system()?; fdo::DBusProxy::new(&connection)? .request_name(DBUS_NAME, fdo::RequestNameFlags::ReplaceExisting.into())?; let mut object_server = zbus::ObjectServer::new(&connection); let config = Arc::new(Mutex::new(config)); match CtrlCharge::new(config.clone()) { Ok(mut ctrl) => { // Do a reload of any settings ctrl.reload() .unwrap_or_else(|err| warn!("Battery charge limit: {}", err)); // Then register to dbus server ctrl.add_to_server(&mut object_server); } Err(err) => { error!("charge_control: {}", err); } } match CtrlAnimeDisplay::new() { Ok(ctrl) => { ctrl.add_to_server(&mut object_server); } Err(err) => { error!("AniMe control: {}", err); } } match CtrlGraphics::new() { Ok(mut ctrl) => { ctrl.reload() .unwrap_or_else(|err| warn!("Gfx controller: {}", err)); ctrl.add_to_server(&mut object_server); } Err(err) => { error!("Gfx control: {}", err); } } // Collect tasks for task thread let mut tasks: Vec>> = Vec::new(); match CtrlFanAndCPU::new(config.clone()) { Ok(mut ctrl) => { ctrl.reload() .unwrap_or_else(|err| warn!("Profile control: {}", err)); let tmp = Arc::new(Mutex::new(ctrl)); DbusFanAndCpu::new(tmp.clone()).add_to_server(&mut object_server); tasks.push(tmp); } Err(err) => { error!("Profile control: {}", err); } }; if let Some(laptop) = laptop { let ctrl = CtrlKbdBacklight::new( laptop.usb_product(), laptop.condev_iface(), laptop.supported_modes().to_owned(), config, ); let tmp = Arc::new(Mutex::new(ctrl)); DbusKbdBacklight::new(tmp.clone()).add_to_server(&mut object_server); tasks.push(tmp); } // TODO: implement messaging between threads to check fails // These tasks generally read a sys path or file to check for a // change let _handle = std::thread::Builder::new() .name("asusd watch".to_string()) .spawn(move || loop { std::thread::sleep(std::time::Duration::from_millis(100)); for ctrl in tasks.iter() { if let Ok(mut lock) = ctrl.try_lock() { lock.do_task().ok(); } } }); loop { if let Err(err) = object_server.try_handle_next() { eprintln!("{}", err); } } }