use std::sync::Arc; use config::AuraConfig; use config_traits::StdConfig; use log::info; use rog_aura::keyboard::{AuraLaptopUsbPackets, LedUsbPackets}; use rog_aura::usb::{AURA_LAPTOP_LED_APPLY, AURA_LAPTOP_LED_SET}; use rog_aura::{AuraDeviceType, AuraEffect, LedBrightness, PowerZones, AURA_LAPTOP_LED_MSG_LEN}; use rog_platform::hid_raw::HidRaw; use rog_platform::keyboard_led::KeyboardBacklight; use tokio::sync::{Mutex, MutexGuard}; use crate::error::RogError; pub mod config; pub mod trait_impls; #[derive(Debug, Clone)] pub struct Aura { pub hid: Option>>, pub backlight: Option>>, pub config: Arc>, } impl Aura { /// Initialise the device if required. pub async fn do_initialization(&self) -> Result<(), RogError> { Ok(()) } pub async fn lock_config(&self) -> MutexGuard<'_, AuraConfig> { self.config.lock().await } /// Will lock the internal config and update. If anything else has locked /// this in scope then a deadlock can occur. pub async fn update_config(&self) -> Result<(), RogError> { let mut config = self.config.lock().await; let bright = if let Some(bl) = self.backlight.as_ref() { bl.lock().await.get_brightness().unwrap_or_default() } else { config.brightness.into() }; config.read(); config.brightness = bright.into(); config.write(); Ok(()) } pub async fn write_current_config_mode(&self, config: &mut AuraConfig) -> Result<(), RogError> { if config.multizone_on { let mode = config.current_mode; let mut create = false; // There is no multizone config for this mode so create one here // using the colours of rainbow if it exists, or first available // mode, or random if config.multizone.is_none() { create = true; } else if let Some(multizones) = config.multizone.as_ref() { if !multizones.contains_key(&mode) { create = true; } } if create { info!("No user-set config for zone founding, attempting a default"); config.create_multizone_default()?; } if let Some(multizones) = config.multizone.as_mut() { if let Some(set) = multizones.get(&mode) { for mode in set.clone() { self.write_effect_and_apply(config.led_type, &mode).await?; } } } } else { let mode = config.current_mode; if let Some(effect) = config.builtins.get(&mode).cloned() { self.write_effect_and_apply(config.led_type, &effect) .await?; } } Ok(()) } /// Write the AuraEffect to the device. Will lock `backlight` or `hid`. /// /// If per-key or software-mode is active it must be marked as disabled in /// config. pub async fn write_effect_and_apply( &self, dev_type: AuraDeviceType, mode: &AuraEffect, ) -> Result<(), RogError> { if matches!(dev_type, AuraDeviceType::LaptopKeyboardTuf) { if let Some(platform) = &self.backlight { let buf = [ 1, mode.mode as u8, mode.colour1.r, mode.colour1.g, mode.colour1.b, mode.speed as u8, ]; platform.lock().await.set_kbd_rgb_mode(&buf)?; } } else if let Some(hid_raw) = &self.hid { let bytes: [u8; AURA_LAPTOP_LED_MSG_LEN] = mode.into(); let hid_raw = hid_raw.lock().await; hid_raw.write_bytes(&bytes)?; hid_raw.write_bytes(&AURA_LAPTOP_LED_SET)?; // Changes won't persist unless apply is set hid_raw.write_bytes(&AURA_LAPTOP_LED_APPLY)?; } else { return Err(RogError::NoAuraKeyboard); } Ok(()) } pub async fn set_brightness(&self, value: u8) -> Result<(), RogError> { if let Some(backlight) = &self.backlight { backlight.lock().await.set_brightness(value)?; return Ok(()); } Err(RogError::MissingFunction( "No LED backlight control available".to_string(), )) } /// Set combination state for boot animation/sleep animation/all leds/keys /// leds/side leds LED active pub async fn set_power_states(&self, config: &AuraConfig) -> Result<(), RogError> { if matches!(config.led_type, rog_aura::AuraDeviceType::LaptopKeyboardTuf) { if let Some(backlight) = &self.backlight { // TODO: tuf bool array let buf = config.enabled.to_bytes(config.led_type); backlight.lock().await.set_kbd_rgb_state(&buf)?; } } else if let Some(hid_raw) = &self.hid { let hid_raw = hid_raw.lock().await; if let Some(p) = config.enabled.states.first() { if p.zone == PowerZones::Ally { let msg = [ 0x5d, 0xd1, 0x09, 0x01, p.new_to_byte() as u8, 0x0, 0x0, ]; hid_raw.write_bytes(&msg)?; return Ok(()); } } let bytes = config.enabled.to_bytes(config.led_type); let msg = [ 0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2], bytes[3], ]; hid_raw.write_bytes(&msg)?; } Ok(()) } /// Write an effect block. This is for per-key, but can be repurposed to /// write the raw factory mode packets - when doing this it is expected that /// only the first `Vec` (`effect[0]`) is valid. pub async fn write_effect_block( &self, config: &mut AuraConfig, effect: &AuraLaptopUsbPackets, ) -> Result<(), RogError> { if config.brightness == LedBrightness::Off { config.brightness = LedBrightness::Med; config.write(); } let pkt_type = effect[0][1]; const PER_KEY_TYPE: u8 = 0xbc; if let Some(hid_raw) = &self.hid { let hid_raw = hid_raw.lock().await; if pkt_type != PER_KEY_TYPE { config.per_key_mode_active = false; hid_raw.write_bytes(&effect[0])?; hid_raw.write_bytes(&AURA_LAPTOP_LED_SET)?; // hid_raw.write_bytes(&LED_APPLY)?; } else { if !config.per_key_mode_active { let init = LedUsbPackets::get_init_msg(); hid_raw.write_bytes(&init)?; config.per_key_mode_active = true; } for row in effect.iter() { hid_raw.write_bytes(row)?; } } } else if matches!(config.led_type, rog_aura::AuraDeviceType::LaptopKeyboardTuf) { if let Some(tuf) = &self.backlight { for row in effect.iter() { let r = row[9]; let g = row[10]; let b = row[11]; tuf.lock().await.set_kbd_rgb_mode(&[ 0, 0, r, g, b, 0, ])?; } } } Ok(()) } pub async fn fix_ally_power(&mut self) -> Result<(), RogError> { if self.config.lock().await.led_type == AuraDeviceType::Ally { if let Some(hid_raw) = &self.hid { let mut config = self.config.lock().await; if config.ally_fix.is_none() { let msg = [ 0x5d, 0xbd, 0x01, 0xff, 0xff, 0xff, 0xff, ]; hid_raw.lock().await.write_bytes(&msg)?; info!("Reset Ally power settings to base"); config.ally_fix = Some(true); } config.write(); } } Ok(()) } }