From 1041cf5dbc5b4efac0c3df538177e045153293d9 Mon Sep 17 00:00:00 2001 From: Luke D Jones Date: Mon, 3 Aug 2020 13:53:21 +1200 Subject: [PATCH] Small fix to RGB per-key --- rog-client/examples/ball.rs | 2 +- rog-core/src/ctrl_anime.rs | 17 ++++ rog-core/src/ctrl_charge.rs | 20 +++++ rog-core/src/ctrl_fan_cpu.rs | 37 +++++++++ rog-core/src/ctrl_leds.rs | 56 ++++++++++++- rog-core/src/daemon.rs | 147 ++++++++++------------------------- rog-core/src/dbus.rs | 29 ++++--- rog-core/src/laptops.rs | 19 +---- 8 files changed, 185 insertions(+), 142 deletions(-) diff --git a/rog-client/examples/ball.rs b/rog-client/examples/ball.rs index f2210da6..9d5c446e 100644 --- a/rog-client/examples/ball.rs +++ b/rog-client/examples/ball.rs @@ -93,6 +93,6 @@ fn main() -> Result<(), Box> { writer.write_colour_block(&colours)?; // can change 100 times per second, so need to slow it down - std::thread::sleep(std::time::Duration::from_millis(30)); + //std::thread::sleep(std::time::Duration::from_millis(30)); } } diff --git a/rog-core/src/ctrl_anime.rs b/rog-core/src/ctrl_anime.rs index ac4a9000..a2af7351 100644 --- a/rog-core/src/ctrl_anime.rs +++ b/rog-core/src/ctrl_anime.rs @@ -14,6 +14,8 @@ use rog_client::error::AuraError; use rusb::{Device, DeviceHandle}; use std::error::Error; use std::time::Duration; +use tokio::sync::mpsc::Receiver; +use tokio::task::JoinHandle; #[allow(dead_code)] #[derive(Debug)] @@ -57,6 +59,21 @@ impl AniMeWriter { }) } + /// Spawns two tasks which continuously check for changes + pub(crate) fn spawn_task( + mut ctrlr: AniMeWriter, + mut recv: Receiver>>, + ) -> JoinHandle<()> { + tokio::spawn(async move { + while let Some(image) = recv.recv().await { + ctrlr + .do_command(AnimatrixCommand::WriteImage(image)) + .await + .unwrap_or_else(|err| warn!("{}", err)); + } + }) + } + #[inline] fn get_device(vendor: u16, product: u16) -> Result, rusb::Error> { for device in rusb::devices()?.iter() { diff --git a/rog-core/src/ctrl_charge.rs b/rog-core/src/ctrl_charge.rs index 33af93c7..4a07b1fa 100644 --- a/rog-core/src/ctrl_charge.rs +++ b/rog-core/src/ctrl_charge.rs @@ -4,6 +4,10 @@ use std::error::Error; use std::fs::OpenOptions; use std::io::Write; use std::path::Path; +use std::sync::Arc; +use tokio::sync::mpsc::Receiver; +use tokio::sync::Mutex; +use tokio::task::JoinHandle; static BAT_CHARGE_PATH: &str = "/sys/class/power_supply/BAT0/charge_control_end_threshold"; @@ -18,6 +22,22 @@ impl CtrlCharge { Ok(CtrlCharge { path }) } + /// Spawns two tasks which continuously check for changes + pub(crate) fn spawn_task( + ctrlr: CtrlCharge, + config: Arc>, + mut recv: Receiver, + ) -> JoinHandle<()> { + tokio::spawn(async move { + while let Some(n) = recv.recv().await { + let mut config = config.lock().await; + ctrlr + .set_charge_limit(n, &mut config) + .unwrap_or_else(|err| warn!("{:?}", err)); + } + }) + } + fn get_battery_path() -> Result<&'static str, std::io::Error> { if Path::new(BAT_CHARGE_PATH).exists() { Ok(BAT_CHARGE_PATH) diff --git a/rog-core/src/ctrl_fan_cpu.rs b/rog-core/src/ctrl_fan_cpu.rs index 906a898a..5783c1a5 100644 --- a/rog-core/src/ctrl_fan_cpu.rs +++ b/rog-core/src/ctrl_fan_cpu.rs @@ -5,6 +5,10 @@ use std::fs::OpenOptions; use std::io::{Read, Write}; use std::path::Path; use std::str::FromStr; +use std::sync::Arc; +use tokio::sync::mpsc::Receiver; +use tokio::sync::Mutex; +use tokio::task::JoinHandle; static FAN_TYPE_1_PATH: &str = "/sys/devices/platform/asus-nb-wmi/throttle_thermal_policy"; static FAN_TYPE_2_PATH: &str = "/sys/devices/platform/asus-nb-wmi/fan_boost_mode"; @@ -21,6 +25,39 @@ impl CtrlFanAndCPU { Ok(CtrlFanAndCPU { path }) } + /// Spawns two tasks which continuously check for changes + pub(crate) fn spawn_task( + ctrlr: CtrlFanAndCPU, + config: Arc>, + mut recv: Receiver, + ) -> JoinHandle<()> { + let gate1 = Arc::new(Mutex::new(ctrlr)); + let gate2 = gate1.clone(); + let config1 = config.clone(); + // spawn an endless loop + tokio::spawn(async move { + while let Some(mode) = recv.recv().await { + let mut config = config1.lock().await; + if let Ok(mut lock) = gate1.try_lock() { + lock.set_fan_mode(mode, &mut config) + .unwrap_or_else(|err| warn!("{:?}", err)); + } + } + }); + // need to watch file path + tokio::spawn(async move { + loop { + if let Ok(mut lock) = gate2.try_lock() { + let mut config = config.lock().await; + lock.fan_mode_check_change(&mut config) + .unwrap_or_else(|err| warn!("{:?}", err)); + } + + tokio::time::delay_for(std::time::Duration::from_millis(500)).await; + } + }) + } + fn get_fan_path() -> Result<&'static str, std::io::Error> { if Path::new(FAN_TYPE_1_PATH).exists() { Ok(FAN_TYPE_1_PATH) diff --git a/rog-core/src/ctrl_leds.rs b/rog-core/src/ctrl_leds.rs index 0dbc8a0a..286dccc0 100644 --- a/rog-core/src/ctrl_leds.rs +++ b/rog-core/src/ctrl_leds.rs @@ -3,12 +3,18 @@ static LED_APPLY: [u8; 17] = [0x5d, 0xb4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, static LED_SET: [u8; 17] = [0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; use crate::{config::Config, error::RogError}; +use dbus::{channel::Sender, nonblock::SyncConnection, tree::Signal}; use log::{info, warn}; use rog_client::{ - aura_brightness_bytes, aura_modes::AuraModes, fancy::KeyColourArray, LED_MSG_LEN, + aura_brightness_bytes, aura_modes::AuraModes, fancy::KeyColourArray, DBUS_IFACE, DBUS_PATH, + LED_MSG_LEN, }; use std::fs::OpenOptions; use std::io::Write; +use std::sync::Arc; +use tokio::sync::mpsc::Receiver; +use tokio::sync::Mutex; +use tokio::task::JoinHandle; pub struct LedWriter { dev_node: String, @@ -23,8 +29,7 @@ impl LedWriter { enumerator.match_subsystem("hidraw")?; for device in enumerator.scan_devices()? { - if let Some(parent) = device - .parent_with_subsystem_devtype("usb", "usb_device")? { + if let Some(parent) = device.parent_with_subsystem_devtype("usb", "usb_device")? { if parent.attribute_value("idProduct").unwrap() == id_product // && device.parent().unwrap().sysnum().unwrap() == 3 { @@ -45,6 +50,43 @@ impl LedWriter { )) } + /// Spawns two tasks which continuously check for changes + pub(crate) fn spawn_task( + mut ctrlr: LedWriter, + config: Arc>, + mut recv: Receiver, + connection: Arc, + signal: Arc>, + ) -> JoinHandle<()> { + tokio::spawn(async move { + while let Some(command) = recv.recv().await { + let mut config = config.lock().await; + match &command { + AuraModes::RGB(_) => { + ctrlr + .do_command(command, &mut config) + .await + .unwrap_or_else(|err| warn!("{}", err)); + } + _ => { + let json = serde_json::to_string(&command).unwrap(); + ctrlr + .do_command(command, &mut config) + .await + .unwrap_or_else(|err| warn!("{}", err)); + connection + .send( + signal + .msg(&DBUS_PATH.into(), &DBUS_IFACE.into()) + .append1(json), + ) + .unwrap_or_else(|_| 0); + } + } + } + }) + } + pub async fn do_command( &mut self, mode: AuraModes, @@ -92,6 +134,14 @@ impl LedWriter { config.write(); info!("LED brightness set to {:#?}", n); } + AuraModes::RGB(v) => { + if v.is_empty() || v[0].is_empty() { + let bytes = KeyColourArray::get_init_msg(); + self.write_bytes(&bytes).await?; + } else { + self.write_effect(&v).await?; + } + } _ => { let mode_num: u8 = u8::from(&mode); self.write_mode(&mode).await?; diff --git a/rog-core/src/daemon.rs b/rog-core/src/daemon.rs index 968a28b0..decda7ec 100644 --- a/rog-core/src/daemon.rs +++ b/rog-core/src/daemon.rs @@ -1,25 +1,17 @@ use crate::{ - config::Config, - ctrl_anime::{AniMeWriter, AnimatrixCommand}, - ctrl_charge::CtrlCharge, - ctrl_fan_cpu::CtrlFanAndCPU, - ctrl_leds::LedWriter, - dbus::dbus_create_tree, - laptops::match_laptop, + config::Config, ctrl_anime::AniMeWriter, ctrl_charge::CtrlCharge, ctrl_fan_cpu::CtrlFanAndCPU, + ctrl_leds::LedWriter, dbus::dbus_create_tree, laptops::match_laptop, }; -use dbus::{channel::Sender, nonblock::Process, nonblock::SyncConnection, tree::Signal}; +use dbus::{channel::Sender, nonblock::SyncConnection, tree::Signal}; use dbus_tokio::connection; use log::{error, info, warn}; -use rog_client::{aura_modes::AuraModes, DBUS_IFACE, DBUS_NAME, DBUS_PATH}; +use rog_client::{DBUS_IFACE, DBUS_NAME, DBUS_PATH}; use std::error::Error; use std::sync::Arc; - use tokio::sync::Mutex; -pub(super) type DbusU8Type = Arc>>; - // Timing is such that: // - interrupt write is minimum 1ms (sometimes lower) // - read interrupt must timeout, minimum of 1ms @@ -30,11 +22,9 @@ pub(super) type DbusU8Type = Arc>>; // // DBUS processing takes 6ms if not tokiod pub async fn start_daemon() -> Result<(), Box> { - let mut laptop = match_laptop(); + let laptop = match_laptop(); let mut config = Config::default().load(laptop.supported_modes()); - info!("Config loaded"); - let mut led_control = LedWriter::new(laptop.usb_product(), laptop.supported_modes().to_owned()) .map_or_else( |err| { @@ -42,7 +32,7 @@ pub async fn start_daemon() -> Result<(), Box> { None }, |ledwriter| { - info!("LED Writer loaded"); + info!("Device has keyboard backlight control"); Some(ledwriter) }, ); @@ -53,7 +43,7 @@ pub async fn start_daemon() -> Result<(), Box> { None }, |ledwriter| { - info!("Charge control loaded"); + info!("Device has battery charge threshold control"); Some(ledwriter) }, ); @@ -64,7 +54,7 @@ pub async fn start_daemon() -> Result<(), Box> { None }, |ledwriter| { - info!("Fan & CPU control loaded"); + info!("Device has thermal throttle control"); Some(ledwriter) }, ); @@ -103,10 +93,10 @@ pub async fn start_daemon() -> Result<(), Box> { let ( tree, - mut aura_command_recv, - mut animatrix_recv, - fan_mode, - charge_limit, + aura_command_recv, + animatrix_recv, + fan_mode_recv, + charge_limit_recv, led_changed_signal, fanmode_signal, charge_limit_signal, @@ -132,95 +122,36 @@ pub async fn start_daemon() -> Result<(), Box> { charge_limit_signal, ); - // Keyboard reader goes in separate task because we want a high interrupt timeout - // and don't want that to hold up other tasks, or miss keystrokes - - // Possible Animatrix - if laptop.support_animatrix() { - if let Ok(mut animatrix_writer) = AniMeWriter::new() { - info!("Device has an AniMe Matrix display"); - tokio::spawn(async move { - while let Some(image) = animatrix_recv.recv().await { - animatrix_writer - .do_command(AnimatrixCommand::WriteImage(image)) - .await - .unwrap_or_else(|err| warn!("{}", err)); - } - }); - laptop.set_support_animatrix(false); - } + let mut handles = Vec::new(); + // Begin all tasks + if let Ok(ctrlr) = AniMeWriter::new() { + info!("Device has an AniMe Matrix display"); + handles.push(AniMeWriter::spawn_task(ctrlr, animatrix_recv)); } - // start the keyboard reader and laptop-action loop - let config1 = config.clone(); - // spawning this in a function causes a segfault for reasons I haven't investigated yet - tokio::spawn(async move { - loop { - // TODO: MAKE SYS COMMANDS OPERATE USING CHANNEL LIKE AURA MODES - // Fan mode - if let Some(ctrlr) = fan_control.as_mut() { - let mut config = config1.lock().await; - ctrlr - .fan_mode_check_change(&mut config) - .unwrap_or_else(|err| warn!("{:?}", err)); - - if let Ok(mut lock) = fan_mode.try_lock() { - if let Some(n) = lock.take() { - let mut config = config1.lock().await; - ctrlr - .set_fan_mode(n, &mut config) - .unwrap_or_else(|err| warn!("{:?}", err)); - } - } - } - - // Charge limit - if let Some(ctrlr) = charge_control.as_mut() { - if let Ok(mut lock) = charge_limit.try_lock() { - if let Some(n) = lock.take() { - let mut config = config1.lock().await; - ctrlr - .set_charge_limit(n, &mut config) - .unwrap_or_else(|err| warn!("{:?}", err)); - } - } - } - tokio::time::delay_for(std::time::Duration::from_millis(500)).await; - } - }); - - // start the main loop - loop { - connection.process_all(); - - while let Some(command) = aura_command_recv.recv().await { - if let Some(writer) = led_control.as_mut() { - let mut config = config.lock().await; - match &command { - AuraModes::RGB(_) => { - writer - .do_command(command, &mut config) - .await - .unwrap_or_else(|err| warn!("{}", err)); - } - _ => { - let json = serde_json::to_string(&command)?; - writer - .do_command(command, &mut config) - .await - .unwrap_or_else(|err| warn!("{}", err)); - connection - .send( - led_changed_signal - .msg(&DBUS_PATH.into(), &DBUS_IFACE.into()) - .append1(json), - ) - .unwrap_or_else(|_| 0); - } - } - } - } + if let Some(ctrlr) = fan_control.take() { + handles.push(CtrlFanAndCPU::spawn_task(ctrlr, config.clone(), fan_mode_recv)); } + + if let Some(ctrlr) = charge_control.take() { + handles.push(CtrlCharge::spawn_task(ctrlr, config.clone(), charge_limit_recv)); + } + + if let Some(ctrlr) = led_control.take() { + handles.push(LedWriter::spawn_task( + ctrlr, + config.clone(), + aura_command_recv, + connection.clone(), + led_changed_signal, + )); + } + + for handle in handles { + handle.await?; + } + + Ok(()) } fn start_signal_task( diff --git a/rog-core/src/dbus.rs b/rog-core/src/dbus.rs index 393c3b16..ae1c445d 100644 --- a/rog-core/src/dbus.rs +++ b/rog-core/src/dbus.rs @@ -1,5 +1,4 @@ use crate::config::Config; -use crate::daemon::DbusU8Type; use dbus::tree::{Factory, MTSync, Method, MethodErr, Signal, Tree}; use log::warn; use rog_client::{aura_modes::AuraModes, DBUS_IFACE, DBUS_PATH}; @@ -99,16 +98,16 @@ fn set_animatrix( .annotate("org.freedesktop.DBus.Method.NoReply", "true") } -fn set_fan_mode(data: DbusU8Type) -> Method { +fn set_fan_mode(sender: Mutex>) -> Method { let factory = Factory::new_sync::<()>(); factory // method for ledmessage .method("SetFanMode", (), { move |m| { - if let Ok(mut lock) = data.try_lock() { + if let Ok(mut lock) = sender.try_lock() { let mut iter = m.msg.iter_init(); let byte: u8 = iter.read()?; - *lock = Some(byte); + lock.try_send(byte).unwrap_or_else(|_err| {}); Ok(vec![]) } else { Err(MethodErr::failed("Could not lock daemon for access")) @@ -151,16 +150,16 @@ fn get_charge_limit(config: Arc>) -> Method { .outarg::("limit") } -fn set_charge_limit(data: DbusU8Type) -> Method { +fn set_charge_limit(sender: Mutex>) -> Method { let factory = Factory::new_sync::<()>(); factory // method for ledmessage .method("SetChargeLimit", (), { move |m| { - if let Ok(mut lock) = data.try_lock() { + if let Ok(mut lock) = sender.try_lock() { let mut iter = m.msg.iter_init(); let byte: u8 = iter.read()?; - *lock = Some(byte); + lock.try_send(byte).unwrap_or_else(|_err| {}); Ok(vec![]) } else { Err(MethodErr::failed("Could not lock daemon for access")) @@ -178,16 +177,16 @@ pub(super) fn dbus_create_tree( Tree, Receiver, Receiver>>, - DbusU8Type, - DbusU8Type, + Receiver, + Receiver, Arc>, Arc>, Arc>, ) { let (aura_command_send, aura_command_recv) = channel::(1); let (animatrix_send, animatrix_recv) = channel::>>(1); - let fan_mode: DbusU8Type = Arc::new(Mutex::new(None)); - let charge_limit: DbusU8Type = Arc::new(Mutex::new(None)); + let (fan_mode_send, fan_mode_recv) = channel::(1); + let (charge_send, charge_recv) = channel::(1); let factory = Factory::new_sync::<()>(); @@ -211,8 +210,8 @@ pub(super) fn dbus_create_tree( .interface(DBUS_IFACE, ()) .add_m(set_keyboard_backlight(Mutex::new(aura_command_send))) .add_m(set_animatrix(Mutex::new(animatrix_send))) - .add_m(set_fan_mode(fan_mode.clone())) - .add_m(set_charge_limit(charge_limit.clone())) + .add_m(set_fan_mode(Mutex::new(fan_mode_send))) + .add_m(set_charge_limit(Mutex::new(charge_send))) .add_m(get_fan_mode(config.clone())) .add_m(get_charge_limit(config.clone())) .add_m(get_keyboard_backlight(config.clone())) @@ -227,8 +226,8 @@ pub(super) fn dbus_create_tree( tree, aura_command_recv, animatrix_recv, - fan_mode, - charge_limit, + fan_mode_recv, + charge_recv, key_backlight_changed, fanmode_changed, chrg_limit_changed, diff --git a/rog-core/src/laptops.rs b/rog-core/src/laptops.rs index 38901cc5..de48cc64 100644 --- a/rog-core/src/laptops.rs +++ b/rog-core/src/laptops.rs @@ -18,7 +18,6 @@ pub(crate) fn match_laptop() -> LaptopBase { return LaptopBase { usb_product: "1854".to_string(), supported_modes: vec![SINGLE, BREATHING, STROBE], - support_animatrix: false, }; } _ => {} @@ -39,16 +38,13 @@ fn select_1866_device(prod: String) -> LaptopBase { let mut laptop = LaptopBase { usb_product: prod, supported_modes: vec![], - support_animatrix: false, }; // AniMe, no RGB - if board_name.starts_with("GA401") { - info!("No RGB control available"); - // TODO: actual check for the AniMe device here - laptop.support_animatrix = true; - // No AniMe, no RGB - } else if board_name.starts_with("GA502") || board_name.starts_with("GU502") { + if board_name.starts_with("GA401") + || board_name.starts_with("GA502") + || board_name.starts_with("GU502") + { info!("No RGB control available"); // RGB, per-key settings, no zones } else if board_name.starts_with("GX502") @@ -114,7 +110,6 @@ fn select_1866_device(prod: String) -> LaptopBase { pub(super) struct LaptopBase { usb_product: String, supported_modes: Vec, - support_animatrix: bool, } impl LaptopBase { @@ -124,10 +119,4 @@ impl LaptopBase { pub(super) fn supported_modes(&self) -> &[u8] { &self.supported_modes } - pub(super) fn support_animatrix(&self) -> bool { - self.support_animatrix - } - pub(super) fn set_support_animatrix(&mut self, supported: bool) { - self.support_animatrix = supported; - } }