mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
Refactor, rename, organise rog-aura stuff better
This commit is contained in:
728
rog-aura/src/keyboard/advanced.rs
Normal file
728
rog-aura/src/keyboard/advanced.rs
Normal 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
|
||||
}
|
||||
}
|
||||
562
rog-aura/src/keyboard/layouts.rs
Normal file
562
rog-aura/src/keyboard/layouts.rs
Normal 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:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
rog-aura/src/keyboard/mod.rs
Normal file
20
rog-aura/src/keyboard/mod.rs
Normal 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,
|
||||
}
|
||||
472
rog-aura/src/keyboard/power.rs
Normal file
472
rog-aura/src/keyboard/power.rs
Normal 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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user