mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
Per-key example
This commit is contained in:
73
examples/per-key-effect.rs
Normal file
73
examples/per-key-effect.rs
Normal 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(());
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/aura.rs
31
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<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;
|
||||
|
||||
178
src/core.rs
178
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<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",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user