Merge branch 'fluke/aura_advanced' into 'main'

Fluke/aura advanced

See merge request asus-linux/asusctl!148
This commit is contained in:
Luke Jones
2023-01-08 09:28:23 +00:00
13 changed files with 227 additions and 227 deletions

3
Cargo.lock generated
View File

@@ -666,6 +666,7 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
"toml",
]
[[package]]
@@ -816,6 +817,8 @@ version = "4.5.8"
dependencies = [
"config-traits",
"dirs",
"env_logger",
"log",
"rog_anime",
"rog_aura",
"rog_dbus",

128
MANUAL.md
View File

@@ -117,60 +117,67 @@ I'm unsure of how many laptops this works on, so please try it.
An Aura config itself is a file with contents:
```json
{
"name": "aura-default",
"aura": [
{
"Breathe": {
"led_type": {
"Key": "W"
},
"start_colour1": [
255,
0,
20
```ron
(
name: "aura-default",
aura: (
effects: [
Breathe((
led: W,
start_colour1: (255, 0, 20),
start_colour2: (20, 255, 0),
speed: Low,
)),
Breathe((
led: A,
start_colour1: (255, 0, 20),
start_colour2: (20, 255, 0),
speed: Low,
)),
Breathe((
led: S,
start_colour1: (255, 0, 20),
start_colour2: (20, 255, 0),
speed: Low,
)),
Breathe((
led: D,
start_colour1: (255, 0, 20),
start_colour2: (20, 255, 0),
speed: Low,
)),
Breathe((
led: F,
start_colour1: (255, 0, 0),
start_colour2: (255, 0, 0),
speed: High,
)),
Static((
led: RCtrl,
colour: (0, 0, 255),
)),
Static((
led: LCtrl,
colour: (0, 0, 255),
)),
Static((
led: Esc,
colour: (0, 0, 255),
)),
DoomFlicker((
led: N9,
start_colour: (0, 0, 255),
max_percentage: 80,
min_percentage: 40,
)),
],
"start_colour2": [
20,
255,
0
],
"speed": "Low"
}
},
{
"Static": {
"led_type": {
"Key": "Esc"
},
"colour": [
0,
0,
255
]
}
},
{
"Flicker": {
"led_type": {
"Key": "N9"
},
"start_colour": [
0,
0,
255
],
"max_percentage": 80,
"min_percentage": 40
}
}
]
}
zoned: false,
),
)
```
If your laptop supports multizone, `"led_type"` can also be `"Zone": <one of the following>`
- `"None"`
If your laptop supports multizone, `"led"` can also be `"Zone": <one of the following>`
- `SingleZone` // Keyboards with only one zone
- `ZonedKbLeft` // keyboard left
- `ZonedKbLeftMid` // keyboard left-middle
- `ZonedKbRightMid` // etc
@@ -182,6 +189,25 @@ If your laptop supports multizone, `"led_type"` can also be `"Zone": <one of the
- `LightbarLeftCorner`
- `LightbarLeft`
Single zone example:
```ron
(
name: "aura-default",
aura: (
effects: [
DoomFlicker((
led: SingleZone,
start_colour: (200, 40, 5),
max_percentage: 80,
min_percentage: 40,
)),
],
zoned: true,
),
)
```
At the moment there are only three effects available as shown in the example. More will come in the future
but this may take me some time.

View File

@@ -9,6 +9,7 @@ edition = "2021"
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
toml.workspace = true
ron.workspace = true
log.workspace = true

View File

@@ -31,6 +31,46 @@ where
.unwrap_or_else(|e| panic!("Could not create {:?} {e}", Self::config_dir()));
}
config.push(self.file_name());
let mut do_rename = !config.exists();
let mut cfg_old = config.clone();
// Migrating all configs to .ron format, so we do need to check for older ones
if do_rename {
warn!("Config {cfg_old:?} does not exist, looking for .cfg next");
cfg_old.pop();
let tmp = self.file_name();
let parts: Vec<_> = tmp.split('.').collect();
cfg_old.push(format!("{}.cfg", parts[0]));
}
if do_rename && cfg_old.exists() {
// Now we gotta rename it
warn!("Renaming {cfg_old:?} to {config:?}");
std::fs::rename(&cfg_old, &config).unwrap_or_else(|err| {
error!(
"Could not rename. Please remove {} then restart service: Error {}",
self.file_name(),
err
)
});
do_rename = false;
}
if do_rename && !cfg_old.exists() {
warn!("Config {cfg_old:?} does not exist, looking for .conf next");
cfg_old.pop();
let tmp = self.file_name();
let parts: Vec<_> = tmp.split('.').collect();
cfg_old.push(format!("{}.conf", parts[0]));
}
if do_rename && cfg_old.exists() {
// Now we gotta rename it
warn!("Renaming {cfg_old:?} to {config:?}");
std::fs::rename(&cfg_old, &config).unwrap_or_else(|err| {
error!(
"Could not rename. Please remove {} then restart service: Error {}",
self.file_name(),
err
)
});
}
config
}
@@ -146,6 +186,8 @@ where
self = data;
} else if let Ok(data) = serde_json::from_str(&buf) {
self = data;
} else if let Ok(data) = toml::from_str(&buf) {
self = data;
} else {
self.rename_file_old();
self = Self::new();
@@ -205,8 +247,12 @@ where
self = data;
} else if let Ok(data) = serde_json::from_str(&buf) {
self = data;
} else if let Ok(data) = toml::from_str(&buf) {
self = data;
} else if let Ok(data) = serde_json::from_str::<OldConfig>(&buf) {
self = data.into();
} else if let Ok(data) = toml::from_str::<OldConfig>(&buf) {
self = data.into();
} else {
self.rename_file_old();
self = Self::new();
@@ -274,10 +320,16 @@ where
self = data;
} else if let Ok(data) = serde_json::from_str(&buf) {
self = data;
} else if let Ok(data) = toml::from_str(&buf) {
self = data;
} else if let Ok(data) = serde_json::from_str::<OldConfig>(&buf) {
self = data.into();
} else if let Ok(data) = toml::from_str::<OldConfig>(&buf) {
self = data.into();
} else if let Ok(data) = serde_json::from_str::<OldConfig2>(&buf) {
self = data.into();
} else if let Ok(data) = toml::from_str::<OldConfig2>(&buf) {
self = data.into();
} else {
self.rename_file_old();
self = Self::new();

View File

@@ -30,3 +30,7 @@ rog_platform = { path = "../rog-platform" }
config-traits = { path = "../config-traits" }
zbus.workspace = true
# cli and logging
log.workspace = true
env_logger.workspace = true

View File

@@ -1,92 +1,30 @@
use std::fs::{create_dir, OpenOptions};
use std::io::{Read, Write};
use std::path::PathBuf;
use std::time::Duration;
use config_traits::{StdConfig, StdConfigLoad1};
use rog_anime::{ActionLoader, AnimTime, AnimeType, Fade, Sequences as AnimeSequences, Vec2};
use rog_aura::advanced::LedCode;
use rog_aura::effects::{AdvancedEffects as AuraSequences, Breathe, DoomFlicker, Effect, Static};
use rog_aura::{Colour, Speed};
use serde::de::DeserializeOwned;
use serde_derive::{Deserialize, Serialize};
use crate::error::Error;
pub trait ConfigLoadSave<T: DeserializeOwned + serde::Serialize> {
fn name(&self) -> String;
const ROOT_CONF_DIR: &str = "rog";
fn default_with_name(name: String) -> T;
fn write(&self) -> Result<(), Error>
where
Self: serde::Serialize,
{
let mut path = if let Some(dir) = dirs::config_dir() {
dir
} else {
return Err(Error::XdgVars);
};
path.push("rog");
if !path.exists() {
create_dir(path.clone())?;
}
let name = self.name();
path.push(name + ".cfg");
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&path)?;
let json = serde_json::to_string_pretty(&self).unwrap();
file.write_all(json.as_bytes())?;
Ok(())
}
fn load(name: String) -> Result<T, Error> {
let mut path = if let Some(dir) = dirs::config_dir() {
dir
} else {
return Err(Error::XdgVars);
};
path.push("rog");
if !path.exists() {
create_dir(path.clone())?;
}
path.push(name.clone() + ".cfg");
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)?;
let mut buf = String::new();
if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len == 0 {
let default = Self::default_with_name(name);
let json = serde_json::to_string_pretty(&default).unwrap();
file.write_all(json.as_bytes())?;
return Ok(default);
} else if let Ok(data) = serde_json::from_str::<T>(&buf) {
return Ok(data);
}
}
Err(Error::ConfigLoadFail)
}
fn root_conf_dir() -> PathBuf {
let mut dir = dirs::config_dir().unwrap_or_else(|| PathBuf::from("/tmp"));
dir.push(ROOT_CONF_DIR);
dir
}
#[derive(Debug, Deserialize, Serialize)]
pub struct UserAnimeConfig {
pub struct ConfigAnime {
pub name: String,
pub anime: Vec<ActionLoader>,
}
impl UserAnimeConfig {
impl ConfigAnime {
pub fn create(&self, anime_type: AnimeType) -> Result<AnimeSequences, Error> {
let mut seq = AnimeSequences::new(anime_type);
@@ -96,25 +34,17 @@ impl UserAnimeConfig {
Ok(seq)
}
}
impl ConfigLoadSave<UserAnimeConfig> for UserAnimeConfig {
fn name(&self) -> String {
self.name.clone()
}
fn default_with_name(name: String) -> Self {
UserAnimeConfig {
name,
..Default::default()
}
pub fn set_name(mut self, name: String) -> Self {
self.name = name;
self
}
}
impl Default for UserAnimeConfig {
impl Default for ConfigAnime {
fn default() -> Self {
Self {
name: "default".to_owned(),
name: "anime-default".to_owned(),
anime: vec![
ActionLoader::AsusImage {
file: "/usr/share/asusd/anime/custom/diagonal-template.png".into(),
@@ -172,26 +102,36 @@ impl Default for UserAnimeConfig {
}
}
impl StdConfig for ConfigAnime {
fn new() -> Self {
Self::default()
}
fn file_name(&self) -> String {
format!("{}.ron", self.name)
}
fn config_dir() -> std::path::PathBuf {
root_conf_dir()
}
}
impl StdConfigLoad1 for ConfigAnime {}
#[derive(Debug, Deserialize, Serialize)]
pub struct UserAuraConfig {
pub struct ConfigAura {
pub name: String,
pub aura: AuraSequences,
}
impl ConfigLoadSave<UserAuraConfig> for UserAuraConfig {
fn name(&self) -> String {
self.name.clone()
}
fn default_with_name(name: String) -> Self {
UserAuraConfig {
name,
..Default::default()
}
impl ConfigAura {
pub fn set_name(mut self, name: String) -> Self {
self.name = name;
self
}
}
impl Default for UserAuraConfig {
impl Default for ConfigAura {
fn default() -> Self {
let mut seq = AuraSequences::new(false);
let mut key = Effect::Breathe(Breathe::new(
@@ -228,86 +168,52 @@ impl Default for UserAuraConfig {
seq.push(key);
Self {
name: "default".to_owned(),
name: "aura-default".to_owned(),
aura: seq,
}
}
}
impl StdConfig for ConfigAura {
fn new() -> Self {
Self::default()
}
fn file_name(&self) -> String {
format!("{}.ron", self.name)
}
fn config_dir() -> std::path::PathBuf {
root_conf_dir()
}
}
impl StdConfigLoad1 for ConfigAura {}
#[derive(Debug, Default, Deserialize, Serialize)]
#[serde(default)]
pub struct UserConfig {
pub struct ConfigBase {
/// Name of active anime config file in the user config directory
pub active_anime: Option<String>,
/// Name of active aura config file in the user config directory
pub active_aura: Option<String>,
}
impl UserConfig {
pub fn new() -> Self {
impl StdConfig for ConfigBase {
fn new() -> Self {
Self {
active_anime: Some("anime-default".to_owned()),
active_aura: Some("aura-default".to_owned()),
}
}
pub fn load(&mut self) -> Result<(), Error> {
let mut path = if let Some(dir) = dirs::config_dir() {
dir
} else {
return Err(Error::XdgVars);
};
path.push("rog");
if !path.exists() {
create_dir(path.clone())?;
}
path.push("rog-user.cfg");
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)?;
let mut buf = String::new();
if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len == 0 {
let json = serde_json::to_string_pretty(&self).unwrap();
file.write_all(json.as_bytes())?;
} else if let Ok(data) = serde_json::from_str::<UserConfig>(&buf) {
self.active_anime = data.active_anime;
self.active_aura = data.active_aura;
return Ok(());
}
}
Ok(())
fn file_name(&self) -> String {
"rog-user.ron".to_owned()
}
pub fn write(&self) -> Result<(), Error> {
let mut path = if let Some(dir) = dirs::config_dir() {
dir
} else {
return Err(Error::XdgVars);
};
path.push("rog");
if !path.exists() {
create_dir(path.clone())?;
}
path.push("rog-user.cfg");
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&path)?;
let json = serde_json::to_string_pretty(&self).unwrap();
file.write_all(json.as_bytes())?;
Ok(())
fn config_dir() -> std::path::PathBuf {
root_conf_dir()
}
}
impl StdConfigLoad1 for ConfigBase {}

View File

@@ -4,6 +4,7 @@ use std::sync::{Arc, Mutex};
use std::thread::sleep;
use std::time::{Duration, Instant};
use config_traits::StdConfig;
use rog_anime::error::AnimeError;
use rog_anime::{ActionData, ActionLoader, AnimTime, Fade, Sequences, Vec2};
use rog_dbus::RogDbusClientBlocking;
@@ -11,8 +12,8 @@ use serde_derive::{Deserialize, Serialize};
use zbus::dbus_interface;
use zbus::zvariant::{ObjectPath, Type};
use crate::config::ConfigAnime;
use crate::error::Error;
use crate::user_config::{ConfigLoadSave, UserAnimeConfig};
#[derive(Debug, Clone, Deserialize, Serialize, Type)]
pub struct Timer {
@@ -134,7 +135,7 @@ impl<'a> CtrlAnimeInner<'static> {
}
pub struct CtrlAnime<'a> {
config: Arc<Mutex<UserAnimeConfig>>,
config: Arc<Mutex<ConfigAnime>>,
client: RogDbusClientBlocking<'a>,
inner: Arc<Mutex<CtrlAnimeInner<'a>>>,
/// Must be the same Atomic as in CtrlAnimeInner
@@ -143,7 +144,7 @@ pub struct CtrlAnime<'a> {
impl CtrlAnime<'static> {
pub fn new(
config: Arc<Mutex<UserAnimeConfig>>,
config: Arc<Mutex<ConfigAnime>>,
inner: Arc<Mutex<CtrlAnimeInner<'static>>>,
client: RogDbusClientBlocking<'static>,
inner_early_return: Arc<AtomicBool>,
@@ -206,7 +207,7 @@ impl CtrlAnime<'static> {
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
}
config.anime.push(action);
config.write()?;
config.write();
let json = serde_json::to_string_pretty(&*config).expect("Parse config to JSON failed");
@@ -251,7 +252,7 @@ impl CtrlAnime<'static> {
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
}
config.anime.push(action);
config.write()?;
config.write();
let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
@@ -296,7 +297,7 @@ impl CtrlAnime<'static> {
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
}
config.anime.push(action);
config.write()?;
config.write();
let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
@@ -321,7 +322,7 @@ impl CtrlAnime<'static> {
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
}
config.anime.push(action);
config.write()?;
config.write();
let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
@@ -344,7 +345,7 @@ impl CtrlAnime<'static> {
if (index as usize) < config.anime.len() {
config.anime.remove(index as usize);
}
config.write()?;
config.write();
let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");

View File

@@ -1,15 +1,16 @@
use std::fs::OpenOptions;
use std::io::Read;
use std::io::{Read, Write};
use std::path::PathBuf;
use std::sync::atomic::AtomicBool;
use std::sync::{Arc, Mutex};
use config_traits::{StdConfig, StdConfigLoad1};
use rog_anime::usb::get_anime_type;
use rog_aura::aura_detection::LaptopLedData;
use rog_aura::layouts::KeyLayout;
use rog_dbus::RogDbusClientBlocking;
use rog_user::config::*;
use rog_user::ctrl_anime::{CtrlAnime, CtrlAnimeInner};
use rog_user::user_config::*;
use rog_user::DBUS_NAME;
use smol::Executor;
use zbus::Connection;
@@ -21,6 +22,13 @@ const DATA_DIR: &str = env!("CARGO_MANIFEST_DIR");
const BOARD_NAME: &str = "/sys/class/dmi/id/board_name";
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut logger = env_logger::Builder::new();
logger
.parse_default_env()
.target(env_logger::Target::Stdout)
.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()))
.init();
println!(" user daemon v{}", rog_user::VERSION);
println!(" rog-anime v{}", rog_anime::VERSION);
println!(" rog-dbus v{}", rog_dbus::VERSION);
@@ -29,8 +37,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let (client, _) = RogDbusClientBlocking::new()?;
let supported = client.proxies().supported().supported_functions()?;
let mut config = UserConfig::new();
config.load()?;
let config = ConfigBase::new().load();
let executor = Executor::new();
@@ -39,7 +46,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
if supported.anime_ctrl.0 {
if let Some(cfg) = config.active_anime {
let anime_type = get_anime_type()?;
let anime_config = UserAnimeConfig::load(cfg)?;
let anime_config = ConfigAnime::new().set_name(cfg).load();
let anime = anime_config.create(anime_type)?;
let anime_config = Arc::new(Mutex::new(anime_config));
@@ -70,7 +77,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// if supported.keyboard_led.per_key_led_mode {
if let Some(cfg) = config.active_aura {
let mut aura_config = UserAuraConfig::load(cfg)?;
let mut aura_config = ConfigAura::new().set_name(cfg).load();
// Find and load a matching layout for laptop
let mut file = OpenOptions::new()

View File

@@ -1,4 +1,4 @@
pub mod user_config;
pub mod config;
pub mod error;

View File

@@ -1,7 +1,7 @@
use config_traits::{StdConfig, StdConfigLoad3};
use serde_derive::{Deserialize, Serialize};
const CONFIG_FILE: &str = "asusd.conf";
const CONFIG_FILE: &str = "asusd.ron";
#[derive(Deserialize, Serialize, Default)]
pub struct Config {

View File

@@ -5,7 +5,7 @@ use rog_anime::error::AnimeError;
use rog_anime::{ActionData, ActionLoader, AnimTime, AnimeType, Fade, Vec2};
use serde_derive::{Deserialize, Serialize};
const CONFIG_FILE: &str = "anime.conf";
const CONFIG_FILE: &str = "anime.ron";
#[derive(Deserialize, Serialize)]
pub struct AnimeConfigV341 {

View File

@@ -8,7 +8,7 @@ use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardLed;
use serde_derive::{Deserialize, Serialize};
const CONFIG_FILE: &str = "aura.conf";
const CONFIG_FILE: &str = "aura.ron";
/// Enable/disable LED control in various states such as
/// when the device is awake, suspended, shutting down or

View File

@@ -7,8 +7,8 @@ use serde_derive::{Deserialize, Serialize};
use crate::CONFIG_PATH_BASE;
const CONFIG_FILE: &str = "profile.conf";
const CONFIG_FAN_FILE: &str = "fan_curves.conf";
const CONFIG_FILE: &str = "profile.ron";
const CONFIG_FAN_FILE: &str = "fan_curves.ron";
#[derive(Deserialize, Serialize, Debug)]
pub struct ProfileConfig {