mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-01-22 09:23:19 +01:00
Add the missing dirs, dumbarse
This commit is contained in:
35
asusd-user/Cargo.toml
Normal file
35
asusd-user/Cargo.toml
Normal file
@@ -0,0 +1,35 @@
|
||||
[package]
|
||||
name = "asusd-user"
|
||||
license = "MPL-2.0"
|
||||
version.workspace = true
|
||||
authors = ["Luke D Jones <luke@ljones.dev>"]
|
||||
edition = "2021"
|
||||
description = "Usermode daemon for user settings, anime, per-key lighting"
|
||||
|
||||
[[bin]]
|
||||
name = "asusd-user"
|
||||
path = "src/daemon.rs"
|
||||
|
||||
[dependencies]
|
||||
dirs.workspace = true
|
||||
smol.workspace = true
|
||||
|
||||
# serialisation
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde_derive.workspace = true
|
||||
|
||||
rog_anime = { path = "../rog-anime" }
|
||||
rog_aura = { path = "../rog-aura" }
|
||||
rog_dbus = { path = "../rog-dbus" }
|
||||
rog_platform = { path = "../rog-platform" }
|
||||
config-traits = { path = "../config-traits" }
|
||||
|
||||
zbus.workspace = true
|
||||
|
||||
# cli and logging
|
||||
log.workspace = true
|
||||
env_logger.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
cargo-husky.workspace = true
|
||||
14
asusd-user/README.md
Normal file
14
asusd-user/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# daemon-user
|
||||
|
||||
This crate is for the binary of `asusd-user` and its helper lib.
|
||||
|
||||
The purpose of `asusd-user` is to run in userland and provide the user + third-party apps an interface for such things as creating AniMe sequences (and more in future, see todo list).
|
||||
|
||||
`asusd-user` should try to be as simple as possible while allowing a decent degree of control.
|
||||
|
||||
## TODO
|
||||
|
||||
- [ ] CLI for basic settings/interaction
|
||||
- [ ] RGB keyboard per-key programs
|
||||
- [ ] User profiles (fan, cpu etc). These would be replacing the system-daemon profiles only when the user is active, otherwise system-daemon defaults to system settings.
|
||||
- [ ] Audio EQ visualiser - for use with anime + keyboard lighting
|
||||
219
asusd-user/src/config.rs
Normal file
219
asusd-user/src/config.rs
Normal file
@@ -0,0 +1,219 @@
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
use config_traits::{StdConfig, StdConfigLoad};
|
||||
use rog_anime::{ActionLoader, AnimTime, AnimeType, Fade, Sequences as AnimeSequences, Vec2};
|
||||
use rog_aura::advanced::LedCode;
|
||||
use rog_aura::effects::{AdvancedEffects as AuraSequences, Breathe, DoomFlicker, Effect, Static};
|
||||
use rog_aura::{Colour, Speed};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
const ROOT_CONF_DIR: &str = "rog";
|
||||
|
||||
fn root_conf_dir() -> PathBuf {
|
||||
let mut dir = dirs::config_dir().unwrap_or_else(|| PathBuf::from("/tmp"));
|
||||
dir.push(ROOT_CONF_DIR);
|
||||
dir
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct ConfigAnime {
|
||||
pub name: String,
|
||||
pub anime: Vec<ActionLoader>,
|
||||
}
|
||||
|
||||
impl ConfigAnime {
|
||||
pub fn create(&self, anime_type: AnimeType) -> Result<AnimeSequences, Error> {
|
||||
let mut seq = AnimeSequences::new(anime_type);
|
||||
|
||||
for (idx, action) in self.anime.iter().enumerate() {
|
||||
seq.insert(idx, action)?;
|
||||
}
|
||||
|
||||
Ok(seq)
|
||||
}
|
||||
|
||||
pub fn set_name(mut self, name: String) -> Self {
|
||||
self.name = name;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ConfigAnime {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: "anime-default".to_owned(),
|
||||
anime: vec![
|
||||
ActionLoader::AsusImage {
|
||||
file: "/usr/share/asusd/anime/custom/diagonal-template.png".into(),
|
||||
brightness: 1.0,
|
||||
time: AnimTime::Fade(Fade::new(
|
||||
Duration::from_secs(2),
|
||||
None,
|
||||
Duration::from_secs(2),
|
||||
)),
|
||||
},
|
||||
ActionLoader::AsusAnimation {
|
||||
file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(),
|
||||
brightness: 0.5,
|
||||
time: AnimTime::Fade(Fade::new(
|
||||
Duration::from_secs(6),
|
||||
None,
|
||||
Duration::from_secs(3),
|
||||
)),
|
||||
},
|
||||
ActionLoader::ImageAnimation {
|
||||
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
|
||||
scale: 0.9,
|
||||
angle: 0.65,
|
||||
translation: Vec2::default(),
|
||||
brightness: 0.5,
|
||||
time: AnimTime::Fade(Fade::new(
|
||||
Duration::from_secs(2),
|
||||
Some(Duration::from_secs(2)),
|
||||
Duration::from_secs(2),
|
||||
)),
|
||||
},
|
||||
ActionLoader::Image {
|
||||
file: "/usr/share/asusd/anime/custom/rust.png".into(),
|
||||
scale: 1.0,
|
||||
angle: 0.0,
|
||||
translation: Vec2::default(),
|
||||
time: AnimTime::Fade(Fade::new(
|
||||
Duration::from_secs(2),
|
||||
Some(Duration::from_secs(1)),
|
||||
Duration::from_secs(2),
|
||||
)),
|
||||
brightness: 0.6,
|
||||
},
|
||||
ActionLoader::Pause(Duration::from_secs(1)),
|
||||
ActionLoader::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,
|
||||
time: AnimTime::Count(2),
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfig for ConfigAnime {
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn file_name(&self) -> String {
|
||||
format!("{}.ron", self.name)
|
||||
}
|
||||
|
||||
fn config_dir() -> std::path::PathBuf {
|
||||
root_conf_dir()
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfigLoad for ConfigAnime {}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct ConfigAura {
|
||||
pub name: String,
|
||||
pub aura: AuraSequences,
|
||||
}
|
||||
|
||||
impl ConfigAura {
|
||||
pub fn set_name(mut self, name: String) -> Self {
|
||||
self.name = name;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ConfigAura {
|
||||
fn default() -> Self {
|
||||
let mut seq = AuraSequences::new(false);
|
||||
let mut key = Effect::Breathe(Breathe::new(
|
||||
LedCode::W,
|
||||
Colour(255, 0, 20),
|
||||
Colour(20, 255, 0),
|
||||
Speed::Low,
|
||||
));
|
||||
|
||||
seq.push(key.clone());
|
||||
key.set_led(LedCode::A);
|
||||
seq.push(key.clone());
|
||||
key.set_led(LedCode::S);
|
||||
seq.push(key.clone());
|
||||
key.set_led(LedCode::D);
|
||||
seq.push(key);
|
||||
|
||||
let key = Effect::Breathe(Breathe::new(
|
||||
LedCode::F,
|
||||
Colour(255, 0, 0),
|
||||
Colour(255, 0, 0),
|
||||
Speed::High,
|
||||
));
|
||||
seq.push(key);
|
||||
|
||||
let mut key = Effect::Static(Static::new(LedCode::RCtrl, Colour(0, 0, 255)));
|
||||
seq.push(key.clone());
|
||||
key.set_led(LedCode::LCtrl);
|
||||
seq.push(key.clone());
|
||||
key.set_led(LedCode::Esc);
|
||||
seq.push(key);
|
||||
|
||||
let key = Effect::DoomFlicker(DoomFlicker::new(LedCode::N9, Colour(0, 0, 255), 80, 40));
|
||||
seq.push(key);
|
||||
|
||||
Self {
|
||||
name: "aura-default".to_owned(),
|
||||
aura: seq,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfig for ConfigAura {
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn file_name(&self) -> String {
|
||||
format!("{}.ron", self.name)
|
||||
}
|
||||
|
||||
fn config_dir() -> std::path::PathBuf {
|
||||
root_conf_dir()
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfigLoad for ConfigAura {}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct ConfigBase {
|
||||
/// Name of active anime config file in the user config directory
|
||||
pub active_anime: Option<String>,
|
||||
/// Name of active aura config file in the user config directory
|
||||
pub active_aura: Option<String>,
|
||||
}
|
||||
|
||||
impl StdConfig for ConfigBase {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
active_anime: Some("anime-default".to_owned()),
|
||||
active_aura: Some("aura-default".to_owned()),
|
||||
}
|
||||
}
|
||||
|
||||
fn file_name(&self) -> String {
|
||||
"rog-user.ron".to_owned()
|
||||
}
|
||||
|
||||
fn config_dir() -> std::path::PathBuf {
|
||||
root_conf_dir()
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfigLoad for ConfigBase {}
|
||||
373
asusd-user/src/ctrl_anime.rs
Normal file
373
asusd-user/src/ctrl_anime.rs
Normal file
@@ -0,0 +1,373 @@
|
||||
use std::path::Path;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread::sleep;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use config_traits::StdConfig;
|
||||
use rog_anime::error::AnimeError;
|
||||
use rog_anime::{ActionData, ActionLoader, AnimTime, Fade, Sequences, Vec2};
|
||||
use rog_dbus::RogDbusClientBlocking;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use zbus::dbus_interface;
|
||||
use zbus::zvariant::{ObjectPath, Type};
|
||||
|
||||
use crate::config::ConfigAnime;
|
||||
use crate::error::Error;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, Type)]
|
||||
pub struct Timer {
|
||||
type_of: TimeType,
|
||||
/// If time type is Timer then this is milliseonds, otherwise it is
|
||||
/// animation loop count
|
||||
count: u64,
|
||||
/// Used only for `TimeType::Timer`, milliseonds to fade the image in for
|
||||
fade_in: Option<u64>,
|
||||
/// Used only for `TimeType::Timer`, milliseonds to fade the image out for
|
||||
fade_out: Option<u64>,
|
||||
}
|
||||
|
||||
impl From<Timer> for AnimTime {
|
||||
fn from(time: Timer) -> Self {
|
||||
match time.type_of {
|
||||
TimeType::Timer => {
|
||||
if time.fade_in.is_some() || time.fade_out.is_some() {
|
||||
let fade_in = time
|
||||
.fade_in
|
||||
.map_or(Duration::from_secs(0), Duration::from_millis);
|
||||
let fade_out = time
|
||||
.fade_out
|
||||
.map_or(Duration::from_secs(0), Duration::from_millis);
|
||||
let show_for = if time.count != 0 {
|
||||
Some(Duration::from_millis(time.count))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
AnimTime::Fade(Fade::new(fade_in, show_for, fade_out))
|
||||
} else {
|
||||
AnimTime::Time(Duration::from_millis(time.count))
|
||||
}
|
||||
}
|
||||
TimeType::Count => AnimTime::Count(time.count as u32),
|
||||
TimeType::Infinite => AnimTime::Infinite,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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: RogDbusClientBlocking<'a>,
|
||||
do_early_return: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl<'a> CtrlAnimeInner<'static> {
|
||||
pub fn new(
|
||||
sequences: Sequences,
|
||||
client: RogDbusClientBlocking<'static>,
|
||||
do_early_return: Arc<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(&'a self) -> Result<(), Error> {
|
||||
if self.do_early_return.load(Ordering::SeqCst) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for action in self.sequences.iter() {
|
||||
match action {
|
||||
ActionData::Animation(frames) => {
|
||||
rog_anime::run_animation(frames, &|output| {
|
||||
if self.do_early_return.load(Ordering::Acquire) {
|
||||
return Ok(true); // Do safe exit
|
||||
}
|
||||
self.client
|
||||
.proxies()
|
||||
.anime()
|
||||
.write(output)
|
||||
.map_err(|e| AnimeError::Dbus(format!("{}", e)))
|
||||
.map(|_| false)
|
||||
});
|
||||
}
|
||||
ActionData::Image(image) => {
|
||||
self.client
|
||||
.proxies()
|
||||
.anime()
|
||||
.write(image.as_ref().clone())
|
||||
.ok();
|
||||
}
|
||||
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<ConfigAnime>>,
|
||||
client: RogDbusClientBlocking<'a>,
|
||||
inner: Arc<Mutex<CtrlAnimeInner<'a>>>,
|
||||
/// Must be the same Atomic as in CtrlAnimeInner
|
||||
inner_early_return: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl CtrlAnime<'static> {
|
||||
pub fn new(
|
||||
config: Arc<Mutex<ConfigAnime>>,
|
||||
inner: Arc<Mutex<CtrlAnimeInner<'static>>>,
|
||||
client: RogDbusClientBlocking<'static>,
|
||||
inner_early_return: Arc<AtomicBool>,
|
||||
) -> Result<Self, Error> {
|
||||
Ok(CtrlAnime {
|
||||
config,
|
||||
client,
|
||||
inner,
|
||||
inner_early_return,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn add_to_server(self, server: &mut zbus::Connection) {
|
||||
server
|
||||
.object_server()
|
||||
.at(
|
||||
&ObjectPath::from_str_unchecked("/org/asuslinux/Anime"),
|
||||
self,
|
||||
)
|
||||
.await
|
||||
.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: &str,
|
||||
time: Timer,
|
||||
brightness: f32,
|
||||
) -> zbus::fdo::Result<String> {
|
||||
if let Ok(mut config) = self.config.try_lock() {
|
||||
let time: AnimTime = time.into();
|
||||
let file = Path::new(&file);
|
||||
let action = ActionLoader::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)
|
||||
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
|
||||
}
|
||||
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: &str,
|
||||
scale: f32,
|
||||
angle: f32,
|
||||
xy: (f32, f32),
|
||||
time: Timer,
|
||||
brightness: f32,
|
||||
) -> zbus::fdo::Result<String> {
|
||||
if let Ok(mut config) = self.config.try_lock() {
|
||||
let time: AnimTime = time.into();
|
||||
let file = Path::new(&file);
|
||||
let translation = Vec2::new(xy.0, xy.1);
|
||||
let action = ActionLoader::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)
|
||||
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
|
||||
}
|
||||
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()))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn insert_image(
|
||||
&mut self,
|
||||
index: u32,
|
||||
file: &str,
|
||||
scale: f32,
|
||||
angle: f32,
|
||||
xy: (f32, f32),
|
||||
time: Timer,
|
||||
brightness: f32,
|
||||
) -> zbus::fdo::Result<String> {
|
||||
if let Ok(mut config) = self.config.try_lock() {
|
||||
let file = Path::new(&file);
|
||||
let time = time.into();
|
||||
let action = ActionLoader::Image {
|
||||
file: file.into(),
|
||||
scale,
|
||||
angle,
|
||||
translation: Vec2::new(xy.0, xy.1),
|
||||
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)
|
||||
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
|
||||
}
|
||||
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 = ActionLoader::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)
|
||||
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
|
||||
}
|
||||
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().set_enable_display(on).ok();
|
||||
// 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().set_enable_display(on).ok();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
116
asusd-user/src/daemon.rs
Normal file
116
asusd-user/src/daemon.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use asusd_user::config::*;
|
||||
use asusd_user::ctrl_anime::{CtrlAnime, CtrlAnimeInner};
|
||||
use asusd_user::DBUS_NAME;
|
||||
use config_traits::{StdConfig, StdConfigLoad};
|
||||
use rog_anime::usb::get_anime_type;
|
||||
use rog_aura::aura_detection::LaptopLedData;
|
||||
use rog_aura::layouts::KeyLayout;
|
||||
use rog_dbus::RogDbusClientBlocking;
|
||||
use smol::Executor;
|
||||
use zbus::Connection;
|
||||
|
||||
#[cfg(not(feature = "local_data"))]
|
||||
const DATA_DIR: &str = "/usr/share/rog-gui/";
|
||||
#[cfg(feature = "local_data")]
|
||||
const DATA_DIR: &str = env!("CARGO_MANIFEST_DIR");
|
||||
const BOARD_NAME: &str = "/sys/class/dmi/id/board_name";
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut logger = env_logger::Builder::new();
|
||||
logger
|
||||
.parse_default_env()
|
||||
.target(env_logger::Target::Stdout)
|
||||
.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()))
|
||||
.init();
|
||||
|
||||
println!(" user daemon v{}", asusd_user::VERSION);
|
||||
println!(" rog-anime v{}", rog_anime::VERSION);
|
||||
println!(" rog-dbus v{}", rog_dbus::VERSION);
|
||||
println!("rog-platform v{}", rog_platform::VERSION);
|
||||
|
||||
let (client, _) = RogDbusClientBlocking::new()?;
|
||||
let supported = client.proxies().supported().supported_functions()?;
|
||||
|
||||
let config = ConfigBase::new().load();
|
||||
|
||||
let executor = Executor::new();
|
||||
|
||||
let early_return = Arc::new(AtomicBool::new(false));
|
||||
// Set up the anime data and run loop/thread
|
||||
if supported.anime_ctrl.0 {
|
||||
if let Some(cfg) = config.active_anime {
|
||||
let anime_type = get_anime_type()?;
|
||||
let anime_config = ConfigAnime::new().set_name(cfg).load();
|
||||
let anime = anime_config.create(anime_type)?;
|
||||
let anime_config = Arc::new(Mutex::new(anime_config));
|
||||
|
||||
executor
|
||||
.spawn(async move {
|
||||
// Create server
|
||||
let mut connection = Connection::session().await.unwrap();
|
||||
connection.request_name(DBUS_NAME).await.unwrap();
|
||||
|
||||
// Inner behind mutex required for thread safety
|
||||
let inner = Arc::new(Mutex::new(
|
||||
CtrlAnimeInner::new(anime, client, early_return.clone()).unwrap(),
|
||||
));
|
||||
// Need new client object for dbus control part
|
||||
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||
let anime_control =
|
||||
CtrlAnime::new(anime_config, inner.clone(), client, early_return).unwrap();
|
||||
anime_control.add_to_server(&mut connection).await;
|
||||
loop {
|
||||
if let Ok(inner) = inner.clone().try_lock() {
|
||||
inner.run().ok();
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
|
||||
// if supported.keyboard_led.per_key_led_mode {
|
||||
if let Some(cfg) = config.active_aura {
|
||||
let mut aura_config = ConfigAura::new().set_name(cfg).load();
|
||||
// let baord_name = std::fs::read_to_string(BOARD_NAME)?;
|
||||
|
||||
let led_support = LaptopLedData::get_data();
|
||||
|
||||
let layout = KeyLayout::find_layout(led_support, PathBuf::from(DATA_DIR))
|
||||
.map_err(|e| {
|
||||
println!("{BOARD_NAME}, {e}");
|
||||
})
|
||||
.unwrap_or_else(|_| KeyLayout::default_layout());
|
||||
|
||||
executor
|
||||
.spawn(async move {
|
||||
// Create server
|
||||
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||
// let connection = Connection::session().await.unwrap();
|
||||
// connection.request_name(DBUS_NAME).await.unwrap();
|
||||
|
||||
loop {
|
||||
aura_config.aura.next_state(&layout);
|
||||
let packets = aura_config.aura.create_packets();
|
||||
|
||||
client
|
||||
.proxies()
|
||||
.led()
|
||||
.direct_addressing_raw(packets)
|
||||
.unwrap();
|
||||
std::thread::sleep(std::time::Duration::from_millis(33));
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
// }
|
||||
|
||||
loop {
|
||||
smol::block_on(executor.tick());
|
||||
}
|
||||
}
|
||||
45
asusd-user/src/error.rs
Normal file
45
asusd-user/src/error.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use std::fmt;
|
||||
|
||||
use rog_anime::error::AnimeError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Io(std::io::Error),
|
||||
ConfigLoadFail,
|
||||
ConfigLockFail,
|
||||
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::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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for zbus::fdo::Error {
|
||||
fn from(err: Error) -> Self {
|
||||
zbus::fdo::Error::Failed(format!("Anime zbus error: {}", err))
|
||||
}
|
||||
}
|
||||
11
asusd-user/src/lib.rs
Normal file
11
asusd-user/src/lib.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
pub mod config;
|
||||
|
||||
pub mod error;
|
||||
|
||||
pub mod ctrl_anime;
|
||||
|
||||
pub mod zbus_anime;
|
||||
|
||||
pub static DBUS_NAME: &str = "org.asuslinux.Daemon";
|
||||
|
||||
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
70
asusd-user/src/zbus_anime.rs
Normal file
70
asusd-user/src/zbus_anime.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
//! # `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.
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
|
||||
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<()>;
|
||||
}
|
||||
Reference in New Issue
Block a user