Refactor, rename, organise rog-aura stuff better

This commit is contained in:
Luke D. Jones
2024-04-05 21:20:34 +13:00
parent 4701c019a8
commit 1c729316f7
27 changed files with 770 additions and 974 deletions

View File

@@ -0,0 +1,728 @@
use log::warn;
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
#[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
#[typeshare]
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.
#[typeshare]
#[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
#[allow(clippy::match_same_arms)]
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
}
}
impl From<LedCode> for &str {
fn from(k: LedCode) -> Self {
(&k).into()
}
}
impl From<&LedCode> for &str {
fn from(k: &LedCode) -> Self {
#[allow(clippy::match_same_arms)]
match k {
LedCode::VolUp => "Volume Up",
LedCode::VolDown => "Volume Down",
LedCode::MicMute => "Mute Mic",
LedCode::RogApp => "ROG",
LedCode::RogFan => "Fan Control",
LedCode::Esc => "Escape",
LedCode::F1 => "F1",
LedCode::F2 => "F2",
LedCode::F3 => "F3",
LedCode::F4 => "F4",
LedCode::F5 => "F5",
LedCode::F6 => "F6",
LedCode::F7 => "F7",
LedCode::F8 => "F8",
LedCode::F9 => "F9",
LedCode::F10 => "F10",
LedCode::F11 => "F11",
LedCode::F12 => "F12",
LedCode::Del => "Delete",
LedCode::Tilde => "Tilde",
LedCode::N1 => "1",
LedCode::N2 => "2",
LedCode::N3 => "3",
LedCode::N4 => "4",
LedCode::N5 => "5",
LedCode::N6 => "6",
LedCode::N7 => "7",
LedCode::N8 => "8",
LedCode::N9 => "9",
LedCode::N0 => "0",
LedCode::Hyphen => "-",
LedCode::Equals => "=",
LedCode::Backspace => "Backspace",
LedCode::Backspace3_1 => "Backspace LED 1",
LedCode::Backspace3_2 => "Backspace LED 2",
LedCode::Backspace3_3 => "Backspace LED 3",
LedCode::Home => "Home",
LedCode::Tab => "Tab",
LedCode::Q => "Q",
LedCode::W => "W",
LedCode::E => "E",
LedCode::R => "R",
LedCode::T => "T",
LedCode::Y => "Y",
LedCode::U => "U",
LedCode::I => "I",
LedCode::O => "O",
LedCode::P => "P",
LedCode::LBracket => "[",
LedCode::RBracket => "]",
LedCode::BackSlash => "\\",
LedCode::PgUp => "Page Up",
LedCode::Caps => "Caps Lock",
LedCode::A => "A",
LedCode::S => "S",
LedCode::D => "D",
LedCode::F => "F",
LedCode::G => "G",
LedCode::H => "H",
LedCode::J => "J",
LedCode::K => "K",
LedCode::L => "L",
LedCode::SemiColon => ";",
LedCode::Quote => "'",
LedCode::Return => "Return",
LedCode::Return3_1 => "Return LED 1",
LedCode::Return3_2 => "Return LED 2",
LedCode::Return3_3 => "Return LED 3",
LedCode::PgDn => "Page Down",
LedCode::LShift => "Left Shift",
LedCode::LShift3_1 => "Left Shift LED 1",
LedCode::LShift3_2 => "Left Shift LED 2",
LedCode::LShift3_3 => "Left Shift LED 3",
LedCode::Z => "Z",
LedCode::X => "X",
LedCode::C => "C",
LedCode::V => "V",
LedCode::B => "B",
LedCode::N => "N",
LedCode::M => "M",
LedCode::Comma => ",",
LedCode::Period => ".",
LedCode::Star => "*",
LedCode::NumPadDel => "Delete",
LedCode::NumPadPlus => "+",
LedCode::NumPadEnter => "Enter",
LedCode::NumPadPause => "Pause",
LedCode::NumPadPrtSc => "Print Screen",
LedCode::NumPadHome => "Home",
LedCode::NumLock => "Num-Lock",
LedCode::FwdSlash => "/",
LedCode::Rshift => "Right Shift",
LedCode::Rshift3_1 => "Right Shift LED 1",
LedCode::Rshift3_2 => "Right Shift LED 2",
LedCode::Rshift3_3 => "Right Shift LED 3",
LedCode::End => "End",
LedCode::LCtrl => "Left Control",
LedCode::LFn => "Left Fn",
LedCode::Meta => "Meta",
LedCode::LAlt => "Left Alt",
LedCode::Spacebar => "Space",
LedCode::Spacebar5_1 => "Space LED 1",
LedCode::Spacebar5_2 => "Space LED 2",
LedCode::Spacebar5_3 => "Space LED 3",
LedCode::Spacebar5_4 => "Space LED 4",
LedCode::Spacebar5_5 => "Space LED 5",
LedCode::RAlt => "Right Alt",
LedCode::PrtSc => "Print Screen",
LedCode::RCtrl => "Right Control",
LedCode::Pause => "Pause",
LedCode::Up => "Up",
LedCode::Down => "Down",
LedCode::Left => "Left",
LedCode::Right => "Right",
LedCode::RFn => "Right Fn",
LedCode::MediaPlay => "Media Play",
LedCode::MediaStop => "Media Stop",
LedCode::MediaNext => "Media Next",
LedCode::MediaPrev => "Media Previous",
LedCode::LidLogo => "Lid Logo",
LedCode::LidLeft => "Lid Left",
LedCode::LidRight => "Lid Right",
LedCode::LightbarRight => "Lightbar Right",
LedCode::LightbarRightCorner => "Lightbar Right Corner",
LedCode::LightbarRightBottom => "Lightbar Right Bottom",
LedCode::LightbarLeftBottom => "Lightbar Left Bottom",
LedCode::LightbarLeftCorner => "Lightbar Left Corner",
LedCode::LightbarLeft => "Lightbar Left",
LedCode::Spacing | LedCode::Blocking => "",
LedCode::SingleZone => "Single Zoned Keyboard",
LedCode::ZonedKbLeft => "Left Zone (zone 1)",
LedCode::ZonedKbLeftMid => "Center-left Zone (zone 2)",
LedCode::ZonedKbRightMid => "Center-right Zone (zone 3)",
LedCode::ZonedKbRight => "Right Zone (zone 4)",
}
}
}
#[cfg(test)]
mod tests {
use crate::keyboard::{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
}
}

View File

@@ -0,0 +1,562 @@
//! A series of pre-defined layouts. These were mostly used to generate an
//! editable config.
use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};
use std::slice::Iter;
use log::warn;
use serde::{Deserialize, Serialize};
use crate::aura_detection::LaptopLedData;
use crate::error::Error;
use crate::keyboard::{AdvancedAuraType, LedCode};
use crate::{AuraModeNum, AuraZone};
/// The `key_type` plays a role in effects (eventually). You could for example
/// add a `ShapeType::Spacing` to pad out an effect, such as a laserbeam across
/// a row so that it doesn't appear to *jump* across a gap
///
/// w=1.0, h=1.0 should be considered the size of a typical key like 'A'
#[derive(Debug, Deserialize, Serialize, Clone)]
pub enum KeyShape {
Led {
width: f32,
height: f32,
pad_left: f32,
pad_right: f32,
pad_top: f32,
pad_bottom: f32,
},
Blank {
width: f32,
height: f32,
},
}
impl KeyShape {
pub fn new_led(
width: f32,
height: f32,
pad_left: f32,
pad_right: f32,
pad_top: f32,
pad_bottom: f32,
) -> Self {
Self::Led {
width,
height,
pad_left,
pad_right,
pad_top,
pad_bottom,
}
}
pub fn new_blank(width: f32, height: f32) -> Self {
Self::Blank { width, height }
}
/// Scale the shape up/down. Intended for use in UI on a clone
pub fn scale(&mut self, scale: f32) {
match self {
KeyShape::Led {
width,
height,
pad_left,
pad_right,
pad_top,
pad_bottom,
} => {
*width *= scale;
*height *= scale;
*pad_left *= scale;
*pad_right *= scale;
*pad_top *= scale;
*pad_bottom *= scale;
}
KeyShape::Blank { width, height } => {
*width *= scale;
*height *= scale;
}
}
}
}
/// The first `Key` will determine the row height.
///
/// Every row is considered to start a x=0, with the first row being y=0,
/// and following rows starting after the previous `row_y + pad_top` and
/// `row_x + pad_left`
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct KeyRow {
pad_left: f32,
pad_top: f32,
/// The `Key` is what provides an RGB index location in the final USB
/// packets
row: Vec<(LedCode, String)>,
/// The final data structure merged key_shapes and rows
#[serde(skip)]
built_row: Vec<(LedCode, KeyShape)>,
}
impl KeyRow {
pub fn new(pad_left: f32, pad_top: f32, row: Vec<(LedCode, String)>) -> Self {
Self {
pad_left,
pad_top,
row,
built_row: Default::default(),
}
}
pub fn row(&self) -> Iter<'_, (LedCode, KeyShape)> {
self.built_row.iter()
}
pub fn row_ref(&self) -> &[(LedCode, KeyShape)] {
&self.built_row
}
/// Find and return the heightest height of this row
pub fn height(&self) -> f32 {
if self.built_row.is_empty() {
return 0.0;
}
let mut h = 0.0;
for k in &self.built_row {
let height = match &k.1 {
KeyShape::Led {
height,
pad_top,
pad_bottom,
..
} => height + pad_top + pad_bottom,
KeyShape::Blank { height, .. } => *height,
};
if h < height {
h = height;
}
}
h
}
/// Return the total row width
pub fn width(&self) -> f32 {
if self.built_row.is_empty() {
return 0.0;
}
let mut w = 0.0;
for k in &self.built_row {
match &k.1 {
KeyShape::Led {
width,
pad_left,
pad_right,
..
} => w += width + pad_left + pad_right,
KeyShape::Blank { width, .. } => w += width,
}
}
w
}
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct KeyLayout {
/// Localization of this keyboard layout
locale: String,
/// The shapes of keys used
key_shapes: HashMap<String, KeyShape>,
/// The rows of keys of this layout
key_rows: Vec<KeyRow>,
/// Should be copied from the `LaptopLedData` as laptops may have the same
/// layout, but different EC features
#[serde(skip)]
basic_modes: Vec<AuraModeNum>,
/// Should be copied from the `LaptopLedData` as laptops may have the same
/// layout, but different EC features
#[serde(skip)]
basic_zones: Vec<AuraZone>,
/// Paired with the key selection in UI. Determines if individual keys are
/// selectable, zones, or single zone.
///
/// Should be copied from the `LaptopLedData` as laptops may have the same
/// layout, but different EC features.
#[serde(skip)]
advanced_type: AdvancedAuraType,
}
impl KeyLayout {
fn from_file(path: &Path) -> Result<Self, Error> {
let buf: String = std::fs::read_to_string(path)
.map_err(|e| Error::IoPath(path.to_string_lossy().to_string(), e))?;
if buf.is_empty() {
Err(Error::IoPath(
path.to_string_lossy().to_string(),
std::io::ErrorKind::InvalidData.into(),
))
} else {
let mut data = ron::from_str::<Self>(&buf)?;
let mut unused = HashSet::new();
for k in data.key_shapes.keys() {
unused.insert(k);
}
let rows = &mut data.key_rows;
for row in rows {
for k in &mut row.row {
if let Some(shape) = data.key_shapes.get(&k.1) {
row.built_row.push((k.0, shape.clone()));
unused.remove(&k.1);
} else {
warn!("Key {:?} was missing matching shape {}", k.0, k.1);
}
}
}
if !unused.is_empty() {
warn!("The layout {path:?} had unused shapes {unused:?}",);
}
Ok(data)
}
}
pub fn rows(&self) -> Iter<'_, KeyRow> {
self.key_rows.iter()
}
pub fn rows_ref(&self) -> &[KeyRow] {
&self.key_rows
}
pub fn basic_modes(&self) -> &[AuraModeNum] {
&self.basic_modes
}
pub fn basic_zones(&self) -> &[AuraZone] {
&self.basic_zones
}
pub fn advanced_type(&self) -> &AdvancedAuraType {
&self.advanced_type
}
/// Find the total heighht of the keyboard, not including lightbar rows
pub fn keyboard_height(&self) -> f32 {
let mut height = 0.0;
for r in &self.key_rows {
if let Some(key) = r.row.first() {
if !key.0.is_lightbar_zone() {
height += r.height();
}
}
}
height
}
pub fn max_height(&self) -> f32 {
let mut height = 0.0;
for r in &self.key_rows {
height += r.height();
}
height
}
pub fn max_width(&self) -> f32 {
let mut width = 0.0;
for r in &self.key_rows {
let tmp = r.width();
if width < tmp {
width = tmp;
}
}
width
}
/// Find a layout matching the name in `LaptopLedData` in the provided dir
pub fn find_layout(led_data: LaptopLedData, mut data_path: PathBuf) -> Result<Self, Error> {
// TODO: locales
let layout_name = if led_data.layout_name.is_empty() {
"ga401q".to_owned() // Need some sort of default here due to ROGCC
// expecting it
} else {
led_data.layout_name
};
let layout_file = format!("{layout_name}_US.ron");
data_path.push("layouts");
data_path.push(layout_file);
let path = data_path.as_path();
let mut tmp = KeyLayout::from_file(path)?;
tmp.basic_modes = led_data.basic_modes;
tmp.basic_zones = led_data.basic_zones;
tmp.advanced_type = led_data.advanced_type;
Ok(tmp)
}
pub fn layout_files(mut data_path: PathBuf) -> Result<Vec<PathBuf>, Error> {
data_path.push("layouts");
let path = data_path.as_path();
let mut files = Vec::new();
std::fs::read_dir(path)
.map_err(|e| {
println!("{:?}, {e}", path);
e
})
.unwrap()
.for_each(|p| {
if let Ok(p) = p {
files.push(p.path());
}
});
Ok(files)
}
}
impl KeyLayout {
pub fn default_layout() -> Self {
Self {
locale: "US".to_owned(),
basic_modes: vec![
AuraModeNum::Static,
AuraModeNum::Breathe,
AuraModeNum::Pulse,
],
basic_zones: vec![AuraZone::None],
advanced_type: AdvancedAuraType::None,
key_shapes: HashMap::from([(
"regular".to_owned(),
KeyShape::new_led(1.0, 1.0, 0.1, 0.1, 0.1, 0.1),
)]),
key_rows: vec![
KeyRow::new(
0.1,
0.1,
vec![
(LedCode::Esc, "regular".to_owned()),
(LedCode::F1, "regular".to_owned()),
(LedCode::F2, "regular".to_owned()),
(LedCode::F3, "regular".to_owned()),
(LedCode::F4, "regular".to_owned()),
// not sure which key to put here
(LedCode::F5, "regular".to_owned()),
(LedCode::F6, "regular".to_owned()),
(LedCode::F7, "regular".to_owned()),
(LedCode::F8, "regular".to_owned()),
(LedCode::F9, "regular".to_owned()),
(LedCode::F10, "regular".to_owned()),
(LedCode::F11, "regular".to_owned()),
(LedCode::F12, "regular".to_owned()),
],
),
KeyRow::new(
0.1,
0.1,
vec![
(LedCode::Tilde, "regular".to_owned()),
(LedCode::N1, "regular".to_owned()),
(LedCode::N2, "regular".to_owned()),
(LedCode::N3, "regular".to_owned()),
(LedCode::N4, "regular".to_owned()),
(LedCode::N5, "regular".to_owned()),
(LedCode::N6, "regular".to_owned()),
(LedCode::N7, "regular".to_owned()),
(LedCode::N8, "regular".to_owned()),
(LedCode::N9, "regular".to_owned()),
(LedCode::N0, "regular".to_owned()),
(LedCode::Hyphen, "regular".to_owned()),
(LedCode::Equals, "regular".to_owned()),
(LedCode::Backspace, "regular".to_owned()),
],
),
KeyRow::new(
0.1,
0.1,
vec![
(LedCode::Tab, "regular".to_owned()),
(LedCode::Q, "regular".to_owned()),
(LedCode::W, "regular".to_owned()),
(LedCode::E, "regular".to_owned()),
(LedCode::R, "regular".to_owned()),
(LedCode::T, "regular".to_owned()),
(LedCode::Y, "regular".to_owned()),
(LedCode::U, "regular".to_owned()),
(LedCode::I, "regular".to_owned()),
(LedCode::O, "regular".to_owned()),
(LedCode::P, "regular".to_owned()),
(LedCode::LBracket, "regular".to_owned()),
(LedCode::RBracket, "regular".to_owned()),
(LedCode::BackSlash, "regular".to_owned()),
],
),
KeyRow::new(
0.1,
0.1,
vec![
(LedCode::Caps, "regular".to_owned()),
(LedCode::A, "regular".to_owned()),
(LedCode::S, "regular".to_owned()),
(LedCode::D, "regular".to_owned()),
(LedCode::F, "regular".to_owned()),
(LedCode::G, "regular".to_owned()),
(LedCode::H, "regular".to_owned()),
(LedCode::J, "regular".to_owned()),
(LedCode::K, "regular".to_owned()),
(LedCode::L, "regular".to_owned()),
(LedCode::SemiColon, "regular".to_owned()),
(LedCode::Quote, "regular".to_owned()),
(LedCode::Return, "regular".to_owned()),
],
),
KeyRow::new(
0.1,
0.1,
vec![
(LedCode::LShift, "regular".to_owned()),
(LedCode::Z, "regular".to_owned()),
(LedCode::X, "regular".to_owned()),
(LedCode::C, "regular".to_owned()),
(LedCode::V, "regular".to_owned()),
(LedCode::B, "regular".to_owned()),
(LedCode::N, "regular".to_owned()),
(LedCode::M, "regular".to_owned()),
(LedCode::Comma, "regular".to_owned()),
(LedCode::Period, "regular".to_owned()),
(LedCode::FwdSlash, "regular".to_owned()),
(LedCode::Rshift, "regular".to_owned()),
],
),
KeyRow::new(
0.1,
0.1,
vec![
(LedCode::LCtrl, "regular".to_owned()),
(LedCode::LFn, "regular".to_owned()),
(LedCode::Meta, "regular".to_owned()),
(LedCode::LAlt, "regular".to_owned()),
(LedCode::Spacebar, "regular".to_owned()),
(LedCode::RAlt, "regular".to_owned()),
(LedCode::PrtSc, "regular".to_owned()),
(LedCode::RCtrl, "regular".to_owned()),
],
),
],
}
}
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use std::fs::{self, OpenOptions};
use std::io::Read;
use std::path::PathBuf;
use crate::aura_detection::LedSupportFile;
use crate::keyboard::KeyLayout;
#[test]
fn check_parse_all() {
const DATA_DIR: &str = env!("CARGO_MANIFEST_DIR");
let mut data_path = PathBuf::from(DATA_DIR);
data_path.push("data");
data_path.push("layouts");
let path = data_path.as_path();
for p in fs::read_dir(path)
.map_err(|e| {
println!("{:?}, {e}", path);
e
})
.unwrap()
{
let mut buf = std::fs::read_to_string(p.unwrap().path()).unwrap();
let data: KeyLayout = ron::from_str(&buf).unwrap();
let mut unused = HashSet::new();
for k in data.key_shapes.keys() {
unused.insert(k);
}
let rows = &data.key_rows;
for row in rows {
for k in &row.row {
if data.key_shapes.get(&k.1).is_some() {
unused.remove(&k.1);
} else {
panic!("Key {:?} was missing matching shape {}", k.0, k.1);
}
}
}
assert!(
unused.is_empty(),
"The layout {path:?} had unused shapes {unused:?}",
);
buf.clear();
}
// println!(
// "RON: {}",
// ron::ser::to_string_pretty(&tmp,
// PrettyConfig::new().depth_limit(4)).unwrap() );
// let mut data = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
// data.push("data/aura-support2.json");
// let mut file =
// OpenOptions::new().write(true).create(true).truncate(true).open(&
// data).unwrap(); file.write_all(json.as_bytes()).unwrap();
}
#[test]
fn check_layout_file_links() {
const DATA_DIR: &str = env!("CARGO_MANIFEST_DIR");
let mut data_path = PathBuf::from(DATA_DIR);
data_path.push("data");
data_path.push("aura_support.ron");
let mut buf = std::fs::read_to_string(&data_path).unwrap();
let data: LedSupportFile = ron::from_str(&buf).unwrap();
data_path.pop();
data_path.push("layouts");
data_path.push("loop_prep");
for config in data.get().iter().rev() {
buf.clear();
let layout_file = format!("{}_US.ron", config.layout_name);
data_path.pop();
data_path.push(&layout_file);
let mut file = OpenOptions::new()
.read(true)
.open(&data_path)
.map_err(|e| {
panic!(
"Error checking {data_path:?} for {} : {e:?}",
config.board_name
)
})
.unwrap();
#[allow(clippy::verbose_file_reads)]
if let Err(e) = file.read_to_string(&mut buf) {
panic!(
"Error checking {data_path:?} for {} : {e:?}",
config.board_name
)
}
if let Err(e) = ron::from_str::<KeyLayout>(&buf) {
panic!("Error checking {data_path:?} : {e:?}")
}
}
}
}

View File

@@ -0,0 +1,20 @@
/// All handling for `RgbAddress`ing.
mod advanced;
pub use advanced::*;
/// Helpers for consructing keyboard layouts for UI use and effects
mod layouts;
pub use layouts::*;
mod power;
pub use power::*;
#[derive(Debug, Clone, PartialEq, Eq, Default, serde::Deserialize, serde::Serialize)]
pub enum AdvancedAuraType {
/// A `None` will apply the effect to the whole keyboard via basic-static
/// mode
#[default]
None,
Zoned(Vec<LedCode>),
PerKey,
}

View File

@@ -0,0 +1,472 @@
//! Power state for Laptop MCU RGB/LED. This is generally for newer
//! 0x18c6, 0x19B6, 0x1a30, keyboard models (2021+)
use std::fmt::Debug;
use std::ops::{BitAnd, BitOr};
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value};
use crate::aura_detection::PowerZones;
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuraPowerState {
pub boot: bool,
pub awake: bool,
pub sleep: bool,
pub shutdown: bool,
}
impl Default for AuraPowerState {
/// Defaults all to off
fn default() -> Self {
Self {
boot: false,
awake: false,
sleep: false,
shutdown: false,
}
}
}
impl AuraPowerState {
pub fn to_byte(&self, zone: PowerZones) -> u32 {
match zone {
PowerZones::Logo => {
self.boot as u32
| (self.awake as u32) << 2
| (self.sleep as u32) << 4
| (self.shutdown as u32) << 6
}
PowerZones::Keyboard => {
(self.boot as u32) << 1
| (self.awake as u32) << 3
| (self.sleep as u32) << 5
| (self.shutdown as u32) << 7
}
PowerZones::Lightbar => {
(self.boot as u32) << (7 + 2)
| (self.awake as u32) << (7 + 3)
| (self.sleep as u32) << (7 + 4)
| (self.shutdown as u32) << (7 + 5)
}
PowerZones::Lid => {
(self.boot as u32) << (15 + 1)
| (self.awake as u32) << (15 + 2)
| (self.sleep as u32) << (15 + 3)
| (self.shutdown as u32) << (15 + 4)
}
PowerZones::RearGlow => {
(self.boot as u32) << (23 + 1)
| (self.awake as u32) << (23 + 2)
| (self.sleep as u32) << (23 + 3)
| (self.shutdown as u32) << (23 + 4)
}
}
}
}
/// Track and control the Aura keyboard power state
///
/// # Bits for newer 0x18c6, 0x19B6, 0x1a30, keyboard models
///
/// | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Label |
/// |--------|---------|---------|---------|----------|
/// |00000001| 00000000| 00000000| 00000000|boot_logo_|
/// |00000010| 00000000| 00000000| 00000000|boot_keyb_|
/// |00000100| 00000000| 00000000| 00000000|awake_logo|
/// |00001000| 00000000| 00000000| 00000000|awake_keyb|
/// |00010000| 00000000| 00000000| 00000000|sleep_logo|
/// |00100000| 00000000| 00000000| 00000000|sleep_keyb|
/// |01000000| 00000000| 00000000| 00000000|shut_logo_|
/// |10000000| 00000000| 00000000| 00000000|shut_keyb_|
/// |00000000| 00000010| 00000000| 00000000|boot_bar__|
/// |00000000| 00000100| 00000000| 00000000|awake_bar_|
/// |00000000| 00001000| 00000000| 00000000|sleep_bar_|
/// |00000000| 00010000| 00000000| 00000000|shut_bar__|
/// |00000000| 00000000| 00000001| 00000000|boot_lid__|
/// |00000000| 00000000| 00000010| 00000000|awkae_lid_|
/// |00000000| 00000000| 00000100| 00000000|sleep_lid_|
/// |00000000| 00000000| 00001000| 00000000|shut_lid__|
/// |00000000| 00000000| 00000000| 00000001|boot_rear_|
/// |00000000| 00000000| 00000000| 00000010|awake_rear|
/// |00000000| 00000000| 00000000| 00000100|sleep_rear|
/// |00000000| 00000000| 00000000| 00001000|shut_rear_|
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct LaptopAuraPower {
pub keyboard: AuraPowerState,
pub logo: AuraPowerState,
pub lightbar: AuraPowerState,
pub lid: AuraPowerState,
pub rear_glow: AuraPowerState,
}
impl LaptopAuraPower {
pub fn new_all_on() -> Self {
Self {
keyboard: AuraPowerState {
boot: true,
awake: true,
sleep: true,
shutdown: true,
},
logo: AuraPowerState {
boot: true,
awake: true,
sleep: true,
shutdown: true,
},
lightbar: AuraPowerState {
boot: true,
awake: true,
sleep: true,
shutdown: true,
},
lid: AuraPowerState {
boot: true,
awake: true,
sleep: true,
shutdown: true,
},
rear_glow: AuraPowerState {
boot: true,
awake: true,
sleep: true,
shutdown: true,
},
}
}
pub fn to_bytes(&self) -> [u8; 4] {
let mut a: u32 = 0;
a |= self.keyboard.to_byte(PowerZones::Keyboard);
a |= self.logo.to_byte(PowerZones::Logo);
a |= self.lid.to_byte(PowerZones::Lid);
a |= self.lightbar.to_byte(PowerZones::Lightbar);
a |= self.rear_glow.to_byte(PowerZones::RearGlow);
[
(a & 0xff) as u8,
((a & 0xff00) >> 8) as u8,
((a & 0xff0000) >> 16) as u8,
((a & 0xff000000) >> 24) as u8,
]
}
}
#[typeshare]
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
zvariant(signature = "u")
)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum LaptopTufAuraPower {
Boot = 0,
Awake = 1,
Sleep = 2,
Keyboard = 3,
}
impl LaptopTufAuraPower {
pub const fn dev_id() -> &'static str {
"tuf"
}
}
/// # Bits for older 0x1866 keyboard model
///
/// Keybord and Lightbar require Awake, Boot and Sleep apply to both
/// Keybord and Lightbar regardless of if either are enabled (or Awake is
/// enabled)
///
/// | Byte 1 | Byte 2 | Byte 3 | function | hex |
/// |------------|------------|------------|----------|----------|
/// | 0000, 0000 | 0000, 0000 | 0000, 0010 | Awake | 00,00,02 |
/// | 0000, 1000 | 0000, 0000 | 0000, 0000 | Keyboard | 08,00,00 |
/// | 0000, 0100 | 0000, 0101 | 0000, 0000 | Lightbar | 04,05,00 |
/// | 1100, 0011 | 0001, 0010 | 0000, 1001 | Boot/Sht | c3,12,09 |
/// | 0011, 0000 | 0000, 1000 | 0000, 0100 | Sleep | 30,08,04 |
/// | 1111, 1111 | 0001, 1111 | 0000, 1111 | all on | |
#[typeshare]
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
zvariant(signature = "u")
)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum LaptopOldAuraPower {
Awake = 0x000002,
Keyboard = 0x080000,
Lightbar = 0x040500,
Boot = 0xc31209,
Sleep = 0x300804,
}
impl From<LaptopOldAuraPower> for u32 {
fn from(a: LaptopOldAuraPower) -> Self {
a as u32
}
}
impl LaptopOldAuraPower {
pub fn to_bytes(control: &[Self]) -> [u8; 4] {
let mut a: u32 = 0;
for n in control {
a |= *n as u32;
}
[
((a & 0xff0000) >> 16) as u8,
((a & 0xff00) >> 8) as u8,
(a & 0xff) as u8,
0x00,
]
}
pub const fn dev_id() -> &'static str {
"0x1866"
}
}
impl BitOr<LaptopOldAuraPower> for LaptopOldAuraPower {
type Output = u32;
fn bitor(self, rhs: LaptopOldAuraPower) -> Self::Output {
self as u32 | rhs as u32
}
}
impl BitAnd<LaptopOldAuraPower> for LaptopOldAuraPower {
type Output = u32;
fn bitand(self, rhs: LaptopOldAuraPower) -> Self::Output {
self as u32 & rhs as u32
}
}
#[cfg(test)]
mod test {
use crate::keyboard::{AuraPowerState, LaptopAuraPower, LaptopOldAuraPower};
#[test]
fn check_0x1866_control_bytes() {
let bytes = [LaptopOldAuraPower::Keyboard, LaptopOldAuraPower::Awake];
let bytes = LaptopOldAuraPower::to_bytes(&bytes);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x08, 0x00, 0x02, 0x00]);
let bytes = [LaptopOldAuraPower::Lightbar, LaptopOldAuraPower::Awake];
let bytes = LaptopOldAuraPower::to_bytes(&bytes);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x04, 0x05, 0x02, 0x00]);
let bytes = [LaptopOldAuraPower::Sleep];
let bytes = LaptopOldAuraPower::to_bytes(&bytes);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x30, 0x08, 0x04, 0x00]);
let bytes = [LaptopOldAuraPower::Boot];
let bytes = LaptopOldAuraPower::to_bytes(&bytes);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0xc3, 0x12, 0x09, 0x00]);
let bytes = [
LaptopOldAuraPower::Keyboard,
LaptopOldAuraPower::Lightbar,
LaptopOldAuraPower::Awake,
LaptopOldAuraPower::Sleep,
LaptopOldAuraPower::Boot,
];
let bytes = LaptopOldAuraPower::to_bytes(&bytes);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0xff, 0x1f, 0x000f, 0x00]);
}
#[test]
fn check_0x19b6_control_bytes_binary_rep() {
fn to_binary_string(power: &LaptopAuraPower) -> String {
let bytes = power.to_bytes();
format!(
"{:08b}, {:08b}, {:08b}, {:08b}",
bytes[0], bytes[1], bytes[2], bytes[3]
)
}
let boot_logo_ = to_binary_string(&LaptopAuraPower {
logo: AuraPowerState {
boot: true,
..Default::default()
},
..Default::default()
});
let boot_keyb_ = to_binary_string(&LaptopAuraPower {
keyboard: AuraPowerState {
boot: true,
..Default::default()
},
..Default::default()
});
let sleep_logo = to_binary_string(&LaptopAuraPower {
logo: AuraPowerState {
sleep: true,
..Default::default()
},
..Default::default()
});
let sleep_keyb = to_binary_string(&LaptopAuraPower {
keyboard: AuraPowerState {
sleep: true,
..Default::default()
},
..Default::default()
});
let awake_logo = to_binary_string(&LaptopAuraPower {
logo: AuraPowerState {
awake: true,
..Default::default()
},
..Default::default()
});
let awake_keyb = to_binary_string(&LaptopAuraPower {
keyboard: AuraPowerState {
awake: true,
..Default::default()
},
..Default::default()
});
let shut_logo_ = to_binary_string(&LaptopAuraPower {
logo: AuraPowerState {
shutdown: true,
..Default::default()
},
..Default::default()
});
let shut_keyb_ = to_binary_string(&LaptopAuraPower {
keyboard: AuraPowerState {
shutdown: true,
..Default::default()
},
..Default::default()
});
let boot_bar__ = to_binary_string(&LaptopAuraPower {
lightbar: AuraPowerState {
boot: true,
..Default::default()
},
..Default::default()
});
let awake_bar_ = to_binary_string(&LaptopAuraPower {
lightbar: AuraPowerState {
awake: true,
..Default::default()
},
..Default::default()
});
let sleep_bar_ = to_binary_string(&LaptopAuraPower {
lightbar: AuraPowerState {
sleep: true,
..Default::default()
},
..Default::default()
});
let shut_bar__ = to_binary_string(&LaptopAuraPower {
lightbar: AuraPowerState {
shutdown: true,
..Default::default()
},
..Default::default()
});
let boot_lid__ = to_binary_string(&LaptopAuraPower {
lid: AuraPowerState {
boot: true,
..Default::default()
},
..Default::default()
});
let awake_lid_ = to_binary_string(&LaptopAuraPower {
lid: AuraPowerState {
awake: true,
..Default::default()
},
..Default::default()
});
let sleep_lid_ = to_binary_string(&LaptopAuraPower {
lid: AuraPowerState {
sleep: true,
..Default::default()
},
..Default::default()
});
let shut_lid__ = to_binary_string(&LaptopAuraPower {
lid: AuraPowerState {
shutdown: true,
..Default::default()
},
..Default::default()
});
let boot_rear_ = to_binary_string(&LaptopAuraPower {
rear_glow: AuraPowerState {
boot: true,
..Default::default()
},
..Default::default()
});
let awake_rear = to_binary_string(&LaptopAuraPower {
rear_glow: AuraPowerState {
awake: true,
..Default::default()
},
..Default::default()
});
let sleep_rear = to_binary_string(&LaptopAuraPower {
rear_glow: AuraPowerState {
sleep: true,
..Default::default()
},
..Default::default()
});
let shut_rear_ = to_binary_string(&LaptopAuraPower {
rear_glow: AuraPowerState {
shutdown: true,
..Default::default()
},
..Default::default()
});
assert_eq!(boot_logo_, "00000001, 00000000, 00000000, 00000000");
assert_eq!(boot_keyb_, "00000010, 00000000, 00000000, 00000000");
assert_eq!(awake_logo, "00000100, 00000000, 00000000, 00000000");
assert_eq!(awake_keyb, "00001000, 00000000, 00000000, 00000000");
assert_eq!(sleep_logo, "00010000, 00000000, 00000000, 00000000");
assert_eq!(sleep_keyb, "00100000, 00000000, 00000000, 00000000");
assert_eq!(shut_logo_, "01000000, 00000000, 00000000, 00000000");
assert_eq!(shut_keyb_, "10000000, 00000000, 00000000, 00000000");
//
assert_eq!(boot_bar__, "00000000, 00000010, 00000000, 00000000");
assert_eq!(awake_bar_, "00000000, 00000100, 00000000, 00000000");
assert_eq!(sleep_bar_, "00000000, 00001000, 00000000, 00000000");
assert_eq!(shut_bar__, "00000000, 00010000, 00000000, 00000000");
//
assert_eq!(boot_lid__, "00000000, 00000000, 00000001, 00000000");
assert_eq!(awake_lid_, "00000000, 00000000, 00000010, 00000000");
assert_eq!(sleep_lid_, "00000000, 00000000, 00000100, 00000000");
assert_eq!(shut_lid__, "00000000, 00000000, 00001000, 00000000");
//
assert_eq!(boot_rear_, "00000000, 00000000, 00000000, 00000001");
assert_eq!(awake_rear, "00000000, 00000000, 00000000, 00000010");
assert_eq!(sleep_rear, "00000000, 00000000, 00000000, 00000100");
assert_eq!(shut_rear_, "00000000, 00000000, 00000000, 00001000");
// All on
let byte1 = LaptopAuraPower::new_all_on();
let out = to_binary_string(&byte1);
assert_eq!(out, "11111111, 00011110, 00001111, 00001111");
}
}