anime: add zbus methods

This commit is contained in:
Luke D Jones
2021-04-10 14:21:22 +12:00
parent ece565de1c
commit e515741efa
28 changed files with 741 additions and 299 deletions

View File

@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Revert zbus to 1.9.1
- Use enum to show power states, and catch missing pci path for nvidia.
- Partial user-daemon for anime/per-key done, asusd-user. Includes asusd-user systemd unit.
- user-daemon provides dbus emthods to insert anime actions, remove from index, set leds on/off
# [3.3.0] - 2021-04-3
### Changed

5
Cargo.lock generated
View File

@@ -233,9 +233,13 @@ dependencies = [
"dirs 3.0.1",
"rog_anime",
"rog_dbus",
"rog_types",
"serde",
"serde_derive",
"serde_json",
"zbus",
"zvariant",
"zvariant_derive",
]
[[package]]
@@ -909,6 +913,7 @@ dependencies = [
"png_pong",
"serde",
"serde_derive",
"zbus",
"zvariant",
"zvariant_derive",
]

View File

@@ -2,6 +2,7 @@ use notify_rust::{Hint, Notification, NotificationHandle};
use rog_dbus::{DbusProxies, Signals};
use rog_types::profile::Profile;
use std::error::Error;
use std::thread::sleep;
use std::time::Duration;
fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -18,9 +19,22 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut last_chrg_notif: Option<NotificationHandle> = None;
let recv = proxies.setup_recv(conn);
let mut err_count = 0;
loop {
std::thread::sleep(Duration::from_millis(100));
recv.next_signal().unwrap();
sleep(Duration::from_millis(100));
if let Err(err) = recv.next_signal() {
if err_count < 3 {
err_count += 1;
println!("{}", err);
}
if err_count == 3 {
err_count += 1;
println!("Max error count reached. Spooling silently.");
}
sleep(Duration::from_millis(2000));
continue;
}
err_count = 0;
if let Ok(mut lock) = signals.gfx_vendor.lock() {
if let Some(vendor) = lock.take() {

View File

@@ -1,6 +1,6 @@
use std::{env, path::Path, thread::sleep};
use rog_anime::{Action, Sequences};
use rog_anime::{ActionData, AnimeAction, Sequences};
use rog_dbus::AuraDbusClient;
fn main() {
@@ -15,11 +15,19 @@ fn main() {
let path = Path::new(&args[1]);
let brightness = args[2].parse::<f32>().unwrap();
let mut seq = Sequences::new();
seq.add_asus_gif(path, rog_anime::AnimTime::Infinite, brightness).unwrap();
seq.insert(
0,
&AnimeAction::AsusAnimation {
file: path.into(),
time: rog_anime::AnimTime::Infinite,
brightness,
},
)
.unwrap();
loop {
for action in seq.iter() {
if let Action::Animation(frames) = action {
if let ActionData::Animation(frames) = action {
for frame in frames.frames() {
client
.proxies()

View File

@@ -1,97 +0,0 @@
use std::{
env,
path::Path,
thread::sleep,
time::{Duration, Instant},
};
use glam::Vec2;
use rog_anime::{Action, AnimTime, Sequences};
use rog_dbus::AuraDbusClient;
fn main() {
let (client, _) = AuraDbusClient::new().unwrap();
let args: Vec<String> = env::args().into_iter().collect();
if args.len() < 7 {
println!(
"Usage: <filepath> <scale> <angle> <x pos> <y pos> <brightness> <duration> <filepath>"
);
println!("e.g, asusctl/examples/file.gif 0.9 0.4 0.0 0.0 0.8 0");
return;
}
let mut seq = Sequences::new();
seq.add_image_gif(
Path::new(&args[1]),
args[2].parse::<f32>().unwrap(),
args[3].parse::<f32>().unwrap(),
Vec2::new(
args[4].parse::<f32>().unwrap(),
args[5].parse::<f32>().unwrap(),
),
if let Ok(time) = args[7].parse::<u64>() {
if time != 0 {
AnimTime::Time(Duration::from_secs(time))
} else {
AnimTime::Infinite
}
} else {
AnimTime::Infinite
},
args[6].parse::<f32>().unwrap(),
)
.unwrap();
if args.len() == 9 {
seq.add_image_gif(
Path::new(&args[8]),
args[2].parse::<f32>().unwrap(),
args[3].parse::<f32>().unwrap(),
Vec2::new(
args[4].parse::<f32>().unwrap(),
args[5].parse::<f32>().unwrap(),
),
if let Ok(time) = args[7].parse::<u64>() {
if time != 0 {
AnimTime::Time(Duration::from_secs(time))
} else {
AnimTime::Infinite
}
} else {
AnimTime::Infinite
},
args[6].parse::<f32>().unwrap(),
)
.unwrap();
}
loop {
for action in seq.iter() {
if let Action::Animation(frames) = action {
let mut count = 0;
let start = Instant::now();
'outer: loop {
for frame in frames.frames() {
client
.proxies()
.anime()
.write(frame.frame().clone())
.unwrap();
if let AnimTime::Time(time) = frames.duration() {
if Instant::now().duration_since(start) > time {
break 'outer;
}
} else if let AnimTime::Cycles(times) = frames.duration() {
if count == times {
break 'outer;
}
}
sleep(frame.delay());
}
count +=1;
}
}
}
}
}

View File

@@ -3,10 +3,6 @@ mod aura_cli;
use crate::aura_cli::{LedBrightness, SetAuraBuiltin};
use anime_cli::{AniMeActions, AniMeCommand};
use daemon::{
ctrl_fan_cpu::FanCpuSupportedFunctions, ctrl_leds::LedSupportedFunctions,
ctrl_rog_bios::RogBiosSupportedFunctions, ctrl_supported::SupportedFunctions,
};
use gumdrop::{Opt, Options};
use rog_anime::{AniMeDataBuffer, AniMeImage, Vec2, ANIME_DATA_LEN};
use rog_dbus::AuraDbusClient;
@@ -14,6 +10,10 @@ use rog_types::{
aura_modes::{self, AuraEffect, AuraModeNum},
gfx_vendors::GfxVendors,
profile::{FanLevel, ProfileCommand, ProfileEvent},
supported::{
FanCpuSupportedFunctions, LedSupportedFunctions, RogBiosSupportedFunctions,
SupportedFunctions,
},
};
use std::{env::args, path::Path};
use yansi_term::Colour::Green;

View File

@@ -11,7 +11,7 @@ path = "src/lib.rs"
[[bin]]
name = "asusd-user"
path = "src/main.rs"
path = "src/daemon.rs"
[dependencies]
# serialisation
@@ -21,5 +21,10 @@ serde_derive = "^1.0"
rog_anime = { path = "../rog-anime" }
rog_dbus = { path = "../rog-dbus" }
rog_types = { path = "../rog-types" }
dirs = "3.0.1"
dirs = "3.0.1"
zbus = "^1.9.1"
zvariant = "^2.6"
zvariant_derive = "^2.6"

View File

@@ -0,0 +1,342 @@
use rog_anime::{ActionData, AnimTime, AnimeAction, Sequences, Vec2};
use rog_dbus::AuraDbusClient;
//use crate::dbus::DbusEvents;
use serde_derive::{Deserialize, Serialize};
use std::time::Duration;
use std::{
path::Path,
sync::{
atomic::{AtomicBool, Ordering},
Mutex,
},
};
use std::{sync::Arc, thread::sleep, time::Instant};
use zbus::dbus_interface;
use zvariant::ObjectPath;
use zvariant_derive::Type;
use crate::{error::Error, user_config::UserConfig};
#[derive(Debug, Clone, Deserialize, Serialize, Type)]
pub enum TimeType {
Timer,
Count,
Infinite,
}
/// The inner object exists to allow the zbus proxy to share it with a runner thread
/// and a zbus server behind `Arc<Mutex<T>>`
pub struct CtrlAnimeInner<'a> {
sequences: Sequences,
client: AuraDbusClient<'a>,
do_early_return: &'a AtomicBool,
}
impl<'a> CtrlAnimeInner<'static> {
pub fn new(
sequences: Sequences,
client: AuraDbusClient<'static>,
do_early_return: &'static AtomicBool,
) -> Result<Self, Error> {
Ok(Self {
sequences,
client,
do_early_return,
})
}
/// To be called on each main loop iteration to pump out commands to the anime
pub fn run(&self) -> Result<(), Error> {
if self.do_early_return.load(Ordering::SeqCst) {
return Ok(());
}
for action in self.sequences.iter() {
match action {
ActionData::Animation(frames) => {
let mut count = 0;
let start = Instant::now();
'animation: loop {
for frame in frames.frames() {
if self.do_early_return.load(Ordering::SeqCst) {
return Ok(());
}
self.client
.proxies()
.anime()
.write(frame.frame().clone())
.unwrap();
if let AnimTime::Time(time) = frames.duration() {
if Instant::now().duration_since(start) > time {
break 'animation;
}
}
sleep(frame.delay());
}
if let AnimTime::Cycles(times) = frames.duration() {
count += 1;
if count >= times {
break 'animation;
}
}
}
}
ActionData::Image(image) => {
self.client
.proxies()
.anime()
.write(image.as_ref().clone())
.unwrap();
}
ActionData::Pause(duration) => {
let start = Instant::now();
'pause: loop {
if self.do_early_return.load(Ordering::SeqCst) {
return Ok(());
}
if Instant::now().duration_since(start) > *duration {
break 'pause;
}
sleep(Duration::from_millis(1));
}
}
ActionData::AudioEq => {}
ActionData::SystemInfo => {}
ActionData::TimeDate => {}
ActionData::Matrix => {}
}
}
Ok(())
}
}
pub struct CtrlAnime<'a> {
config: Arc<Mutex<UserConfig>>,
client: AuraDbusClient<'a>,
inner: Arc<Mutex<CtrlAnimeInner<'a>>>,
/// Must be the same Atomic as in CtrlAnimeInner
inner_early_return: &'a AtomicBool,
}
impl<'a> CtrlAnime<'static> {
pub fn new(
config: Arc<Mutex<UserConfig>>,
inner: Arc<Mutex<CtrlAnimeInner<'static>>>,
client: AuraDbusClient<'static>,
inner_early_return: &'static AtomicBool,
) -> Result<Self, Error> {
Ok(CtrlAnime {
config,
inner,
client,
inner_early_return,
})
}
pub fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(
&ObjectPath::from_str_unchecked("/org/asuslinux/Anime"),
self,
)
.map_err(|err| {
println!("CtrlAnime: add_to_server {}", err);
err
})
.ok();
}
}
// The pattern for a zbus method is:
// - Get config lock if required
// - Set inner_early_return to stop the inner run loop temporarily
// - Do actions
// - Write config if required
// - Unset inner_early_return
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlAnime<'static> {
pub fn insert_asus_gif(
&mut self,
index: u32,
file: String,
time: TimeType,
count: u32,
brightness: f32,
) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() {
let time: AnimTime = match time {
TimeType::Timer => AnimTime::Time(Duration::from_millis(count as u64)),
TimeType::Count => AnimTime::Cycles(count),
TimeType::Infinite => AnimTime::Infinite,
};
let file = Path::new(&file);
let action = AnimeAction::AsusAnimation {
file: file.into(),
brightness,
time,
};
// Must make the inner run loop return early
self.inner_early_return.store(true, Ordering::SeqCst);
if let Ok(mut controller) = self.inner.lock() {
controller.sequences.insert(index as usize, &action)?;
}
config.anime.push(action);
config.write()?;
let json = serde_json::to_string_pretty(&*config).expect("Parse config to JSON failed");
// Release the inner run loop again
self.inner_early_return.store(false, Ordering::SeqCst);
return Ok(json);
}
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
}
#[allow(clippy::too_many_arguments)]
pub fn insert_image_gif(
&mut self,
index: u32,
file: String,
scale: f32,
angle: f32,
xy: (f32, f32),
time: TimeType,
count: u32,
brightness: f32,
) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() {
let time: AnimTime = match time {
TimeType::Timer => AnimTime::Time(Duration::from_millis(count as u64)),
TimeType::Count => AnimTime::Cycles(count),
TimeType::Infinite => AnimTime::Infinite,
};
let file = Path::new(&file);
let translation = Vec2::new(xy.0, xy.1);
let action = AnimeAction::ImageAnimation {
file: file.into(),
scale,
angle,
translation,
brightness,
time,
};
// Must make the inner run loop return early
self.inner_early_return.store(true, Ordering::SeqCst);
if let Ok(mut controller) = self.inner.lock() {
controller.sequences.insert(index as usize, &action)?;
}
config.anime.push(action);
config.write()?;
let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
// Release the inner run loop again
self.inner_early_return.store(false, Ordering::SeqCst);
return Ok(json);
}
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
}
pub fn insert_image(
&mut self,
index: u32,
file: String,
scale: f32,
angle: f32,
xy: (f32, f32),
brightness: f32,
) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() {
let file = Path::new(&file);
let action = AnimeAction::Image {
file: file.into(),
scale,
angle,
translation: Vec2::new(xy.0, xy.1),
brightness,
};
// Must make the inner run loop return early
self.inner_early_return.store(true, Ordering::SeqCst);
if let Ok(mut controller) = self.inner.lock() {
controller.sequences.insert(index as usize, &action)?;
}
config.anime.push(action);
config.write()?;
let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
// Release the inner run loop again
self.inner_early_return.store(false, Ordering::SeqCst);
return Ok(json);
}
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
}
pub fn insert_pause(&mut self, index: u32, millis: u64) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() {
let action = AnimeAction::Pause(Duration::from_millis(millis));
// Must make the inner run loop return early
self.inner_early_return.store(true, Ordering::SeqCst);
if let Ok(mut controller) = self.inner.lock() {
controller.sequences.insert(index as usize, &action)?;
}
config.anime.push(action);
config.write()?;
let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
// Release the inner run loop again
self.inner_early_return.store(false, Ordering::SeqCst);
return Ok(json);
}
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
}
pub fn remove_item(&mut self, index: u32) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() {
// Must make the inner run loop return early
self.inner_early_return.store(true, Ordering::SeqCst);
if let Ok(mut controller) = self.inner.lock() {
controller.sequences.remove_item(index as usize);
}
if (index as usize) < config.anime.len() {
config.anime.remove(index as usize);
}
config.write()?;
let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
// Release the inner run loop again
self.inner_early_return.store(false, Ordering::SeqCst);
return Ok(json);
}
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
}
pub fn set_state(&mut self, on: bool) -> zbus::fdo::Result<()> {
// Operations here need to be in specific order
if on {
self.client.proxies().anime().toggle_on(on)?;
// Let the inner loop run
self.inner_early_return.store(false, Ordering::SeqCst);
} else {
// Must make the inner run loop return early
self.inner_early_return.store(true, Ordering::SeqCst);
self.client.proxies().anime().toggle_on(on)?;
}
Ok(())
}
}

67
daemon-user/src/daemon.rs Normal file
View File

@@ -0,0 +1,67 @@
use rog_dbus::AuraDbusClient;
use rog_types::supported::SupportedFunctions;
use rog_user::{
ctrl_anime::{CtrlAnime, CtrlAnimeInner},
user_config::*,
DBUS_NAME,
};
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
use zbus::{fdo, Connection};
use std::sync::atomic::AtomicBool;
/// The anime loop needs an atomic to make it exit early if required
static ANIME_INNER_EARLY_RETURN: AtomicBool = AtomicBool::new(false);
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!(" rog-dbus version {}", rog_dbus::VERSION);
let (client, _) = AuraDbusClient::new().unwrap();
let supported = client.proxies().supported().get_supported_functions()?;
let supported = serde_json::from_str::<SupportedFunctions>(&&supported).unwrap();
let mut config = UserConfig::new();
config.load_config()?;
let anime = config.create_anime()?;
let config = Arc::new(Mutex::new(config));
// Create server
let connection = Connection::new_session()?;
fdo::DBusProxy::new(&connection)?
.request_name(DBUS_NAME, fdo::RequestNameFlags::ReplaceExisting.into())?;
let mut server = zbus::ObjectServer::new(&connection);
// Set up the anime data and run loop/thread
if supported.anime_ctrl.0 {
// Inner behind mutex required for thread safety
let inner = Arc::new(Mutex::new(CtrlAnimeInner::new(
anime,
client,
&ANIME_INNER_EARLY_RETURN,
)?));
// Need new client object for dbus control part
let (client, _) = AuraDbusClient::new().unwrap();
let anime_control =
CtrlAnime::new(config, inner.clone(), client, &ANIME_INNER_EARLY_RETURN)?;
anime_control.add_to_server(&mut server);
// Thread using inner
let _anime_thread = thread::Builder::new()
.name("Anime User".into())
.spawn(move || loop {
if let Ok(inner) = inner.try_lock() {
inner.run().unwrap();
}
})?;
}
if supported.keyboard_led.per_key_led_mode {}
loop {
if let Err(err) = server.try_handle_next() {
println!("{}", err);
}
}
}

View File

@@ -6,6 +6,7 @@ use rog_anime::error::AnimeError;
pub enum Error {
Io(std::io::Error),
ConfigLoadFail,
ConfigLockFail,
XdgVars,
Anime(AnimeError),
}
@@ -16,6 +17,7 @@ impl fmt::Display for Error {
match self {
Error::Io(err) => write!(f, "Failed to open: {}", err),
Error::ConfigLoadFail => write!(f, "Failed to load user config"),
Error::ConfigLockFail => write!(f, "Failed to lock user config"),
Error::XdgVars => write!(f, "XDG environment vars appear unset"),
Error::Anime(err) => write!(f, "Anime error: {}", err),
}
@@ -35,3 +37,9 @@ impl From<AnimeError> for Error {
Error::Anime(err)
}
}
impl From<Error> for zbus::fdo::Error {
fn from(err: Error) -> Self {
zbus::fdo::Error::Failed(format!("Anime zbus error: {}", err))
}
}

View File

@@ -1,3 +1,9 @@
pub mod user_config;
pub mod error;
pub mod ctrl_anime;
pub mod zbus_anime;
pub static DBUS_NAME: &str = "org.asuslinux.Daemon";

View File

@@ -1,66 +0,0 @@
use rog_anime::{Action, AnimTime};
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) => {
let mut count = 0;
'animation: loop {
for frame in frames.frames() {
client.proxies().anime().write(frame.frame().clone())?;
if let AnimTime::Time(time) = frames.duration() {
if Instant::now().duration_since(start) > time {
break 'animation;
}
}
sleep(frame.delay());
}
if let AnimTime::Cycles(times) = frames.duration() {
count += 1;
if count >= times {
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 => {}
}
}
}
}

View File

@@ -1,46 +1,17 @@
use std::{
fs::{create_dir, OpenOptions},
io::{Read, Write},
path::PathBuf,
time::Duration,
};
use rog_anime::{AnimTime, Sequences, Vec2};
use rog_anime::{AnimTime, AnimeAction, 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,
time: AnimTime,
brightness: f32,
},
/// Basic image, can have properties changed
ImageAnimation {
file: PathBuf,
scale: f32,
angle: f32,
translation: Vec2,
time: AnimTime,
brightness: f32,
},
Image {
file: PathBuf,
scale: f32,
angle: f32,
translation: Vec2,
brightness: f32,
},
/// A pause to be used between sequences
Pause(Duration),
pub anime: Vec<AnimeAction>,
}
impl UserConfig {
@@ -114,38 +85,35 @@ impl UserConfig {
}
}
Ok(())
//Err(Error::ConfigLoadFail)
}
pub fn write(&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().write(true).create(true).truncate(true).open(&path)?;
let json = serde_json::to_string_pretty(&self).unwrap();
dbg!(&json);
file.write_all(json.as_bytes())?;
Ok(())
}
pub fn create_anime(&self) -> Result<Sequences, Error> {
let mut seq = Sequences::new();
for anime in self.anime.iter() {
match anime {
AnimeAction::AsusAnimation {
file,
time: duration,
brightness,
} => seq.add_asus_gif(&file, *duration, *brightness)?,
AnimeAction::ImageAnimation {
file,
scale,
angle,
translation,
time: 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)?,
}
for (idx, action) in self.anime.iter().enumerate() {
seq.insert(idx, action)?;
}
Ok(seq)

View File

@@ -0,0 +1,68 @@
//! # DBus interface proxy for: `org.asuslinux.Daemon`
//!
//! This code was generated by `zbus-xmlgen` `1.0.0` from DBus introspection data.
//! Source: `Interface '/org/asuslinux/Anime' from service 'org.asuslinux.Daemon' on session bus`.
//!
//! You may prefer to adapt it, instead of using it verbatim.
//!
//! More information can be found in the
//! [Writing a client proxy](https://dbus.pages.freedesktop.org/zbus/client.html)
//! section of the zbus documentation.
//!
//! This DBus object implements
//! [standard DBus interfaces](https://dbus.freedesktop.org/doc/dbus-specification.html),
//! (`org.freedesktop.DBus.*`) for which the following zbus proxies can be used:
//!
//! * [`zbus::fdo::PeerProxy`]
//! * [`zbus::fdo::IntrospectableProxy`]
//! * [`zbus::fdo::PropertiesProxy`]
//!
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
use zbus::dbus_proxy;
#[dbus_proxy(interface = "org.asuslinux.Daemon")]
trait Daemon {
/// InsertAsusGif method
fn insert_asus_gif(
&self,
index: u32,
file: &str,
time: u32,
count: u32,
brightness: f64,
) -> zbus::Result<String>;
/// InsertImage method
fn insert_image(
&self,
index: u32,
file: &str,
scale: f64,
angle: f64,
xy: &(f64, f64),
brightness: f64,
) -> zbus::Result<String>;
/// InsertImageGif method
fn insert_image_gif(
&self,
index: u32,
file: &str,
scale: f64,
angle: f64,
xy: &(f64, f64),
time: u32,
count: u32,
brightness: f64,
) -> zbus::Result<String>;
/// InsertPause method
fn insert_pause(&self, index: u32, millis: u64) -> zbus::Result<String>;
/// RemoveItem method
fn remove_item(&self, index: u32) -> zbus::Result<String>;
/// SetState method
fn set_state(&self, on: bool) -> zbus::Result<()>;
}

View File

@@ -15,6 +15,7 @@ const ON_OFF: u8 = 0x04;
use log::{error, info, warn};
use rog_anime::{AniMeDataBuffer, AniMePacketType};
use rog_types::supported::AnimeSupportedFunctions;
use rusb::{Device, DeviceHandle};
use std::error::Error;
use std::time::Duration;
@@ -23,10 +24,6 @@ use zvariant::ObjectPath;
use crate::GetSupported;
use serde_derive::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct AnimeSupportedFunctions(pub bool);
impl GetSupported for CtrlAnimeDisplay {
type A = AnimeSupportedFunctions;

View File

@@ -1,7 +1,7 @@
use crate::{config::Config, error::RogError, GetSupported};
//use crate::dbus::DbusEvents;
use log::{info, warn};
use serde_derive::{Deserialize, Serialize};
use rog_types::supported::ChargeSupportedFunctions;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;
@@ -12,11 +12,6 @@ use zvariant::ObjectPath;
static BAT_CHARGE_PATH: &str = "/sys/class/power_supply/BAT0/charge_control_end_threshold";
#[derive(Serialize, Deserialize)]
pub struct ChargeSupportedFunctions {
pub charge_level_set: bool,
}
impl GetSupported for CtrlCharge {
type A = ChargeSupportedFunctions;

View File

@@ -1,8 +1,7 @@
use crate::error::RogError;
use crate::{config::Config, GetSupported};
use log::{info, warn};
use rog_types::profile::{FanLevel, Profile, ProfileEvent};
use serde_derive::{Deserialize, Serialize};
use rog_types::{profile::{FanLevel, Profile, ProfileEvent}, supported::FanCpuSupportedFunctions};
use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;
@@ -20,13 +19,6 @@ pub struct CtrlFanAndCpu {
config: Arc<Mutex<Config>>,
}
#[derive(Serialize, Deserialize)]
pub struct FanCpuSupportedFunctions {
pub stock_fan_modes: bool,
pub min_max_freq: bool,
pub fan_curve_set: bool,
}
impl GetSupported for CtrlFanAndCpu {
type A = FanCpuSupportedFunctions;

View File

@@ -10,10 +10,7 @@ use crate::{
laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES},
};
use log::{error, info, warn};
use rog_types::{
aura_modes::{AuraEffect, AuraModeNum, LedBrightness},
LED_MSG_LEN,
};
use rog_types::{LED_MSG_LEN, aura_modes::{AuraEffect, AuraModeNum, LedBrightness}, supported::LedSupportedFunctions};
use std::fs::OpenOptions;
use std::io::{Read, Write};
use std::path::Path;
@@ -24,15 +21,6 @@ use zvariant::ObjectPath;
use crate::GetSupported;
use serde_derive::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct LedSupportedFunctions {
pub brightness_set: bool,
pub stock_led_modes: Option<Vec<AuraModeNum>>,
pub multizone_led_mode: bool,
pub per_key_led_mode: bool,
}
impl GetSupported for CtrlKbdBacklight {
type A = LedSupportedFunctions;

View File

@@ -1,6 +1,6 @@
use crate::{config::Config, error::RogError, GetSupported};
use log::{error, info, warn};
use serde_derive::{Deserialize, Serialize};
use rog_types::supported::RogBiosSupportedFunctions;
use std::fs::OpenOptions;
use std::io::BufRead;
use std::io::{Read, Write};
@@ -23,12 +23,6 @@ pub struct CtrlRogBios {
_config: Arc<Mutex<Config>>,
}
#[derive(Serialize, Deserialize)]
pub struct RogBiosSupportedFunctions {
pub post_sound_toggle: bool,
pub dedicated_gfx_toggle: bool,
}
impl GetSupported for CtrlRogBios {
type A = RogBiosSupportedFunctions;

View File

@@ -4,12 +4,13 @@ use zbus::dbus_interface;
use zvariant::ObjectPath;
use crate::{
ctrl_anime::{AnimeSupportedFunctions, CtrlAnimeDisplay},
ctrl_charge::{ChargeSupportedFunctions, CtrlCharge},
ctrl_fan_cpu::{CtrlFanAndCpu, FanCpuSupportedFunctions},
ctrl_leds::{CtrlKbdBacklight, LedSupportedFunctions},
ctrl_rog_bios::{CtrlRogBios, RogBiosSupportedFunctions},
GetSupported,
ctrl_anime::CtrlAnimeDisplay, ctrl_charge::CtrlCharge, ctrl_fan_cpu::CtrlFanAndCpu,
ctrl_leds::CtrlKbdBacklight, ctrl_rog_bios::CtrlRogBios, GetSupported,
};
use rog_types::supported::{
AnimeSupportedFunctions, ChargeSupportedFunctions, FanCpuSupportedFunctions,
LedSupportedFunctions, RogBiosSupportedFunctions,
};
#[derive(Serialize, Deserialize)]

View File

@@ -17,11 +17,12 @@ gif = "^0.11.2"
serde = "^1.0"
serde_derive = "^1.0"
zbus = "^1.9.1"
zvariant = "^2.5"
zvariant_derive = "^2.5"
glam = { version = "*", features = ["serde"] }
[features]
default = ["zbus"]
zbus = []
default = ["z"]
z = []

View File

@@ -1,5 +1,5 @@
use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "zbus")]
#[cfg(feature = "z")]
use zvariant_derive::Type;
/// The first 7 bytes of a USB packet are accounted for by `USB_PREFIX1` and `USB_PREFIX2`
@@ -16,7 +16,7 @@ const USB_PREFIX2: [u8; 7] = [0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02];
/// The minimal serializable data that can be transferred over wire types.
/// Other data structures in `rog_anime` will convert to this.
#[cfg_attr(feature = "zbus", derive(Type))]
#[cfg_attr(feature = "z", derive(Type))]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AniMeDataBuffer(Vec<u8>);

View File

@@ -3,6 +3,9 @@ use png_pong::decode::Error as PngError;
use std::error::Error;
use std::fmt;
#[cfg(feature = "z")]
use zbus::fdo;
#[derive(Debug)]
pub enum AnimeError {
NoFrames,
@@ -12,6 +15,8 @@ pub enum AnimeError {
Format,
/// The input was incorrect size, expected size is `IncorrectSize(width, height)`
IncorrectSize(u32, u32),
#[cfg(feature = "z")]
Zbus(fdo::Error)
}
impl fmt::Display for AnimeError {
@@ -28,6 +33,8 @@ impl fmt::Display for AnimeError {
"The input image size is incorrect, expected {}x{}",
width, height
),
#[cfg(feature = "z")]
AnimeError::Zbus(e) => write!(f, "ZBUS error: {}", e),
}
}
}
@@ -51,3 +58,10 @@ impl From<DecodingError> for AnimeError {
AnimeError::Gif(err)
}
}
#[cfg(feature = "z")]
impl From<AnimeError> for fdo::Error {
fn from(err: AnimeError) -> Self {
fdo::Error::Failed(format!("{}", err))
}
}

View File

@@ -24,6 +24,7 @@ impl AniMeFrame {
#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
pub enum AnimTime {
/// Time in milliseconds for animation to run
Time(Duration),
Cycles(u32),
Infinite,
@@ -35,7 +36,6 @@ impl Default for AnimTime {
}
}
/// A gif animation. This is a collection of frames from the gif, and a duration
/// that the animation should be shown for.
#[derive(Debug, Clone, Deserialize, Serialize)]

View File

@@ -1,13 +1,44 @@
use std::{path::Path, time::Duration};
use std::{
path::{Path, PathBuf},
time::Duration,
};
use glam::Vec2;
use serde_derive::{Deserialize, Serialize};
use crate::{AniMeDataBuffer, AniMeGif, AniMeImage, AnimTime, error::AnimeError};
use crate::{error::AnimeError, AniMeDataBuffer, AniMeGif, AniMeImage, AnimTime};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum AnimeAction {
/// Full gif sequence. Immutable.
AsusAnimation {
file: PathBuf,
time: AnimTime,
brightness: f32,
},
/// Basic image, can have properties changed
ImageAnimation {
file: PathBuf,
scale: f32,
angle: f32,
translation: Vec2,
time: AnimTime,
brightness: f32,
},
Image {
file: PathBuf,
scale: f32,
angle: f32,
translation: Vec2,
brightness: f32,
},
/// A pause to be used between sequences
Pause(Duration),
}
///
#[derive(Debug, Deserialize, Serialize)]
pub enum Action {
pub enum ActionData {
/// Full gif sequence. Immutable.
Animation(AniMeGif),
/// Basic image, can have properties changed and image updated via those properties
@@ -26,40 +57,92 @@ pub enum Action {
/// An optimised precomputed set of actions
#[derive(Debug, Deserialize, Serialize, Default)]
pub struct Sequences(Vec<Action>);
pub struct Sequences(Vec<ActionData>);
impl Sequences {
pub fn new() -> Self {
Self(Vec::new())
}
pub fn add_asus_gif(
pub fn insert(&mut self, index: usize, action: &AnimeAction) -> Result<(), AnimeError> {
match action {
AnimeAction::AsusAnimation {
file,
time: duration,
brightness,
} => self.insert_asus_gif(index, &file, *duration, *brightness)?,
AnimeAction::ImageAnimation {
file,
scale,
angle,
translation,
time: duration,
brightness,
} => self.insert_image_gif(
index,
&file,
*scale,
*angle,
*translation,
*duration,
*brightness,
)?,
AnimeAction::Image {
file,
scale,
angle,
translation,
brightness,
} => self.insert_png(index, &file, *scale, *angle, *translation, *brightness)?,
AnimeAction::Pause(duration) => self.insert_pause(index, *duration),
};
Ok(())
}
pub fn remove_item(&mut self, index: usize) -> Option<ActionData> {
if index < self.0.len() {
return Some(self.0.remove(index));
}
None
}
fn insert_asus_gif(
&mut self,
mut index: usize,
file: &Path,
duration: AnimTime,
brightness: f32,
) -> Result<(), AnimeError> {
if index > self.0.len() {
index = self.0.len() - 1;
}
let frames = AniMeGif::create_diagonal_gif(file, duration, brightness)?;
self.0.push(Action::Animation(frames));
self.0.insert(index, ActionData::Animation(frames));
Ok(())
}
pub fn add_png(
fn insert_png(
&mut self,
mut index: usize,
file: &Path,
scale: f32,
angle: f32,
translation: Vec2,
brightness: f32,
) -> Result<(), AnimeError> {
if index > self.0.len() {
index = self.0.len() - 1;
}
let image = AniMeImage::from_png(file, scale, angle, translation, brightness)?;
let data = <AniMeDataBuffer>::from(&image);
self.0.push(Action::Image(Box::new(data)));
self.0.insert(index, ActionData::Image(Box::new(data)));
Ok(())
}
pub fn add_image_gif(
#[allow(clippy::too_many_arguments)]
fn insert_image_gif(
&mut self,
mut index: usize,
file: &Path,
scale: f32,
angle: f32,
@@ -67,15 +150,20 @@ impl Sequences {
duration: AnimTime,
brightness: f32,
) -> Result<(), AnimeError> {
if index > self.0.len() {
index = self.0.len() - 1;
}
let frames =
AniMeGif::create_png_gif(file, scale, angle, translation, duration, brightness)?;
self.0.push(Action::Animation(frames));
self.0.insert(index, ActionData::Animation(frames));
Ok(())
}
pub fn add_pause(&mut self, duration: Duration) -> Result<(), AnimeError> {
self.0.push(Action::Pause(duration));
Ok(())
fn insert_pause(&mut self, mut index: usize, duration: Duration) {
if index > self.0.len() {
index = self.0.len() - 1;
}
self.0.insert(index, ActionData::Pause(duration));
}
pub fn iter(&self) -> ActionIterator {
@@ -92,9 +180,9 @@ pub struct ActionIterator<'a> {
}
impl<'a> Iterator for ActionIterator<'a> {
type Item = &'a Action;
type Item = &'a ActionData;
fn next(&mut self) -> Option<&'a Action> {
fn next(&mut self) -> Option<&'a ActionData> {
if self.next_idx == self.actions.0.len() {
self.next_idx = 0;
return None;

View File

@@ -14,5 +14,5 @@ gumdrop = "^0.8"
rog_fan_curve = { version = "^0.1", features = ["serde"] }
serde = "^1.0"
serde_derive = "^1.0"
zvariant = "^2.5"
zvariant_derive = "^2.5"
zvariant = "^2.6"
zvariant_derive = "^2.6"

View File

@@ -16,6 +16,8 @@ pub mod aura_perkey;
pub mod gfx_vendors;
pub mod supported;
pub mod error;
pub static VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@@ -0,0 +1,41 @@
use serde_derive::{Deserialize, Serialize};
use crate::aura_modes::AuraModeNum;
#[derive(Serialize, Deserialize)]
pub struct SupportedFunctions {
pub anime_ctrl: AnimeSupportedFunctions,
pub charge_ctrl: ChargeSupportedFunctions,
pub fan_cpu_ctrl: FanCpuSupportedFunctions,
pub keyboard_led: LedSupportedFunctions,
pub rog_bios_ctrl: RogBiosSupportedFunctions,
}
#[derive(Serialize, Deserialize)]
pub struct AnimeSupportedFunctions(pub bool);
#[derive(Serialize, Deserialize)]
pub struct ChargeSupportedFunctions {
pub charge_level_set: bool,
}
#[derive(Serialize, Deserialize)]
pub struct FanCpuSupportedFunctions {
pub stock_fan_modes: bool,
pub min_max_freq: bool,
pub fan_curve_set: bool,
}
#[derive(Serialize, Deserialize)]
pub struct LedSupportedFunctions {
pub brightness_set: bool,
pub stock_led_modes: Option<Vec<AuraModeNum>>,
pub multizone_led_mode: bool,
pub per_key_led_mode: bool,
}
#[derive(Serialize, Deserialize)]
pub struct RogBiosSupportedFunctions {
pub post_sound_toggle: bool,
pub dedicated_gfx_toggle: bool,
}