diff --git a/examples/per-key-effect.rs b/examples/per-key-effect.rs new file mode 100644 index 00000000..6b62ea47 --- /dev/null +++ b/examples/per-key-effect.rs @@ -0,0 +1,73 @@ +use daemon::aura::{BuiltInModeByte, Key, KeyColourArray}; +use daemon::daemon::{DBUS_IFACE, DBUS_NAME, DBUS_PATH}; +use dbus::Error as DbusError; +use dbus::{ffidisp::Connection, Message}; +use std::{thread, time}; + +pub fn dbus_led_builtin_write(bytes: &[u8]) -> Result<(), Box> { + let bus = Connection::new_system()?; + //let proxy = bus.with_proxy(DBUS_IFACE, "/", Duration::from_millis(5000)); + let msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "ledmessage")? + .append1(bytes.to_vec()); + let r = bus.send_with_reply_and_block(msg, 5000)?; + if let Some(reply) = r.get1::<&str>() { + println!("Success: {:x?}", reply); + return Ok(()); + } + Err(Box::new(DbusError::new_custom("name", "message"))) +} + +fn main() -> Result<(), Box> { + let bus = Connection::new_system()?; + + let mut per_key_led = Vec::new(); + let mut key_colours = KeyColourArray::new(); + key_colours.set(Key::ROG, 255, 0, 0); + key_colours.set(Key::L, 255, 0, 0); + key_colours.set(Key::I, 255, 0, 0); + key_colours.set(Key::N, 255, 0, 0); + key_colours.set(Key::U, 255, 0, 0); + key_colours.set(Key::X, 255, 0, 0); + per_key_led.push(key_colours.clone()); + + for _ in 0..51 { + *key_colours.key(Key::ROG).0 -= 5; + *key_colours.key(Key::L).0 -= 5; + *key_colours.key(Key::I).0 -= 5; + *key_colours.key(Key::N).0 -= 5; + *key_colours.key(Key::U).0 -= 5; + *key_colours.key(Key::X).0 -= 5; + per_key_led.push(key_colours.clone()); + } + for _ in 0..51 { + *key_colours.key(Key::ROG).0 += 5; + *key_colours.key(Key::L).0 += 5; + *key_colours.key(Key::I).0 += 5; + *key_colours.key(Key::N).0 += 5; + *key_colours.key(Key::U).0 += 5; + *key_colours.key(Key::X).0 += 5; + per_key_led.push(key_colours.clone()); + } + + let time = time::Duration::from_millis(2); + + let row = KeyColourArray::get_init_msg(); + let msg = + Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "ledmessage")?.append1(row); + bus.send(msg).unwrap(); + + loop { + for group in &per_key_led { + for row in group.get() { + thread::sleep(time); + let msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "ledmessage")? + .append1(row.to_vec()); + bus.send(msg).unwrap(); + // if let Some(reply) = r.get1::<&str>() { + // println!("Success: {:x?}", reply); + // return Ok(()); + // } + } + } + } +} diff --git a/src/aura.rs b/src/aura.rs index 5201360e..3e72526c 100644 --- a/src/aura.rs +++ b/src/aura.rs @@ -226,8 +226,8 @@ impl BuiltInModeBytes { if bytes[0] == 0x5d && bytes[1] == 0xb3 { let b = BuiltInModeByte::from(bytes[3]); match b { - BuiltInModeByte::Stable => self.stable.copy_from_slice(bytes), - BuiltInModeByte::Breathe => self.breathe.copy_from_slice(bytes), + BuiltInModeByte::Single => self.stable.copy_from_slice(bytes), + BuiltInModeByte::Breathing => self.breathe.copy_from_slice(bytes), BuiltInModeByte::Cycle => self.cycle.copy_from_slice(bytes), BuiltInModeByte::Rainbow => self.rainbow.copy_from_slice(bytes), BuiltInModeByte::Rain => self.rain.copy_from_slice(bytes), @@ -245,8 +245,8 @@ impl BuiltInModeBytes { pub fn get_field_from(&mut self, byte: u8) -> Option<&[u8]> { let bytes = match BuiltInModeByte::from(byte) { - BuiltInModeByte::Stable => &self.stable, - BuiltInModeByte::Breathe => &self.breathe, + BuiltInModeByte::Single => &self.stable, + BuiltInModeByte::Breathing => &self.breathe, BuiltInModeByte::Cycle => &self.cycle, BuiltInModeByte::Rainbow => &self.rainbow, BuiltInModeByte::Rain => &self.rain, @@ -294,8 +294,8 @@ impl Default for BuiltInModeBytes { #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] pub enum BuiltInModeByte { - Stable = 0x00, - Breathe = 0x01, + Single = 0x00, + Breathing = 0x01, Cycle = 0x02, Rainbow = 0x03, Rain = 0x04, @@ -310,14 +310,15 @@ pub enum BuiltInModeByte { } impl Default for BuiltInModeByte { fn default() -> Self { - BuiltInModeByte::Stable + BuiltInModeByte::Single } } + impl From for BuiltInModeByte { fn from(byte: u8) -> Self { match byte { - 0x00 => Self::Stable, - 0x01 => Self::Breathe, + 0x00 => Self::Single, + 0x01 => Self::Breathing, 0x02 => Self::Cycle, 0x03 => Self::Rainbow, 0x04 => Self::Rain, @@ -342,8 +343,8 @@ impl From<&u8> for BuiltInModeByte { impl From for u8 { fn from(byte: BuiltInModeByte) -> Self { match byte { - BuiltInModeByte::Stable => 0x00, - BuiltInModeByte::Breathe => 0x01, + BuiltInModeByte::Single => 0x00, + BuiltInModeByte::Breathing => 0x01, BuiltInModeByte::Cycle => 0x02, BuiltInModeByte::Rainbow => 0x03, BuiltInModeByte::Rain => 0x04, @@ -381,6 +382,14 @@ impl KeyColourArray { KeyColourArray(set) } + /// Initialise and clear the keyboard for custom effects + pub fn get_init_msg() -> Vec { + let mut init = vec![0u8; 64]; + init[0] = 0x5d; // Report ID + init[1] = 0xbc; // Mode = custom??, 0xb3 is builtin + init + } + pub fn set(&mut self, key: Key, r: u8, g: u8, b: u8) { let (rr, gg, bb) = self.key(key); *rr = r; diff --git a/src/core.rs b/src/core.rs index 78519331..ab39d0c7 100644 --- a/src/core.rs +++ b/src/core.rs @@ -176,86 +176,16 @@ impl RogCore { Ok(()) } - /// Write the bytes read from the device interrupt to the buffer arg, and returns the - /// count of bytes written - /// - /// `report_filter_bytes` is used to filter the data read from the interupt so - /// only the relevant byte array is returned. - pub(crate) fn poll_keyboard(&mut self, report_filter_bytes: &[u8]) -> Option<[u8; 32]> { - let mut buf = [0u8; 32]; - match self - .handle - .read_interrupt(self.keys_endpoint, &mut buf, Duration::from_micros(1)) - { - Ok(_) => { - if report_filter_bytes.contains(&buf[0]) { - return Some(buf); - } - } - Err(err) => { - error!("Failed to read keyboard interrupt: {:?}", err); - } - } - None - } - - /// A direct call to systemd to suspend the PC. - /// - /// This avoids desktop environments being required to handle it - /// (which means it works while in a TTY also) - pub(crate) fn suspend_with_systemd(&self) { - std::process::Command::new("systemctl") - .arg("suspend") - .spawn() - .map_or_else(|err| warn!("Failed to suspend: {}", err), |_| {}); - } - - /// A direct call to rfkill to suspend wireless devices. - /// - /// This avoids desktop environments being required to handle it (which - /// means it works while in a TTY also) - pub(crate) fn toggle_airplane_mode(&self) { - match Command::new("rfkill").arg("list").output() { - Ok(output) => { - if output.status.success() { - let patterns = &["yes"]; - let ac = AhoCorasick::new(patterns); - if ac.earliest_find(output.stdout).is_some() { - Command::new("rfkill") - .arg("unblock") - .arg("all") - .spawn() - .map_or_else( - |err| warn!("Could not unblock rf devices: {}", err), - |_| {}, - ); - } else { - let _ = Command::new("rfkill") - .arg("block") - .arg("all") - .spawn() - .map_or_else( - |err| warn!("Could not block rf devices: {}", err), - |_| {}, - ); - } - } else { - warn!("Could not list rf devices"); - } - } - Err(err) => { - warn!("Could not list rf devices: {}", err); - } - } - } - pub(crate) fn aura_set_and_save( &mut self, supported_modes: &[BuiltInModeByte], bytes: &[u8], ) -> Result<(), AuraError> { let mode = BuiltInModeByte::from(bytes[3]); - if supported_modes.contains(&mode) || bytes[1] == 0xba { + if bytes[1] == 0xbc { + self.aura_write(bytes)?; + return Ok(()); + } else if supported_modes.contains(&mode) || bytes[1] == 0xba { let messages = [bytes]; self.aura_write_messages(&messages)?; self.config.set_field_from(bytes); @@ -317,7 +247,9 @@ impl RogCore { .get_field_from(supported_modes[idx_next].into()) .unwrap() .to_owned(); - self.aura_set_and_save(supported_modes, &mode_next) + self.aura_set_and_save(supported_modes, &mode_next)?; + info!("Switched LED mode to {:#?}", supported_modes[idx_next]); + Ok(()) } /// Select previous Aura effect @@ -341,7 +273,9 @@ impl RogCore { .get_field_from(supported_modes[idx_next].into()) .unwrap() .to_owned(); - self.aura_set_and_save(supported_modes, &mode_next) + self.aura_set_and_save(supported_modes, &mode_next)?; + info!("Switched LED mode to {:#?}", supported_modes[idx_next]); + Ok(()) } pub(crate) fn fan_mode_step(&mut self) -> Result<(), Box> { @@ -358,8 +292,7 @@ impl RogCore { let mut buf = String::new(); if let Ok(_) = file.read_to_string(&mut buf) { let mut n = u8::from_str_radix(&buf.trim_end(), 10)?; - let level: &str = FanLevel::from(n).into(); - info!("Current fan mode: {}", level); + info!("Current fan mode: {:#?}", FanLevel::from(n)); if n < 2 { n += 1; @@ -367,14 +300,87 @@ impl RogCore { n = 0; } - let level: &str = FanLevel::from(n).into(); - info!("Fan mode stepped to: {}", level); + info!("Fan mode stepped to: {:#?}", FanLevel::from(n)); file.write(format!("{:?}\n", n).as_bytes())?; self.config.fan_mode = n; self.config.write(); } Ok(()) } + + /// Write the bytes read from the device interrupt to the buffer arg, and returns the + /// count of bytes written + /// + /// `report_filter_bytes` is used to filter the data read from the interupt so + /// only the relevant byte array is returned. + pub(crate) fn poll_keyboard(&mut self, report_filter_bytes: &[u8]) -> Option<[u8; 32]> { + let mut buf = [0u8; 32]; + match self + .handle + .read_interrupt(self.keys_endpoint, &mut buf, Duration::from_millis(1)) + { + Ok(_) => { + if report_filter_bytes.contains(&buf[0]) { + return Some(buf); + } + } + Err(err) => match err { + rusb::Error::Timeout => {} + _ => error!("Failed to read keyboard interrupt: {:?}", err), + }, + } + None + } + + /// A direct call to systemd to suspend the PC. + /// + /// This avoids desktop environments being required to handle it + /// (which means it works while in a TTY also) + pub(crate) fn suspend_with_systemd(&self) { + std::process::Command::new("systemctl") + .arg("suspend") + .spawn() + .map_or_else(|err| warn!("Failed to suspend: {}", err), |_| {}); + } + + /// A direct call to rfkill to suspend wireless devices. + /// + /// This avoids desktop environments being required to handle it (which + /// means it works while in a TTY also) + pub(crate) fn toggle_airplane_mode(&self) { + match Command::new("rfkill").arg("list").output() { + Ok(output) => { + if output.status.success() { + let patterns = &["yes"]; + let ac = AhoCorasick::new(patterns); + if ac.earliest_find(output.stdout).is_some() { + Command::new("rfkill") + .arg("unblock") + .arg("all") + .spawn() + .map_or_else( + |err| warn!("Could not unblock rf devices: {}", err), + |_| {}, + ); + } else { + let _ = Command::new("rfkill") + .arg("block") + .arg("all") + .spawn() + .map_or_else( + |err| warn!("Could not block rf devices: {}", err), + |_| {}, + ); + } + } else { + warn!("Could not list rf devices"); + } + } + Err(err) => { + warn!("Could not list rf devices: {}", err); + } + } + } } pub(crate) struct Backlight { @@ -457,7 +463,7 @@ impl FromStr for LedBrightness { } } } - +#[derive(Debug)] enum FanLevel { Normal, Boost, @@ -484,13 +490,3 @@ impl From for u8 { } } } - -impl From for &str { - fn from(n: FanLevel) -> Self { - match n { - FanLevel::Normal => "Normal", - FanLevel::Boost => "Boosted", - FanLevel::Silent => "Silent", - } - } -} diff --git a/src/daemon.rs b/src/daemon.rs index 8baf3af7..6596c403 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -10,6 +10,7 @@ use dbus::{ use log::{error, info, warn}; use std::error::Error; use std::sync::{Arc, Mutex}; +use std::thread; use std::time::Duration; pub fn start_daemon() -> Result<(), Box> { @@ -40,7 +41,7 @@ pub fn start_daemon() -> Result<(), Box> { connection.request_name(DBUS_IFACE, false, true, false)?; let factory = Factory::new_sync::<()>(); - let daemon = Arc::new(Mutex::new(rogcore)); + let input: Arc>>> = Arc::new(Mutex::new(None)); let tree = factory.tree(()).add( factory.object_path(DBUS_PATH, ()).add( @@ -48,26 +49,19 @@ pub fn start_daemon() -> Result<(), Box> { factory // method for ledmessage .method("ledmessage", (), { - let daemon = daemon.clone(); - let supported = Vec::from(laptop.supported_modes()); + let input = input.clone(); + move |m| { - if let Ok(mut lock) = daemon.try_lock() { - let bytes: Vec = m.msg.read1()?; - match lock.aura_set_and_save(&supported, &bytes[..]) { - Ok(_) => { - let mret = m - .msg - .method_return() - .append1(&format!("Wrote {:x?}", bytes)); - Ok(vec![mret]) - } - Err(err) => { - warn!("{:?}", err); - Err(MethodErr::failed(&err)) - } - } + let bytes: Vec = m.msg.read1()?; + if let Ok(mut lock) = input.lock() { + *lock = Some(bytes.to_vec()); + let mret = m + .msg + .method_return() + .append1(&format!("Wrote {:x?}", bytes)); + return Ok(vec![mret]); } else { - Err(MethodErr::failed("Could not lock daemon for access")) + return Err(MethodErr::failed("Could not lock daemon for access")); } } }) @@ -80,19 +74,31 @@ pub fn start_daemon() -> Result<(), Box> { // We add the tree to the connection so that incoming method calls will be handled. tree.start_receive_send(&connection); + //thread::spawn(move || loop {}); + + let supported = Vec::from(laptop.supported_modes()); loop { + //thread::sleep(Duration::from_millis(2)); connection - .process(Duration::from_millis(10)) + .process(Duration::from_millis(20)) .unwrap_or_else(|err| { error!("{:?}", err); false }); - // TODO: this needs to move to a thread, but there is unsafety - if let Ok(mut lock) = daemon.try_lock() { - laptop.run(&mut lock).unwrap_or_else(|err| { + if let Ok(mut lock) = input.try_lock() { + if let Some(bytes) = &*lock { + rogcore.aura_set_and_save(&supported, &bytes)?; + *lock = None; + } + } + + match laptop.run(&mut rogcore) { + Ok(_) => {} + Err(err) => { error!("{:?}", err); - }); + panic!("Force crash for systemd to restart service") + } } } } diff --git a/src/laptops/gl753.rs b/src/laptops/gl753.rs index 44c031e6..149d6db6 100644 --- a/src/laptops/gl753.rs +++ b/src/laptops/gl753.rs @@ -37,8 +37,8 @@ impl LaptopGL753 { //from `lsusb -vd 0b05:1866` key_endpoint: 0x83, supported_modes: [ - BuiltInModeByte::Stable, - BuiltInModeByte::Breathe, + BuiltInModeByte::Single, + BuiltInModeByte::Breathing, BuiltInModeByte::Cycle, ], backlight: Backlight::new("intel_backlight").unwrap(), diff --git a/src/laptops/gx502.rs b/src/laptops/gx502.rs index c44bbd04..cee599f2 100644 --- a/src/laptops/gx502.rs +++ b/src/laptops/gx502.rs @@ -71,8 +71,8 @@ impl LaptopGX502 { //from `lsusb -vd 0b05:1866` key_endpoint: 0x83, supported_modes: [ - BuiltInModeByte::Stable, - BuiltInModeByte::Breathe, + BuiltInModeByte::Single, + BuiltInModeByte::Breathing, BuiltInModeByte::Cycle, BuiltInModeByte::Rainbow, BuiltInModeByte::Rain,