Big refactor out of Aura LED data structs

This commit is contained in:
Luke
2020-05-01 22:07:28 +12:00
parent 38ab4bc182
commit bf6bf2e2f1
23 changed files with 1862 additions and 802 deletions

21
Cargo.lock generated
View File

@@ -628,9 +628,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.3"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
checksum = "4c1f4b0efa5fc5e8ceb705136bfee52cfdb6a4e3509f770b478cd6ed434232a7"
dependencies = [
"proc-macro2",
]
@@ -665,6 +665,17 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac"
[[package]]
name = "rog-aura"
version = "0.8.0"
dependencies = [
"dbus",
"gumdrop",
"serde",
"serde_derive",
"thiserror",
]
[[package]]
name = "rog-daemon"
version = "0.8.0"
@@ -675,10 +686,10 @@ dependencies = [
"gumdrop",
"intel-pstate",
"log",
"rog-aura",
"rusb",
"serde",
"serde_derive",
"thiserror",
"tokio",
"toml",
"uhid-virt",
@@ -859,9 +870,9 @@ dependencies = [
[[package]]
name = "tokio"
version = "0.2.19"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d9c43f1bb96970e153bcbae39a65e249ccb942bd9d36dbdf086024920417c9c"
checksum = "05c1d570eb1a36f0345a5ce9c6c6e665b70b73d11236912c0b477616aeec47b1"
dependencies = [
"bytes",
"fnv",

View File

@@ -1,50 +1,5 @@
[package]
name = "rog-daemon"
version = "0.8.0"
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
repository = "https://github.com/flukejones/rog-core"
homepage = "https://github.com/flukejones/rog-core"
description = "A daemon app for ASUS GX502 and similar laptops to control missing features"
edition = "2018"
[lib]
name = "daemon"
path = "src/lib.rs"
[[bin]]
name = "rog-core"
path = "src/main.rs"
[dependencies]
rusb = "^0.5.5"
# cli and logging
gumdrop = "^0.8.0"
log = "^0.4.8"
env_logger = "^0.7.1"
# async
dbus = { version = "^0.8.2", features = ["futures"] }
dbus-tokio = "^0.5.1"
tokio = { version = "0.2.4", features = ["rt-threaded", "macros", "sync"] }
# serialisation
serde = "1.0"
serde_derive = "1.0"
toml = "0.5"
# Device control
# sysfs-class = "^0.1.2" # used for backlight control mostly
# cpu power management
intel-pstate = "^0.2.1"
# virtualisation of HID, mainly for outputting consumer key codes
uhid-virt = "^0.0.4"
#keycode = "0.3"
#
thiserror = "^1.0.15"
[workspace]
members = ["rog-core", "aura"]
[profile.release]
lto = true

View File

@@ -7,7 +7,7 @@ includedir = $(prefix)/include
datarootdir = $(prefix)/share
datadir = $(datarootdir)
SRC = Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '*/src/*.rs')
SRC = Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.rs')
.PHONY: all clean distclean install uninstall update

17
aura/Cargo.toml Normal file
View File

@@ -0,0 +1,17 @@
[package]
name = "rog-aura"
version = "0.8.0"
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
repository = "https://github.com/flukejones/rog-core"
homepage = "https://github.com/flukejones/rog-core"
description = "A small library of effect types and conversions for ROG Aura"
edition = "2018"
[dependencies]
gumdrop = "^0.8.0"
dbus = { version = "^0.8.2" }
serde = "1.0"
serde_derive = "1.0"
thiserror = "^1.0.15"

View File

@@ -1,4 +1,4 @@
use daemon::aura::{AuraDbusWriter, Key, KeyColourArray};
use rog_aura::{AuraDbusWriter, Key, KeyColourArray};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let writer = AuraDbusWriter::new()?;

81
aura/src/aura_dbus.rs Normal file
View File

@@ -0,0 +1,81 @@
use super::*;
use dbus::{ffidisp::Connection, Message};
use std::error::Error;
use std::{thread, time::Duration};
/// Simplified way to write a effect block
pub struct AuraDbusWriter {
connection: Connection,
block_time: u64,
}
impl AuraDbusWriter {
#[inline]
pub fn new() -> Result<Self, Box<dyn Error>> {
Ok(AuraDbusWriter {
connection: Connection::new_system()?,
block_time: 10,
})
}
/// This method must always be called before the very first write to initialise
/// the keyboard LED EC in the correct mode
#[inline]
pub fn init_effect(&self) -> Result<(), Box<dyn Error>> {
let msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "ledmessage")?
.append1(KeyColourArray::get_init_msg());
self.connection.send(msg).unwrap();
Ok(())
}
/// Write a single colour block.
///
/// Intentionally blocks for 10ms after sending to allow the block to
/// be written to the keyboard EC. This should not be async.
#[inline]
pub fn write_colour_block(
&self,
key_colour_array: &KeyColourArray,
) -> Result<(), Box<dyn Error>> {
let group = key_colour_array.get();
let msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "ledeffect")?
.append1(&group[0].to_vec())
.append1(&group[1].to_vec())
.append1(&group[2].to_vec())
.append1(&group[3].to_vec())
.append1(&group[4].to_vec())
.append1(&group[5].to_vec())
.append1(&group[6].to_vec())
.append1(&group[7].to_vec())
.append1(&group[8].to_vec())
.append1(&group[9].to_vec());
self.connection.send(msg).unwrap();
thread::sleep(Duration::from_millis(self.block_time));
Ok(())
}
#[inline]
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")?
.append1(bytes.to_vec());
let r = self.connection.send_with_reply_and_block(msg, 5000)?;
if let Some(reply) = r.get1::<&str>() {
return Ok(reply.to_owned());
}
Err(Box::new(dbus::Error::new_custom("name", "message")))
}
#[inline]
pub fn write_builtin_mode(
&self,
mode: &SetAuraBuiltin,
) -> Result<String, Box<dyn std::error::Error>> {
let bytes = <[u8; LED_MSG_LEN]>::from(mode);
self.write_bytes(&bytes)
}
#[inline]
pub fn write_brightness(&self, level: u8) -> Result<String, Box<dyn std::error::Error>> {
self.write_bytes(&aura_brightness_bytes(level))
}
}

166
aura/src/builtins.rs Normal file
View File

@@ -0,0 +1,166 @@
use super::cli_options::*;
use super::LED_MSG_LEN;
use serde_derive::{Deserialize, Serialize};
/// Container for the byte strings used in modes. Generally useful for settings
/// and other usecases.
#[derive(Deserialize, Serialize)]
pub struct BuiltInModeBytes {
pub stable: [u8; LED_MSG_LEN],
pub breathe: [u8; LED_MSG_LEN],
pub cycle: [u8; LED_MSG_LEN],
pub rainbow: [u8; LED_MSG_LEN],
pub rain: [u8; LED_MSG_LEN],
pub random: [u8; LED_MSG_LEN],
pub highlight: [u8; LED_MSG_LEN],
pub laser: [u8; LED_MSG_LEN],
pub ripple: [u8; LED_MSG_LEN],
pub pulse: [u8; LED_MSG_LEN],
pub thinzoomy: [u8; LED_MSG_LEN],
pub widezoomy: [u8; LED_MSG_LEN],
pub multi_static: [[u8; LED_MSG_LEN]; 4],
}
impl BuiltInModeBytes {
#[inline]
pub fn set_field_from(&mut self, bytes: &[u8]) {
if bytes[0] == 0x5d && bytes[1] == 0xb3 {
let b = BuiltInModeByte::from(bytes[3]);
match b {
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),
BuiltInModeByte::Random => self.random.copy_from_slice(bytes),
BuiltInModeByte::Highlight => self.highlight.copy_from_slice(bytes),
BuiltInModeByte::Laser => self.laser.copy_from_slice(bytes),
BuiltInModeByte::Ripple => self.ripple.copy_from_slice(bytes),
BuiltInModeByte::Pulse => self.pulse.copy_from_slice(bytes),
BuiltInModeByte::ThinZoomy => self.thinzoomy.copy_from_slice(bytes),
BuiltInModeByte::WideZoomy => self.widezoomy.copy_from_slice(bytes),
_ => {}
}
}
}
#[inline]
pub fn get_field_from(&mut self, byte: u8) -> Option<&[u8]> {
let bytes = match BuiltInModeByte::from(byte) {
BuiltInModeByte::Single => &self.stable,
BuiltInModeByte::Breathing => &self.breathe,
BuiltInModeByte::Cycle => &self.cycle,
BuiltInModeByte::Rainbow => &self.rainbow,
BuiltInModeByte::Rain => &self.rain,
BuiltInModeByte::Random => &self.random,
BuiltInModeByte::Highlight => &self.highlight,
BuiltInModeByte::Laser => &self.laser,
BuiltInModeByte::Ripple => &self.ripple,
BuiltInModeByte::Pulse => &self.pulse,
BuiltInModeByte::ThinZoomy => &self.thinzoomy,
BuiltInModeByte::WideZoomy => &self.widezoomy,
_ => return None,
};
return Some(bytes);
}
}
impl Default for BuiltInModeBytes {
fn default() -> Self {
BuiltInModeBytes {
stable: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Stable(SingleColour::default())),
breathe: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Breathe(TwoColourSpeed::default())),
cycle: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Cycle(SingleSpeed::default())),
rainbow: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Rainbow(
SingleSpeedDirection::default(),
)),
rain: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Rain(TwoColourSpeed::default())),
random: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Disco(SingleSpeed::default())),
highlight: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Highlight(
SingleColourSpeed::default(),
)),
laser: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Laser(SingleColourSpeed::default())),
ripple: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Ripple(SingleColourSpeed::default())),
pulse: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Pulse(SingleColour::default())),
thinzoomy: <[u8; LED_MSG_LEN]>::from(
SetAuraBuiltin::ThinZoomy(SingleColour::default()),
),
widezoomy: <[u8; LED_MSG_LEN]>::from(
SetAuraBuiltin::WideZoomy(SingleColour::default()),
),
multi_static: <[[u8; LED_MSG_LEN]; 4]>::from(SetAuraBuiltin::MultiStatic(
MultiColour::default(),
)),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub enum BuiltInModeByte {
Single = 0x00,
Breathing = 0x01,
Cycle = 0x02,
Rainbow = 0x03,
Rain = 0x04,
Random = 0x05,
Highlight = 0x06,
Laser = 0x07,
Ripple = 0x08,
Pulse = 0x0a,
ThinZoomy = 0x0b,
WideZoomy = 0x0c,
None,
}
impl Default for BuiltInModeByte {
#[inline]
fn default() -> Self {
BuiltInModeByte::Single
}
}
impl From<u8> for BuiltInModeByte {
#[inline]
fn from(byte: u8) -> Self {
match byte {
0x00 => Self::Single,
0x01 => Self::Breathing,
0x02 => Self::Cycle,
0x03 => Self::Rainbow,
0x04 => Self::Rain,
0x05 => Self::Random,
0x06 => Self::Highlight,
0x07 => Self::Laser,
0x08 => Self::Ripple,
0x0a => Self::Pulse,
0x0b => Self::ThinZoomy,
0x0c => Self::WideZoomy,
_ => Self::None,
}
}
}
impl From<&u8> for BuiltInModeByte {
#[inline]
fn from(byte: &u8) -> Self {
Self::from(*byte)
}
}
impl From<BuiltInModeByte> for u8 {
#[inline]
fn from(byte: BuiltInModeByte) -> Self {
match byte {
BuiltInModeByte::Single => 0x00,
BuiltInModeByte::Breathing => 0x01,
BuiltInModeByte::Cycle => 0x02,
BuiltInModeByte::Rainbow => 0x03,
BuiltInModeByte::Rain => 0x04,
BuiltInModeByte::Random => 0x05,
BuiltInModeByte::Highlight => 0x06,
BuiltInModeByte::Laser => 0x07,
BuiltInModeByte::Ripple => 0x08,
BuiltInModeByte::Pulse => 0x0a,
BuiltInModeByte::ThinZoomy => 0x0b,
BuiltInModeByte::WideZoomy => 0x0c,
BuiltInModeByte::None => 0xff,
}
}
}

View File

@@ -15,10 +15,4 @@ pub enum AuraError {
PollKeyboard,
#[error("mode not supported")]
NotSupported,
#[error("USB error")]
UsbError(rusb::Error),
#[error("IO error")]
IOError(#[from] std::io::Error),
#[error("external command failed")]
CommandFailed,
}

265
aura/src/fancy.rs Normal file
View File

@@ -0,0 +1,265 @@
#[derive(Clone)]
pub struct KeyColourArray([[u8; 64]; 10]);
impl KeyColourArray {
pub fn new() -> Self {
let mut set = [[0u8; 64]; 10];
for (count, row) in set.iter_mut().enumerate() {
row[0] = 0x5d; // Report ID
row[1] = 0xbc; // Mode = custom??, 0xb3 is builtin
row[2] = 0x00;
row[3] = 0x01; // ??
row[4] = 0x01; // ??, 4,5,6 are normally RGB for builtin mode colours
row[5] = 0x01; // ??
row[6] = (count as u8) << 4; // Key group
row[7] = 0x10; // 0b00010000 addressing? flips for group a0
if count == 9 {
row[7] = 0x08; // 0b00001000
}
row[8] = 0x00;
}
KeyColourArray(set)
}
/// Initialise and clear the keyboard for custom effects
#[inline]
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
}
#[inline]
pub fn set(&mut self, key: Key, r: u8, g: u8, b: u8) {
let (rr, gg, bb) = self.key(key);
*rr = r;
*gg = g;
*bb = b;
}
pub fn key(&mut self, key: Key) -> (&mut u8, &mut u8, &mut u8) {
// Tuples are indexes in to array
let (row, col) = match key {
Key::VolUp => (0, 15),
Key::VolDown => (0, 18),
Key::MicMute => (0, 21),
Key::ROG => (0, 24),
//
Key::Esc => (1, 24),
Key::F1 => (1, 30),
Key::F2 => (1, 33),
Key::F3 => (1, 36),
Key::F4 => (1, 39),
Key::F5 => (1, 45),
Key::F6 => (1, 48),
Key::F7 => (1, 51),
Key::F8 => (1, 54),
//
Key::F9 => (2, 12),
Key::F10 => (2, 15),
Key::F11 => (2, 18),
Key::F12 => (2, 21),
Key::Del => (2, 24),
Key::Tilde => (2, 39),
Key::N1 => (2, 42),
Key::N2 => (2, 45),
Key::N3 => (2, 48),
Key::N4 => (2, 51),
Key::N5 => (2, 54),
//
Key::N6 => (3, 9),
Key::N7 => (3, 12),
Key::N8 => (3, 15),
Key::N9 => (3, 18),
Key::N0 => (3, 21),
Key::Hyphen => (3, 24),
Key::Equals => (3, 27),
Key::BkSpc1 => (3, 30),
Key::BkSpc2 => (3, 33),
Key::BkSpc3 => (3, 36),
Key::Home => (3, 39),
Key::Tab => (3, 54),
//
Key::Q => (4, 9),
Key::W => (4, 12),
Key::E => (4, 15),
Key::R => (4, 18),
Key::T => (4, 21),
Key::Y => (4, 24),
Key::U => (4, 27),
Key::I => (4, 30),
Key::O => (4, 33),
Key::P => (4, 36),
Key::LBracket => (4, 39),
Key::RBracket => (4, 42),
Key::BackSlash => (4, 45),
Key::PgUp => (4, 54),
//
Key::Caps => (5, 21),
Key::A => (5, 24),
Key::S => (5, 27),
Key::D => (5, 30),
Key::F => (5, 33),
Key::G => (5, 36),
Key::H => (5, 39),
Key::J => (5, 42),
Key::K => (5, 45),
Key::L => (5, 48),
Key::SemiColon => (5, 51),
Key::Quote => (5, 54),
//
Key::Ret1 => (6, 12),
Key::Ret2 => (6, 15),
Key::Ret3 => (6, 18),
Key::PgDn => (6, 21),
Key::LShift => (6, 36),
Key::Z => (6, 42),
Key::X => (6, 45),
Key::C => (6, 48),
Key::V => (6, 51),
Key::B => (6, 54),
//
Key::N => (7, 9),
Key::M => (7, 12),
Key::Comma => (7, 15),
Key::Period => (7, 18),
Key::FwdSlash => (7, 21),
Key::Rshift1 => (7, 27),
Key::Rshift2 => (7, 30),
Key::Rshift3 => (7, 33),
Key::End => (7, 36),
Key::LCtrl => (7, 51),
Key::LFn => (7, 54),
//
Key::Meta => (8, 9),
Key::LAlt => (8, 12),
Key::Space1 => (8, 15),
Key::Space2 => (8, 18),
Key::Space3 => (8, 21),
Key::Space4 => (8, 24),
Key::RAlt => (8, 30),
Key::PrtSc => (8, 33),
Key::RCtrl => (8, 36),
Key::Up => (8, 42),
Key::RFn => (8, 51),
//
Key::Left => (9, 54),
//
Key::Down => (10, 9),
Key::Right => (10, 12),
};
// LOLOLOLOLOLOLOL! Look it's safe okay
unsafe {
(
&mut *(&mut self.0[row][col] as *mut u8),
&mut *(&mut self.0[row][col + 1] as *mut u8),
&mut *(&mut self.0[row][col + 2] as *mut u8),
)
}
}
#[inline]
pub fn get(&self) -> &[[u8; 64]; 10] {
&self.0
}
}
pub enum Key {
VolUp,
VolDown,
MicMute,
ROG,
Esc,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
Del,
Tilde,
N1,
N2,
N3,
N4,
N5,
N6,
N7,
N8,
N9,
N0,
Hyphen,
Equals,
BkSpc1,
BkSpc2,
BkSpc3,
Home,
Tab,
Q,
W,
E,
R,
T,
Y,
U,
I,
O,
P,
LBracket,
RBracket,
BackSlash,
PgUp,
Caps,
A,
S,
D,
F,
G,
H,
J,
K,
L,
SemiColon,
Quote,
Ret1,
Ret2,
Ret3,
PgDn,
LShift,
Z,
X,
C,
V,
B,
N,
M,
Comma,
Period,
FwdSlash,
Rshift1,
Rshift2,
Rshift3,
End,
LCtrl,
LFn,
Meta,
LAlt,
Space1,
Space2,
Space3,
Space4,
RAlt,
PrtSc,
RCtrl,
Up,
Down,
Left,
Right,
RFn,
}

233
aura/src/lib.rs Normal file
View File

@@ -0,0 +1,233 @@
pub static DBUS_NAME: &'static str = "org.rogcore.Daemon";
pub static DBUS_PATH: &'static str = "/org/rogcore/Daemon";
pub static DBUS_IFACE: &'static str = "org.rogcore.Daemon";
pub const LED_MSG_LEN: usize = 17;
mod builtins;
pub use builtins::*;
/// Contains mostly only what is required for parsing CLI options
pub mod cli_options;
mod fancy;
mod aura_dbus;
pub use aura_dbus::*;
pub use fancy::*;
pub mod error;
use crate::cli_options::*;
/// Writes aout the correct byte string for brightness
///
/// The HID descriptor looks like:
///
/// ```
/// 0x06, 0x31, 0xFF, // Usage Page (Vendor Defined 0xFF31)
/// 0x09, 0x76, // Usage (0x76)
/// 0xA1, 0x01, // Collection (Application)
/// 0x85, 0x5A, // Report ID (90)
/// 0x19, 0x00, // Usage Minimum (0x00)
/// 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
/// 0x15, 0x00, // Logical Minimum (0)
/// 0x26, 0xFF, 0x00, // Logical Maximum (255)
/// 0x75, 0x08, // Report Size (8)
/// 0x95, 0x05, // Report Count (5)
/// 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
/// 0x19, 0x00, // Usage Minimum (0x00)
/// 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
/// 0x15, 0x00, // Logical Minimum (0)
/// 0x26, 0xFF, 0x00, // Logical Maximum (255)
/// 0x75, 0x08, // Report Size (8)
/// 0x95, 0x3F, // Report Count (63)
/// 0xB1, 0x00, // Feature (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
/// 0xC0, // End Collection
/// ```
pub fn aura_brightness_bytes(brightness: u8) -> [u8; 17] {
// TODO: check brightness range
[
0x5A, 0xBA, 0xC5, 0xC4, brightness, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]
}
/// Parses `SetAuraBuiltin` in to packet data
///
/// Byte structure:
///
/// ```
/// | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12|
/// |---|---|---|---|---|---|---|---|---|---|---|---|---|
/// |5d |b3 |00 |03 |ff |00 |00 |00 |00 |00 |00 |ff |00 |
/// ```
///
/// Bytes 0 and 1 should always be 5d, b3
///
/// On multizone laptops byte 2 is the zone number, RGB in usual
/// place, byte 3 set to zero
///
/// Byte 3 sets the mode type:
/// - 00 = static
/// - 01 = breathe (can set two colours)
/// - 02 = cycle (through all colours)
/// - 03 = rainbow
/// - 04 = rain (byte 9 sets random colour)
/// - 05 = random keys, red, white, turquoise
/// - 06 = pressed keys light up and fade
/// - 07 = pressed key emits laser
/// - 08 = pressed key emits water ripple
/// - 09 = no effect/not used
/// - 0a fast pulse (no speed setting)
/// - 0b vertical line racing to right (no speed setting)
/// - 0c wider vertical line racing to right (no speed setting)
///
/// Bytes 4, 5, 6 are Red, Green, Blue
///
/// Byte 7 sets speed from
/// - 0x00 = Off
/// - 0xe1 = Slow
/// - 0xeb = Medium
/// - 0xf5 = Fast
///
/// Byte 8 sets rainbow direction:
/// - 0x00 = rightwards
/// - 0x01 = leftwards
/// - 0x02 = upwards
/// - 0x03 = downwards
///
/// Bytes 10, 11, 12 are Red, Green, Blue for second colour if mode supports it
///
/// The HID descriptor looks like:
/// ```
/// 0x06, 0x31, 0xFF, // Usage Page (Vendor Defined 0xFF31)
/// 0x09, 0x79, // Usage (0x79)
/// 0xA1, 0x01, // Collection (Application)
/// 0x85, 0x5D, // Report ID (93)
/// 0x19, 0x00, // Usage Minimum (0x00)
/// 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
/// 0x15, 0x00, // Logical Minimum (0)
/// 0x26, 0xFF, 0x00, // Logical Maximum (255)
/// 0x75, 0x08, // Report Size (8)
/// 0x95, 0x1F, // Report Count (31)
/// 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
/// 0x19, 0x00, // Usage Minimum (0x00)
/// 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
/// 0x15, 0x00, // Logical Minimum (0)
/// 0x26, 0xFF, 0x00, // Logical Maximum (255)
/// 0x75, 0x08, // Report Size (8)
/// 0x95, 0x3F, // Report Count (63)
/// 0x91, 0x00, // Output (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
/// 0x19, 0x00, // Usage Minimum (0x00)
/// 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
/// 0x15, 0x00, // Logical Minimum (0)
/// 0x26, 0xFF, 0x00, // Logical Maximum (255)
/// 0x75, 0x08, // Report Size (8)
/// 0x95, 0x3F, // Report Count (63)
/// 0xB1, 0x00, // Feature (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
/// 0xC0, // End Collection
/// ```
///
/// This descriptor is also used for the per-key LED settings
impl From<&SetAuraBuiltin> for [u8; LED_MSG_LEN] {
fn from(mode: &SetAuraBuiltin) -> Self {
let mut msg = [0u8; LED_MSG_LEN];
msg[0] = 0x5d;
msg[1] = 0xb3;
match mode {
SetAuraBuiltin::Stable(_) => msg[3] = 0x00,
SetAuraBuiltin::Breathe(_) => msg[3] = 0x01,
SetAuraBuiltin::Cycle(_) => msg[3] = 0x02,
SetAuraBuiltin::Rainbow(_) => msg[3] = 0x03,
SetAuraBuiltin::Rain(_) => msg[3] = 0x04,
SetAuraBuiltin::Disco(_) => msg[3] = 0x05,
SetAuraBuiltin::Highlight(_) => msg[3] = 0x06,
SetAuraBuiltin::Laser(_) => msg[3] = 0x07,
SetAuraBuiltin::Ripple(_) => msg[3] = 0x08,
SetAuraBuiltin::Pulse(_) => msg[3] = 0x0a,
SetAuraBuiltin::ThinZoomy(_) => msg[3] = 0x0b,
SetAuraBuiltin::WideZoomy(_) => msg[3] = 0x0c,
_ => panic!("Mode not convertable to array"),
}
match mode {
SetAuraBuiltin::Rainbow(settings) => {
msg[7] = settings.speed as u8;
msg[8] = settings.direction as u8;
}
SetAuraBuiltin::Rain(settings) => {
msg[4] = settings.colour.0;
msg[5] = settings.colour.1;
msg[6] = settings.colour.2;
msg[7] = settings.speed as u8;
msg[9] = settings.colour2.2;
}
SetAuraBuiltin::Breathe(settings) => {
msg[4] = settings.colour.0;
msg[5] = settings.colour.1;
msg[6] = settings.colour.2;
msg[7] = settings.speed as u8;
msg[10] = settings.colour2.0;
msg[11] = settings.colour2.1;
msg[12] = settings.colour2.2;
}
SetAuraBuiltin::Cycle(settings) | SetAuraBuiltin::Disco(settings) => {
msg[7] = settings.speed as u8;
}
SetAuraBuiltin::Highlight(settings)
| SetAuraBuiltin::Laser(settings)
| SetAuraBuiltin::Ripple(settings) => {
msg[4] = settings.colour.0;
msg[5] = settings.colour.1;
msg[6] = settings.colour.2;
msg[7] = settings.speed as u8;
}
SetAuraBuiltin::Stable(settings)
| SetAuraBuiltin::Pulse(settings)
| SetAuraBuiltin::ThinZoomy(settings)
| SetAuraBuiltin::WideZoomy(settings) => {
msg[4] = settings.colour.0;
msg[5] = settings.colour.1;
msg[6] = settings.colour.2;
}
_ => panic!("Mode not convertable to array"),
}
msg
}
}
impl From<SetAuraBuiltin> for [u8; LED_MSG_LEN] {
#[inline]
fn from(mode: SetAuraBuiltin) -> Self {
<[u8; LED_MSG_LEN]>::from(&mode)
}
}
impl From<SetAuraBuiltin> for [[u8; LED_MSG_LEN]; 4] {
#[inline]
fn from(mode: SetAuraBuiltin) -> Self {
let mut msg = [[0u8; LED_MSG_LEN]; 4];
for i in 0..4 {
msg[i][0] = 0x5d;
msg[i][1] = 0xb3;
msg[i][2] = i as u8 + 1;
}
match mode {
SetAuraBuiltin::MultiStatic(settings) => {
msg[0][4] = settings.colour1.0;
msg[0][5] = settings.colour1.1;
msg[0][6] = settings.colour1.2;
msg[1][4] = settings.colour2.0;
msg[1][5] = settings.colour2.1;
msg[1][6] = settings.colour2.2;
msg[2][4] = settings.colour3.0;
msg[2][5] = settings.colour3.1;
msg[2][6] = settings.colour3.2;
msg[3][4] = settings.colour4.0;
msg[3][5] = settings.colour4.1;
msg[3][6] = settings.colour4.2;
}
_ => panic!("Mode not convertable to array"),
}
msg
}
}

1016
rog-core/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

45
rog-core/Cargo.toml Normal file
View File

@@ -0,0 +1,45 @@
[package]
name = "rog-daemon"
version = "0.8.0"
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
repository = "https://github.com/flukejones/rog-core"
homepage = "https://github.com/flukejones/rog-core"
description = "A daemon app for ASUS GX502 and similar laptops to control missing features"
edition = "2018"
[lib]
name = "daemon"
path = "src/lib.rs"
[[bin]]
name = "rog-core"
path = "src/main.rs"
[dependencies]
rog-aura = { path = "../aura" }
rusb = "^0.5.5"
# cli and logging
gumdrop = "^0.8.0"
log = "^0.4.8"
env_logger = "^0.7.1"
# async
dbus = { version = "^0.8.2", features = ["futures"] }
dbus-tokio = "^0.5.1"
tokio = { version = "0.2.4", features = ["rt-threaded", "macros", "sync"] }
# serialisation
serde = "1.0"
serde_derive = "1.0"
toml = "0.5"
# Device control
# sysfs-class = "^0.1.2" # used for backlight control mostly
# cpu power management
intel-pstate = "^0.2.1"
# virtualisation of HID, mainly for outputting consumer key codes
uhid-virt = "^0.0.4"
#keycode = "0.3"

View File

@@ -1,4 +1,4 @@
use crate::aura::BuiltInModeBytes;
use rog_aura::BuiltInModeBytes;
use serde_derive::{Deserialize, Serialize};
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};

View File

@@ -1,13 +1,9 @@
// Return show-stopping errors, otherwise map error to a log level
use crate::{
aura::{aura_brightness_bytes, BuiltInModeByte},
config::Config,
error::AuraError,
virt_device::VirtKeys,
};
use crate::{config::Config, virt_device::VirtKeys};
use gumdrop::Options;
use log::{debug, error, info, warn};
use rog_aura::{aura_brightness_bytes, error::AuraError, BuiltInModeByte};
use rusb::DeviceHandle;
use std::error::Error;
use std::fs::OpenOptions;
@@ -48,7 +44,11 @@ pub(crate) struct RogCore {
}
impl RogCore {
pub(crate) fn new(vendor: u16, product: u16, led_endpoint: u8) -> Result<RogCore, AuraError> {
pub(crate) fn new(
vendor: u16,
product: u16,
led_endpoint: u8,
) -> Result<RogCore, Box<dyn Error>> {
let mut dev_handle = RogCore::get_device(vendor, product)?;
dev_handle.set_active_configuration(0).unwrap_or(());
@@ -72,9 +72,7 @@ impl RogCore {
}
dev_handle.set_auto_detach_kernel_driver(true).unwrap();
dev_handle
.claim_interface(interface)
.map_err(AuraError::UsbError)?;
dev_handle.claim_interface(interface)?;
Ok(RogCore {
handle: dev_handle,
@@ -115,14 +113,14 @@ impl RogCore {
fn get_device(
vendor: u16,
product: u16,
) -> Result<DeviceHandle<rusb::GlobalContext>, AuraError> {
) -> Result<DeviceHandle<rusb::GlobalContext>, rusb::Error> {
for device in rusb::devices().unwrap().iter() {
let device_desc = device.device_descriptor().unwrap();
if device_desc.vendor_id() == vendor && device_desc.product_id() == product {
return device.open().map_err(|err| AuraError::UsbError(err));
return device.open();
}
}
Err(AuraError::UsbError(rusb::Error::NoDevice))
Err(rusb::Error::NoDevice)
}
pub(crate) fn fan_mode_step(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>> {

View File

@@ -1,11 +1,11 @@
use crate::{aura::*, config::Config, core::*, laptops::match_laptop};
use crate::{config::Config, core::*, laptops::match_laptop};
use dbus::{
nonblock::Process,
tree::{Factory, MTSync, Method, MethodErr, Tree},
};
use dbus_tokio::connection;
use log::{error, info, warn};
use rog_aura::{DBUS_IFACE, DBUS_PATH};
use std::error::Error;
use std::sync::Arc;
use std::time::{Duration, Instant};
@@ -134,6 +134,8 @@ pub async fn start_daemon() -> Result<(), Box<dyn Error>> {
std::thread::sleep(Duration::from_millis(200));
} else if now.duration_since(time_mark).as_millis() > 100 {
std::thread::sleep(Duration::from_millis(50));
} else {
std::thread::sleep(Duration::from_micros(300));
}
}
});

View File

@@ -1,7 +1,6 @@
use crate::aura::BuiltInModeByte;
use crate::config::Config;
use crate::core::{LedWriter, RogCore};
use crate::error::AuraError;
use rog_aura::{error::AuraError, BuiltInModeByte};
//use keycode::{KeyMap, KeyMappingId, KeyState, KeyboardState};
use crate::virt_device::ConsumerKeys;
use log::{info, warn};

View File

@@ -1,11 +1,7 @@
pub mod aura;
/// Contains mostly only what is required for parsing CLI options
pub mod cli_options;
mod config;
/// The core module which allows writing to LEDs or polling the
/// laptop keyboard attached devices
pub mod core;
pub mod daemon;
mod error;
pub mod laptops;
mod virt_device;

View File

@@ -1,12 +1,8 @@
use daemon::{
aura::{AuraDbusWriter, LED_MSG_LEN},
cli_options::SetAuraBuiltin,
core::LedBrightness,
daemon::start_daemon,
};
use daemon::{core::LedBrightness, daemon::start_daemon};
use env_logger::{Builder, Target};
use gumdrop::Options;
use log::LevelFilter;
use rog_aura::{cli_options::SetAuraBuiltin, AuraDbusWriter, LED_MSG_LEN};
static VERSION: &'static str = "0.8.0";

View File

@@ -1,714 +0,0 @@
pub static DBUS_NAME: &'static str = "org.rogcore.Daemon";
pub static DBUS_PATH: &'static str = "/org/rogcore/Daemon";
pub static DBUS_IFACE: &'static str = "org.rogcore.Daemon";
pub const LED_MSG_LEN: usize = 17;
use crate::cli_options::*;
use serde_derive::{Deserialize, Serialize};
/// Writes aout the correct byte string for brightness
///
/// The HID descriptor looks like:
///
/// ```
/// 0x06, 0x31, 0xFF, // Usage Page (Vendor Defined 0xFF31)
/// 0x09, 0x76, // Usage (0x76)
/// 0xA1, 0x01, // Collection (Application)
/// 0x85, 0x5A, // Report ID (90)
/// 0x19, 0x00, // Usage Minimum (0x00)
/// 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
/// 0x15, 0x00, // Logical Minimum (0)
/// 0x26, 0xFF, 0x00, // Logical Maximum (255)
/// 0x75, 0x08, // Report Size (8)
/// 0x95, 0x05, // Report Count (5)
/// 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
/// 0x19, 0x00, // Usage Minimum (0x00)
/// 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
/// 0x15, 0x00, // Logical Minimum (0)
/// 0x26, 0xFF, 0x00, // Logical Maximum (255)
/// 0x75, 0x08, // Report Size (8)
/// 0x95, 0x3F, // Report Count (63)
/// 0xB1, 0x00, // Feature (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
/// 0xC0, // End Collection
/// ```
pub fn aura_brightness_bytes(brightness: u8) -> [u8; 17] {
// TODO: check brightness range
[
0x5A, 0xBA, 0xC5, 0xC4, brightness, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]
}
/// Parses `SetAuraBuiltin` in to packet data
///
/// Byte structure:
///
/// ```
/// | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12|
/// |---|---|---|---|---|---|---|---|---|---|---|---|---|
/// |5d |b3 |00 |03 |ff |00 |00 |00 |00 |00 |00 |ff |00 |
/// ```
///
/// Bytes 0 and 1 should always be 5d, b3
///
/// On multizone laptops byte 2 is the zone number, RGB in usual
/// place, byte 3 set to zero
///
/// Byte 3 sets the mode type:
/// - 00 = static
/// - 01 = breathe (can set two colours)
/// - 02 = cycle (through all colours)
/// - 03 = rainbow
/// - 04 = rain (byte 9 sets random colour)
/// - 05 = random keys, red, white, turquoise
/// - 06 = pressed keys light up and fade
/// - 07 = pressed key emits laser
/// - 08 = pressed key emits water ripple
/// - 09 = no effect/not used
/// - 0a fast pulse (no speed setting)
/// - 0b vertical line racing to right (no speed setting)
/// - 0c wider vertical line racing to right (no speed setting)
///
/// Bytes 4, 5, 6 are Red, Green, Blue
///
/// Byte 7 sets speed from
/// - 0x00 = Off
/// - 0xe1 = Slow
/// - 0xeb = Medium
/// - 0xf5 = Fast
///
/// Byte 8 sets rainbow direction:
/// - 0x00 = rightwards
/// - 0x01 = leftwards
/// - 0x02 = upwards
/// - 0x03 = downwards
///
/// Bytes 10, 11, 12 are Red, Green, Blue for second colour if mode supports it
///
/// The HID descriptor looks like:
/// ```
/// 0x06, 0x31, 0xFF, // Usage Page (Vendor Defined 0xFF31)
/// 0x09, 0x79, // Usage (0x79)
/// 0xA1, 0x01, // Collection (Application)
/// 0x85, 0x5D, // Report ID (93)
/// 0x19, 0x00, // Usage Minimum (0x00)
/// 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
/// 0x15, 0x00, // Logical Minimum (0)
/// 0x26, 0xFF, 0x00, // Logical Maximum (255)
/// 0x75, 0x08, // Report Size (8)
/// 0x95, 0x1F, // Report Count (31)
/// 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
/// 0x19, 0x00, // Usage Minimum (0x00)
/// 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
/// 0x15, 0x00, // Logical Minimum (0)
/// 0x26, 0xFF, 0x00, // Logical Maximum (255)
/// 0x75, 0x08, // Report Size (8)
/// 0x95, 0x3F, // Report Count (63)
/// 0x91, 0x00, // Output (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
/// 0x19, 0x00, // Usage Minimum (0x00)
/// 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
/// 0x15, 0x00, // Logical Minimum (0)
/// 0x26, 0xFF, 0x00, // Logical Maximum (255)
/// 0x75, 0x08, // Report Size (8)
/// 0x95, 0x3F, // Report Count (63)
/// 0xB1, 0x00, // Feature (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
/// 0xC0, // End Collection
/// ```
///
/// This descriptor is also used for the per-key LED settings
impl From<&SetAuraBuiltin> for [u8; LED_MSG_LEN] {
fn from(mode: &SetAuraBuiltin) -> Self {
let mut msg = [0u8; LED_MSG_LEN];
msg[0] = 0x5d;
msg[1] = 0xb3;
match mode {
SetAuraBuiltin::Stable(_) => msg[3] = 0x00,
SetAuraBuiltin::Breathe(_) => msg[3] = 0x01,
SetAuraBuiltin::Cycle(_) => msg[3] = 0x02,
SetAuraBuiltin::Rainbow(_) => msg[3] = 0x03,
SetAuraBuiltin::Rain(_) => msg[3] = 0x04,
SetAuraBuiltin::Disco(_) => msg[3] = 0x05,
SetAuraBuiltin::Highlight(_) => msg[3] = 0x06,
SetAuraBuiltin::Laser(_) => msg[3] = 0x07,
SetAuraBuiltin::Ripple(_) => msg[3] = 0x08,
SetAuraBuiltin::Pulse(_) => msg[3] = 0x0a,
SetAuraBuiltin::ThinZoomy(_) => msg[3] = 0x0b,
SetAuraBuiltin::WideZoomy(_) => msg[3] = 0x0c,
_ => panic!("Mode not convertable to array"),
}
match mode {
SetAuraBuiltin::Rainbow(settings) => {
msg[7] = settings.speed as u8;
msg[8] = settings.direction as u8;
}
SetAuraBuiltin::Rain(settings) => {
msg[4] = settings.colour.0;
msg[5] = settings.colour.1;
msg[6] = settings.colour.2;
msg[7] = settings.speed as u8;
msg[9] = settings.colour2.2;
}
SetAuraBuiltin::Breathe(settings) => {
msg[4] = settings.colour.0;
msg[5] = settings.colour.1;
msg[6] = settings.colour.2;
msg[7] = settings.speed as u8;
msg[10] = settings.colour2.0;
msg[11] = settings.colour2.1;
msg[12] = settings.colour2.2;
}
SetAuraBuiltin::Cycle(settings) | SetAuraBuiltin::Disco(settings) => {
msg[7] = settings.speed as u8;
}
SetAuraBuiltin::Highlight(settings)
| SetAuraBuiltin::Laser(settings)
| SetAuraBuiltin::Ripple(settings) => {
msg[4] = settings.colour.0;
msg[5] = settings.colour.1;
msg[6] = settings.colour.2;
msg[7] = settings.speed as u8;
}
SetAuraBuiltin::Stable(settings)
| SetAuraBuiltin::Pulse(settings)
| SetAuraBuiltin::ThinZoomy(settings)
| SetAuraBuiltin::WideZoomy(settings) => {
msg[4] = settings.colour.0;
msg[5] = settings.colour.1;
msg[6] = settings.colour.2;
}
_ => panic!("Mode not convertable to array"),
}
msg
}
}
impl From<SetAuraBuiltin> for [u8; LED_MSG_LEN] {
fn from(mode: SetAuraBuiltin) -> Self {
<[u8; LED_MSG_LEN]>::from(&mode)
}
}
impl From<SetAuraBuiltin> for [[u8; LED_MSG_LEN]; 4] {
fn from(mode: SetAuraBuiltin) -> Self {
let mut msg = [[0u8; LED_MSG_LEN]; 4];
for i in 0..4 {
msg[i][0] = 0x5d;
msg[i][1] = 0xb3;
msg[i][2] = i as u8 + 1;
}
match mode {
SetAuraBuiltin::MultiStatic(settings) => {
msg[0][4] = settings.colour1.0;
msg[0][5] = settings.colour1.1;
msg[0][6] = settings.colour1.2;
msg[1][4] = settings.colour2.0;
msg[1][5] = settings.colour2.1;
msg[1][6] = settings.colour2.2;
msg[2][4] = settings.colour3.0;
msg[2][5] = settings.colour3.1;
msg[2][6] = settings.colour3.2;
msg[3][4] = settings.colour4.0;
msg[3][5] = settings.colour4.1;
msg[3][6] = settings.colour4.2;
}
_ => panic!("Mode not convertable to array"),
}
msg
}
}
/// Container for the byte strings used in modes. Generally useful for settings
/// and other usecases.
#[derive(Deserialize, Serialize)]
pub struct BuiltInModeBytes {
pub stable: [u8; LED_MSG_LEN],
pub breathe: [u8; LED_MSG_LEN],
pub cycle: [u8; LED_MSG_LEN],
pub rainbow: [u8; LED_MSG_LEN],
pub rain: [u8; LED_MSG_LEN],
pub random: [u8; LED_MSG_LEN],
pub highlight: [u8; LED_MSG_LEN],
pub laser: [u8; LED_MSG_LEN],
pub ripple: [u8; LED_MSG_LEN],
pub pulse: [u8; LED_MSG_LEN],
pub thinzoomy: [u8; LED_MSG_LEN],
pub widezoomy: [u8; LED_MSG_LEN],
pub multi_static: [[u8; LED_MSG_LEN]; 4],
}
impl BuiltInModeBytes {
pub fn set_field_from(&mut self, bytes: &[u8]) {
if bytes[0] == 0x5d && bytes[1] == 0xb3 {
let b = BuiltInModeByte::from(bytes[3]);
match b {
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),
BuiltInModeByte::Random => self.random.copy_from_slice(bytes),
BuiltInModeByte::Highlight => self.highlight.copy_from_slice(bytes),
BuiltInModeByte::Laser => self.laser.copy_from_slice(bytes),
BuiltInModeByte::Ripple => self.ripple.copy_from_slice(bytes),
BuiltInModeByte::Pulse => self.pulse.copy_from_slice(bytes),
BuiltInModeByte::ThinZoomy => self.thinzoomy.copy_from_slice(bytes),
BuiltInModeByte::WideZoomy => self.widezoomy.copy_from_slice(bytes),
_ => {}
}
}
}
pub fn get_field_from(&mut self, byte: u8) -> Option<&[u8]> {
let bytes = match BuiltInModeByte::from(byte) {
BuiltInModeByte::Single => &self.stable,
BuiltInModeByte::Breathing => &self.breathe,
BuiltInModeByte::Cycle => &self.cycle,
BuiltInModeByte::Rainbow => &self.rainbow,
BuiltInModeByte::Rain => &self.rain,
BuiltInModeByte::Random => &self.random,
BuiltInModeByte::Highlight => &self.highlight,
BuiltInModeByte::Laser => &self.laser,
BuiltInModeByte::Ripple => &self.ripple,
BuiltInModeByte::Pulse => &self.pulse,
BuiltInModeByte::ThinZoomy => &self.thinzoomy,
BuiltInModeByte::WideZoomy => &self.widezoomy,
_ => return None,
};
return Some(bytes);
}
}
impl Default for BuiltInModeBytes {
fn default() -> Self {
BuiltInModeBytes {
stable: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Stable(SingleColour::default())),
breathe: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Breathe(TwoColourSpeed::default())),
cycle: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Cycle(SingleSpeed::default())),
rainbow: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Rainbow(
SingleSpeedDirection::default(),
)),
rain: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Rain(TwoColourSpeed::default())),
random: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Disco(SingleSpeed::default())),
highlight: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Highlight(
SingleColourSpeed::default(),
)),
laser: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Laser(SingleColourSpeed::default())),
ripple: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Ripple(SingleColourSpeed::default())),
pulse: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Pulse(SingleColour::default())),
thinzoomy: <[u8; LED_MSG_LEN]>::from(
SetAuraBuiltin::ThinZoomy(SingleColour::default()),
),
widezoomy: <[u8; LED_MSG_LEN]>::from(
SetAuraBuiltin::WideZoomy(SingleColour::default()),
),
multi_static: <[[u8; LED_MSG_LEN]; 4]>::from(SetAuraBuiltin::MultiStatic(
MultiColour::default(),
)),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub enum BuiltInModeByte {
Single = 0x00,
Breathing = 0x01,
Cycle = 0x02,
Rainbow = 0x03,
Rain = 0x04,
Random = 0x05,
Highlight = 0x06,
Laser = 0x07,
Ripple = 0x08,
Pulse = 0x0a,
ThinZoomy = 0x0b,
WideZoomy = 0x0c,
None,
}
impl Default for BuiltInModeByte {
fn default() -> Self {
BuiltInModeByte::Single
}
}
impl From<u8> for BuiltInModeByte {
fn from(byte: u8) -> Self {
match byte {
0x00 => Self::Single,
0x01 => Self::Breathing,
0x02 => Self::Cycle,
0x03 => Self::Rainbow,
0x04 => Self::Rain,
0x05 => Self::Random,
0x06 => Self::Highlight,
0x07 => Self::Laser,
0x08 => Self::Ripple,
0x0a => Self::Pulse,
0x0b => Self::ThinZoomy,
0x0c => Self::WideZoomy,
_ => Self::None,
}
}
}
impl From<&u8> for BuiltInModeByte {
fn from(byte: &u8) -> Self {
Self::from(*byte)
}
}
impl From<BuiltInModeByte> for u8 {
fn from(byte: BuiltInModeByte) -> Self {
match byte {
BuiltInModeByte::Single => 0x00,
BuiltInModeByte::Breathing => 0x01,
BuiltInModeByte::Cycle => 0x02,
BuiltInModeByte::Rainbow => 0x03,
BuiltInModeByte::Rain => 0x04,
BuiltInModeByte::Random => 0x05,
BuiltInModeByte::Highlight => 0x06,
BuiltInModeByte::Laser => 0x07,
BuiltInModeByte::Ripple => 0x08,
BuiltInModeByte::Pulse => 0x0a,
BuiltInModeByte::ThinZoomy => 0x0b,
BuiltInModeByte::WideZoomy => 0x0c,
BuiltInModeByte::None => 0xff,
}
}
}
#[derive(Clone)]
pub struct KeyColourArray([[u8; 64]; 10]);
impl KeyColourArray {
pub fn new() -> Self {
let mut set = [[0u8; 64]; 10];
for (count, row) in set.iter_mut().enumerate() {
row[0] = 0x5d; // Report ID
row[1] = 0xbc; // Mode = custom??, 0xb3 is builtin
row[2] = 0x00;
row[3] = 0x01; // ??
row[4] = 0x01; // ??, 4,5,6 are normally RGB for builtin mode colours
row[5] = 0x01; // ??
row[6] = (count as u8) << 4; // Key group
row[7] = 0x10; // 0b00010000 addressing? flips for group a0
if count == 9 {
row[7] = 0x08; // 0b00001000
}
row[8] = 0x00;
}
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;
*gg = g;
*bb = b;
}
pub fn key(&mut self, key: Key) -> (&mut u8, &mut u8, &mut u8) {
// Tuples are indexes in to array
let (row, col) = match key {
Key::VolUp => (0, 15),
Key::VolDown => (0, 18),
Key::MicMute => (0, 21),
Key::ROG => (0, 24),
//
Key::Esc => (1, 24),
Key::F1 => (1, 30),
Key::F2 => (1, 33),
Key::F3 => (1, 36),
Key::F4 => (1, 39),
Key::F5 => (1, 45),
Key::F6 => (1, 48),
Key::F7 => (1, 51),
Key::F8 => (1, 54),
//
Key::F9 => (2, 12),
Key::F10 => (2, 15),
Key::F11 => (2, 18),
Key::F12 => (2, 21),
Key::Del => (2, 24),
Key::Tilde => (2, 39),
Key::N1 => (2, 42),
Key::N2 => (2, 45),
Key::N3 => (2, 48),
Key::N4 => (2, 51),
Key::N5 => (2, 54),
//
Key::N6 => (3, 9),
Key::N7 => (3, 12),
Key::N8 => (3, 15),
Key::N9 => (3, 18),
Key::N0 => (3, 21),
Key::Hyphen => (3, 24),
Key::Equals => (3, 27),
Key::BkSpc1 => (3, 30),
Key::BkSpc2 => (3, 33),
Key::BkSpc3 => (3, 36),
Key::Home => (3, 39),
Key::Tab => (3, 54),
//
Key::Q => (4, 9),
Key::W => (4, 12),
Key::E => (4, 15),
Key::R => (4, 18),
Key::T => (4, 21),
Key::Y => (4, 24),
Key::U => (4, 27),
Key::I => (4, 30),
Key::O => (4, 33),
Key::P => (4, 36),
Key::LBracket => (4, 39),
Key::RBracket => (4, 42),
Key::BackSlash => (4, 45),
Key::PgUp => (4, 54),
//
Key::Caps => (5, 21),
Key::A => (5, 24),
Key::S => (5, 27),
Key::D => (5, 30),
Key::F => (5, 33),
Key::G => (5, 36),
Key::H => (5, 39),
Key::J => (5, 42),
Key::K => (5, 45),
Key::L => (5, 48),
Key::SemiColon => (5, 51),
Key::Quote => (5, 54),
//
Key::Ret1 => (6, 12),
Key::Ret2 => (6, 15),
Key::Ret3 => (6, 18),
Key::PgDn => (6, 21),
Key::LShift => (6, 36),
Key::Z => (6, 42),
Key::X => (6, 45),
Key::C => (6, 48),
Key::V => (6, 51),
Key::B => (6, 54),
//
Key::N => (7, 9),
Key::M => (7, 12),
Key::Comma => (7, 15),
Key::Period => (7, 18),
Key::FwdSlash => (7, 21),
Key::Rshift1 => (7, 27),
Key::Rshift2 => (7, 30),
Key::Rshift3 => (7, 33),
Key::End => (7, 36),
Key::LCtrl => (7, 51),
Key::LFn => (7, 54),
//
Key::Meta => (8, 9),
Key::LAlt => (8, 12),
Key::Space1 => (8, 15),
Key::Space2 => (8, 18),
Key::Space3 => (8, 21),
Key::Space4 => (8, 24),
Key::RAlt => (8, 30),
Key::PrtSc => (8, 33),
Key::RCtrl => (8, 36),
Key::Up => (8, 42),
Key::RFn => (8, 51),
//
Key::Left => (9, 54),
//
Key::Down => (10, 9),
Key::Right => (10, 12),
};
// LOLOLOLOLOLOLOL! Look it's safe okay
unsafe {
(
&mut *(&mut self.0[row][col] as *mut u8),
&mut *(&mut self.0[row][col + 1] as *mut u8),
&mut *(&mut self.0[row][col + 2] as *mut u8),
)
}
}
pub fn get(&self) -> &[[u8; 64]; 10] {
&self.0
}
}
pub enum Key {
VolUp,
VolDown,
MicMute,
ROG,
Esc,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
Del,
Tilde,
N1,
N2,
N3,
N4,
N5,
N6,
N7,
N8,
N9,
N0,
Hyphen,
Equals,
BkSpc1,
BkSpc2,
BkSpc3,
Home,
Tab,
Q,
W,
E,
R,
T,
Y,
U,
I,
O,
P,
LBracket,
RBracket,
BackSlash,
PgUp,
Caps,
A,
S,
D,
F,
G,
H,
J,
K,
L,
SemiColon,
Quote,
Ret1,
Ret2,
Ret3,
PgDn,
LShift,
Z,
X,
C,
V,
B,
N,
M,
Comma,
Period,
FwdSlash,
Rshift1,
Rshift2,
Rshift3,
End,
LCtrl,
LFn,
Meta,
LAlt,
Space1,
Space2,
Space3,
Space4,
RAlt,
PrtSc,
RCtrl,
Up,
Down,
Left,
Right,
RFn,
}
use dbus::{ffidisp::Connection, Message};
use std::error::Error;
use std::{thread, time::Duration};
/// Simplified way to write a effect block
pub struct AuraDbusWriter {
connection: Connection,
block_time: u64,
}
impl AuraDbusWriter {
pub fn new() -> Result<Self, Box<dyn Error>> {
Ok(AuraDbusWriter {
connection: Connection::new_system()?,
block_time: 10,
})
}
/// This method must always be called before the very first write to initialise
/// the keyboard LED EC in the correct mode
pub fn init_effect(&self) -> Result<(), Box<dyn Error>> {
let msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "ledmessage")?
.append1(KeyColourArray::get_init_msg());
self.connection.send(msg).unwrap();
Ok(())
}
/// Write a single colour block.
///
/// Intentionally blocks for 10ms after sending to allow the block to
/// be written to the keyboard EC. This should not be async.
pub fn write_colour_block(
&self,
key_colour_array: &KeyColourArray,
) -> Result<(), Box<dyn Error>> {
let group = key_colour_array.get();
let msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "ledeffect")?
.append1(&group[0].to_vec())
.append1(&group[1].to_vec())
.append1(&group[2].to_vec())
.append1(&group[3].to_vec())
.append1(&group[4].to_vec())
.append1(&group[5].to_vec())
.append1(&group[6].to_vec())
.append1(&group[7].to_vec())
.append1(&group[8].to_vec())
.append1(&group[9].to_vec());
self.connection.send(msg).unwrap();
thread::sleep(Duration::from_millis(self.block_time));
Ok(())
}
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")?
.append1(bytes.to_vec());
let r = self.connection.send_with_reply_and_block(msg, 5000)?;
if let Some(reply) = r.get1::<&str>() {
return Ok(reply.to_owned());
}
Err(Box::new(dbus::Error::new_custom("name", "message")))
}
pub fn write_builtin_mode(
&self,
mode: &SetAuraBuiltin,
) -> Result<String, Box<dyn std::error::Error>> {
let bytes = <[u8; LED_MSG_LEN]>::from(mode);
self.write_bytes(&bytes)
}
pub fn write_brightness(&self, level: u8) -> Result<String, Box<dyn std::error::Error>> {
self.write_bytes(&aura_brightness_bytes(level))
}
}

Binary file not shown.

Binary file not shown.