mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
Final round of fixes for new version
This commit is contained in:
21
rog-client/Cargo.toml
Normal file
21
rog-client/Cargo.toml
Normal 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"
|
||||
42
rog-client/examples/animatrix.rs
Normal file
42
rog-client/examples/animatrix.rs
Normal 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();
|
||||
}
|
||||
94
rog-client/examples/ball.rs
Normal file
94
rog-client/examples/ball.rs
Normal 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));
|
||||
}
|
||||
}
|
||||
28
rog-client/examples/comet.rs
Normal file
28
rog-client/examples/comet.rs
Normal 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));
|
||||
}
|
||||
}
|
||||
53
rog-client/examples/iterate-keys.rs
Normal file
53
rog-client/examples/iterate-keys.rs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
rog-client/examples/non-skewed.bmp
Normal file
BIN
rog-client/examples/non-skewed.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
rog-client/examples/non-skewed_r.bmp
Normal file
BIN
rog-client/examples/non-skewed_r.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
30
rog-client/examples/per-key-effect-2.rs
Normal file
30
rog-client/examples/per-key-effect-2.rs
Normal 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)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
rog-client/examples/pulser.rs
Normal file
37
rog-client/examples/pulser.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
rog-client/examples/rust.bmp
Normal file
BIN
rog-client/examples/rust.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.6 KiB |
BIN
rog-client/examples/test-skinny-45deg.bmp
Normal file
BIN
rog-client/examples/test-skinny-45deg.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
BIN
rog-client/examples/test.bmp
Normal file
BIN
rog-client/examples/test.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
BIN
rog-client/examples/test2.bmp
Normal file
BIN
rog-client/examples/test2.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.6 KiB |
112
rog-client/src/animatrix_dbus.rs
Normal file
112
rog-client/src/animatrix_dbus.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
230
rog-client/src/anime_matrix.rs
Normal file
230
rog-client/src/anime_matrix.rs
Normal 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
144
rog-client/src/aura_dbus.rs
Normal 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
164
rog-client/src/builtins.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
215
rog-client/src/cli_options.rs
Normal file
215
rog-client/src/cli_options.rs
Normal 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
18
rog-client/src/error.rs
Normal 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
455
rog-client/src/fancy.rs
Normal 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
239
rog-client/src/lib.rs
Normal 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user