From 6528ec95c2873d66b30f2830dc5d60719693dbc8 Mon Sep 17 00:00:00 2001 From: Luke D Jones Date: Fri, 19 Mar 2021 20:16:18 +1300 Subject: [PATCH] Massive refactor of led control - Write brightness to kernel LED class path Closes #63, #53 --- CHANGELOG.md | 6 + Cargo.lock | 10 +- asus-notify/src/main.rs | 2 +- asusctl/Cargo.toml | 2 +- asusctl/examples/ball.rs | 2 +- asusctl/examples/comet.rs | 2 +- asusctl/examples/iterate-keys.rs | 2 +- asusctl/examples/per-key-effect-2.rs | 2 +- asusctl/examples/pulser.rs | 2 +- asusctl/src/aura_cli.rs | 331 ++++++++++++++ asusctl/src/main.rs | 33 +- daemon/Cargo.toml | 2 +- daemon/src/config.rs | 50 +-- daemon/src/config_aura.rs | 124 ++++++ daemon/src/config_old.rs | 51 ++- daemon/src/ctrl_gfx/gfx.rs | 17 +- daemon/src/ctrl_gfx/mod.rs | 12 +- daemon/src/ctrl_leds.rs | 353 +++++++-------- daemon/src/daemon.rs | 22 +- daemon/src/laptops.rs | 66 +-- daemon/src/lib.rs | 1 + data/asusd-ledmodes.toml | 72 ++-- rog-dbus/src/lib.rs | 4 +- rog-dbus/src/zbus_led.rs | 56 +-- rog-types/Cargo.toml | 2 +- rog-types/src/aura_modes.rs | 476 +++++++++++---------- rog-types/src/{fancy.rs => aura_perkey.rs} | 4 +- rog-types/src/cli_options.rs | 88 ---- rog-types/src/gfx_vendors.rs | 35 +- rog-types/src/lib.rs | 266 +----------- 30 files changed, 1131 insertions(+), 964 deletions(-) create mode 100644 asusctl/src/aura_cli.rs create mode 100644 daemon/src/config_aura.rs rename rog-types/src/{fancy.rs => aura_perkey.rs} (99%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f60eec8..fea1ea74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Changed +- Refactor keyboard LED handling +- Added --list for profiles (Thanks @aqez) +### Broken +- Per-key LED modes, which need thinking about how to go ahead with for future + # [3.1.7] - 2021-03-11 ### Changed - Refactor many parts of daemon diff --git a/Cargo.lock b/Cargo.lock index 8407b4bc..3efea808 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,7 +33,7 @@ dependencies = [ [[package]] name = "asusctl" -version = "3.1.3" +version = "3.1.4" dependencies = [ "daemon", "gumdrop", @@ -196,7 +196,7 @@ dependencies = [ [[package]] name = "daemon" -version = "3.1.7" +version = "3.2.0" dependencies = [ "env_logger", "intel-pstate", @@ -504,9 +504,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "538c092e5586f4cdd7dd8078c4a79220e3e168880218124dcbce860f0ea938c6" +checksum = "ba4aede83fc3617411dc6993bc8c70919750c1c257c6ca6a502aed6e0e2394ae" [[package]] name = "libudev-sys" @@ -908,7 +908,7 @@ dependencies = [ [[package]] name = "rog_types" -version = "3.0.0" +version = "3.1.0" dependencies = [ "gumdrop", "rog_fan_curve", diff --git a/asus-notify/src/main.rs b/asus-notify/src/main.rs index 6f715eae..0331d6b7 100644 --- a/asus-notify/src/main.rs +++ b/asus-notify/src/main.rs @@ -67,7 +67,7 @@ fn main() -> Result<(), Box> { } let x = do_notif(&format!( "Keyboard LED mode changed to {}", - <&str>::from(&ledmode) + ledmode.mode_name() ))?; last_led_notif = Some(x); } diff --git a/asusctl/Cargo.toml b/asusctl/Cargo.toml index 4611290d..fb627bc7 100644 --- a/asusctl/Cargo.toml +++ b/asusctl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "asusctl" -version = "3.1.3" +version = "3.1.4" authors = ["Luke D Jones "] edition = "2018" diff --git a/asusctl/examples/ball.rs b/asusctl/examples/ball.rs index 1436038c..d2e80888 100644 --- a/asusctl/examples/ball.rs +++ b/asusctl/examples/ball.rs @@ -1,5 +1,5 @@ use rog_dbus::AuraDbusClient; -use rog_types::fancy::{GX502Layout, Key, KeyColourArray, KeyLayout}; +use rog_types::aura_perkey::{GX502Layout, Key, KeyColourArray, KeyLayout}; use std::collections::LinkedList; #[derive(Debug, Clone)] diff --git a/asusctl/examples/comet.rs b/asusctl/examples/comet.rs index 590f8744..a3b09b69 100644 --- a/asusctl/examples/comet.rs +++ b/asusctl/examples/comet.rs @@ -1,5 +1,5 @@ use rog_dbus::AuraDbusClient; -use rog_types::fancy::{GX502Layout, KeyColourArray, KeyLayout}; +use rog_types::aura_perkey::{GX502Layout, KeyColourArray, KeyLayout}; fn main() -> Result<(), Box> { let (dbus, _) = AuraDbusClient::new()?; diff --git a/asusctl/examples/iterate-keys.rs b/asusctl/examples/iterate-keys.rs index 3d52a577..78be8dca 100644 --- a/asusctl/examples/iterate-keys.rs +++ b/asusctl/examples/iterate-keys.rs @@ -1,5 +1,5 @@ use rog_dbus::AuraDbusClient; -use rog_types::fancy::{GX502Layout, Key, KeyColourArray, KeyLayout}; +use rog_types::aura_perkey::{GX502Layout, Key, KeyColourArray, KeyLayout}; fn main() -> Result<(), Box> { let (dbus, _) = AuraDbusClient::new()?; diff --git a/asusctl/examples/per-key-effect-2.rs b/asusctl/examples/per-key-effect-2.rs index e35a7e9c..5f3947ad 100644 --- a/asusctl/examples/per-key-effect-2.rs +++ b/asusctl/examples/per-key-effect-2.rs @@ -1,5 +1,5 @@ use rog_dbus::AuraDbusClient; -use rog_types::fancy::{Key, KeyColourArray}; +use rog_types::aura_perkey::{Key, KeyColourArray}; fn main() -> Result<(), Box> { let (dbus, _) = AuraDbusClient::new()?; diff --git a/asusctl/examples/pulser.rs b/asusctl/examples/pulser.rs index 61c79f29..c080e9e5 100644 --- a/asusctl/examples/pulser.rs +++ b/asusctl/examples/pulser.rs @@ -1,5 +1,5 @@ use rog_dbus::AuraDbusClient; -use rog_types::fancy::{GX502Layout, KeyColourArray, KeyLayout}; +use rog_types::aura_perkey::{GX502Layout, KeyColourArray, KeyLayout}; fn main() -> Result<(), Box> { let (dbus, _) = AuraDbusClient::new()?; diff --git a/asusctl/src/aura_cli.rs b/asusctl/src/aura_cli.rs new file mode 100644 index 00000000..999d9fce --- /dev/null +++ b/asusctl/src/aura_cli.rs @@ -0,0 +1,331 @@ +use rog_types::{ + aura_modes::{Colour, Direction, Speed, AuraEffect, AuraModeNum, AuraZone}, + error::AuraError, +}; +use gumdrop::Options; +use std::str::FromStr; + +#[derive(Options)] +pub struct LedBrightness { + level: Option, +} +impl LedBrightness { + pub fn new(level: Option) -> Self { + LedBrightness { level } + } + + pub fn level(&self) -> Option { + self.level + } +} +impl FromStr for LedBrightness { + type Err = AuraError; + + fn from_str(s: &str) -> Result { + let s = s.to_lowercase(); + match s.as_str() { + "off" => Ok(LedBrightness { level: Some(0x00) }), + "low" => Ok(LedBrightness { level: Some(0x01) }), + "med" => Ok(LedBrightness { level: Some(0x02) }), + "high" => Ok(LedBrightness { level: Some(0x03) }), + _ => { + print!("Invalid argument, must be one of: off, low, med, high"); + Err(AuraError::ParseBrightness) + } + } + } +} +impl ToString for LedBrightness { + fn to_string(&self) -> String { + let s = match self.level { + Some(0x00) => "low", + Some(0x01) => "med", + Some(0x02) => "high", + _ => "unknown", + }; + s.to_string() + } +} + +#[derive(Debug, Clone, Options, Default)] +pub struct SingleSpeed { + #[options(help = "print help message")] + help: bool, + #[options(no_long, meta = "WORD", help = "set the speed: low, med, high")] + pub speed: Speed, +} +#[derive(Debug, Clone, Options, Default)] +pub struct SingleSpeedDirection { + #[options(help = "print help message")] + help: bool, + #[options(no_long, meta = "", help = "set the direction: up, down, left, right")] + pub direction: Direction, + #[options(no_long, meta = "", help = "set the speed: low, med, high")] + pub speed: Speed, +} + +#[derive(Debug, Clone, Default, Options)] +pub struct SingleColour { + #[options(help = "print help message")] + help: bool, + #[options(no_long, meta = "", help = "set the RGB value e.g, ff00ff")] + pub colour: Colour, +} + +#[derive(Debug, Clone, Default, Options)] +pub struct SingleColourSpeed { + #[options(help = "print help message")] + help: bool, + #[options(no_long, meta = "", help = "set the RGB value e.g, ff00ff")] + pub colour: Colour, + #[options(no_long, meta = "", help = "set the speed: low, med, high")] + pub speed: Speed, +} + +#[derive(Debug, Clone, Options, Default)] +pub struct TwoColourSpeed { + #[options(help = "print help message")] + help: bool, + #[options(no_long, meta = "", help = "set the first RGB value e.g, ff00ff")] + pub colour: Colour, + #[options(no_long, meta = "", help = "set the second RGB value e.g, ff00ff")] + pub colour2: Colour, + #[options(no_long, meta = "", help = "set the speed: low, med, high")] + pub speed: Speed, +} + +#[derive(Debug, Clone, Default, Options)] +pub struct MultiColour { + #[options(help = "print help message")] + help: bool, + #[options(short = "a", meta = "", help = "set the RGB value e.g, ff00ff")] + pub colour1: Colour, + #[options(short = "b", meta = "", help = "set the RGB value e.g, ff00ff")] + pub colour2: Colour, + #[options(short = "c", meta = "", help = "set the RGB value e.g, ff00ff")] + pub colour3: Colour, + #[options(short = "d", meta = "", help = "set the RGB value e.g, ff00ff")] + pub colour4: Colour, +} + +#[derive(Debug, Clone, Default, Options)] +pub struct MultiColourSpeed { + #[options(help = "print help message")] + help: bool, + #[options(short = "a", meta = "", help = "set the RGB value e.g, ff00ff")] + pub colour1: Colour, + #[options(short = "b", meta = "", help = "set the RGB value e.g, ff00ff")] + pub colour2: Colour, + #[options(short = "c", meta = "", help = "set the RGB value e.g, ff00ff")] + pub colour3: Colour, + #[options(short = "d", meta = "", help = "set the RGB value e.g, ff00ff")] + pub colour4: Colour, + #[options(no_long, meta = "", help = "set the speed: low, med, high")] + pub speed: Speed, +} + +/// Byte value for setting the built-in mode. +/// +/// Enum corresponds to the required integer value +#[derive(Options)] +pub enum SetAuraBuiltin { + #[options(help = "set a single static colour")] + Static(SingleColour), + #[options(help = "pulse between one or two colours")] + Breathe(TwoColourSpeed), + #[options(help = "strobe through all colours")] + Strobe(SingleSpeed), + #[options(help = "rainbow cycling in one of four directions")] + Rainbow(SingleSpeedDirection), + #[options(help = "rain pattern mimicking raindrops")] + Star(TwoColourSpeed), + #[options(help = "rain pattern of three preset colours")] + Rain(SingleSpeed), + #[options(help = "pressed keys are highlighted to fade")] + Highlight(SingleColourSpeed), + #[options(help = "pressed keys generate horizontal laser")] + Laser(SingleColourSpeed), + #[options(help = "pressed keys ripple outwards like a splash")] + Ripple(SingleColourSpeed), + #[options(help = "set a rapid pulse")] + Pulse(SingleColour), + #[options(help = "set a vertical line zooming from left")] + Comet(SingleColour), + #[options(help = "set a wide vertical line zooming from left")] + Flash(SingleColour), + #[options(help = "4-zone multi-colour")] + MultiStatic(MultiColour), + #[options(help = "4-zone multi-colour breathing")] + MultiBreathe(MultiColourSpeed), +} + +impl Default for SetAuraBuiltin { + fn default() -> Self { + SetAuraBuiltin::Static(SingleColour::default()) + } +} + + +impl From<&SingleColour> for AuraEffect { + fn from(aura: &SingleColour) -> Self { + Self { + colour1: aura.colour, + ..Default::default() + } + } +} + +impl From<&SingleSpeed> for AuraEffect { + fn from(aura: &SingleSpeed) -> Self { + Self { + speed: aura.speed, + ..Default::default() + } + } +} + +impl From<&SingleColourSpeed> for AuraEffect { + fn from(aura: &SingleColourSpeed) -> Self { + Self { + colour1: aura.colour, + speed: aura.speed, + ..Default::default() + } + } +} + +impl From<&TwoColourSpeed> for AuraEffect { + fn from(aura: &TwoColourSpeed) -> Self { + Self { + colour1: aura.colour, + colour2: aura.colour2, + ..Default::default() + } + } +} + +impl From<&SingleSpeedDirection> for AuraEffect { + fn from(aura: &SingleSpeedDirection) -> Self { + Self { + speed: aura.speed, + direction: aura.direction, + ..Default::default() + } + } +} + +impl From<&SetAuraBuiltin> for AuraEffect { + fn from(aura: &SetAuraBuiltin) -> Self { + match aura { + SetAuraBuiltin::Static(x) => { + let mut data: AuraEffect = x.into(); + data.mode = AuraModeNum::Static; + data + } + SetAuraBuiltin::Breathe(x) => { + let mut data: AuraEffect = x.into(); + data.mode = AuraModeNum::Breathe; + data + } + SetAuraBuiltin::Strobe(x) => { + let mut data: AuraEffect = x.into(); + data.mode = AuraModeNum::Strobe; + data + } + SetAuraBuiltin::Rainbow(x) => { + let mut data: AuraEffect = x.into(); + data.mode = AuraModeNum::Rainbow; + data + } + SetAuraBuiltin::Star(x) => { + let mut data: AuraEffect = x.into(); + data.mode = AuraModeNum::Star; + data + } + SetAuraBuiltin::Rain(x) => { + let mut data: AuraEffect = x.into(); + data.mode = AuraModeNum::Rain; + data + } + SetAuraBuiltin::Highlight(x) => { + let mut data: AuraEffect = x.into(); + data.mode = AuraModeNum::Highlight; + data + } + SetAuraBuiltin::Laser(x) => { + let mut data: AuraEffect = x.into(); + data.mode = AuraModeNum::Laser; + data + } + SetAuraBuiltin::Ripple(x) => { + let mut data: AuraEffect = x.into(); + data.mode = AuraModeNum::Ripple; + data + } + SetAuraBuiltin::Pulse(x) => { + let mut data: AuraEffect = x.into(); + data.mode = AuraModeNum::Pulse; + data + } + SetAuraBuiltin::Comet(x) => { + let mut data: AuraEffect = x.into(); + data.mode = AuraModeNum::Comet; + data + } + SetAuraBuiltin::Flash(x) => { + let mut data: AuraEffect = x.into(); + data.mode = AuraModeNum::Flash; + data + } + _ => AuraEffect::default(), + } + } +} + +impl From<&SetAuraBuiltin> for Vec { + fn from(aura: &SetAuraBuiltin) -> Vec { + let mut zones = vec![AuraEffect::default(); 4]; + match aura { + SetAuraBuiltin::MultiStatic(data) => { + zones[0].mode = AuraModeNum::Static; + zones[0].zone = AuraZone::One; + zones[0].colour1 = data.colour1; + + zones[1].mode = AuraModeNum::Static; + zones[1].zone = AuraZone::Two; + zones[1].colour1 = data.colour2; + + zones[2].mode = AuraModeNum::Static; + zones[2].zone = AuraZone::Three; + zones[2].colour1 = data.colour3; + + zones[3].mode = AuraModeNum::Static; + zones[3].zone = AuraZone::Four; + zones[3].colour1 = data.colour4; + }, + SetAuraBuiltin::MultiBreathe(data) => { + zones[0].mode = AuraModeNum::Breathe; + zones[0].zone = AuraZone::One; + zones[0].colour1 = data.colour1; + zones[0].speed = data.speed; + + zones[1].mode = AuraModeNum::Breathe; + zones[1].zone = AuraZone::Two; + zones[1].colour1 = data.colour2; + zones[1].speed = data.speed; + + zones[2].mode = AuraModeNum::Breathe; + zones[2].zone = AuraZone::Three; + zones[2].colour1 = data.colour3; + zones[2].speed = data.speed; + + zones[3].mode = AuraModeNum::Breathe; + zones[3].zone = AuraZone::Four; + zones[3].colour1 = data.colour4; + zones[3].speed = data.speed; + } + _ => {}, + } + zones + } +} \ No newline at end of file diff --git a/asusctl/src/main.rs b/asusctl/src/main.rs index 56aa8107..dd8d7756 100644 --- a/asusctl/src/main.rs +++ b/asusctl/src/main.rs @@ -1,3 +1,5 @@ +mod aura_cli; + use daemon::{ ctrl_fan_cpu::FanCpuSupportedFunctions, ctrl_leds::LedSupportedFunctions, ctrl_rog_bios::RogBiosSupportedFunctions, ctrl_supported::SupportedFunctions, @@ -6,14 +8,15 @@ use gumdrop::{Opt, Options}; use rog_dbus::AuraDbusClient; use rog_types::{ anime_matrix::{AniMeDataBuffer, FULL_PANE_LEN}, - aura_modes::AuraModes, - cli_options::{AniMeActions, AniMeStatusValue, LedBrightness, SetAuraBuiltin}, + aura_modes::{AuraEffect, AuraModeNum}, + cli_options::{AniMeActions, AniMeStatusValue}, gfx_vendors::GfxVendors, profile::{FanLevel, ProfileCommand, ProfileEvent}, }; use std::env::args; use yansi_term::Colour::Green; use yansi_term::Colour::Red; +use crate::aura_cli::{LedBrightness, SetAuraBuiltin}; #[derive(Default, Options)] struct CLIStart { @@ -199,7 +202,7 @@ fn main() -> Result<(), Box> { let level = dbus.proxies().led().get_led_brightness()?; println!("Current keyboard led brightness: {}", level.to_string()); } - Some(level) => dbus.proxies().led().set_brightness(level)?, + Some(level) => dbus.proxies().led().set_led_brightness(level)?, } } @@ -325,9 +328,12 @@ fn handle_led_mode( .lines() .map(|s| s.to_string()) .collect(); - for (_, command) in commands.iter().enumerate().filter(|(mode_num, _)| { + for command in commands.iter().filter(|mode| { if let Some(modes) = supported.stock_led_modes.as_ref() { - return modes.contains(&(*mode_num as u8)); + return modes.contains(&::from(mode.as_str())); + } + if supported.multizone_led_mode { + return true; } false }) { @@ -351,9 +357,20 @@ fn handle_led_mode( println!("{}", mode.self_usage()); return Ok(()); } - dbus.proxies() - .led() - .set_led_mode(&::from(mode))?; + match mode { + SetAuraBuiltin::MultiStatic(_) | SetAuraBuiltin::MultiBreathe(_) => { + let zones = >::from(mode); + for eff in zones { + dbus.proxies() + .led() + .set_led_mode(&eff)? + } + } + _ => dbus + .proxies() + .led() + .set_led_mode(&::from(mode))?, + } } Ok(()) } diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index 99a2f756..80ed4160 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "daemon" -version = "3.1.7" +version = "3.2.0" license = "MPL-2.0" readme = "README.md" authors = ["Luke "] diff --git a/daemon/src/config.rs b/daemon/src/config.rs index 24226a9e..82ef1058 100644 --- a/daemon/src/config.rs +++ b/daemon/src/config.rs @@ -1,6 +1,6 @@ use log::{error, info, warn}; use rog_fan_curve::Curve; -use rog_types::{aura_modes::AuraModes, gfx_vendors::GfxVendors}; +use rog_types::gfx_vendors::GfxVendors; use serde_derive::{Deserialize, Serialize}; use std::collections::BTreeMap; use std::fs::{File, OpenOptions}; @@ -10,6 +10,7 @@ use crate::config_old::*; use crate::VERSION; pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf"; +pub static AURA_CONFIG_PATH: &str = "/etc/asusd/asusd.conf"; #[derive(Deserialize, Serialize)] pub struct Config { @@ -20,9 +21,6 @@ pub struct Config { #[serde(skip)] pub curr_fan_mode: u8, pub bat_charge_limit: u8, - pub kbd_led_brightness: u8, - pub kbd_backlight_mode: u8, - pub kbd_backlight_modes: Vec, pub power_profiles: BTreeMap, } @@ -40,9 +38,6 @@ impl Default for Config { toggle_profiles: vec!["normal".into(), "boost".into(), "silent".into()], curr_fan_mode: 0, bat_charge_limit: 100, - kbd_led_brightness: 1, - kbd_backlight_mode: 0, - kbd_backlight_modes: Vec::new(), power_profiles: pwr, } } @@ -50,7 +45,7 @@ impl Default for Config { impl Config { /// `load` will attempt to read the config, and panic if the dir is missing - pub fn load(supported_led_modes: &[u8]) -> Self { + pub fn load() -> Self { let mut file = OpenOptions::new() .read(true) .write(true) @@ -65,10 +60,15 @@ impl Config { let mut buf = String::new(); if let Ok(read_len) = file.read_to_string(&mut buf) { if read_len == 0 { - return Config::create_default(&mut file, &supported_led_modes); + return Config::create_default(&mut file); } else { if let Ok(data) = serde_json::from_str(&buf) { return data; + } else if let Ok(data) = serde_json::from_str::(&buf) { + let config = data.into_current(); + config.write(); + info!("Updated config version to: {}", VERSION); + return config; } else if let Ok(data) = serde_json::from_str::(&buf) { let config = data.into_current(); config.write(); @@ -89,17 +89,11 @@ impl Config { panic!("Please remove {} then restart asusd", CONFIG_PATH); } } - Config::create_default(&mut file, &supported_led_modes) + Config::create_default(&mut file) } - fn create_default(file: &mut File, supported_led_modes: &[u8]) -> Self { - // create a default config here - let mut config = Config::default(); - - for n in supported_led_modes { - config.kbd_backlight_modes.push(AuraModes::from(*n)) - } - + fn create_default(file: &mut File) -> Self { + let config = Config::default(); // Should be okay to unwrap this as is since it is a Default let json = serde_json::to_string_pretty(&config).unwrap(); file.write_all(json.as_bytes()) @@ -141,26 +135,6 @@ impl Config { file.write_all(json.as_bytes()) .unwrap_or_else(|err| error!("Could not write config: {}", err)); } - - pub fn set_mode_data(&mut self, mode: AuraModes) { - let byte: u8 = (&mode).into(); - for (index, n) in self.kbd_backlight_modes.iter().enumerate() { - if byte == u8::from(n) { - // Consume it, OMNOMNOMNOM - self.kbd_backlight_modes[index] = mode; - break; - } - } - } - - pub fn get_led_mode_data(&self, num: u8) -> Option<&AuraModes> { - for mode in &self.kbd_backlight_modes { - if u8::from(mode) == num { - return Some(mode); - } - } - None - } } #[derive(Deserialize, Serialize)] diff --git a/daemon/src/config_aura.rs b/daemon/src/config_aura.rs new file mode 100644 index 00000000..55b6696b --- /dev/null +++ b/daemon/src/config_aura.rs @@ -0,0 +1,124 @@ +use crate::laptops::LaptopLedData; +use log::{error, warn}; +use rog_types::aura_modes::{AuraEffect, AuraModeNum, AuraMultiZone, AuraZone}; +use serde_derive::{Deserialize, Serialize}; +use std::collections::BTreeMap; +use std::fs::{File, OpenOptions}; +use std::io::{Read, Write}; + +pub static AURA_CONFIG_PATH: &str = "/etc/asusd/aura.conf"; + +#[derive(Deserialize, Serialize)] +pub struct AuraConfig { + pub brightness: u8, + pub current_mode: AuraModeNum, + pub builtins: BTreeMap, + pub multizone: Option, +} + +impl Default for AuraConfig { + fn default() -> Self { + AuraConfig { + brightness: 1, + current_mode: AuraModeNum::Static, + builtins: BTreeMap::new(), + multizone: None, + } + } +} + +impl AuraConfig { + /// `load` will attempt to read the config, and panic if the dir is missing + pub fn load(supported_led_modes: &LaptopLedData) -> Self { + let mut file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(&AURA_CONFIG_PATH) + .unwrap_or_else(|_| { + panic!( + "The file {} or directory /etc/asusd/ is missing", + AURA_CONFIG_PATH + ) + }); // okay to cause panic here + let mut buf = String::new(); + if let Ok(read_len) = file.read_to_string(&mut buf) { + if read_len == 0 { + return AuraConfig::create_default(&mut file, &supported_led_modes); + } else { + if let Ok(data) = serde_json::from_str(&buf) { + return data; + } + warn!("Could not deserialise {}", AURA_CONFIG_PATH); + panic!("Please remove {} then restart asusd", AURA_CONFIG_PATH); + } + } + AuraConfig::create_default(&mut file, &supported_led_modes) + } + + fn create_default(file: &mut File, support_data: &LaptopLedData) -> Self { + // create a default config here + let mut config = AuraConfig::default(); + + for n in &support_data.standard { + config + .builtins + .insert(*n, AuraEffect::default_with_mode(*n)); + } + + // Should be okay to unwrap this as is since it is a Default + let json = serde_json::to_string(&config).unwrap(); + file.write_all(json.as_bytes()) + .unwrap_or_else(|_| panic!("Could not write {}", AURA_CONFIG_PATH)); + config + } + + pub fn read(&mut self) { + let mut file = OpenOptions::new() + .read(true) + .open(&AURA_CONFIG_PATH) + .unwrap_or_else(|err| panic!("Error reading {}: {}", AURA_CONFIG_PATH, err)); + let mut buf = String::new(); + if let Ok(l) = file.read_to_string(&mut buf) { + if l == 0 { + warn!("File is empty {}", AURA_CONFIG_PATH); + } else { + let x: AuraConfig = serde_json::from_str(&buf) + .unwrap_or_else(|_| panic!("Could not deserialise {}", AURA_CONFIG_PATH)); + *self = x; + } + } + } + + pub fn write(&self) { + let mut file = File::create(AURA_CONFIG_PATH).expect("Couldn't overwrite config"); + let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed"); + file.write_all(json.as_bytes()) + .unwrap_or_else(|err| error!("Could not write config: {}", err)); + } + + /// Multipurpose, will accecpt AuraEffect with zones and put in the correct store + pub fn set_builtin(&mut self, effect: AuraEffect) { + match effect.zone() { + AuraZone::None => { + self.builtins.insert(*effect.mode(), effect); + } + _ => { + if let Some(multi) = self.multizone.as_mut() { + multi.set(effect) + } + } + } + } + + pub fn get_multizone(&self, aura_type: AuraModeNum) -> Option<&[AuraEffect; 4]> { + if let Some(multi) = &self.multizone { + if aura_type == AuraModeNum::Static { + return Some(multi.static_()); + } else if aura_type == AuraModeNum::Breathe { + return Some(multi.breathe()); + } + } + None + } +} diff --git a/daemon/src/config_old.rs b/daemon/src/config_old.rs index 4ac38cfa..fdb8e264 100644 --- a/daemon/src/config_old.rs +++ b/daemon/src/config_old.rs @@ -1,10 +1,11 @@ -use rog_types::{aura_modes::AuraModes, gfx_vendors::GfxVendors}; +use rog_types::{aura_modes::AuraEffect, gfx_vendors::GfxVendors}; use serde_derive::{Deserialize, Serialize}; use std::collections::BTreeMap; use crate::config::{Config, Profile}; /// for parsing old v2.1.2 config +#[allow(dead_code)] #[derive(Deserialize)] pub(crate) struct ConfigV212 { gfx_managed: bool, @@ -15,7 +16,7 @@ pub(crate) struct ConfigV212 { power_profile: u8, kbd_led_brightness: u8, kbd_backlight_mode: u8, - kbd_backlight_modes: Vec, + kbd_backlight_modes: Vec, } impl ConfigV212 { @@ -27,15 +28,13 @@ impl ConfigV212 { toggle_profiles: self.toggle_profiles, curr_fan_mode: self.power_profile, bat_charge_limit: self.bat_charge_limit, - kbd_led_brightness: self.kbd_led_brightness, - kbd_backlight_mode: self.kbd_backlight_mode, - kbd_backlight_modes: self.kbd_backlight_modes, power_profiles: self.power_profiles, } } } /// for parsing old v2.2.2 config +#[allow(dead_code)] #[derive(Deserialize)] pub(crate) struct ConfigV222 { gfx_managed: bool, @@ -46,7 +45,7 @@ pub(crate) struct ConfigV222 { power_profile: u8, kbd_led_brightness: u8, kbd_backlight_mode: u8, - kbd_backlight_modes: Vec, + kbd_backlight_modes: Vec, } impl ConfigV222 { @@ -58,14 +57,12 @@ impl ConfigV222 { toggle_profiles: self.toggle_profiles, curr_fan_mode: self.power_profile, bat_charge_limit: self.bat_charge_limit, - kbd_led_brightness: self.kbd_led_brightness, - kbd_backlight_mode: self.kbd_backlight_mode, - kbd_backlight_modes: self.kbd_backlight_modes, power_profiles: self.power_profiles, } } } +/// for parsing old v3.0.1 config #[derive(Deserialize, Serialize)] pub(crate) struct ConfigV301 { pub gfx_managed: bool, @@ -78,7 +75,7 @@ pub(crate) struct ConfigV301 { pub bat_charge_limit: u8, pub kbd_led_brightness: u8, pub kbd_backlight_mode: u8, - pub kbd_backlight_modes: Vec, + pub kbd_backlight_modes: Vec, pub power_profiles: BTreeMap, } @@ -91,9 +88,37 @@ impl ConfigV301 { toggle_profiles: self.toggle_profiles, curr_fan_mode: self.curr_fan_mode, bat_charge_limit: self.bat_charge_limit, - kbd_led_brightness: self.kbd_led_brightness, - kbd_backlight_mode: self.kbd_backlight_mode, - kbd_backlight_modes: self.kbd_backlight_modes, + power_profiles: self.power_profiles, + } + } +} + +/// for parsing old v3.1.7 config +#[derive(Deserialize, Serialize)] +pub(crate) struct ConfigV317 { + pub gfx_mode: GfxVendors, + pub gfx_managed: bool, + pub active_profile: String, + pub toggle_profiles: Vec, + #[serde(skip)] + pub curr_fan_mode: u8, + pub bat_charge_limit: u8, + pub kbd_led_brightness: u8, + pub kbd_backlight_mode: u8, + #[serde(skip)] + pub kbd_backlight_modes: Option, + pub power_profiles: BTreeMap, +} + +impl ConfigV317 { + pub(crate) fn into_current(self) -> Config { + Config { + gfx_mode: GfxVendors::Hybrid, + gfx_managed: self.gfx_managed, + active_profile: self.active_profile, + toggle_profiles: self.toggle_profiles, + curr_fan_mode: self.curr_fan_mode, + bat_charge_limit: self.bat_charge_limit, power_profiles: self.power_profiles, } } diff --git a/daemon/src/ctrl_gfx/gfx.rs b/daemon/src/ctrl_gfx/gfx.rs index 22d3afb7..0046468f 100644 --- a/daemon/src/ctrl_gfx/gfx.rs +++ b/daemon/src/ctrl_gfx/gfx.rs @@ -43,7 +43,7 @@ trait Dbus { impl Dbus for CtrlGraphics { fn vendor(&self) -> String { self.get_gfx_mode() - .map(|gfx| gfx.into()) + .map(|gfx| (<&str>::from(gfx)).into()) .unwrap_or_else(|err| format!("Get vendor failed: {}", err)) } @@ -238,7 +238,7 @@ impl CtrlGraphics { Ok(()) } - fn write_modprobe_conf() -> Result<(), RogError> { + fn write_modprobe_conf(content: &[u8]) -> Result<(), RogError> { info!("GFX: Writing {}", MODPROBE_PATH); let mut file = std::fs::OpenOptions::new() @@ -248,7 +248,7 @@ impl CtrlGraphics { .open(MODPROBE_PATH) .map_err(|err| RogError::Path(MODPROBE_PATH.into(), err))?; - file.write_all(MODPROBE_BASE) + file.write_all(content) .and_then(|_| file.sync_all()) .map_err(|err| RogError::Write(MODPROBE_PATH.into(), err))?; @@ -393,7 +393,14 @@ impl CtrlGraphics { bus: &PciBus, ) -> Result<(), RogError> { Self::write_xorg_conf(vendor)?; - Self::write_modprobe_conf()?; // TODO: Not required here, should put in startup? + // Write different modprobe to enable boot control to work + match vendor { + GfxVendors::Nvidia | GfxVendors::Hybrid | GfxVendors::Compute => { + Self::write_modprobe_conf(MODPROBE_BASE)? + } + // GfxVendors::Compute => {} + GfxVendors::Integrated => Self::write_modprobe_conf(MODPROBE_INTEGRATED)?, + } // Rescan before doing remove or add drivers bus.rescan() @@ -408,8 +415,6 @@ impl CtrlGraphics { })?; } } - // TODO: compute mode, needs different setup - // GfxVendors::Compute => {} GfxVendors::Integrated => { for driver in NVIDIA_DRIVERS.iter() { Self::do_driver_action(driver, "rmmod")?; diff --git a/daemon/src/ctrl_gfx/mod.rs b/daemon/src/ctrl_gfx/mod.rs index 18d08f00..61699bf6 100644 --- a/daemon/src/ctrl_gfx/mod.rs +++ b/daemon/src/ctrl_gfx/mod.rs @@ -21,6 +21,15 @@ options nvidia NVreg_DynamicPowerManagement=0x02 options nvidia-drm modeset=1 "#; +static MODPROBE_INTEGRATED: &[u8] = br#"# Automatically generated by asusd +blacklist i2c_nvidia_gpu +blacklist nvidia +blacklist nvidia-drm +blacklist nvidia-modeset +blacklist nouveau +alias nouveau off +"#; + const XORG_FILE: &str = "90-nvidia-primary.conf"; const XORG_PATH: &str = "/etc/X11/xorg.conf.d/"; @@ -29,8 +38,7 @@ Section "OutputClass" Identifier "nvidia" MatchDriver "nvidia-drm" Driver "nvidia" - Option "AllowEmptyInitialConfiguration" - Option "AllowExternalGpus""#; + Option "AllowEmptyInitialConfiguration" "true""#; static PRIMARY_GPU_NVIDIA: &[u8] = br#" Option "PrimaryGPU" "true""#; diff --git a/daemon/src/ctrl_leds.rs b/daemon/src/ctrl_leds.rs index e0f67ba0..c5531974 100644 --- a/daemon/src/ctrl_leds.rs +++ b/daemon/src/ctrl_leds.rs @@ -5,15 +5,13 @@ static LED_SET: [u8; 17] = [0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 static KBD_BRIGHT_PATH: &str = "/sys/class/leds/asus::kbd_backlight/brightness"; use crate::{ - config::Config, + config_aura::AuraConfig, error::RogError, - laptops::{match_laptop, HELP_ADDRESS}, + laptops::{match_laptop, LaptopLedData, HELP_ADDRESS}, }; use log::{error, info, warn}; use rog_types::{ - aura_brightness_bytes, - aura_modes::{AuraModes, PER_KEY}, - fancy::KeyColourArray, + aura_modes::{AuraEffect, AuraModeNum}, LED_MSG_LEN, }; use std::fs::OpenOptions; @@ -29,7 +27,8 @@ use serde_derive::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] pub struct LedSupportedFunctions { pub brightness_set: bool, - pub stock_led_modes: Option>, + pub stock_led_modes: Option>, + pub multizone_led_mode: bool, pub per_key_led_mode: bool, } @@ -39,21 +38,20 @@ impl GetSupported for CtrlKbdBacklight { fn get_supported() -> Self::A { // let mode = <&str>::from(&::from(*mode)); let mut stock_led_modes = None; - let mut per_key_led_mode = false; + let multizone_led_mode = false; + let per_key_led_mode = false; if let Some(laptop) = match_laptop() { - let modes = laptop.supported_modes().to_vec(); - if modes.contains(&PER_KEY) { - per_key_led_mode = true; - let modes = modes.iter().filter(|x| **x != PER_KEY).copied().collect(); - stock_led_modes = Some(modes); + stock_led_modes = if laptop.supported_modes().standard.is_empty() { + None } else { - stock_led_modes = Some(modes); - } + Some(laptop.supported_modes().standard.clone()) + }; } LedSupportedFunctions { brightness_set: CtrlKbdBacklight::get_kbd_bright_path().is_ok(), stock_led_modes, + multizone_led_mode, per_key_led_mode, } } @@ -64,9 +62,9 @@ pub struct CtrlKbdBacklight { #[allow(dead_code)] kbd_node: Option, pub bright_node: String, - supported_modes: Vec, + supported_modes: LaptopLedData, flip_effect_write: bool, - config: Arc>, + config: AuraConfig, } pub struct DbusKbdBacklight { @@ -96,48 +94,42 @@ impl crate::ZbusAdd for DbusKbdBacklight { } } +/// The main interface for changing, reading, or notfying signals +/// +/// LED commands are split between Brightness, Modes, Per-Key #[dbus_interface(name = "org.asuslinux.Daemon")] impl DbusKbdBacklight { - fn set_led_mode(&mut self, data: String) { - if let Ok(data) = serde_json::from_str(&data) { - if let Ok(mut ctrl) = self.inner.try_lock() { - if let Ok(mut cfg) = ctrl.config.clone().try_lock() { - match &data { - AuraModes::PerKey(_) => { - ctrl.do_command(data, &mut cfg) - .unwrap_or_else(|err| warn!("{}", err)); - } - _ => { - if let Ok(json) = serde_json::to_string(&data) { - match ctrl.do_command(data, &mut cfg) { - Ok(_) => { - self.notify_led(&json).ok(); - } - Err(err) => { - warn!("{}", err); - } - } - } - } - } + fn set_brightness(&mut self, brightness: u8) { + if let Ok(ctrl) = self.inner.try_lock() { + ctrl.set_brightness(brightness) + .map_err(|err| warn!("{}", err)) + .ok(); + } + } + + fn set_led_mode(&mut self, effect: AuraEffect) { + if let Ok(mut ctrl) = self.inner.try_lock() { + let mode_name = effect.mode_name(); + match ctrl.do_command(effect) { + Ok(_) => { + self.notify_led(&mode_name).ok(); + } + Err(err) => { + warn!("{}", err); } } - } else { - warn!("SetKeyBacklight could not deserialise"); } } fn next_led_mode(&self) { if let Ok(mut ctrl) = self.inner.try_lock() { - if let Ok(mut cfg) = ctrl.config.clone().try_lock() { - ctrl.toggle_mode(false, &mut cfg) - .unwrap_or_else(|err| warn!("{}", err)); + ctrl.toggle_mode(false) + .unwrap_or_else(|err| warn!("{}", err)); - if let Some(mode) = cfg.get_led_mode_data(cfg.kbd_backlight_mode) { - if let Ok(json) = serde_json::to_string(&mode) { - self.notify_led(&json) - .unwrap_or_else(|err| warn!("{}", err)); - } + if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { + if let Ok(json) = serde_json::to_string(&mode) { + self.notify_led(&json) + .unwrap_or_else(|err| warn!("{}", err)); } } } @@ -145,40 +137,24 @@ impl DbusKbdBacklight { fn prev_led_mode(&self) { if let Ok(mut ctrl) = self.inner.try_lock() { - if let Ok(mut cfg) = ctrl.config.clone().try_lock() { - ctrl.toggle_mode(true, &mut cfg) - .unwrap_or_else(|err| warn!("{}", err)); + ctrl.toggle_mode(true) + .unwrap_or_else(|err| warn!("{}", err)); - if let Some(mode) = cfg.get_led_mode_data(cfg.kbd_backlight_mode) { - if let Ok(json) = serde_json::to_string(&mode) { - self.notify_led(&json) - .unwrap_or_else(|err| warn!("{}", err)); - } + if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { + if let Ok(json) = serde_json::to_string(&mode) { + self.notify_led(&json) + .unwrap_or_else(|err| warn!("{}", err)); } } } } /// Return the current mode data + #[dbus_interface(property)] fn led_mode(&self) -> String { if let Ok(ctrl) = self.inner.try_lock() { - if let Ok(cfg) = ctrl.config.clone().try_lock() { - if let Some(mode) = cfg.get_led_mode_data(cfg.kbd_backlight_mode) { - if let Ok(json) = serde_json::to_string(&mode) { - return json; - } - } - } - } - warn!("SetKeyBacklight could not deserialise"); - "SetKeyBacklight could not deserialise".to_string() - } - - /// Return a list of available modes - fn led_modes(&self) -> String { - if let Ok(ctrl) = self.inner.try_lock() { - if let Ok(cfg) = ctrl.config.clone().try_lock() { - if let Ok(json) = serde_json::to_string(&cfg.kbd_backlight_modes) { + if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { + if let Ok(json) = serde_json::to_string(&mode) { return json; } } @@ -187,14 +163,25 @@ impl DbusKbdBacklight { "SetKeyBacklight could not deserialise".to_string() } - /// Return the current LED brightness - fn led_brightness(&self) -> i8 { + /// Return a list of available modes + #[dbus_interface(property)] + fn led_modes(&self) -> String { if let Ok(ctrl) = self.inner.try_lock() { - if let Ok(cfg) = ctrl.config.clone().try_lock() { - return cfg.kbd_led_brightness as i8; + if let Ok(json) = serde_json::to_string(&ctrl.config.builtins) { + return json; } } warn!("SetKeyBacklight could not deserialise"); + "SetKeyBacklight could not serialise".to_string() + } + + /// Return the current LED brightness + #[dbus_interface(property)] + fn led_brightness(&self) -> i8 { + if let Ok(ctrl) = self.inner.try_lock() { + return ctrl.get_brightness().map(|n| n as i8).unwrap_or(-1); + } + warn!("SetKeyBacklight could not serialise"); -1 } @@ -205,44 +192,40 @@ impl DbusKbdBacklight { impl crate::Reloadable for CtrlKbdBacklight { fn reload(&mut self) -> Result<(), RogError> { // set current mode (if any) - if let Ok(mut config) = self.config.clone().try_lock() { - if self.supported_modes.len() > 1 { - if self.supported_modes.contains(&config.kbd_backlight_mode) { - let mode = config - .get_led_mode_data(config.kbd_backlight_mode) - .ok_or(RogError::NotSupported)? - .to_owned(); - self.write_mode(&mode)?; - info!("Reloaded last used mode"); - } else { - warn!( - "An unsupported mode was set: {}, reset to first mode available", - <&str>::from(&::from(config.kbd_backlight_mode)) - ); - for (idx, mode) in config.kbd_backlight_modes.iter_mut().enumerate() { - if !self.supported_modes.contains(&mode.into()) { - config.kbd_backlight_modes.remove(idx); - config.write(); - break; - } - } - config.kbd_backlight_mode = self.supported_modes[0]; - // TODO: do a recursive call with a boxed dyn future later - let mode = config - .get_led_mode_data(config.kbd_backlight_mode) - .ok_or(RogError::NotSupported)? - .to_owned(); - self.write_mode(&mode)?; - info!("Reloaded last used mode"); - } + if self.supported_modes.standard.len() > 1 { + let current_mode = self.config.current_mode; + if self.supported_modes.standard.contains(&(current_mode)) { + let mode = self + .config + .builtins + .get(¤t_mode) + .ok_or(RogError::NotSupported)? + .to_owned(); + self.write_mode(&mode)?; + info!("Reloaded last used mode"); + } else { + warn!( + "An unsupported mode was set: {}, reset to first mode available", + <&str>::from(&self.config.current_mode) + ); + self.config.builtins.remove(¤t_mode); + self.config.current_mode = AuraModeNum::Static; + // TODO: do a recursive call with a boxed dyn future later + let mode = self + .config + .builtins + .get(¤t_mode) + .ok_or(RogError::NotSupported)? + .to_owned(); + self.write_mode(&mode)?; + info!("Reloaded last used mode"); } - - // Reload brightness - let bright = config.kbd_led_brightness; - let bytes = aura_brightness_bytes(bright); - self.write_bytes(&bytes)?; - info!("Reloaded last used brightness"); } + + // Reload brightness + let bright = self.config.brightness; + self.set_brightness(bright)?; + info!("Reloaded last used brightness"); Ok(()) } } @@ -262,12 +245,10 @@ impl crate::CtrlTask for CtrlKbdBacklight { file.read_exact(&mut buf) .map_err(|err| RogError::Read("buffer".into(), err))?; if let Some(num) = char::from(buf[0]).to_digit(10) { - if let Ok(mut config) = self.config.clone().try_lock() { - if config.kbd_led_brightness != num as u8 { - config.read(); - config.kbd_led_brightness = num as u8; - config.write(); - } + if self.config.brightness != num as u8 { + self.config.read(); + self.config.brightness = num as u8; + self.config.write(); } return Ok(()); } @@ -280,8 +261,8 @@ impl CtrlKbdBacklight { pub fn new( id_product: &str, condev_iface: Option<&String>, - supported_modes: Vec, - config: Arc>, + supported_modes: LaptopLedData, + config: AuraConfig, ) -> Result { // TODO: return error if *all* nodes are None let led_node = Self::get_node_failover(id_product, None, Self::scan_led_node).map_or_else( @@ -334,6 +315,37 @@ impl CtrlKbdBacklight { } } + pub fn get_brightness(&self) -> Result { + let mut file = OpenOptions::new() + .read(true) + .open(&self.bright_node) + .map_err(|err| match err.kind() { + std::io::ErrorKind::NotFound => { + RogError::MissingLedBrightNode((&self.bright_node).into(), err) + } + _ => RogError::Path((&self.bright_node).into(), err), + })?; + let mut buf = [0u8; 1]; + file.read_exact(&mut buf) + .map_err(|err| RogError::Read("buffer".into(), err))?; + Ok(buf[0]) + } + + pub fn set_brightness(&self, brightness: u8) -> Result<(), RogError> { + let mut file = OpenOptions::new() + .write(true) + .open(&self.bright_node) + .map_err(|err| match err.kind() { + std::io::ErrorKind::NotFound => { + RogError::MissingLedBrightNode((&self.bright_node).into(), err) + } + _ => RogError::Path((&self.bright_node).into(), err), + })?; + file.write_all(&[brightness]) + .map_err(|err| RogError::Read("buffer".into(), err))?; + Ok(()) + } + fn get_node_failover( id_product: &str, iface: Option<&String>, @@ -432,8 +444,8 @@ impl CtrlKbdBacklight { )) } - pub fn do_command(&mut self, mode: AuraModes, config: &mut Config) -> Result<(), RogError> { - self.set_and_save(mode, config) + pub(crate) fn do_command(&mut self, mode: AuraEffect) -> Result<(), RogError> { + self.set_and_save(mode) } /// Should only be used if the bytes you are writing are verified correct @@ -470,100 +482,61 @@ impl CtrlKbdBacklight { /// /// This needs to be universal so that settings applied by dbus stick #[inline] - fn set_and_save(&mut self, mode: AuraModes, config: &mut Config) -> Result<(), RogError> { - match mode { - AuraModes::LedBrightness(n) => { - let bytes: [u8; LED_MSG_LEN] = (&mode).into(); - self.write_bytes(&bytes)?; - config.read(); - config.kbd_led_brightness = n; - config.write(); - info!("LED brightness set to {:#?}", n); - } - AuraModes::PerKey(v) => { - if v.is_empty() || v[0].is_empty() { - let bytes = KeyColourArray::get_init_msg(); - self.write_bytes(&bytes)?; - } else { - self.write_effect(&v)?; - } - } - _ => { - config.read(); - let mode_num: u8 = u8::from(&mode); - self.write_mode(&mode)?; - config.kbd_backlight_mode = mode_num; - config.set_mode_data(mode); - config.write(); - } - } + fn set_and_save(&mut self, mode: AuraEffect) -> Result<(), RogError> { + self.config.read(); + self.write_mode(&mode)?; + self.config.current_mode = *mode.mode(); + self.config.set_builtin(mode); + self.config.write(); Ok(()) } #[inline] - fn toggle_mode(&mut self, reverse: bool, config: &mut Config) -> Result<(), RogError> { - let current = config.kbd_backlight_mode; - if let Some(idx) = self.supported_modes.iter().position(|v| *v == current) { + fn toggle_mode(&mut self, reverse: bool) -> Result<(), RogError> { + let current = self.config.current_mode; + if let Some(idx) = self + .supported_modes + .standard + .iter() + .position(|v| *v == current) + { let mut idx = idx; // goes past end of array if reverse { if idx == 0 { - idx = self.supported_modes.len() - 1; + idx = self.supported_modes.standard.len() - 1; } else { idx -= 1; } } else { idx += 1; - if idx == self.supported_modes.len() { + if idx == self.supported_modes.standard.len() { idx = 0; } } - let next = self.supported_modes[idx]; + let next = self.supported_modes.standard[idx]; - config.read(); - if let Some(data) = config.get_led_mode_data(next) { + self.config.read(); + if let Some(data) = self.config.builtins.get(&next) { self.write_mode(&data)?; - config.kbd_backlight_mode = next; + self.config.current_mode = next; } - config.write(); + self.config.write(); } Ok(()) } #[inline] - fn write_mode(&mut self, mode: &AuraModes) -> Result<(), RogError> { - let mode_num: u8 = u8::from(mode); - if !self.supported_modes.contains(&mode_num) { + fn write_mode(&self, mode: &AuraEffect) -> Result<(), RogError> { + if !self.supported_modes.standard.contains(&mode.mode()) { return Err(RogError::NotSupported); } - match mode { - AuraModes::PerKey(v) => { - if v.is_empty() || v[0].is_empty() { - let bytes = KeyColourArray::get_init_msg(); - self.write_bytes(&bytes)?; - } else { - self.write_effect(v)?; - } - } - AuraModes::MultiStatic(_) | AuraModes::MultiBreathe(_) => { - let bytes: [[u8; LED_MSG_LEN]; 4] = mode.into(); - for array in bytes.iter() { - self.write_bytes(array)?; - } - self.write_bytes(&LED_SET)?; - // Changes won't persist unless apply is set - self.write_bytes(&LED_APPLY)?; - return Ok(()); - } - _ => { - let bytes: [u8; LED_MSG_LEN] = mode.into(); - self.write_bytes(&bytes)?; - self.write_bytes(&LED_SET)?; - // Changes won't persist unless apply is set - self.write_bytes(&LED_APPLY)?; - } - } + let bytes: [u8; LED_MSG_LEN] = mode.into(); + self.write_bytes(&bytes)?; + self.write_bytes(&LED_SET)?; + // Changes won't persist unless apply is set + self.write_bytes(&LED_APPLY)?; Ok(()) } } diff --git a/daemon/src/daemon.rs b/daemon/src/daemon.rs index a43c4623..10accc2d 100644 --- a/daemon/src/daemon.rs +++ b/daemon/src/daemon.rs @@ -1,10 +1,10 @@ -use daemon::ctrl_charge::CtrlCharge; use daemon::ctrl_fan_cpu::{CtrlFanAndCPU, DbusFanAndCpu}; use daemon::ctrl_leds::{CtrlKbdBacklight, DbusKbdBacklight}; use daemon::laptops::match_laptop; use daemon::{ config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported, }; +use daemon::{config_aura::AuraConfig, ctrl_charge::CtrlCharge}; use daemon::{ctrl_anime::CtrlAnimeDisplay, ctrl_gfx::gfx::CtrlGraphics}; use daemon::{CtrlTask, Reloadable, ZbusAdd}; @@ -50,12 +50,9 @@ fn start_daemon() -> Result<(), Box> { print_board_info(); println!("{}", serde_json::to_string_pretty(&supported).unwrap()); - let laptop = match_laptop(); - let config = if let Some(laptop) = laptop.as_ref() { - Config::load(laptop.supported_modes()) - } else { - Config::load(&[]) - }; + let config = Config::load(); + let enable_gfx_switching = config.gfx_managed; + let config = Arc::new(Mutex::new(config)); let connection = Connection::new_system()?; fdo::DBusProxy::new(&connection)? @@ -64,9 +61,6 @@ fn start_daemon() -> Result<(), Box> { supported.add_to_server(&mut object_server); - let enable_gfx_switching = config.gfx_managed; - let config = Arc::new(Mutex::new(config)); - match CtrlRogBios::new(config.clone()) { Ok(mut ctrl) => { // Do a reload of any settings @@ -133,7 +127,7 @@ fn start_daemon() -> Result<(), Box> { // Collect tasks for task thread let mut tasks: Vec>> = Vec::new(); - if let Ok(mut ctrl) = CtrlFanAndCPU::new(config.clone()).map_err(|err| { + if let Ok(mut ctrl) = CtrlFanAndCPU::new(config).map_err(|err| { error!("Profile control: {}", err); }) { ctrl.reload() @@ -142,12 +136,14 @@ fn start_daemon() -> Result<(), Box> { DbusFanAndCpu::new(tmp).add_to_server(&mut object_server); }; - if let Some(laptop) = laptop { + if let Some(laptop) = match_laptop() { + let aura_config = AuraConfig::load(laptop.supported_modes()); + if let Ok(ctrl) = CtrlKbdBacklight::new( laptop.usb_product(), laptop.condev_iface(), laptop.supported_modes().to_owned(), - config, + aura_config, ) .map_err(|err| { error!("Keyboard control: {}", err); diff --git a/daemon/src/laptops.rs b/daemon/src/laptops.rs index 9b8b92f4..366f4d0a 100644 --- a/daemon/src/laptops.rs +++ b/daemon/src/laptops.rs @@ -1,20 +1,19 @@ use log::{info, warn}; -use rog_types::aura_modes::{AuraModes, BREATHING, STATIC}; +use rog_types::aura_modes::AuraModeNum; use serde_derive::{Deserialize, Serialize}; use std::fs::OpenOptions; use std::io::Read; pub static LEDMODE_CONFIG_PATH: &str = "/etc/asusd/asusd-ledmodes.toml"; - pub static HELP_ADDRESS: &str = "https://gitlab.com/asus-linux/asus-nb-ctrl"; - static LAPTOP_DEVICES: [u16; 4] = [0x1866, 0x1869, 0x1854, 0x19b6]; +/// A helper of sorts specifically for functions tied to laptop models #[derive(Debug)] pub struct LaptopBase { usb_product: String, condev_iface: Option, // required for finding the Consumer Device interface - supported_modes: Vec, + led_support: LaptopLedData, } impl LaptopBase { @@ -24,8 +23,8 @@ impl LaptopBase { pub fn condev_iface(&self) -> Option<&String> { self.condev_iface.as_ref() } - pub fn supported_modes(&self) -> &[u8] { - &self.supported_modes + pub fn supported_modes(&self) -> &LaptopLedData { + &self.led_support } } @@ -38,10 +37,7 @@ pub fn match_laptop() -> Option { let prod_str = format!("{:x?}", device_desc.product_id()); if device_desc.product_id() == 0x1854 { - let mut laptop = laptop(prod_str, None); - if laptop.supported_modes.is_empty() { - laptop.supported_modes = vec![STATIC, BREATHING]; - } + let laptop = laptop(prod_str, None); return Some(laptop); } @@ -65,12 +61,18 @@ fn laptop(prod: String, condev_iface: Option) -> LaptopBase { let mut laptop = LaptopBase { usb_product: prod, condev_iface, - supported_modes: vec![], + led_support: LaptopLedData { + board_names: vec![], + prod_family: String::new(), + standard: vec![], + multizone: false, + per_key: false, + }, }; - if let Some(modes) = LEDModeGroup::load_from_config() { + if let Some(modes) = LedSupportFile::load_from_config() { if let Some(led_modes) = modes.matcher(&prod_family, &board_name) { - laptop.supported_modes = led_modes; + laptop.led_support = led_modes; return laptop; } } @@ -92,7 +94,7 @@ pub fn print_modes(supported_modes: &[u8]) { if !supported_modes.is_empty() { info!("Supported Keyboard LED modes are:"); for mode in supported_modes { - let mode = <&str>::from(&::from(*mode)); + let mode = <&str>::from(&::from(*mode)); info!("- {}", mode); } info!( @@ -105,19 +107,28 @@ pub fn print_modes(supported_modes: &[u8]) { } #[derive(Debug, Deserialize, Serialize)] -struct LEDModeGroup { - led_modes: Vec, +struct LedSupportFile { + led_data: Vec, } -impl LEDModeGroup { +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct LaptopLedData { + pub prod_family: String, + pub board_names: Vec, + pub standard: Vec, + pub multizone: bool, + pub per_key: bool, +} + +impl LedSupportFile { /// Consumes the LEDModes - fn matcher(self, prod_family: &str, board_name: &str) -> Option> { - for led_modes in self.led_modes { - if prod_family.contains(&led_modes.prod_family) { - for board in led_modes.board_names { - if board_name.contains(&board) { - info!("Matched to {} {}", led_modes.prod_family, board); - return Some(led_modes.led_modes); + fn matcher(self, prod_family: &str, board_name: &str) -> Option { + for config in self.led_data { + if prod_family.contains(&config.prod_family) { + for board in &config.board_names { + if board_name.contains(board) { + info!("Matched to {} {}", config.prod_family, board); + return Some(config); } } } @@ -142,10 +153,3 @@ impl LEDModeGroup { None } } - -#[derive(Debug, Deserialize, Serialize)] -struct LEDModes { - prod_family: String, - board_names: Vec, - led_modes: Vec, -} diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index aa59cd58..7f646a44 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -1,6 +1,7 @@ #![deny(unused_must_use)] /// Configuration loading, saving pub mod config; +pub mod config_aura; pub(crate) mod config_old; /// Control of AniMe matrix display pub mod ctrl_anime; diff --git a/data/asusd-ledmodes.toml b/data/asusd-ledmodes.toml index e68caf74..741250f9 100644 --- a/data/asusd-ledmodes.toml +++ b/data/asusd-ledmodes.toml @@ -1,59 +1,83 @@ -[[led_modes]] +[[led_data]] prod_family = "Zephyrus S" board_names = ["GX502", "GX701", "G531", "GL531", "G532"] -led_modes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 255] +standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"] +multizone = false +per_key = true -[[led_modes]] +[[led_data]] prod_family = "Zephyrus M" board_names = ["GU502GV"] -led_modes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 255] +standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"] +multizone = false +per_key = true -[[led_modes]] +[[led_data]] prod_family = "ROG Zephyrus M15" board_names = ["GU502LW"] -led_modes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 255] +standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"] +multizone = false +per_key = true -[[led_modes]] +[[led_data]] prod_family = "ROG Zephyrus M15" board_names = ["GU502LU"] -led_modes = [0, 1, 2, 10] +standard = ["Static", "Breathe", "Strobe", "Pulse"] +multizone = false +per_key = false -[[led_modes]] +[[led_data]] prod_family = "Zephyrus" board_names = ["GM501GM", "GX531"] -led_modes = [0, 1, 2, 3, 10, 13] +standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"] +multizone = true +per_key = false -[[led_modes]] +[[led_data]] prod_family = "ROG Strix" board_names = ["G531GW"] -led_modes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 255] +standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"] +multizone = false +per_key = true -[[led_modes]] +[[led_data]] prod_family = "ROG Strix" board_names = ["GX531", "G512LV", "G712LV"] -led_modes = [0, 1, 2, 3, 10, 13, 14] +standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"] +multizone = true +per_key = false -[[led_modes]] +[[led_data]] prod_family = "ROG Strix" board_names = ["G512LI", "G712LI", "G531GD"] -led_modes = [0, 1, 2, 3, 10] +standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"] +multizone = false +per_key = false -[[led_modes]] +[[led_data]] prod_family = "Strix" board_names = ["G731GV", "G731GW", "G531GV"] -led_modes = [0, 1, 2, 3, 13, 14] +standard = ["Static", "Breathe", "Strobe", "Rainbow"] +multizone = true +per_key = false -[[led_modes]] +[[led_data]] prod_family = "Strix" board_names = ["G731GT", "G731GU", "G531GT", "G531GU"] -led_modes = [0, 1, 2, 3] +standard = ["Static", "Breathe", "Strobe", "Rainbow"] +multizone = false +per_key = false -[[led_modes]] +[[led_data]] prod_family = "Strix Scar" board_names = ["G531", "G731"] -led_modes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 255] +standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"] +multizone = true +per_key = true -[[led_modes]] +[[led_data]] prod_family = "ROG" board_names = ["GL553VE"] -led_modes = [0, 1, 2, 13, 14] \ No newline at end of file +standard = ["Static", "Breathe", "Strobe"] +multizone = true +per_key = false \ No newline at end of file diff --git a/rog-dbus/src/lib.rs b/rog-dbus/src/lib.rs index 13f2fe66..547d655d 100644 --- a/rog-dbus/src/lib.rs +++ b/rog-dbus/src/lib.rs @@ -11,7 +11,7 @@ pub mod zbus_profile; pub mod zbus_rogbios; pub mod zbus_supported; -use rog_types::aura_modes::AuraModes; +use rog_types::aura_modes::AuraEffect; use std::sync::{Arc, Mutex}; use zbus::{Connection, Result, SignalReceiver}; @@ -89,7 +89,7 @@ pub struct Signals { pub gfx_vendor: Arc>>, pub gfx_action: Arc>>, pub profile: Arc>>, - pub led_mode: Arc>>, + pub led_mode: Arc>>, pub charge: Arc>>, } diff --git a/rog-dbus/src/zbus_led.rs b/rog-dbus/src/zbus_led.rs index 61ae47c0..f9161768 100644 --- a/rog-dbus/src/zbus_led.rs +++ b/rog-dbus/src/zbus_led.rs @@ -23,7 +23,7 @@ use std::sync::{Arc, Mutex}; use zbus::{dbus_proxy, Connection, Result}; -use rog_types::{aura_modes::AuraModes, cli_options::LedBrightness, fancy::KeyColourArray}; +use rog_types::{aura_modes::AuraEffect, aura_perkey::KeyColourArray}; const BLOCKING_TIME: u64 = 40; // 100ms = 10 FPS, max 50ms = 20 FPS, 40ms = 25 FPS @@ -32,27 +32,34 @@ const BLOCKING_TIME: u64 = 40; // 100ms = 10 FPS, max 50ms = 20 FPS, 40ms = 25 F default_path = "/org/asuslinux/Led" )] trait Daemon { - /// LedBrightness method - fn led_brightness(&self) -> zbus::Result; - - /// LedMode method - fn led_mode(&self) -> zbus::Result; - - /// LedModes method - fn led_modes(&self) -> zbus::Result; - /// NextLedMode method fn next_led_mode(&self) -> zbus::Result<()>; /// PrevLedMode method fn prev_led_mode(&self) -> zbus::Result<()>; + /// SetBrightness method + fn set_brightness(&self, brightness: u8) -> zbus::Result<()>; + /// SetLedMode method - fn set_led_mode(&self, data: &str) -> zbus::Result<()>; + fn set_led_mode(&self, effect: &AuraEffect) -> zbus::Result<()>; /// NotifyLed signal + /// NotifyLed signal #[dbus_proxy(signal)] fn notify_led(&self, data: &str) -> zbus::Result<()>; + + /// LedBrightness property + #[dbus_proxy(property)] + fn led_brightness(&self) -> zbus::Result; + + /// LedMode property + #[dbus_proxy(property)] + fn led_mode(&self) -> zbus::Result; + + /// LedModes property + #[dbus_proxy(property)] + fn led_modes(&self) -> zbus::Result; } pub struct LedProxy<'a>(DaemonProxy<'a>); @@ -68,16 +75,13 @@ impl<'a> LedProxy<'a> { } #[inline] - pub fn get_led_brightness(&self) -> Result { - match self.0.led_brightness()? { - -1 => Ok(LedBrightness::new(None)), - level => Ok(LedBrightness::new(Some(level as u8))), - } + pub fn get_led_brightness(&self) -> Result { + self.0.led_brightness() } #[inline] - pub fn set_brightness(&self, level: u8) -> Result<()> { - self.set_led_mode(&AuraModes::LedBrightness(level))?; + pub fn set_led_brightness(&self, level: u8) -> Result<()> { + self.0.set_brightness(level)?; Ok(()) } @@ -92,8 +96,8 @@ impl<'a> LedProxy<'a> { } #[inline] - pub fn set_led_mode(&self, mode: &AuraModes) -> Result<()> { - self.0.set_led_mode(&serde_json::to_string(mode).unwrap()) + pub fn set_led_mode(&self, mode: &AuraEffect) -> Result<()> { + self.0.set_led_mode(mode) } /// Write a single colour block. @@ -107,9 +111,8 @@ impl<'a> LedProxy<'a> { for v in group { vecs.push(v.to_vec()); } - let mode = AuraModes::PerKey(vecs); - - self.set_led_mode(&mode)?; + // TODO: let mode = AuraModes::PerKey(vecs); + // self.set_led_mode(&mode)?; std::thread::sleep(std::time::Duration::from_millis(BLOCKING_TIME)); @@ -124,12 +127,13 @@ impl<'a> LedProxy<'a> { /// the keyboard LED EC in the correct mode #[inline] pub fn init_effect(&self) -> Result<()> { - let mode = AuraModes::PerKey(vec![vec![]]); - self.0.set_led_mode(&serde_json::to_string(&mode).unwrap()) + // TODO: let mode = AuraModes::PerKey(vec![vec![]]); + // self.0.set_led_mode(&serde_json::to_string(&mode).unwrap()) + Ok(()) } #[inline] - pub fn connect_notify_led(&self, led: Arc>>) -> zbus::fdo::Result<()> { + pub fn connect_notify_led(&self, led: Arc>>) -> zbus::fdo::Result<()> { self.0.connect_notify_led(move |data| { if let Ok(mut lock) = led.lock() { if let Ok(dat) = serde_json::from_str(&data) { diff --git a/rog-types/Cargo.toml b/rog-types/Cargo.toml index 87106aeb..531823fa 100644 --- a/rog-types/Cargo.toml +++ b/rog-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rog_types" -version = "3.0.0" +version = "3.1.0" license = "MPL-2.0" readme = "README.md" authors = ["Luke "] diff --git a/rog-types/src/aura_modes.rs b/rog-types/src/aura_modes.rs index fe1b5486..cf8b1e7e 100644 --- a/rog-types/src/aura_modes.rs +++ b/rog-types/src/aura_modes.rs @@ -1,31 +1,21 @@ -use crate::cli_options::SetAuraBuiltin; +// static LED_INIT1: [u8; 2] = [0x5d, 0xb9]; +// static LED_INIT2: &str = "]ASUS Tech.Inc."; // ] == 0x5d +// static LED_INIT3: [u8; 6] = [0x5d, 0x05, 0x20, 0x31, 0, 0x08]; +// static LED_INIT4: &str = "^ASUS Tech.Inc."; // ^ == 0x5e +// static LED_INIT5: [u8; 6] = [0x5e, 0x05, 0x20, 0x31, 0, 0x08]; + +use crate::LED_MSG_LEN; use crate::error::AuraError; -use gumdrop::Options; use serde_derive::{Deserialize, Serialize}; use std::str::FromStr; +use zvariant_derive::Type; -pub const STATIC: u8 = 0x00; -pub const BREATHING: u8 = 0x01; -pub const STROBE: u8 = 0x02; -pub const RAINBOW: u8 = 0x03; -pub const STAR: u8 = 0x04; -pub const RAIN: u8 = 0x05; -pub const HIGHLIGHT: u8 = 0x06; -pub const LASER: u8 = 0x07; -pub const RIPPLE: u8 = 0x08; -pub const PULSE: u8 = 0x0a; -pub const COMET: u8 = 0x0b; -pub const FLASH: u8 = 0x0c; -pub const MULTISTATIC: u8 = 0x0d; -pub const MULTIBREATHE: u8 = 0x0e; -pub const PER_KEY: u8 = 0xff; - -#[derive(Debug, Clone, Deserialize, Serialize)] +#[derive(Debug, Clone, PartialEq, Copy, Deserialize, Serialize, Type)] pub struct Colour(pub u8, pub u8, pub u8); impl Default for Colour { fn default() -> Self { - Colour(128, 0, 0) + Colour(166, 0, 0) } } @@ -43,7 +33,7 @@ impl FromStr for Colour { } } -#[derive(Debug, Copy, Clone, Deserialize, Serialize)] +#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize, Type)] pub enum Speed { Low = 0xe1, Med = 0xeb, @@ -71,7 +61,7 @@ impl FromStr for Speed { /// Used for Rainbow mode. /// /// Enum corresponds to the required integer value -#[derive(Debug, Copy, Clone, Deserialize, Serialize)] +#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize, Type)] pub enum Direction { Right, Left, @@ -98,219 +88,265 @@ impl FromStr for Direction { } } -#[derive(Debug, Clone, Options, Default, Deserialize, Serialize)] -pub struct SingleSpeed { - #[serde(skip)] - #[options(help = "print help message")] - help: bool, - #[options(no_long, meta = "WORD", help = "set the speed: low, med, high")] - pub speed: Speed, +/// Writes out the correct byte string for brightness +pub const fn aura_brightness_bytes(brightness: u8) -> [u8; 17] { + [ + 0x5A, 0xBA, 0xC5, 0xC4, brightness, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ] } -#[derive(Debug, Clone, Options, Default, Deserialize, Serialize)] -pub struct SingleSpeedDirection { - #[serde(skip)] - #[options(help = "print help message")] - help: bool, - #[options(no_long, meta = "", help = "set the direction: up, down, left, right")] + +#[derive( + Debug, Type, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Deserialize, Serialize, +)] +pub enum AuraModeNum { + Static = 0, + Breathe = 1, + Strobe = 2, + Rainbow = 3, + Star = 4, + Rain = 5, + Highlight = 6, + Laser = 7, + Ripple = 8, + Pulse = 10, + Comet = 11, + Flash = 12, +} + +impl From<&AuraModeNum> for &str { + fn from(mode: &AuraModeNum) -> Self { + match mode { + AuraModeNum::Static => "Static", + AuraModeNum::Breathe => "Breathing", + AuraModeNum::Strobe => "Strobing", + AuraModeNum::Rainbow => "Rainbow", + AuraModeNum::Star => "Stars", + AuraModeNum::Rain => "Rain", + AuraModeNum::Highlight => "Keypress Highlight", + AuraModeNum::Laser => "Keypress Laser", + AuraModeNum::Ripple => "Keypress Ripple", + AuraModeNum::Pulse => "Pulse", + AuraModeNum::Comet => "Comet", + AuraModeNum::Flash => "Flash", + } + } +} +impl From<&str> for AuraModeNum { + fn from(mode: &str) -> Self { + match mode { + "Static" => AuraModeNum::Static, + "Breathing" => AuraModeNum::Breathe, + "Strobing" => AuraModeNum::Strobe, + "Rainbow" => AuraModeNum::Rainbow, + "Stars" => AuraModeNum::Star, + "Rain" => AuraModeNum::Rain, + "Keypress Highlight" => AuraModeNum::Highlight, + "Keypress Laser" => AuraModeNum::Laser, + "Keypress Ripple" => AuraModeNum::Ripple, + "Pulse" => AuraModeNum::Pulse, + "Comet" => AuraModeNum::Comet, + "Flash" => AuraModeNum::Flash, + _ => AuraModeNum::Static, + } + } +} + +impl From for AuraModeNum { + fn from(mode: u8) -> Self { + match mode { + 0 => AuraModeNum::Static, + 1 => AuraModeNum::Breathe, + 2 => AuraModeNum::Strobe, + 3 => AuraModeNum::Rainbow, + 4 => AuraModeNum::Star, + 5 => AuraModeNum::Rain, + 6 => AuraModeNum::Highlight, + 7 => AuraModeNum::Laser, + 8 => AuraModeNum::Ripple, + 10 => AuraModeNum::Pulse, + 11 => AuraModeNum::Comet, + 12 => AuraModeNum::Flash, + _ => AuraModeNum::Static, + } + } +} + +#[derive(Deserialize, Serialize)] +pub struct AuraMultiZone { + static_: [AuraEffect; 4], + breathe: [AuraEffect; 4], +} + +impl AuraMultiZone { + pub fn set(&mut self, effect: AuraEffect) { + if effect.mode == AuraModeNum::Static { + match effect.zone { + AuraZone::None => {} + AuraZone::One => self.static_[0] = effect, + AuraZone::Two => self.static_[1] = effect, + AuraZone::Three => self.static_[2] = effect, + AuraZone::Four => self.static_[3] = effect, + } + } else if effect.mode == AuraModeNum::Breathe { + match effect.zone { + AuraZone::None => {} + AuraZone::One => self.breathe[0] = effect, + AuraZone::Two => self.breathe[1] = effect, + AuraZone::Three => self.breathe[2] = effect, + AuraZone::Four => self.breathe[3] = effect, + } + } + } + + pub fn static_(&self) -> &[AuraEffect; 4] { + &self.static_ + } + + pub fn breathe(&self) -> &[AuraEffect; 4] { + &self.breathe + } +} + +impl Default for AuraMultiZone { + fn default() -> Self { + Self { + static_: [ + AuraEffect { + mode: AuraModeNum::Static, + zone: AuraZone::One, + ..Default::default() + }, + AuraEffect { + mode: AuraModeNum::Static, + zone: AuraZone::Two, + ..Default::default() + }, + AuraEffect { + mode: AuraModeNum::Static, + zone: AuraZone::Three, + ..Default::default() + }, + AuraEffect { + mode: AuraModeNum::Static, + zone: AuraZone::Four, + ..Default::default() + }, + ], + breathe: [ + AuraEffect { + mode: AuraModeNum::Breathe, + zone: AuraZone::One, + ..Default::default() + }, + AuraEffect { + mode: AuraModeNum::Breathe, + zone: AuraZone::Two, + ..Default::default() + }, + AuraEffect { + mode: AuraModeNum::Breathe, + zone: AuraZone::Three, + ..Default::default() + }, + AuraEffect { + mode: AuraModeNum::Breathe, + zone: AuraZone::Four, + ..Default::default() + }, + ], + } + } +} + +/// Base effects have no zoning, while multizone is 1-4 +#[derive(Debug, Type, Copy, Clone, PartialEq, Deserialize, Serialize)] +pub enum AuraZone { + None, + One, + Two, + Three, + Four, +} + +/// Default factory modes structure +#[derive(Debug, Type, Clone, Deserialize, Serialize)] +pub struct AuraEffect { + /// The effect type + pub mode: AuraModeNum, + /// `AuraZone::None` for no zone or zoneless keyboards + pub zone: AuraZone, + /// Primary colour for all modes + pub colour1: Colour, + /// Secondary colour in some modes like Breathing or Stars + pub colour2: Colour, + /// One of three speeds for modes that support speed (most that animate) + pub speed: Speed, + /// Up, down, left, right. Only Rainbow mode seems to use this pub direction: Direction, - #[options(no_long, meta = "", help = "set the speed: low, med, high")] - pub speed: Speed, } -#[derive(Debug, Clone, Default, Options, Deserialize, Serialize)] -pub struct SingleColour { - #[serde(skip)] - #[options(help = "print help message")] - help: bool, - #[options(no_long, meta = "", help = "set the RGB value e.g, ff00ff")] - pub colour: Colour, -} +impl AuraEffect { + pub fn mode(&self) -> &AuraModeNum { + &self.mode + } -#[derive(Debug, Clone, Default, Options, Deserialize, Serialize)] -pub struct SingleColourSpeed { - #[serde(skip)] - #[options(help = "print help message")] - help: bool, - #[options(no_long, meta = "", help = "set the RGB value e.g, ff00ff")] - pub colour: Colour, - #[options(no_long, meta = "", help = "set the speed: low, med, high")] - pub speed: Speed, -} + pub fn mode_name(&self) -> String { + (<&str>::from(&self.mode)).to_string() + } -#[derive(Debug, Clone, Options, Default, Deserialize, Serialize)] -pub struct TwoColourSpeed { - #[serde(skip)] - #[options(help = "print help message")] - help: bool, - #[options(no_long, meta = "", help = "set the first RGB value e.g, ff00ff")] - pub colour: Colour, - #[options(no_long, meta = "", help = "set the second RGB value e.g, ff00ff")] - pub colour2: Colour, - #[options(no_long, meta = "", help = "set the speed: low, med, high")] - pub speed: Speed, -} + pub fn mode_num(&self) -> u8 { + self.mode as u8 + } -#[derive(Debug, Clone, Default, Options, Deserialize, Serialize)] -pub struct MultiColour { - #[serde(skip)] - #[options(help = "print help message")] - help: bool, - #[options(short = "a", meta = "", help = "set the RGB value e.g, ff00ff")] - pub colour1: Colour, - #[options(short = "b", meta = "", help = "set the RGB value e.g, ff00ff")] - pub colour2: Colour, - #[options(short = "c", meta = "", help = "set the RGB value e.g, ff00ff")] - pub colour3: Colour, - #[options(short = "d", meta = "", help = "set the RGB value e.g, ff00ff")] - pub colour4: Colour, -} + pub fn default_with_mode(mode: AuraModeNum) -> Self { + Self { + mode, + ..Default::default() + } + } -#[derive(Debug, Clone, Default, Options, Deserialize, Serialize)] -pub struct MultiColourSpeed { - #[options(help = "print help message")] - help: bool, - #[options(short = "a", meta = "", help = "set the RGB value e.g, ff00ff")] - pub colour1: Colour, - #[options(short = "b", meta = "", help = "set the RGB value e.g, ff00ff")] - pub colour2: Colour, - #[options(short = "c", meta = "", help = "set the RGB value e.g, ff00ff")] - pub colour3: Colour, - #[options(short = "d", meta = "", help = "set the RGB value e.g, ff00ff")] - pub colour4: Colour, - #[options(no_long, meta = "", help = "set the speed: low, med, high")] - pub speed: Speed, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub enum AuraModes { - Static(SingleColour), - Breathe(TwoColourSpeed), - Strobe(SingleSpeed), - Rainbow(SingleSpeedDirection), - Star(TwoColourSpeed), - Rain(SingleSpeed), - Highlight(SingleColourSpeed), - Laser(SingleColourSpeed), - Ripple(SingleColourSpeed), - Pulse(SingleColour), - Comet(SingleColour), - Flash(SingleColour), - MultiStatic(MultiColour), - MultiBreathe(MultiColourSpeed), - LedBrightness(u8), - // TODO: use a serializable structure for this (KeyColourArray) - PerKey(Vec>), -} - -impl From for AuraModes { - fn from(mode: SetAuraBuiltin) -> Self { - (&mode).into() + pub fn zone(&self) -> AuraZone { + self.zone } } -impl From<&SetAuraBuiltin> for AuraModes { - fn from(mode: &SetAuraBuiltin) -> Self { - match mode { - SetAuraBuiltin::Static(x) => AuraModes::Static(x.clone()), - SetAuraBuiltin::Breathe(x) => AuraModes::Breathe(x.clone()), - SetAuraBuiltin::Strobe(x) => AuraModes::Strobe(x.clone()), - SetAuraBuiltin::Rainbow(x) => AuraModes::Rainbow(x.clone()), - SetAuraBuiltin::Star(x) => AuraModes::Star(x.clone()), - SetAuraBuiltin::Rain(x) => AuraModes::Rain(x.clone()), - SetAuraBuiltin::Highlight(x) => AuraModes::Highlight(x.clone()), - SetAuraBuiltin::Laser(x) => AuraModes::Laser(x.clone()), - SetAuraBuiltin::Ripple(x) => AuraModes::Ripple(x.clone()), - SetAuraBuiltin::Pulse(x) => AuraModes::Pulse(x.clone()), - SetAuraBuiltin::Comet(x) => AuraModes::Comet(x.clone()), - SetAuraBuiltin::Flash(x) => AuraModes::Flash(x.clone()), - SetAuraBuiltin::MultiStatic(x) => AuraModes::MultiStatic(x.clone()), - SetAuraBuiltin::MultiBreathe(x) => AuraModes::MultiBreathe(x.clone()), +impl Default for AuraEffect { + fn default() -> Self { + Self { + mode: AuraModeNum::Static, + zone: AuraZone::None, + colour1: Colour(166, 0, 0), + colour2: Colour(0, 0, 0), + speed: Speed::Med, + direction: Direction::Right, } } } -/// Very specific mode conversion required because numbering isn't linear -impl From for u8 { - fn from(mode: AuraModes) -> Self { - u8::from(&mode) - } -} +/// Parses `AuraEffect` in to packet data for writing to the USB interface +/// +/// Byte structure: +/// ```ignore +/// | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12| +/// |---|---|---|---|---|---|---|---|---|---|---|---|---| +/// |5d |b3 |00 |03 |ff |00 |00 |00 |00 |00 |00 |ff |00 | +/// ``` +impl From<&AuraEffect> for [u8; LED_MSG_LEN] { + fn from(aura: &AuraEffect) -> Self { + let mut msg = [0u8; LED_MSG_LEN]; + msg[0] = 0x5d; + msg[1] = 0xb3; + msg[2] = aura.zone as u8; + msg[3] = aura.mode as u8; + msg[4] = aura.colour1.0; + msg[5] = aura.colour1.1; + msg[6] = aura.colour1.2; + msg[7] = aura.speed as u8; + msg[8] = aura.direction as u8; + msg[10] = aura.colour2.0; + msg[11] = aura.colour2.1; + msg[12] = aura.colour2.2; -/// Very specific mode conversion required because numbering isn't linear -impl From<&mut AuraModes> for u8 { - fn from(mode: &mut AuraModes) -> Self { - u8::from(&*mode) - } -} - -/// Very specific mode conversion required because numbering isn't linear -impl From<&AuraModes> for u8 { - fn from(mode: &AuraModes) -> Self { - match mode { - AuraModes::Static(_) => STATIC, - AuraModes::Breathe(_) => BREATHING, - AuraModes::Strobe(_) => STROBE, - AuraModes::Rainbow(_) => RAINBOW, - AuraModes::Star(_) => STAR, - AuraModes::Rain(_) => RAIN, - AuraModes::Highlight(_) => HIGHLIGHT, - AuraModes::Laser(_) => LASER, - AuraModes::Ripple(_) => RIPPLE, - AuraModes::Pulse(_) => PULSE, - AuraModes::Comet(_) => COMET, - AuraModes::Flash(_) => FLASH, - AuraModes::MultiStatic(_) => MULTISTATIC, - AuraModes::MultiBreathe(_) => MULTIBREATHE, - AuraModes::PerKey(_) => PER_KEY, - _ => panic!("Invalid mode"), - } - } -} - -impl From<&AuraModes> for &str { - fn from(mode: &AuraModes) -> Self { - match mode { - AuraModes::Static(_) => "Static", - AuraModes::Breathe(_) => "Breathing", - AuraModes::Strobe(_) => "Strobing", - AuraModes::Rainbow(_) => "Rainbow", - AuraModes::Star(_) => "Stars", - AuraModes::Rain(_) => "Rain", - AuraModes::Highlight(_) => "Keypress Highlight", - AuraModes::Laser(_) => "Keypress Laser", - AuraModes::Ripple(_) => "Keypress Ripple", - AuraModes::Pulse(_) => "Pulse", - AuraModes::Comet(_) => "Comet", - AuraModes::Flash(_) => "Flash", - AuraModes::MultiStatic(_) => "4-Zone Static Colours", - AuraModes::MultiBreathe(_) => "4-Zone Breathing Colours", - AuraModes::PerKey(_) => "RGB per-key", - _ => panic!("Invalid mode"), - } - } -} - -/// Exists to convert back from correct bytes. PER_KEY byte intentionally left off as it -/// does not correspond to an actual pre-set mode, nor does brightness. -impl From for AuraModes { - fn from(byte: u8) -> Self { - match byte { - STATIC => AuraModes::Static(SingleColour::default()), - BREATHING => AuraModes::Breathe(TwoColourSpeed::default()), - STROBE => AuraModes::Strobe(SingleSpeed::default()), - RAINBOW => AuraModes::Rainbow(SingleSpeedDirection::default()), - STAR => AuraModes::Star(TwoColourSpeed::default()), - RAIN => AuraModes::Rain(SingleSpeed::default()), - HIGHLIGHT => AuraModes::Highlight(SingleColourSpeed::default()), - LASER => AuraModes::Laser(SingleColourSpeed::default()), - RIPPLE => AuraModes::Ripple(SingleColourSpeed::default()), - PULSE => AuraModes::Pulse(SingleColour::default()), - COMET => AuraModes::Comet(SingleColour::default()), - FLASH => AuraModes::Flash(SingleColour::default()), - MULTISTATIC => AuraModes::MultiStatic(MultiColour::default()), - MULTIBREATHE => AuraModes::MultiBreathe(MultiColourSpeed::default()), - PER_KEY => AuraModes::PerKey(vec![]), - _ => panic!("Invalid mode byte"), - } + msg } } diff --git a/rog-types/src/fancy.rs b/rog-types/src/aura_perkey.rs similarity index 99% rename from rog-types/src/fancy.rs rename to rog-types/src/aura_perkey.rs index a838039a..82721643 100644 --- a/rog-types/src/fancy.rs +++ b/rog-types/src/aura_perkey.rs @@ -35,8 +35,8 @@ impl KeyColourArray { /// Initialise and clear the keyboard for custom effects #[inline] - pub fn get_init_msg() -> Vec { - let mut init = vec![0u8; 64]; + pub const fn get_init_msg() -> [u8; 64] { + let mut init = [0u8; 64]; init[0] = 0x5d; // Report ID init[1] = 0xbc; // Mode = custom??, 0xb3 is builtin init diff --git a/rog-types/src/cli_options.rs b/rog-types/src/cli_options.rs index 7323b37a..fa7c072d 100644 --- a/rog-types/src/cli_options.rs +++ b/rog-types/src/cli_options.rs @@ -1,97 +1,9 @@ use crate::{ - aura_modes::{ - MultiColour, MultiColourSpeed, SingleColour, SingleColourSpeed, SingleSpeed, - SingleSpeedDirection, TwoColourSpeed, - }, error::AuraError, }; use gumdrop::Options; -use serde_derive::{Deserialize, Serialize}; use std::str::FromStr; -#[derive(Options)] -pub struct LedBrightness { - level: Option, -} -impl LedBrightness { - pub fn new(level: Option) -> Self { - LedBrightness { level } - } - - pub fn level(&self) -> Option { - self.level - } -} -impl FromStr for LedBrightness { - type Err = AuraError; - - fn from_str(s: &str) -> Result { - let s = s.to_lowercase(); - match s.as_str() { - "off" => Ok(LedBrightness { level: Some(0x00) }), - "low" => Ok(LedBrightness { level: Some(0x01) }), - "med" => Ok(LedBrightness { level: Some(0x02) }), - "high" => Ok(LedBrightness { level: Some(0x03) }), - _ => { - print!("Invalid argument, must be one of: off, low, med, high"); - Err(AuraError::ParseBrightness) - } - } - } -} -impl ToString for LedBrightness { - fn to_string(&self) -> String { - let s = match self.level { - Some(0x00) => "low", - Some(0x01) => "med", - Some(0x02) => "high", - _ => "unknown", - }; - s.to_string() - } -} - -/// Byte value for setting the built-in mode. -/// -/// Enum corresponds to the required integer value -#[derive(Options, Deserialize, Serialize)] -pub enum SetAuraBuiltin { - #[options(help = "set a single static colour")] - Static(SingleColour), - #[options(help = "pulse between one or two colours")] - Breathe(TwoColourSpeed), - #[options(help = "strobe through all colours")] - Strobe(SingleSpeed), - #[options(help = "rainbow cycling in one of four directions")] - Rainbow(SingleSpeedDirection), - #[options(help = "rain pattern mimicking raindrops")] - Star(TwoColourSpeed), - #[options(help = "rain pattern of three preset colours")] - Rain(SingleSpeed), - #[options(help = "pressed keys are highlighted to fade")] - Highlight(SingleColourSpeed), - #[options(help = "pressed keys generate horizontal laser")] - Laser(SingleColourSpeed), - #[options(help = "pressed keys ripple outwards like a splash")] - Ripple(SingleColourSpeed), - #[options(help = "set a rapid pulse")] - Pulse(SingleColour), - #[options(help = "set a vertical line zooming from left")] - Comet(SingleColour), - #[options(help = "set a wide vertical line zooming from left")] - Flash(SingleColour), - #[options(help = "4-zone multi-colour")] - MultiStatic(MultiColour), - #[options(help = "4-zone multi-colour breathing")] - MultiBreathe(MultiColourSpeed), -} - -impl Default for SetAuraBuiltin { - fn default() -> Self { - SetAuraBuiltin::Static(SingleColour::default()) - } -} - #[derive(Copy, Clone, Debug)] pub enum AniMeStatusValue { On, diff --git a/rog-types/src/gfx_vendors.rs b/rog-types/src/gfx_vendors.rs index c901845b..54330c7e 100644 --- a/rog-types/src/gfx_vendors.rs +++ b/rog-types/src/gfx_vendors.rs @@ -1,4 +1,6 @@ +use crate::error::GraphicsError; use serde_derive::{Deserialize, Serialize}; +use std::str::FromStr; #[derive(Debug, PartialEq, Copy, Clone, Deserialize, Serialize)] pub enum GfxVendors { @@ -8,10 +10,6 @@ pub enum GfxVendors { Hybrid, } -use std::str::FromStr; - -use crate::error::GraphicsError; - impl FromStr for GfxVendors { type Err = GraphicsError; @@ -30,9 +28,9 @@ impl FromStr for GfxVendors { } } -impl Into<&str> for GfxVendors { - fn into(self) -> &'static str { - match self { +impl From<&GfxVendors> for &str { + fn from(gfx: &GfxVendors) -> &'static str { + match gfx { GfxVendors::Nvidia => "nvidia", GfxVendors::Hybrid => "hybrid", GfxVendors::Compute => "compute", @@ -41,14 +39,9 @@ impl Into<&str> for GfxVendors { } } -impl Into for GfxVendors { - fn into(self) -> String { - match self { - GfxVendors::Nvidia => "nvidia".to_string(), - GfxVendors::Hybrid => "hybrid".to_string(), - GfxVendors::Compute => "compute".to_string(), - GfxVendors::Integrated => "integrated".to_string(), - } +impl From for &str { + fn from(gfx: GfxVendors) -> &'static str { + (&gfx).into() } } @@ -82,17 +75,7 @@ impl From<&GfxCtrlAction> for &str { } } -impl From<&GfxCtrlAction> for String { - fn from(mode: &GfxCtrlAction) -> Self { - match mode { - GfxCtrlAction::Reboot => "reboot".into(), - GfxCtrlAction::RestartX => "restartx".into(), - GfxCtrlAction::None => "none".into(), - } - } -} - -impl From for String { +impl From for &str { fn from(mode: GfxCtrlAction) -> Self { (&mode).into() } diff --git a/rog-types/src/lib.rs b/rog-types/src/lib.rs index 872626e8..3da40802 100644 --- a/rog-types/src/lib.rs +++ b/rog-types/src/lib.rs @@ -1,10 +1,13 @@ +//! This crate is intended for shared types (eg, between daemon and CLI), or +//! for types that might be useful in third-party crates perhaps for +//! sending messages over dbus wire + pub static DBUS_NAME: &str = "org.asuslinux.Daemon"; pub static DBUS_PATH: &str = "/org/asuslinux/Daemon"; pub static DBUS_IFACE: &str = "org.asuslinux.Daemon"; pub const LED_MSG_LEN: usize = 17; pub mod aura_modes; -use aura_modes::AuraModes; pub mod profile; @@ -12,7 +15,7 @@ pub mod profile; pub mod cli_options; /// Enables you to create fancy RGB effects -pub mod fancy; +pub mod aura_perkey; /// Helper functions for the AniMe display pub mod anime_matrix; @@ -22,262 +25,3 @@ pub mod gfx_vendors; pub mod error; pub static VERSION: &str = env!("CARGO_PKG_VERSION"); - -// static LED_INIT1: [u8; 2] = [0x5d, 0xb9]; -// static LED_INIT2: &str = "]ASUS Tech.Inc."; // ] == 0x5d -// static LED_INIT3: [u8; 6] = [0x5d, 0x05, 0x20, 0x31, 0, 0x08]; -// static LED_INIT4: &str = "^ASUS Tech.Inc."; // ^ == 0x5e -// static LED_INIT5: [u8; 6] = [0x5e, 0x05, 0x20, 0x31, 0, 0x08]; - -/// Writes aout the correct byte string for brightness -/// -/// The HID descriptor looks like: -/// -/// ```ignore -/// 0x06, 0x31, 0xFF, // Usage Page (Vendor Defined 0xFF31) -/// 0x09, 0x76, // Usage (0x76) -/// 0xA1, 0x01, // Collection (Application) -/// 0x85, 0x5A, // Report ID (90) -/// 0x19, 0x00, // Usage Minimum (0x00) -/// 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF) -/// 0x15, 0x00, // Logical Minimum (0) -/// 0x26, 0xFF, 0x00, // Logical Maximum (255) -/// 0x75, 0x08, // Report Size (8) -/// 0x95, 0x05, // Report Count (5) -/// 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) -/// 0x19, 0x00, // Usage Minimum (0x00) -/// 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF) -/// 0x15, 0x00, // Logical Minimum (0) -/// 0x26, 0xFF, 0x00, // Logical Maximum (255) -/// 0x75, 0x08, // Report Size (8) -/// 0x95, 0x3F, // Report Count (63) -/// 0xB1, 0x00, // Feature (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) -/// 0xC0, // End Collection -/// ``` -pub fn aura_brightness_bytes(brightness: u8) -> [u8; 17] { - // TODO: check brightness range - [ - 0x5A, 0xBA, 0xC5, 0xC4, brightness, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ] -} - -/// Parses `AuraCommands` in to packet data -/// -/// Byte structure: -/// -/// ```ignore -/// | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12| -/// |---|---|---|---|---|---|---|---|---|---|---|---|---| -/// |5d |b3 |00 |03 |ff |00 |00 |00 |00 |00 |00 |ff |00 | -/// ``` -/// -/// Bytes 0 and 1 should always be 5d, b3 -/// -/// On multizone laptops byte 2 is the zone number, RGB in usual -/// place, byte 3 set to zero -/// -/// Byte 3 sets the mode type: -/// - 00 = static -/// - 01 = breathe (can set two colours) -/// - 02 = strobe (through all colours) -/// - 03 = rainbow -/// - 04 = star (byte 9 sets rain colour) -/// - 05 = rain keys, red, white, turquoise -/// - 06 = pressed keys light up and fade -/// - 07 = pressed key emits laser -/// - 08 = pressed key emits water ripple -/// - 09 = no effect/not used -/// - 0a fast pulse (no speed setting) -/// - 0b vertical line racing to right (no speed setting) -/// - 0c wider vertical line racing to right (no speed setting) -/// -/// Bytes 4, 5, 6 are Red, Green, Blue -/// -/// Byte 7 sets speed from -/// - 0x00 = Off -/// - 0xe1 = Slow -/// - 0xeb = Medium -/// - 0xf5 = Fast -/// -/// Byte 8 sets rainbow direction: -/// - 0x00 = rightwards -/// - 0x01 = leftwards -/// - 0x02 = upwards -/// - 0x03 = downwards -/// -/// Bytes 10, 11, 12 are Red, Green, Blue for second colour if mode supports it -/// -/// The HID descriptor looks like: -/// ```ignore -/// 0x06, 0x31, 0xFF, // Usage Page (Vendor Defined 0xFF31) -/// 0x09, 0x79, // Usage (0x79) -/// 0xA1, 0x01, // Collection (Application) -/// 0x85, 0x5D, // Report ID (93) -/// 0x19, 0x00, // Usage Minimum (0x00) -/// 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF) -/// 0x15, 0x00, // Logical Minimum (0) -/// 0x26, 0xFF, 0x00, // Logical Maximum (255) -/// 0x75, 0x08, // Report Size (8) -/// 0x95, 0x1F, // Report Count (31) -/// 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) -/// 0x19, 0x00, // Usage Minimum (0x00) -/// 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF) -/// 0x15, 0x00, // Logical Minimum (0) -/// 0x26, 0xFF, 0x00, // Logical Maximum (255) -/// 0x75, 0x08, // Report Size (8) -/// 0x95, 0x3F, // Report Count (63) -/// 0x91, 0x00, // Output (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) -/// 0x19, 0x00, // Usage Minimum (0x00) -/// 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF) -/// 0x15, 0x00, // Logical Minimum (0) -/// 0x26, 0xFF, 0x00, // Logical Maximum (255) -/// 0x75, 0x08, // Report Size (8) -/// 0x95, 0x3F, // Report Count (63) -/// 0xB1, 0x00, // Feature (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) -/// 0xC0, // End Collection -/// ``` -/// -/// This descriptor is also used for the per-key LED settings -impl From<&AuraModes> for [u8; LED_MSG_LEN] { - fn from(mode: &AuraModes) -> Self { - let mut msg = [0u8; LED_MSG_LEN]; - msg[0] = 0x5d; - msg[1] = 0xb3; - msg[7] = 0xeb; - match mode { - AuraModes::LedBrightness(n) => return aura_brightness_bytes(*n), - AuraModes::Static(_) => msg[3] = 0x00, - AuraModes::Breathe(_) => msg[3] = 0x01, - AuraModes::Strobe(_) => msg[3] = 0x02, - AuraModes::Rainbow(_) => msg[3] = 0x03, - AuraModes::Star(_) => msg[3] = 0x04, - AuraModes::Rain(_) => msg[3] = 0x05, - AuraModes::Highlight(_) => msg[3] = 0x06, - AuraModes::Laser(_) => msg[3] = 0x07, - AuraModes::Ripple(_) => msg[3] = 0x08, - AuraModes::Pulse(_) => msg[3] = 0x0a, - AuraModes::Comet(_) => msg[3] = 0x0b, - AuraModes::Flash(_) => msg[3] = 0x0c, - _ => panic!("Mode not convertable to 1D array: {}", <&str>::from(mode)), - } - - match mode { - AuraModes::Rainbow(settings) => { - msg[7] = settings.speed as u8; - msg[8] = settings.direction as u8; - } - AuraModes::Star(settings) => { - msg[4] = settings.colour.0; - msg[5] = settings.colour.1; - msg[6] = settings.colour.2; - msg[7] = settings.speed as u8; - msg[9] = settings.colour2.2; - } - AuraModes::Breathe(settings) => { - msg[4] = settings.colour.0; - msg[5] = settings.colour.1; - msg[6] = settings.colour.2; - msg[7] = settings.speed as u8; - msg[10] = settings.colour2.0; - msg[11] = settings.colour2.1; - msg[12] = settings.colour2.2; - } - AuraModes::Strobe(settings) | AuraModes::Rain(settings) => { - msg[7] = settings.speed as u8; - } - AuraModes::Highlight(settings) - | AuraModes::Laser(settings) - | AuraModes::Ripple(settings) => { - msg[4] = settings.colour.0; - msg[5] = settings.colour.1; - msg[6] = settings.colour.2; - msg[7] = settings.speed as u8; - } - AuraModes::Static(settings) - | AuraModes::Pulse(settings) - | AuraModes::Comet(settings) - | AuraModes::Flash(settings) => { - msg[4] = settings.colour.0; - msg[5] = settings.colour.1; - msg[6] = settings.colour.2; - } - _ => panic!("Mode not convertable to 1D array: {}", <&str>::from(mode)), - } - msg - } -} - -impl From for [u8; LED_MSG_LEN] { - #[inline] - fn from(mode: AuraModes) -> Self { - <[u8; LED_MSG_LEN]>::from(&mode) - } -} - -impl From for [[u8; LED_MSG_LEN]; 4] { - #[inline] - fn from(mode: AuraModes) -> Self { - <[[u8; LED_MSG_LEN]; 4]>::from(&mode) - } -} - -impl From<&AuraModes> for [[u8; LED_MSG_LEN]; 4] { - #[inline] - fn from(mode: &AuraModes) -> Self { - let mut msg = [[0u8; LED_MSG_LEN]; 4]; - match mode { - AuraModes::MultiStatic(settings) => { - for (i, row) in msg.iter_mut().enumerate() { - row[0] = 0x5d; - row[1] = 0xb3; - row[2] = i as u8 + 1; // zone - row[3] = 0x00; // mode - row[7] = 0xeb; // static needs speed? - } - msg[0][4] = settings.colour1.0; - msg[0][5] = settings.colour1.1; - msg[0][6] = settings.colour1.2; - msg[1][4] = settings.colour2.0; - msg[1][5] = settings.colour2.1; - msg[1][6] = settings.colour2.2; - msg[2][4] = settings.colour3.0; - msg[2][5] = settings.colour3.1; - msg[2][6] = settings.colour3.2; - msg[3][4] = settings.colour4.0; - msg[3][5] = settings.colour4.1; - msg[3][6] = settings.colour4.2; - } - AuraModes::MultiBreathe(settings) => { - for (i, row) in msg.iter_mut().enumerate() { - row[0] = 0x5d; - row[1] = 0xb3; - row[2] = i as u8 + 1; // zone - row[3] = 0x01; // mode - } - let speed = match settings.speed { - aura_modes::Speed::Low => 0xfd, - aura_modes::Speed::Med => 0xfe, - aura_modes::Speed::High => 0xff, - }; - msg[0][4] = settings.colour1.0; - msg[0][5] = settings.colour1.1; - msg[0][6] = settings.colour1.2; - msg[0][7] = speed; // fd, fe, ff - msg[1][4] = settings.colour2.0; - msg[1][5] = settings.colour2.1; - msg[1][6] = settings.colour2.2; - msg[1][7] = speed; - msg[2][4] = settings.colour3.0; - msg[2][5] = settings.colour3.1; - msg[2][6] = settings.colour3.2; - msg[2][7] = speed; - msg[3][4] = settings.colour4.0; - msg[3][5] = settings.colour4.1; - msg[3][6] = settings.colour4.2; - msg[3][7] = speed; - } - _ => panic!("Mode not convertable to 2D array: {}", <&str>::from(mode)), - } - msg - } -}