diff --git a/CHANGELOG.md b/CHANGELOG.md index 685e33d4..b71bdf87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,38 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] + +## [0.15.0] - 2020-07-09 +### Changed +- Support "Calc" fn key on G712 +- ROG key has limited customization, see README.md for details. + + Power + + Sleep + + MediaRecord + + MediaFastFwd + + MediaRewind + + MediaNext + + MediaPrev + + MediaStop + + MediaPlayPause + + MediaVolMute + + MediaVolUp + + MediaVolDown + + BacklightInc + + BacklightDec + + ControlConfig + + LaunchTextEditor + + LaunchEmailApp + + LaunchNewsReader + + LaunchCalendar + + LaunchCalculator + + LaunchWebBrowser + + FileBrowser + +### BREAKING +- `"rog_key": "ControlConfig",` is required to be added below line 1 in `/etc/rogcore.conf` + alternatively the config can be removed, allowing it to be recreated. + ## [0.14.5] - 2020-07-07 ### Changed - Correct some device handling and order of operations @@ -18,20 +50,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Changes to the way rog-core is initialised -## [0.14.3] - 2020-05-07 +## [0.14.3] - 2020-07-05 ### Changed - Proper fix of laptop mode conversion bug -## [0.14.2] - 2020-04-07 +## [0.14.2] - 2020-07-04 ### Changed - Try to correct how laptop modes are handled -## [0.14.1] - 2020-04-07 +## [0.14.1] - 2020-07-04 ### Added - Support for G512 series - Support for GU502 (variant of GA15) -## [0.14.0] - 2020-01-07 +## [0.14.0] - 2020-07-01 ### Changed - Further refine the way setting LED modes works so it is more universal, this also allows for setting the brightness through `SetKeyBacklight` @@ -41,12 +73,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Dbus method `GetKeyBacklight` - Dbus method `GetKeyBacklightModes` -## [0.13.1] - 2020-29-06 +## [0.13.1] - 2020-06-29 ### Fixed - Properly initialise the device - Better log formatting -## [0.13.0] - 2020-29-06 +## [0.13.0] - 2020-06-29 ### Changed - Dbus command `LedWriteBytes` renamed to `SetKeyBacklight` - Dbus command `SetKeyBacklight` will now take a JSON string instead of an array of bytes @@ -75,7 +107,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Config file is now in JSON format, and will store only the LED modes that a laptop supports rather than the whole set -## [0.12.2] - 2020-29-06 +## [0.12.2] - 2020-06-29 ### Changed - "FanMode", "ChargeLimit" to "SetFanMode", "SetChargeLimit" @@ -84,7 +116,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Dbus methods "GetFanMode", "GetChargeLimit" - Support for ROG Strix G712 -## [0.12.0] - 2020-26-06 +## [0.12.0] - 2020-06-26 ### Changed - Add modes for FX531 LEDs - Change where USB reset is called @@ -94,7 +126,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### BREAKING CHANGE - `bat_charge_limit = 100` must be appended to the top of `/etc/rogcore.conf` -## [0.11.1] - 2020-11-06 +## [0.11.1] - 2020-06-11 ### Changed - Use DBUS_NAME instead of DBUS_IFACE when requesting the name - Give different names for the bytearray arguments so that pydbus is not confused @@ -104,7 +136,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 mpsc channels offers a more reactive app - Small fix for unreported mode indexing issue -## [0.11.0] - 2020-09-06 +## [0.11.0] - 2020-06-09 ### BREAKING CHANGE - Many of the RGB built-in modes have been renamed to match Armory-Crate names, this means that the `/etc/rogcore.conf` needs to be removed so it can be @@ -116,23 +148,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Rename client crate to better suit - Added a device reset for both keyboard and AniMe devices before configuring them -## [0.10.0] - 2020-23-05 +## [0.10.0] - 2020-05-23 ### Changed - Correctly set AMD boost - Add animatrix support for G14 laptops -## [0.9.7] - 2020-23-05 +## [0.9.7] - 2020-05-23 ### Changed - Start differentiating between models using the 0x1866 USB device - Refactor how to send multizone over dbus, and how to write it (write 4 packets before writing SET/APPLY) - Begin implementing profiles per board_name - Boost toggle for AMD (not freq adjustment yet) -## [0.9.6] - 2020-22-05 +## [0.9.6] - 2020-05-22 ### Changed - Fix needing to double-tap fan-mode to change mode -## [0.9.5] - 2020-22-05 +## [0.9.5] - 2020-05-22 ### Changed - Flip writing order of effect colour blocks every other block write to try and even out. Previously the bottom rows were always last to be written. diff --git a/Cargo.lock b/Cargo.lock index 5edf6237..3ef6e23e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -697,7 +697,7 @@ checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" [[package]] name = "rog-client" -version = "0.14.3" +version = "0.15.0" dependencies = [ "dbus", "gumdrop", @@ -710,7 +710,7 @@ dependencies = [ [[package]] name = "rog-daemon" -version = "0.14.5" +version = "0.15.0" dependencies = [ "dbus", "dbus-tokio", diff --git a/README.md b/README.md index c0c2ef2a..8feab0a9 100644 --- a/README.md +++ b/README.md @@ -201,6 +201,39 @@ Optional arguments: -s SPEED set the speed: low, med, high ``` +## ROG Key + +The ROG key can be customized in a limited way by use of presets: +- Power +- Sleep +- MediaRecord +- MediaFastFwd +- MediaRewind +- MediaNext +- MediaPrev +- MediaStop +- MediaPlayPause +- MediaVolMute +- MediaVolUp +- MediaVolDown +- BacklightInc +- BacklightDec +- ControlConfig +- LaunchTextEditor +- LaunchEmailApp +- LaunchNewsReader +- LaunchCalendar +- LaunchCalculator +- LaunchWebBrowser +- FileBrowser + +This key can be changed in /etc/rogcore.conf, for example: + +``` +{ + "rog_key": "FileBrowser", +``` + ## Daemon mode If the daemon service is enabled then on boot the following will be reloaded from save: diff --git a/debian/changelog b/debian/changelog index bb3d0ae8..49102580 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,32 @@ +rog-core (0.15.0) focal; urgency=medium + + - Support "Calc" fn key on G712 + - ROG key has limited customization, see README.md for details. + + Power + + Sleep + + MediaRecord + + MediaFastFwd + + MediaRewind + + MediaNext + + MediaPrev + + MediaStop + + MediaPlayPause + + MediaVolMute + + MediaVolUp + + MediaVolDown + + BacklightInc + + BacklightDec + + ControlConfig + + LaunchTextEditor + + LaunchEmailApp + + LaunchNewsReader + + LaunchCalendar + + LaunchCalculator + + LaunchWebBrowser + + FileBrowser + + -- Luke Jones Thu, 09 Jul 2020 11:14:16 +1200 + rog-core (0.14.5) focal; urgency=medium - Use udev rules to init the service diff --git a/reverse_eng/g712/IMG_20200628_083130_5.jpg b/reverse_eng/g712/IMG_20200628_083130_5.jpg new file mode 100644 index 00000000..7220c72f Binary files /dev/null and b/reverse_eng/g712/IMG_20200628_083130_5.jpg differ diff --git a/reverse_eng/g712/laptop_info b/reverse_eng/g712/laptop_info new file mode 100644 index 00000000..873401df --- /dev/null +++ b/reverse_eng/g712/laptop_info @@ -0,0 +1,130 @@ + +Bus 001 Device 002: ID 0b05:1866 ASUSTek Computer, Inc. N-KEY Device +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0b05 ASUSTek Computer, Inc. + idProduct 0x1866 + bcdDevice 0.02 + iManufacturer 1 ASUSTeK Computer Inc. + iProduct 2 N-KEY Device + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 0x005b + bNumInterfaces 3 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xe0 + Self Powered + Remote Wakeup + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 1 Boot Interface Subclass + bInterfaceProtocol 1 Keyboard + iInterface 3 (error) + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.10 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 83 + Report Descriptors: + ** UNAVAILABLE ** + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 1 Boot Interface Subclass + bInterfaceProtocol 1 Keyboard + iInterface 1 ASUSTeK Computer Inc. + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.10 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 65 + Report Descriptors: + ** UNAVAILABLE ** + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 1 Boot Interface Subclass + bInterfaceProtocol 1 Keyboard + iInterface 1 ASUSTeK Computer Inc. + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.10 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 167 + Report Descriptors: + ** UNAVAILABLE ** + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x04 EP 4 OUT + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 +Device Status: 0x0001 + Self Powered diff --git a/reverse_eng/g712/system_info b/reverse_eng/g712/system_info new file mode 100644 index 00000000..7960bf8b --- /dev/null +++ b/reverse_eng/g712/system_info @@ -0,0 +1,50 @@ +artem@art-kub:~$ cat /etc/*-release +DISTRIB_ID=Ubuntu +DISTRIB_RELEASE=20.04 +DISTRIB_CODENAME=focal +DISTRIB_DESCRIPTION="Ubuntu 20.04 LTS" +NAME="Ubuntu" +VERSION="20.04 LTS (Focal Fossa)" +ID=ubuntu +ID_LIKE=debian +PRETTY_NAME="Ubuntu 20.04 LTS" +VERSION_ID="20.04" +HOME_URL="https://www.ubuntu.com/" +SUPPORT_URL="https://help.ubuntu.com/" +BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" +PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" +VERSION_CODENAME=focal +UBUNTU_CODENAME=focal + +artem@art-kub:~$ hostnamectl + Static hostname: art-kub + Icon name: computer-laptop + Chassis: laptop + Machine ID: f3792a953d24486bb8881f4ab1b93e44 + Boot ID: 5f064ae70f274461909bea1526a5b4e3 + Operating System: Ubuntu 20.04 LTS + Kernel: Linux 5.4.0-39-generic + Architecture: x86-64 + +artem@art-kub:~$ cat /proc/version +Linux version 5.4.0-39-generic (buildd@lcy01-amd64-016) (gcc version 9.3.0 (Ubuntu 9.3.0-10ubuntu2)) #43-Ubuntu SMP Fri Jun 19 10:28:31 UTC 2020 + +artem@art-kub:~$ uname -a +Linux art-kub 5.4.0-39-generic #43-Ubuntu SMP Fri Jun 19 10:28:31 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux + + +artem@art-kub:~$ cat /sys/class/dmi/id/product_name +ROG Strix G712LV_G712LV + +artem@art-kub:~$ cat /sys/class/dmi/id/product_family +ROG Strix + +artem@art-kub:~$ cat /sys/class/dmi/id/board_name +G712LV + +artem@art-kub:~$ lsusb |grep 0b05 +Bus 001 Device 002: ID 0b05:1866 ASUSTek Computer, Inc. N-KEY Device + + + + diff --git a/reverse_eng/g712/usbhid.log b/reverse_eng/g712/usbhid.log new file mode 100644 index 00000000..abbaca99 --- /dev/null +++ b/reverse_eng/g712/usbhid.log @@ -0,0 +1,28 @@ +001:002:002:DESCRIPTOR 1593321424.949103 + 06 31 FF 09 76 A1 01 85 5A 19 00 2A FF 00 15 00 + 26 FF 00 75 08 95 05 81 00 19 00 2A FF 00 15 00 + 26 FF 00 75 08 95 3F B1 00 C0 05 0C 09 01 A1 01 + 85 02 19 00 2A 3C 02 15 00 26 3C 02 75 10 95 02 + 81 00 C0 06 31 FF 09 79 A1 01 85 5D 19 00 2A FF + 00 15 00 26 FF 00 75 08 95 1F 81 00 19 00 2A FF + 00 15 00 26 FF 00 75 08 95 3F 91 00 19 00 2A FF + 00 15 00 26 FF 00 75 08 95 3F B1 00 C0 06 31 FF + 09 80 A1 01 85 5E 19 00 2A FF 00 15 00 26 FF 00 + 75 08 95 05 81 00 19 00 2A FF 00 15 00 26 FF 00 + 75 08 95 3F B1 00 C0 + +001:002:001:DESCRIPTOR 1593321424.951280 + 05 01 09 06 A1 01 85 09 75 01 95 08 05 07 19 E0 + 29 E7 15 00 25 01 81 02 95 08 75 01 81 03 95 05 + 75 01 05 08 19 01 29 05 91 02 95 01 75 03 91 03 + 95 F0 75 01 05 07 19 00 29 EF 15 00 25 01 81 02 + C0 + +001:002:000:DESCRIPTOR 1593321424.952588 + 05 01 09 06 A1 01 85 01 75 01 95 08 05 07 19 E0 + 29 E7 15 00 25 01 81 02 95 01 75 08 81 03 95 05 + 75 01 05 08 19 01 29 05 91 02 95 01 75 03 91 03 + 95 06 75 08 15 00 26 FF 00 05 07 19 00 2A FF 00 + 81 00 95 C0 75 01 05 07 19 00 29 EF 15 00 25 01 + 81 02 C0 + diff --git a/rog-client/Cargo.toml b/rog-client/Cargo.toml index e4b40180..8e4aa058 100644 --- a/rog-client/Cargo.toml +++ b/rog-client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rog-client" -version = "0.14.3" +version = "0.15.0" license = "MPL-2.0" readme = "README.md" authors = ["Luke "] diff --git a/rog-core/Cargo.toml b/rog-core/Cargo.toml index db3ad4b0..63f4addc 100644 --- a/rog-core/Cargo.toml +++ b/rog-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rog-daemon" -version = "0.14.5" +version = "0.15.0" license = "MPL-2.0" readme = "README.md" authors = ["Luke "] diff --git a/rog-core/src/config.rs b/rog-core/src/config.rs index d945829d..2b0a2640 100644 --- a/rog-core/src/config.rs +++ b/rog-core/src/config.rs @@ -1,3 +1,4 @@ +use crate::virt_device::ConsumerKeys; use rog_client::aura_modes::AuraModes; use serde_derive::{Deserialize, Serialize}; use std::fs::{File, OpenOptions}; @@ -7,6 +8,7 @@ pub static CONFIG_PATH: &str = "/etc/rogcore.conf"; #[derive(Default, Deserialize, Serialize)] pub struct Config { + pub rog_key: ConsumerKeys, pub fan_mode: u8, pub bat_charge_limit: u8, pub brightness: u8, diff --git a/rog-core/src/laptops.rs b/rog-core/src/laptops.rs index 167e0918..543e498f 100644 --- a/rog-core/src/laptops.rs +++ b/rog-core/src/laptops.rs @@ -7,7 +7,7 @@ use rog_client::{ error::AuraError, }; //use keycode::{KeyMap, KeyMappingId, KeyState, KeyboardState}; -use crate::virt_device::ConsumerKeys; +use crate::virt_device::{ConsumerKeys, KeyboardKeys}; use log::{info, warn}; static HELP_ADDRESS: &str = "https://github.com/flukejones/rog-core"; @@ -192,30 +192,22 @@ impl LaptopBase { } FnKeys::ScreenBrightUp => rogcore.virt_keys().press(ConsumerKeys::BacklightInc.into()), //self.backlight.step_up(), FnKeys::ScreenBrightDn => rogcore.virt_keys().press(ConsumerKeys::BacklightDec.into()), - FnKeys::ScreenToggle => rogcore.virt_keys().press(ConsumerKeys::BacklightTog.into()), + FnKeys::ScreenToggle => {} FnKeys::Sleep => rogcore.suspend_with_systemd(), FnKeys::AirplaneMode => rogcore.toggle_airplane_mode(), - FnKeys::MicToggle => {} + FnKeys::MicToggle => rogcore.virt_keys().press(KeyboardKeys::MicToggle.into()), FnKeys::Fan => { rogcore.fan_mode_step(&mut config).unwrap_or_else(|err| { warn!("Couldn't toggle fan mode: {:?}", err); }); } - - FnKeys::TouchPadToggle => { - let mut key = [0u8; 32]; - key[0] = 0x01; - key[3] = 0x070; - rogcore.virt_keys().press(key); - } - FnKeys::Rog => { - //rogcore.aura_effect_init()?; - //rogcore.aura_write_effect(&self.per_key_led)?; - let mut key = [0u8; 32]; - key[0] = 0x01; - key[3] = 0x68; // XF86Tools? F13 - rogcore.virt_keys().press(key); - } + FnKeys::TouchPadToggle => rogcore + .virt_keys() + .press(KeyboardKeys::TouchpadToggle.into()), + FnKeys::Rog => rogcore.virt_keys().press(config.rog_key.into()), + FnKeys::Calc => rogcore + .virt_keys() + .press(ConsumerKeys::LaunchCalculator.into()), FnKeys::None => { if key_buf[0] != 0x5A { info!("Unmapped key, attempt passthrough: {:X?}", &key_buf[1]); @@ -266,6 +258,7 @@ pub enum FnKeys { LedBrightDown = 0xC5, AuraPrevious = 0xB2, AuraNext = 0xB3, + Calc = 0x90, None, } @@ -285,6 +278,7 @@ impl From for FnKeys { 0xC5 => FnKeys::LedBrightDown, 0xB2 => FnKeys::AuraPrevious, 0xB3 => FnKeys::AuraNext, + 0x90 => FnKeys::Calc, _ => FnKeys::None, } } diff --git a/rog-core/src/main.rs b/rog-core/src/main.rs index ff5deda5..06bb58d2 100644 --- a/rog-core/src/main.rs +++ b/rog-core/src/main.rs @@ -9,7 +9,7 @@ use rog_client::{ }; use std::io::Write; -static VERSION: &str = "0.14.5"; +static VERSION: &str = "0.15.0"; #[derive(Options)] struct CLIStart { diff --git a/rog-core/src/virt_device.rs b/rog-core/src/virt_device.rs index 0a848c43..7cf81640 100644 --- a/rog-core/src/virt_device.rs +++ b/rog-core/src/virt_device.rs @@ -1,4 +1,5 @@ use log::error; +use serde_derive::{Deserialize, Serialize}; use uhid_virt::{Bus, CreateParams, UHIDDevice}; /// Create a virtual device to emit key-presses @@ -113,12 +114,11 @@ impl VirtKeys { } #[allow(dead_code)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Deserialize, Serialize)] pub enum ConsumerKeys { Power = 0x30, Sleep = 0x32, - Menu = 0x0040, - + // Menu = 0x40, MediaRecord = 0xB2, MediaFastFwd = 0xB3, MediaRewind = 0xB4, @@ -126,41 +126,21 @@ pub enum ConsumerKeys { MediaPrev = 0xB6, MediaStop = 0xB7, MediaPlayPause = 0xCD, - MediaPause = 0xB0, - MediaVolMute = 0xE2, MediaVolUp = 0xE9, MediaVolDown = 0xEA, - BacklightInc = 0x6F, BacklightDec = 0x70, - - BacklightTog = 0x72, // USAGE (Backlight toggle? display toggle?) - BacklightMin = 0x73, - BacklightMax = 0x74, - + // BacklightTog = 0x72, // USAGE (Backlight toggle? display toggle?) ControlConfig = 0x183, - - LaunchWordEditor = 0x184, LaunchTextEditor = 0x185, - LaunchSpreadSheet = 0x186, - LaunchGraphicsEditor = 0x187, - LaunchPresentationApp = 0x188, - LaunchDatabaseEditor = 0x189, LaunchEmailApp = 0x18A, LaunchNewsReader = 0x18B, - LaunchCalendarApp = 0x018E, - LaunchTaskManagementApp = 0x18F, + LaunchCalendar = 0x018E, + LaunchCalculator = 0x192, LaunchWebBrowser = 0x196, - ControlPanel = 0x19F, - - VideoOutStep = 0x82, - - Documents = 0x1A7, + // VideoOutStep = 0x82, FileBrowser = 0x1B4, - ImageBrowser = 0x1B6, - AudioBrowser = 0x1B7, - MovieBrowser = 0x1B8, } impl From for [u8; 32] { @@ -172,3 +152,30 @@ impl From for [u8; 32] { bytes } } + +impl Default for ConsumerKeys { + fn default() -> Self { + ConsumerKeys::ControlConfig + } +} + +/// Implements only a small subset of useful keys +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum KeyboardKeys { + Config = 0x68, // Desktop configuration, F13 + MicToggle = 0x6f, // Microphone toggle, F20 + TouchpadToggle = 0x70, // Touchpad toggle, F21 + WWW = 0xf0, // Web browser + Sleep = 0xf8, // Sleep + Coffee = 0xf9, // lockscreen +} + +impl From for [u8; 32] { + fn from(key: KeyboardKeys) -> Self { + let mut bytes = [0u8; 32]; + bytes[0] = 0x01; // report ID for keyboard + bytes[3] = key as u8; + bytes + } +}