mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-01-22 09:23:19 +01:00
417 lines
14 KiB
Rust
417 lines
14 KiB
Rust
use std::collections::BTreeMap;
|
|
|
|
use config_traits::{StdConfig, StdConfigLoad};
|
|
use log::{debug, info, warn};
|
|
use rog_aura::aura_detection::LedSupportData;
|
|
use rog_aura::keyboard::LaptopAuraPower;
|
|
use rog_aura::{
|
|
AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, Direction, LedBrightness, Speed, GRADIENT,
|
|
};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::error::RogError;
|
|
|
|
#[derive(Deserialize, Serialize, Default, Debug, Clone)]
|
|
// #[serde(default)]
|
|
pub struct AuraConfig {
|
|
#[serde(skip)]
|
|
pub led_type: AuraDeviceType,
|
|
#[serde(skip)]
|
|
pub support_data: LedSupportData,
|
|
pub config_name: String,
|
|
#[serde(skip_serializing_if = "Option::is_none", default)]
|
|
pub ally_fix: Option<bool>,
|
|
pub brightness: LedBrightness,
|
|
pub current_mode: AuraModeNum,
|
|
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
|
|
#[serde(skip_serializing_if = "Option::is_none", default)]
|
|
pub multizone: Option<BTreeMap<AuraModeNum, Vec<AuraEffect>>>,
|
|
pub multizone_on: bool,
|
|
pub enabled: LaptopAuraPower,
|
|
#[serde(skip)]
|
|
pub per_key_mode_active: bool,
|
|
}
|
|
|
|
impl StdConfig for AuraConfig {
|
|
/// Detect the keyboard type and load from default DB if data available
|
|
fn new() -> Self {
|
|
panic!("This should not be used");
|
|
}
|
|
|
|
fn file_name(&self) -> String {
|
|
if self.config_name.is_empty() {
|
|
panic!("Config file name should not be empty");
|
|
}
|
|
self.config_name.to_owned()
|
|
}
|
|
|
|
fn config_dir() -> std::path::PathBuf {
|
|
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
|
|
}
|
|
}
|
|
|
|
impl StdConfigLoad for AuraConfig {}
|
|
|
|
impl AuraConfig {
|
|
/// Detect the keyboard type and load from default DB if data available
|
|
pub fn new(prod_id: &str) -> Self {
|
|
info!("Setting up AuraConfig for {prod_id:?}");
|
|
// create a default config here
|
|
let device_type = AuraDeviceType::from(prod_id);
|
|
if device_type == AuraDeviceType::Unknown {
|
|
warn!("idProduct:{prod_id:?} is unknown");
|
|
}
|
|
let support_data = LedSupportData::get_data(prod_id);
|
|
let enabled = LaptopAuraPower::new(device_type, &support_data);
|
|
let mut config = AuraConfig {
|
|
led_type: device_type,
|
|
support_data,
|
|
config_name: format!("aura_{prod_id}.ron"),
|
|
ally_fix: None,
|
|
brightness: LedBrightness::Med,
|
|
current_mode: AuraModeNum::Static,
|
|
builtins: BTreeMap::new(),
|
|
multizone: None,
|
|
multizone_on: false,
|
|
enabled,
|
|
per_key_mode_active: false,
|
|
};
|
|
|
|
for n in &config.support_data.basic_modes {
|
|
debug!("creating default for {n}");
|
|
config
|
|
.builtins
|
|
.insert(*n, AuraEffect::default_with_mode(*n));
|
|
|
|
if !config.support_data.basic_zones.is_empty() {
|
|
let mut default = vec![];
|
|
for (i, tmp) in config.support_data.basic_zones.iter().enumerate() {
|
|
default.push(AuraEffect {
|
|
mode: *n,
|
|
zone: *tmp,
|
|
colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]),
|
|
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
|
|
speed: Speed::Med,
|
|
direction: Direction::Left,
|
|
});
|
|
}
|
|
if let Some(m) = config.multizone.as_mut() {
|
|
m.insert(*n, default);
|
|
} else {
|
|
let mut tmp = BTreeMap::new();
|
|
tmp.insert(*n, default);
|
|
config.multizone = Some(tmp);
|
|
}
|
|
}
|
|
}
|
|
config
|
|
}
|
|
|
|
/// Set the mode data, current mode, and if multizone enabled.
|
|
///
|
|
/// Multipurpose, will accept `AuraEffect` with zones and put in the correct
|
|
/// store.
|
|
pub fn set_builtin(&mut self, effect: AuraEffect) {
|
|
self.current_mode = effect.mode;
|
|
if effect.zone() == AuraZone::None {
|
|
self.builtins.insert(*effect.mode(), effect);
|
|
self.multizone_on = false;
|
|
} else {
|
|
if let Some(multi) = self.multizone.as_mut() {
|
|
if let Some(fx) = multi.get_mut(effect.mode()) {
|
|
for fx in fx.iter_mut() {
|
|
if fx.zone == effect.zone {
|
|
*fx = effect;
|
|
return;
|
|
}
|
|
}
|
|
fx.push(effect);
|
|
} else {
|
|
multi.insert(*effect.mode(), vec![effect]);
|
|
}
|
|
} else {
|
|
let mut tmp = BTreeMap::new();
|
|
tmp.insert(*effect.mode(), vec![effect]);
|
|
self.multizone = Some(tmp);
|
|
}
|
|
self.multizone_on = true;
|
|
}
|
|
}
|
|
|
|
pub fn get_multizone(&self, aura_type: AuraModeNum) -> Option<&[AuraEffect]> {
|
|
if let Some(multi) = &self.multizone {
|
|
return multi.get(&aura_type).map(|v| v.as_slice());
|
|
}
|
|
None
|
|
}
|
|
|
|
/// Create a default for the `current_mode` if multizone and no config
|
|
/// exists.
|
|
pub fn create_multizone_default(&mut self) -> Result<(), RogError> {
|
|
let mut default = vec![];
|
|
for (i, tmp) in self.support_data.basic_zones.iter().enumerate() {
|
|
default.push(AuraEffect {
|
|
mode: self.current_mode,
|
|
zone: *tmp,
|
|
colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]),
|
|
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
|
|
speed: Speed::Med,
|
|
direction: Direction::Left,
|
|
});
|
|
}
|
|
if default.is_empty() {
|
|
return Err(RogError::AuraEffectNotSupported);
|
|
}
|
|
|
|
if let Some(multizones) = self.multizone.as_mut() {
|
|
multizones.insert(self.current_mode, default);
|
|
} else {
|
|
let mut tmp = BTreeMap::new();
|
|
tmp.insert(self.current_mode, default);
|
|
self.multizone = Some(tmp);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Reload the config from disk then verify and update it if required.
|
|
/// Always rewrites the file to disk.
|
|
pub fn load_and_update_config(prod_id: &str) -> AuraConfig {
|
|
// New loads data from the DB also
|
|
let mut config_init = AuraConfig::new(prod_id);
|
|
// config_init.set_filename(prod_id);
|
|
let mut config_loaded = config_init.clone().load();
|
|
// update the initialised data with what we loaded from disk
|
|
for mode_init in &mut config_init.builtins {
|
|
// update init values from loaded values if they exist
|
|
if let Some(loaded) = config_loaded.builtins.get(mode_init.0) {
|
|
*mode_init.1 = loaded.clone();
|
|
}
|
|
}
|
|
// Then replace just incase the initialised data contains new modes added
|
|
config_loaded.builtins = config_init.builtins;
|
|
config_loaded.support_data = config_init.support_data;
|
|
config_loaded.led_type = config_init.led_type;
|
|
config_loaded.ally_fix = config_init.ally_fix;
|
|
|
|
for enabled_init in &mut config_init.enabled.states {
|
|
for enabled in &mut config_loaded.enabled.states {
|
|
if enabled.zone == enabled_init.zone {
|
|
*enabled_init = *enabled;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
config_loaded.enabled = config_init.enabled;
|
|
|
|
if let (Some(mut multizone_init), Some(multizone_loaded)) =
|
|
(config_init.multizone, config_loaded.multizone.as_mut())
|
|
{
|
|
for mode in multizone_init.iter_mut() {
|
|
// update init values from loaded values if they exist
|
|
if let Some(loaded) = multizone_loaded.get(mode.0) {
|
|
let mut new_set = Vec::new();
|
|
let data = LedSupportData::get_data(prod_id);
|
|
// only reuse a zone mode if the mode is supported
|
|
for mode in loaded {
|
|
if data.basic_modes.contains(&mode.mode) {
|
|
new_set.push(mode.clone());
|
|
}
|
|
}
|
|
*mode.1 = new_set;
|
|
}
|
|
}
|
|
*multizone_loaded = multizone_init;
|
|
}
|
|
|
|
config_loaded.write();
|
|
config_loaded
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use rog_aura::keyboard::AuraPowerState;
|
|
use rog_aura::{
|
|
AuraEffect, AuraModeNum, AuraZone, Colour, Direction, LedBrightness, PowerZones, Speed,
|
|
};
|
|
|
|
use super::AuraConfig;
|
|
|
|
#[test]
|
|
fn set_multizone_4key_config() {
|
|
std::env::set_var("BOARD_NAME", "");
|
|
let mut config = AuraConfig::new("19b6");
|
|
|
|
let effect = AuraEffect {
|
|
colour1: Colour {
|
|
r: 0xff,
|
|
g: 0x00,
|
|
b: 0xff,
|
|
},
|
|
zone: AuraZone::Key1,
|
|
..Default::default()
|
|
};
|
|
config.set_builtin(effect);
|
|
|
|
assert!(config.multizone.is_some());
|
|
|
|
let effect = AuraEffect {
|
|
colour1: Colour {
|
|
r: 0x00,
|
|
g: 0xff,
|
|
b: 0xff,
|
|
},
|
|
zone: AuraZone::Key2,
|
|
..Default::default()
|
|
};
|
|
config.set_builtin(effect);
|
|
|
|
let effect = AuraEffect {
|
|
colour1: Colour {
|
|
r: 0xff,
|
|
g: 0xff,
|
|
b: 0x00,
|
|
},
|
|
zone: AuraZone::Key3,
|
|
..Default::default()
|
|
};
|
|
config.set_builtin(effect);
|
|
|
|
let effect = AuraEffect {
|
|
colour1: Colour {
|
|
r: 0x00,
|
|
g: 0xff,
|
|
b: 0x00,
|
|
},
|
|
zone: AuraZone::Key4,
|
|
..Default::default()
|
|
};
|
|
let effect_clone = effect.clone();
|
|
config.set_builtin(effect);
|
|
// This should replace existing
|
|
config.set_builtin(effect_clone);
|
|
|
|
let res = config.multizone.unwrap();
|
|
let sta = res.get(&AuraModeNum::Static).unwrap();
|
|
assert_eq!(sta.len(), 4);
|
|
assert_eq!(sta[0].colour1, Colour {
|
|
r: 0xff,
|
|
g: 0x00,
|
|
b: 0xff
|
|
});
|
|
assert_eq!(sta[1].colour1, Colour {
|
|
r: 0x00,
|
|
g: 0xff,
|
|
b: 0xff
|
|
});
|
|
assert_eq!(sta[2].colour1, Colour {
|
|
r: 0xff,
|
|
g: 0xff,
|
|
b: 0x00
|
|
});
|
|
assert_eq!(sta[3].colour1, Colour {
|
|
r: 0x00,
|
|
g: 0xff,
|
|
b: 0x00
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn set_multizone_multimode_config() {
|
|
std::env::set_var("BOARD_NAME", "");
|
|
let mut config = AuraConfig::new("19b6");
|
|
|
|
let effect = AuraEffect {
|
|
zone: AuraZone::Key1,
|
|
..Default::default()
|
|
};
|
|
config.set_builtin(effect);
|
|
|
|
assert!(config.multizone.is_some());
|
|
|
|
let effect = AuraEffect {
|
|
zone: AuraZone::Key2,
|
|
mode: AuraModeNum::Breathe,
|
|
..Default::default()
|
|
};
|
|
config.set_builtin(effect);
|
|
|
|
let effect = AuraEffect {
|
|
zone: AuraZone::Key3,
|
|
mode: AuraModeNum::Comet,
|
|
..Default::default()
|
|
};
|
|
config.set_builtin(effect);
|
|
|
|
let effect = AuraEffect {
|
|
zone: AuraZone::Key4,
|
|
mode: AuraModeNum::Pulse,
|
|
..Default::default()
|
|
};
|
|
config.set_builtin(effect);
|
|
|
|
let res = config.multizone.unwrap();
|
|
let sta = res.get(&AuraModeNum::Static).unwrap();
|
|
assert_eq!(sta.len(), 1);
|
|
|
|
let sta = res.get(&AuraModeNum::Breathe).unwrap();
|
|
assert_eq!(sta.len(), 1);
|
|
|
|
let sta = res.get(&AuraModeNum::Comet).unwrap();
|
|
assert_eq!(sta.len(), 1);
|
|
|
|
let sta = res.get(&AuraModeNum::Pulse).unwrap();
|
|
assert_eq!(sta.len(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn verify_0x1866_g531i() {
|
|
std::env::set_var("BOARD_NAME", "G513I");
|
|
let mut config = AuraConfig::new("1866");
|
|
|
|
assert_eq!(config.brightness, LedBrightness::Med);
|
|
assert_eq!(config.builtins.len(), 5);
|
|
assert_eq!(config.builtins.first_entry().unwrap().get(), &AuraEffect {
|
|
mode: AuraModeNum::Static,
|
|
zone: AuraZone::None,
|
|
colour1: Colour { r: 166, g: 0, b: 0 },
|
|
colour2: Colour { r: 0, g: 0, b: 0 },
|
|
speed: Speed::Med,
|
|
direction: Direction::Right
|
|
});
|
|
assert_eq!(config.enabled.states.len(), 1);
|
|
assert_eq!(config.enabled.states[0], AuraPowerState {
|
|
zone: PowerZones::KeyboardAndLightbar,
|
|
boot: true,
|
|
awake: true,
|
|
sleep: true,
|
|
shutdown: true
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn verify_0x19b6_g634j() {
|
|
std::env::set_var("BOARD_NAME", "G634J");
|
|
let mut config = AuraConfig::new("19b6");
|
|
|
|
assert_eq!(config.brightness, LedBrightness::Med);
|
|
assert_eq!(config.builtins.len(), 12);
|
|
assert_eq!(config.builtins.first_entry().unwrap().get(), &AuraEffect {
|
|
mode: AuraModeNum::Static,
|
|
zone: AuraZone::None,
|
|
colour1: Colour { r: 166, g: 0, b: 0 },
|
|
colour2: Colour { r: 0, g: 0, b: 0 },
|
|
speed: Speed::Med,
|
|
direction: Direction::Right
|
|
});
|
|
assert_eq!(config.enabled.states.len(), 4);
|
|
assert_eq!(config.enabled.states[0], AuraPowerState {
|
|
zone: PowerZones::Keyboard,
|
|
boot: true,
|
|
awake: true,
|
|
sleep: true,
|
|
shutdown: true
|
|
});
|
|
}
|
|
}
|