feat(rog-aura): Add AnimationMode and color utilities

This commit is contained in:
mihai2mn
2026-01-24 16:52:44 +01:00
parent a0dd0b36dd
commit 5a8908ebd6
2 changed files with 145 additions and 0 deletions

141
rog-aura/src/animation.rs Normal file
View File

@@ -0,0 +1,141 @@
//! Software-controlled LED animation modes for the asusd daemon.
//!
//! These modes run as background tasks in asusd and continuously update
//! the LED colors without requiring the GUI to be open.
use serde::{Deserialize, Serialize};
use crate::Colour;
/// Animation modes that can be run by the asusd daemon.
///
/// Note: This type is serialized as JSON for DBus transport since zvariant
/// doesn't support enums with heterogeneous variants.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub enum AnimationMode {
/// No animation running
#[default]
None,
/// Rainbow effect - cycles through HSV hue (0-360°)
Rainbow {
/// Update interval in milliseconds (lower = faster)
speed_ms: u32,
},
/// Color cycle - transitions between a list of colors
ColorCycle {
/// Update interval in milliseconds
speed_ms: u32,
/// Colors to cycle through
colors: Vec<Colour>,
},
/// Breathe effect - fades between two colors through black
Breathe {
/// Update interval in milliseconds
speed_ms: u32,
/// Primary color
color1: Colour,
/// Secondary color (fades to this, then back)
color2: Colour,
},
/// Pulse effect - brightness animation (dims and brightens)
Pulse {
/// Update interval in milliseconds
speed_ms: u32,
/// Base color to pulse
color: Colour,
/// Minimum brightness factor (0.0 - 1.0)
min_brightness: f32,
/// Maximum brightness factor (0.0 - 1.0)
max_brightness: f32,
},
}
impl AnimationMode {
/// Returns true if this is an active animation mode
pub fn is_active(&self) -> bool {
!matches!(self, Self::None)
}
/// Get the speed/interval in milliseconds
pub fn speed_ms(&self) -> u32 {
match self {
Self::None => 0,
Self::Rainbow { speed_ms } => *speed_ms,
Self::ColorCycle { speed_ms, .. } => *speed_ms,
Self::Breathe { speed_ms, .. } => *speed_ms,
Self::Pulse { speed_ms, .. } => *speed_ms,
}
}
}
/// Apply a brightness factor to a color by scaling RGB values.
/// This allows simulating granular brightness on hardware with limited levels.
pub fn apply_brightness(color: Colour, factor: f32) -> Colour {
let factor = factor.clamp(0.0, 1.0);
Colour {
r: (color.r as f32 * factor) as u8,
g: (color.g as f32 * factor) as u8,
b: (color.b as f32 * factor) as u8,
}
}
/// Convert HSV to RGB color.
/// Hue: 0-360, Saturation: 0.0-1.0, Value: 0.0-1.0
pub fn hsv_to_rgb(h: f32, s: f32, v: f32) -> Colour {
let c = v * s;
let h_prime = h / 60.0;
let x = c * (1.0 - ((h_prime % 2.0) - 1.0).abs());
let m = v - c;
let (r1, g1, b1) = match h_prime as u32 {
0 => (c, x, 0.0),
1 => (x, c, 0.0),
2 => (0.0, c, x),
3 => (0.0, x, c),
4 => (x, 0.0, c),
_ => (c, 0.0, x),
};
Colour {
r: ((r1 + m) * 255.0) as u8,
g: ((g1 + m) * 255.0) as u8,
b: ((b1 + m) * 255.0) as u8,
}
}
/// Linear interpolation between two colors
pub fn lerp_colour(c1: &Colour, c2: &Colour, t: f32) -> Colour {
let t = t.clamp(0.0, 1.0);
Colour {
r: (c1.r as f32 + (c2.r as f32 - c1.r as f32) * t) as u8,
g: (c1.g as f32 + (c2.g as f32 - c1.g as f32) * t) as u8,
b: (c1.b as f32 + (c2.b as f32 - c1.b as f32) * t) as u8,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hsv_to_rgb_red() {
let c = hsv_to_rgb(0.0, 1.0, 1.0);
assert_eq!(c.r, 255);
assert_eq!(c.g, 0);
assert_eq!(c.b, 0);
}
#[test]
fn test_brightness() {
let c = Colour {
r: 100,
g: 200,
b: 50,
};
let dim = apply_brightness(c, 0.5);
assert_eq!(dim.r, 50);
assert_eq!(dim.g, 100);
assert_eq!(dim.b, 25);
}
}

View File

@@ -16,6 +16,10 @@ pub mod effects;
mod builtin_modes;
pub use builtin_modes::*;
/// Software-controlled animation modes for daemon
pub mod animation;
pub use animation::{apply_brightness, hsv_to_rgb, lerp_colour, AnimationMode};
/// Helper for detecting what is available
pub mod aura_detection;
pub mod error;