Complete rename

This commit is contained in:
Luke D Jones
2020-08-04 20:05:12 +12:00
parent 76a8f7678c
commit 584ca023ad
50 changed files with 150 additions and 307 deletions

78
asus-nb/src/anime_dbus.rs Normal file
View File

@@ -0,0 +1,78 @@
use crate::anime_matrix::AniMePacketType;
use crate::{DBUS_IFACE, DBUS_NAME, DBUS_PATH};
use dbus::channel::Sender;
use dbus::{blocking::Connection, Message};
use std::error::Error;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use std::{thread, time::Duration};
pub const ANIME_PANE1_PREFIX: [u8; 7] = [0x5e, 0xc0, 0x02, 0x01, 0x00, 0x73, 0x02];
pub const ANIME_PANE2_PREFIX: [u8; 7] = [0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02];
/// Interface for the AniMe dot-matrix display
///
/// The resolution is 34x56 (1904) but only 1,215 LEDs in the top-left are used.
/// The display is available only on select GA401 models.
///
/// Actual image ration when displayed is stretched width.
///
/// Data structure should be nested array of [[u8; 33]; 56]
pub struct AniMeDbusWriter {
connection: Box<Connection>,
block_time: u64,
stop: Arc<AtomicBool>,
}
impl AniMeDbusWriter {
#[inline]
pub fn new() -> Result<Self, Box<dyn Error>> {
let connection = Connection::new_system()?;
Ok(AniMeDbusWriter {
connection: Box::new(connection),
block_time: 25,
stop: Arc::new(AtomicBool::new(false)),
})
}
pub fn write_image_to_buf(_buf: &mut AniMePacketType, _image_data: &[u8]) {
unimplemented!("Image format is in progress of being worked out")
}
/// Write an Animatrix image
///
/// The expected input here is *two* Vectors, 640 bytes in length. The two vectors
/// are each one half of the full image write.
///
/// After each write a flush is written, it is assumed that this tells the device to
/// go ahead and display the written bytes
///
/// # Note:
/// The vectors are expected to contain the full sequence of bytes as follows
///
/// - Write packet 1: 0x5e 0xc0 0x02 0x01 0x00 0x73 0x02 .. <led brightness>
/// - Write packet 2: 0x5e 0xc0 0x02 0x74 0x02 0x73 0x02 .. <led brightness>
///
/// Where led brightness is 0..255, low to high
#[inline]
pub fn write_image(&mut self, image: &mut AniMePacketType) -> Result<(), Box<dyn Error>> {
if image[0][0] != ANIME_PANE1_PREFIX[0] && image[0][6] != ANIME_PANE1_PREFIX[6] {
image[0][..7].copy_from_slice(&ANIME_PANE1_PREFIX);
}
if image[1][0] != ANIME_PANE2_PREFIX[0] && image[1][6] != ANIME_PANE2_PREFIX[6] {
image[1][..7].copy_from_slice(&ANIME_PANE2_PREFIX);
}
let mut msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "AnimatrixWrite")?
.append2(image[0].to_vec(), image[1].to_vec());
msg.set_no_reply(true);
self.connection.send(msg).unwrap();
thread::sleep(Duration::from_millis(self.block_time));
if self.stop.load(Ordering::Relaxed) {
panic!("Got signal to stop!");
}
Ok(())
}
}

236
asus-nb/src/anime_matrix.rs Normal file
View File

@@ -0,0 +1,236 @@
pub const WIDTH: usize = 34; // Width is definitely 34 items
pub const HEIGHT: usize = 56;
pub type AniMeBufferType = [[u8; WIDTH]; HEIGHT];
pub type AniMePacketType = [[u8; 640]; 2];
const BLOCK_START: usize = 7;
const BLOCK_END: usize = 634;
use yansi_term::Colour::RGB;
/// Helper structure for writing images.
///
/// See the examples for ways to write an image to `AniMeMatrix` format.
pub struct AniMeMatrix(AniMeBufferType);
impl Default for AniMeMatrix {
fn default() -> Self {
Self::new()
}
}
impl AniMeMatrix {
pub fn new() -> Self {
AniMeMatrix([[0u8; WIDTH]; HEIGHT])
}
pub fn get(&self) -> &AniMeBufferType {
&self.0
}
pub fn get_mut(&mut self) -> &mut AniMeBufferType {
&mut self.0
}
pub fn fill_with(&mut self, fill: u8) {
for row in self.0.iter_mut() {
for x in row.iter_mut() {
*x = fill;
}
}
}
pub fn debug_print(&self) {
// this is the index from right. It is used to progressively shorten rows
let mut prog_row_len = WIDTH - 2;
for (count, row) in self.0.iter().enumerate() {
// Write the top block of LEDs (first 7 rows)
if count < 6 {
if count % 2 != 0 {
print!(" ");
} else {
print!(" ");
}
let tmp = if count == 0 || count == 1 || count == 3 || count == 5 {
row[1..].iter()
} else {
row.iter()
};
for x in tmp {
print!(" {}", RGB(*x, *x, *x).paint(&format!("{:#04X}", x)));
}
println!();
} else {
// Switch to next block (looks like )
if count % 2 != 0 {
// Row after 6 is only 1 less, then rows after 7 follow pattern
if count == 7 {
prog_row_len -= 1;
} else {
prog_row_len -= 2;
}
} else {
prog_row_len += 1; // if count 6, 0
}
let index = row.len() - prog_row_len;
if count % 2 == 0 {
print!(" ");
}
for (i, x) in row.iter().enumerate() {
if i >= index {
print!(" {}", RGB(*x, *x, *x).paint(&format!("{:#04X}", x)));
} else {
print!(" ");
}
}
println!();
}
}
}
}
impl From<AniMeMatrix> for AniMePacketType {
/// Do conversion from the nested Vec in AniMeMatrix to the two required
/// packets suitable for sending over USB
#[inline]
fn from(anime: AniMeMatrix) -> Self {
let mut buffers = [[0; 640]; 2];
let mut write_index = BLOCK_START;
let mut write_block = &mut buffers[0];
let mut block1_done = false;
// this is the index from right. It is used to progressively shorten rows
let mut prog_row_len = WIDTH - 2;
for (count, row) in anime.0.iter().enumerate() {
// Write the top block of LEDs (first 7 rows)
if count < 6 {
for (i, x) in row.iter().enumerate() {
// Rows 0, 1, 3, 5 are short and misaligned
if count == 0 || count == 1 || count == 3 || count == 5 {
if i > 0 {
write_block[write_index - 1] = *x;
}
} else {
write_block[write_index] = *x;
}
write_index += 1;
}
} else {
// Switch to next block (looks like )
if count % 2 != 0 {
// Row after 6 is only 1 less, then rows after 7 follow pattern
if count == 7 {
prog_row_len -= 1;
} else {
prog_row_len -= 2;
}
} else {
prog_row_len += 1; // if count 6, 0
}
let index = row.len() - prog_row_len;
for n in row.iter().skip(index) {
// Require a special case to catch the correct end-of-packet which is
// 6 bytes from the end
if write_index == BLOCK_END && !block1_done {
block1_done = true;
write_block = &mut buffers[1];
write_index = BLOCK_START;
}
write_block[write_index] = *n;
write_index += 1;
}
}
}
buffers
}
}
#[cfg(test)]
mod tests {
use crate::anime_matrix::{AniMeMatrix, AniMePacketType};
#[test]
fn check_data_alignment() {
let mut matrix = AniMeMatrix::new();
{
let tmp = matrix.get_mut();
for row in tmp.iter_mut() {
row[row.len() - 1] = 0xff;
}
}
let matrix: AniMePacketType = AniMePacketType::from(matrix);
// The bytes at the right of the initial AniMeMatrix should always end up aligned in the
// same place after conversion to data packets
// Check against manually worked out right align
assert_eq!(
matrix[0].to_vec(),
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
]
.to_vec()
);
assert_eq!(
matrix[1].to_vec(),
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0,
0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0,
0, 0, 0, 0
]
.to_vec()
);
}
}

287
asus-nb/src/aura_modes.rs Normal file
View File

@@ -0,0 +1,287 @@
use crate::cli_options;
use crate::cli_options::SetAuraBuiltin;
use serde_derive::{Deserialize, Serialize};
pub const SINGLE: u8 = 0x00;
pub const BREATHING: u8 = 0x01;
pub const STROBE: u8 = 0x02;
pub const RAINBOW: u8 = 0x03;
pub const STAR: u8 = 0x04;
pub const RAIN: u8 = 0x05;
pub const HIGHLIGHT: u8 = 0x06;
pub const LASER: u8 = 0x07;
pub const RIPPLE: u8 = 0x08;
pub const PULSE: u8 = 0x0a;
pub const COMET: u8 = 0x0b;
pub const FLASH: u8 = 0x0c;
pub const MULTISTATIC: u8 = 0x0d;
pub const RGB: u8 = 0xf0;
#[derive(Clone, Deserialize, Serialize)]
pub struct Colour(pub u8, pub u8, pub u8);
impl From<cli_options::Colour> for Colour {
fn from(c: cli_options::Colour) -> Self {
Colour(c.0, c.1, c.2)
}
}
impl Default for Colour {
fn default() -> Self {
Colour(255, 255, 255)
}
}
#[derive(Copy, Clone, Deserialize, Serialize)]
pub enum Speed {
Low = 0xe1,
Med = 0xeb,
High = 0xf5,
}
impl From<cli_options::Speed> for Speed {
fn from(s: cli_options::Speed) -> Self {
match s {
cli_options::Speed::Low => Speed::Low,
cli_options::Speed::Med => Speed::Med,
cli_options::Speed::High => Speed::High,
}
}
}
impl Default for Speed {
fn default() -> Self {
Speed::Med
}
}
/// Used for Rainbow mode.
///
/// Enum corresponds to the required integer value
#[derive(Copy, Clone, Deserialize, Serialize)]
pub enum Direction {
Right,
Left,
Up,
Down,
}
impl From<cli_options::Direction> for Direction {
fn from(s: cli_options::Direction) -> Self {
match s {
cli_options::Direction::Right => Direction::Right,
cli_options::Direction::Left => Direction::Left,
cli_options::Direction::Up => Direction::Up,
cli_options::Direction::Down => Direction::Down,
}
}
}
impl Default for Direction {
fn default() -> Self {
Direction::Right
}
}
#[derive(Clone, Default, Deserialize, Serialize)]
pub struct TwoColourSpeed {
pub colour: Colour,
pub colour2: Colour,
pub speed: Speed,
}
impl From<cli_options::TwoColourSpeed> for TwoColourSpeed {
fn from(mode: cli_options::TwoColourSpeed) -> Self {
TwoColourSpeed {
colour: mode.colour.into(),
colour2: mode.colour2.into(),
speed: mode.speed.into(),
}
}
}
#[derive(Clone, Default, Deserialize, Serialize)]
pub struct SingleSpeed {
pub speed: Speed,
}
impl From<cli_options::SingleSpeed> for SingleSpeed {
fn from(mode: cli_options::SingleSpeed) -> Self {
SingleSpeed {
speed: mode.speed.into(),
}
}
}
#[derive(Clone, Default, Deserialize, Serialize)]
pub struct SingleColour {
pub colour: Colour,
}
impl From<cli_options::SingleColour> for SingleColour {
fn from(mode: cli_options::SingleColour) -> Self {
SingleColour {
colour: mode.colour.into(),
}
}
}
#[derive(Clone, Default, Deserialize, Serialize)]
pub struct MultiColour {
pub colour1: Colour,
pub colour2: Colour,
pub colour3: Colour,
pub colour4: Colour,
}
impl From<cli_options::MultiColour> for MultiColour {
fn from(mode: cli_options::MultiColour) -> Self {
MultiColour {
colour1: mode.colour1.into(),
colour2: mode.colour2.into(),
colour3: mode.colour3.into(),
colour4: mode.colour4.into(),
}
}
}
#[derive(Clone, Default, Deserialize, Serialize)]
pub struct SingleSpeedDirection {
pub direction: Direction,
pub speed: Speed,
}
impl From<cli_options::SingleSpeedDirection> for SingleSpeedDirection {
fn from(mode: cli_options::SingleSpeedDirection) -> Self {
SingleSpeedDirection {
direction: mode.direction.into(),
speed: mode.speed.into(),
}
}
}
#[derive(Clone, Default, Deserialize, Serialize)]
pub struct SingleColourSpeed {
pub colour: Colour,
pub speed: Speed,
}
impl From<cli_options::SingleColourSpeed> for SingleColourSpeed {
fn from(mode: cli_options::SingleColourSpeed) -> Self {
SingleColourSpeed {
colour: mode.colour.into(),
speed: mode.speed.into(),
}
}
}
#[derive(Clone, Deserialize, Serialize)]
pub enum AuraModes {
Stable(SingleColour),
Breathe(TwoColourSpeed),
Strobe(SingleSpeed),
Rainbow(SingleSpeedDirection),
Star(TwoColourSpeed),
Rain(SingleSpeed),
Highlight(SingleColourSpeed),
Laser(SingleColourSpeed),
Ripple(SingleColourSpeed),
Pulse(SingleColour),
Comet(SingleColour),
Flash(SingleColour),
MultiStatic(MultiColour),
LedBrightness(u8),
// TODO: use a serializable structure for this (KeyColourArray)
RGB(Vec<Vec<u8>>),
}
impl From<SetAuraBuiltin> for AuraModes {
fn from(mode: SetAuraBuiltin) -> Self {
match mode {
SetAuraBuiltin::Stable(x) => AuraModes::Stable(x.into()),
SetAuraBuiltin::Breathe(x) => AuraModes::Breathe(x.into()),
SetAuraBuiltin::Strobe(x) => AuraModes::Strobe(x.into()),
SetAuraBuiltin::Rainbow(x) => AuraModes::Rainbow(x.into()),
SetAuraBuiltin::Star(x) => AuraModes::Star(x.into()),
SetAuraBuiltin::Rain(x) => AuraModes::Rain(x.into()),
SetAuraBuiltin::Highlight(x) => AuraModes::Highlight(x.into()),
SetAuraBuiltin::Laser(x) => AuraModes::Laser(x.into()),
SetAuraBuiltin::Ripple(x) => AuraModes::Ripple(x.into()),
SetAuraBuiltin::Pulse(x) => AuraModes::Pulse(x.into()),
SetAuraBuiltin::Comet(x) => AuraModes::Comet(x.into()),
SetAuraBuiltin::Flash(x) => AuraModes::Flash(x.into()),
SetAuraBuiltin::MultiStatic(x) => AuraModes::MultiStatic(x.into()),
}
}
}
/// Very specific mode conversion required because numbering isn't linear
impl From<AuraModes> for u8 {
fn from(mode: AuraModes) -> Self {
u8::from(&mode)
}
}
/// Very specific mode conversion required because numbering isn't linear
impl From<&mut AuraModes> for u8 {
fn from(mode: &mut AuraModes) -> Self {
u8::from(&*mode)
}
}
/// Very specific mode conversion required because numbering isn't linear
impl From<&AuraModes> for u8 {
fn from(mode: &AuraModes) -> Self {
match mode {
AuraModes::Stable(_) => SINGLE,
AuraModes::Breathe(_) => BREATHING,
AuraModes::Strobe(_) => STROBE,
AuraModes::Rainbow(_) => RAINBOW,
AuraModes::Star(_) => STAR,
AuraModes::Rain(_) => RAIN,
AuraModes::Highlight(_) => HIGHLIGHT,
AuraModes::Laser(_) => LASER,
AuraModes::Ripple(_) => RIPPLE,
AuraModes::Pulse(_) => PULSE,
AuraModes::Comet(_) => COMET,
AuraModes::Flash(_) => FLASH,
AuraModes::MultiStatic(_) => MULTISTATIC,
AuraModes::RGB(_) => RGB,
_ => panic!("Invalid mode"),
}
}
}
impl From<&AuraModes> for &str {
fn from(mode: &AuraModes) -> Self {
match mode {
AuraModes::Stable(_) => "Static",
AuraModes::Breathe(_) => "Breathing",
AuraModes::Strobe(_) => "Strobing",
AuraModes::Rainbow(_) => "Rainbow",
AuraModes::Star(_) => "Stars",
AuraModes::Rain(_) => "Rain",
AuraModes::Highlight(_) => "Keypress Highlight",
AuraModes::Laser(_) => "Keypress Laser",
AuraModes::Ripple(_) => "Keypress Ripple",
AuraModes::Pulse(_) => "Pulse",
AuraModes::Comet(_) => "Comet",
AuraModes::Flash(_) => "Flash",
AuraModes::MultiStatic(_) => "4-Zone Static Colours",
AuraModes::RGB(_) => "RGB per-key",
_ => panic!("Invalid mode"),
}
}
}
/// Exists to convert back from correct bytes. RGB byte intentionally left off as it
/// does not correspond to an actual pre-set mode, nor does brightness.
impl From<u8> for AuraModes {
fn from(byte: u8) -> Self {
match byte {
SINGLE => AuraModes::Stable(SingleColour::default()),
BREATHING => AuraModes::Breathe(TwoColourSpeed::default()),
STROBE => AuraModes::Strobe(SingleSpeed::default()),
RAINBOW => AuraModes::Rainbow(SingleSpeedDirection::default()),
STAR => AuraModes::Star(TwoColourSpeed::default()),
RAIN => AuraModes::Rain(SingleSpeed::default()),
HIGHLIGHT => AuraModes::Highlight(SingleColourSpeed::default()),
LASER => AuraModes::Laser(SingleColourSpeed::default()),
RIPPLE => AuraModes::Ripple(SingleColourSpeed::default()),
PULSE => AuraModes::Pulse(SingleColour::default()),
COMET => AuraModes::Comet(SingleColour::default()),
FLASH => AuraModes::Flash(SingleColour::default()),
MULTISTATIC => AuraModes::MultiStatic(MultiColour::default()),
RGB => AuraModes::RGB(vec![]),
_ => panic!("Invalid mode byte"),
}
}
}

215
asus-nb/src/cli_options.rs Normal file
View File

@@ -0,0 +1,215 @@
use crate::error::AuraError;
use gumdrop::Options;
use serde_derive::{Deserialize, Serialize};
use std::str::FromStr;
#[derive(Options)]
pub struct LedBrightness {
level: u8,
}
impl LedBrightness {
pub fn level(&self) -> u8 {
self.level
}
}
impl FromStr for LedBrightness {
type Err = AuraError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
match s.as_str() {
"off" => Ok(LedBrightness { level: 0x00 }),
"low" => Ok(LedBrightness { level: 0x01 }),
"med" => Ok(LedBrightness { level: 0x02 }),
"high" => Ok(LedBrightness { level: 0x03 }),
_ => {
println!("Missing required argument, must be one of:\noff,low,med,high\n");
Err(AuraError::ParseBrightness)
}
}
}
}
#[derive(Deserialize, Serialize)]
pub struct Colour(pub u8, pub u8, pub u8);
impl Default for Colour {
fn default() -> Self {
Colour(255, 0, 0)
}
}
impl FromStr for Colour {
type Err = AuraError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() < 6 {
return Err(AuraError::ParseColour);
}
let r = u8::from_str_radix(&s[0..2], 16).or(Err(AuraError::ParseColour))?;
let g = u8::from_str_radix(&s[2..4], 16).or(Err(AuraError::ParseColour))?;
let b = u8::from_str_radix(&s[4..6], 16).or(Err(AuraError::ParseColour))?;
Ok(Colour(r, g, b))
}
}
#[derive(Deserialize, Serialize)]
pub enum Speed {
Low = 0xe1,
Med = 0xeb,
High = 0xf5,
}
impl Default for Speed {
fn default() -> Self {
Speed::Med
}
}
impl FromStr for Speed {
type Err = AuraError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
match s.as_str() {
"low" => Ok(Speed::Low),
"med" => Ok(Speed::Med),
"high" => Ok(Speed::High),
_ => Err(AuraError::ParseSpeed),
}
}
}
/// Used for Rainbow mode.
///
/// Enum corresponds to the required integer value
#[derive(Deserialize, Serialize)]
pub enum Direction {
Right,
Left,
Up,
Down,
}
impl Default for Direction {
fn default() -> Self {
Direction::Right
}
}
impl FromStr for Direction {
type Err = AuraError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
match s.as_str() {
"right" => Ok(Direction::Right),
"up" => Ok(Direction::Up),
"down" => Ok(Direction::Down),
"left" => Ok(Direction::Left),
_ => Err(AuraError::ParseDirection),
}
}
}
#[derive(Default, Options, Deserialize, Serialize)]
pub struct TwoColourSpeed {
#[options(help = "print help message")]
help: bool,
#[options(no_long, meta = "HEX", help = "set the first RGB value e.g, ff00ff")]
pub colour: Colour,
#[options(no_long, meta = "HEX", help = "set the second RGB value e.g, ff00ff")]
pub colour2: Colour,
#[options(no_long, help = "set the speed: low, med, high")]
pub speed: Speed,
}
#[derive(Default, Options, Deserialize, Serialize)]
pub struct SingleSpeed {
#[options(help = "print help message")]
help: bool,
#[options(no_long, meta = "WORD", help = "set the speed: low, med, high")]
pub speed: Speed,
}
#[derive(Default, Options, Deserialize, Serialize)]
pub struct SingleColour {
#[options(help = "print help message")]
help: bool,
#[options(no_long, meta = "HEX", help = "set the RGB value e.g, ff00ff")]
pub colour: Colour,
}
#[derive(Default, Options, Deserialize, Serialize)]
pub struct MultiColour {
#[options(help = "print help message")]
help: bool,
#[options(meta = "HEX", help = "set the RGB value e.g, ff00ff")]
pub colour1: Colour,
#[options(meta = "HEX", help = "set the RGB value e.g, ff00ff")]
pub colour2: Colour,
#[options(meta = "HEX", help = "set the RGB value e.g, ff00ff")]
pub colour3: Colour,
#[options(meta = "HEX", help = "set the RGB value e.g, ff00ff")]
pub colour4: Colour,
}
#[derive(Default, Options, Deserialize, Serialize)]
pub struct SingleSpeedDirection {
#[options(help = "print help message")]
help: bool,
#[options(
no_long,
meta = "DIR",
help = "set the direction: up, down, left, right"
)]
pub direction: Direction,
#[options(no_long, help = "set the speed: low, med, high")]
pub speed: Speed,
}
#[derive(Default, Options, Deserialize, Serialize)]
pub struct SingleColourSpeed {
#[options(help = "print help message")]
help: bool,
#[options(no_long, meta = "HEX", help = "set the RGB value e.g, ff00ff")]
pub colour: Colour,
#[options(no_long, help = "set the speed: low, med, high")]
pub speed: Speed,
}
/// Byte value for setting the built-in mode.
///
/// Enum corresponds to the required integer value
#[derive(Options, Deserialize, Serialize)]
pub enum SetAuraBuiltin {
#[options(help = "set a single static colour")]
Stable(SingleColour),
#[options(help = "pulse between one or two colours")]
Breathe(TwoColourSpeed),
#[options(help = "strobe through all colours")]
Strobe(SingleSpeed),
#[options(help = "rainbow cycling in one of four directions")]
Rainbow(SingleSpeedDirection),
#[options(help = "rain pattern mimicking raindrops")]
Star(TwoColourSpeed),
#[options(help = "rain pattern of three preset colours")]
Rain(SingleSpeed),
#[options(help = "pressed keys are highlighted to fade")]
Highlight(SingleColourSpeed),
#[options(help = "pressed keys generate horizontal laser")]
Laser(SingleColourSpeed),
#[options(help = "pressed keys ripple outwards like a splash")]
Ripple(SingleColourSpeed),
#[options(help = "set a rapid pulse")]
Pulse(SingleColour),
#[options(help = "set a vertical line zooming from left")]
Comet(SingleColour),
#[options(help = "set a wide vertical line zooming from left")]
Flash(SingleColour),
#[options(help = "4-zone multi-colour")]
MultiStatic(MultiColour),
}
impl Default for SetAuraBuiltin {
fn default() -> Self {
SetAuraBuiltin::Stable(SingleColour {
help: false,
colour: Colour(255, 0, 0),
})
}
}

124
asus-nb/src/core_dbus.rs Normal file
View File

@@ -0,0 +1,124 @@
use super::*;
use crate::fancy::KeyColourArray;
use dbus::channel::Sender;
use dbus::{blocking::Connection, channel::Token, Message};
use std::error::Error;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use std::{thread, time::Duration};
/// Simplified way to write a effect block
pub struct AuraDbusWriter {
connection: Box<Connection>,
block_time: u64,
stop: Arc<AtomicBool>,
stop_token: Token,
}
impl AuraDbusWriter {
#[inline]
pub fn new() -> Result<Self, Box<dyn Error>> {
let connection = Connection::new_system()?;
let stop = Arc::new(AtomicBool::new(false));
let stopper2 = stop.clone();
let match_rule = dbus::message::MatchRule::new_signal(DBUS_IFACE, "KeyBacklightChanged");
let stop_token = connection.add_match(match_rule, move |_: (), _, msg| {
if msg.read1::<&str>().is_ok() {
stopper2.store(true, Ordering::Relaxed);
}
true
})?;
Ok(AuraDbusWriter {
connection: Box::new(connection),
block_time: 33333,
stop,
stop_token,
})
}
/// 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 std::error::Error>> {
let mode = AuraModes::RGB(vec![vec![]]);
let mut msg =
Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "SetKeyBacklight")?
.append1(serde_json::to_string(&mode)?);
msg.set_no_reply(true);
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(
&mut self,
key_colour_array: &KeyColourArray,
) -> Result<(), Box<dyn Error>> {
let group = key_colour_array.get();
let mut vecs = Vec::with_capacity(group.len());
for v in group {
vecs.push(v.to_vec());
}
let mode = AuraModes::RGB(vecs);
let mut msg =
Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "SetKeyBacklight")?
.append1(serde_json::to_string(&mode)?);
msg.set_no_reply(true);
self.connection.send(msg).unwrap();
thread::sleep(Duration::from_micros(self.block_time));
self.connection.process(Duration::from_micros(500))?;
if self.stop.load(Ordering::Relaxed) {
self.connection.remove_match(self.stop_token)?;
println!("Keyboard backlight was changed, exiting");
std::process::exit(1)
}
Ok(())
}
#[inline]
pub fn write_keyboard_leds(&self, mode: &AuraModes) -> Result<(), Box<dyn std::error::Error>> {
let mut msg =
Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "SetKeyBacklight")?
.append1(serde_json::to_string(mode)?);
msg.set_no_reply(true);
self.connection.send(msg).unwrap();
Ok(())
}
#[inline]
pub fn write_fan_mode(&self, level: u8) -> Result<(), Box<dyn std::error::Error>> {
let mut msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "SetFanMode")?
.append1(level);
msg.set_no_reply(true);
self.connection.send(msg).unwrap();
Ok(())
}
#[inline]
pub fn write_charge_limit(&self, level: u8) -> Result<(), Box<dyn std::error::Error>> {
let mut msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "SetChargeLimit")?
.append1(level);
msg.set_no_reply(true);
self.connection.send(msg).unwrap();
Ok(())
}
#[inline]
pub fn write_builtin_mode(&self, mode: &AuraModes) -> Result<(), Box<dyn std::error::Error>> {
self.write_keyboard_leds(mode)
}
#[inline]
pub fn write_brightness(&self, level: u8) -> Result<String, Box<dyn std::error::Error>> {
self.write_keyboard_leds(&AuraModes::LedBrightness(level))?;
Ok(String::new())
}
}

20
asus-nb/src/error.rs Normal file
View File

@@ -0,0 +1,20 @@
use std::fmt;
pub enum AuraError {
ParseColour,
ParseSpeed,
ParseDirection,
ParseBrightness,
}
impl fmt::Display for AuraError {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AuraError::ParseColour => write!(f, "Could not parse colour"),
AuraError::ParseSpeed => write!(f, "Could not parse speed"),
AuraError::ParseDirection => write!(f, "Could not parse direction"),
AuraError::ParseBrightness => write!(f, "Could not parse brightness"),
}
}
}

455
asus-nb/src/fancy.rs Normal file
View File

@@ -0,0 +1,455 @@
/// A `KeyColourArray` contains all data to change the full set of keyboard
/// key colours individually.
///
/// Each row of the internal array is a full HID packet that can be sent
/// to the keyboard EC. One row controls one group of keys, these keys are not
/// necessarily all on the same row of the keyboard, with some splitting between
/// two rows.
#[derive(Clone)]
pub struct KeyColourArray([[u8; 64]; 11]);
impl Default for KeyColourArray {
fn default() -> Self {
Self::new()
}
}
impl KeyColourArray {
pub fn new() -> Self {
let mut set = [[0u8; 64]; 11];
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
if count == 10 {
row[7] = 0x08; // 0b00001000
} else {
row[7] = 0x10; // 0b00010000 addressing? flips for group a0
}
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) {
if let Some((rr, gg, bb)) = self.key(key) {
*rr = r;
*gg = g;
*bb = b;
}
}
/// Indexes in to `KeyColourArray` at the correct row and column
/// to set a series of three bytes to the chosen R,G,B values
pub fn key(&mut self, key: Key) -> Option<(&mut u8, &mut u8, &mut u8)> {
// Tuples are indexes in to array
let (row, col) = match key {
Key::VolDown => (0, 15),
Key::VolUp => (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),
Key::None => return None,
};
// LOLOLOLOLOLOLOL! Look it's safe okay
unsafe {
Some((
&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]; 11] {
&self.0
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
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,
None,
}
pub trait KeyLayout {
fn get_rows(&self) -> &Vec<[Key; 17]>;
}
pub struct GX502Layout(Vec<[Key; 17]>);
impl KeyLayout for GX502Layout {
fn get_rows(&self) -> &Vec<[Key; 17]> {
&self.0
}
}
impl Default for GX502Layout {
fn default() -> Self {
GX502Layout(vec![
[
Key::None,
Key::None,
Key::VolDown,
Key::VolUp,
Key::MicMute,
Key::ROG,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
],
[
Key::Esc,
Key::None,
Key::F1,
Key::F2,
Key::F3,
Key::F4,
Key::None, // not sure which key to put here
Key::F5,
Key::F6,
Key::F7,
Key::F8,
Key::F9,
Key::F9,
Key::F10,
Key::F11,
Key::F12,
Key::Del,
],
[
Key::Tilde,
Key::N1,
Key::N2,
Key::N3,
Key::N4,
Key::N5,
Key::N6,
Key::N7,
Key::N8,
Key::N9,
Key::N0,
Key::Hyphen,
Key::Equals,
Key::BkSpc1,
Key::BkSpc2,
Key::BkSpc3,
Key::Home,
],
[
Key::Tab,
Key::Q,
Key::W,
Key::E,
Key::R,
Key::T,
Key::Y,
Key::U,
Key::I,
Key::O,
Key::P,
Key::LBracket,
Key::RBracket,
Key::BackSlash,
Key::BackSlash,
Key::BackSlash,
Key::PgUp,
],
[
Key::Caps,
Key::A,
Key::S,
Key::D,
Key::F,
Key::G,
Key::H,
Key::J,
Key::K,
Key::L,
Key::SemiColon,
Key::Quote,
Key::Quote,
Key::Ret1,
Key::Ret2,
Key::Ret3,
Key::PgDn,
],
[
Key::LShift,
Key::LShift,
Key::Z,
Key::X,
Key::C,
Key::V,
Key::B,
Key::N,
Key::M,
Key::Comma,
Key::Period,
Key::FwdSlash,
Key::FwdSlash,
Key::Rshift1,
Key::Rshift2,
Key::Rshift3,
Key::End,
],
[
Key::LCtrl,
Key::LFn,
Key::Meta,
Key::LAlt,
Key::Space1,
Key::Space2,
Key::Space3,
Key::Space4,
Key::Space4,
Key::RAlt,
Key::PrtSc,
Key::RCtrl,
Key::RCtrl,
Key::Left,
Key::Up,
Key::Right,
Key::RFn,
],
[
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::Left,
Key::Down,
Key::Right,
Key::None,
],
])
}
}

252
asus-nb/src/lib.rs Normal file
View File

@@ -0,0 +1,252 @@
pub static DBUS_NAME: &str = "org.asus-linux.Daemon";
pub static DBUS_PATH: &str = "/org/asus-linux/Daemon";
pub static DBUS_IFACE: &str = "org.asus-linux.Daemon";
pub const LED_MSG_LEN: usize = 17;
pub mod aura_modes;
use aura_modes::AuraModes;
/// Contains mostly only what is required for parsing CLI options
pub mod cli_options;
/// Enables you to create fancy RGB effects
pub mod fancy;
/// The main dbus group for system controls, e.g, fan control, keyboard LED's
pub mod core_dbus;
/// Specific dbus for writing to the AniMe Matrix display (if supported)
pub mod anime_dbus;
/// Helper functions for the AniMe display
pub mod anime_matrix;
pub mod error;
// static LED_INIT1: [u8; 2] = [0x5d, 0xb9];
// static LED_INIT2: &str = "]ASUS Tech.Inc."; // ] == 0x5d
// static LED_INIT3: [u8; 6] = [0x5d, 0x05, 0x20, 0x31, 0, 0x08];
// static LED_INIT4: &str = "^ASUS Tech.Inc."; // ^ == 0x5e
// static LED_INIT5: [u8; 6] = [0x5e, 0x05, 0x20, 0x31, 0, 0x08];
/// Writes aout the correct byte string for brightness
///
/// The HID descriptor looks like:
///
/// ```ignore
/// 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 `AuraCommands` in to packet data
///
/// Byte structure:
///
/// ```ignore
/// | 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 = strobe (through all colours)
/// - 03 = rainbow
/// - 04 = star (byte 9 sets rain colour)
/// - 05 = rain 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:
/// ```ignore
/// 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<&AuraModes> for [u8; LED_MSG_LEN] {
fn from(mode: &AuraModes) -> Self {
let mut msg = [0u8; LED_MSG_LEN];
msg[0] = 0x5d;
msg[1] = 0xb3;
match mode {
AuraModes::LedBrightness(n) => return aura_brightness_bytes(*n),
AuraModes::Stable(_) => msg[3] = 0x00,
AuraModes::Breathe(_) => msg[3] = 0x01,
AuraModes::Strobe(_) => msg[3] = 0x02,
AuraModes::Rainbow(_) => msg[3] = 0x03,
AuraModes::Star(_) => msg[3] = 0x04,
AuraModes::Rain(_) => msg[3] = 0x05,
AuraModes::Highlight(_) => msg[3] = 0x06,
AuraModes::Laser(_) => msg[3] = 0x07,
AuraModes::Ripple(_) => msg[3] = 0x08,
AuraModes::Pulse(_) => msg[3] = 0x0a,
AuraModes::Comet(_) => msg[3] = 0x0b,
AuraModes::Flash(_) => msg[3] = 0x0c,
_ => panic!("Mode not convertable to 1D array: {}", <&str>::from(mode)),
}
match mode {
AuraModes::Rainbow(settings) => {
msg[7] = settings.speed as u8;
msg[8] = settings.direction as u8;
}
AuraModes::Star(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;
}
AuraModes::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;
}
AuraModes::Strobe(settings) | AuraModes::Rain(settings) => {
msg[7] = settings.speed as u8;
}
AuraModes::Highlight(settings)
| AuraModes::Laser(settings)
| AuraModes::Ripple(settings) => {
msg[4] = settings.colour.0;
msg[5] = settings.colour.1;
msg[6] = settings.colour.2;
msg[7] = settings.speed as u8;
}
AuraModes::Stable(settings)
| AuraModes::Pulse(settings)
| AuraModes::Comet(settings)
| AuraModes::Flash(settings) => {
msg[4] = settings.colour.0;
msg[5] = settings.colour.1;
msg[6] = settings.colour.2;
}
_ => panic!("Mode not convertable to 1D array: {}", <&str>::from(mode)),
}
msg
}
}
impl From<AuraModes> for [u8; LED_MSG_LEN] {
#[inline]
fn from(mode: AuraModes) -> Self {
<[u8; LED_MSG_LEN]>::from(&mode)
}
}
impl From<AuraModes> for [[u8; LED_MSG_LEN]; 4] {
#[inline]
fn from(mode: AuraModes) -> Self {
<[[u8; LED_MSG_LEN]; 4]>::from(&mode)
}
}
impl From<&AuraModes> for [[u8; LED_MSG_LEN]; 4] {
#[inline]
fn from(mode: &AuraModes) -> Self {
let mut msg = [[0u8; LED_MSG_LEN]; 4];
for (i, row) in msg.iter_mut().enumerate() {
row[0] = 0x5d;
row[1] = 0xb3;
row[2] = i as u8 + 1;
}
match mode {
AuraModes::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 2D array: {}", <&str>::from(mode)),
}
msg
}
}