mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
anime: initial system config work
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
use rog_dbus::AuraDbusClient;
|
|
||||||
use rog_aura::{GX502Layout, Key, KeyColourArray, KeyLayout};
|
use rog_aura::{GX502Layout, Key, KeyColourArray, KeyLayout};
|
||||||
|
use rog_dbus::AuraDbusClient;
|
||||||
use std::collections::LinkedList;
|
use std::collections::LinkedList;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use rog_dbus::AuraDbusClient;
|
|
||||||
use rog_aura::{GX502Layout, KeyColourArray, KeyLayout};
|
use rog_aura::{GX502Layout, KeyColourArray, KeyLayout};
|
||||||
|
use rog_dbus::AuraDbusClient;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let (dbus, _) = AuraDbusClient::new()?;
|
let (dbus, _) = AuraDbusClient::new()?;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use rog_dbus::AuraDbusClient;
|
|
||||||
use rog_aura::{GX502Layout, Key, KeyColourArray, KeyLayout};
|
use rog_aura::{GX502Layout, Key, KeyColourArray, KeyLayout};
|
||||||
|
use rog_dbus::AuraDbusClient;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let (dbus, _) = AuraDbusClient::new()?;
|
let (dbus, _) = AuraDbusClient::new()?;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use rog_dbus::AuraDbusClient;
|
|
||||||
use rog_aura::{Key, KeyColourArray};
|
use rog_aura::{Key, KeyColourArray};
|
||||||
|
use rog_dbus::AuraDbusClient;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let (dbus, _) = AuraDbusClient::new()?;
|
let (dbus, _) = AuraDbusClient::new()?;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use rog_dbus::AuraDbusClient;
|
|
||||||
use rog_aura::{GX502Layout, KeyColourArray, KeyLayout};
|
use rog_aura::{GX502Layout, KeyColourArray, KeyLayout};
|
||||||
|
use rog_dbus::AuraDbusClient;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let (dbus, _) = AuraDbusClient::new()?;
|
let (dbus, _) = AuraDbusClient::new()?;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use gumdrop::Options;
|
use gumdrop::Options;
|
||||||
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed, error::Error};
|
use rog_aura::{error::Error, AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Options)]
|
#[derive(Options)]
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ use crate::aura_cli::{LedBrightness, SetAuraBuiltin};
|
|||||||
use anime_cli::{AnimeActions, AnimeCommand};
|
use anime_cli::{AnimeActions, AnimeCommand};
|
||||||
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_aura::{self, AuraEffect, AuraModeNum};
|
use rog_aura::{self, AuraEffect, AuraModeNum};
|
||||||
|
use rog_dbus::AuraDbusClient;
|
||||||
use rog_types::{
|
use rog_types::{
|
||||||
gfx_vendors::GfxVendors,
|
gfx_vendors::GfxVendors,
|
||||||
profile::{FanLevel, ProfileCommand, ProfileEvent},
|
profile::{FanLevel, ProfileCommand, ProfileEvent},
|
||||||
|
|||||||
@@ -147,7 +147,6 @@ impl<'a> CtrlAnime<'static> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// The pattern for a zbus method is:
|
// The pattern for a zbus method is:
|
||||||
// - Get config lock if required
|
// - Get config lock if required
|
||||||
// - Set inner_early_return to stop the inner run loop temporarily
|
// - Set inner_early_return to stop the inner run loop temporarily
|
||||||
|
|||||||
@@ -6,4 +6,4 @@ pub mod ctrl_anime;
|
|||||||
|
|
||||||
pub mod zbus_anime;
|
pub mod zbus_anime;
|
||||||
|
|
||||||
pub static DBUS_NAME: &str = "org.asuslinux.Daemon";
|
pub static DBUS_NAME: &str = "org.asuslinux.Daemon";
|
||||||
|
|||||||
@@ -101,7 +101,11 @@ impl UserConfig {
|
|||||||
|
|
||||||
path.push("rog-user.cfg");
|
path.push("rog-user.cfg");
|
||||||
|
|
||||||
let mut file = OpenOptions::new().write(true).create(true).truncate(true).open(&path)?;
|
let mut file = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.truncate(true)
|
||||||
|
.open(&path)?;
|
||||||
|
|
||||||
let json = serde_json::to_string_pretty(&self).unwrap();
|
let json = serde_json::to_string_pretty(&self).unwrap();
|
||||||
dbg!(&json);
|
dbg!(&json);
|
||||||
|
|||||||
131
daemon/src/config_anime.rs
Normal file
131
daemon/src/config_anime.rs
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
use log::{error, warn};
|
||||||
|
use rog_anime::{error::AnimeError, ActionData, AnimTime, AnimeAction, Vec2};
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use std::fs::{File, OpenOptions};
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
pub static ANIME_CONFIG_PATH: &str = "/etc/asusd/anime.conf";
|
||||||
|
pub static ANIME_CACHE_PATH: &str = "/etc/asusd/anime-cache.conf";
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Default)]
|
||||||
|
pub struct AnimeConfigCached {
|
||||||
|
pub system: Option<ActionData>,
|
||||||
|
pub boot: Option<ActionData>,
|
||||||
|
pub suspend: Option<ActionData>,
|
||||||
|
pub shutdown: Option<ActionData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnimeConfigCached {
|
||||||
|
pub fn init_from_config(&mut self, config: &AnimeConfig) -> Result<(), AnimeError> {
|
||||||
|
if let Some(ref sys) = config.system {
|
||||||
|
self.system = Some(ActionData::from_anime_action(sys)?)
|
||||||
|
}
|
||||||
|
if let Some(ref boot) = config.boot {
|
||||||
|
self.boot = Some(ActionData::from_anime_action(boot)?)
|
||||||
|
}
|
||||||
|
if let Some(ref suspend) = config.boot {
|
||||||
|
self.suspend = Some(ActionData::from_anime_action(suspend)?)
|
||||||
|
}
|
||||||
|
if let Some(ref shutdown) = config.boot {
|
||||||
|
self.shutdown = Some(ActionData::from_anime_action(shutdown)?)
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Config for base system actions for the anime display
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct AnimeConfig {
|
||||||
|
pub system: Option<AnimeAction>,
|
||||||
|
pub boot: Option<AnimeAction>,
|
||||||
|
pub suspend: Option<AnimeAction>,
|
||||||
|
pub shutdown: Option<AnimeAction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AnimeConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
AnimeConfig {
|
||||||
|
system: None,
|
||||||
|
boot: None,
|
||||||
|
suspend: None,
|
||||||
|
shutdown: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnimeConfig {
|
||||||
|
/// `load` will attempt to read the config, and panic if the dir is missing
|
||||||
|
pub fn load() -> Self {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.open(&ANIME_CONFIG_PATH)
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
panic!(
|
||||||
|
"The file {} or directory /etc/asusd/ is missing",
|
||||||
|
ANIME_CONFIG_PATH
|
||||||
|
)
|
||||||
|
}); // okay to cause panic here
|
||||||
|
let mut buf = String::new();
|
||||||
|
if let Ok(read_len) = file.read_to_string(&mut buf) {
|
||||||
|
if read_len == 0 {
|
||||||
|
return AnimeConfig::create_default(&mut file);
|
||||||
|
} else {
|
||||||
|
if let Ok(data) = serde_json::from_str(&buf) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
warn!("Could not deserialise {}", ANIME_CONFIG_PATH);
|
||||||
|
panic!("Please remove {} then restart asusd", ANIME_CONFIG_PATH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimeConfig::create_default(&mut file)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_default(file: &mut File) -> Self {
|
||||||
|
// create a default config here
|
||||||
|
let config = AnimeConfig {
|
||||||
|
system: None,
|
||||||
|
boot: Some(AnimeAction::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::Time(Duration::from_secs(5)),
|
||||||
|
}),
|
||||||
|
suspend: None,
|
||||||
|
shutdown: None,
|
||||||
|
};
|
||||||
|
// Should be okay to unwrap this as is since it is a Default
|
||||||
|
let json = serde_json::to_string(&config).unwrap();
|
||||||
|
file.write_all(json.as_bytes())
|
||||||
|
.unwrap_or_else(|_| panic!("Could not write {}", ANIME_CONFIG_PATH));
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self) {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.open(&ANIME_CONFIG_PATH)
|
||||||
|
.unwrap_or_else(|err| panic!("Error reading {}: {}", ANIME_CONFIG_PATH, err));
|
||||||
|
let mut buf = String::new();
|
||||||
|
if let Ok(l) = file.read_to_string(&mut buf) {
|
||||||
|
if l == 0 {
|
||||||
|
warn!("File is empty {}", ANIME_CONFIG_PATH);
|
||||||
|
} else {
|
||||||
|
let x: AnimeConfig = serde_json::from_str(&buf)
|
||||||
|
.unwrap_or_else(|_| panic!("Could not deserialise {}", ANIME_CONFIG_PATH));
|
||||||
|
*self = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&self) {
|
||||||
|
let mut file = File::create(ANIME_CONFIG_PATH).expect("Couldn't overwrite config");
|
||||||
|
let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed");
|
||||||
|
file.write_all(json.as_bytes())
|
||||||
|
.unwrap_or_else(|err| error!("Could not write config: {}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -147,7 +147,6 @@ impl AuraConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct AuraMultiZone {
|
pub struct AuraMultiZone {
|
||||||
static_: [AuraEffect; 4],
|
static_: [AuraEffect; 4],
|
||||||
@@ -233,4 +232,4 @@ impl Default for AuraMultiZone {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use rog_aura::AuraEffect;
|
|
||||||
use rog_types::{gfx_vendors::GfxVendors, profile::Profile};
|
use rog_types::{gfx_vendors::GfxVendors, profile::Profile};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
@@ -66,7 +65,6 @@ impl ConfigV324 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct ConfigV341 {
|
pub struct ConfigV341 {
|
||||||
pub gfx_mode: GfxVendors,
|
pub gfx_mode: GfxVendors,
|
||||||
@@ -94,4 +92,4 @@ impl ConfigV341 {
|
|||||||
power_profiles: self.power_profiles,
|
power_profiles: self.power_profiles,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,66 +4,49 @@ use rog_anime::{
|
|||||||
pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on, pkts_for_init, PROD_ID,
|
pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on, pkts_for_init, PROD_ID,
|
||||||
VENDOR_ID,
|
VENDOR_ID,
|
||||||
},
|
},
|
||||||
AnimeDataBuffer, AnimePacketType,
|
ActionData, AnimTime, AnimeDataBuffer, AnimePacketType, ANIME_DATA_LEN,
|
||||||
};
|
};
|
||||||
use rog_types::supported::AnimeSupportedFunctions;
|
use rog_types::supported::AnimeSupportedFunctions;
|
||||||
use rusb::{Device, DeviceHandle};
|
use rusb::{Device, DeviceHandle};
|
||||||
use std::error::Error;
|
use std::{
|
||||||
use std::time::Duration;
|
error::Error,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
thread::sleep,
|
||||||
|
time::Instant,
|
||||||
|
};
|
||||||
|
use std::{sync::atomic::AtomicBool, time::Duration};
|
||||||
use zbus::dbus_interface;
|
use zbus::dbus_interface;
|
||||||
use zvariant::ObjectPath;
|
use zvariant::ObjectPath;
|
||||||
|
|
||||||
use crate::GetSupported;
|
use crate::{
|
||||||
|
config_anime::{AnimeConfig, AnimeConfigCached},
|
||||||
|
error::RogError,
|
||||||
|
GetSupported,
|
||||||
|
};
|
||||||
|
|
||||||
impl GetSupported for CtrlAnimeDisplay {
|
impl GetSupported for CtrlAnime {
|
||||||
type A = AnimeSupportedFunctions;
|
type A = AnimeSupportedFunctions;
|
||||||
|
|
||||||
fn get_supported() -> Self::A {
|
fn get_supported() -> Self::A {
|
||||||
AnimeSupportedFunctions(CtrlAnimeDisplay::get_device(VENDOR_ID, PROD_ID).is_ok())
|
AnimeSupportedFunctions(CtrlAnime::get_device(VENDOR_ID, PROD_ID).is_ok())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CtrlAnimeDisplay {
|
pub struct CtrlAnime {
|
||||||
handle: DeviceHandle<rusb::GlobalContext>,
|
handle: DeviceHandle<rusb::GlobalContext>,
|
||||||
|
cache: AnimeConfigCached,
|
||||||
|
_config: AnimeConfig,
|
||||||
|
// set to force thread to exit
|
||||||
|
thread_exit: AtomicBool,
|
||||||
|
// Set to false when the thread exits
|
||||||
|
thread_running: AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::ZbusAdd for CtrlAnimeDisplay {
|
impl CtrlAnime {
|
||||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
|
||||||
server
|
|
||||||
.at(
|
|
||||||
&ObjectPath::from_str_unchecked("/org/asuslinux/Anime"),
|
|
||||||
self,
|
|
||||||
)
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!("CtrlAnimeDisplay: add_to_server {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
|
||||||
impl CtrlAnimeDisplay {
|
|
||||||
/// Writes a data stream of length
|
|
||||||
fn write(&self, input: AnimeDataBuffer) {
|
|
||||||
self.write_data_buffer(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_on_off(&self, status: bool) {
|
|
||||||
self.write_bytes(&pkt_for_set_on(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_boot_on_off(&self, on: bool) {
|
|
||||||
self.write_bytes(&pkt_for_set_boot(on));
|
|
||||||
self.write_bytes(&pkt_for_apply());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CtrlAnimeDisplay {
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> Result<CtrlAnimeDisplay, Box<dyn Error>> {
|
pub fn new(config: AnimeConfig) -> Result<CtrlAnime, Box<dyn Error>> {
|
||||||
// We don't expect this ID to ever change
|
// We don't expect this ID to ever change
|
||||||
let device = CtrlAnimeDisplay::get_device(0x0b05, 0x193b)?;
|
let device = CtrlAnime::get_device(0x0b05, 0x193b)?;
|
||||||
|
|
||||||
let mut device = device.open()?;
|
let mut device = device.open()?;
|
||||||
device.reset()?;
|
device.reset()?;
|
||||||
@@ -79,7 +62,16 @@ impl CtrlAnimeDisplay {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
info!("Device has an AniMe Matrix display");
|
info!("Device has an AniMe Matrix display");
|
||||||
let ctrl = CtrlAnimeDisplay { handle: device };
|
let mut cache = AnimeConfigCached::default();
|
||||||
|
cache.init_from_config(&config)?;
|
||||||
|
|
||||||
|
let ctrl = CtrlAnime {
|
||||||
|
handle: device,
|
||||||
|
cache,
|
||||||
|
_config: config,
|
||||||
|
thread_exit: AtomicBool::new(false),
|
||||||
|
thread_running: AtomicBool::new(false),
|
||||||
|
};
|
||||||
ctrl.do_initialization();
|
ctrl.do_initialization();
|
||||||
|
|
||||||
Ok(ctrl)
|
Ok(ctrl)
|
||||||
@@ -95,6 +87,108 @@ impl CtrlAnimeDisplay {
|
|||||||
Err(rusb::Error::NoDevice)
|
Err(rusb::Error::NoDevice)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DOUBLE THREAD NEST!
|
||||||
|
fn run_thread(inner: Arc<Mutex<CtrlAnime>>, action: Option<ActionData>, mut once: bool) {
|
||||||
|
std::thread::Builder::new()
|
||||||
|
.name("AniMe system thread start".into())
|
||||||
|
.spawn(move || {
|
||||||
|
// Make the loop exit first
|
||||||
|
loop {
|
||||||
|
if let Ok(lock) = inner.try_lock() {
|
||||||
|
lock.thread_exit
|
||||||
|
.store(true, std::sync::atomic::Ordering::SeqCst);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
if let Ok(lock) = inner.try_lock() {
|
||||||
|
if !lock
|
||||||
|
.thread_running
|
||||||
|
.load(std::sync::atomic::Ordering::SeqCst)
|
||||||
|
{
|
||||||
|
lock.thread_exit
|
||||||
|
.store(false, std::sync::atomic::Ordering::SeqCst);
|
||||||
|
info!("AniMe system thread exited");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::thread::Builder::new()
|
||||||
|
.name("AniMe system actions".into())
|
||||||
|
.spawn(move || {
|
||||||
|
info!("AniMe system thread started");
|
||||||
|
'main: loop {
|
||||||
|
if let Ok(lock) = inner.try_lock() {
|
||||||
|
if !once
|
||||||
|
&& lock.thread_exit.load(std::sync::atomic::Ordering::SeqCst)
|
||||||
|
{
|
||||||
|
break 'main;
|
||||||
|
}
|
||||||
|
if let Some(ref action) = action {
|
||||||
|
match action {
|
||||||
|
ActionData::Animation(frames) => {
|
||||||
|
let mut count = 0;
|
||||||
|
let start = Instant::now();
|
||||||
|
'animation: loop {
|
||||||
|
for frame in frames.frames() {
|
||||||
|
lock.write_data_buffer(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActionData::Image(image) => {
|
||||||
|
once = false;
|
||||||
|
lock.write_data_buffer(image.as_ref().clone())
|
||||||
|
}
|
||||||
|
ActionData::Pause(_) => {}
|
||||||
|
ActionData::AudioEq => {}
|
||||||
|
ActionData::SystemInfo => {}
|
||||||
|
ActionData::TimeDate => {}
|
||||||
|
ActionData::Matrix => {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break 'main;
|
||||||
|
}
|
||||||
|
if once {
|
||||||
|
let data =
|
||||||
|
AnimeDataBuffer::from_vec([0u8; ANIME_DATA_LEN].to_vec());
|
||||||
|
lock.write_data_buffer(data);
|
||||||
|
break 'main;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'exit: loop {
|
||||||
|
if let Ok(lock) = inner.try_lock() {
|
||||||
|
lock.thread_exit
|
||||||
|
.store(false, std::sync::atomic::Ordering::SeqCst);
|
||||||
|
lock.thread_running
|
||||||
|
.store(false, std::sync::atomic::Ordering::SeqCst);
|
||||||
|
break 'exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|err| info!("AniMe system thread: {:?}", err))
|
||||||
|
.ok();
|
||||||
|
})
|
||||||
|
.map(|err| info!("AniMe system thread: {:?}", err))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
fn write_bytes(&self, message: &[u8]) {
|
fn write_bytes(&self, message: &[u8]) {
|
||||||
match self.handle.write_control(
|
match self.handle.write_control(
|
||||||
0x21, // request_type
|
0x21, // request_type
|
||||||
@@ -126,3 +220,89 @@ impl CtrlAnimeDisplay {
|
|||||||
self.write_bytes(&pkts[1]);
|
self.write_bytes(&pkts[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct CtrlAnimeTask(pub Arc<Mutex<CtrlAnime>>);
|
||||||
|
|
||||||
|
impl crate::CtrlTask for CtrlAnimeTask {
|
||||||
|
fn do_task(&self) -> Result<(), RogError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CtrlAnimeReloader(pub Arc<Mutex<CtrlAnime>>);
|
||||||
|
|
||||||
|
impl crate::Reloadable for CtrlAnimeReloader {
|
||||||
|
fn reload(&mut self) -> Result<(), RogError> {
|
||||||
|
if let Ok(lock) = self.0.try_lock() {
|
||||||
|
let action = lock.cache.boot.clone();
|
||||||
|
CtrlAnime::run_thread(self.0.clone(), action, true);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>);
|
||||||
|
|
||||||
|
/// The struct with the main dbus methods requires this trait
|
||||||
|
impl crate::ZbusAdd for CtrlAnimeZbus {
|
||||||
|
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||||
|
server
|
||||||
|
.at(
|
||||||
|
&ObjectPath::from_str_unchecked("/org/asuslinux/Anime"),
|
||||||
|
self,
|
||||||
|
)
|
||||||
|
.map_err(|err| {
|
||||||
|
warn!("CtrlAnimeDisplay: add_to_server {}", err);
|
||||||
|
err
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// None of these calls can be guarnateed to succeed unless we loop until okay
|
||||||
|
// If the try_lock *does* succeed then any other thread trying to lock will not grab it
|
||||||
|
// until we finish.
|
||||||
|
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||||
|
impl CtrlAnimeZbus {
|
||||||
|
/// Writes a data stream of length. Will force system thread to exit until it is restarted
|
||||||
|
fn write(&self, input: AnimeDataBuffer) {
|
||||||
|
'outer: loop {
|
||||||
|
if let Ok(lock) = self.0.try_lock() {
|
||||||
|
lock.thread_exit
|
||||||
|
.store(true, std::sync::atomic::Ordering::SeqCst);
|
||||||
|
lock.write_data_buffer(input);
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_on_off(&self, status: bool) {
|
||||||
|
'outer: loop {
|
||||||
|
if let Ok(lock) = self.0.try_lock() {
|
||||||
|
lock.write_bytes(&pkt_for_set_on(status));
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_boot_on_off(&self, on: bool) {
|
||||||
|
'outer: loop {
|
||||||
|
if let Ok(lock) = self.0.try_lock() {
|
||||||
|
lock.write_bytes(&pkt_for_set_boot(on));
|
||||||
|
lock.write_bytes(&pkt_for_apply());
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_main_loop(&self, on: bool) {
|
||||||
|
'outer: loop {
|
||||||
|
if let Ok(lock) = self.0.try_lock() {
|
||||||
|
lock.thread_exit
|
||||||
|
.store(on, std::sync::atomic::Ordering::SeqCst);
|
||||||
|
CtrlAnime::run_thread(self.0.clone(), lock.cache.system.clone(), false);
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
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}, supported::FanCpuSupportedFunctions};
|
use rog_types::{
|
||||||
|
profile::{FanLevel, Profile, ProfileEvent},
|
||||||
|
supported::FanCpuSupportedFunctions,
|
||||||
|
};
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@@ -31,18 +34,18 @@ impl GetSupported for CtrlFanAndCpu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DbusFanAndCpu {
|
pub struct FanAndCpuZbus {
|
||||||
inner: Arc<Mutex<CtrlFanAndCpu>>,
|
inner: Arc<Mutex<CtrlFanAndCpu>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DbusFanAndCpu {
|
impl FanAndCpuZbus {
|
||||||
pub fn new(inner: Arc<Mutex<CtrlFanAndCpu>>) -> Self {
|
pub fn new(inner: Arc<Mutex<CtrlFanAndCpu>>) -> Self {
|
||||||
Self { inner }
|
Self { inner }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||||
impl DbusFanAndCpu {
|
impl FanAndCpuZbus {
|
||||||
/// Set profile details
|
/// Set profile details
|
||||||
fn set_profile(&self, profile: String) {
|
fn set_profile(&self, profile: String) {
|
||||||
if let Ok(event) = serde_json::from_str(&profile) {
|
if let Ok(event) = serde_json::from_str(&profile) {
|
||||||
@@ -170,7 +173,7 @@ impl DbusFanAndCpu {
|
|||||||
fn notify_profile(&self, profile: &str) -> zbus::Result<()> {}
|
fn notify_profile(&self, profile: &str) -> zbus::Result<()> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::ZbusAdd for DbusFanAndCpu {
|
impl crate::ZbusAdd for FanAndCpuZbus {
|
||||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||||
server
|
server
|
||||||
.at(
|
.at(
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ use crate::{
|
|||||||
laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES},
|
laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES},
|
||||||
};
|
};
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use rog_aura::{AuraEffect, AuraModeNum, LED_MSG_LEN, LedBrightness, usb::{LED_APPLY, LED_SET}};
|
use rog_aura::{
|
||||||
|
usb::{LED_APPLY, LED_SET},
|
||||||
|
AuraEffect, LedBrightness, LED_MSG_LEN,
|
||||||
|
};
|
||||||
use rog_types::supported::LedSupportedFunctions;
|
use rog_types::supported::LedSupportedFunctions;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
@@ -50,6 +53,37 @@ pub struct CtrlKbdBacklight {
|
|||||||
config: AuraConfig,
|
config: AuraConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct CtrlKbdBacklightTask(pub Arc<Mutex<CtrlKbdBacklight>>);
|
||||||
|
|
||||||
|
impl crate::CtrlTask for CtrlKbdBacklightTask {
|
||||||
|
fn do_task(&self) -> Result<(), RogError> {
|
||||||
|
if let Ok(mut lock) = self.0.try_lock() {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.open(&lock.bright_node)
|
||||||
|
.map_err(|err| match err.kind() {
|
||||||
|
std::io::ErrorKind::NotFound => {
|
||||||
|
RogError::MissingLedBrightNode((&lock.bright_node).into(), err)
|
||||||
|
}
|
||||||
|
_ => RogError::Path((&lock.bright_node).into(), err),
|
||||||
|
})?;
|
||||||
|
let mut buf = [0u8; 1];
|
||||||
|
file.read_exact(&mut buf)
|
||||||
|
.map_err(|err| RogError::Read("buffer".into(), err))?;
|
||||||
|
if let Some(num) = char::from(buf[0]).to_digit(10) {
|
||||||
|
if lock.config.brightness != num.into() {
|
||||||
|
lock.config.read();
|
||||||
|
lock.config.brightness = num.into();
|
||||||
|
lock.config.write();
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
return Err(RogError::ParseLed);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct DbusKbdBacklight {
|
pub struct DbusKbdBacklight {
|
||||||
inner: Arc<Mutex<CtrlKbdBacklight>>,
|
inner: Arc<Mutex<CtrlKbdBacklight>>,
|
||||||
}
|
}
|
||||||
@@ -166,73 +200,6 @@ impl DbusKbdBacklight {
|
|||||||
fn notify_led(&self, data: &str) -> zbus::Result<()>;
|
fn notify_led(&self, data: &str) -> zbus::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::Reloadable for CtrlKbdBacklight {
|
|
||||||
fn reload(&mut self) -> Result<(), RogError> {
|
|
||||||
// set current mode (if any)
|
|
||||||
if self.supported_modes.standard.len() > 1 {
|
|
||||||
let current_mode = self.config.current_mode;
|
|
||||||
if self.supported_modes.standard.contains(&(current_mode)) {
|
|
||||||
let mode = self
|
|
||||||
.config
|
|
||||||
.builtins
|
|
||||||
.get(¤t_mode)
|
|
||||||
.ok_or(RogError::NotSupported)?
|
|
||||||
.to_owned();
|
|
||||||
self.write_mode(&mode)?;
|
|
||||||
info!("Reloaded last used mode");
|
|
||||||
} else {
|
|
||||||
warn!(
|
|
||||||
"An unsupported mode was set: {}, reset to first mode available",
|
|
||||||
<&str>::from(&self.config.current_mode)
|
|
||||||
);
|
|
||||||
self.config.builtins.remove(¤t_mode);
|
|
||||||
self.config.current_mode = AuraModeNum::Static;
|
|
||||||
// TODO: do a recursive call with a boxed dyn future later
|
|
||||||
let mode = self
|
|
||||||
.config
|
|
||||||
.builtins
|
|
||||||
.get(¤t_mode)
|
|
||||||
.ok_or(RogError::NotSupported)?
|
|
||||||
.to_owned();
|
|
||||||
self.write_mode(&mode)?;
|
|
||||||
info!("Reloaded last used mode");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reload brightness
|
|
||||||
let bright = self.config.brightness;
|
|
||||||
self.set_brightness(bright)?;
|
|
||||||
info!("Reloaded last used brightness");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl crate::CtrlTask for CtrlKbdBacklight {
|
|
||||||
fn do_task(&mut self) -> Result<(), RogError> {
|
|
||||||
let mut file = OpenOptions::new()
|
|
||||||
.read(true)
|
|
||||||
.open(&self.bright_node)
|
|
||||||
.map_err(|err| match err.kind() {
|
|
||||||
std::io::ErrorKind::NotFound => {
|
|
||||||
RogError::MissingLedBrightNode((&self.bright_node).into(), err)
|
|
||||||
}
|
|
||||||
_ => RogError::Path((&self.bright_node).into(), err),
|
|
||||||
})?;
|
|
||||||
let mut buf = [0u8; 1];
|
|
||||||
file.read_exact(&mut buf)
|
|
||||||
.map_err(|err| RogError::Read("buffer".into(), err))?;
|
|
||||||
if let Some(num) = char::from(buf[0]).to_digit(10) {
|
|
||||||
if self.config.brightness != num.into() {
|
|
||||||
self.config.read();
|
|
||||||
self.config.brightness = num.into();
|
|
||||||
self.config.write();
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
Err(RogError::ParseLed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CtrlKbdBacklight {
|
impl CtrlKbdBacklight {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(supported_modes: LaptopLedData, config: AuraConfig) -> Result<Self, RogError> {
|
pub fn new(supported_modes: LaptopLedData, config: AuraConfig) -> Result<Self, RogError> {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use zbus::dbus_interface;
|
|||||||
use zvariant::ObjectPath;
|
use zvariant::ObjectPath;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ctrl_anime::CtrlAnimeDisplay, ctrl_charge::CtrlCharge, ctrl_fan_cpu::CtrlFanAndCpu,
|
ctrl_anime::CtrlAnime, ctrl_charge::CtrlCharge, ctrl_fan_cpu::CtrlFanAndCpu,
|
||||||
ctrl_leds::CtrlKbdBacklight, ctrl_rog_bios::CtrlRogBios, GetSupported,
|
ctrl_leds::CtrlKbdBacklight, ctrl_rog_bios::CtrlRogBios, GetSupported,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ impl GetSupported for SupportedFunctions {
|
|||||||
fn get_supported() -> Self::A {
|
fn get_supported() -> Self::A {
|
||||||
SupportedFunctions {
|
SupportedFunctions {
|
||||||
keyboard_led: CtrlKbdBacklight::get_supported(),
|
keyboard_led: CtrlKbdBacklight::get_supported(),
|
||||||
anime_ctrl: CtrlAnimeDisplay::get_supported(),
|
anime_ctrl: CtrlAnime::get_supported(),
|
||||||
charge_ctrl: CtrlCharge::get_supported(),
|
charge_ctrl: CtrlCharge::get_supported(),
|
||||||
fan_cpu_ctrl: CtrlFanAndCpu::get_supported(),
|
fan_cpu_ctrl: CtrlFanAndCpu::get_supported(),
|
||||||
rog_bios_ctrl: CtrlRogBios::get_supported(),
|
rog_bios_ctrl: CtrlRogBios::get_supported(),
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use daemon::ctrl_leds::{CtrlKbdBacklight, DbusKbdBacklight};
|
use daemon::ctrl_leds::{CtrlKbdBacklight, CtrlKbdBacklightTask, DbusKbdBacklight};
|
||||||
use daemon::{
|
use daemon::{
|
||||||
config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported,
|
config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported,
|
||||||
};
|
};
|
||||||
use daemon::{config_aura::AuraConfig, ctrl_charge::CtrlCharge};
|
use daemon::{config_anime::AnimeConfig, config_aura::AuraConfig, ctrl_charge::CtrlCharge};
|
||||||
use daemon::{ctrl_anime::CtrlAnimeDisplay, ctrl_gfx::gfx::CtrlGraphics};
|
use daemon::{ctrl_anime::*, ctrl_gfx::gfx::CtrlGraphics};
|
||||||
use daemon::{
|
use daemon::{
|
||||||
ctrl_fan_cpu::{CtrlFanAndCpu, DbusFanAndCpu},
|
ctrl_fan_cpu::{CtrlFanAndCpu, FanAndCpuZbus},
|
||||||
laptops::LaptopLedData,
|
laptops::LaptopLedData,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -41,27 +41,24 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timing is such that:
|
/// The actual main loop for the daemon
|
||||||
// - interrupt write is minimum 1ms (sometimes lower)
|
|
||||||
// - read interrupt must timeout, minimum of 1ms
|
|
||||||
// - for a single usb packet, 2ms total.
|
|
||||||
// - to maintain constant times of 1ms, per-key colours should use
|
|
||||||
// the effect endpoint so that the complete colour block is written
|
|
||||||
// as fast as 1ms per row of the matrix inside it. (10ms total time)
|
|
||||||
fn start_daemon() -> Result<(), Box<dyn Error>> {
|
fn start_daemon() -> Result<(), Box<dyn Error>> {
|
||||||
let supported = SupportedFunctions::get_supported();
|
let supported = SupportedFunctions::get_supported();
|
||||||
print_board_info();
|
print_board_info();
|
||||||
println!("{}", serde_json::to_string_pretty(&supported).unwrap());
|
println!("{}", serde_json::to_string_pretty(&supported).unwrap());
|
||||||
|
|
||||||
let config = Config::load();
|
// Collect tasks for task thread
|
||||||
let enable_gfx_switching = config.gfx_managed;
|
let mut tasks: Vec<Box<dyn CtrlTask + Send>> = Vec::new();
|
||||||
let config = Arc::new(Mutex::new(config));
|
// Start zbus server
|
||||||
|
|
||||||
let connection = Connection::new_system()?;
|
let connection = Connection::new_system()?;
|
||||||
fdo::DBusProxy::new(&connection)?
|
fdo::DBusProxy::new(&connection)?
|
||||||
.request_name(DBUS_NAME, fdo::RequestNameFlags::ReplaceExisting.into())?;
|
.request_name(DBUS_NAME, fdo::RequestNameFlags::ReplaceExisting.into())?;
|
||||||
let mut object_server = zbus::ObjectServer::new(&connection);
|
let mut object_server = zbus::ObjectServer::new(&connection);
|
||||||
|
|
||||||
|
let config = Config::load();
|
||||||
|
let enable_gfx_switching = config.gfx_managed;
|
||||||
|
let config = Arc::new(Mutex::new(config));
|
||||||
|
|
||||||
supported.add_to_server(&mut object_server);
|
supported.add_to_server(&mut object_server);
|
||||||
|
|
||||||
match CtrlRogBios::new(config.clone()) {
|
match CtrlRogBios::new(config.clone()) {
|
||||||
@@ -90,15 +87,52 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match CtrlAnimeDisplay::new() {
|
match CtrlFanAndCpu::new(config.clone()) {
|
||||||
|
Ok(mut ctrl) => {
|
||||||
|
ctrl.reload()
|
||||||
|
.unwrap_or_else(|err| warn!("Profile control: {}", err));
|
||||||
|
let tmp = Arc::new(Mutex::new(ctrl));
|
||||||
|
FanAndCpuZbus::new(tmp).add_to_server(&mut object_server);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("Profile control: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match CtrlAnime::new(AnimeConfig::load()) {
|
||||||
Ok(ctrl) => {
|
Ok(ctrl) => {
|
||||||
ctrl.add_to_server(&mut object_server);
|
let inner = Arc::new(Mutex::new(ctrl));
|
||||||
|
|
||||||
|
let mut reload = CtrlAnimeReloader(inner.clone());
|
||||||
|
reload
|
||||||
|
.reload()
|
||||||
|
.unwrap_or_else(|err| warn!("AniMe: {}", err));
|
||||||
|
|
||||||
|
let zbus = CtrlAnimeZbus(inner.clone());
|
||||||
|
zbus.add_to_server(&mut object_server);
|
||||||
|
|
||||||
|
tasks.push(Box::new(CtrlAnimeTask(inner)));
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("AniMe control: {}", err);
|
error!("AniMe control: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let laptop = LaptopLedData::get_data();
|
||||||
|
let aura_config = AuraConfig::load(&laptop);
|
||||||
|
match CtrlKbdBacklight::new(laptop, aura_config) {
|
||||||
|
Ok(ctrl) => {
|
||||||
|
let tmp = Arc::new(Mutex::new(ctrl));
|
||||||
|
DbusKbdBacklight::new(tmp.clone()).add_to_server(&mut object_server);
|
||||||
|
let task = CtrlKbdBacklightTask(tmp);
|
||||||
|
tasks.push(Box::new(task));
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("Keyboard control: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Graphics switching requires some checks on boot specifically for g-sync capable laptops
|
||||||
if enable_gfx_switching {
|
if enable_gfx_switching {
|
||||||
match CtrlGraphics::new(config.clone()) {
|
match CtrlGraphics::new(config.clone()) {
|
||||||
Ok(mut ctrl) => {
|
Ok(mut ctrl) => {
|
||||||
@@ -141,48 +175,24 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect tasks for task thread
|
|
||||||
let mut tasks: Vec<Arc<Mutex<dyn CtrlTask + Send>>> = Vec::new();
|
|
||||||
|
|
||||||
if let Ok(mut ctrl) = CtrlFanAndCpu::new(config).map_err(|err| {
|
|
||||||
error!("Profile control: {}", err);
|
|
||||||
}) {
|
|
||||||
ctrl.reload()
|
|
||||||
.unwrap_or_else(|err| warn!("Profile control: {}", err));
|
|
||||||
let tmp = Arc::new(Mutex::new(ctrl));
|
|
||||||
DbusFanAndCpu::new(tmp).add_to_server(&mut object_server);
|
|
||||||
};
|
|
||||||
|
|
||||||
let laptop = LaptopLedData::get_data();
|
|
||||||
let aura_config = AuraConfig::load(&laptop);
|
|
||||||
if let Ok(ctrl) = CtrlKbdBacklight::new(laptop, aura_config).map_err(|err| {
|
|
||||||
error!("Keyboard control: {}", err);
|
|
||||||
err
|
|
||||||
}) {
|
|
||||||
let tmp = Arc::new(Mutex::new(ctrl));
|
|
||||||
DbusKbdBacklight::new(tmp.clone()).add_to_server(&mut object_server);
|
|
||||||
tasks.push(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: implement messaging between threads to check fails
|
// TODO: implement messaging between threads to check fails
|
||||||
// These tasks generally read a sys path or file to check for a
|
|
||||||
// change
|
// Run tasks
|
||||||
let handle = std::thread::Builder::new()
|
let handle = std::thread::Builder::new()
|
||||||
.name("asusd watch".to_string())
|
.name("asusd watch".to_string())
|
||||||
.spawn(move || loop {
|
.spawn(move || loop {
|
||||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||||
|
|
||||||
for ctrl in tasks.iter() {
|
for ctrl in tasks.iter() {
|
||||||
if let Ok(mut lock) = ctrl.try_lock() {
|
ctrl.do_task()
|
||||||
lock.do_task()
|
.map_err(|err| {
|
||||||
.map_err(|err| {
|
warn!("do_task error: {}", err);
|
||||||
warn!("do_task error: {}", err);
|
})
|
||||||
})
|
.ok();
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Run zbus server
|
||||||
object_server
|
object_server
|
||||||
.with(
|
.with(
|
||||||
&ObjectPath::from_str_unchecked("/org/asuslinux/Charge"),
|
&ObjectPath::from_str_unchecked("/org/asuslinux/Charge"),
|
||||||
@@ -196,6 +206,7 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
|
// Loop to check errors and iterate zbus server
|
||||||
loop {
|
loop {
|
||||||
if let Err(err) = &handle {
|
if let Err(err) = &handle {
|
||||||
error!("{}", err);
|
error!("{}", err);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#![deny(unused_must_use)]
|
#![deny(unused_must_use)]
|
||||||
/// Configuration loading, saving
|
/// Configuration loading, saving
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
pub mod config_anime;
|
||||||
pub mod config_aura;
|
pub mod config_aura;
|
||||||
pub(crate) mod config_old;
|
pub(crate) mod config_old;
|
||||||
/// Control of AniMe matrix display
|
/// Control of AniMe matrix display
|
||||||
@@ -48,7 +49,7 @@ pub trait ZbusAdd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait CtrlTask {
|
pub trait CtrlTask {
|
||||||
fn do_task(&mut self) -> Result<(), RogError>;
|
fn do_task(&self) -> Result<(), RogError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CtrlTaskComplex {
|
pub trait CtrlTaskComplex {
|
||||||
|
|||||||
89
design-patterns.md
Normal file
89
design-patterns.md
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# Daemon
|
||||||
|
|
||||||
|
## Controller pattern
|
||||||
|
|
||||||
|
There are a series of traits in the daemon for use with controller objects. Not all traits are required:
|
||||||
|
|
||||||
|
- `Reloadable`, for controllers that need the ability to reload (typically on start)
|
||||||
|
- `ZbusAdd`, for controllers that have zbus derive. These need to run on the zbus server.
|
||||||
|
- `CtrlTask`, for controllers that need to run tasks every loop.
|
||||||
|
- `GetSupported`, see if the hardware/functions this controller requires are supported.
|
||||||
|
|
||||||
|
The first 3 trait objects get owned by the daemon methods that required them, which is why an `Arc<Mutex<T>>` is required.
|
||||||
|
|
||||||
|
Generally the actual controller object will need to live in its own world as its own struct.
|
||||||
|
Then for each trait that is required a new struct is required that can have the trait implemented, and that struct would have a reference to the main controller via `Arc<Mutex<T>>`.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
Main controller:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub struct CtrlAnime {
|
||||||
|
<things the controller requires>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CtrlAnime {
|
||||||
|
<functions the controller exposes>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The task trait:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub struct CtrlAnimeTask(Arc<Mutex<CtrlAnime>>);
|
||||||
|
|
||||||
|
impl crate::CtrlTask for CtrlAnimeTask {
|
||||||
|
fn do_task(&self) -> Result<(), RogError> {
|
||||||
|
if let Ok(lock) = self.inner.try_lock() {
|
||||||
|
<some action>
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The reloader trait
|
||||||
|
```rust
|
||||||
|
pub struct CtrlAnimeReloader(Arc<Mutex<CtrlAnime>>);
|
||||||
|
|
||||||
|
impl crate::Reloadable for CtrlAnimeReloader {
|
||||||
|
fn reload(&mut self) -> Result<(), RogError> {
|
||||||
|
if let Ok(lock) = self.inner.try_lock() {
|
||||||
|
<some action>
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The Zbus requirements:
|
||||||
|
```rust
|
||||||
|
pub struct CtrlAnimeZbus(Arc<Mutex<CtrlAnime>>);
|
||||||
|
|
||||||
|
impl crate::ZbusAdd for CtrlAnimeZbus {
|
||||||
|
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||||
|
server
|
||||||
|
.at(
|
||||||
|
&ObjectPath::from_str_unchecked("/org/asuslinux/Anime"),
|
||||||
|
self,
|
||||||
|
)
|
||||||
|
.map_err(|err| {
|
||||||
|
warn!("CtrlAnimeDisplay: add_to_server {}", err);
|
||||||
|
err
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||||
|
impl CtrlAnimeZbus {
|
||||||
|
fn <zbus method>() {
|
||||||
|
if let Ok(lock) = self.inner.try_lock() {
|
||||||
|
<some action>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The controller can then be added to the daemon parts as required.
|
||||||
@@ -16,7 +16,7 @@ pub enum AnimeError {
|
|||||||
/// 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 = "dbus")]
|
#[cfg(feature = "dbus")]
|
||||||
Zbus(fdo::Error)
|
Zbus(fdo::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for AnimeError {
|
impl fmt::Display for AnimeError {
|
||||||
@@ -68,4 +68,4 @@ impl From<AnimeError> for fdo::Error {
|
|||||||
fn from(err: AnimeError) -> Self {
|
fn from(err: AnimeError) -> Self {
|
||||||
fdo::Error::Failed(format!("{}", err))
|
fdo::Error::Failed(format!("{}", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,4 +30,4 @@ pub use sequencer::*;
|
|||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
/// Provides const methods to create the USB HID control packets
|
/// Provides const methods to create the USB HID control packets
|
||||||
pub mod usb;
|
pub mod usb;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use std::{
|
|||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{error::AnimeError, AnimeDataBuffer, AnimeGif, AnimeImage, AnimTime};
|
use crate::{error::AnimeError, AnimTime, AnimeDataBuffer, AnimeGif, AnimeImage};
|
||||||
|
|
||||||
/// All the possible AniMe actions that can be used. This enum is intended to be
|
/// All the possible AniMe actions that can be used. This enum is intended to be
|
||||||
/// a helper for loading up `ActionData`.
|
/// a helper for loading up `ActionData`.
|
||||||
@@ -40,7 +40,7 @@ pub enum AnimeAction {
|
|||||||
|
|
||||||
/// All the possible AniMe actions that can be used. The enum is intended to be
|
/// All the possible AniMe actions that can be used. The enum is intended to be
|
||||||
/// used in a array allowing the user to cycle through a series of actions.
|
/// used in a array allowing the user to cycle through a series of actions.
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub enum ActionData {
|
pub enum ActionData {
|
||||||
/// Full gif sequence. Immutable.
|
/// Full gif sequence. Immutable.
|
||||||
Animation(AnimeGif),
|
Animation(AnimeGif),
|
||||||
@@ -58,6 +58,50 @@ pub enum ActionData {
|
|||||||
Matrix,
|
Matrix,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ActionData {
|
||||||
|
pub fn from_anime_action(action: &AnimeAction) -> Result<ActionData, AnimeError> {
|
||||||
|
let a = match action {
|
||||||
|
AnimeAction::AsusAnimation {
|
||||||
|
file,
|
||||||
|
time: duration,
|
||||||
|
brightness,
|
||||||
|
} => ActionData::Animation(AnimeGif::create_diagonal_gif(
|
||||||
|
&file,
|
||||||
|
*duration,
|
||||||
|
*brightness,
|
||||||
|
)?),
|
||||||
|
AnimeAction::ImageAnimation {
|
||||||
|
file,
|
||||||
|
scale,
|
||||||
|
angle,
|
||||||
|
translation,
|
||||||
|
time: duration,
|
||||||
|
brightness,
|
||||||
|
} => ActionData::Animation(AnimeGif::create_png_gif(
|
||||||
|
&file,
|
||||||
|
*scale,
|
||||||
|
*angle,
|
||||||
|
*translation,
|
||||||
|
*duration,
|
||||||
|
*brightness,
|
||||||
|
)?),
|
||||||
|
AnimeAction::Image {
|
||||||
|
file,
|
||||||
|
scale,
|
||||||
|
angle,
|
||||||
|
translation,
|
||||||
|
brightness,
|
||||||
|
} => {
|
||||||
|
let image = AnimeImage::from_png(&file, *scale, *angle, *translation, *brightness)?;
|
||||||
|
let data = <AnimeDataBuffer>::from(&image);
|
||||||
|
ActionData::Image(Box::new(data))
|
||||||
|
}
|
||||||
|
AnimeAction::Pause(duration) => ActionData::Pause(*duration),
|
||||||
|
};
|
||||||
|
Ok(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An optimised precomputed set of actions that the user can cycle through
|
/// An optimised precomputed set of actions that the user can cycle through
|
||||||
#[derive(Debug, Deserialize, Serialize, Default)]
|
#[derive(Debug, Deserialize, Serialize, Default)]
|
||||||
pub struct Sequences(Vec<ActionData>);
|
pub struct Sequences(Vec<ActionData>);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ pub const fn pkts_for_init() -> [[u8; PACKET_SIZE]; 2] {
|
|||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
while count < INIT_STR.len() {
|
while count < INIT_STR.len() {
|
||||||
packets[0][count] = INIT_STR[count];
|
packets[0][count] = INIT_STR[count];
|
||||||
count +=1;
|
count += 1;
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
packets[1][0] = DEV_PAGE; // write it to be sure?
|
packets[1][0] = DEV_PAGE; // write it to be sure?
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use std::str::FromStr;
|
|||||||
#[cfg(feature = "dbus")]
|
#[cfg(feature = "dbus")]
|
||||||
use zvariant_derive::Type;
|
use zvariant_derive::Type;
|
||||||
|
|
||||||
use crate::{LED_MSG_LEN, error::Error};
|
use crate::{error::Error, LED_MSG_LEN};
|
||||||
|
|
||||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
@@ -121,9 +121,7 @@ impl FromStr for Direction {
|
|||||||
|
|
||||||
/// Enum of modes that convert to the actual number required by a USB HID packet
|
/// Enum of modes that convert to the actual number required by a USB HID packet
|
||||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||||
#[derive(
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Deserialize, Serialize)]
|
||||||
Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Deserialize, Serialize,
|
|
||||||
)]
|
|
||||||
pub enum AuraModeNum {
|
pub enum AuraModeNum {
|
||||||
Static = 0,
|
Static = 0,
|
||||||
Breathe = 1,
|
Breathe = 1,
|
||||||
|
|||||||
@@ -23,4 +23,4 @@ impl fmt::Display for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for Error {}
|
impl error::Error for Error {}
|
||||||
|
|||||||
@@ -13,4 +13,4 @@ pub mod usb;
|
|||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
pub const LED_MSG_LEN: usize = 17;
|
pub const LED_MSG_LEN: usize = 17;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ impl KeyColourArray {
|
|||||||
KeyColourArray(set)
|
KeyColourArray(set)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialise and clear the keyboard for custom effects, this must be done for
|
/// Initialise and clear the keyboard for custom effects, this must be done for
|
||||||
/// every time mode switches from builtin to custom
|
/// every time mode switches from builtin to custom
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn get_init_msg() -> [u8; 64] {
|
pub const fn get_init_msg() -> [u8; 64] {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use crate::error::Error;
|
|||||||
/// used in a array allowing the user to cycle through a series of actions.
|
/// used in a array allowing the user to cycle through a series of actions.
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub enum ActionData {
|
pub enum ActionData {
|
||||||
Static
|
Static,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An optimised precomputed set of actions that the user can cycle through
|
/// An optimised precomputed set of actions that the user can cycle through
|
||||||
|
|||||||
@@ -13,4 +13,4 @@ pub const fn aura_brightness_bytes(brightness: u8) -> [u8; 17] {
|
|||||||
[
|
[
|
||||||
0x5A, 0xBA, 0xC5, 0xC4, brightness, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0x5A, 0xBA, 0xC5, 0xC4, brightness, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ pub mod zbus_rogbios;
|
|||||||
pub mod zbus_supported;
|
pub mod zbus_supported;
|
||||||
|
|
||||||
use rog_aura::AuraEffect;
|
use rog_aura::AuraEffect;
|
||||||
use rog_types::{
|
use rog_types::gfx_vendors::{GfxRequiredUserAction, GfxVendors};
|
||||||
gfx_vendors::{GfxRequiredUserAction, GfxVendors},
|
|
||||||
};
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use zbus::{Connection, Result, SignalReceiver};
|
use zbus::{Connection, Result, SignalReceiver};
|
||||||
|
|
||||||
|
|||||||
@@ -17,28 +17,4 @@ impl fmt::Display for GraphicsError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error for GraphicsError {}
|
impl Error for GraphicsError {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum AnimeError {
|
|
||||||
InvalidBitmap,
|
|
||||||
Io(std::io::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for AnimeError {
|
|
||||||
// This trait requires `fmt` with this exact signature.
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
AnimeError::InvalidBitmap => write!(f, "Bitmap is invalid"),
|
|
||||||
AnimeError::Io(e) => write!(f, "Could not open: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for AnimeError {}
|
|
||||||
|
|
||||||
impl From<std::io::Error> for AnimeError {
|
|
||||||
fn from(err: std::io::Error) -> Self {
|
|
||||||
AnimeError::Io(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user