mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
Refactor code to reduce LedWriter to single thread
- Fixes race conditions (mostly) between awaits - Fixes possible deadlock of mode changes when effect is running - Fixes sending kill signal to effect clients
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
use rog_aura::{AuraDbusWriter, Key, KeyColourArray};
|
use rog_aura::{AuraDbusWriter, Key, KeyColourArray};
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let writer = AuraDbusWriter::new()?;
|
let mut writer = AuraDbusWriter::new()?;
|
||||||
|
|
||||||
let mut per_key_led = Vec::new();
|
let mut per_key_led = Vec::new();
|
||||||
let mut key_colours = KeyColourArray::new();
|
let mut key_colours = KeyColourArray::new();
|
||||||
|
|||||||
@@ -1,20 +1,26 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use dbus::{ffidisp::Connection, Message};
|
use dbus::blocking::BlockingSender;
|
||||||
|
use dbus::channel::Sender;
|
||||||
|
use dbus::{blocking::Connection, Message};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
use std::{thread, time::Duration};
|
use std::{thread, time::Duration};
|
||||||
|
|
||||||
/// Simplified way to write a effect block
|
/// Simplified way to write a effect block
|
||||||
pub struct AuraDbusWriter {
|
pub struct AuraDbusWriter {
|
||||||
connection: Connection,
|
connection: Box<Connection>,
|
||||||
block_time: u64,
|
block_time: u64,
|
||||||
|
stop: Arc<Mutex<bool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuraDbusWriter {
|
impl AuraDbusWriter {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> Result<Self, Box<dyn Error>> {
|
pub fn new() -> Result<Self, Box<dyn Error>> {
|
||||||
|
let connection = Connection::new_system()?;
|
||||||
Ok(AuraDbusWriter {
|
Ok(AuraDbusWriter {
|
||||||
connection: Connection::new_system()?,
|
connection: Box::new(connection),
|
||||||
block_time: 10,
|
block_time: 10,
|
||||||
|
stop: Arc::new(Mutex::new(false)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,7 +28,23 @@ impl AuraDbusWriter {
|
|||||||
/// the keyboard LED EC in the correct mode
|
/// the keyboard LED EC in the correct mode
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn init_effect(&self) -> Result<(), Box<dyn Error>> {
|
pub fn init_effect(&self) -> Result<(), Box<dyn Error>> {
|
||||||
let msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "ledmessage")?
|
let match_rule = dbus::message::MatchRule::new_signal(DBUS_IFACE, "LedCancelEffect");
|
||||||
|
let stopper = self.stop.clone();
|
||||||
|
self.connection
|
||||||
|
.add_match(match_rule, move |_: (), _, msg| {
|
||||||
|
println!("GOT {:?}", msg);
|
||||||
|
if let Ok(stop) = msg.read1::<bool>() {
|
||||||
|
if stop {
|
||||||
|
if let Ok(mut lock) = stopper.lock() {
|
||||||
|
println!("SHOULD STOP");
|
||||||
|
*lock = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "LedWriteBytes")?
|
||||||
.append1(KeyColourArray::get_init_msg());
|
.append1(KeyColourArray::get_init_msg());
|
||||||
self.connection.send(msg).unwrap();
|
self.connection.send(msg).unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -34,11 +56,13 @@ impl AuraDbusWriter {
|
|||||||
/// be written to the keyboard EC. This should not be async.
|
/// be written to the keyboard EC. This should not be async.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn write_colour_block(
|
pub fn write_colour_block(
|
||||||
&self,
|
&mut self,
|
||||||
key_colour_array: &KeyColourArray,
|
key_colour_array: &KeyColourArray,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<(), Box<dyn Error>> {
|
||||||
|
self.connection.process(Duration::from_micros(300))?;
|
||||||
|
|
||||||
let group = key_colour_array.get();
|
let group = key_colour_array.get();
|
||||||
let msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "ledeffect")?
|
let msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "LedWriteEffect")?
|
||||||
.append1(&group[0].to_vec())
|
.append1(&group[0].to_vec())
|
||||||
.append1(&group[1].to_vec())
|
.append1(&group[1].to_vec())
|
||||||
.append1(&group[2].to_vec())
|
.append1(&group[2].to_vec())
|
||||||
@@ -51,14 +75,21 @@ impl AuraDbusWriter {
|
|||||||
.append1(&group[9].to_vec());
|
.append1(&group[9].to_vec());
|
||||||
self.connection.send(msg).unwrap();
|
self.connection.send(msg).unwrap();
|
||||||
thread::sleep(Duration::from_millis(self.block_time));
|
thread::sleep(Duration::from_millis(self.block_time));
|
||||||
|
if let Ok(lock) = self.stop.try_lock() {
|
||||||
|
if *lock {
|
||||||
|
panic!("Stopping!");
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn write_bytes(&self, bytes: &[u8]) -> Result<String, Box<dyn std::error::Error>> {
|
pub fn write_bytes(&self, bytes: &[u8]) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
let msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "ledmessage")?
|
let msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "LedWriteBytes")?
|
||||||
.append1(bytes.to_vec());
|
.append1(bytes.to_vec());
|
||||||
let r = self.connection.send_with_reply_and_block(msg, 5000)?;
|
let r = self
|
||||||
|
.connection
|
||||||
|
.send_with_reply_and_block(msg, Duration::from_millis(5000))?;
|
||||||
if let Some(reply) = r.get1::<&str>() {
|
if let Some(reply) = r.get1::<&str>() {
|
||||||
return Ok(reply.to_owned());
|
return Ok(reply.to_owned());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ impl BuiltInModeBytes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_field_from(&mut self, byte: u8) -> Option<&[u8]> {
|
pub fn get_field_from(&self, byte: u8) -> Option<&[u8]> {
|
||||||
let bytes = match BuiltInModeByte::from(byte) {
|
let bytes = match BuiltInModeByte::from(byte) {
|
||||||
BuiltInModeByte::Single => &self.stable,
|
BuiltInModeByte::Single => &self.stable,
|
||||||
BuiltInModeByte::Breathing => &self.breathe,
|
BuiltInModeByte::Breathing => &self.breathe,
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ env_logger = "^0.7.1"
|
|||||||
# async
|
# async
|
||||||
dbus = { version = "^0.8.2", features = ["futures"] }
|
dbus = { version = "^0.8.2", features = ["futures"] }
|
||||||
dbus-tokio = "^0.5.1"
|
dbus-tokio = "^0.5.1"
|
||||||
tokio = { version = "0.2.4", features = ["rt-threaded", "macros", "sync"] }
|
tokio = { version = "0.2.4", features = ["rt-threaded", "sync"] }
|
||||||
|
|
||||||
# serialisation
|
# serialisation
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
|
|||||||
@@ -300,6 +300,16 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum AuraCommand {
|
||||||
|
BrightInc,
|
||||||
|
BrightDec,
|
||||||
|
BuiltinNext,
|
||||||
|
BuiltinPrev,
|
||||||
|
WriteBytes(Vec<u8>),
|
||||||
|
WriteEffect(Vec<Vec<u8>>),
|
||||||
|
ReloadLast,
|
||||||
|
}
|
||||||
|
|
||||||
/// UNSAFE: Must live as long as RogCore
|
/// UNSAFE: Must live as long as RogCore
|
||||||
///
|
///
|
||||||
/// Because we're holding a pointer to something that *may* go out of scope while the
|
/// Because we're holding a pointer to something that *may* go out of scope while the
|
||||||
@@ -310,6 +320,8 @@ where
|
|||||||
C: rusb::UsbContext,
|
C: rusb::UsbContext,
|
||||||
{
|
{
|
||||||
handle: NonNull<DeviceHandle<C>>,
|
handle: NonNull<DeviceHandle<C>>,
|
||||||
|
bright_min_max: (u8, u8),
|
||||||
|
supported_modes: Vec<BuiltInModeByte>,
|
||||||
led_endpoint: u8,
|
led_endpoint: u8,
|
||||||
initialised: bool,
|
initialised: bool,
|
||||||
_phantom: PhantomData<&'d DeviceHandle<C>>,
|
_phantom: PhantomData<&'d DeviceHandle<C>>,
|
||||||
@@ -323,61 +335,125 @@ impl<'d, C> LedWriter<'d, C>
|
|||||||
where
|
where
|
||||||
C: rusb::UsbContext,
|
C: rusb::UsbContext,
|
||||||
{
|
{
|
||||||
pub fn new(device_handle: NonNull<DeviceHandle<C>>, led_endpoint: u8) -> Self {
|
pub fn new(
|
||||||
|
device_handle: NonNull<DeviceHandle<C>>,
|
||||||
|
led_endpoint: u8,
|
||||||
|
bright_min_max: (u8, u8),
|
||||||
|
supported_modes: Vec<BuiltInModeByte>,
|
||||||
|
) -> Self {
|
||||||
LedWriter {
|
LedWriter {
|
||||||
handle: device_handle,
|
handle: device_handle,
|
||||||
led_endpoint,
|
led_endpoint,
|
||||||
|
bright_min_max,
|
||||||
|
supported_modes,
|
||||||
initialised: false,
|
initialised: false,
|
||||||
_phantom: PhantomData,
|
_phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn do_command(
|
||||||
|
&mut self,
|
||||||
|
command: AuraCommand,
|
||||||
|
config: &mut Config,
|
||||||
|
) -> Result<(), AuraError> {
|
||||||
|
if !self.initialised {
|
||||||
|
self.write_bytes(&LED_INIT1).await?;
|
||||||
|
self.write_bytes(LED_INIT2.as_bytes()).await?;
|
||||||
|
self.write_bytes(&LED_INIT3).await?;
|
||||||
|
self.write_bytes(LED_INIT4.as_bytes()).await?;
|
||||||
|
self.write_bytes(&LED_INIT5).await?;
|
||||||
|
self.initialised = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
match command {
|
||||||
|
AuraCommand::BrightInc => {
|
||||||
|
let mut bright = config.brightness;
|
||||||
|
if bright < self.bright_min_max.1 {
|
||||||
|
bright += 1;
|
||||||
|
config.brightness = bright;
|
||||||
|
let bytes = aura_brightness_bytes(bright);
|
||||||
|
self.set_and_save(&bytes, config).await?;
|
||||||
|
info!("Increased LED brightness to {:#?}", bright);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AuraCommand::BrightDec => {
|
||||||
|
let mut bright = config.brightness;
|
||||||
|
if bright > self.bright_min_max.0 {
|
||||||
|
bright -= 1;
|
||||||
|
config.brightness = bright;
|
||||||
|
let bytes = aura_brightness_bytes(bright);
|
||||||
|
self.set_and_save(&bytes, config).await?;
|
||||||
|
info!("Decreased LED brightness to {:#?}", bright);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AuraCommand::BuiltinNext => {
|
||||||
|
// TODO: different path for multi-zone (byte 2 controlled, non-zero)
|
||||||
|
let mode_curr = config.current_mode[3];
|
||||||
|
let idx = self
|
||||||
|
.supported_modes
|
||||||
|
.binary_search(&mode_curr.into())
|
||||||
|
.unwrap();
|
||||||
|
let idx_next = if idx < self.supported_modes.len() - 1 {
|
||||||
|
idx + 1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
self.set_builtin(config, idx_next).await?;
|
||||||
|
}
|
||||||
|
AuraCommand::BuiltinPrev => {
|
||||||
|
// TODO: different path for multi-zone (byte 2 controlled, non-zero)
|
||||||
|
let mode_curr = config.current_mode[3];
|
||||||
|
let idx = self
|
||||||
|
.supported_modes
|
||||||
|
.binary_search(&mode_curr.into())
|
||||||
|
.unwrap();
|
||||||
|
let idx_next = if idx > 0 {
|
||||||
|
idx - 1
|
||||||
|
} else {
|
||||||
|
self.supported_modes.len() - 1
|
||||||
|
};
|
||||||
|
self.set_builtin(config, idx_next).await?;
|
||||||
|
}
|
||||||
|
AuraCommand::WriteBytes(bytes) => self.write_bytes(&bytes).await?,
|
||||||
|
AuraCommand::WriteEffect(effect) => self.write_effect(effect).await?,
|
||||||
|
AuraCommand::ReloadLast => self.reload_last_builtin(&config).await?,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Should only be used if the bytes you are writing are verified correct
|
/// Should only be used if the bytes you are writing are verified correct
|
||||||
pub async fn aura_write(&mut self, message: &[u8]) -> Result<(), AuraError> {
|
async fn write_bytes(&mut self, message: &[u8]) -> Result<(), AuraError> {
|
||||||
match unsafe { self.handle.as_ref() }.write_interrupt(
|
match unsafe { self.handle.as_ref() }.write_interrupt(
|
||||||
self.led_endpoint,
|
self.led_endpoint,
|
||||||
message,
|
message,
|
||||||
Duration::from_millis(1),
|
Duration::from_millis(2),
|
||||||
) {
|
) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(err) => match err {
|
Err(err) => match err {
|
||||||
rusb::Error::Timeout => {}
|
rusb::Error::Timeout => {}
|
||||||
_ => error!("Failed to read keyboard interrupt: {:?}", err),
|
_ => error!("Failed to write to led interrupt: {:?}", err),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn aura_write_messages(&mut self, messages: &[&[u8]]) -> Result<(), AuraError> {
|
async fn write_array_of_bytes(&mut self, messages: &[&[u8]]) -> Result<(), AuraError> {
|
||||||
if !self.initialised {
|
|
||||||
self.aura_write(&LED_INIT1).await?;
|
|
||||||
self.aura_write(LED_INIT2.as_bytes()).await?;
|
|
||||||
self.aura_write(&LED_INIT3).await?;
|
|
||||||
self.aura_write(LED_INIT4.as_bytes()).await?;
|
|
||||||
self.aura_write(&LED_INIT5).await?;
|
|
||||||
self.initialised = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for message in messages {
|
for message in messages {
|
||||||
self.aura_write(*message).await?;
|
self.write_bytes(*message).await?;
|
||||||
self.aura_write(&LED_SET).await?;
|
self.write_bytes(&LED_SET).await?;
|
||||||
}
|
}
|
||||||
// Changes won't persist unless apply is set
|
// Changes won't persist unless apply is set
|
||||||
self.aura_write(&LED_APPLY).await?;
|
self.write_bytes(&LED_APPLY).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write an effect block
|
/// Write an effect block
|
||||||
///
|
///
|
||||||
/// `aura_effect_init` must be called any effect routine, and called only once.
|
/// `aura_effect_init` must be called any effect routine, and called only once.
|
||||||
pub async fn async_write_effect(
|
async fn write_effect(&self, effect: Vec<Vec<u8>>) -> Result<(), AuraError> {
|
||||||
&self,
|
|
||||||
endpoint: u8,
|
|
||||||
effect: Vec<Vec<u8>>,
|
|
||||||
) -> Result<(), AuraError> {
|
|
||||||
for row in effect.iter() {
|
for row in effect.iter() {
|
||||||
match unsafe { self.handle.as_ref() }.write_interrupt(
|
match unsafe { self.handle.as_ref() }.write_interrupt(
|
||||||
endpoint,
|
self.led_endpoint,
|
||||||
row,
|
row,
|
||||||
Duration::from_millis(1),
|
Duration::from_millis(1),
|
||||||
) {
|
) {
|
||||||
@@ -392,19 +468,15 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Used to set a builtin mode and save the settings for it
|
/// Used to set a builtin mode and save the settings for it
|
||||||
pub async fn aura_set_and_save(
|
async fn set_and_save(&mut self, bytes: &[u8], config: &mut Config) -> Result<(), AuraError> {
|
||||||
&mut self,
|
|
||||||
supported_modes: &[BuiltInModeByte],
|
|
||||||
bytes: &[u8],
|
|
||||||
config: &mut Config,
|
|
||||||
) -> Result<(), AuraError> {
|
|
||||||
let mode = BuiltInModeByte::from(bytes[3]);
|
let mode = BuiltInModeByte::from(bytes[3]);
|
||||||
|
// safety pass-through of possible effect write
|
||||||
if bytes[1] == 0xbc {
|
if bytes[1] == 0xbc {
|
||||||
self.aura_write(bytes).await?;
|
self.write_bytes(bytes).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else if supported_modes.contains(&mode) || bytes[1] == 0xba {
|
} else if self.supported_modes.contains(&mode) || bytes[1] == 0xba {
|
||||||
let messages = [bytes];
|
let messages = [bytes];
|
||||||
self.aura_write_messages(&messages).await?;
|
self.write_array_of_bytes(&messages).await?;
|
||||||
config.set_field_from(bytes);
|
config.set_field_from(bytes);
|
||||||
config.write();
|
config.write();
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -413,93 +485,31 @@ where
|
|||||||
Err(AuraError::NotSupported)
|
Err(AuraError::NotSupported)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn aura_bright_inc(
|
/// Used to set a builtin mode and save the settings for it
|
||||||
&mut self,
|
async fn reload_last_builtin(&mut self, config: &Config) -> Result<(), AuraError> {
|
||||||
supported_modes: &[BuiltInModeByte],
|
let mode_curr = config.current_mode[3];
|
||||||
max_bright: u8,
|
let mode = config
|
||||||
config: &mut Config,
|
.builtin_modes
|
||||||
) -> Result<(), AuraError> {
|
.get_field_from(mode_curr)
|
||||||
let mut bright = config.brightness;
|
.unwrap()
|
||||||
if bright < max_bright {
|
.to_owned();
|
||||||
bright += 1;
|
self.write_bytes(&mode).await?;
|
||||||
config.brightness = bright;
|
info!("Reloaded last built-in mode");
|
||||||
let bytes = aura_brightness_bytes(bright);
|
|
||||||
self.aura_set_and_save(supported_modes, &bytes, config)
|
|
||||||
.await?;
|
|
||||||
info!("Increased LED brightness to {:#?}", bright);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn aura_bright_dec(
|
|
||||||
&mut self,
|
|
||||||
supported_modes: &[BuiltInModeByte],
|
|
||||||
min_bright: u8,
|
|
||||||
config: &mut Config,
|
|
||||||
) -> Result<(), AuraError> {
|
|
||||||
let mut bright = config.brightness;
|
|
||||||
if bright > min_bright {
|
|
||||||
bright -= 1;
|
|
||||||
config.brightness = bright;
|
|
||||||
let bytes = aura_brightness_bytes(bright);
|
|
||||||
self.aura_set_and_save(supported_modes, &bytes, config)
|
|
||||||
.await?;
|
|
||||||
info!("Decreased LED brightness to {:#?}", bright);
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Select next Aura effect
|
/// Select next Aura effect
|
||||||
///
|
///
|
||||||
/// If the current effect is the last one then the effect selected wraps around to the first.
|
/// If the current effect is the last one then the effect selected wraps around to the first.
|
||||||
pub async fn aura_mode_next(
|
async fn set_builtin(&mut self, config: &mut Config, index: usize) -> Result<(), AuraError> {
|
||||||
&mut self,
|
|
||||||
supported_modes: &[BuiltInModeByte],
|
|
||||||
config: &mut Config,
|
|
||||||
) -> Result<(), AuraError> {
|
|
||||||
// TODO: different path for multi-zone (byte 2 controlled, non-zero)
|
|
||||||
let mode_curr = config.current_mode[3];
|
|
||||||
let idx = supported_modes.binary_search(&mode_curr.into()).unwrap();
|
|
||||||
let idx_next = if idx < supported_modes.len() - 1 {
|
|
||||||
idx + 1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
let mode_next = config
|
let mode_next = config
|
||||||
.builtin_modes
|
.builtin_modes
|
||||||
.get_field_from(supported_modes[idx_next].into())
|
.get_field_from(self.supported_modes[index].into())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_owned();
|
.to_owned();
|
||||||
self.aura_set_and_save(supported_modes, &mode_next, config)
|
println!("{:X?}", &mode_next);
|
||||||
.await?;
|
self.set_and_save(&mode_next, config).await?;
|
||||||
info!("Switched LED mode to {:#?}", supported_modes[idx_next]);
|
info!("Switched LED mode to {:#?}", self.supported_modes[index]);
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Select previous Aura effect
|
|
||||||
///
|
|
||||||
/// If the current effect is the first one then the effect selected wraps around to the last.
|
|
||||||
pub async fn aura_mode_prev(
|
|
||||||
&mut self,
|
|
||||||
supported_modes: &[BuiltInModeByte],
|
|
||||||
config: &mut Config,
|
|
||||||
) -> Result<(), AuraError> {
|
|
||||||
// TODO: different path for multi-zone (byte 2 controlled, non-zero)
|
|
||||||
let mode_curr = config.current_mode[3];
|
|
||||||
let idx = supported_modes.binary_search(&mode_curr.into()).unwrap();
|
|
||||||
let idx_next = if idx > 0 {
|
|
||||||
idx - 1
|
|
||||||
} else {
|
|
||||||
supported_modes.len() - 1
|
|
||||||
};
|
|
||||||
let mode_next = config
|
|
||||||
.builtin_modes
|
|
||||||
.get_field_from(supported_modes[idx_next].into())
|
|
||||||
.unwrap()
|
|
||||||
.to_owned();
|
|
||||||
self.aura_set_and_save(supported_modes, &mode_next, config)
|
|
||||||
.await?;
|
|
||||||
info!("Switched LED mode to {:#?}", supported_modes[idx_next]);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
use crate::{config::Config, core::*, laptops::match_laptop};
|
use crate::{config::Config, core::*, laptops::match_laptop};
|
||||||
use dbus::{
|
use dbus::{
|
||||||
|
channel::Sender,
|
||||||
nonblock::Process,
|
nonblock::Process,
|
||||||
tree::{Factory, MTSync, Method, MethodErr, Tree},
|
tree::{Factory, MTSync, Method, MethodErr, Signal, Tree},
|
||||||
};
|
};
|
||||||
use dbus_tokio::connection;
|
use dbus_tokio::connection;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use rog_aura::{BuiltInModeByte, DBUS_IFACE, DBUS_PATH};
|
use rog_aura::{DBUS_IFACE, DBUS_PATH};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::sync::Arc;
|
use std::sync::{mpsc, Arc};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
@@ -46,19 +47,17 @@ pub async fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
|
|
||||||
// Reload settings
|
// Reload settings
|
||||||
rogcore.reload(&mut config).await?;
|
rogcore.reload(&mut config).await?;
|
||||||
let mut led_writer = LedWriter::new(rogcore.get_raw_device_handle(), laptop.led_endpoint());
|
let mut led_writer = LedWriter::new(
|
||||||
{
|
rogcore.get_raw_device_handle(),
|
||||||
let mode_curr = config.current_mode[3];
|
laptop.led_endpoint(),
|
||||||
let mode = config
|
(laptop.min_led_bright(), laptop.max_led_bright()),
|
||||||
.builtin_modes
|
laptop.supported_modes().to_owned(),
|
||||||
.get_field_from(BuiltInModeByte::from(mode_curr).into())
|
);
|
||||||
.unwrap()
|
led_writer
|
||||||
.to_owned();
|
.do_command(AuraCommand::ReloadLast, &mut config)
|
||||||
led_writer.aura_write(&mode).await?;
|
.await?;
|
||||||
}
|
|
||||||
|
|
||||||
// Set up the mutexes
|
// Set up the mutexes
|
||||||
let led_writer = Arc::new(Mutex::new(led_writer));
|
|
||||||
let config = Arc::new(Mutex::new(config));
|
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 {
|
||||||
@@ -70,13 +69,12 @@ pub async fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
.request_name(DBUS_IFACE, false, true, true)
|
.request_name(DBUS_IFACE, false, true, true)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let (tree, input, effect) = dbus_create_tree();
|
let (aura_command_send, aura_command_recv) = mpsc::sync_channel::<AuraCommand>(1);
|
||||||
|
|
||||||
|
let (tree, input, effect, effect_cancel_signal) = dbus_create_tree();
|
||||||
// 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);
|
||||||
|
|
||||||
let supported = Vec::from(laptop.supported_modes());
|
|
||||||
let led_endpoint = laptop.led_endpoint();
|
|
||||||
|
|
||||||
// Keyboard reader goes in separate task because we want a high interrupt timeout
|
// Keyboard reader goes in separate task because we want a high interrupt timeout
|
||||||
// and don't want that to hold up other tasks, or miss keystrokes
|
// and don't want that to hold up other tasks, or miss keystrokes
|
||||||
let keyboard_reader = KeyboardReader::new(
|
let keyboard_reader = KeyboardReader::new(
|
||||||
@@ -85,15 +83,15 @@ pub async fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
laptop.key_filter().to_owned(),
|
laptop.key_filter().to_owned(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let led_writer1 = led_writer.clone();
|
|
||||||
let config1 = config.clone();
|
let config1 = config.clone();
|
||||||
// start the keyboard reader and laptop-action loop
|
// start the keyboard reader and laptop-action loop
|
||||||
let key_read_handle = tokio::spawn(async move {
|
let key_read_handle = tokio::spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
|
let acs = aura_command_send.clone();
|
||||||
let data = keyboard_reader.poll_keyboard().await;
|
let data = keyboard_reader.poll_keyboard().await;
|
||||||
if let Some(bytes) = data {
|
if let Some(bytes) = data {
|
||||||
laptop
|
laptop
|
||||||
.run(&mut rogcore, &led_writer1, &config1, bytes)
|
.run(&mut rogcore, &config1, bytes, acs)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| warn!("{:?}", err))
|
.map_err(|err| warn!("{:?}", err))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -107,32 +105,60 @@ pub async fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
loop {
|
loop {
|
||||||
connection.process_all();
|
connection.process_all();
|
||||||
|
|
||||||
let led_writer = led_writer.clone();
|
let res = aura_command_recv.recv_timeout(Duration::from_micros(50));
|
||||||
if let Ok(mut lock) = input.try_lock() {
|
if let Ok(command) = res {
|
||||||
if let Some(bytes) = lock.take() {
|
let mut config = config.lock().await;
|
||||||
let mut led_writer = led_writer.lock().await;
|
led_writer
|
||||||
let mut config = config.lock().await;
|
.do_command(command, &mut config)
|
||||||
led_writer
|
.await
|
||||||
.aura_set_and_save(&supported, &bytes, &mut config)
|
.map_err(|err| warn!("{:?}", err))
|
||||||
.await
|
.unwrap();
|
||||||
.map_err(|err| warn!("{:?}", err))
|
|
||||||
.unwrap();
|
|
||||||
time_mark = Instant::now();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write a colour block
|
connection
|
||||||
let led_writer = led_writer.clone();
|
.send(
|
||||||
if let Ok(mut lock) = effect.try_lock() {
|
effect_cancel_signal
|
||||||
// Spawn a writer
|
.msg(&DBUS_PATH.into(), &DBUS_IFACE.into())
|
||||||
if let Some(stuff) = lock.take() {
|
.append1(true),
|
||||||
let led_writer = led_writer.lock().await;
|
)
|
||||||
led_writer
|
.unwrap();
|
||||||
.async_write_effect(led_endpoint, stuff)
|
// Clear any possible queued effect
|
||||||
.await
|
let mut effect = effect.lock().await;
|
||||||
.map_err(|err| warn!("{:?}", err))
|
*effect = None;
|
||||||
.unwrap();
|
time_mark = Instant::now();
|
||||||
time_mark = Instant::now();
|
} else {
|
||||||
|
if let Ok(mut lock) = input.try_lock() {
|
||||||
|
if let Some(bytes) = lock.take() {
|
||||||
|
if bytes.len() > 8 {
|
||||||
|
let mut config = config.lock().await;
|
||||||
|
led_writer
|
||||||
|
.do_command(AuraCommand::WriteBytes(bytes), &mut config)
|
||||||
|
.await
|
||||||
|
.map_err(|err| warn!("{:?}", err))
|
||||||
|
.unwrap();
|
||||||
|
// Also cancel any effect client
|
||||||
|
connection
|
||||||
|
.send(
|
||||||
|
effect_cancel_signal.msg(&DBUS_PATH.into(), &DBUS_IFACE.into()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
time_mark = Instant::now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Write a colour block
|
||||||
|
if let Ok(mut effect_lock) = effect.try_lock() {
|
||||||
|
// Spawn a writer
|
||||||
|
if let Some(stuff) = effect_lock.take() {
|
||||||
|
if stuff.len() == 10 {
|
||||||
|
let mut config = config.lock().await;
|
||||||
|
led_writer
|
||||||
|
.do_command(AuraCommand::WriteEffect(stuff), &mut config)
|
||||||
|
.await
|
||||||
|
.map_err(|err| warn!("{:?}", err))
|
||||||
|
.unwrap();
|
||||||
|
time_mark = Instant::now();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +184,7 @@ fn dbus_create_ledmsg_method(msg: LedMsgType) -> Method<MTSync, ()> {
|
|||||||
let factory = Factory::new_sync::<()>();
|
let factory = Factory::new_sync::<()>();
|
||||||
factory
|
factory
|
||||||
// method for ledmessage
|
// method for ledmessage
|
||||||
.method("ledmessage", (), {
|
.method("LedWriteBytes", (), {
|
||||||
move |m| {
|
move |m| {
|
||||||
let bytes: Vec<u8> = m.msg.read1()?;
|
let bytes: Vec<u8> = m.msg.read1()?;
|
||||||
if let Ok(mut lock) = msg.try_lock() {
|
if let Ok(mut lock) = msg.try_lock() {
|
||||||
@@ -181,7 +207,7 @@ fn dbus_create_ledeffect_method(effect: EffectType) -> Method<MTSync, ()> {
|
|||||||
let factory = Factory::new_sync::<()>();
|
let factory = Factory::new_sync::<()>();
|
||||||
factory
|
factory
|
||||||
// method for ledmessage
|
// method for ledmessage
|
||||||
.method("ledeffect", (), {
|
.method("LedWriteEffect", (), {
|
||||||
move |m| {
|
move |m| {
|
||||||
if let Ok(mut lock) = effect.try_lock() {
|
if let Ok(mut lock) = effect.try_lock() {
|
||||||
let mut iter = m.msg.iter_init();
|
let mut iter = m.msg.iter_init();
|
||||||
@@ -219,18 +245,20 @@ fn dbus_create_ledeffect_method(effect: EffectType) -> Method<MTSync, ()> {
|
|||||||
.inarg::<Vec<u8>, _>("bytearray")
|
.inarg::<Vec<u8>, _>("bytearray")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dbus_create_tree() -> (Tree<MTSync, ()>, LedMsgType, EffectType) {
|
fn dbus_create_tree() -> (Tree<MTSync, ()>, LedMsgType, EffectType, Arc<Signal<()>>) {
|
||||||
let input: LedMsgType = Arc::new(Mutex::new(None));
|
let input_bytes: LedMsgType = Arc::new(Mutex::new(None));
|
||||||
let effect: EffectType = Arc::new(Mutex::new(None));
|
let input_effect: EffectType = Arc::new(Mutex::new(None));
|
||||||
|
|
||||||
let factory = Factory::new_sync::<()>();
|
let factory = Factory::new_sync::<()>();
|
||||||
|
let effect_cancel_sig = Arc::new(factory.signal("LedCancelEffect", ()));
|
||||||
let tree = factory.tree(()).add(
|
let tree = factory.tree(()).add(
|
||||||
factory.object_path(DBUS_PATH, ()).add(
|
factory.object_path(DBUS_PATH, ()).add(
|
||||||
factory
|
factory
|
||||||
.interface(DBUS_IFACE, ())
|
.interface(DBUS_IFACE, ())
|
||||||
.add_m(dbus_create_ledmsg_method(input.clone()))
|
.add_m(dbus_create_ledmsg_method(input_bytes.clone()))
|
||||||
.add_m(dbus_create_ledeffect_method(effect.clone())),
|
.add_m(dbus_create_ledeffect_method(input_effect.clone()))
|
||||||
|
.add_s(effect_cancel_sig.clone()),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
(tree, input, effect)
|
(tree, input_bytes, input_effect, effect_cancel_sig)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::core::{LedWriter, RogCore};
|
use crate::core::{AuraCommand, RogCore};
|
||||||
use rog_aura::{error::AuraError, BuiltInModeByte};
|
use rog_aura::{error::AuraError, BuiltInModeByte};
|
||||||
//use keycode::{KeyMap, KeyMappingId, KeyState, KeyboardState};
|
//use keycode::{KeyMap, KeyMappingId, KeyState, KeyboardState};
|
||||||
use crate::virt_device::ConsumerKeys;
|
use crate::virt_device::ConsumerKeys;
|
||||||
@@ -78,33 +78,37 @@ pub(super) struct LaptopBase {
|
|||||||
//backlight: Backlight,
|
//backlight: Backlight,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use std::sync::mpsc;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
impl LaptopBase {
|
impl LaptopBase {
|
||||||
/// Pass in LedWriter as Mutex so it is only locked when required
|
/// Pass in LedWriter as Mutex so it is only locked when required
|
||||||
pub(super) async fn run<'a, C>(
|
pub(super) async fn run(
|
||||||
&self,
|
&self,
|
||||||
rogcore: &mut RogCore,
|
rogcore: &mut RogCore,
|
||||||
led_writer: &Mutex<LedWriter<'a, C>>,
|
|
||||||
config: &Mutex<Config>,
|
config: &Mutex<Config>,
|
||||||
key_buf: [u8; 32],
|
key_buf: [u8; 32],
|
||||||
) -> Result<(), AuraError>
|
aura_command: mpsc::SyncSender<AuraCommand>,
|
||||||
where
|
) -> Result<(), AuraError> {
|
||||||
C: rusb::UsbContext,
|
|
||||||
{
|
|
||||||
match self.usb_product {
|
match self.usb_product {
|
||||||
0x1869 | 0x1866 => {
|
0x1869 | 0x1866 => {
|
||||||
self.gx502_runner(rogcore, led_writer, config, key_buf)
|
self.gx502_runner(rogcore, config, key_buf, aura_command)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
0x1854 => {
|
0x1854 => {
|
||||||
self.gl753_runner(rogcore, led_writer, config, key_buf)
|
self.gl753_runner(rogcore, config, key_buf, aura_command)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
_ => panic!("No runner available for this device"),
|
_ => panic!("No runner available for this device"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn min_led_bright(&self) -> u8 {
|
||||||
|
self.min_led_bright
|
||||||
|
}
|
||||||
|
pub(super) fn max_led_bright(&self) -> u8 {
|
||||||
|
self.max_led_bright
|
||||||
|
}
|
||||||
pub(super) fn led_endpoint(&self) -> u8 {
|
pub(super) fn led_endpoint(&self) -> u8 {
|
||||||
self.led_endpoint
|
self.led_endpoint
|
||||||
}
|
}
|
||||||
@@ -125,47 +129,25 @@ impl LaptopBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 0x1866, per-key LEDs, media-keys split from vendor specific
|
// 0x1866, per-key LEDs, media-keys split from vendor specific
|
||||||
async fn gx502_runner<'a, C>(
|
async fn gx502_runner(
|
||||||
&self,
|
&self,
|
||||||
rogcore: &mut RogCore,
|
rogcore: &mut RogCore,
|
||||||
led_writer: &Mutex<LedWriter<'a, C>>,
|
|
||||||
config: &Mutex<Config>,
|
config: &Mutex<Config>,
|
||||||
key_buf: [u8; 32],
|
key_buf: [u8; 32],
|
||||||
) -> Result<(), AuraError>
|
aura_command: mpsc::SyncSender<AuraCommand>,
|
||||||
where
|
) -> Result<(), AuraError> {
|
||||||
C: rusb::UsbContext,
|
|
||||||
{
|
|
||||||
let max_led_bright = self.max_led_bright;
|
|
||||||
let min_led_bright = self.min_led_bright;
|
|
||||||
let supported_modes = self.supported_modes.to_owned();
|
|
||||||
match GX502Keys::from(key_buf[1]) {
|
match GX502Keys::from(key_buf[1]) {
|
||||||
GX502Keys::LedBrightUp => {
|
GX502Keys::LedBrightUp => {
|
||||||
let mut led_writer = led_writer.lock().await;
|
aura_command.send(AuraCommand::BrightInc).unwrap();
|
||||||
let mut config = config.lock().await;
|
|
||||||
led_writer
|
|
||||||
.aura_bright_inc(&supported_modes, max_led_bright, &mut config)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
GX502Keys::LedBrightDown => {
|
GX502Keys::LedBrightDown => {
|
||||||
let mut led_writer = led_writer.lock().await;
|
aura_command.send(AuraCommand::BrightDec).unwrap();
|
||||||
let mut config = config.lock().await;
|
|
||||||
led_writer
|
|
||||||
.aura_bright_dec(&supported_modes, min_led_bright, &mut config)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
GX502Keys::AuraNext => {
|
GX502Keys::AuraNext => {
|
||||||
let mut led_writer = led_writer.lock().await;
|
aura_command.send(AuraCommand::BuiltinNext).unwrap();
|
||||||
let mut config = config.lock().await;
|
|
||||||
led_writer
|
|
||||||
.aura_mode_next(&supported_modes, &mut config)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
GX502Keys::AuraPrevious => {
|
GX502Keys::AuraPrevious => {
|
||||||
let mut led_writer = led_writer.lock().await;
|
aura_command.send(AuraCommand::BuiltinPrev).unwrap();
|
||||||
let mut config = config.lock().await;
|
|
||||||
led_writer
|
|
||||||
.aura_mode_prev(&supported_modes, &mut config)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
GX502Keys::ScreenBrightUp => {
|
GX502Keys::ScreenBrightUp => {
|
||||||
rogcore.virt_keys().press(ConsumerKeys::BacklightInc.into())
|
rogcore.virt_keys().press(ConsumerKeys::BacklightInc.into())
|
||||||
@@ -210,33 +192,19 @@ impl LaptopBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GL753VE == 0x1854, 4 zone keyboard
|
// GL753VE == 0x1854, 4 zone keyboard
|
||||||
async fn gl753_runner<'a, C>(
|
async fn gl753_runner(
|
||||||
&self,
|
&self,
|
||||||
rogcore: &mut RogCore,
|
rogcore: &mut RogCore,
|
||||||
led_writer: &Mutex<LedWriter<'a, C>>,
|
_config: &Mutex<Config>,
|
||||||
config: &Mutex<Config>,
|
|
||||||
key_buf: [u8; 32],
|
key_buf: [u8; 32],
|
||||||
) -> Result<(), AuraError>
|
aura_command: mpsc::SyncSender<AuraCommand>,
|
||||||
where
|
) -> Result<(), AuraError> {
|
||||||
C: rusb::UsbContext,
|
|
||||||
{
|
|
||||||
let max_led_bright = self.max_led_bright;
|
|
||||||
let min_led_bright = self.min_led_bright;
|
|
||||||
let supported_modes = self.supported_modes.to_owned();
|
|
||||||
match GL753Keys::from(key_buf[1]) {
|
match GL753Keys::from(key_buf[1]) {
|
||||||
GL753Keys::LedBrightUp => {
|
GL753Keys::LedBrightUp => {
|
||||||
let mut led_writer = led_writer.lock().await;
|
aura_command.send(AuraCommand::BrightInc).unwrap();
|
||||||
let mut config = config.lock().await;
|
|
||||||
led_writer
|
|
||||||
.aura_bright_inc(&supported_modes, max_led_bright, &mut config)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
GL753Keys::LedBrightDown => {
|
GL753Keys::LedBrightDown => {
|
||||||
let mut led_writer = led_writer.lock().await;
|
aura_command.send(AuraCommand::BrightDec).unwrap();
|
||||||
let mut config = config.lock().await;
|
|
||||||
led_writer
|
|
||||||
.aura_bright_dec(&supported_modes, min_led_bright, &mut config)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
GL753Keys::ScreenBrightUp => {
|
GL753Keys::ScreenBrightUp => {
|
||||||
rogcore.virt_keys().press(ConsumerKeys::BacklightInc.into())
|
rogcore.virt_keys().press(ConsumerKeys::BacklightInc.into())
|
||||||
|
|||||||
@@ -40,9 +40,12 @@ struct LedModeCommand {
|
|||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut builder = Builder::new();
|
let mut builder = Builder::new();
|
||||||
builder.target(Target::Stdout);
|
builder
|
||||||
builder.format_timestamp(None);
|
.target(Target::Stdout)
|
||||||
builder.filter(None, LevelFilter::Info).init();
|
.format_module_path(false)
|
||||||
|
.format_timestamp(None)
|
||||||
|
.filter(None, LevelFilter::Info)
|
||||||
|
.init();
|
||||||
|
|
||||||
let parsed = CLIStart::parse_args_default_or_exit();
|
let parsed = CLIStart::parse_args_default_or_exit();
|
||||||
if parsed.daemon {
|
if parsed.daemon {
|
||||||
|
|||||||
Reference in New Issue
Block a user