Advanced Aura feature

Groundwork for 'advanced' aura modes
Add single zone + Doom light flash
Fix mocking for ROGCC
Better prepare & change to mapping of keyboard layouts to models and functions
Refactor and begin using new key layout stuff
Enable first arg to rogcc to set layout in mocking feature mode
Complete refactor of key layouts, and to RON serde
This commit is contained in:
Luke D. Jones
2022-12-11 11:50:47 +13:00
parent e3ecaa92bd
commit 1cbffedaeb
134 changed files with 8249 additions and 4390 deletions

576
rog-aura/src/advanced.rs Normal file
View File

@@ -0,0 +1,576 @@
use log::warn;
use serde::{Deserialize, Serialize};
#[cfg(feature = "dbus")]
use zbus::zvariant::Type;
/// The `LedCode` used in setting up keyboard layouts is important because it
/// determines the idexing for an RGB value in the final USB packets (for
/// per-key addressable keyboards).
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
pub enum LedCode {
VolUp,
VolDown,
MicMute,
#[default]
RogApp,
RogFan,
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,
Backspace,
/// For keyboards where the backspace button has 3 LED
Backspace3_1,
Backspace3_2,
Backspace3_3,
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,
Return,
/// For keyboards where the return button has 3 LED
Return3_1,
Return3_2,
Return3_3,
PgDn,
LShift,
/// For keyboards where the left shift button has 3 LED
LShift3_1,
LShift3_2,
LShift3_3,
Z,
X,
C,
V,
B,
N,
M,
Comma,
Period,
FwdSlash,
Star,
NumPadDel,
NumPadPlus,
NumPadEnter,
NumPadPause,
NumPadPrtSc,
NumPadHome,
NumLock,
Rshift,
Rshift3_1,
Rshift3_2,
Rshift3_3,
End,
LCtrl,
LFn,
Meta,
LAlt,
Spacebar,
/// For keyboards where the spacebar button has 5 LED
Spacebar5_1,
Spacebar5_2,
Spacebar5_3,
Spacebar5_4,
Spacebar5_5,
Pause,
RAlt,
PrtSc,
RCtrl,
Up,
Down,
Left,
Right,
RFn,
MediaPlay,
MediaStop,
MediaNext,
MediaPrev,
LidLogo,
LidLeft,
LidRight,
/// Used by per-key and multizoned
LightbarRight,
/// Used by per-key and multizoned
LightbarRightCorner,
/// Used by per-key and multizoned
LightbarRightBottom,
/// Used by per-key and multizoned
LightbarLeftBottom,
/// Used by per-key and multizoned
LightbarLeftCorner,
/// Used by per-key and multizoned
LightbarLeft,
/// Use if the keyboard supports only a single zone. This zone uses the same
/// packet data as the `Zoned*` below
SingleZone,
/// Use if the keyboard supports 4 zones, this is the left zone
ZonedKbLeft,
/// Use if the keyboard supports 4 zones, this is the left-center zone
ZonedKbLeftMid,
/// Use if the keyboard supports 4 zones, this is the right-center zone
ZonedKbRightMid,
/// Use if the keyboard supports 4 zones, this is the right zone
ZonedKbRight,
/// To be ignored by effects
Spacing,
/// To be ignored by effects
Blocking,
}
impl LedCode {
pub fn is_placeholder(&self) -> bool {
matches!(self, Self::Spacing | Self::Blocking)
}
pub fn is_keyboard_zone(&self) -> bool {
matches!(
self,
Self::ZonedKbLeft | Self::ZonedKbLeftMid | Self::ZonedKbRightMid | Self::ZonedKbRight
)
}
pub fn is_lightbar_zone(&self) -> bool {
matches!(
self,
Self::LightbarLeft
| Self::LightbarLeftCorner
| Self::LightbarLeftBottom
| Self::LightbarRightBottom
| Self::LightbarRightCorner
| Self::LightbarRight
)
}
}
/// Represents the per-key raw USB packets
pub type UsbPackets = Vec<Vec<u8>>;
/// A `UsbPackets` 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.
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct LedUsbPackets {
/// The packet data used to send data to the USB keyboard
usb_packets: UsbPackets,
/// Wether or not this packet collection is zoned. The determines which
/// starting bytes are used and what the indexing is for lightbar RGB
/// colours
zoned: bool,
}
impl Default for LedUsbPackets {
fn default() -> Self {
Self::new_per_key()
}
}
impl LedUsbPackets {
/// Set up a series of per-key packets. This includes setting all the
/// required starting bytes per packet, but does not set any colours.
///
/// These packets will not work with per-zone keyboards
pub fn new_per_key() -> Self {
let mut set = vec![vec![0u8; 64]; 11];
// set[0].copy_from_slice(&KeyColourArray::get_init_msg());
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;
}
Self {
usb_packets: set,
zoned: false,
}
}
/// Create new zoned packets. Although the result is a nested `Vec` only the
/// first vector is available. The final packet is slightly different
/// for single-zoned compared to multizoned.
///
/// This packet will not work with per-key keyboards
///
/// Wireshark captures show:
/// ```ignore
/// 5d,bc,01,01,00,00,00,00,00,ff,00,00, RED, single zone
/// 5d,bc,01,01,04,00,00,00,00,ff,00,00, RED, multizone
/// ```
pub fn new_zoned(multizoned: bool) -> Self {
let mut pkt = vec![0u8; 64];
pkt[0] = 0x5d; // Report ID
pkt[1] = 0xbc; // Mode = custom??, 0xb3 is builtin
pkt[2] = 0x01;
pkt[3] = 0x01; // ??
if !multizoned {
pkt[4] = 0x00; // This doesn't actually seem to matter on this
// keyboard?
} else {
pkt[4] = 0x04; // ??, 4,5,6 are normally RGB for builtin mode
// colours
}
Self {
usb_packets: vec![pkt],
zoned: true,
}
}
/// Initialise and clear the keyboard for custom effects, this must be done
/// for every time mode switches from builtin to custom
#[inline]
pub const fn get_init_msg() -> [u8; 64] {
let mut init = [0u8; 64];
init[0] = 0x5d; // Report ID
init[1] = 0xbc; // Mode = custom??, 0xb3 is builtin
init
}
/// Set the RGB colour of an `LedCode`
#[inline]
pub fn set(&mut self, key: LedCode, r: u8, g: u8, b: u8) {
if let Some(c) = self.rgb_for_led_code(key) {
c[0] = r;
c[1] = g;
c[2] = b;
}
}
/// Indexes in to `UsbPackets` at the correct row and column
/// to set a series of three bytes to the chosen R,G,B values
///
/// Indexing is different for `zoned` and assumes that only one packet is
/// generated for all the zones
fn rgb_for_led_code(&mut self, led_code: LedCode) -> Option<&mut [u8]> {
let zoned = self.zoned;
// Tuples are indexes in to array
let (row, col) = match led_code {
LedCode::VolDown => (0, 15),
LedCode::VolUp => (0, 18),
LedCode::MicMute => (0, 21),
LedCode::RogApp => (0, 24),
//
LedCode::Esc => (1, 24),
LedCode::F1 => (1, 30),
LedCode::F2 => (1, 33),
LedCode::F3 => (1, 36),
LedCode::F4 => (1, 39),
LedCode::F5 => (1, 45),
LedCode::F6 => (1, 48),
LedCode::F7 => (1, 51),
LedCode::F8 => (1, 54),
//
LedCode::F9 => (2, 12),
LedCode::F10 => (2, 15),
LedCode::F11 => (2, 18),
LedCode::F12 => (2, 21),
LedCode::Del => (2, 24),
LedCode::Tilde => (2, 39),
LedCode::N1 => (2, 42),
LedCode::N2 => (2, 45),
LedCode::N3 => (2, 48),
LedCode::N4 => (2, 51),
LedCode::N5 => (2, 54),
//
LedCode::N6 => (3, 9),
LedCode::N7 => (3, 12),
LedCode::N8 => (3, 15),
LedCode::N9 => (3, 18),
LedCode::N0 => (3, 21),
LedCode::Hyphen => (3, 24),
LedCode::Equals => (3, 27),
LedCode::Backspace3_1 => (3, 30),
LedCode::Backspace3_2 => (3, 33),
LedCode::Backspace3_3 => (3, 36),
LedCode::Home => (3, 39),
LedCode::Tab => (3, 54),
//
LedCode::Q => (4, 9),
LedCode::W => (4, 12),
LedCode::E => (4, 15),
LedCode::R => (4, 18),
LedCode::T => (4, 21),
LedCode::Y => (4, 24),
LedCode::U => (4, 27),
LedCode::I => (4, 30),
LedCode::O => (4, 33),
LedCode::P => (4, 36),
LedCode::LBracket => (4, 39),
LedCode::RBracket => (4, 42),
LedCode::BackSlash => (4, 45),
LedCode::PgUp => (4, 54),
//
LedCode::Caps => (5, 21),
LedCode::A => (5, 24),
LedCode::S => (5, 27),
LedCode::D => (5, 30),
LedCode::F => (5, 33),
LedCode::G => (5, 36),
LedCode::H => (5, 39),
LedCode::J => (5, 42),
LedCode::K => (5, 45),
LedCode::L => (5, 48),
LedCode::SemiColon => (5, 51),
LedCode::Quote => (5, 54),
//
LedCode::Return => (6, 9),
LedCode::Return3_1 => (6, 12),
LedCode::Return3_2 => (6, 15),
LedCode::Return3_3 => (6, 18),
LedCode::PgDn => (6, 21),
LedCode::LShift => (6, 36),
// TODO: Find correct locations
LedCode::LShift3_1 => (6, 36),
LedCode::LShift3_2 => (6, 36),
LedCode::LShift3_3 => (6, 36),
LedCode::Z => (6, 42),
LedCode::X => (6, 45),
LedCode::C => (6, 48),
LedCode::V => (6, 51),
LedCode::B => (6, 54),
//
LedCode::N => (7, 9),
LedCode::M => (7, 12),
LedCode::Comma => (7, 15),
LedCode::Period => (7, 18),
LedCode::FwdSlash => (7, 21),
LedCode::Rshift => (7, 24),
LedCode::Rshift3_1 => (7, 27),
LedCode::Rshift3_2 => (7, 30),
LedCode::Rshift3_3 => (7, 33),
LedCode::End => (7, 36),
LedCode::LCtrl => (7, 51),
LedCode::LFn => (7, 54),
//
LedCode::Meta => (8, 9),
LedCode::LAlt => (8, 12),
LedCode::Spacebar5_1 => (8, 15),
LedCode::Spacebar5_2 => (8, 18),
LedCode::Spacebar5_3 => (8, 21),
LedCode::Spacebar5_4 => (8, 24),
LedCode::Spacebar5_5 => (8, 27),
LedCode::RAlt => (8, 30),
LedCode::PrtSc => (8, 33),
LedCode::RCtrl => (8, 36),
LedCode::Up => (8, 42),
LedCode::RFn => (8, 51),
//
LedCode::Left => (9, 54),
//
LedCode::Down => (10, 9),
LedCode::Right => (10, 12),
LedCode::LidLogo => (11, 9),
LedCode::LidLeft => (11, 36),
LedCode::LidRight => (11, 39),
//
LedCode::SingleZone | LedCode::ZonedKbLeft => (0, 9),
LedCode::ZonedKbLeftMid => (0, 12),
LedCode::ZonedKbRightMid => (0, 15),
LedCode::ZonedKbRight => (0, 18),
LedCode::LightbarRight => if zoned {(0, 27)} else { (11, 15)},
LedCode::LightbarRightCorner => if zoned {(0, 30)} else {(11, 18)},
LedCode::LightbarRightBottom => if zoned {(0, 33)} else{(11, 21)},
LedCode::LightbarLeftBottom => if zoned {(0, 36)} else{(11, 24)},
LedCode::LightbarLeftCorner => if zoned {(0, 39)} else{(11, 27)},
LedCode::LightbarLeft => if zoned {(0, 42)} else{(11, 30)},
//
LedCode::Spacing
| LedCode::Blocking
// TODO: the addressing of the following
| LedCode::MediaPlay
| LedCode::MediaStop
| LedCode::MediaPrev
| LedCode::MediaNext
| LedCode::Pause
| LedCode::NumLock
| LedCode::Star
| LedCode::NumPadDel
| LedCode::NumPadPlus
| LedCode::NumPadEnter
| LedCode::NumPadPause
| LedCode::NumPadPrtSc
| LedCode::NumPadHome
| LedCode::RogFan
| LedCode::Spacebar
| LedCode::Backspace => return None,
};
if self.zoned && row > 0 {
warn!(
"LedCode {led_code:?} for zoned is not correct or out of Zone range. Setting to 0",
);
return None;
}
Some(&mut self.usb_packets[row][col..=col + 2])
}
#[inline]
pub fn get(&self) -> UsbPackets {
self.usb_packets.clone()
}
#[inline]
pub fn get_ref(&self) -> &UsbPackets {
&self.usb_packets
}
#[inline]
pub fn get_mut(&mut self) -> &mut UsbPackets {
&mut self.usb_packets
}
}
impl From<LedUsbPackets> for UsbPackets {
fn from(k: LedUsbPackets) -> Self {
k.usb_packets
}
}
#[cfg(test)]
mod tests {
use crate::advanced::{LedCode, LedUsbPackets, UsbPackets};
macro_rules! colour_check_zoned {
($zone:expr, $pkt_idx_start:expr) => {
let mut zone = LedUsbPackets::new_zoned(true);
let c = zone.rgb_for_led_code($zone).unwrap();
c[0] = 255;
c[1] = 255;
c[2] = 255;
let pkt: UsbPackets = zone.into();
assert_eq!(pkt[0][$pkt_idx_start], 0xff);
assert_eq!(pkt[0][$pkt_idx_start + 1], 0xff);
assert_eq!(pkt[0][$pkt_idx_start + 2], 0xff);
};
}
#[test]
fn zone_to_packet_check() {
let zone = LedUsbPackets::new_zoned(true);
let pkt: UsbPackets = zone.into();
assert_eq!(pkt[0][0], 0x5d);
assert_eq!(pkt[0][1], 0xbc);
assert_eq!(pkt[0][2], 0x01);
assert_eq!(pkt[0][3], 0x01);
assert_eq!(pkt[0][4], 0x04);
colour_check_zoned!(LedCode::ZonedKbLeft, 9);
colour_check_zoned!(LedCode::ZonedKbLeftMid, 12);
colour_check_zoned!(LedCode::ZonedKbRightMid, 15);
colour_check_zoned!(LedCode::ZonedKbRight, 18);
colour_check_zoned!(LedCode::LightbarRight, 27);
colour_check_zoned!(LedCode::LightbarRightCorner, 30);
colour_check_zoned!(LedCode::LightbarRightBottom, 33);
colour_check_zoned!(LedCode::LightbarLeftBottom, 36);
colour_check_zoned!(LedCode::LightbarLeftCorner, 39);
colour_check_zoned!(LedCode::LightbarLeft, 42);
}
#[test]
fn perkey_to_packet_check() {
let per_key = LedUsbPackets::new_per_key();
let pkt: UsbPackets = per_key.into();
assert_eq!(pkt[0][0], 0x5d);
assert_eq!(pkt[0][1], 0xbc);
assert_eq!(pkt[0][2], 0x00);
assert_eq!(pkt[0][3], 0x01);
assert_eq!(pkt[0][4], 0x01);
assert_eq!(pkt[0][5], 0x01);
let mut per_key = LedUsbPackets::new_per_key();
let c = per_key.rgb_for_led_code(LedCode::D).unwrap();
c[0] = 255;
c[1] = 255;
c[2] = 255;
let c = per_key.rgb_for_led_code(LedCode::O).unwrap();
c[0] = 255;
c[1] = 255;
c[2] = 255;
let c = per_key.rgb_for_led_code(LedCode::N0).unwrap();
c[0] = 255;
c[1] = 255;
c[2] = 255;
let c = per_key.rgb_for_led_code(LedCode::M).unwrap();
c[0] = 255;
c[1] = 255;
c[2] = 255;
let pkt: UsbPackets = per_key.into();
assert_eq!(pkt[5][30], 0xff); // D, red
assert_eq!(pkt[5][31], 0xff); // D
assert_eq!(pkt[5][32], 0xff); // D
assert_eq!(pkt[5][33], 0x00); // D
assert_eq!(pkt[4][33], 0xff); // O, red
assert_eq!(pkt[4][34], 0xff); // O
assert_eq!(pkt[4][35], 0xff); // O
assert_eq!(pkt[4][36], 0x00); // O
assert_eq!(pkt[7][12], 0xff); // M, red
assert_eq!(pkt[7][13], 0xff); // M
assert_eq!(pkt[7][14], 0xff); // M
assert_eq!(pkt[7][15], 0x00); // M
}
}