mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
feat(rog-aura): Add AnimationMode and color utilities
This commit is contained in:
141
rog-aura/src/animation.rs
Normal file
141
rog-aura/src/animation.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,10 @@ pub mod effects;
|
|||||||
mod builtin_modes;
|
mod builtin_modes;
|
||||||
pub use 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
|
/// Helper for detecting what is available
|
||||||
pub mod aura_detection;
|
pub mod aura_detection;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|||||||
Reference in New Issue
Block a user