mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
Move two common functions to a trait object
This commit is contained in:
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -15,6 +15,17 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-trait"
|
||||||
|
version = "0.1.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a265e3abeffdce30b2e26b7a11b222fe37c6067404001b434101457d0385eb92"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atty"
|
name = "atty"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
@@ -645,6 +656,7 @@ dependencies = [
|
|||||||
name = "rog-daemon"
|
name = "rog-daemon"
|
||||||
version = "0.15.3"
|
version = "0.15.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
"dbus",
|
"dbus",
|
||||||
"dbus-tokio",
|
"dbus-tokio",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ path = "src/main.rs"
|
|||||||
rog-client = { path = "../rog-client" }
|
rog-client = { path = "../rog-client" }
|
||||||
rusb = "^0.6.0"
|
rusb = "^0.6.0"
|
||||||
udev = "^0.4.0"
|
udev = "^0.4.0"
|
||||||
|
async-trait = "0.1.36"
|
||||||
|
|
||||||
# cli and logging
|
# cli and logging
|
||||||
gumdrop = "^0.8.0"
|
gumdrop = "^0.8.0"
|
||||||
|
|||||||
@@ -9,12 +9,15 @@ const INIT: u8 = 0xc2;
|
|||||||
const APPLY: u8 = 0xc3;
|
const APPLY: u8 = 0xc3;
|
||||||
const SET: u8 = 0xc4;
|
const SET: u8 = 0xc4;
|
||||||
|
|
||||||
use log::{error, warn};
|
use crate::config::Config;
|
||||||
|
use log::{error, info, warn};
|
||||||
use rog_client::error::AuraError;
|
use rog_client::error::AuraError;
|
||||||
use rusb::{Device, DeviceHandle};
|
use rusb::{Device, DeviceHandle};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::sync::mpsc::Receiver;
|
use tokio::sync::mpsc::Receiver;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -26,16 +29,45 @@ pub enum AnimatrixCommand {
|
|||||||
//ReloadLast,
|
//ReloadLast,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AniMeWriter {
|
pub struct CtrlAnimeDisplay {
|
||||||
handle: DeviceHandle<rusb::GlobalContext>,
|
handle: DeviceHandle<rusb::GlobalContext>,
|
||||||
initialised: bool,
|
initialised: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AniMeWriter {
|
use ::dbus::{nonblock::SyncConnection, tree::Signal};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl crate::Controller for CtrlAnimeDisplay {
|
||||||
|
type A = Vec<Vec<u8>>;
|
||||||
|
|
||||||
|
/// Spawns two tasks which continuously check for changes
|
||||||
|
fn spawn_task(
|
||||||
|
mut self,
|
||||||
|
_: Arc<Mutex<Config>>,
|
||||||
|
mut recv: Receiver<Self::A>,
|
||||||
|
_: Option<Arc<SyncConnection>>,
|
||||||
|
_: Option<Arc<Signal<()>>>,
|
||||||
|
) -> JoinHandle<()> {
|
||||||
|
tokio::spawn(async move {
|
||||||
|
while let Some(image) = recv.recv().await {
|
||||||
|
self.do_command(AnimatrixCommand::WriteImage(image))
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn reload_from_config(&mut self, _: &mut Config) -> Result<(), Box<dyn Error>> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CtrlAnimeDisplay {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> Result<AniMeWriter, Box<dyn Error>> {
|
pub fn new() -> Result<CtrlAnimeDisplay, Box<dyn Error>> {
|
||||||
// We don't expect this ID to ever change
|
// We don't expect this ID to ever change
|
||||||
let device = AniMeWriter::get_device(0x0b05, 0x193b).map_err(|err| {
|
let device = CtrlAnimeDisplay::get_device(0x0b05, 0x193b).map_err(|err| {
|
||||||
warn!("Could not get AniMe display handle: {:?}", err);
|
warn!("Could not get AniMe display handle: {:?}", err);
|
||||||
err
|
err
|
||||||
})?;
|
})?;
|
||||||
@@ -53,27 +85,13 @@ impl AniMeWriter {
|
|||||||
err
|
err
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(AniMeWriter {
|
info!("Device has an AniMe Matrix display");
|
||||||
|
Ok(CtrlAnimeDisplay {
|
||||||
handle: device,
|
handle: device,
|
||||||
initialised: false,
|
initialised: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawns two tasks which continuously check for changes
|
|
||||||
pub(crate) fn spawn_task(
|
|
||||||
mut ctrlr: AniMeWriter,
|
|
||||||
mut recv: Receiver<Vec<Vec<u8>>>,
|
|
||||||
) -> JoinHandle<()> {
|
|
||||||
tokio::spawn(async move {
|
|
||||||
while let Some(image) = recv.recv().await {
|
|
||||||
ctrlr
|
|
||||||
.do_command(AnimatrixCommand::WriteImage(image))
|
|
||||||
.await
|
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_device(vendor: u16, product: u16) -> Result<Device<rusb::GlobalContext>, rusb::Error> {
|
fn get_device(vendor: u16, product: u16) -> Result<Device<rusb::GlobalContext>, rusb::Error> {
|
||||||
for device in rusb::devices()?.iter() {
|
for device in rusb::devices()?.iter() {
|
||||||
|
|||||||
@@ -15,29 +15,60 @@ pub struct CtrlCharge {
|
|||||||
path: &'static str,
|
path: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CtrlCharge {
|
use ::dbus::{nonblock::SyncConnection, tree::Signal};
|
||||||
pub(super) fn new() -> Result<Self, Box<dyn Error>> {
|
use async_trait::async_trait;
|
||||||
let path = CtrlCharge::get_battery_path()?;
|
|
||||||
|
|
||||||
Ok(CtrlCharge { path })
|
#[async_trait]
|
||||||
}
|
impl crate::Controller for CtrlCharge {
|
||||||
|
type A = u8;
|
||||||
|
|
||||||
/// Spawns two tasks which continuously check for changes
|
/// Spawns two tasks which continuously check for changes
|
||||||
pub(crate) fn spawn_task(
|
fn spawn_task(
|
||||||
ctrlr: CtrlCharge,
|
self,
|
||||||
config: Arc<Mutex<Config>>,
|
config: Arc<Mutex<Config>>,
|
||||||
mut recv: Receiver<u8>,
|
mut recv: Receiver<Self::A>,
|
||||||
|
_: Option<Arc<SyncConnection>>,
|
||||||
|
_: Option<Arc<Signal<()>>>,
|
||||||
) -> JoinHandle<()> {
|
) -> JoinHandle<()> {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
while let Some(n) = recv.recv().await {
|
while let Some(n) = recv.recv().await {
|
||||||
let mut config = config.lock().await;
|
let mut config = config.lock().await;
|
||||||
ctrlr
|
self.set_charge_limit(n, &mut config)
|
||||||
.set_charge_limit(n, &mut config)
|
|
||||||
.unwrap_or_else(|err| warn!("{:?}", err));
|
.unwrap_or_else(|err| warn!("{:?}", err));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn reload_from_config(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>> {
|
||||||
|
config.read();
|
||||||
|
info!("Reloaded battery charge limit");
|
||||||
|
self.set_charge_limit(config.bat_charge_limit, config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CtrlCharge {
|
||||||
|
pub(super) fn new() -> Result<Self, Box<dyn Error>> {
|
||||||
|
let path = CtrlCharge::get_battery_path()?;
|
||||||
|
info!("Device has battery charge threshold control");
|
||||||
|
Ok(CtrlCharge { path })
|
||||||
|
}
|
||||||
|
|
||||||
|
// /// Spawns two tasks which continuously check for changes
|
||||||
|
// pub(crate) fn spawn_task(
|
||||||
|
// self,
|
||||||
|
// config: Arc<Mutex<Config>>,
|
||||||
|
// mut recv: Receiver<u8>,
|
||||||
|
// ) -> JoinHandle<()> {
|
||||||
|
// tokio::spawn(async move {
|
||||||
|
// while let Some(n) = recv.recv().await {
|
||||||
|
// let mut config = config.lock().await;
|
||||||
|
// self
|
||||||
|
// .set_charge_limit(n, &mut config)
|
||||||
|
// .unwrap_or_else(|err| warn!("{:?}", err));
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
fn get_battery_path() -> Result<&'static str, std::io::Error> {
|
fn get_battery_path() -> Result<&'static str, std::io::Error> {
|
||||||
if Path::new(BAT_CHARGE_PATH).exists() {
|
if Path::new(BAT_CHARGE_PATH).exists() {
|
||||||
Ok(BAT_CHARGE_PATH)
|
Ok(BAT_CHARGE_PATH)
|
||||||
@@ -49,14 +80,14 @@ impl CtrlCharge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn bat_charge_limit_reload(
|
// pub(super) fn reload_from_config(
|
||||||
&self,
|
// &self,
|
||||||
config: &mut Config,
|
// config: &mut Config,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
// ) -> Result<(), Box<dyn Error>> {
|
||||||
config.read();
|
// config.read();
|
||||||
info!("Reloaded battery charge limit");
|
// info!("Reloaded battery charge limit");
|
||||||
self.set_charge_limit(config.bat_charge_limit, config)
|
// self.set_charge_limit(config.bat_charge_limit, config)
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub(super) fn set_charge_limit(
|
pub(super) fn set_charge_limit(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -18,20 +18,22 @@ pub struct CtrlFanAndCPU {
|
|||||||
path: &'static str,
|
path: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CtrlFanAndCPU {
|
use ::dbus::{nonblock::SyncConnection, tree::Signal};
|
||||||
pub(super) fn new() -> Result<Self, Box<dyn Error>> {
|
use async_trait::async_trait;
|
||||||
let path = CtrlFanAndCPU::get_fan_path()?;
|
|
||||||
|
|
||||||
Ok(CtrlFanAndCPU { path })
|
#[async_trait]
|
||||||
}
|
impl crate::Controller for CtrlFanAndCPU {
|
||||||
|
type A = u8;
|
||||||
|
|
||||||
/// Spawns two tasks which continuously check for changes
|
/// Spawns two tasks which continuously check for changes
|
||||||
pub(crate) fn spawn_task(
|
fn spawn_task(
|
||||||
ctrlr: CtrlFanAndCPU,
|
self,
|
||||||
config: Arc<Mutex<Config>>,
|
config: Arc<Mutex<Config>>,
|
||||||
mut recv: Receiver<u8>,
|
mut recv: Receiver<Self::A>,
|
||||||
|
_: Option<Arc<SyncConnection>>,
|
||||||
|
_: Option<Arc<Signal<()>>>,
|
||||||
) -> JoinHandle<()> {
|
) -> JoinHandle<()> {
|
||||||
let gate1 = Arc::new(Mutex::new(ctrlr));
|
let gate1 = Arc::new(Mutex::new(self));
|
||||||
let gate2 = gate1.clone();
|
let gate2 = gate1.clone();
|
||||||
let config1 = config.clone();
|
let config1 = config.clone();
|
||||||
// spawn an endless loop
|
// spawn an endless loop
|
||||||
@@ -58,6 +60,23 @@ impl CtrlFanAndCPU {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn reload_from_config(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut file = OpenOptions::new().write(true).open(self.path)?;
|
||||||
|
file.write_all(format!("{:?}\n", config.fan_mode).as_bytes())
|
||||||
|
.unwrap_or_else(|err| error!("Could not write to {}, {:?}", self.path, err));
|
||||||
|
self.set_pstate_for_fan_mode(FanLevel::from(config.fan_mode), config)?;
|
||||||
|
info!("Reloaded fan mode: {:?}", FanLevel::from(config.fan_mode));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CtrlFanAndCPU {
|
||||||
|
pub(super) fn new() -> Result<Self, Box<dyn Error>> {
|
||||||
|
let path = CtrlFanAndCPU::get_fan_path()?;
|
||||||
|
info!("Device has thermal throttle control");
|
||||||
|
Ok(CtrlFanAndCPU { path })
|
||||||
|
}
|
||||||
|
|
||||||
fn get_fan_path() -> Result<&'static str, std::io::Error> {
|
fn get_fan_path() -> Result<&'static str, std::io::Error> {
|
||||||
if Path::new(FAN_TYPE_1_PATH).exists() {
|
if Path::new(FAN_TYPE_1_PATH).exists() {
|
||||||
Ok(FAN_TYPE_1_PATH)
|
Ok(FAN_TYPE_1_PATH)
|
||||||
@@ -71,15 +90,6 @@ impl CtrlFanAndCPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn fan_mode_reload(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>> {
|
|
||||||
let mut file = OpenOptions::new().write(true).open(self.path)?;
|
|
||||||
file.write_all(format!("{:?}\n", config.fan_mode).as_bytes())
|
|
||||||
.unwrap_or_else(|err| error!("Could not write to {}, {:?}", self.path, err));
|
|
||||||
self.set_pstate_for_fan_mode(FanLevel::from(config.fan_mode), config)?;
|
|
||||||
info!("Reloaded fan mode: {:?}", FanLevel::from(config.fan_mode));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn fan_mode_check_change(
|
pub(super) fn fan_mode_check_change(
|
||||||
&mut self,
|
&mut self,
|
||||||
config: &mut Config,
|
config: &mut Config,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use rog_client::{
|
|||||||
aura_brightness_bytes, aura_modes::AuraModes, fancy::KeyColourArray, DBUS_IFACE, DBUS_PATH,
|
aura_brightness_bytes, aura_modes::AuraModes, fancy::KeyColourArray, DBUS_IFACE, DBUS_PATH,
|
||||||
LED_MSG_LEN,
|
LED_MSG_LEN,
|
||||||
};
|
};
|
||||||
|
use std::error::Error;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -16,67 +17,47 @@ use tokio::sync::mpsc::Receiver;
|
|||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
pub struct LedWriter {
|
pub struct CtrlKbdBacklight {
|
||||||
dev_node: String,
|
dev_node: String,
|
||||||
supported_modes: Vec<u8>,
|
supported_modes: Vec<u8>,
|
||||||
flip_effect_write: bool,
|
flip_effect_write: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LedWriter {
|
use async_trait::async_trait;
|
||||||
#[inline]
|
|
||||||
pub fn new(id_product: &str, supported_modes: Vec<u8>) -> Result<Self, std::io::Error> {
|
|
||||||
let mut enumerator = udev::Enumerator::new()?;
|
|
||||||
enumerator.match_subsystem("hidraw")?;
|
|
||||||
|
|
||||||
for device in enumerator.scan_devices()? {
|
#[async_trait]
|
||||||
if let Some(parent) = device.parent_with_subsystem_devtype("usb", "usb_device")? {
|
impl crate::Controller for CtrlKbdBacklight {
|
||||||
if parent.attribute_value("idProduct").unwrap() == id_product
|
type A = AuraModes;
|
||||||
// && device.parent().unwrap().sysnum().unwrap() == 3
|
|
||||||
{
|
|
||||||
if let Some(dev_node) = device.devnode() {
|
|
||||||
info!("Using device at: {:?} for LED control", dev_node);
|
|
||||||
return Ok(LedWriter {
|
|
||||||
dev_node: dev_node.to_string_lossy().to_string(),
|
|
||||||
supported_modes,
|
|
||||||
flip_effect_write: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(std::io::Error::new(
|
|
||||||
std::io::ErrorKind::NotFound,
|
|
||||||
"Device node not found",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Spawns two tasks which continuously check for changes
|
/// Spawns two tasks which continuously check for changes
|
||||||
pub(crate) fn spawn_task(
|
fn spawn_task(
|
||||||
mut ctrlr: LedWriter,
|
mut self,
|
||||||
config: Arc<Mutex<Config>>,
|
config: Arc<Mutex<Config>>,
|
||||||
mut recv: Receiver<AuraModes>,
|
mut recv: Receiver<Self::A>,
|
||||||
connection: Arc<SyncConnection>,
|
connection: Option<Arc<SyncConnection>>,
|
||||||
signal: Arc<Signal<()>>,
|
signal: Option<Arc<Signal<()>>>,
|
||||||
) -> JoinHandle<()> {
|
) -> JoinHandle<()> {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
while let Some(command) = recv.recv().await {
|
while let Some(command) = recv.recv().await {
|
||||||
let mut config = config.lock().await;
|
let mut config = config.lock().await;
|
||||||
match &command {
|
match &command {
|
||||||
AuraModes::RGB(_) => {
|
AuraModes::RGB(_) => {
|
||||||
ctrlr
|
self.do_command(command, &mut config)
|
||||||
.do_command(command, &mut config)
|
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let json = serde_json::to_string(&command).unwrap();
|
let json = serde_json::to_string(&command).unwrap();
|
||||||
ctrlr
|
self.do_command(command, &mut config)
|
||||||
.do_command(command, &mut config)
|
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
connection
|
connection
|
||||||
|
.as_ref()
|
||||||
|
.expect("LED Controller must have DBUS connection")
|
||||||
.send(
|
.send(
|
||||||
signal
|
signal
|
||||||
|
.as_ref()
|
||||||
|
.expect("LED Controller must have DBUS signal")
|
||||||
.msg(&DBUS_PATH.into(), &DBUS_IFACE.into())
|
.msg(&DBUS_PATH.into(), &DBUS_IFACE.into())
|
||||||
.append1(json),
|
.append1(json),
|
||||||
)
|
)
|
||||||
@@ -87,111 +68,7 @@ impl LedWriter {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn do_command(
|
async fn reload_from_config(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>> {
|
||||||
&mut self,
|
|
||||||
mode: AuraModes,
|
|
||||||
config: &mut Config,
|
|
||||||
) -> Result<(), RogError> {
|
|
||||||
self.set_and_save(mode, config).await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Should only be used if the bytes you are writing are verified correct
|
|
||||||
#[inline]
|
|
||||||
async fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
|
|
||||||
if let Ok(mut file) = OpenOptions::new().write(true).open(&self.dev_node) {
|
|
||||||
file.write_all(message).unwrap();
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
Err(RogError::NotSupported)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write an effect block
|
|
||||||
#[inline]
|
|
||||||
async fn write_effect(&mut self, effect: &[Vec<u8>]) -> Result<(), RogError> {
|
|
||||||
if self.flip_effect_write {
|
|
||||||
for row in effect.iter().rev() {
|
|
||||||
self.write_bytes(row).await?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for row in effect.iter() {
|
|
||||||
self.write_bytes(row).await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.flip_effect_write = !self.flip_effect_write;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Used to set a builtin mode and save the settings for it
|
|
||||||
///
|
|
||||||
/// This needs to be universal so that settings applied by dbus stick
|
|
||||||
#[inline]
|
|
||||||
async fn set_and_save(&mut self, mode: AuraModes, config: &mut Config) -> Result<(), RogError> {
|
|
||||||
match mode {
|
|
||||||
AuraModes::LedBrightness(n) => {
|
|
||||||
let bytes: [u8; LED_MSG_LEN] = (&mode).into();
|
|
||||||
self.write_bytes(&bytes).await?;
|
|
||||||
config.brightness = n;
|
|
||||||
config.write();
|
|
||||||
info!("LED brightness set to {:#?}", n);
|
|
||||||
}
|
|
||||||
AuraModes::RGB(v) => {
|
|
||||||
if v.is_empty() || v[0].is_empty() {
|
|
||||||
let bytes = KeyColourArray::get_init_msg();
|
|
||||||
self.write_bytes(&bytes).await?;
|
|
||||||
} else {
|
|
||||||
self.write_effect(&v).await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let mode_num: u8 = u8::from(&mode);
|
|
||||||
self.write_mode(&mode).await?;
|
|
||||||
config.current_mode = mode_num;
|
|
||||||
config.set_mode_data(mode);
|
|
||||||
config.write();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
async fn write_mode(&mut self, mode: &AuraModes) -> Result<(), RogError> {
|
|
||||||
match mode {
|
|
||||||
AuraModes::RGB(v) => {
|
|
||||||
if v.is_empty() || v[0].is_empty() {
|
|
||||||
let bytes = KeyColourArray::get_init_msg();
|
|
||||||
self.write_bytes(&bytes).await?;
|
|
||||||
} else {
|
|
||||||
self.write_effect(v).await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let mode_num: u8 = u8::from(mode);
|
|
||||||
match mode {
|
|
||||||
AuraModes::MultiStatic(_) => {
|
|
||||||
if self.supported_modes.contains(&mode_num) {
|
|
||||||
let bytes: [[u8; LED_MSG_LEN]; 4] = mode.into();
|
|
||||||
for array in bytes.iter() {
|
|
||||||
self.write_bytes(array).await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
if self.supported_modes.contains(&mode_num) {
|
|
||||||
let bytes: [u8; LED_MSG_LEN] = mode.into();
|
|
||||||
self.write_bytes(&bytes).await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.write_bytes(&LED_SET).await?;
|
|
||||||
// Changes won't persist unless apply is set
|
|
||||||
self.write_bytes(&LED_APPLY).await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub async fn reload_last_builtin(&mut self, config: &mut Config) -> Result<(), RogError> {
|
|
||||||
// set current mode (if any)
|
// set current mode (if any)
|
||||||
if self.supported_modes.len() > 1 {
|
if self.supported_modes.len() > 1 {
|
||||||
if self.supported_modes.contains(&config.current_mode) {
|
if self.supported_modes.contains(&config.current_mode) {
|
||||||
@@ -232,3 +109,145 @@ impl LedWriter {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CtrlKbdBacklight {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(id_product: &str, supported_modes: Vec<u8>) -> Result<Self, std::io::Error> {
|
||||||
|
let mut enumerator = udev::Enumerator::new()?;
|
||||||
|
enumerator.match_subsystem("hidraw")?;
|
||||||
|
|
||||||
|
for device in enumerator.scan_devices()? {
|
||||||
|
if let Some(parent) = device.parent_with_subsystem_devtype("usb", "usb_device")? {
|
||||||
|
if parent.attribute_value("idProduct").unwrap() == id_product
|
||||||
|
// && device.parent().unwrap().sysnum().unwrap() == 3
|
||||||
|
{
|
||||||
|
if let Some(dev_node) = device.devnode() {
|
||||||
|
info!("Device has keyboard backlight control");
|
||||||
|
info!("Using device at: {:?} for LED control", dev_node);
|
||||||
|
return Ok(CtrlKbdBacklight {
|
||||||
|
dev_node: dev_node.to_string_lossy().to_string(),
|
||||||
|
supported_modes,
|
||||||
|
flip_effect_write: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::NotFound,
|
||||||
|
"Device node not found",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn do_command(
|
||||||
|
&mut self,
|
||||||
|
mode: AuraModes,
|
||||||
|
config: &mut Config,
|
||||||
|
) -> Result<(), Box<dyn Error>> {
|
||||||
|
self.set_and_save(mode, config).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Should only be used if the bytes you are writing are verified correct
|
||||||
|
#[inline]
|
||||||
|
async fn write_bytes(&self, message: &[u8]) -> Result<(), Box<dyn Error>> {
|
||||||
|
if let Ok(mut file) = OpenOptions::new().write(true).open(&self.dev_node) {
|
||||||
|
file.write_all(message).unwrap();
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(Box::new(RogError::NotSupported))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write an effect block
|
||||||
|
#[inline]
|
||||||
|
async fn write_effect(&mut self, effect: &[Vec<u8>]) -> Result<(), Box<dyn Error>> {
|
||||||
|
if self.flip_effect_write {
|
||||||
|
for row in effect.iter().rev() {
|
||||||
|
self.write_bytes(row).await?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for row in effect.iter() {
|
||||||
|
self.write_bytes(row).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.flip_effect_write = !self.flip_effect_write;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used to set a builtin mode and save the settings for it
|
||||||
|
///
|
||||||
|
/// This needs to be universal so that settings applied by dbus stick
|
||||||
|
#[inline]
|
||||||
|
async fn set_and_save(
|
||||||
|
&mut self,
|
||||||
|
mode: AuraModes,
|
||||||
|
config: &mut Config,
|
||||||
|
) -> Result<(), Box<dyn Error>> {
|
||||||
|
match mode {
|
||||||
|
AuraModes::LedBrightness(n) => {
|
||||||
|
let bytes: [u8; LED_MSG_LEN] = (&mode).into();
|
||||||
|
self.write_bytes(&bytes).await?;
|
||||||
|
config.brightness = n;
|
||||||
|
config.write();
|
||||||
|
info!("LED brightness set to {:#?}", n);
|
||||||
|
}
|
||||||
|
AuraModes::RGB(v) => {
|
||||||
|
if v.is_empty() || v[0].is_empty() {
|
||||||
|
let bytes = KeyColourArray::get_init_msg();
|
||||||
|
self.write_bytes(&bytes).await?;
|
||||||
|
} else {
|
||||||
|
self.write_effect(&v).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let mode_num: u8 = u8::from(&mode);
|
||||||
|
self.write_mode(&mode).await?;
|
||||||
|
config.current_mode = mode_num;
|
||||||
|
config.set_mode_data(mode);
|
||||||
|
config.write();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
async fn write_mode(&mut self, mode: &AuraModes) -> Result<(), Box<dyn Error>> {
|
||||||
|
match mode {
|
||||||
|
AuraModes::RGB(v) => {
|
||||||
|
if v.is_empty() || v[0].is_empty() {
|
||||||
|
let bytes = KeyColourArray::get_init_msg();
|
||||||
|
self.write_bytes(&bytes).await?;
|
||||||
|
} else {
|
||||||
|
self.write_effect(v).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let mode_num: u8 = u8::from(mode);
|
||||||
|
match mode {
|
||||||
|
AuraModes::MultiStatic(_) => {
|
||||||
|
if self.supported_modes.contains(&mode_num) {
|
||||||
|
let bytes: [[u8; LED_MSG_LEN]; 4] = mode.into();
|
||||||
|
for array in bytes.iter() {
|
||||||
|
self.write_bytes(array).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if self.supported_modes.contains(&mode_num) {
|
||||||
|
let bytes: [u8; LED_MSG_LEN] = mode.into();
|
||||||
|
self.write_bytes(&bytes).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.write_bytes(&LED_SET).await?;
|
||||||
|
// Changes won't persist unless apply is set
|
||||||
|
self.write_bytes(&LED_APPLY).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[inline]
|
||||||
|
// pub async fn reload_from_config(&mut self, config: &mut Config) -> Result<(), RogError> {
|
||||||
|
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
config::Config, ctrl_anime::AniMeWriter, ctrl_charge::CtrlCharge, ctrl_fan_cpu::CtrlFanAndCPU,
|
config::Config, ctrl_anime::CtrlAnimeDisplay, ctrl_charge::CtrlCharge, ctrl_fan_cpu::CtrlFanAndCPU,
|
||||||
ctrl_leds::LedWriter, dbus::dbus_create_tree, laptops::match_laptop,
|
ctrl_leds::CtrlKbdBacklight, dbus::dbus_create_tree, laptops::match_laptop,
|
||||||
};
|
};
|
||||||
|
|
||||||
use dbus::{channel::Sender, nonblock::SyncConnection, tree::Signal};
|
use dbus::{channel::Sender, nonblock::SyncConnection, tree::Signal};
|
||||||
|
|
||||||
|
use crate::Controller;
|
||||||
use dbus_tokio::connection;
|
use dbus_tokio::connection;
|
||||||
use log::{error, info, warn};
|
use log::{error, warn};
|
||||||
use rog_client::{DBUS_IFACE, DBUS_NAME, DBUS_PATH};
|
use rog_client::{DBUS_IFACE, DBUS_NAME, DBUS_PATH};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -25,16 +26,13 @@ pub async fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
let laptop = match_laptop();
|
let laptop = match_laptop();
|
||||||
let mut config = Config::default().load(laptop.supported_modes());
|
let mut config = Config::default().load(laptop.supported_modes());
|
||||||
|
|
||||||
let mut led_control = LedWriter::new(laptop.usb_product(), laptop.supported_modes().to_owned())
|
let mut led_control = CtrlKbdBacklight::new(laptop.usb_product(), laptop.supported_modes().to_owned())
|
||||||
.map_or_else(
|
.map_or_else(
|
||||||
|err| {
|
|err| {
|
||||||
error!("{}", err);
|
error!("{}", err);
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
|ledwriter| {
|
Some,
|
||||||
info!("Device has keyboard backlight control");
|
|
||||||
Some(ledwriter)
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut charge_control = CtrlCharge::new().map_or_else(
|
let mut charge_control = CtrlCharge::new().map_or_else(
|
||||||
@@ -42,10 +40,7 @@ pub async fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
error!("{}", err);
|
error!("{}", err);
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
|ledwriter| {
|
Some,
|
||||||
info!("Device has battery charge threshold control");
|
|
||||||
Some(ledwriter)
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut fan_control = CtrlFanAndCPU::new().map_or_else(
|
let mut fan_control = CtrlFanAndCPU::new().map_or_else(
|
||||||
@@ -53,34 +48,28 @@ pub async fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
error!("{}", err);
|
error!("{}", err);
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
|ledwriter| {
|
Some,
|
||||||
info!("Device has thermal throttle control");
|
|
||||||
Some(ledwriter)
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Reload settings
|
// Reload settings
|
||||||
if let Some(ctrlr) = fan_control.as_mut() {
|
if let Some(ctrl) = fan_control.as_mut() {
|
||||||
ctrlr
|
ctrl.reload_from_config(&mut config)
|
||||||
.fan_mode_reload(&mut config)
|
.await
|
||||||
.unwrap_or_else(|err| warn!("Fan mode: {}", err));
|
.unwrap_or_else(|err| warn!("Fan mode: {}", err));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ctrlr) = charge_control.as_mut() {
|
if let Some(ctrl) = charge_control.as_mut() {
|
||||||
ctrlr
|
ctrl.reload_from_config(&mut config)
|
||||||
.bat_charge_limit_reload(&mut config)
|
.await
|
||||||
.unwrap_or_else(|err| warn!("Battery charge limit: {}", err));
|
.unwrap_or_else(|err| warn!("Battery charge limit: {}", err));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(writer) = led_control.as_mut() {
|
if let Some(ctrl) = led_control.as_mut() {
|
||||||
writer
|
ctrl.reload_from_config(&mut config)
|
||||||
.reload_last_builtin(&mut config)
|
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|err| warn!("Reload settings: {}", err));
|
.unwrap_or_else(|err| warn!("Reload settings: {}", err));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the mutexes
|
|
||||||
let config = Arc::new(Mutex::new(config));
|
|
||||||
let (resource, connection) = connection::new_system_sync()?;
|
let (resource, connection) = connection::new_system_sync()?;
|
||||||
tokio::spawn(async {
|
tokio::spawn(async {
|
||||||
let err = resource.await;
|
let err = resource.await;
|
||||||
@@ -91,6 +80,7 @@ pub async fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
.request_name(DBUS_NAME, false, true, true)
|
.request_name(DBUS_NAME, false, true, true)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
let config = Arc::new(Mutex::new(config));
|
||||||
let (
|
let (
|
||||||
tree,
|
tree,
|
||||||
aura_command_recv,
|
aura_command_recv,
|
||||||
@@ -101,6 +91,7 @@ pub async fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
fanmode_signal,
|
fanmode_signal,
|
||||||
charge_limit_signal,
|
charge_limit_signal,
|
||||||
) = dbus_create_tree(config.clone());
|
) = dbus_create_tree(config.clone());
|
||||||
|
|
||||||
// We add the tree to the connection so that incoming method calls will be handled.
|
// We add the tree to the connection so that incoming method calls will be handled.
|
||||||
tree.start_receive_send(&*connection);
|
tree.start_receive_send(&*connection);
|
||||||
|
|
||||||
@@ -122,28 +113,26 @@ pub async fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
charge_limit_signal,
|
charge_limit_signal,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut handles = Vec::new();
|
|
||||||
// Begin all tasks
|
// Begin all tasks
|
||||||
if let Ok(ctrlr) = AniMeWriter::new() {
|
let mut handles = Vec::new();
|
||||||
info!("Device has an AniMe Matrix display");
|
if let Ok(ctrl) = CtrlAnimeDisplay::new() {
|
||||||
handles.push(AniMeWriter::spawn_task(ctrlr, animatrix_recv));
|
handles.push(ctrl.spawn_task(config.clone(), animatrix_recv, None, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ctrlr) = fan_control.take() {
|
if let Some(ctrl) = fan_control.take() {
|
||||||
handles.push(CtrlFanAndCPU::spawn_task(ctrlr, config.clone(), fan_mode_recv));
|
handles.push(ctrl.spawn_task(config.clone(), fan_mode_recv, None, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ctrlr) = charge_control.take() {
|
if let Some(ctrl) = charge_control.take() {
|
||||||
handles.push(CtrlCharge::spawn_task(ctrlr, config.clone(), charge_limit_recv));
|
handles.push(ctrl.spawn_task(config.clone(), charge_limit_recv, None, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ctrlr) = led_control.take() {
|
if let Some(ctrl) = led_control.take() {
|
||||||
handles.push(LedWriter::spawn_task(
|
handles.push(ctrl.spawn_task(
|
||||||
ctrlr,
|
|
||||||
config.clone(),
|
config.clone(),
|
||||||
aura_command_recv,
|
aura_command_recv,
|
||||||
connection.clone(),
|
Some(connection.clone()),
|
||||||
led_changed_signal,
|
Some(led_changed_signal),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,6 +143,7 @@ pub async fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Move these in to the controllers tasks
|
||||||
fn start_signal_task(
|
fn start_signal_task(
|
||||||
connection: Arc<SyncConnection>,
|
connection: Arc<SyncConnection>,
|
||||||
config: Arc<Mutex<Config>>,
|
config: Arc<Mutex<Config>>,
|
||||||
@@ -179,6 +169,7 @@ fn start_signal_task(
|
|||||||
)
|
)
|
||||||
.unwrap_or_else(|_| 0);
|
.unwrap_or_else(|_| 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.bat_charge_limit != last_charge_limit {
|
if config.bat_charge_limit != last_charge_limit {
|
||||||
last_charge_limit = config.bat_charge_limit;
|
last_charge_limit = config.bat_charge_limit;
|
||||||
connection
|
connection
|
||||||
@@ -200,7 +191,9 @@ async fn send_boot_signals(
|
|||||||
charge_limit_signal: Arc<Signal<()>>,
|
charge_limit_signal: Arc<Signal<()>>,
|
||||||
led_changed_signal: Arc<Signal<()>>,
|
led_changed_signal: Arc<Signal<()>>,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<(), Box<dyn Error>> {
|
||||||
|
|
||||||
let config = config.lock().await;
|
let config = config.lock().await;
|
||||||
|
|
||||||
if let Some(data) = config.get_led_mode_data(config.current_mode) {
|
if let Some(data) = config.get_led_mode_data(config.current_mode) {
|
||||||
connection
|
connection
|
||||||
.send(
|
.send(
|
||||||
@@ -210,6 +203,7 @@ async fn send_boot_signals(
|
|||||||
)
|
)
|
||||||
.unwrap_or_else(|_| 0);
|
.unwrap_or_else(|_| 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
connection
|
connection
|
||||||
.send(
|
.send(
|
||||||
fanmode_signal
|
fanmode_signal
|
||||||
@@ -217,6 +211,7 @@ async fn send_boot_signals(
|
|||||||
.append1(config.fan_mode),
|
.append1(config.fan_mode),
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|_| 0);
|
.unwrap_or_else(|_| 0);
|
||||||
|
|
||||||
connection
|
connection
|
||||||
.send(
|
.send(
|
||||||
charge_limit_signal
|
charge_limit_signal
|
||||||
@@ -224,5 +219,6 @@ async fn send_boot_signals(
|
|||||||
.append1(config.bat_charge_limit),
|
.append1(config.bat_charge_limit),
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|_| 0);
|
.unwrap_or_else(|_| 0);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum RogError {
|
pub enum RogError {
|
||||||
ParseFanLevel,
|
ParseFanLevel,
|
||||||
NotSupported,
|
NotSupported,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for RogError {}
|
||||||
|
|
||||||
impl fmt::Display for RogError {
|
impl fmt::Display for RogError {
|
||||||
// This trait requires `fmt` with this exact signature.
|
// This trait requires `fmt` with this exact signature.
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
|||||||
@@ -17,3 +17,29 @@ mod dbus;
|
|||||||
mod laptops;
|
mod laptops;
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use config::Config;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::{mpsc::Receiver, Mutex};
|
||||||
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
|
use ::dbus::{nonblock::SyncConnection, tree::Signal};
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait Controller {
|
||||||
|
type A;
|
||||||
|
|
||||||
|
async fn reload_from_config(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>>;
|
||||||
|
|
||||||
|
/// Spawn an infinitely running task (usually) which checks a Receiver for input,
|
||||||
|
/// and may send a signal over dbus
|
||||||
|
fn spawn_task(
|
||||||
|
self,
|
||||||
|
config: Arc<Mutex<Config>>,
|
||||||
|
recv: Receiver<Self::A>,
|
||||||
|
connection: Option<Arc<SyncConnection>>,
|
||||||
|
signal: Option<Arc<Signal<()>>>,
|
||||||
|
) -> JoinHandle<()>;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user