mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
Groundwork for 'advanced' aura modes Add single zone + Doom light flash Fix mocking for ROGCC Better prepare & change to mapping of keyboard layouts to models and functions Refactor and begin using new key layout stuff Enable first arg to rogcc to set layout in mocking feature mode Complete refactor of key layouts, and to RON serde
230 lines
6.8 KiB
Rust
230 lines
6.8 KiB
Rust
use std::convert::TryFrom;
|
|
use std::path::PathBuf;
|
|
use std::time::Duration;
|
|
|
|
use glam::Vec2;
|
|
use serde_derive::{Deserialize, Serialize};
|
|
|
|
use crate::error::Result;
|
|
use crate::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType};
|
|
|
|
/// All the possible `AniMe` actions that can be used. This enum is intended to
|
|
/// be a helper for loading up `ActionData`.
|
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
pub enum ActionLoader {
|
|
/// Full gif sequence. Immutable.
|
|
AsusAnimation {
|
|
file: PathBuf,
|
|
time: AnimTime,
|
|
brightness: f32,
|
|
},
|
|
/// Image designed to be pixel perfect using the slanted template
|
|
AsusImage {
|
|
file: PathBuf,
|
|
time: AnimTime,
|
|
brightness: f32,
|
|
},
|
|
/// Animated gif. If the file is a png a static gif is created using the
|
|
/// `time` properties
|
|
ImageAnimation {
|
|
file: PathBuf,
|
|
scale: f32,
|
|
angle: f32,
|
|
translation: Vec2,
|
|
time: AnimTime,
|
|
brightness: f32,
|
|
},
|
|
Image {
|
|
file: PathBuf,
|
|
scale: f32,
|
|
angle: f32,
|
|
translation: Vec2,
|
|
time: AnimTime,
|
|
brightness: f32,
|
|
},
|
|
/// A pause to be used between sequences
|
|
Pause(Duration),
|
|
}
|
|
|
|
/// All the possible `AniMe` actions that can be used. The enum is intended to
|
|
/// be used in a array allowing the user to cycle through a series of actions.
|
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
pub enum ActionData {
|
|
/// Full gif sequence. Immutable.
|
|
Animation(AnimeGif),
|
|
/// Basic image, can have properties changed and image updated via those
|
|
/// properties
|
|
Image(Box<AnimeDataBuffer>),
|
|
/// A pause to be used between sequences
|
|
Pause(Duration),
|
|
/// Placeholder
|
|
AudioEq,
|
|
/// Placeholder
|
|
SystemInfo,
|
|
/// Placeholder
|
|
TimeDate,
|
|
/// Placeholder
|
|
Matrix,
|
|
}
|
|
|
|
impl ActionData {
|
|
pub fn from_anime_action(anime_type: AnimeType, action: &ActionLoader) -> Result<ActionData> {
|
|
let a = match action {
|
|
ActionLoader::AsusAnimation {
|
|
file,
|
|
time,
|
|
brightness,
|
|
} => ActionData::Animation(AnimeGif::from_diagonal_gif(
|
|
file,
|
|
*time,
|
|
*brightness,
|
|
anime_type,
|
|
)?),
|
|
ActionLoader::AsusImage {
|
|
file,
|
|
time,
|
|
brightness,
|
|
} => match time {
|
|
AnimTime::Infinite => {
|
|
let image = AnimeDiagonal::from_png(file, None, *brightness, anime_type)?;
|
|
let data = image.into_data_buffer(anime_type)?;
|
|
ActionData::Image(Box::new(data))
|
|
}
|
|
_ => ActionData::Animation(AnimeGif::from_diagonal_png(
|
|
file,
|
|
anime_type,
|
|
*time,
|
|
*brightness,
|
|
)?),
|
|
},
|
|
ActionLoader::ImageAnimation {
|
|
file,
|
|
scale,
|
|
angle,
|
|
translation,
|
|
time,
|
|
brightness,
|
|
} => {
|
|
if let Some(ext) = file.extension() {
|
|
if ext.to_string_lossy().to_lowercase() == "png" {
|
|
return Ok(ActionData::Animation(AnimeGif::from_png(
|
|
file,
|
|
*scale,
|
|
*angle,
|
|
*translation,
|
|
*time,
|
|
*brightness,
|
|
anime_type,
|
|
)?));
|
|
}
|
|
}
|
|
ActionData::Animation(AnimeGif::from_gif(
|
|
file,
|
|
*scale,
|
|
*angle,
|
|
*translation,
|
|
*time,
|
|
*brightness,
|
|
anime_type,
|
|
)?)
|
|
}
|
|
ActionLoader::Image {
|
|
file,
|
|
scale,
|
|
angle,
|
|
translation,
|
|
brightness,
|
|
time,
|
|
} => {
|
|
match time {
|
|
AnimTime::Infinite => {
|
|
// If no time then create a plain static image
|
|
let image = AnimeImage::from_png(
|
|
file,
|
|
*scale,
|
|
*angle,
|
|
*translation,
|
|
*brightness,
|
|
anime_type,
|
|
)?;
|
|
let data = <AnimeDataBuffer>::try_from(&image)?;
|
|
ActionData::Image(Box::new(data))
|
|
}
|
|
_ => ActionData::Animation(AnimeGif::from_png(
|
|
file,
|
|
*scale,
|
|
*angle,
|
|
*translation,
|
|
*time,
|
|
*brightness,
|
|
anime_type,
|
|
)?),
|
|
}
|
|
}
|
|
ActionLoader::Pause(duration) => ActionData::Pause(*duration),
|
|
};
|
|
Ok(a)
|
|
}
|
|
}
|
|
|
|
/// An optimised precomputed set of actions that the user can cycle through
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
pub struct Sequences(Vec<ActionData>, AnimeType);
|
|
|
|
impl Sequences {
|
|
#[inline]
|
|
pub fn new(anime_type: AnimeType) -> Self {
|
|
Self(Vec::new(), anime_type)
|
|
}
|
|
|
|
/// Use a base `AnimeAction` to generate the precomputed data and insert in
|
|
/// to the run buffer
|
|
#[inline]
|
|
pub fn insert(&mut self, index: usize, action: &ActionLoader) -> Result<()> {
|
|
self.0
|
|
.insert(index, ActionData::from_anime_action(self.1, action)?);
|
|
Ok(())
|
|
}
|
|
|
|
/// Remove an item at this position from the run buffer. If the `index`
|
|
/// supplied is not in range then `None` is returned, otherwise the
|
|
/// `ActionData` at that location is yeeted and returned.
|
|
#[inline]
|
|
pub fn remove_item(&mut self, index: usize) -> Option<ActionData> {
|
|
if index < self.0.len() {
|
|
return Some(self.0.remove(index));
|
|
}
|
|
None
|
|
}
|
|
|
|
pub fn iter(&self) -> ActionIterator<'_> {
|
|
ActionIterator {
|
|
actions: self,
|
|
next_idx: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Iteractor helper for iterating over all the actions in `Sequences`
|
|
pub struct ActionIterator<'a> {
|
|
actions: &'a Sequences,
|
|
next_idx: usize,
|
|
}
|
|
|
|
impl<'a> Iterator for ActionIterator<'a> {
|
|
type Item = &'a ActionData;
|
|
|
|
#[inline]
|
|
fn next(&mut self) -> Option<&'a ActionData> {
|
|
if self.next_idx == self.actions.0.len() {
|
|
self.next_idx = 0;
|
|
return None;
|
|
}
|
|
|
|
let current = self.next_idx;
|
|
self.next_idx += 1;
|
|
|
|
Some(&self.actions.0[current])
|
|
}
|
|
}
|