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 - Revert zbus to 1.9.1
- Use enum to show power states, and catch missing pci path for nvidia. - 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. - 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 # [3.3.0] - 2021-04-3
### Changed ### Changed

5
Cargo.lock generated
View File

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

View File

@@ -2,6 +2,7 @@ use notify_rust::{Hint, Notification, NotificationHandle};
use rog_dbus::{DbusProxies, Signals}; use rog_dbus::{DbusProxies, Signals};
use rog_types::profile::Profile; use rog_types::profile::Profile;
use std::error::Error; use std::error::Error;
use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
fn main() -> Result<(), Box<dyn std::error::Error>> { 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 mut last_chrg_notif: Option<NotificationHandle> = None;
let recv = proxies.setup_recv(conn); let recv = proxies.setup_recv(conn);
let mut err_count = 0;
loop { loop {
std::thread::sleep(Duration::from_millis(100)); sleep(Duration::from_millis(100));
recv.next_signal().unwrap(); 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 Ok(mut lock) = signals.gfx_vendor.lock() {
if let Some(vendor) = lock.take() { if let Some(vendor) = lock.take() {

View File

@@ -1,6 +1,6 @@
use std::{env, path::Path, thread::sleep}; use std::{env, path::Path, thread::sleep};
use rog_anime::{Action, Sequences}; use rog_anime::{ActionData, AnimeAction, Sequences};
use rog_dbus::AuraDbusClient; use rog_dbus::AuraDbusClient;
fn main() { fn main() {
@@ -15,11 +15,19 @@ fn main() {
let path = Path::new(&args[1]); let path = Path::new(&args[1]);
let brightness = args[2].parse::<f32>().unwrap(); let brightness = args[2].parse::<f32>().unwrap();
let mut seq = Sequences::new(); 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 { loop {
for action in seq.iter() { for action in seq.iter() {
if let Action::Animation(frames) = action { if let ActionData::Animation(frames) = action {
for frame in frames.frames() { for frame in frames.frames() {
client client
.proxies() .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 crate::aura_cli::{LedBrightness, SetAuraBuiltin};
use anime_cli::{AniMeActions, AniMeCommand}; 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 gumdrop::{Opt, Options};
use rog_anime::{AniMeDataBuffer, AniMeImage, Vec2, ANIME_DATA_LEN}; use rog_anime::{AniMeDataBuffer, AniMeImage, Vec2, ANIME_DATA_LEN};
use rog_dbus::AuraDbusClient; use rog_dbus::AuraDbusClient;
@@ -14,6 +10,10 @@ use rog_types::{
aura_modes::{self, AuraEffect, AuraModeNum}, aura_modes::{self, AuraEffect, AuraModeNum},
gfx_vendors::GfxVendors, gfx_vendors::GfxVendors,
profile::{FanLevel, ProfileCommand, ProfileEvent}, profile::{FanLevel, ProfileCommand, ProfileEvent},
supported::{
FanCpuSupportedFunctions, LedSupportedFunctions, RogBiosSupportedFunctions,
SupportedFunctions,
},
}; };
use std::{env::args, path::Path}; use std::{env::args, path::Path};
use yansi_term::Colour::Green; use yansi_term::Colour::Green;

View File

@@ -11,7 +11,7 @@ path = "src/lib.rs"
[[bin]] [[bin]]
name = "asusd-user" name = "asusd-user"
path = "src/main.rs" path = "src/daemon.rs"
[dependencies] [dependencies]
# serialisation # serialisation
@@ -21,5 +21,10 @@ serde_derive = "^1.0"
rog_anime = { path = "../rog-anime" } rog_anime = { path = "../rog-anime" }
rog_dbus = { path = "../rog-dbus" } 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 { pub enum Error {
Io(std::io::Error), Io(std::io::Error),
ConfigLoadFail, ConfigLoadFail,
ConfigLockFail,
XdgVars, XdgVars,
Anime(AnimeError), Anime(AnimeError),
} }
@@ -16,6 +17,7 @@ impl fmt::Display for Error {
match self { match self {
Error::Io(err) => write!(f, "Failed to open: {}", err), Error::Io(err) => write!(f, "Failed to open: {}", err),
Error::ConfigLoadFail => write!(f, "Failed to load user config"), 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::XdgVars => write!(f, "XDG environment vars appear unset"),
Error::Anime(err) => write!(f, "Anime error: {}", err), Error::Anime(err) => write!(f, "Anime error: {}", err),
} }
@@ -35,3 +37,9 @@ impl From<AnimeError> for Error {
Error::Anime(err) 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 user_config;
pub mod error; 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::{ use std::{
fs::{create_dir, OpenOptions}, fs::{create_dir, OpenOptions},
io::{Read, Write}, io::{Read, Write},
path::PathBuf,
time::Duration, time::Duration,
}; };
use rog_anime::{AnimTime, Sequences, Vec2}; use rog_anime::{AnimTime, AnimeAction, Sequences, Vec2};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use crate::error::Error; use crate::error::Error;
#[derive(Debug, Default, Deserialize, Serialize)] #[derive(Debug, Default, Deserialize, Serialize)]
pub struct UserConfig { pub struct UserConfig {
anime: Vec<AnimeAction>, pub 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),
} }
impl UserConfig { impl UserConfig {
@@ -114,38 +85,35 @@ impl UserConfig {
} }
} }
Ok(()) 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> { pub fn create_anime(&self) -> Result<Sequences, Error> {
let mut seq = Sequences::new(); let mut seq = Sequences::new();
for anime in self.anime.iter() { for (idx, action) in self.anime.iter().enumerate() {
match anime { seq.insert(idx, action)?;
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)?,
}
} }
Ok(seq) 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 log::{error, info, warn};
use rog_anime::{AniMeDataBuffer, AniMePacketType}; use rog_anime::{AniMeDataBuffer, AniMePacketType};
use rog_types::supported::AnimeSupportedFunctions;
use rusb::{Device, DeviceHandle}; use rusb::{Device, DeviceHandle};
use std::error::Error; use std::error::Error;
use std::time::Duration; use std::time::Duration;
@@ -23,10 +24,6 @@ use zvariant::ObjectPath;
use crate::GetSupported; use crate::GetSupported;
use serde_derive::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct AnimeSupportedFunctions(pub bool);
impl GetSupported for CtrlAnimeDisplay { impl GetSupported for CtrlAnimeDisplay {
type A = AnimeSupportedFunctions; type A = AnimeSupportedFunctions;

View File

@@ -1,7 +1,7 @@
use crate::{config::Config, error::RogError, GetSupported}; use crate::{config::Config, error::RogError, GetSupported};
//use crate::dbus::DbusEvents; //use crate::dbus::DbusEvents;
use log::{info, warn}; use log::{info, warn};
use serde_derive::{Deserialize, Serialize}; use rog_types::supported::ChargeSupportedFunctions;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io::Write; use std::io::Write;
use std::path::Path; 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"; 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 { impl GetSupported for CtrlCharge {
type A = ChargeSupportedFunctions; type A = ChargeSupportedFunctions;

View File

@@ -1,8 +1,7 @@
use crate::error::RogError; use crate::error::RogError;
use crate::{config::Config, GetSupported}; use crate::{config::Config, GetSupported};
use log::{info, warn}; use log::{info, warn};
use rog_types::profile::{FanLevel, Profile, ProfileEvent}; use rog_types::{profile::{FanLevel, Profile, ProfileEvent}, supported::FanCpuSupportedFunctions};
use serde_derive::{Deserialize, Serialize};
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io::Write; use std::io::Write;
use std::path::Path; use std::path::Path;
@@ -20,13 +19,6 @@ pub struct CtrlFanAndCpu {
config: Arc<Mutex<Config>>, 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 { impl GetSupported for CtrlFanAndCpu {
type A = FanCpuSupportedFunctions; type A = FanCpuSupportedFunctions;

View File

@@ -10,10 +10,7 @@ use crate::{
laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES}, laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES},
}; };
use log::{error, info, warn}; use log::{error, info, warn};
use rog_types::{ use rog_types::{LED_MSG_LEN, aura_modes::{AuraEffect, AuraModeNum, LedBrightness}, supported::LedSupportedFunctions};
aura_modes::{AuraEffect, AuraModeNum, LedBrightness},
LED_MSG_LEN,
};
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::path::Path; use std::path::Path;
@@ -24,15 +21,6 @@ use zvariant::ObjectPath;
use crate::GetSupported; 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 { impl GetSupported for CtrlKbdBacklight {
type A = LedSupportedFunctions; type A = LedSupportedFunctions;

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,6 +3,9 @@ use png_pong::decode::Error as PngError;
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
#[cfg(feature = "z")]
use zbus::fdo;
#[derive(Debug)] #[derive(Debug)]
pub enum AnimeError { pub enum AnimeError {
NoFrames, NoFrames,
@@ -12,6 +15,8 @@ pub enum AnimeError {
Format, Format,
/// The input was incorrect size, expected size is `IncorrectSize(width, height)` /// The input was incorrect size, expected size is `IncorrectSize(width, height)`
IncorrectSize(u32, u32), IncorrectSize(u32, u32),
#[cfg(feature = "z")]
Zbus(fdo::Error)
} }
impl fmt::Display for AnimeError { impl fmt::Display for AnimeError {
@@ -28,6 +33,8 @@ impl fmt::Display for AnimeError {
"The input image size is incorrect, expected {}x{}", "The input image size is incorrect, expected {}x{}",
width, height 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) 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)] #[derive(Debug, Copy, Clone, Deserialize, Serialize)]
pub enum AnimTime { pub enum AnimTime {
/// Time in milliseconds for animation to run
Time(Duration), Time(Duration),
Cycles(u32), Cycles(u32),
Infinite, Infinite,
@@ -35,7 +36,6 @@ impl Default for AnimTime {
} }
} }
/// A gif animation. This is a collection of frames from the gif, and a duration /// A gif animation. This is a collection of frames from the gif, and a duration
/// that the animation should be shown for. /// that the animation should be shown for.
#[derive(Debug, Clone, Deserialize, Serialize)] #[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 glam::Vec2;
use serde_derive::{Deserialize, Serialize}; 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)] #[derive(Debug, Deserialize, Serialize)]
pub enum Action { pub enum ActionData {
/// Full gif sequence. Immutable. /// Full gif sequence. Immutable.
Animation(AniMeGif), Animation(AniMeGif),
/// Basic image, can have properties changed and image updated via those properties /// 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 /// An optimised precomputed set of actions
#[derive(Debug, Deserialize, Serialize, Default)] #[derive(Debug, Deserialize, Serialize, Default)]
pub struct Sequences(Vec<Action>); pub struct Sequences(Vec<ActionData>);
impl Sequences { impl Sequences {
pub fn new() -> Self { pub fn new() -> Self {
Self(Vec::new()) 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 self,
mut index: usize,
file: &Path, file: &Path,
duration: AnimTime, duration: AnimTime,
brightness: f32, brightness: f32,
) -> Result<(), AnimeError> { ) -> Result<(), AnimeError> {
if index > self.0.len() {
index = self.0.len() - 1;
}
let frames = AniMeGif::create_diagonal_gif(file, duration, brightness)?; let frames = AniMeGif::create_diagonal_gif(file, duration, brightness)?;
self.0.push(Action::Animation(frames)); self.0.insert(index, ActionData::Animation(frames));
Ok(()) Ok(())
} }
pub fn add_png( fn insert_png(
&mut self, &mut self,
mut index: usize,
file: &Path, file: &Path,
scale: f32, scale: f32,
angle: f32, angle: f32,
translation: Vec2, translation: Vec2,
brightness: f32, brightness: f32,
) -> Result<(), AnimeError> { ) -> Result<(), AnimeError> {
if index > self.0.len() {
index = self.0.len() - 1;
}
let image = AniMeImage::from_png(file, scale, angle, translation, brightness)?; let image = AniMeImage::from_png(file, scale, angle, translation, brightness)?;
let data = <AniMeDataBuffer>::from(&image); let data = <AniMeDataBuffer>::from(&image);
self.0.push(Action::Image(Box::new(data))); self.0.insert(index, ActionData::Image(Box::new(data)));
Ok(()) Ok(())
} }
pub fn add_image_gif( #[allow(clippy::too_many_arguments)]
fn insert_image_gif(
&mut self, &mut self,
mut index: usize,
file: &Path, file: &Path,
scale: f32, scale: f32,
angle: f32, angle: f32,
@@ -67,15 +150,20 @@ impl Sequences {
duration: AnimTime, duration: AnimTime,
brightness: f32, brightness: f32,
) -> Result<(), AnimeError> { ) -> Result<(), AnimeError> {
if index > self.0.len() {
index = self.0.len() - 1;
}
let frames = let frames =
AniMeGif::create_png_gif(file, scale, angle, translation, duration, brightness)?; AniMeGif::create_png_gif(file, scale, angle, translation, duration, brightness)?;
self.0.push(Action::Animation(frames)); self.0.insert(index, ActionData::Animation(frames));
Ok(()) Ok(())
} }
pub fn add_pause(&mut self, duration: Duration) -> Result<(), AnimeError> { fn insert_pause(&mut self, mut index: usize, duration: Duration) {
self.0.push(Action::Pause(duration)); if index > self.0.len() {
Ok(()) index = self.0.len() - 1;
}
self.0.insert(index, ActionData::Pause(duration));
} }
pub fn iter(&self) -> ActionIterator { pub fn iter(&self) -> ActionIterator {
@@ -92,9 +180,9 @@ pub struct ActionIterator<'a> {
} }
impl<'a> Iterator for 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() { if self.next_idx == self.actions.0.len() {
self.next_idx = 0; self.next_idx = 0;
return None; return None;

View File

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

View File

@@ -16,6 +16,8 @@ pub mod aura_perkey;
pub mod gfx_vendors; pub mod gfx_vendors;
pub mod supported;
pub mod error; pub mod error;
pub static VERSION: &str = env!("CARGO_PKG_VERSION"); 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,
}