mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
anime: CLI and user-daemon work
This commit is contained in:
37
daemon-user/src/error.rs
Normal file
37
daemon-user/src/error.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use std::fmt;
|
||||
|
||||
use rog_anime::error::AnimeError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Io(std::io::Error),
|
||||
ConfigLoadFail,
|
||||
XdgVars,
|
||||
Anime(AnimeError),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
// This trait requires `fmt` with this exact signature.
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Error::Io(err) => write!(f, "Failed to open: {}", err),
|
||||
Error::ConfigLoadFail => write!(f, "Failed to load user config"),
|
||||
Error::XdgVars => write!(f, "XDG environment vars appear unset"),
|
||||
Error::Anime(err) => write!(f, "Anime error: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
fn from(err: std::io::Error) -> Self {
|
||||
Error::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AnimeError> for Error {
|
||||
fn from(err: AnimeError) -> Self {
|
||||
Error::Anime(err)
|
||||
}
|
||||
}
|
||||
3
daemon-user/src/lib.rs
Normal file
3
daemon-user/src/lib.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod user_config;
|
||||
|
||||
pub mod error;
|
||||
60
daemon-user/src/main.rs
Normal file
60
daemon-user/src/main.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
use rog_anime::Action;
|
||||
use rog_dbus::AuraDbusClient;
|
||||
use rog_user::user_config::*;
|
||||
|
||||
use std::{
|
||||
thread::sleep,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!(" rog-dbus version {}", rog_dbus::VERSION);
|
||||
|
||||
let (client, _) = AuraDbusClient::new().unwrap();
|
||||
|
||||
let mut config = UserConfig::new();
|
||||
config.load_config()?;
|
||||
let anime = config.create_anime()?;
|
||||
|
||||
// TODO:
|
||||
// - find user config dir with xdg
|
||||
// - load user config
|
||||
// - start anime
|
||||
// A way to reload when the config changes
|
||||
|
||||
loop {
|
||||
for action in anime.iter() {
|
||||
let start = Instant::now();
|
||||
|
||||
match action {
|
||||
Action::Animation(frames) => 'animation: loop {
|
||||
for frame in frames.frames() {
|
||||
client.proxies().anime().write(frame.frame().clone())?;
|
||||
if let Some(time) = frames.duration() {
|
||||
if Instant::now().duration_since(start) > time {
|
||||
break 'animation;
|
||||
}
|
||||
}
|
||||
sleep(frame.delay());
|
||||
}
|
||||
if frames.duration().is_none() {
|
||||
break 'animation;
|
||||
}
|
||||
},
|
||||
Action::Image(image) => {
|
||||
client.proxies().anime().write(image.as_ref().clone())?;
|
||||
}
|
||||
Action::Pause(duration) => 'pause: loop {
|
||||
if Instant::now().duration_since(start) > *duration {
|
||||
break 'pause;
|
||||
}
|
||||
sleep(Duration::from_millis(10));
|
||||
},
|
||||
Action::AudioEq => {}
|
||||
Action::SystemInfo => {}
|
||||
Action::TimeDate => {}
|
||||
Action::Matrix => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
151
daemon-user/src/user_config.rs
Normal file
151
daemon-user/src/user_config.rs
Normal file
@@ -0,0 +1,151 @@
|
||||
use std::{
|
||||
fs::{create_dir, OpenOptions},
|
||||
io::{Read, Write},
|
||||
path::PathBuf,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use rog_anime::{Sequences, Vec2};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||
pub struct UserConfig {
|
||||
anime: Vec<AnimeAction>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub enum AnimeAction {
|
||||
/// Full gif sequence. Immutable.
|
||||
AsusAnimation {
|
||||
file: PathBuf,
|
||||
duration: Option<Duration>,
|
||||
brightness: f32,
|
||||
},
|
||||
/// Basic image, can have properties changed
|
||||
ImageAnimation {
|
||||
file: PathBuf,
|
||||
scale: f32,
|
||||
angle: f32,
|
||||
translation: Vec2,
|
||||
duration: Option<Duration>,
|
||||
brightness: f32,
|
||||
},
|
||||
Image {
|
||||
file: PathBuf,
|
||||
scale: f32,
|
||||
angle: f32,
|
||||
translation: Vec2,
|
||||
brightness: f32,
|
||||
},
|
||||
/// A pause to be used between sequences
|
||||
Pause(Duration),
|
||||
}
|
||||
|
||||
impl UserConfig {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
anime: vec![
|
||||
AnimeAction::AsusAnimation {
|
||||
file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(),
|
||||
brightness: 0.5,
|
||||
duration: None,
|
||||
},
|
||||
AnimeAction::ImageAnimation {
|
||||
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
|
||||
scale: 0.9,
|
||||
angle: 0.65,
|
||||
translation: Vec2::default(),
|
||||
brightness: 0.5,
|
||||
duration: Some(Duration::from_secs(5)),
|
||||
},
|
||||
AnimeAction::Image {
|
||||
file: "/usr/share/asusd/anime/custom/rust.png".into(),
|
||||
scale: 1.0,
|
||||
angle: 0.0,
|
||||
translation: Vec2::default(),
|
||||
brightness: 0.6,
|
||||
},
|
||||
AnimeAction::Pause(Duration::from_secs(6)),
|
||||
AnimeAction::ImageAnimation {
|
||||
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
|
||||
scale: 0.9,
|
||||
angle: 0.0,
|
||||
translation: Vec2::new(3.0, 2.0),
|
||||
brightness: 0.5,
|
||||
duration: None,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_config(&mut self) -> Result<(), Error> {
|
||||
let mut path = if let Some(dir) = dirs::config_dir() {
|
||||
dir
|
||||
} else {
|
||||
return Err(Error::XdgVars);
|
||||
};
|
||||
|
||||
path.push("rog");
|
||||
if !path.exists() {
|
||||
create_dir(path.clone())?;
|
||||
}
|
||||
|
||||
path.push("rog-user.cfg");
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(&path)?;
|
||||
|
||||
let mut buf = String::new();
|
||||
|
||||
if let Ok(read_len) = file.read_to_string(&mut buf) {
|
||||
if read_len == 0 {
|
||||
let json = serde_json::to_string_pretty(&self).unwrap();
|
||||
file.write_all(json.as_bytes())?;
|
||||
} else if let Ok(data) = serde_json::from_str::<UserConfig>(&buf) {
|
||||
self.anime = data.anime;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
//Err(Error::ConfigLoadFail)
|
||||
}
|
||||
|
||||
pub fn create_anime(&self) -> Result<Sequences, Error> {
|
||||
let mut seq = Sequences::new();
|
||||
|
||||
for anime in self.anime.iter() {
|
||||
match anime {
|
||||
AnimeAction::AsusAnimation {
|
||||
file,
|
||||
duration,
|
||||
brightness,
|
||||
} => seq.add_asus_gif(&file, *duration, *brightness)?,
|
||||
AnimeAction::ImageAnimation {
|
||||
file,
|
||||
scale,
|
||||
angle,
|
||||
translation,
|
||||
duration,
|
||||
brightness,
|
||||
} => {
|
||||
seq.add_image_gif(&file, *scale, *angle, *translation, *duration, *brightness)?
|
||||
}
|
||||
AnimeAction::Image {
|
||||
file,
|
||||
scale,
|
||||
angle,
|
||||
translation,
|
||||
brightness,
|
||||
} => seq.add_png(&file, *scale, *angle, *translation, *brightness)?,
|
||||
AnimeAction::Pause(duration) => seq.add_pause(*duration)?,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(seq)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user