Per-key example

This commit is contained in:
Luke
2020-04-24 22:01:13 +12:00
parent 515888393c
commit ccae9dd764
6 changed files with 214 additions and 130 deletions

View File

@@ -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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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(());
// }
}
}
}
}

View File

@@ -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<u8> 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<BuiltInModeByte> 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<u8> {
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;

View File

@@ -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<dyn Error>> {
@@ -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<FanLevel> for u8 {
}
}
}
impl From<FanLevel> for &str {
fn from(n: FanLevel) -> Self {
match n {
FanLevel::Normal => "Normal",
FanLevel::Boost => "Boosted",
FanLevel::Silent => "Silent",
}
}
}

View File

@@ -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<dyn Error>> {
@@ -40,7 +41,7 @@ pub fn start_daemon() -> Result<(), Box<dyn Error>> {
connection.request_name(DBUS_IFACE, false, true, false)?;
let factory = Factory::new_sync::<()>();
let daemon = Arc::new(Mutex::new(rogcore));
let input: Arc<Mutex<Option<Vec<u8>>>> = 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<dyn Error>> {
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<u8> = 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<u8> = 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<dyn Error>> {
// 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")
}
}
}
}

View File

@@ -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(),

View File

@@ -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,