Final round of fixes for new version

This commit is contained in:
Luke
2020-06-09 20:46:14 +12:00
parent cb8032c7c2
commit 964c24f8fa
68 changed files with 220 additions and 176 deletions

21
rog-client/Cargo.toml Normal file
View File

@@ -0,0 +1,21 @@
[package]
name = "rog-client"
version = "0.11.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"
yansi-term = "0.1.2"
[dev-dependencies]
tinybmp = "0.2.3"

View File

@@ -0,0 +1,42 @@
use rog_client::{AniMeDbusWriter, AniMeMatrix, AniMePacketType, HEIGHT, WIDTH};
use tinybmp::{Bmp, Pixel};
fn main() {
let mut writer = AniMeDbusWriter::new().unwrap();
let bmp =
Bmp::from_slice(include_bytes!("non-skewed_r.bmp")).expect("Failed to parse BMP image");
let pixels: Vec<Pixel> = bmp.into_iter().collect();
//assert_eq!(pixels.len(), 56 * 56);
// Try an outline, top and right
let mut matrix = AniMeMatrix::new();
// Aligned left
for px in pixels {
if (px.x as usize / 2) < WIDTH && (px.y as usize) < HEIGHT {
if px.x % 2 == 0 {
matrix.get_mut()[px.y as usize][px.x as usize / 2] = px.color as u8;
}
}
}
// Throw an alignment border up
// {
// let tmp = matrix.get_mut();
// for x in tmp[0].iter_mut() {
// *x = 0xff;
// }
// for row in tmp.iter_mut() {
// row[row.len() - 1] = 0xff;
// }
// }
matrix.debug_print();
let mut matrix: AniMePacketType = AniMePacketType::from(matrix);
// println!("{:?}", matrix[0].to_vec());
// println!("{:?}", matrix[1].to_vec());
writer.write_image(&mut matrix).unwrap();
}

View File

@@ -0,0 +1,94 @@
use rog_client::{AuraDbusWriter, GX502Layout, Key, KeyColourArray, KeyLayout};
use std::collections::LinkedList;
#[derive(Debug, Clone)]
struct Ball {
position: (i32, i32),
direction: (i32, i32),
trail: LinkedList<(i32, i32)>,
}
impl Ball {
fn new(x: i32, y: i32, trail_len: u32) -> Self {
let mut trail = LinkedList::new();
for _ in 1..=trail_len {
trail.push_back((x, y));
}
Ball {
position: (x, y),
direction: (1, 1),
trail,
}
}
fn update(&mut self, key_map: &Vec<[Key; 17]>) {
let pos = self.position;
let dir = self.direction;
if pos.0 + dir.0 > key_map[pos.1 as usize].len() as i32 - 1 || pos.0 + dir.0 < 0 {
self.direction.0 *= -1;
} else if key_map[(pos.1) as usize][(pos.0 + dir.0) as usize] == Key::None {
self.direction.0 *= -1;
}
if pos.1 + dir.1 > key_map.len() as i32 - 1 || pos.1 + dir.1 < 0 {
self.direction.1 *= -1;
} else if key_map[(pos.1 + dir.1) as usize][(pos.0) as usize] == Key::None {
self.direction.1 *= -1;
}
self.trail.pop_front();
self.trail.push_back(self.position);
self.position.0 += self.direction.0;
self.position.1 += self.direction.1;
if self.position.0 > key_map[self.position.1 as usize].len() as i32 {
self.position.0 = key_map[self.position.1 as usize].len() as i32 - 1;
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut writer = AuraDbusWriter::new()?;
let mut colours = KeyColourArray::new();
let layout = GX502Layout::default();
let mut balls = [Ball::new(2, 1, 12), Ball::new(4, 6, 12)];
writer.init_effect()?;
let rows = layout.get_rows();
loop {
for (n, ball) in balls.iter_mut().enumerate() {
ball.update(rows);
for (i, pos) in ball.trail.iter().enumerate() {
if let Some(c) = colours.key(rows[pos.1 as usize][pos.0 as usize]) {
*c.0 = 0;
*c.1 = 0;
*c.2 = 0;
if n == 0 {
*c.0 = i as u8 * (255 / ball.trail.len() as u8);
} else if n == 1 {
*c.1 = i as u8 * (255 / ball.trail.len() as u8);
} else if n == 2 {
*c.2 = i as u8 * (255 / ball.trail.len() as u8);
}
};
}
if let Some(c) = colours.key(rows[ball.position.1 as usize][ball.position.0 as usize]) {
*c.0 = 255;
*c.1 = 255;
*c.2 = 255;
};
}
writer.write_colour_block(&colours)?;
// can change 100 times per second, so need to slow it down
std::thread::sleep(std::time::Duration::from_millis(60));
}
}

View File

@@ -0,0 +1,28 @@
use rog_client::{AuraDbusWriter, GX502Layout, KeyColourArray, KeyLayout};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut writer = AuraDbusWriter::new()?;
let layout = GX502Layout::default();
writer.init_effect()?;
let rows = layout.get_rows();
let mut column = 0;
loop {
let mut key_colours = KeyColourArray::new();
for row in rows {
if let Some(c) = key_colours.key(row[column as usize]) {
*c.0 = 255;
};
}
if column == rows[0].len() - 1 {
column = 0
} else {
column += 1;
}
writer.write_colour_block(&key_colours)?;
std::thread::sleep(std::time::Duration::from_millis(250));
}
}

View File

@@ -0,0 +1,53 @@
use rog_client::{AuraDbusWriter, GX502Layout, Key, KeyColourArray, KeyLayout};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut writer = AuraDbusWriter::new()?;
let mut key_colours = KeyColourArray::new();
let layout = GX502Layout::default();
writer.init_effect()?;
let rows = layout.get_rows();
loop {
for (r, row) in rows.iter().enumerate() {
for (k, key) in row.iter().enumerate() {
if let Some(c) = key_colours.key(*key) {
*c.0 = 255;
};
// Last key of previous row
if k == 0 {
if r == 0 {
let k = &rows[rows.len() - 1][rows[rows.len() - 1].len() - 1];
if let Some(c) = key_colours.key(*k) {
*c.0 = 0;
};
} else {
let k = &rows[r - 1][rows[r - 1].len() - 1];
if let Some(c) = key_colours.key(*k) {
*c.0 = 0;
};
}
} else {
let k = &rows[r][k - 1];
if let Some(c) = key_colours.key(*k) {
*c.0 = 0;
};
}
if let Some(c) = key_colours.key(Key::Up) {
*c.0 = 255;
};
*key_colours.key(Key::Left).unwrap().0 = 255;
*key_colours.key(Key::Right).unwrap().0 = 255;
*key_colours.key(Key::Down).unwrap().0 = 255;
*key_colours.key(Key::W).unwrap().0 = 255;
*key_colours.key(Key::A).unwrap().0 = 255;
*key_colours.key(Key::S).unwrap().0 = 255;
*key_colours.key(Key::D).unwrap().0 = 255;
writer.write_colour_block(&key_colours)?;
std::thread::sleep(std::time::Duration::from_millis(100));
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,30 @@
use rog_client::{AuraDbusWriter, Key, KeyColourArray};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut writer = AuraDbusWriter::new()?;
let mut key_colours = KeyColourArray::new();
writer.init_effect()?;
loop {
let count = 49;
for _ in 0..count {
*key_colours.key(Key::ROG).unwrap().0 += 5;
*key_colours.key(Key::L).unwrap().0 += 5;
*key_colours.key(Key::I).unwrap().0 += 5;
*key_colours.key(Key::N).unwrap().0 += 5;
*key_colours.key(Key::U).unwrap().0 += 5;
*key_colours.key(Key::X).unwrap().0 += 5;
writer.write_colour_block(&key_colours)?;
}
for _ in 0..count {
*key_colours.key(Key::ROG).unwrap().0 -= 5;
*key_colours.key(Key::L).unwrap().0 -= 5;
*key_colours.key(Key::I).unwrap().0 -= 5;
*key_colours.key(Key::N).unwrap().0 -= 5;
*key_colours.key(Key::U).unwrap().0 -= 5;
*key_colours.key(Key::X).unwrap().0 -= 5;
writer.write_colour_block(&key_colours)?;
}
}
}

View File

@@ -0,0 +1,37 @@
use rog_client::{AuraDbusWriter, GX502Layout, KeyColourArray, KeyLayout};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut writer = AuraDbusWriter::new()?;
let mut key_colours = KeyColourArray::new();
let layout = GX502Layout::default();
writer.init_effect()?;
let rows = layout.get_rows();
let mut fade = 50;
let mut flip = false;
loop {
for row in rows {
for (k, key) in row.iter().enumerate() {
if let Some(c) = key_colours.key(*key) {
*c.0 = 255 / fade / (k + 1) as u8;
};
}
}
writer.write_colour_block(&key_colours)?;
if flip {
if fade > 1 {
fade -= 1;
} else {
flip = !flip;
}
} else if fade < 17 {
fade += 1;
} else {
flip = !flip;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -0,0 +1,112 @@
use super::*;
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.
///
/// Amory crate assumes first row is 33 pixels/bytes, this means the actual structure
/// likely follows this format with every second line offset physically by half.
/// Even rows (mod 1) are aligned right, odd are offset left by half (`..=` is inclusive)
///
/// - 0..=32, row 0, 33 pixels // len starts at 37 if using formula
/// - 33..=66, row 1, 33 pixels
/// - 68..=101, row 2, 33 pixels
/// - 101..=134, row 3, 33 pixels
/// - 135..=168, row 4, 33 pixels
/// - 169..=202, row 5, 33 pixels // Should be last offset line?
/// - 203..=236, row 6, 33 pixels
/// - 237..=268, row 7, 31 pixels -2 px from last
/// - 269..=301, row 8, 32 pixels +1 px from last
/// - 302..=332, row 9, 30 pixels -2
/// - 333..=364, row 10, 31 pixels +1
/// - 365..=394, row 11, 29 pixels -2
/// - 395..=425, row 12, 30 pixels +1
/// - 426..=454, row 13, 28 pixels -2
/// - 455..=484, row 14, 29 pixels +1
/// - 485..=512, row 15, 27 pixels -2
/// - 513..=541, row 16, 28 pixels +1
/// - 542..=568, row 17, 26 pixels -2
/// - 569..=596, row 18, 27 pixels +1
/// - 597..=622, row 19, 25 pixels -2
/// - BEGIN NEXT BLOCK AT IDX627 (when writing out the one dimensional array)
/// - 623..=649, row 20, 26 pixels +1
/// - .. 57 rows (from 0)
///
/// Image is 33x56, and
///
/// The formula below starts at row 7
/// ```
/// if current_row_idx != 0 && current_row_idx.mod(1) == 0
/// then current_row_len = last_row_len + 1
/// else current_row_len = last_row_len - 2, // offset left by half
/// ```
///
/// 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(())
}
}

View File

@@ -0,0 +1,230 @@
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;
pub struct AniMeMatrix(AniMeBufferType);
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 if count == 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)));
}
print!("\n");
} 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!(" ");
}
}
print!("\n");
}
}
}
}
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 index..row.len() {
// 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;
}
//println!("{:?}", write_block.to_vec());
write_block[write_index] = row[n];
write_index += 1;
}
}
}
buffers
}
}
#[cfg(test)]
mod tests {
use crate::{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()
);
}
}

144
rog-client/src/aura_dbus.rs Normal file
View File

@@ -0,0 +1,144 @@
use super::*;
use dbus::blocking::BlockingSender;
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};
/// Simplified way to write a effect block
pub struct AuraDbusWriter {
connection: Box<Connection>,
block_time: u64,
stop: Arc<AtomicBool>,
}
impl AuraDbusWriter {
#[inline]
pub fn new() -> Result<Self, Box<dyn Error>> {
let connection = Connection::new_system()?;
Ok(AuraDbusWriter {
connection: Box::new(connection),
block_time: 33333,
stop: Arc::new(AtomicBool::new(false)),
})
}
/// 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<String, Box<dyn std::error::Error>> {
let match_rule = dbus::message::MatchRule::new_signal(DBUS_IFACE, "LedCancelEffect");
let stopper = self.stop.clone();
self.connection
.add_match(match_rule, move |_: (), _, msg| {
println!("GOT {:?}", msg);
if let Ok(stop) = msg.read1::<bool>() {
if stop {
stopper.store(true, Ordering::Relaxed);
}
}
true
})?;
let msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "LedWriteBytes")?
.append1(KeyColourArray::get_init_msg());
let r = self
.connection
.send_with_reply_and_block(msg, Duration::from_millis(5000))?;
if let Some(reply) = r.get1::<&str>() {
return Ok(reply.to_owned());
}
Err(Box::new(dbus::Error::new_custom("name", "message")))
}
/// 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>> {
// self.connection.process(Duration::from_micros(300))?;
let group = key_colour_array.get();
let mut msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "LedWriteEffect")?
.append3(&group[0].to_vec(), &group[1].to_vec(), &group[2].to_vec())
.append3(&group[3].to_vec(), &group[4].to_vec(), &group[5].to_vec())
.append3(&group[6].to_vec(), &group[7].to_vec(), &group[8].to_vec())
.append2(&group[9].to_vec(), &group[10].to_vec());
msg.set_no_reply(true);
self.connection.send(msg).unwrap();
thread::sleep(Duration::from_micros(self.block_time));
if self.stop.load(Ordering::Relaxed) {
panic!("Go signal to stop!");
}
Ok(())
}
#[inline]
pub fn write_multizone(
&mut self,
group: &[[u8; LED_MSG_LEN]; 4],
) -> Result<String, Box<dyn std::error::Error>> {
self.connection.process(Duration::from_micros(300))?;
let msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "LedWriteMultizone")?
.append1(&group[0].to_vec())
.append1(&group[1].to_vec())
.append1(&group[2].to_vec())
.append1(&group[3].to_vec());
let r = self
.connection
.send_with_reply_and_block(msg, Duration::from_millis(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_bytes(&self, bytes: &[u8]) -> Result<String, Box<dyn std::error::Error>> {
let msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "LedWriteBytes")?
.append1(bytes.to_vec());
let r = self
.connection
.send_with_reply_and_block(msg, Duration::from_millis(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_fan_mode(&self, level: u8) -> Result<String, Box<dyn std::error::Error>> {
let msg =
Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "FanMode")?.append1(level);
let r = self
.connection
.send_with_reply_and_block(msg, Duration::from_millis(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))
}
}

164
rog-client/src/builtins.rs Normal file
View File

@@ -0,0 +1,164 @@
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 strobe: [u8; LED_MSG_LEN],
pub rainbow: [u8; LED_MSG_LEN],
pub star: [u8; LED_MSG_LEN],
pub rain: [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 comet: [u8; LED_MSG_LEN],
pub flash: [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::Strobe => self.strobe.copy_from_slice(bytes),
BuiltInModeByte::Rainbow => self.rainbow.copy_from_slice(bytes),
BuiltInModeByte::Star => self.star.copy_from_slice(bytes),
BuiltInModeByte::Rain => self.rain.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::Comet => self.comet.copy_from_slice(bytes),
BuiltInModeByte::Flash => self.flash.copy_from_slice(bytes),
_ => {}
}
}
}
#[inline]
pub fn get_field_from(&self, byte: u8) -> Option<&[u8]> {
let bytes = match BuiltInModeByte::from(byte) {
BuiltInModeByte::Single => &self.stable,
BuiltInModeByte::Breathing => &self.breathe,
BuiltInModeByte::Strobe => &self.strobe,
BuiltInModeByte::Rainbow => &self.rainbow,
BuiltInModeByte::Star => &self.star,
BuiltInModeByte::Rain => &self.rain,
BuiltInModeByte::Highlight => &self.highlight,
BuiltInModeByte::Laser => &self.laser,
BuiltInModeByte::Ripple => &self.ripple,
BuiltInModeByte::Pulse => &self.pulse,
BuiltInModeByte::Comet => &self.comet,
BuiltInModeByte::Flash => &self.flash,
_ => return None,
};
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())),
strobe: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Strobe(SingleSpeed::default())),
rainbow: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Rainbow(
SingleSpeedDirection::default(),
)),
star: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Star(TwoColourSpeed::default())),
rain: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Rain(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())),
comet: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Comet(SingleColour::default())),
flash: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Flash(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,
Strobe = 0x02,
Rainbow = 0x03,
Star = 0x04,
Rain = 0x05,
Highlight = 0x06,
Laser = 0x07,
Ripple = 0x08,
Pulse = 0x0a,
Comet = 0x0b,
Flash = 0x0c,
MultiStatic,
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::Strobe,
0x03 => Self::Rainbow,
0x04 => Self::Star,
0x05 => Self::Rain,
0x06 => Self::Highlight,
0x07 => Self::Laser,
0x08 => Self::Ripple,
0x0a => Self::Pulse,
0x0b => Self::Comet,
0x0c => Self::Flash,
_ => 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::Strobe => 0x02,
BuiltInModeByte::Rainbow => 0x03,
BuiltInModeByte::Star => 0x04,
BuiltInModeByte::Rain => 0x05,
BuiltInModeByte::Highlight => 0x06,
BuiltInModeByte::Laser => 0x07,
BuiltInModeByte::Ripple => 0x08,
BuiltInModeByte::Pulse => 0x0a,
BuiltInModeByte::Comet => 0x0b,
BuiltInModeByte::Flash => 0x0c,
BuiltInModeByte::MultiStatic => 0x00,
BuiltInModeByte::None => 0xff,
}
}
}

View File

@@ -0,0 +1,215 @@
use crate::error::AuraError;
use gumdrop::Options;
use std::fmt::Debug;
use std::str::FromStr;
#[derive(Debug, 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(Debug)]
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(Debug, Copy, Clone)]
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(Debug, Copy, Clone)]
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(Debug, Default, Options)]
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(Debug, Default, Options)]
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(Debug, Default, Options)]
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(Debug, Default, Options)]
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(Debug, Default, Options)]
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(Debug, Default, Options)]
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(Debug, Options)]
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),
})
}
}

18
rog-client/src/error.rs Normal file
View File

@@ -0,0 +1,18 @@
use std::fmt::Debug;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AuraError {
#[error("unable to parse string to colour")]
ParseColour,
#[error("unable to parse string to speed")]
ParseSpeed,
#[error("unable to parse string to direction")]
ParseDirection,
#[error("unable to parse string to brightness")]
ParseBrightness,
#[error("could not poll the keyboard for input")]
PollKeyboard,
#[error("mode not supported")]
NotSupported,
}

455
rog-client/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,
],
])
}
}

239
rog-client/src/lib.rs Normal file
View File

@@ -0,0 +1,239 @@
pub static DBUS_NAME: &str = "org.rogcore.Daemon";
pub static DBUS_PATH: &str = "/org/rogcore/Daemon";
pub static DBUS_IFACE: &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::*;
mod animatrix_dbus;
pub use animatrix_dbus::*;
mod anime_matrix;
pub use anime_matrix::*;
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 = 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:
/// ```
/// 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::Strobe(_) => msg[3] = 0x02,
SetAuraBuiltin::Rainbow(_) => msg[3] = 0x03,
SetAuraBuiltin::Star(_) => msg[3] = 0x04,
SetAuraBuiltin::Rain(_) => msg[3] = 0x05,
SetAuraBuiltin::Highlight(_) => msg[3] = 0x06,
SetAuraBuiltin::Laser(_) => msg[3] = 0x07,
SetAuraBuiltin::Ripple(_) => msg[3] = 0x08,
SetAuraBuiltin::Pulse(_) => msg[3] = 0x0a,
SetAuraBuiltin::Comet(_) => msg[3] = 0x0b,
SetAuraBuiltin::Flash(_) => 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::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;
}
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::Strobe(settings) | SetAuraBuiltin::Rain(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::Comet(settings)
| SetAuraBuiltin::Flash(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
}
}