Compare commits

..

2 Commits

Author SHA1 Message Date
Luke Jones 2b396f35c2 Merge branch 'main' into 'main'
Update Discord Icon in Readme

See merge request asus-linux/asusctl!181
2024-05-07 01:59:22 +00:00
Ashwanth Balakrishnan 6c503541bb Update Discord Icon to Readme 2024-04-23 16:19:28 +00:00
179 changed files with 8260 additions and 15501 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ echo '+cargo clippy --all -- -D warnings'
cargo clippy --all -- -D warnings cargo clippy --all -- -D warnings
echo '+cargo test --all' echo '+cargo test --all'
cargo test --all -- --test-threads=1 cargo test --all
echo '+cargo cranky' echo '+cargo cranky'
cargo cranky cargo cranky
+2 -2
View File
@@ -17,7 +17,7 @@ image: rust:latest
- target/release/.cargo-lock - target/release/.cargo-lock
before_script: before_script:
- apt-get update -qq && apt-get install -y -qq libudev-dev libgtk-3-dev grep llvm clang libclang-dev libsdl2-dev libsdl2-gfx-dev - apt-get update -qq && apt-get install -y -qq libinput-dev libseat-dev libudev-dev libgtk-3-dev grep llvm clang libclang-dev libsdl2-dev libsdl2-gfx-dev
stages: stages:
- format - format
@@ -52,7 +52,7 @@ test:
<<: *rust_cache <<: *rust_cache
script: script:
- mkdir -p .git/hooks > /dev/null - mkdir -p .git/hooks > /dev/null
- cargo test --all -- --test-threads=1 - cargo test --all
release: release:
only: only:
+5 -198
View File
@@ -1,205 +1,12 @@
# Changelog # Changelog
All notable changes to this project will be documented in this file.
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] ## [Unreleased]
## [v6.1.2]
### Changed
- Try a slightly different tact to fix charge control slider
## [v6.1.1]
### Changed
- Fix aura data matching
- Fix charge control slider
## [v6.1.0]
### Changed
- Update deps
- Add support for G513RC RGB modes
- Many UI fixes
- Fixes to PPT settings in UI
## [v6.1.0-rc7]
- Refactor PPT handling more:
1. Per profile, per-ac/dc
2. Do not apply unless group is enabled
3. Better reset/disable handling
4. Selecting a profile defaults PPT to off/disabled
- Bugfix: prevent an AniMe thread async deadlock
## [v6.1.0-rc6]
### Changed
- Two small fixes, one for `low-power` profile name, and one for base gpu tdp
- Move to using platform_profile api only (no throttle_thermal_policy)
## [v6.1.0-rc5]
### Changed
- Per-AC/DC, per-profile tunings enabled (Battery vs AC power + platform profile)
- Add ability to restore PPT defaults
- Add PPT help dialogue to UI
- Bug fix: correctly set charge limit from UI
## [v6.1.0-rc4]
### Changed
- Bug fix: UI was setting incorrect value for FPPT
- Bug fix: Re-add callbacks for the throttle and epp settings in UI
- Bug fix: Fix UI settigns for AniMe Matrix display
- Bug fix: better handle missing tray (for example gnome)
- Strip out all outdated and unsafe tuning stuff
- Allow each performance profile to have different PPT settings
## [v6.1.0-rc3]
### Changed
- Bug fixes
- Partial support for per-profile CPU tunings (WIP)
## [v6.1.0-rc2]
### Added
- asus-armoury driver support. WIP, will be adjusted/changed further
- More "Slash" display controls
## [v6.1.0-rc1]
### Added
- ROG Arion external driver LED support
- Add GA605W LED layout
- Add GA605 + GU605 Slash support
### Changed
- Fix attribute writes. At some point the kernel API seems to have changed.
- Extremely large refactor of Aura device handling. Should enable easy add of different kinds now.
- Rename CLI args for aura related properties. This will likely change further as more devices are added
## [v6.0.12]
### Changed
- Add Ally X aura config
- Fixes to Ally led power configs
- Fix CLI led modes
- Add FX517Z to aura_support
- Add G614JJ Aura support
- Add G614JZ (2023 Strix G16) Aura support
## [v6.0.11]
### Changed
- Renamed `Strobe` effect to `RainbowCycle` to prevent confusion over what it is
- Ranamed `Rainbow` effect to `RainbowWave`
- Cleaned up serde crate deps
- Fixed AniMe on GA402XZ
- Update some of the G713 device aura features
- Update some of the G513 device aura features
## [v6.0.10]
### Added
- Add the GA401I model to aura_support.
### Changed
- Aura support return a default aura definition instead of nothing
- Minor updates in aura controller to ensure configs are updated if the support file changes
- Don't panic if -ENODEV on fan_curve enable
- Adjust the G513Q support to match what is on the asus website.
- Adjust init sequence of anime to prevent accidental use of Slash as Anime
- Enable notifs and tray icon without supergfx
## [v6.0.9]
### Added
- Add G513RS to laptop DB.
- Add G512LI to laptop DB.
### Changed
- Rename and recreate the default Anime config if cache setup fails
### Fixed
- Nuke the issue of GUI taking 100% of a CPU core
## [v6.0.8]
### Added
- Add G512L laptop DB entry
### Changed
- Add more tests to verify things
### Fix
- asusctl incorrectly assumes fan-curves unsupported. Now fixed.
- try to fix ROGCC using CPU time.
## [v6.0.7]
### Changed
- Add a config option to set if throttle policy is changed on ac/bat change (UI only)
- Allow X11 GUI. This is *not* supported. Please see readme.
- Fixes to some GUI widget layouts and sizing
- Do a backup HID raw write fro brightness if the read-back value does not match. This is a temporary solve for some G14 and G16 until the kernel patch is ready.
- Reimplement the older 0x1866 MCU keyboard control power bits plus UI control for it. If you had a keyboard affected by Lightbar issues and it is older than a couple of years this should help. If not, please file a bug.
## [v6.0.6]
### Added
- Add GX650R laptop to aura DB
### Changed
- Further tweaks to aura init
- More logging
- Fix TUF laptop led power
- Sanitize the dbus path for aura devices (remove `.` chars)
- Even more error handling (gracefully)
- Better checking for dbus interfaces
- Remove dbus `supported_interfaces`
- dbus ObjectManager is now at root `/`
## [v6.0.5]
### Changed
- Better more robust error handling in ROGCC
- Try to better handle pre-2021 laptops with lightbar
- Add more logging to try and trace the charge_control_end_threshold issue
- Make `kbd_brightness` optional to work around issues on some laptops that seem to lack it. Likely this will all need a refactor *again* if work proceeds in kernel for better RGB support.
## [v6.0.4]
### Changed
- Remove some `todo()` in rogcc
- Add missing `#[zbus(property)]` derive to Slash dbus properties
- Match G533Q laptop explicitly to the 0x8166 PID
## [v6.0.3]
### NOTE
- Xorg is not supported any longer. All major desktops and distros are Wayland.
### Changed
- Add a check to prevent non-TUF laptops with screwed up method return from TUF acpi methods from trying to add a TUF aura device without actually being a TUF laptop.
- Make the G834JZ entry in aura db generic for all G834J
## [v6.0.2]
### Changed
- Clean up code
- Don't panic if no aura device in ROGCC
- Try to prevent an errorenous tuf keyboard detection
## [v6.0.1] ## [v6.0.1]
### Added ### Added
Generated
+1444 -3394
View File
File diff suppressed because it is too large Load Diff
+46 -41
View File
@@ -1,6 +1,32 @@
[workspace]
members = [
"asusctl",
"asusd",
"asusd-user",
"config-traits",
"cpuctl",
"dmi-id",
"rog-platform",
"rog-dbus",
"rog-anime",
"rog-aura",
"rog-profiles",
"rog-control-center",
"rog-slash",
"simulators",
]
default-members = [
"asusctl",
"asusd",
"asusd-user",
"cpuctl",
"rog-control-center",
]
resolver = "2"
[workspace.package] [workspace.package]
version = "6.1.2" version = "6.0.1"
rust-version = "1.82" rust-version = "1.77"
license = "MPL-2.0" license = "MPL-2.0"
readme = "README.md" readme = "README.md"
authors = ["Luke <luke@ljones.dev>"] authors = ["Luke <luke@ljones.dev>"]
@@ -9,46 +35,25 @@ homepage = "https://gitlab.com/asus-linux/asusctl"
description = "Laptop feature control for ASUS ROG laptops and others" description = "Laptop feature control for ASUS ROG laptops and others"
edition = "2021" edition = "2021"
[workspace]
resolver = "2"
members = [
"asusctl",
"asusd",
"asusd-user",
"config-traits",
"dmi-id",
"rog-platform",
"rog-dbus",
"rog-anime",
"rog-aura",
"rog-profiles",
"rog-control-center",
"rog-slash",
"simulators",
"rog-scsi",
]
default-members = ["asusctl", "asusd", "asusd-user", "rog-control-center"]
[workspace.dependencies] [workspace.dependencies]
tokio = { version = "^1.39.0", default-features = false, features = [ tokio = { version = "^1.36.0", default-features = false, features = [
"macros", "macros",
"sync", "sync",
"time", "time",
"rt", "rt-multi-thread",
"rt-multi-thread",
] } ] }
concat-idents = "^1.1" concat-idents = "^1.1"
dirs = "^4.0" dirs = "^4.0"
smol = "^2.0" smol = "^1.3"
mio = "0.8.11" mio = "0.8.11"
futures-util = "0.3.31" zbus = "4.1"
zbus = "5.1.1" logind-zbus = { version = "4.0.2" } #, default-features = false, features = ["non_blocking"] }
logind-zbus = { version = "5.0.0" } #, default-features = false, features = ["non_blocking"] }
serde = { version = "^1.0", features = ["serde_derive"] } serde = "^1.0"
serde_derive = "^1.0"
ron = "*" ron = "*"
typeshare = "1.0.0"
log = "^0.4" log = "^0.4"
env_logger = "^0.10.0" env_logger = "^0.10.0"
@@ -66,26 +71,26 @@ gif = "^0.12.0"
versions = "6.2" versions = "6.2"
notify-rust = { version = "4.11.4", features = ["z", "async"] } notify-rust = { git = "https://github.com/flukejones/notify-rust.git", rev = "54176413b81189a3e4edbdc20a0b4f7e2e35c063", default-features = false, features = [
"z",
sg = { git = "https://github.com/flukejones/sg-rs.git" } ] }
[profile.release] [profile.release]
# thin = 57s, asusd = 9.0M # thin = 57s, asusd = 9.0M
# fat = 72s, asusd = 6.4M # fat = 72s, asusd = 6.4M
lto = "thin" lto = "fat"
debug = false debug = false
opt-level = 3 opt-level = 3
panic = "abort" panic = "abort"
# codegen-units = 1 codegen-units = 1
[profile.dev] [profile.dev]
opt-level = 1 opt-level = 1
# codegen-units = 1 codegen-units = 16
[profile.dev.package."*"] [profile.dev.package."*"]
opt-level = 1 opt-level = 1
# codegen-units = 1 codegen-units = 16
[profile.bench] [profile.bench]
debug = false debug = false
+9 -8
View File
@@ -1,4 +1,4 @@
VERSION := $(shell /usr/bin/grep -Pm1 'version = "(\d+.\d+.\d+.*)"' Cargo.toml | cut -d'"' -f2) VERSION := $(shell /usr/bin/grep -Pm1 'version = "(\d.\d.\d)"' Cargo.toml | cut -d'"' -f2)
INSTALL = install INSTALL = install
INSTALL_PROGRAM = ${INSTALL} -D -m 0755 INSTALL_PROGRAM = ${INSTALL} -D -m 0755
@@ -30,11 +30,6 @@ else
TARGET = debug TARGET = debug
endif endif
X11 ?= 0
ifeq ($(X11),1)
ARGS += --features "rog-control-center/x11"
endif
VENDORED ?= 0 VENDORED ?= 0
ifeq ($(VENDORED),1) ifeq ($(VENDORED),1)
ARGS += --frozen ARGS += --frozen
@@ -118,10 +113,16 @@ vendor:
mv .cargo/config ./cargo-config mv .cargo/config ./cargo-config
rm -rf .cargo rm -rf .cargo
rm -rf vendor rm -rf vendor
cargo vendor-filterer --all-features --platform x86_64-unknown-linux-gnu vendor cargo vendor-filterer --platform x86_64-unknown-linux-gnu vendor
tar pcfJ vendor_asusctl_$(VERSION).tar.xz vendor tar pcfJ vendor_asusctl_$(VERSION).tar.xz vendor
rm -rf vendor rm -rf vendor
bindings:
typeshare ./rog-anime/src/ --lang=typescript --output-file=bindings/ts/anime.ts
typeshare ./rog-aura/src/ --lang=typescript --output-file=bindings/ts/aura.ts
typeshare ./rog-profiles/src/ --lang=typescript --output-file=bindings/ts/profiles.ts
typeshare ./rog-platform/src/ --lang=typescript --output-file=bindings/ts/platform.ts
translate: translate:
find -name \*.slint | xargs slint-tr-extractor -o rog-control-center/translations/en/rog-control-center.po find -name \*.slint | xargs slint-tr-extractor -o rog-control-center/translations/en/rog-control-center.po
@@ -132,7 +133,7 @@ ifeq ($(VENDORED),1)
tar pxf vendor_asusctl_$(VERSION).tar.xz tar pxf vendor_asusctl_$(VERSION).tar.xz
endif endif
cargo build $(ARGS) cargo build $(ARGS)
ifeq ($(STRIP_BINARIES),1) ifneq ($(STRIP_BINARIES),0)
strip -s ./target/$(TARGET)/$(BIN_C) strip -s ./target/$(TARGET)/$(BIN_C)
strip -s ./target/$(TARGET)/$(BIN_D) strip -s ./target/$(TARGET)/$(BIN_D)
strip -s ./target/$(TARGET)/$(BIN_U) strip -s ./target/$(TARGET)/$(BIN_U)
+9 -27
View File
@@ -1,6 +1,6 @@
# `asusctl` for ASUS ROG # `asusctl` for ASUS ROG
[![Become a Patron!](https://github.com/codebard/patron-button-and-widgets-by-codebard/blob/master/images/become_a_patron_button.png?raw=true)](https://www.patreon.com/bePatron?u=7602281) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/V7V5CLU67) - [Asus Linux Website](https://asus-linux.org/) [Become a Patron!](https://www.patreon.com/bePatron?u=7602281) - [Asus Linux Website](https://asus-linux.org/)
**WARNING:** Many features are developed in tandem with kernel patches. If you see a feature is missing you either need a patched kernel or latest release. **WARNING:** Many features are developed in tandem with kernel patches. If you see a feature is missing you either need a patched kernel or latest release.
@@ -11,15 +11,7 @@ Now includes a GUI, `rog-control-center`.
## Kernel support ## Kernel support
Due to on-going driver work the minimum suggested kernel version is always **the latest*, as improvements and fixes are continuous. **The minimum supported kernel version is 6.10**, which will contain the patches from [here](https://lore.kernel.org/platform-driver-x86/20240404001652.86207-1-luke@ljones.dev/). Z13 devices will need [these](https://lore.kernel.org/linux-input/20240416090402.31057-1-luke@ljones.dev/T/#t)
Support for some new features is not avilable unless you run a patched kernel with the work I am doing [in this github repo](https://github.com/flukejones/linux/tree/wip/ally-6.13). Use the linked branch, or `wip/ally-6.12`. Everything that is done here is upstreamed eventually (a long process).
Z13 devices will need [these](https://lore.kernel.org/linux-input/20240416090402.31057-1-luke@ljones.dev/T/#t)
## X11 support
X11 is not supported at all, as in I will not help you with X11 issues if there are any due to limited time and it being unmaintained itself. You can however build `rog-control-center` with it enabled `cargo build --features "rog-control-center/x11"`.
## Goals ## Goals
@@ -37,10 +29,6 @@ The level of support for laptops is dependent on folks submitting data to includ
See the [rog-aura readme](./rog-aura/README.md) for more details. See the [rog-aura readme](./rog-aura/README.md) for more details.
## Discord
[![Discord](https://img.shields.io/badge/Discord-7289DA?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/B8GftRW2Hd)
## SUPPORTED LAPTOPS ## SUPPORTED LAPTOPS
Most ASUS gaming laptops that have a USB keyboard. If `lsusb` shows something similar Most ASUS gaming laptops that have a USB keyboard. If `lsusb` shows something similar
@@ -78,15 +66,14 @@ The list is a bit outdated as many features have been enabled in the Linux kerne
A gui is now in the repo - ROG Control Center. At this time it is still a WIP, but it has almost all features in place already. A gui is now in the repo - ROG Control Center. At this time it is still a WIP, but it has almost all features in place already.
**NOTE**: Xorg is not supported.
# BUILDING # BUILDING
Rust and cargo are required, they can be installed from [rustup.rs](https://rustup.rs/) or from the distro repos if newer than 1.75. Rust and cargo are required, they can be installed from [rustup.rs](https://rustup.rs/) or from the distro repos if newer than 1.75.
**fedora:** **fedora:**
dnf install cmake clang-devel libxkbcommon-devel systemd-devel expat-devel pcre2-devel libzstd-devel gtk3-devel dnf install cmake clang-devel libinput-devel libseat-devel libgbm-devel libxkbcommon-devel systemd-devel \
libdrm-devel expat-devel pcre2-devel libzstd-devel libappindicator-gtk3
make make
sudo make install sudo make install
@@ -95,16 +82,7 @@ Rust and cargo are required, they can be installed from [rustup.rs](https://rust
Works with KDE Plasma (without GTK packages) Works with KDE Plasma (without GTK packages)
zypper in -t pattern devel_basis zypper in -t pattern devel_basis
zypper in rustup make cmake clang-devel libxkbcommon-devel systemd-devel expat-devel pcre2-devel libzstd-devel gtk3-devel zypper in rustup make cmake libinput-devel libseat-devel libgbm-devel systemd-devel clang-devel llvm-devel gdk-pixbuf-devel cairo-devel pango-devel freetype-devel libexpat-devel libayatana-indicator3-7
make
sudo make install
**Debian(unsuported):**
officially unsuported,but you can still try and test it by yourself(some features may not be available).
sudo apt install libclang-dev libudev-dev libfontconfig-dev build-essential cmake libxkbcommon-dev
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
make make
sudo make install sudo make install
@@ -144,6 +122,10 @@ Generation of the bindings with `make bindings` requires `typeshare` to be insta
Dbus introsepction XML requires with `make introspection` requires `anime_sim` to be running before starting `asusd`. Dbus introsepction XML requires with `make introspection` requires `anime_sim` to be running before starting `asusd`.
# Join the Community
[![Discord](https://img.shields.io/badge/Discord-7289DA?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/z8y99XqPb7)
# OTHER # OTHER
## AniMe Matrix simulator ## AniMe Matrix simulator
+3 -4
View File
@@ -10,20 +10,19 @@ edition.workspace = true
[dependencies] [dependencies]
rog_anime = { path = "../rog-anime" } rog_anime = { path = "../rog-anime" }
rog_scsi = { path = "../rog-scsi" }
rog_slash = { path = "../rog-slash" } rog_slash = { path = "../rog-slash" }
rog_aura = { path = "../rog-aura" } rog_aura = { path = "../rog-aura" }
rog_dbus = { path = "../rog-dbus" } rog_dbus = { path = "../rog-dbus" }
rog_profiles = { path = "../rog-profiles" } rog_profiles = { path = "../rog-profiles" }
rog_platform = { path = "../rog-platform" } rog_platform = { path = "../rog-platform" }
asusd = { path = "../asusd" }
dmi_id = { path = "../dmi-id" } dmi_id = { path = "../dmi-id" }
log.workspace = true
env_logger.workspace = true
ron.workspace = true ron.workspace = true
gumdrop.workspace = true gumdrop.workspace = true
zbus.workspace = true zbus.workspace = true
[dev-dependencies] [dev-dependencies]
rog_dbus = { path = "../rog-dbus" } rog_dbus = { path = "../rog-dbus" }
cargo-husky.workspace = true
+2 -2
View File
@@ -23,10 +23,10 @@ fn main() -> Result<(), Box<dyn Error>> {
Path::new(&args[1]), Path::new(&args[1]),
None, None,
args[2].parse::<f32>().unwrap(), args[2].parse::<f32>().unwrap(),
AnimeType::GA401 AnimeType::GA401,
)?; )?;
let anime_type = get_anime_type(); let anime_type = get_anime_type()?;
proxy.write(matrix.into_data_buffer(anime_type)?).unwrap(); proxy.write(matrix.into_data_buffer(anime_type)?).unwrap();
+1 -1
View File
@@ -29,7 +29,7 @@ fn main() {
} }
} }
let anime_type = get_anime_type(); let anime_type = get_anime_type().unwrap();
proxy proxy
.write(matrix.into_data_buffer(anime_type).unwrap()) .write(matrix.into_data_buffer(anime_type).unwrap())
.unwrap(); .unwrap();
+9 -6
View File
@@ -19,13 +19,16 @@ fn main() {
let path = Path::new(&args[1]); let path = Path::new(&args[1]);
let brightness = args[2].parse::<f32>().unwrap(); let brightness = args[2].parse::<f32>().unwrap();
let anime_type = get_anime_type(); let anime_type = get_anime_type().unwrap();
let mut seq = Sequences::new(anime_type); let mut seq = Sequences::new(anime_type);
seq.insert(0, &ActionLoader::AsusAnimation { seq.insert(
file: path.into(), 0,
time: rog_anime::AnimTime::Infinite, &ActionLoader::AsusAnimation {
brightness file: path.into(),
}) time: rog_anime::AnimTime::Infinite,
brightness,
},
)
.unwrap(); .unwrap();
loop { loop {
+1 -1
View File
@@ -14,7 +14,7 @@ fn main() {
let conn = Connection::system().unwrap(); let conn = Connection::system().unwrap();
let proxy = AnimeProxyBlocking::new(&conn).unwrap(); let proxy = AnimeProxyBlocking::new(&conn).unwrap();
let anime_type = get_anime_type(); let anime_type = get_anime_type().unwrap();
let mut matrix = AnimeGrid::new(anime_type); let mut matrix = AnimeGrid::new(anime_type);
let tmp = matrix.get_mut(); let tmp = matrix.get_mut();
+1 -1
View File
@@ -9,7 +9,7 @@ use zbus::blocking::Connection;
fn main() { fn main() {
let conn = Connection::system().unwrap(); let conn = Connection::system().unwrap();
let proxy = AnimeProxyBlocking::new(&conn).unwrap(); let proxy = AnimeProxyBlocking::new(&conn).unwrap();
let anime_type = get_anime_type(); let anime_type = get_anime_type().unwrap();
let mut matrix = AnimeDataBuffer::new(anime_type); let mut matrix = AnimeDataBuffer::new(anime_type);
matrix.data_mut()[1] = 100; // start = 1 matrix.data_mut()[1] = 100; // start = 1
for n in matrix.data_mut()[2..32].iter_mut() { for n in matrix.data_mut()[2..32].iter_mut() {
+3 -3
View File
@@ -20,17 +20,17 @@ fn main() -> Result<(), Box<dyn Error>> {
exit(-1); exit(-1);
} }
let anime_type = get_anime_type(); let anime_type = get_anime_type()?;
let matrix = AnimeImage::from_png( let matrix = AnimeImage::from_png(
Path::new(&args[1]), Path::new(&args[1]),
args[2].parse::<f32>().unwrap(), args[2].parse::<f32>().unwrap(),
args[3].parse::<f32>().unwrap(), args[3].parse::<f32>().unwrap(),
Vec2::new( Vec2::new(
args[4].parse::<f32>().unwrap(), args[4].parse::<f32>().unwrap(),
args[5].parse::<f32>().unwrap() args[5].parse::<f32>().unwrap(),
), ),
args[6].parse::<f32>().unwrap(), args[6].parse::<f32>().unwrap(),
anime_type anime_type,
)?; )?;
proxy.write(<AnimeDataBuffer>::try_from(&matrix)?).unwrap(); proxy.write(<AnimeDataBuffer>::try_from(&matrix)?).unwrap();
+3 -3
View File
@@ -23,17 +23,17 @@ fn main() -> Result<(), Box<dyn Error>> {
exit(-1); exit(-1);
} }
let anime_type = get_anime_type(); let anime_type = get_anime_type()?;
let mut matrix = AnimeImage::from_png( let mut matrix = AnimeImage::from_png(
Path::new(&args[1]), Path::new(&args[1]),
args[2].parse::<f32>().unwrap(), args[2].parse::<f32>().unwrap(),
args[3].parse::<f32>().unwrap(), args[3].parse::<f32>().unwrap(),
Vec2::new( Vec2::new(
args[4].parse::<f32>().unwrap(), args[4].parse::<f32>().unwrap(),
args[5].parse::<f32>().unwrap() args[5].parse::<f32>().unwrap(),
), ),
args[6].parse::<f32>().unwrap(), args[6].parse::<f32>().unwrap(),
anime_type anime_type,
)?; )?;
loop { loop {
+2 -2
View File
@@ -36,10 +36,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Colour { Colour {
r: 200, r: 200,
g: 110, g: 110,
b: 0 b: 0,
}, },
100, 100,
10 10,
)); ));
seq.push(zone); seq.push(zone);
+7 -7
View File
@@ -40,7 +40,7 @@ pub struct AnimeCommand {
#[options(no_short, meta = "", help = "Off with his head!!!")] #[options(no_short, meta = "", help = "Off with his head!!!")]
pub off_with_his_head: Option<bool>, pub off_with_his_head: Option<bool>,
#[options(command)] #[options(command)]
pub command: Option<AnimeActions> pub command: Option<AnimeActions>,
} }
#[derive(Options)] #[derive(Options)]
@@ -54,7 +54,7 @@ pub enum AnimeActions {
#[options(help = "display an animated diagonal/pixel-perfect GIF")] #[options(help = "display an animated diagonal/pixel-perfect GIF")]
PixelGif(AnimeGifDiagonal), PixelGif(AnimeGifDiagonal),
#[options(help = "change which builtin animations are shown")] #[options(help = "change which builtin animations are shown")]
SetBuiltins(Builtins) SetBuiltins(Builtins),
} }
#[derive(Options)] #[derive(Options)]
@@ -82,7 +82,7 @@ pub struct Builtins {
)] )]
pub shutdown: AnimShutdown, pub shutdown: AnimShutdown,
#[options(meta = "", help = "set/apply the animations <true/false>")] #[options(meta = "", help = "set/apply the animations <true/false>")]
pub set: Option<bool> pub set: Option<bool>,
} }
#[derive(Options)] #[derive(Options)]
@@ -100,7 +100,7 @@ pub struct AnimeImage {
#[options(meta = "", default = "0.0", help = "the angle in radians")] #[options(meta = "", default = "0.0", help = "the angle in radians")]
pub angle: f32, pub angle: f32,
#[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")] #[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")]
pub bright: f32 pub bright: f32,
} }
#[derive(Options)] #[derive(Options)]
@@ -110,7 +110,7 @@ pub struct AnimeImageDiagonal {
#[options(meta = "", help = "full path to the png to display")] #[options(meta = "", help = "full path to the png to display")]
pub path: String, pub path: String,
#[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")] #[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")]
pub bright: f32 pub bright: f32,
} }
#[derive(Options)] #[derive(Options)]
@@ -134,7 +134,7 @@ pub struct AnimeGif {
default = "1", default = "1",
help = "how many loops to play - 0 is infinite" help = "how many loops to play - 0 is infinite"
)] )]
pub loops: u32 pub loops: u32,
} }
#[derive(Options)] #[derive(Options)]
@@ -150,5 +150,5 @@ pub struct AnimeGifDiagonal {
default = "1", default = "1",
help = "how many loops to play - 0 is infinite" help = "how many loops to play - 0 is infinite"
)] )]
pub loops: u32 pub loops: u32,
} }
+19 -21
View File
@@ -17,7 +17,7 @@ pub struct LedPowerCommand1 {
#[options(meta = "", help = "Control boot animations <true/false>")] #[options(meta = "", help = "Control boot animations <true/false>")]
pub boot: Option<bool>, pub boot: Option<bool>,
#[options(meta = "", help = "Control suspend animations <true/false>")] #[options(meta = "", help = "Control suspend animations <true/false>")]
pub sleep: Option<bool> pub sleep: Option<bool>,
} }
#[derive(Options, Debug)] #[derive(Options, Debug)]
@@ -25,7 +25,7 @@ pub struct LedPowerCommand2 {
#[options(help = "print help message")] #[options(help = "print help message")]
pub help: bool, pub help: bool,
#[options(command)] #[options(command)]
pub command: Option<SetAuraZoneEnabled> pub command: Option<SetAuraZoneEnabled>,
} }
#[derive(Options, Debug)] #[derive(Options, Debug)]
@@ -41,8 +41,6 @@ pub enum SetAuraZoneEnabled {
Lid(AuraPowerStates), Lid(AuraPowerStates),
#[options(help = "")] #[options(help = "")]
RearGlow(AuraPowerStates), RearGlow(AuraPowerStates),
#[options(help = "")]
Ally(AuraPowerStates)
} }
#[derive(Debug, Clone, Options)] #[derive(Debug, Clone, Options)]
@@ -56,12 +54,12 @@ pub struct AuraPowerStates {
#[options(help = "defaults to false if option unused")] #[options(help = "defaults to false if option unused")]
pub sleep: bool, pub sleep: bool,
#[options(help = "defaults to false if option unused")] #[options(help = "defaults to false if option unused")]
pub shutdown: bool pub shutdown: bool,
} }
#[derive(Options)] #[derive(Options)]
pub struct LedBrightness { pub struct LedBrightness {
level: Option<u8> level: Option<u8>,
} }
impl LedBrightness { impl LedBrightness {
pub fn new(level: Option<u8>) -> Self { pub fn new(level: Option<u8>) -> Self {
@@ -96,7 +94,7 @@ impl ToString for LedBrightness {
Some(0x00) => "low", Some(0x00) => "low",
Some(0x01) => "med", Some(0x01) => "med",
Some(0x02) => "high", Some(0x02) => "high",
_ => "unknown" _ => "unknown",
}; };
s.to_owned() s.to_owned()
} }
@@ -113,7 +111,7 @@ pub struct SingleSpeed {
meta = "", meta = "",
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left" help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
)] )]
pub zone: AuraZone pub zone: AuraZone,
} }
#[derive(Debug, Clone, Options, Default)] #[derive(Debug, Clone, Options, Default)]
@@ -129,7 +127,7 @@ pub struct SingleSpeedDirection {
meta = "", meta = "",
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left" help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
)] )]
pub zone: AuraZone pub zone: AuraZone,
} }
#[derive(Debug, Clone, Default, Options)] #[derive(Debug, Clone, Default, Options)]
@@ -143,7 +141,7 @@ pub struct SingleColour {
meta = "", meta = "",
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left" help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
)] )]
pub zone: AuraZone pub zone: AuraZone,
} }
#[derive(Debug, Clone, Default, Options)] #[derive(Debug, Clone, Default, Options)]
@@ -159,7 +157,7 @@ pub struct SingleColourSpeed {
meta = "", meta = "",
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left" help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
)] )]
pub zone: AuraZone pub zone: AuraZone,
} }
#[derive(Debug, Clone, Options, Default)] #[derive(Debug, Clone, Options, Default)]
@@ -177,7 +175,7 @@ pub struct TwoColourSpeed {
meta = "", meta = "",
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left" help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
)] )]
pub zone: AuraZone pub zone: AuraZone,
} }
#[derive(Debug, Clone, Default, Options)] #[derive(Debug, Clone, Default, Options)]
@@ -191,7 +189,7 @@ pub struct MultiZone {
#[options(short = "c", meta = "", help = "set the RGB value e.g, ff00ff")] #[options(short = "c", meta = "", help = "set the RGB value e.g, ff00ff")]
pub colour3: Colour, pub colour3: Colour,
#[options(short = "d", meta = "", help = "set the RGB value e.g, ff00ff")] #[options(short = "d", meta = "", help = "set the RGB value e.g, ff00ff")]
pub colour4: Colour pub colour4: Colour,
} }
#[derive(Debug, Clone, Default, Options)] #[derive(Debug, Clone, Default, Options)]
@@ -207,7 +205,7 @@ pub struct MultiColourSpeed {
#[options(short = "d", meta = "", help = "set the RGB value e.g, ff00ff")] #[options(short = "d", meta = "", help = "set the RGB value e.g, ff00ff")]
pub colour4: Colour, pub colour4: Colour,
#[options(no_long, meta = "", help = "set the speed: low, med, high")] #[options(no_long, meta = "", help = "set the speed: low, med, high")]
pub speed: Speed pub speed: Speed,
} }
/// Byte value for setting the built-in mode. /// Byte value for setting the built-in mode.
@@ -221,9 +219,9 @@ pub enum SetAuraBuiltin {
#[options(help = "pulse between one or two colours")] #[options(help = "pulse between one or two colours")]
Breathe(TwoColourSpeed), // 1 Breathe(TwoColourSpeed), // 1
#[options(help = "strobe through all colours")] #[options(help = "strobe through all colours")]
RainbowCycle(SingleSpeed), // 2 Strobe(SingleSpeed), // 2
#[options(help = "rainbow cycling in one of four directions")] #[options(help = "rainbow cycling in one of four directions")]
RainbowWave(SingleSpeedDirection), // 3 Rainbow(SingleSpeedDirection), // 3
#[options(help = "rain pattern mimicking raindrops")] #[options(help = "rain pattern mimicking raindrops")]
Stars(TwoColourSpeed), // 4 Stars(TwoColourSpeed), // 4
#[options(help = "rain pattern of three preset colours")] #[options(help = "rain pattern of three preset colours")]
@@ -239,7 +237,7 @@ pub enum SetAuraBuiltin {
#[options(help = "set a vertical line zooming from left")] #[options(help = "set a vertical line zooming from left")]
Comet(SingleColour), // 11 Comet(SingleColour), // 11
#[options(help = "set a wide vertical line zooming from left")] #[options(help = "set a wide vertical line zooming from left")]
Flash(SingleColour) // 12 Flash(SingleColour), // 12
} }
impl Default for SetAuraBuiltin { impl Default for SetAuraBuiltin {
@@ -314,14 +312,14 @@ impl From<&SetAuraBuiltin> for AuraEffect {
data.mode = AuraModeNum::Breathe; data.mode = AuraModeNum::Breathe;
data data
} }
SetAuraBuiltin::RainbowCycle(x) => { SetAuraBuiltin::Strobe(x) => {
let mut data: AuraEffect = x.into(); let mut data: AuraEffect = x.into();
data.mode = AuraModeNum::RainbowCycle; data.mode = AuraModeNum::Strobe;
data data
} }
SetAuraBuiltin::RainbowWave(x) => { SetAuraBuiltin::Rainbow(x) => {
let mut data: AuraEffect = x.into(); let mut data: AuraEffect = x.into();
data.mode = AuraModeNum::RainbowWave; data.mode = AuraModeNum::Rainbow;
data data
} }
SetAuraBuiltin::Stars(x) => { SetAuraBuiltin::Stars(x) => {
+36 -22
View File
@@ -1,10 +1,9 @@
use gumdrop::Options; use gumdrop::Options;
use rog_platform::platform::PlatformProfile; use rog_platform::platform::ThrottlePolicy;
use crate::anime_cli::AnimeCommand; use crate::anime_cli::AnimeCommand;
use crate::aura_cli::{LedBrightness, LedPowerCommand1, LedPowerCommand2, SetAuraBuiltin}; use crate::aura_cli::{LedBrightness, LedPowerCommand1, LedPowerCommand2, SetAuraBuiltin};
use crate::fan_curve_cli::FanCurveCommand; use crate::fan_curve_cli::FanCurveCommand;
use crate::scsi_cli::ScsiCommand;
use crate::slash_cli::SlashCommand; use crate::slash_cli::SlashCommand;
#[derive(Default, Options)] #[derive(Default, Options)]
@@ -23,20 +22,18 @@ pub struct CliStart {
pub prev_kbd_bright: bool, pub prev_kbd_bright: bool,
#[options(meta = "", help = "Set your battery charge limit <20-100>")] #[options(meta = "", help = "Set your battery charge limit <20-100>")]
pub chg_limit: Option<u8>, pub chg_limit: Option<u8>,
#[options(help = "Toggle one-shot battery charge to 100%")]
pub one_shot_chg: bool,
#[options(command)] #[options(command)]
pub command: Option<CliCommand> pub command: Option<CliCommand>,
} }
#[derive(Options)] #[derive(Options)]
pub enum CliCommand { pub enum CliCommand {
#[options(help = "Set the keyboard lighting from built-in modes")] #[options(help = "Set the keyboard lighting from built-in modes")]
Aura(LedModeCommand), LedMode(LedModeCommand),
#[options(help = "Set the LED power states")] #[options(help = "Set the LED power states")]
AuraPowerOld(LedPowerCommand1), LedPow1(LedPowerCommand1),
#[options(help = "Set the LED power states")] #[options(help = "Set the LED power states")]
AuraPower(LedPowerCommand2), LedPow2(LedPowerCommand2),
#[options(help = "Set or select platform_profile")] #[options(help = "Set or select platform_profile")]
Profile(ProfileCommand), Profile(ProfileCommand),
#[options(help = "Set, select, or modify fan curves if supported")] #[options(help = "Set, select, or modify fan curves if supported")]
@@ -47,13 +44,8 @@ pub enum CliCommand {
Anime(AnimeCommand), Anime(AnimeCommand),
#[options(name = "slash", help = "Manage Slash Ledbar")] #[options(name = "slash", help = "Manage Slash Ledbar")]
Slash(SlashCommand), Slash(SlashCommand),
#[options(name = "scsi", help = "Manage SCSI external drive")] #[options(help = "Change bios settings")]
Scsi(ScsiCommand), Bios(BiosCommand),
#[options(
help = "Change platform settings. This is a new interface exposed by the asus-armoury \
driver, some of the settings will be the same as the older platform interface"
)]
Armoury(ArmouryCommand)
} }
#[derive(Debug, Clone, Options)] #[derive(Debug, Clone, Options)]
@@ -71,7 +63,7 @@ pub struct ProfileCommand {
pub profile_get: bool, pub profile_get: bool,
#[options(meta = "", help = "set the active profile")] #[options(meta = "", help = "set the active profile")]
pub profile_set: Option<PlatformProfile> pub profile_set: Option<ThrottlePolicy>,
} }
#[derive(Options)] #[derive(Options)]
@@ -83,22 +75,44 @@ pub struct LedModeCommand {
#[options(help = "switch to previous aura mode")] #[options(help = "switch to previous aura mode")]
pub prev_mode: bool, pub prev_mode: bool,
#[options(command)] #[options(command)]
pub command: Option<SetAuraBuiltin> pub command: Option<SetAuraBuiltin>,
} }
#[derive(Options)] #[derive(Options)]
pub struct GraphicsCommand { pub struct GraphicsCommand {
#[options(help = "print help message")] #[options(help = "print help message")]
pub help: bool pub help: bool,
} }
#[derive(Options, Debug)] #[derive(Options, Debug)]
pub struct ArmouryCommand { pub struct BiosCommand {
#[options(help = "print help message")] #[options(help = "print help message")]
pub help: bool, pub help: bool,
#[options( #[options(
free, meta = "",
help = "append each value name followed by the value to set. `-1` sets to default" short = "S",
no_long,
help = "set bios POST sound: asusctl -S <true/false>"
)] )]
pub free: Vec<String> pub post_sound_set: Option<bool>,
#[options(no_long, short = "s", help = "read bios POST sound")]
pub post_sound_get: bool,
#[options(
meta = "",
short = "D",
no_long,
help = "Switch GPU MUX mode: 0 = Discrete, 1 = Optimus, reboot required"
)]
pub gpu_mux_mode_set: Option<u8>,
#[options(no_long, short = "d", help = "get GPU mode")]
pub gpu_mux_mode_get: bool,
#[options(
meta = "",
short = "O",
no_long,
help = "Set device panel overdrive <true/false>"
)]
pub panel_overdrive_set: Option<bool>,
#[options(no_long, short = "o", help = "get panel overdrive")]
pub panel_overdrive_get: bool,
} }
+3 -3
View File
@@ -1,5 +1,5 @@
use gumdrop::Options; use gumdrop::Options;
use rog_platform::platform::PlatformProfile; use rog_platform::platform::ThrottlePolicy;
use rog_profiles::fan_curve_set::CurveData; use rog_profiles::fan_curve_set::CurveData;
use rog_profiles::FanCurvePU; use rog_profiles::FanCurvePU;
@@ -18,7 +18,7 @@ pub struct FanCurveCommand {
meta = "", meta = "",
help = "profile to modify fan-curve for. Shows data if no options provided" help = "profile to modify fan-curve for. Shows data if no options provided"
)] )]
pub mod_profile: Option<PlatformProfile>, pub mod_profile: Option<ThrottlePolicy>,
#[options( #[options(
meta = "", meta = "",
@@ -45,5 +45,5 @@ pub struct FanCurveCommand {
help = "data format = 30c:1%,49c:2%,59c:3%,69c:4%,79c:31%,89c:49%,99c:56%,109c:58%. \ help = "data format = 30c:1%,49c:2%,59c:3%,69c:4%,79c:31%,89c:49%,99c:56%,109c:58%. \
`--mod-profile` required. If '%' is omitted the fan range is 0-255" `--mod-profile` required. If '%' is omitted the fan range is 0-255"
)] )]
pub data: Option<CurveData> pub data: Option<CurveData>,
} }
+322 -526
View File
File diff suppressed because it is too large Load Diff
-35
View File
@@ -1,35 +0,0 @@
use gumdrop::Options;
use rog_scsi::{AuraMode, Colour, Direction, Speed};
#[derive(Options)]
pub struct ScsiCommand {
#[options(help = "print help message")]
pub help: bool,
#[options(help = "Enable the SCSI drive LEDs")]
pub enable: Option<bool>,
#[options(meta = "", help = "Set LED mode (so 'list' for all options)")]
pub mode: Option<AuraMode>,
#[options(
meta = "",
help = "Set LED mode speed <slowest, slow, med, fast, fastest> (does not apply to all)"
)]
pub speed: Option<Speed>,
#[options(
meta = "",
help = "Set LED mode direction <forward, reverse> (does not apply to all)"
)]
pub direction: Option<Direction>,
#[options(
meta = "",
help = "Set LED colours <hex>, specify up to 4 with repeated arg"
)]
pub colours: Vec<Colour>,
#[options(help = "list available animations")]
pub list: bool
}
+5 -20
View File
@@ -7,29 +7,14 @@ pub struct SlashCommand {
pub help: bool, pub help: bool,
#[options(help = "Enable the Slash Ledbar")] #[options(help = "Enable the Slash Ledbar")]
pub enable: bool, pub enable: bool,
#[options(help = "Disable the Slash Ledbar")] #[options(help = "Ddisable the Slash Ledbar")]
pub disable: bool, pub disable: bool,
#[options(short = "l", meta = "", help = "Set brightness value <0-255>")] #[options(meta = "", help = "Set brightness value <0-255>")]
pub brightness: Option<u8>, pub brightness: Option<u8>,
#[options(meta = "", help = "Set interval value <0-5>")] #[options(meta = "", help = "Set interval value <0-255>")]
pub interval: Option<u8>, pub interval: Option<u8>,
#[options(meta = "", help = "Set SlashMode (so 'list' for all options)")] #[options(help = "Set SlashMode (so 'list' for all options)")]
pub mode: Option<SlashMode>, pub slash_mode: Option<SlashMode>,
#[options(help = "list available animations")] #[options(help = "list available animations")]
pub list: bool, pub list: bool,
#[options(short = "B", meta = "", help = "Show the animation on boot")]
pub show_on_boot: Option<bool>,
#[options(short = "S", meta = "", help = "Show the animation on shutdown")]
pub show_on_shutdown: Option<bool>,
#[options(short = "s", meta = "", help = "Show the animation on sleep")]
pub show_on_sleep: Option<bool>,
#[options(short = "b", meta = "", help = "Show the animation on battery")]
pub show_on_battery: Option<bool>,
#[options(
short = "w",
meta = "",
help = "Show the low-battery warning animation"
)]
pub show_battery_warning: Option<bool>
} }
+7 -4
View File
@@ -12,16 +12,13 @@ edition.workspace = true
name = "asusd-user" name = "asusd-user"
path = "src/daemon.rs" path = "src/daemon.rs"
[features]
default = []
local_data = []
[dependencies] [dependencies]
dirs.workspace = true dirs.workspace = true
smol.workspace = true smol.workspace = true
# serialisation # serialisation
serde.workspace = true serde.workspace = true
serde_derive.workspace = true
ron.workspace = true ron.workspace = true
rog_anime = { path = "../rog-anime" } rog_anime = { path = "../rog-anime" }
@@ -32,3 +29,9 @@ config-traits = { path = "../config-traits" }
zbus.workspace = true zbus.workspace = true
env_logger.workspace = true env_logger.workspace = true
[dev-dependencies]
cargo-husky.workspace = true
[package.metadata.cargo-machete]
ignored = ["serde"]
+21 -21
View File
@@ -6,7 +6,7 @@ use rog_anime::{ActionLoader, AnimTime, AnimeType, Fade, Sequences as AnimeSeque
use rog_aura::effects::{AdvancedEffects as AuraSequences, Breathe, DoomFlicker, Effect, Static}; use rog_aura::effects::{AdvancedEffects as AuraSequences, Breathe, DoomFlicker, Effect, Static};
use rog_aura::keyboard::LedCode; use rog_aura::keyboard::LedCode;
use rog_aura::{Colour, Speed}; use rog_aura::{Colour, Speed};
use serde::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use crate::error::Error; use crate::error::Error;
@@ -21,7 +21,7 @@ fn root_conf_dir() -> PathBuf {
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct ConfigAnime { pub struct ConfigAnime {
pub name: String, pub name: String,
pub anime: Vec<ActionLoader> pub anime: Vec<ActionLoader>,
} }
impl ConfigAnime { impl ConfigAnime {
@@ -52,8 +52,8 @@ impl Default for ConfigAnime {
time: AnimTime::Fade(Fade::new( time: AnimTime::Fade(Fade::new(
Duration::from_secs(2), Duration::from_secs(2),
None, None,
Duration::from_secs(2) Duration::from_secs(2),
)) )),
}, },
ActionLoader::AsusAnimation { ActionLoader::AsusAnimation {
file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(), file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(),
@@ -61,8 +61,8 @@ impl Default for ConfigAnime {
time: AnimTime::Fade(Fade::new( time: AnimTime::Fade(Fade::new(
Duration::from_secs(6), Duration::from_secs(6),
None, None,
Duration::from_secs(3) Duration::from_secs(3),
)) )),
}, },
ActionLoader::ImageAnimation { ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(), file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
@@ -73,8 +73,8 @@ impl Default for ConfigAnime {
time: AnimTime::Fade(Fade::new( time: AnimTime::Fade(Fade::new(
Duration::from_secs(2), Duration::from_secs(2),
Some(Duration::from_secs(2)), Some(Duration::from_secs(2)),
Duration::from_secs(2) Duration::from_secs(2),
)) )),
}, },
ActionLoader::Image { ActionLoader::Image {
file: "/usr/share/asusd/anime/custom/rust.png".into(), file: "/usr/share/asusd/anime/custom/rust.png".into(),
@@ -84,9 +84,9 @@ impl Default for ConfigAnime {
time: AnimTime::Fade(Fade::new( time: AnimTime::Fade(Fade::new(
Duration::from_secs(2), Duration::from_secs(2),
Some(Duration::from_secs(1)), Some(Duration::from_secs(1)),
Duration::from_secs(2) Duration::from_secs(2),
)), )),
brightness: 0.6 brightness: 0.6,
}, },
ActionLoader::Pause(Duration::from_secs(1)), ActionLoader::Pause(Duration::from_secs(1)),
ActionLoader::ImageAnimation { ActionLoader::ImageAnimation {
@@ -95,9 +95,9 @@ impl Default for ConfigAnime {
angle: 0.0, angle: 0.0,
translation: Vec2::new(3.0, 2.0), translation: Vec2::new(3.0, 2.0),
brightness: 0.5, brightness: 0.5,
time: AnimTime::Count(2) time: AnimTime::Count(2),
}, },
] ],
} }
} }
} }
@@ -121,7 +121,7 @@ impl StdConfigLoad for ConfigAnime {}
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct ConfigAura { pub struct ConfigAura {
pub name: String, pub name: String,
pub aura: AuraSequences pub aura: AuraSequences,
} }
impl ConfigAura { impl ConfigAura {
@@ -139,14 +139,14 @@ impl Default for ConfigAura {
Colour { Colour {
r: 255, r: 255,
g: 0, g: 0,
b: 20 b: 20,
}, },
Colour { Colour {
r: 20, r: 20,
g: 255, g: 255,
b: 0 b: 0,
}, },
Speed::Low Speed::Low,
)); ));
seq.push(key.clone()); seq.push(key.clone());
@@ -161,7 +161,7 @@ impl Default for ConfigAura {
LedCode::F, LedCode::F,
Colour { r: 255, g: 0, b: 0 }, Colour { r: 255, g: 0, b: 0 },
Colour { r: 255, g: 0, b: 0 }, Colour { r: 255, g: 0, b: 0 },
Speed::High Speed::High,
)); ));
seq.push(key); seq.push(key);
@@ -176,13 +176,13 @@ impl Default for ConfigAura {
LedCode::N9, LedCode::N9,
Colour { r: 0, g: 0, b: 255 }, Colour { r: 0, g: 0, b: 255 },
80, 80,
40 40,
)); ));
seq.push(key); seq.push(key);
Self { Self {
name: "aura-default".to_owned(), name: "aura-default".to_owned(),
aura: seq aura: seq,
} }
} }
} }
@@ -209,14 +209,14 @@ pub struct ConfigBase {
/// Name of active anime config file in the user config directory /// Name of active anime config file in the user config directory
pub active_anime: Option<String>, pub active_anime: Option<String>,
/// Name of active aura config file in the user config directory /// Name of active aura config file in the user config directory
pub active_aura: Option<String> pub active_aura: Option<String>,
} }
impl StdConfig for ConfigBase { impl StdConfig for ConfigBase {
fn new() -> Self { fn new() -> Self {
Self { Self {
active_anime: Some("anime-default".to_owned()), active_anime: Some("anime-default".to_owned()),
active_aura: Some("aura-default".to_owned()) active_aura: Some("aura-default".to_owned()),
} }
} }
+23 -20
View File
@@ -9,7 +9,7 @@ use rog_anime::error::AnimeError;
use rog_anime::{ActionData, ActionLoader, AnimTime, Fade, Sequences, Vec2}; use rog_anime::{ActionData, ActionLoader, AnimTime, Fade, Sequences, Vec2};
use rog_dbus::zbus_anime::AnimeProxyBlocking; use rog_dbus::zbus_anime::AnimeProxyBlocking;
use ron::ser::PrettyConfig; use ron::ser::PrettyConfig;
use serde::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use zbus::interface; use zbus::interface;
use zbus::zvariant::{ObjectPath, Type}; use zbus::zvariant::{ObjectPath, Type};
@@ -25,7 +25,7 @@ pub struct Timer {
/// Used only for `TimeType::Timer`, milliseonds to fade the image in for /// Used only for `TimeType::Timer`, milliseonds to fade the image in for
fade_in: u64, fade_in: u64,
/// Used only for `TimeType::Timer`, milliseonds to fade the image out for /// Used only for `TimeType::Timer`, milliseonds to fade the image out for
fade_out: u64 fade_out: u64,
} }
impl From<Timer> for AnimTime { impl From<Timer> for AnimTime {
@@ -46,7 +46,7 @@ impl From<Timer> for AnimTime {
} }
} }
TimeType::Count => AnimTime::Count(time.count as u32), TimeType::Count => AnimTime::Count(time.count as u32),
TimeType::Infinite => AnimTime::Infinite TimeType::Infinite => AnimTime::Infinite,
} }
} }
} }
@@ -55,7 +55,7 @@ impl From<Timer> for AnimTime {
pub enum TimeType { pub enum TimeType {
Timer, Timer,
Count, Count,
Infinite Infinite,
} }
/// The inner object exists to allow the zbus proxy to share it with a runner /// The inner object exists to allow the zbus proxy to share it with a runner
@@ -63,25 +63,25 @@ pub enum TimeType {
pub struct CtrlAnimeInner<'a> { pub struct CtrlAnimeInner<'a> {
sequences: Sequences, sequences: Sequences,
client: AnimeProxyBlocking<'a>, client: AnimeProxyBlocking<'a>,
do_early_return: Arc<AtomicBool> do_early_return: Arc<AtomicBool>,
} }
impl CtrlAnimeInner<'static> { impl<'a> CtrlAnimeInner<'static> {
pub fn new( pub fn new(
sequences: Sequences, sequences: Sequences,
client: AnimeProxyBlocking<'static>, client: AnimeProxyBlocking<'static>,
do_early_return: Arc<AtomicBool> do_early_return: Arc<AtomicBool>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
Ok(Self { Ok(Self {
sequences, sequences,
client, client,
do_early_return do_early_return,
}) })
} }
/// To be called on each main loop iteration to pump out commands to the /// To be called on each main loop iteration to pump out commands to the
/// anime /// anime
pub fn run(&self) -> Result<(), Error> { pub fn run(&'a self) -> Result<(), Error> {
if self.do_early_return.load(Ordering::SeqCst) { if self.do_early_return.load(Ordering::SeqCst) {
return Ok(()); return Ok(());
} }
@@ -130,7 +130,7 @@ pub struct CtrlAnime<'a> {
client: AnimeProxyBlocking<'a>, client: AnimeProxyBlocking<'a>,
inner: Arc<Mutex<CtrlAnimeInner<'a>>>, inner: Arc<Mutex<CtrlAnimeInner<'a>>>,
/// Must be the same Atomic as in CtrlAnimeInner /// Must be the same Atomic as in CtrlAnimeInner
inner_early_return: Arc<AtomicBool> inner_early_return: Arc<AtomicBool>,
} }
impl CtrlAnime<'static> { impl CtrlAnime<'static> {
@@ -138,20 +138,23 @@ impl CtrlAnime<'static> {
config: Arc<Mutex<ConfigAnime>>, config: Arc<Mutex<ConfigAnime>>,
inner: Arc<Mutex<CtrlAnimeInner<'static>>>, inner: Arc<Mutex<CtrlAnimeInner<'static>>>,
client: AnimeProxyBlocking<'static>, client: AnimeProxyBlocking<'static>,
inner_early_return: Arc<AtomicBool> inner_early_return: Arc<AtomicBool>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
Ok(CtrlAnime { Ok(CtrlAnime {
config, config,
client, client,
inner, inner,
inner_early_return inner_early_return,
}) })
} }
pub async fn add_to_server(self, server: &mut zbus::Connection) { pub async fn add_to_server(self, server: &mut zbus::Connection) {
server server
.object_server() .object_server()
.at(&ObjectPath::from_str_unchecked("/xyz/ljones/Anime"), self) .at(
&ObjectPath::from_str_unchecked("/org/asuslinux/Anime"),
self,
)
.await .await
.map_err(|err| { .map_err(|err| {
println!("CtrlAnime: add_to_server {}", err); println!("CtrlAnime: add_to_server {}", err);
@@ -167,14 +170,14 @@ impl CtrlAnime<'static> {
// - Do actions // - Do actions
// - Write config if required // - Write config if required
// - Unset inner_early_return // - Unset inner_early_return
#[interface(name = "xyz.ljones.Asusd")] #[interface(name = "org.asuslinux.Daemon")]
impl CtrlAnime<'static> { impl CtrlAnime<'static> {
pub fn insert_asus_gif( pub fn insert_asus_gif(
&mut self, &mut self,
index: u32, index: u32,
file: &str, file: &str,
time: Timer, time: Timer,
brightness: f32 brightness: f32,
) -> zbus::fdo::Result<String> { ) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() { if let Ok(mut config) = self.config.try_lock() {
let time: AnimTime = time.into(); let time: AnimTime = time.into();
@@ -182,7 +185,7 @@ impl CtrlAnime<'static> {
let action = ActionLoader::AsusAnimation { let action = ActionLoader::AsusAnimation {
file: file.into(), file: file.into(),
brightness, brightness,
time time,
}; };
// Must make the inner run loop return early // Must make the inner run loop return early
@@ -216,7 +219,7 @@ impl CtrlAnime<'static> {
angle: f32, angle: f32,
xy: (f32, f32), xy: (f32, f32),
time: Timer, time: Timer,
brightness: f32 brightness: f32,
) -> zbus::fdo::Result<String> { ) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() { if let Ok(mut config) = self.config.try_lock() {
let time: AnimTime = time.into(); let time: AnimTime = time.into();
@@ -228,7 +231,7 @@ impl CtrlAnime<'static> {
angle, angle,
translation, translation,
brightness, brightness,
time time,
}; };
// Must make the inner run loop return early // Must make the inner run loop return early
@@ -261,7 +264,7 @@ impl CtrlAnime<'static> {
angle: f32, angle: f32,
xy: (f32, f32), xy: (f32, f32),
time: Timer, time: Timer,
brightness: f32 brightness: f32,
) -> zbus::fdo::Result<String> { ) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() { if let Ok(mut config) = self.config.try_lock() {
let file = Path::new(&file); let file = Path::new(&file);
@@ -272,7 +275,7 @@ impl CtrlAnime<'static> {
angle, angle,
translation: Vec2::new(xy.0, xy.1), translation: Vec2::new(xy.0, xy.1),
brightness, brightness,
time time,
}; };
// Must make the inner run loop return early // Must make the inner run loop return early
+12 -7
View File
@@ -11,7 +11,8 @@ use rog_aura::aura_detection::LedSupportData;
use rog_aura::keyboard::KeyLayout; use rog_aura::keyboard::KeyLayout;
use rog_dbus::zbus_anime::AnimeProxyBlocking; use rog_dbus::zbus_anime::AnimeProxyBlocking;
use rog_dbus::zbus_aura::AuraProxyBlocking; use rog_dbus::zbus_aura::AuraProxyBlocking;
use rog_dbus::{list_iface_blocking, DBUS_NAME}; use rog_dbus::zbus_platform::PlatformProxyBlocking;
use rog_dbus::DBUS_NAME;
use smol::Executor; use smol::Executor;
use zbus::Connection; use zbus::Connection;
@@ -35,16 +36,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("rog-platform v{}", rog_platform::VERSION); println!("rog-platform v{}", rog_platform::VERSION);
let conn = zbus::blocking::Connection::system().unwrap(); let conn = zbus::blocking::Connection::system().unwrap();
let platform_proxy = PlatformProxyBlocking::new(&conn).unwrap();
let supported = list_iface_blocking()?; let supported = platform_proxy
.supported_interfaces()
.unwrap_or_default()
.contains(&"Anime".to_string());
let config = ConfigBase::new().load(); let config = ConfigBase::new().load();
let executor = Executor::new(); let executor = Executor::new();
let early_return = Arc::new(AtomicBool::new(false)); let early_return = Arc::new(AtomicBool::new(false));
// Set up the anime data and run loop/thread // Set up the anime data and run loop/thread
if supported.contains(&"xyz.ljones.Anime".to_string()) { if supported {
if let Some(cfg) = config.active_anime { if let Some(cfg) = config.active_anime {
let anime_type = get_anime_type(); let anime_type = get_anime_type()?;
let anime_config = ConfigAnime::new().set_name(cfg).load(); let anime_config = ConfigAnime::new().set_name(cfg).load();
let anime = anime_config.create(anime_type)?; let anime = anime_config.create(anime_type)?;
let anime_config = Arc::new(Mutex::new(anime_config)); let anime_config = Arc::new(Mutex::new(anime_config));
@@ -61,16 +66,16 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
CtrlAnimeInner::new( CtrlAnimeInner::new(
anime, anime,
anime_proxy_blocking.clone(), anime_proxy_blocking.clone(),
early_return.clone() early_return.clone(),
) )
.unwrap() .unwrap(),
)); ));
// Need new client object for dbus control part // Need new client object for dbus control part
let anime_control = CtrlAnime::new( let anime_control = CtrlAnime::new(
anime_config, anime_config,
inner.clone(), inner.clone(),
anime_proxy_blocking, anime_proxy_blocking,
early_return early_return,
) )
.unwrap(); .unwrap();
anime_control.add_to_server(&mut connection).await; anime_control.add_to_server(&mut connection).await;
+2 -2
View File
@@ -8,7 +8,7 @@ pub enum Error {
ConfigLoadFail, ConfigLoadFail,
ConfigLockFail, ConfigLockFail,
XdgVars, XdgVars,
Anime(AnimeError) Anime(AnimeError),
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@@ -19,7 +19,7 @@ impl fmt::Display for Error {
Error::ConfigLoadFail => write!(f, "Failed to load user config"), Error::ConfigLoadFail => write!(f, "Failed to load user config"),
Error::ConfigLockFail => write!(f, "Failed to lock user config"), Error::ConfigLockFail => write!(f, "Failed to lock user config"),
Error::XdgVars => write!(f, "XDG environment vars appear unset"), Error::XdgVars => write!(f, "XDG environment vars appear unset"),
Error::Anime(err) => write!(f, "Anime error: {}", err) Error::Anime(err) => write!(f, "Anime error: {}", err),
} }
} }
} }
+10 -7
View File
@@ -1,8 +1,8 @@
//! # `DBus` interface proxy for: `xyz.ljones.Asusd` //! # `DBus` interface proxy for: `org.asuslinux.Daemon`
//! //!
//! This code was generated by `zbus-xmlgen` `1.0.0` from `DBus` introspection //! This code was generated by `zbus-xmlgen` `1.0.0` from `DBus` introspection
//! data. Source: `Interface '/xyz/ljones/Anime' from service //! data. Source: `Interface '/org/asuslinux/Anime' from service
//! 'xyz.ljones.Asusd' on session bus`. //! 'org.asuslinux.Daemon' on session bus`.
//! //!
//! You may prefer to adapt it, instead of using it verbatim. //! You may prefer to adapt it, instead of using it verbatim.
//! //!
@@ -23,7 +23,10 @@
use zbus::proxy; use zbus::proxy;
#[proxy(interface = "xyz.ljones.Asusd", default_path = "/xyz/ljones/Anime")] #[proxy(
interface = "org.asuslinux.Daemon",
default_path = "/org/asuslinux/Anime"
)]
trait Daemon { trait Daemon {
/// InsertAsusGif method /// InsertAsusGif method
fn insert_asus_gif( fn insert_asus_gif(
@@ -32,7 +35,7 @@ trait Daemon {
file: &str, file: &str,
time: u32, time: u32,
count: u32, count: u32,
brightness: f64 brightness: f64,
) -> zbus::Result<String>; ) -> zbus::Result<String>;
/// InsertImage method /// InsertImage method
@@ -43,7 +46,7 @@ trait Daemon {
scale: f64, scale: f64,
angle: f64, angle: f64,
xy: &(f64, f64), xy: &(f64, f64),
brightness: f64 brightness: f64,
) -> zbus::Result<String>; ) -> zbus::Result<String>;
/// InsertImageGif method /// InsertImageGif method
@@ -56,7 +59,7 @@ trait Daemon {
xy: &(f64, f64), xy: &(f64, f64),
time: u32, time: u32,
count: u32, count: u32,
brightness: f64 brightness: f64,
) -> zbus::Result<String>; ) -> zbus::Result<String>;
/// InsertPause method /// InsertPause method
+4 -2
View File
@@ -18,7 +18,6 @@ config-traits = { path = "../config-traits" }
rog_anime = { path = "../rog-anime", features = ["dbus"] } rog_anime = { path = "../rog-anime", features = ["dbus"] }
rog_slash = { path = "../rog-slash", features = ["dbus"] } rog_slash = { path = "../rog-slash", features = ["dbus"] }
rog_aura = { path = "../rog-aura", features = ["dbus"] } rog_aura = { path = "../rog-aura", features = ["dbus"] }
rog_scsi = { path = "../rog-scsi", features = ["dbus"] }
rog_platform = { path = "../rog-platform" } rog_platform = { path = "../rog-platform" }
rog_profiles = { path = "../rog-profiles" } rog_profiles = { path = "../rog-profiles" }
dmi_id = { path = "../dmi-id" } dmi_id = { path = "../dmi-id" }
@@ -34,14 +33,17 @@ tokio.workspace = true
log.workspace = true log.workspace = true
env_logger.workspace = true env_logger.workspace = true
futures-util.workspace = true
zbus.workspace = true zbus.workspace = true
logind-zbus.workspace = true logind-zbus.workspace = true
# serialisation # serialisation
serde.workspace = true serde.workspace = true
serde_derive.workspace = true
concat-idents.workspace = true concat-idents.workspace = true
[dev-dependencies] [dev-dependencies]
cargo-husky.workspace = true cargo-husky.workspace = true
[package.metadata.cargo-machete]
ignored = ["serde"]
-425
View File
@@ -1,425 +0,0 @@
use std::sync::Arc;
use config_traits::StdConfig;
use futures_util::lock::Mutex;
use log::{debug, error, info};
use rog_platform::asus_armoury::{AttrValue, Attribute, FirmwareAttribute, FirmwareAttributes};
use rog_platform::platform::{PlatformProfile, RogPlatform};
use rog_platform::power::AsusPower;
use serde::{Deserialize, Serialize};
use zbus::object_server::SignalEmitter;
use zbus::zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Type, Value};
use zbus::{fdo, interface, Connection};
use crate::config::Config;
use crate::error::RogError;
use crate::{Reloadable, ASUS_ZBUS_PATH};
const MOD_NAME: &str = "asus_armoury";
#[derive(Debug, Default, Clone, Deserialize, Serialize, Type, Value, OwnedValue)]
pub struct PossibleValues {
strings: Vec<String>,
nums: Vec<i32>
}
fn dbus_path_for_attr(attr_name: &str) -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{attr_name}")).into()
}
#[derive(Clone)]
pub struct AsusArmouryAttribute {
attr: Attribute,
config: Arc<Mutex<Config>>,
/// platform control required here for access to PPD or Throttle profile
platform: RogPlatform,
power: AsusPower
}
impl AsusArmouryAttribute {
pub fn new(
attr: Attribute,
platform: RogPlatform,
power: AsusPower,
config: Arc<Mutex<Config>>
) -> Self {
Self {
attr,
config,
platform,
power
}
}
pub async fn move_to_zbus(self, connection: &Connection) -> Result<(), RogError> {
let path = dbus_path_for_attr(self.attr.name());
connection
.object_server()
.at(path.clone(), self)
.await
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
.ok();
Ok(())
}
async fn watch_and_notify(
&mut self,
signal_ctxt: SignalEmitter<'static>
) -> Result<(), RogError> {
use futures_util::StreamExt;
let name = self.name();
macro_rules! watch_value_notify {
($attr_str:expr, $fn_prop_changed:ident) => {
match self.attr.get_watcher($attr_str) {
Ok(watch) => {
let name = <&str>::from(name);
let ctrl = self.clone();
let sig = signal_ctxt.clone();
tokio::spawn(async move {
let mut buffer = [0; 32];
watch
.into_event_stream(&mut buffer)
.unwrap()
.for_each(|_| async {
debug!("{} changed", name);
ctrl.$fn_prop_changed(&sig).await.ok();
})
.await;
});
}
Err(e) => info!(
"inotify watch failed: {}. You can ignore this if your device does not \
support the feature",
e
)
}
};
}
// "current_value", "default_value", "min_value", "max_value"
watch_value_notify!("current_value", current_value_changed);
watch_value_notify!("default_value", default_value_changed);
watch_value_notify!("min_value", min_value_changed);
watch_value_notify!("max_value", max_value_changed);
Ok(())
}
}
impl crate::Reloadable for AsusArmouryAttribute {
async fn reload(&mut self) -> Result<(), RogError> {
info!("Reloading {}", self.attr.name());
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self
.power
.get_online()
.map_err(|e| {
error!("Could not get power status: {e:?}");
e
})
.unwrap_or_default();
let config = if power_plugged == 1 {
&self.config.lock().await.ac_profile_tunings
} else {
&self.config.lock().await.dc_profile_tunings
};
if let Some(tuning) = config.get(&profile) {
if tuning.enabled {
if let Some(tune) = tuning.group.get(&self.name()) {
self.attr
.set_current_value(&AttrValue::Integer(*tune))
.map_err(|e| {
error!("Could not set {} value: {e:?}", self.attr.name());
self.attr.base_path_exists();
e
})?;
info!("Set {} to {:?}", self.attr.name(), tune);
}
}
}
Ok(())
}
}
/// If return is `-1` on a property then there is avilable value for that
/// property
#[interface(name = "xyz.ljones.AsusArmoury")]
impl AsusArmouryAttribute {
#[zbus(property)]
fn name(&self) -> FirmwareAttribute {
self.attr.name().into()
}
#[zbus(property)]
async fn available_attrs(&self) -> Vec<String> {
let mut attrs = Vec::new();
if !matches!(self.attr.default_value(), AttrValue::None) {
attrs.push("default_value".to_string());
}
if !matches!(self.attr.min_value(), AttrValue::None) {
attrs.push("min_value".to_string());
}
if !matches!(self.attr.max_value(), AttrValue::None) {
attrs.push("max_value".to_string());
}
if !matches!(self.attr.scalar_increment(), AttrValue::None) {
attrs.push("scalar_increment".to_string());
}
if !matches!(self.attr.possible_values(), AttrValue::None) {
attrs.push("possible_values".to_string());
}
// TODO: Don't unwrap, use error
if let Ok(value) = self.attr.current_value().map_err(|e| {
error!("Failed to read: {e:?}");
e
}) {
if !matches!(value, AttrValue::None) {
attrs.push("current_value".to_string());
}
}
attrs
}
/// If return is `-1` then there is no default value
#[zbus(property)]
async fn default_value(&self) -> i32 {
match self.attr.default_value() {
AttrValue::Integer(i) => *i,
_ => -1
}
}
async fn restore_default(&self) -> fdo::Result<()> {
self.attr.restore_default()?;
if self.name().is_ppt() {
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self
.power
.get_online()
.map_err(|e| {
error!("Could not get power status: {e:?}");
e
})
.unwrap_or_default();
let mut config = self.config.lock().await;
let tuning = config.select_tunings(power_plugged == 1, profile);
if let Some(tune) = tuning.group.get_mut(&self.name()) {
if let AttrValue::Integer(i) = self.attr.default_value() {
*tune = *i;
}
}
if tuning.enabled {
self.attr
.set_current_value(self.attr.default_value())
.map_err(|e| {
error!("Could not set value: {e:?}");
e
})?;
}
config.write();
}
Ok(())
}
#[zbus(property)]
async fn min_value(&self) -> i32 {
match self.attr.min_value() {
AttrValue::Integer(i) => *i,
_ => -1
}
}
#[zbus(property)]
async fn max_value(&self) -> i32 {
match self.attr.max_value() {
AttrValue::Integer(i) => *i,
_ => -1
}
}
#[zbus(property)]
async fn scalar_increment(&self) -> i32 {
match self.attr.scalar_increment() {
AttrValue::Integer(i) => *i,
_ => -1
}
}
#[zbus(property)]
async fn possible_values(&self) -> Vec<i32> {
match self.attr.possible_values() {
AttrValue::EnumInt(i) => i.clone(),
_ => Vec::default()
}
}
#[zbus(property)]
async fn current_value(&self) -> fdo::Result<i32> {
if self.name().is_ppt() {
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self
.power
.get_online()
.map_err(|e| {
error!("Could not get power status: {e:?}");
e
})
.unwrap_or_default();
let mut config = self.config.lock().await;
let tuning = config.select_tunings(power_plugged == 1, profile);
if let Some(tune) = tuning.group.get(&self.name()) {
return Ok(*tune);
} else if let AttrValue::Integer(i) = self.attr.default_value() {
return Ok(*i);
}
return Err(fdo::Error::Failed(
"Could not read current value".to_string()
));
}
if let Ok(AttrValue::Integer(i)) = self.attr.current_value() {
return Ok(i);
}
Err(fdo::Error::Failed(
"Could not read current value".to_string()
))
}
#[zbus(property)]
async fn set_current_value(&mut self, value: i32) -> fdo::Result<()> {
if self.name().is_ppt() {
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self
.power
.get_online()
.map_err(|e| {
error!("Could not get power status: {e:?}");
e
})
.unwrap_or_default();
let mut config = self.config.lock().await;
let tuning = config.select_tunings(power_plugged == 1, profile);
if let Some(tune) = tuning.group.get_mut(&self.name()) {
*tune = value;
} else {
tuning.group.insert(self.name(), value);
debug!("Store tuning config for {} = {:?}", self.attr.name(), value);
}
if tuning.enabled {
self.attr
.set_current_value(&AttrValue::Integer(value))
.map_err(|e| {
error!("Could not set value: {e:?}");
e
})?;
}
} else {
self.attr
.set_current_value(&AttrValue::Integer(value))
.map_err(|e| {
error!("Could not set value: {e:?}");
e
})?;
let has_attr = self
.config
.lock()
.await
.armoury_settings
.contains_key(&self.name());
if has_attr {
if let Some(setting) = self
.config
.lock()
.await
.armoury_settings
.get_mut(&self.name())
{
*setting = value
}
} else {
debug!("Adding config for {}", self.attr.name());
self.config
.lock()
.await
.armoury_settings
.insert(self.name(), value);
debug!("Set config for {} = {:?}", self.attr.name(), value);
}
}
self.config.lock().await.write();
Ok(())
}
}
pub async fn start_attributes_zbus(
conn: &Connection,
platform: RogPlatform,
power: AsusPower,
attributes: FirmwareAttributes,
config: Arc<Mutex<Config>>
) -> Result<(), RogError> {
for attr in attributes.attributes() {
let mut attr = AsusArmouryAttribute::new(
attr.clone(),
platform.clone(),
power.clone(),
config.clone()
);
attr.reload().await?;
let path = dbus_path_for_attr(attr.attr.name());
let sig = zbus::object_server::SignalEmitter::new(conn, path)?;
attr.watch_and_notify(sig).await?;
attr.move_to_zbus(conn).await?;
}
Ok(())
}
pub async fn set_config_or_default(
attrs: &FirmwareAttributes,
config: &mut Config,
power_plugged: bool,
profile: PlatformProfile
) {
for attr in attrs.attributes().iter() {
let name: FirmwareAttribute = attr.name().into();
if name.is_ppt() {
let tuning = config.select_tunings(power_plugged, profile);
if !tuning.enabled {
debug!("Tuning group is not enabled, skipping");
return;
}
if let Some(tune) = tuning.group.get(&name) {
attr.set_current_value(&AttrValue::Integer(*tune))
.map_err(|e| {
error!("Failed to set {}: {e}", <&str>::from(name));
})
.ok();
} else {
let default = attr.default_value();
attr.set_current_value(default)
.map_err(|e| {
error!("Failed to set {}: {e}", <&str>::from(name));
})
.ok();
if let AttrValue::Integer(i) = default {
tuning.group.insert(name, *i);
info!(
"Set default tuning config for {} = {:?}",
<&str>::from(name),
i
);
// config.write();
}
}
}
}
}
-182
View File
@@ -1,182 +0,0 @@
use std::time::Duration;
use config_traits::{StdConfig, StdConfigLoad};
use rog_anime::error::AnimeError;
use rog_anime::usb::Brightness;
use rog_anime::{
ActionData, ActionLoader, AnimTime, Animations, AnimeType, DeviceState, Fade, Vec2
};
use serde::{Deserialize, Serialize};
const CONFIG_FILE: &str = "anime.ron";
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
pub struct AniMeConfigCached {
pub system: Vec<ActionData>,
pub boot: Vec<ActionData>,
pub wake: Vec<ActionData>,
pub shutdown: Vec<ActionData>
}
impl AniMeConfigCached {
pub fn init_from_config(
&mut self,
config: &AniMeConfig,
anime_type: AnimeType
) -> Result<(), AnimeError> {
let mut sys = Vec::with_capacity(config.system.len());
for ani in &config.system {
sys.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.system = sys;
let mut boot = Vec::with_capacity(config.boot.len());
for ani in &config.boot {
boot.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.boot = boot;
let mut wake = Vec::with_capacity(config.wake.len());
for ani in &config.wake {
wake.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.wake = wake;
let mut shutdown = Vec::with_capacity(config.shutdown.len());
for ani in &config.shutdown {
shutdown.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.shutdown = shutdown;
Ok(())
}
}
/// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct AniMeConfig {
#[serde(skip)]
pub anime_type: AnimeType,
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>,
// pub brightness: f32,
pub display_enabled: bool,
pub display_brightness: Brightness,
pub builtin_anims_enabled: bool,
pub off_when_unplugged: bool,
pub off_when_suspended: bool,
pub off_when_lid_closed: bool,
pub brightness_on_battery: Brightness,
pub builtin_anims: Animations
}
impl Default for AniMeConfig {
fn default() -> Self {
AniMeConfig {
anime_type: AnimeType::GA402,
system: Vec::new(),
boot: Vec::new(),
wake: Vec::new(),
shutdown: Vec::new(),
// brightness: 1.0,
display_enabled: true,
display_brightness: Brightness::Med,
builtin_anims_enabled: true,
off_when_unplugged: true,
off_when_suspended: true,
off_when_lid_closed: true,
brightness_on_battery: Brightness::Low,
builtin_anims: Animations::default()
}
}
}
impl StdConfig for AniMeConfig {
fn new() -> Self {
Self::create_default()
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
fn config_dir() -> std::path::PathBuf {
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
}
}
impl StdConfigLoad for AniMeConfig {}
impl From<&AniMeConfig> for DeviceState {
fn from(config: &AniMeConfig) -> Self {
DeviceState {
display_enabled: config.display_enabled,
display_brightness: config.display_brightness,
builtin_anims_enabled: config.builtin_anims_enabled,
builtin_anims: config.builtin_anims,
off_when_unplugged: config.off_when_unplugged,
off_when_suspended: config.off_when_suspended,
off_when_lid_closed: config.off_when_lid_closed,
brightness_on_battery: config.brightness_on_battery
}
}
}
impl AniMeConfig {
// fn clamp_config_brightness(mut config: &mut AnimeConfig) {
// if config.brightness < 0.0 || config.brightness > 1.0 {
// warn!(
// "Clamped brightness to [0.0 ; 1.0], was {}",
// config.brightness
// );
// config.brightness = f32::max(0.0, f32::min(1.0, config.brightness));
// }
// }
fn create_default() -> Self {
// create a default config here
AniMeConfig {
system: vec![],
boot: vec![
ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
brightness: 1.0,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(2)),
Duration::from_secs(2)
))
},
],
wake: vec![
ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
brightness: 1.0,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(2)),
Duration::from_secs(2)
))
},
],
shutdown: vec![
ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
scale: 0.9,
angle: 0.0,
translation: Vec2::new(3.0, 2.0),
brightness: 1.0,
time: AnimTime::Infinite
},
],
..Default::default()
}
}
}
-248
View File
@@ -1,248 +0,0 @@
pub mod config;
/// Implements `CtrlTask`, Reloadable, `ZbusRun`
pub mod trait_impls;
use std::convert::TryFrom;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::sleep;
use config_traits::StdConfig;
use futures_util::lock::Mutex;
use log::{debug, error, info, warn};
use rog_anime::usb::{
pkt_flush, pkt_set_brightness, pkt_set_enable_display, pkt_set_enable_powersave_anim,
pkts_for_init, Brightness
};
use rog_anime::{ActionData, AnimeDataBuffer, AnimePacketType};
use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw;
use self::config::{AniMeConfig, AniMeConfigCached};
use crate::error::RogError;
#[derive(Debug, Clone)]
pub struct AniMe {
hid: Option<Arc<Mutex<HidRaw>>>,
usb: Option<Arc<Mutex<USBRaw>>>,
config: Arc<Mutex<AniMeConfig>>,
cache: AniMeConfigCached,
// set to force thread to exit
thread_exit: Arc<AtomicBool>,
// Set to false when the thread exits
thread_running: Arc<AtomicBool>
}
impl AniMe {
pub fn new(
hid: Option<Arc<Mutex<HidRaw>>>,
usb: Option<Arc<Mutex<USBRaw>>>,
config: Arc<Mutex<AniMeConfig>>
) -> Self {
Self {
hid,
usb,
config,
cache: AniMeConfigCached::default(),
thread_exit: Arc::new(AtomicBool::new(false)),
thread_running: Arc::new(AtomicBool::new(false))
}
}
/// Will fail if something is already holding the config lock
async fn do_init_cache(&mut self) {
if let Some(mut config) = self.config.try_lock() {
if let Err(e) = self.cache.init_from_config(&config, config.anime_type) {
error!(
"Trying to cache the Anime Config failed, will reset to default config: {e:?}"
);
config.rename_file_old();
*config = AniMeConfig::new();
config.write();
} else {
debug!("Initialised AniMe cache");
}
} else {
error!("AniMe Matrix could not init cache")
}
}
/// Initialise the device if required.
pub async fn do_initialization(&mut self) -> Result<(), RogError> {
self.do_init_cache().await;
let pkts = pkts_for_init();
self.write_bytes(&pkts[0]).await?;
self.write_bytes(&pkts[1]).await?;
debug!("Succesfully initialised AniMe matrix display");
Ok(())
}
pub async fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
if let Some(hid) = &self.hid {
hid.lock().await.write_bytes(message)?;
} else if let Some(usb) = &self.usb {
usb.lock().await.write_bytes(message)?;
}
Ok(())
}
/// Write only a data packet. This will modify the leds brightness using the
/// global brightness set in config.
async fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) -> Result<(), RogError> {
for led in buffer.data_mut().iter_mut() {
let mut bright = *led as f32;
if bright > 254.0 {
bright = 254.0;
}
*led = bright as u8;
}
let data = AnimePacketType::try_from(buffer)?;
for row in &data {
self.write_bytes(row).await?;
}
self.write_bytes(&pkt_flush()).await
}
pub async fn set_builtins_enabled(
&self,
enabled: bool,
bright: Brightness
) -> Result<(), RogError> {
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
.await?;
self.write_bytes(&pkt_set_enable_display(enabled)).await?;
self.write_bytes(&pkt_set_brightness(bright)).await?;
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
.await
}
/// Start an action thread. This is classed as a singleton and there should
/// be only one running - so the thread uses atomics to signal run/exit.
///
/// Because this also writes to the usb device, other write tries (display
/// only) *must* get the mutex lock and set the `thread_exit` atomic.
async fn run_thread(&self, actions: Vec<ActionData>, mut once: bool) {
if actions.is_empty() {
warn!("AniMe system actions was empty");
return;
}
self.write_bytes(&pkt_set_enable_powersave_anim(false))
.await
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
let thread_exit = self.thread_exit.clone();
let thread_running = self.thread_running.clone();
let anime_type = self.config.lock().await.anime_type;
let inner = self.clone();
// Loop rules:
// - Lock the mutex **only when required**. That is, the lock must be held for
// the shortest duration possible.
// - An AtomicBool used for thread exit should be checked in every loop,
// including nested
// The only reason for this outer thread is to prevent blocking while waiting
// for the next spawned thread to exit
// TODO: turn this in to async task (maybe? COuld still risk blocking main
// thread)
tokio::spawn(async move {
info!("AniMe new system thread started");
// First two loops are to ensure we *do* aquire a lock on the mutex
// The reason the loop is required is because the USB writes can block
// for up to 10ms. We can't fail to get the atomics.
while thread_running.load(Ordering::SeqCst) {
// Make any running loop exit first
thread_exit.store(true, Ordering::SeqCst);
}
info!("AniMe no previous system thread running (now)");
thread_exit.store(false, Ordering::SeqCst);
thread_running.store(true, Ordering::SeqCst);
'main: loop {
for action in &actions {
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
match action {
ActionData::Animation(frames) => {
// TODO: sort all this out
rog_anime::run_animation(frames, &|frame| {
if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: animation sub-loop was asked to exit");
return Ok(true); // Do safe exit
}
let inner = inner.clone();
tokio::task::spawn(async move {
inner
.write_data_buffer(frame)
.await
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
});
Ok(false) // Don't exit yet
});
if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: sub-loop exited and main loop exiting now");
break 'main;
}
}
ActionData::Image(image) => {
once = false;
inner
.write_data_buffer(image.as_ref().clone())
.await
.map_err(|e| error!("{}", e))
.ok();
}
ActionData::Pause(duration) => sleep(*duration),
ActionData::AudioEq
| ActionData::SystemInfo
| ActionData::TimeDate
| ActionData::Matrix => {}
}
}
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
if once || actions.is_empty() {
break 'main;
}
}
// Clear the display on exit
if let Ok(data) =
AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()])
.map_err(|e| error!("{}", e))
{
inner
.write_data_buffer(data)
.await
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
}
// A write can block for many milliseconds so lets not hold the config lock for
// the same period
let enabled = inner.config.lock().await.builtin_anims_enabled;
inner
.write_bytes(&pkt_set_enable_powersave_anim(enabled))
.await
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
// Loop ended, set the atmonics
thread_running.store(false, Ordering::SeqCst);
info!("AniMe system thread exited");
})
.await
.map(|err| info!("AniMe system thread: {:?}", err))
.ok();
}
}
-229
View File
@@ -1,229 +0,0 @@
use std::sync::Arc;
use config::AuraConfig;
use config_traits::StdConfig;
use futures_util::lock::{Mutex, MutexGuard};
use log::info;
use rog_aura::keyboard::{AuraLaptopUsbPackets, LedUsbPackets};
use rog_aura::usb::{AURA_LAPTOP_LED_APPLY, AURA_LAPTOP_LED_SET};
use rog_aura::{AuraDeviceType, AuraEffect, LedBrightness, PowerZones, AURA_LAPTOP_LED_MSG_LEN};
use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardBacklight;
use crate::error::RogError;
pub mod config;
pub mod trait_impls;
#[derive(Debug, Clone)]
pub struct Aura {
pub hid: Option<Arc<Mutex<HidRaw>>>,
pub backlight: Option<Arc<Mutex<KeyboardBacklight>>>,
pub config: Arc<Mutex<AuraConfig>>
}
impl Aura {
/// Initialise the device if required.
pub async fn do_initialization(&self) -> Result<(), RogError> {
Ok(())
}
pub async fn lock_config(&self) -> MutexGuard<AuraConfig> {
self.config.lock().await
}
/// Will lock the internal config and update. If anything else has locked
/// this in scope then a deadlock can occur.
pub async fn update_config(&self) -> Result<(), RogError> {
let mut config = self.config.lock().await;
let bright = if let Some(bl) = self.backlight.as_ref() {
bl.lock().await.get_brightness().unwrap_or_default()
} else {
config.brightness.into()
};
config.read();
config.brightness = bright.into();
config.write();
Ok(())
}
pub async fn write_current_config_mode(&self, config: &mut AuraConfig) -> Result<(), RogError> {
if config.multizone_on {
let mode = config.current_mode;
let mut create = false;
// There is no multizone config for this mode so create one here
// using the colours of rainbow if it exists, or first available
// mode, or random
if config.multizone.is_none() {
create = true;
} else if let Some(multizones) = config.multizone.as_ref() {
if !multizones.contains_key(&mode) {
create = true;
}
}
if create {
info!("No user-set config for zone founding, attempting a default");
config.create_multizone_default()?;
}
if let Some(multizones) = config.multizone.as_mut() {
if let Some(set) = multizones.get(&mode) {
for mode in set.clone() {
self.write_effect_and_apply(config.led_type, &mode).await?;
}
}
}
} else {
let mode = config.current_mode;
if let Some(effect) = config.builtins.get(&mode).cloned() {
self.write_effect_and_apply(config.led_type, &effect)
.await?;
}
}
Ok(())
}
/// Write the AuraEffect to the device. Will lock `backlight` or `hid`.
///
/// If per-key or software-mode is active it must be marked as disabled in
/// config.
pub async fn write_effect_and_apply(
&self,
dev_type: AuraDeviceType,
mode: &AuraEffect
) -> Result<(), RogError> {
if matches!(dev_type, AuraDeviceType::LaptopKeyboardTuf) {
if let Some(platform) = &self.backlight {
let buf = [
1, mode.mode as u8, mode.colour1.r, mode.colour1.g, mode.colour1.b,
mode.speed as u8
];
platform.lock().await.set_kbd_rgb_mode(&buf)?;
}
} else if let Some(hid_raw) = &self.hid {
let bytes: [u8; AURA_LAPTOP_LED_MSG_LEN] = mode.into();
let hid_raw = hid_raw.lock().await;
hid_raw.write_bytes(&bytes)?;
hid_raw.write_bytes(&AURA_LAPTOP_LED_SET)?;
// Changes won't persist unless apply is set
hid_raw.write_bytes(&AURA_LAPTOP_LED_APPLY)?;
} else {
return Err(RogError::NoAuraKeyboard);
}
Ok(())
}
pub async fn set_brightness(&self, value: u8) -> Result<(), RogError> {
if let Some(backlight) = &self.backlight {
backlight.lock().await.set_brightness(value)?;
return Ok(());
}
Err(RogError::MissingFunction(
"No LED backlight control available".to_string()
))
}
/// Set combination state for boot animation/sleep animation/all leds/keys
/// leds/side leds LED active
pub async fn set_power_states(&self, config: &AuraConfig) -> Result<(), RogError> {
if matches!(config.led_type, rog_aura::AuraDeviceType::LaptopKeyboardTuf) {
if let Some(backlight) = &self.backlight {
// TODO: tuf bool array
let buf = config.enabled.to_bytes(config.led_type);
backlight.lock().await.set_kbd_rgb_state(&buf)?;
}
} else if let Some(hid_raw) = &self.hid {
let hid_raw = hid_raw.lock().await;
if let Some(p) = config.enabled.states.first() {
if p.zone == PowerZones::Ally {
let msg = [
0x5d,
0xd1,
0x09,
0x01,
p.new_to_byte() as u8,
0x0,
0x0
];
hid_raw.write_bytes(&msg)?;
return Ok(());
}
}
let bytes = config.enabled.to_bytes(config.led_type);
let msg = [
0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2], bytes[3]
];
hid_raw.write_bytes(&msg)?;
}
Ok(())
}
/// Write an effect block. This is for per-key, but can be repurposed to
/// write the raw factory mode packets - when doing this it is expected that
/// only the first `Vec` (`effect[0]`) is valid.
pub async fn write_effect_block(
&self,
config: &mut AuraConfig,
effect: &AuraLaptopUsbPackets
) -> Result<(), RogError> {
if config.brightness == LedBrightness::Off {
config.brightness = LedBrightness::Med;
config.write();
}
let pkt_type = effect[0][1];
const PER_KEY_TYPE: u8 = 0xbc;
if let Some(hid_raw) = &self.hid {
let hid_raw = hid_raw.lock().await;
if pkt_type != PER_KEY_TYPE {
config.per_key_mode_active = false;
hid_raw.write_bytes(&effect[0])?;
hid_raw.write_bytes(&AURA_LAPTOP_LED_SET)?;
// hid_raw.write_bytes(&LED_APPLY)?;
} else {
if !config.per_key_mode_active {
let init = LedUsbPackets::get_init_msg();
hid_raw.write_bytes(&init)?;
config.per_key_mode_active = true;
}
for row in effect.iter() {
hid_raw.write_bytes(row)?;
}
}
} else if matches!(config.led_type, rog_aura::AuraDeviceType::LaptopKeyboardTuf) {
if let Some(tuf) = &self.backlight {
for row in effect.iter() {
let r = row[9];
let g = row[10];
let b = row[11];
tuf.lock().await.set_kbd_rgb_mode(&[
0, 0, r, g, b, 0
])?;
}
}
}
Ok(())
}
pub async fn fix_ally_power(&mut self) -> Result<(), RogError> {
if self.config.lock().await.led_type == AuraDeviceType::Ally {
if let Some(hid_raw) = &self.hid {
let mut config = self.config.lock().await;
if config.ally_fix.is_none() {
let msg = [
0x5d, 0xbd, 0x01, 0xff, 0xff, 0xff, 0xff
];
hid_raw.lock().await.write_bytes(&msg)?;
info!("Reset Ally power settings to base");
config.ally_fix = Some(true);
}
config.write();
}
}
Ok(())
}
}
-343
View File
@@ -1,343 +0,0 @@
use std::collections::BTreeMap;
use config_traits::StdConfig;
use log::{debug, error, info, warn};
use rog_aura::keyboard::{AuraLaptopUsbPackets, LaptopAuraPower};
use rog_aura::{AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, LedBrightness, PowerZones};
use zbus::fdo::Error as ZbErr;
use zbus::object_server::SignalEmitter;
use zbus::zvariant::OwnedObjectPath;
use zbus::{interface, Connection};
use super::Aura;
use crate::error::RogError;
use crate::{CtrlTask, Reloadable};
pub const AURA_ZBUS_NAME: &str = "Aura";
pub const AURA_ZBUS_PATH: &str = "/xyz/ljones";
#[derive(Clone)]
pub struct AuraZbus(Aura);
impl AuraZbus {
pub fn new(aura: Aura) -> Self {
Self(aura)
}
pub async fn start_tasks(
mut self,
connection: &Connection,
// _signal_ctx: SignalEmitter<'static>,
path: OwnedObjectPath
) -> Result<(), RogError> {
// let task = zbus.clone();
// let signal_ctx = signal_ctx.clone();
self.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
connection
.object_server()
.at(path.clone(), self)
.await
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
.ok();
// TODO: skip this until we keep handles to tasks so they can be killed
// task.create_tasks(signal_ctx).await
Ok(())
}
}
/// The main interface for changing, reading, or notfying
///
/// LED commands are split between Brightness, Modes, Per-Key
#[interface(name = "xyz.ljones.Aura")]
impl AuraZbus {
/// Return the device type for this Aura keyboard
#[zbus(property)]
async fn device_type(&self) -> AuraDeviceType {
self.0.config.lock().await.led_type
}
/// Return the current LED brightness
#[zbus(property)]
async fn brightness(&self) -> Result<LedBrightness, ZbErr> {
if let Some(bl) = self.0.backlight.as_ref() {
return Ok(bl.lock().await.get_brightness().map(|n| n.into())?);
}
Err(ZbErr::Failed("No sysfs brightness control".to_string()))
}
/// Set the keyboard brightness level (0-3)
#[zbus(property)]
async fn set_brightness(&mut self, brightness: LedBrightness) -> Result<(), ZbErr> {
if let Some(bl) = self.0.backlight.as_ref() {
return Ok(bl.lock().await.set_brightness(brightness.into())?);
}
Err(ZbErr::Failed("No sysfs brightness control".to_string()))
}
/// Total levels of brightness available
#[zbus(property)]
async fn supported_brightness(&self) -> Vec<LedBrightness> {
vec![
LedBrightness::Off,
LedBrightness::Low,
LedBrightness::Med,
LedBrightness::High,
]
}
/// The total available modes
#[zbus(property)]
async fn supported_basic_modes(&self) -> Result<Vec<AuraModeNum>, ZbErr> {
let config = self.0.config.lock().await;
Ok(config.builtins.keys().cloned().collect())
}
#[zbus(property)]
async fn supported_basic_zones(&self) -> Result<Vec<AuraZone>, ZbErr> {
let config = self.0.config.lock().await;
Ok(config.support_data.basic_zones.clone())
}
#[zbus(property)]
async fn supported_power_zones(&self) -> Result<Vec<PowerZones>, ZbErr> {
let config = self.0.config.lock().await;
Ok(config.support_data.power_zones.clone())
}
/// The current mode data
#[zbus(property)]
async fn led_mode(&self) -> Result<AuraModeNum, ZbErr> {
// entirely possible to deadlock here, so use try instead of lock()
// let ctrl = self.0.lock().await;
// Ok(config.current_mode)
if let Some(config) = self.0.config.try_lock() {
Ok(config.current_mode)
} else {
Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
}
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode(&mut self, num: AuraModeNum) -> Result<(), ZbErr> {
let mut config = self.0.config.lock().await;
config.current_mode = num;
self.0.write_current_config_mode(&mut config).await?;
if config.brightness == LedBrightness::Off {
config.brightness = LedBrightness::Med;
}
self.0.set_brightness(config.brightness.into()).await?;
config.write();
Ok(())
}
/// The current mode data
#[zbus(property)]
async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> {
// entirely possible to deadlock here, so use try instead of lock()
if let Some(config) = self.0.config.try_lock() {
let mode = config.current_mode;
match config.builtins.get(&mode) {
Some(effect) => Ok(effect.clone()),
None => Err(ZbErr::Failed("Could not get the current effect".into()))
}
} else {
Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
}
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode_data(&mut self, effect: AuraEffect) -> Result<(), ZbErr> {
let mut config = self.0.config.lock().await;
if !config.support_data.basic_modes.contains(&effect.mode)
|| effect.zone != AuraZone::None
&& !config.support_data.basic_zones.contains(&effect.zone)
{
return Err(ZbErr::NotSupported(format!(
"The Aura effect is not supported: {effect:?}"
)));
}
self.0
.write_effect_and_apply(config.led_type, &effect)
.await?;
if config.brightness == LedBrightness::Off {
config.brightness = LedBrightness::Med;
}
self.0.set_brightness(config.brightness.into()).await?;
config.set_builtin(effect);
config.write();
Ok(())
}
/// Get the data set for every mode available
async fn all_mode_data(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
let config = self.0.config.lock().await;
config.builtins.clone()
}
// As property doesn't work for AuraPowerDev (complexity of serialization?)
#[zbus(property)]
async fn led_power(&self) -> LaptopAuraPower {
let config = self.0.config.lock().await;
config.enabled.clone()
}
/// Set a variety of states, input is array of enum.
/// `enabled` sets if the sent array should be disabled or enabled
///
/// For Modern ROG devices the "enabled" flag is ignored.
#[zbus(property)]
async fn set_led_power(&mut self, options: LaptopAuraPower) -> Result<(), ZbErr> {
let mut config = self.0.config.lock().await;
for opt in options.states {
let zone = opt.zone;
for config in config.enabled.states.iter_mut() {
if config.zone == zone {
*config = opt;
}
}
}
config.write();
Ok(self.0.set_power_states(&config).await.map_err(|e| {
warn!("{}", e);
e
})?)
}
/// On machine that have some form of either per-key keyboard or per-zone
/// this can be used to write custom effects over dbus. The input is a
/// nested `Vec<Vec<8>>` where `Vec<u8>` is a raw USB packet
async fn direct_addressing_raw(&self, data: AuraLaptopUsbPackets) -> Result<(), ZbErr> {
let mut config = self.0.config.lock().await;
self.0.write_effect_block(&mut config, &data).await?;
Ok(())
}
}
impl CtrlTask for AuraZbus {
fn zbus_path() -> &'static str {
"/xyz/ljones"
}
async fn create_tasks(&self, _: SignalEmitter<'static>) -> Result<(), RogError> {
let inner1 = self.0.clone();
let inner3 = self.0.clone();
self.create_sys_event_tasks(
move |sleeping| {
let inner1 = inner1.clone();
// unwrap as we want to bomb out of the task
async move {
if !sleeping {
info!("CtrlKbdLedTask reloading brightness and modes");
if let Some(backlight) = &inner1.backlight {
backlight
.lock()
.await
.set_brightness(inner1.config.lock().await.brightness.into())
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})
.unwrap();
}
let mut config = inner1.config.lock().await;
inner1
.write_current_config_mode(&mut config)
.await
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})
.unwrap();
} else if sleeping {
inner1
.update_config()
.await
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})
.unwrap();
}
}
},
move |_shutting_down| {
let inner3 = inner3.clone();
async move {
info!("CtrlKbdLedTask reloading brightness and modes");
if let Some(backlight) = &inner3.backlight {
// unwrap as we want to bomb out of the task
backlight
.lock()
.await
.set_brightness(inner3.config.lock().await.brightness.into())
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})
.unwrap();
}
}
},
move |_lid_closed| {
// on lid change
async move {}
},
move |_power_plugged| {
// power change
async move {}
}
)
.await;
// let ctrl2 = self.0.clone();
// let ctrl = self.0.lock().await;
// if ctrl.led_node.has_brightness_control() {
// let watch = ctrl.led_node.monitor_brightness()?;
// tokio::spawn(async move {
// let mut buffer = [0; 32];
// watch
// .into_event_stream(&mut buffer)
// .unwrap()
// .for_each(|_| async {
// if let Some(lock) = ctrl2.try_lock() {
// load_save(true, lock).unwrap(); // unwrap as we want
// // to
// // bomb out of the
// // task
// }
// })
// .await;
// });
// }
Ok(())
}
}
impl Reloadable for AuraZbus {
async fn reload(&mut self) -> Result<(), RogError> {
self.0.fix_ally_power().await?;
debug!("reloading keyboard mode");
let mut config = self.0.lock_config().await;
self.0.write_current_config_mode(&mut config).await?;
debug!("reloading power states");
self.0
.set_power_states(&config)
.await
.map_err(|err| warn!("{err}"))
.ok();
Ok(())
}
}
-541
View File
@@ -1,541 +0,0 @@
// Plan:
// - Manager has udev monitor on USB looking for ROG devices
// - If a device is found, add it to watch
// - Add it to Zbus server
// - If udev sees device removed then remove the zbus path
use std::sync::Arc;
use dmi_id::DMIID;
use futures_lite::future::block_on;
use futures_util::lock::Mutex;
use log::{debug, error, info, warn};
use mio::{Events, Interest, Poll, Token};
use rog_platform::error::PlatformError;
use rog_platform::hid_raw::HidRaw;
use udev::{Device, MonitorBuilder};
use zbus::zvariant::{ObjectPath, OwnedObjectPath};
use zbus::Connection;
use crate::aura_anime::trait_impls::AniMeZbus;
use crate::aura_laptop::trait_impls::AuraZbus;
use crate::aura_scsi::trait_impls::ScsiZbus;
use crate::aura_slash::trait_impls::SlashZbus;
use crate::aura_types::DeviceHandle;
use crate::error::RogError;
use crate::ASUS_ZBUS_PATH;
const MOD_NAME: &str = "aura";
/// Returns only the Device details concatenated in a form usable for
/// adding/appending to a filename
pub fn filename_partial(parent: &Device) -> Option<OwnedObjectPath> {
if let Some(id_product) = parent.attribute_value("idProduct") {
let id_product = id_product.to_string_lossy();
let mut path = if let Some(devnum) = parent.attribute_value("devnum") {
let devnum = devnum.to_string_lossy();
if let Some(devpath) = parent.attribute_value("devpath") {
let devpath = devpath.to_string_lossy();
format!("{id_product}_{devnum}_{devpath}")
} else {
format!("{id_product}_{devnum}")
}
} else {
format!("{id_product}")
};
if path.contains('.') {
warn!("dbus path for {id_product} contains `.`, removing");
path.replace('.', "").clone_into(&mut path);
}
return Some(ObjectPath::from_str_unchecked(&path).into());
}
None
}
fn dbus_path_for_dev(parent: &Device) -> Option<OwnedObjectPath> {
if let Some(filename) = filename_partial(parent) {
return Some(
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{filename}"))
.into()
);
}
None
}
fn dbus_path_for_tuf() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/tuf")).into()
}
fn dbus_path_for_slash() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/slash")).into()
}
fn dbus_path_for_anime() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/anime")).into()
}
fn dbus_path_for_scsi(prod_id: &str) -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{prod_id}_scsi")).into()
}
fn dev_prop_matches(dev: &Device, prop: &str, value: &str) -> bool {
if let Some(p) = dev.property_value(prop) {
return p == value;
}
false
}
/// A device.
///
/// Each controller within should track its dbus path so it can be removed if
/// required.
pub struct AsusDevice {
device: DeviceHandle,
dbus_path: OwnedObjectPath
}
pub struct DeviceManager {
_dbus_connection: Connection
}
impl DeviceManager {
async fn init_hid_devices(
connection: &Connection,
device: Device
) -> Result<Vec<AsusDevice>, RogError> {
let mut devices = Vec::new();
if let Some(usb_device) = device.parent_with_subsystem_devtype("usb", "usb_device")? {
if let Some(usb_id) = usb_device.attribute_value("idProduct") {
if let Some(vendor_id) = usb_device.attribute_value("idVendor") {
if vendor_id != "0b05" {
debug!("Not ASUS vendor ID");
return Ok(devices);
}
// Almost all devices are identified by the productId.
// So let's see what we have and:
// 1. Generate an interface path
// 2. Create the device
// Use the top-level endpoint, not the parent
if let Ok(hidraw) = HidRaw::from_device(device) {
debug!("Testing device {usb_id:?}");
let dev = Arc::new(Mutex::new(hidraw));
// SLASH DEVICE
if let Ok(dev_type) = DeviceHandle::new_slash_hid(
dev.clone(),
usb_id.to_str().unwrap_or_default()
)
.await
{
if let DeviceHandle::Slash(slash) = dev_type.clone() {
let path =
dbus_path_for_dev(&usb_device).unwrap_or(dbus_path_for_slash());
let ctrl = SlashZbus::new(slash);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path
});
}
}
// ANIME MATRIX DEVICE
if let Ok(dev_type) = DeviceHandle::maybe_anime_hid(
dev.clone(),
usb_id.to_str().unwrap_or_default()
)
.await
{
if let DeviceHandle::AniMe(anime) = dev_type.clone() {
let path =
dbus_path_for_dev(&usb_device).unwrap_or(dbus_path_for_anime());
let ctrl = AniMeZbus::new(anime);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path
});
}
}
// AURA LAPTOP DEVICE
if let Ok(dev_type) = DeviceHandle::maybe_laptop_aura(
Some(dev),
usb_id.to_str().unwrap_or_default()
)
.await
{
if let DeviceHandle::Aura(aura) = dev_type.clone() {
let path =
dbus_path_for_dev(&usb_device).unwrap_or(dbus_path_for_tuf());
let ctrl = AuraZbus::new(aura);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path
});
}
}
}
}
}
}
Ok(devices)
}
/// To be called on daemon startup
async fn init_all_hid(connection: &Connection) -> Result<Vec<AsusDevice>, RogError> {
// track and ensure we use only one hidraw per prod_id
// let mut interfaces = HashSet::new();
let mut devices: Vec<AsusDevice> = Vec::new();
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
PlatformError::Udev("enumerator failed".into(), err)
})?;
enumerator.match_subsystem("hidraw").map_err(|err| {
warn!("{}", err);
PlatformError::Udev("match_subsystem failed".into(), err)
})?;
for device in enumerator
.scan_devices()
.map_err(|e| PlatformError::IoPath("enumerator".to_owned(), e))?
{
devices.append(&mut Self::init_hid_devices(connection, device).await?);
}
Ok(devices)
}
async fn init_scsi(
connection: &Connection,
device: &Device,
path: OwnedObjectPath
) -> Option<AsusDevice> {
// "ID_MODEL_ID" "1932"
// "ID_VENDOR_ID" "0b05"
if dev_prop_matches(device, "ID_VENDOR_ID", "0b05") {
if let Some(dev_node) = device.devnode() {
let prod_id = device
.property_value("ID_MODEL_ID")
.unwrap_or_default()
.to_string_lossy();
if let Ok(dev_type) =
DeviceHandle::maybe_scsi(dev_node.as_os_str().to_str().unwrap(), &prod_id).await
{
if let DeviceHandle::Scsi(scsi) = dev_type.clone() {
let ctrl = ScsiZbus::new(scsi);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
return Some(AsusDevice {
device: dev_type,
dbus_path: path
});
}
}
}
}
None
}
async fn init_all_scsi(connection: &Connection) -> Result<Vec<AsusDevice>, RogError> {
// track and ensure we use only one hidraw per prod_id
// let mut interfaces = HashSet::new();
let mut devices: Vec<AsusDevice> = Vec::new();
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
PlatformError::Udev("enumerator failed".into(), err)
})?;
enumerator.match_subsystem("block").map_err(|err| {
warn!("{}", err);
PlatformError::Udev("match_subsystem failed".into(), err)
})?;
let mut found = Vec::new();
for device in enumerator
.scan_devices()
.map_err(|e| PlatformError::IoPath("enumerator".to_owned(), e))?
{
if let Some(serial) = device.property_value("ID_SERIAL_SHORT") {
let serial = serial.to_string_lossy().to_string();
let path = dbus_path_for_scsi(&serial);
if found.contains(&path) {
continue;
}
if let Some(dev) = Self::init_scsi(connection, &device, path.clone()).await {
devices.push(dev);
found.push(path);
}
} else {
debug!("No serial for SCSI device: {:?}", device.devpath());
}
}
Ok(devices)
}
pub async fn find_all_devices(connection: &Connection) -> Vec<AsusDevice> {
let mut devices: Vec<AsusDevice> = Vec::new();
// HID first, always
if let Ok(devs) = &mut Self::init_all_hid(connection).await {
devices.append(devs);
}
// USB after, need to check if HID picked something up and if so, skip it
let mut do_anime = true;
let mut do_slash = true;
let mut do_kb_backlight = true;
for dev in devices.iter() {
if matches!(dev.device, DeviceHandle::Slash(_)) {
do_slash = false;
}
if matches!(dev.device, DeviceHandle::AniMe(_)) {
do_anime = false;
}
if matches!(dev.device, DeviceHandle::Aura(_) | DeviceHandle::OldAura(_)) {
do_kb_backlight = false;
}
}
if do_slash {
if let Ok(dev_type) = DeviceHandle::new_slash_usb().await {
if let DeviceHandle::Slash(slash) = dev_type.clone() {
let path = dbus_path_for_slash();
let ctrl = SlashZbus::new(slash);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path
});
}
} else {
info!("Tested device was not Slash");
}
}
if do_anime {
if let Ok(dev_type) = DeviceHandle::maybe_anime_usb().await {
// TODO: this is copy/pasted
if let DeviceHandle::AniMe(anime) = dev_type.clone() {
let path = dbus_path_for_anime();
let ctrl = AniMeZbus::new(anime);
if ctrl
.start_tasks(connection, path.clone())
.await
.map_err(|e| error!("Failed to start tasks: {e:?}, not adding this device"))
.is_ok()
{
devices.push(AsusDevice {
device: dev_type,
dbus_path: path
});
}
}
} else {
info!("Tested device was not AniMe Matrix");
}
}
if do_kb_backlight {
// TUF AURA LAPTOP DEVICE
// product_name = ASUS TUF Gaming F15 FX507ZE_FX507ZE
// product_family = ASUS TUF Gaming F15
let product_name = DMIID::new().unwrap_or_default().product_name;
let product_family = DMIID::new().unwrap_or_default().product_family;
info!(
"No USB keyboard aura, system is {product_name}, try using sysfs backlight control"
);
if product_name.contains("TUF") || product_family.contains("TUF") {
info!("TUF laptop, try using sysfs backlight control");
if let Ok(dev_type) = DeviceHandle::maybe_laptop_aura(None, "tuf").await {
if let DeviceHandle::Aura(aura) = dev_type.clone() {
let path = dbus_path_for_tuf();
let ctrl = AuraZbus::new(aura);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path
});
}
}
}
}
if let Ok(devs) = &mut Self::init_all_scsi(connection).await {
devices.append(devs);
}
devices
}
pub async fn new(connection: Connection) -> Result<Self, RogError> {
let conn_copy = connection.clone();
let devices = Self::find_all_devices(&conn_copy).await;
info!("Found {} valid devices on startup", devices.len());
let devices = Arc::new(Mutex::new(devices));
let manager = Self {
_dbus_connection: connection
};
// TODO: The /sysfs/ LEDs don't cause events, so they need to be manually
// checked for and added
std::thread::spawn(move || {
let mut monitor = MonitorBuilder::new()?.listen()?;
let mut poll = Poll::new()?;
let mut events = Events::with_capacity(1024);
poll.registry()
.register(&mut monitor, Token(0), Interest::READABLE)?;
let rt = tokio::runtime::Runtime::new().expect("Unable to create Runtime");
let _enter = rt.enter();
loop {
if poll.poll(&mut events, None).is_err() {
continue;
}
for event in monitor.iter() {
let action = event
.action()
.unwrap_or_default()
.to_string_lossy()
.to_string();
let subsys = if let Some(subsys) = event.subsystem() {
subsys.to_string_lossy().to_string()
} else {
continue;
};
let devices = devices.clone();
let conn_copy = conn_copy.clone();
block_on(async move {
// SCSCI devs
if subsys == "block" {
if action == "remove" {
if let Some(serial) =
event.device().property_value("ID_SERIAL_SHORT")
{
let serial = serial.to_string_lossy().to_string();
let path = dbus_path_for_scsi(&serial);
let index = if let Some(index) = devices
.lock()
.await
.iter()
.position(|dev| dev.dbus_path == path)
{
index
} else {
if dev_prop_matches(&event.device(), "ID_VENDOR_ID", "0b05")
{
warn!("No device for dbus path: {path:?}");
}
return Ok(());
};
info!("removing: {path:?}");
let dev = devices.lock().await.remove(index);
let path = path.clone();
if let DeviceHandle::Scsi(_) = dev.device {
conn_copy
.object_server()
.remove::<ScsiZbus, _>(&path)
.await?;
}
}
} else if action == "add" {
let evdev = event.device();
if let Some(serial) = evdev.property_value("ID_SERIAL_SHORT") {
let serial = serial.to_string_lossy().to_string();
let path = dbus_path_for_scsi(&serial);
if let Some(new_devs) =
Self::init_scsi(&conn_copy, &evdev, path).await
{
devices.lock().await.append(&mut vec![new_devs]);
}
}
};
}
if subsys == "hidraw" {
if let Some(parent) =
event.parent_with_subsystem_devtype("usb", "usb_device")?
{
if action == "remove" {
if let Some(path) = dbus_path_for_dev(&parent) {
// Find the indexs of devices matching the path
let removals: Vec<usize> = devices
.lock()
.await
.iter()
.enumerate()
.filter_map(|(i, dev)| {
if dev.dbus_path == path {
Some(i)
} else {
None
}
})
.collect();
if removals.is_empty() {
return Ok(());
}
info!("removing: {path:?}");
// Iter in reverse so as to not screw up indexing
for index in removals.iter().rev() {
let dev = devices.lock().await.remove(*index);
let path = path.clone();
let res = match dev.device {
DeviceHandle::Aura(_) => {
conn_copy
.object_server()
.remove::<AuraZbus, _>(&path)
.await?
}
DeviceHandle::Slash(_) => {
conn_copy
.object_server()
.remove::<SlashZbus, _>(&path)
.await?
}
DeviceHandle::AniMe(_) => {
conn_copy
.object_server()
.remove::<AniMeZbus, _>(&path)
.await?
}
DeviceHandle::Scsi(_) => {
conn_copy
.object_server()
.remove::<ScsiZbus, _>(&path)
.await?
}
_ => todo!()
};
info!("AuraManager removed: {path:?}, {res}");
}
}
} else if action == "add" {
let evdev = event.device();
if let Ok(mut new_devs) =
Self::init_hid_devices(&conn_copy, evdev)
.await
.map_err(|e| error!("Couldn't add new device: {e:?}"))
{
devices.lock().await.append(&mut new_devs);
}
};
}
}
Ok::<(), RogError>(())
})
.map_err(|e| error!("{e:?}"))
.ok();
}
}
// Required for return type on spawn
#[allow(unreachable_code)]
Ok::<(), RogError>(())
});
Ok(manager)
}
}
-114
View File
@@ -1,114 +0,0 @@
use std::collections::BTreeMap;
use config_traits::{StdConfig, StdConfigLoad};
use rog_aura::AuraDeviceType;
use rog_scsi::{AuraEffect, AuraMode};
use serde::{Deserialize, Serialize};
const CONFIG_FILE: &str = "scsi.ron";
/// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug)]
pub struct ScsiConfig {
#[serde(skip)]
pub dev_type: AuraDeviceType,
pub enabled: bool,
pub current_mode: AuraMode,
pub modes: BTreeMap<AuraMode, AuraEffect>
}
impl ScsiConfig {
pub fn get_effect(&mut self, mode: AuraMode) -> Option<&AuraEffect> {
self.modes.get(&mode)
}
pub fn save_effect(&mut self, effect: AuraEffect) {
self.current_mode = effect.mode;
self.modes.insert(*effect.mode(), effect);
}
}
impl Default for ScsiConfig {
fn default() -> Self {
ScsiConfig {
enabled: true,
current_mode: AuraMode::Static,
dev_type: AuraDeviceType::ScsiExtDisk,
modes: BTreeMap::from([
(AuraMode::Off, AuraEffect::default_with_mode(AuraMode::Off)),
(
AuraMode::Static,
AuraEffect::default_with_mode(AuraMode::Static)
),
(
AuraMode::Breathe,
AuraEffect::default_with_mode(AuraMode::Breathe)
),
(
AuraMode::Flashing,
AuraEffect::default_with_mode(AuraMode::Flashing)
),
(
AuraMode::RainbowCycle,
AuraEffect::default_with_mode(AuraMode::RainbowCycle)
),
(
AuraMode::RainbowWave,
AuraEffect::default_with_mode(AuraMode::RainbowWave)
),
(
AuraMode::RainbowCycleBreathe,
AuraEffect::default_with_mode(AuraMode::RainbowCycleBreathe)
),
(
AuraMode::ChaseFade,
AuraEffect::default_with_mode(AuraMode::ChaseFade)
),
(
AuraMode::RainbowCycleChaseFade,
AuraEffect::default_with_mode(AuraMode::RainbowCycleChaseFade)
),
(
AuraMode::Chase,
AuraEffect::default_with_mode(AuraMode::Chase)
),
(
AuraMode::RainbowCycleChase,
AuraEffect::default_with_mode(AuraMode::RainbowCycleChase)
),
(
AuraMode::RainbowCycleWave,
AuraEffect::default_with_mode(AuraMode::RainbowCycleWave)
),
(
AuraMode::RainbowPulseChase,
AuraEffect::default_with_mode(AuraMode::RainbowPulseChase)
),
(
AuraMode::RandomFlicker,
AuraEffect::default_with_mode(AuraMode::RandomFlicker)
),
(
AuraMode::DoubleFade,
AuraEffect::default_with_mode(AuraMode::DoubleFade)
)
])
}
}
}
impl StdConfig for ScsiConfig {
fn new() -> Self {
Self::default()
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
fn config_dir() -> std::path::PathBuf {
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
}
}
impl StdConfigLoad for ScsiConfig {}
-45
View File
@@ -1,45 +0,0 @@
use std::sync::Arc;
use config::ScsiConfig;
use futures_util::lock::{Mutex, MutexGuard};
use rog_scsi::{AuraEffect, Device, Task};
use crate::error::RogError;
pub mod config;
pub mod trait_impls;
#[derive(Clone)]
pub struct ScsiAura {
device: Arc<Mutex<Device>>,
config: Arc<Mutex<ScsiConfig>>
}
impl ScsiAura {
pub fn new(device: Arc<Mutex<Device>>, config: Arc<Mutex<ScsiConfig>>) -> Self {
Self { device, config }
}
pub async fn lock_config(&self) -> MutexGuard<ScsiConfig> {
self.config.lock().await
}
pub async fn write_effect(&self, effect: &AuraEffect) -> Result<(), RogError> {
let tasks: Vec<Task> = effect.into();
for task in &tasks {
self.device.lock().await.perform(task).ok();
}
Ok(())
}
/// Initialise the device if required. Locks the internal config so be wary
/// of deadlocks.
pub async fn do_initialization(&self) -> Result<(), RogError> {
let config = self.config.lock().await;
let mode = config.current_mode;
if let Some(effect) = config.modes.get(&mode) {
self.write_effect(effect).await?;
}
Ok(())
}
}
-116
View File
@@ -1,116 +0,0 @@
use std::collections::BTreeMap;
use config_traits::StdConfig;
use log::error;
use rog_aura::AuraDeviceType;
use rog_scsi::{AuraEffect, AuraMode};
use zbus::fdo::Error as ZbErr;
use zbus::zvariant::OwnedObjectPath;
use zbus::{interface, Connection};
use super::ScsiAura;
use crate::error::RogError;
#[derive(Clone)]
pub struct ScsiZbus(ScsiAura);
impl ScsiZbus {
pub fn new(scsi: ScsiAura) -> Self {
Self(scsi)
}
pub async fn start_tasks(
self,
connection: &Connection,
path: OwnedObjectPath
) -> Result<(), RogError> {
connection
.object_server()
.at(path.clone(), self)
.await
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
.ok();
Ok(())
}
}
#[interface(name = "xyz.ljones.ScsiAura")]
impl ScsiZbus {
/// Return the device type for this Aura keyboard
#[zbus(property)]
async fn device_type(&self) -> AuraDeviceType {
self.0.config.lock().await.dev_type
}
/// Get enabled or not
#[zbus(property)]
async fn enabled(&self) -> bool {
let lock = self.0.lock_config().await;
lock.enabled
}
/// Set enabled true or false
#[zbus(property)]
async fn set_enabled(&self, enabled: bool) {
let mut config = self.0.lock_config().await;
config.enabled = enabled;
config.write();
}
#[zbus(property)]
async fn led_mode(&self) -> u8 {
let config = self.0.lock_config().await;
config.current_mode as u8
}
#[zbus(property)]
async fn set_led_mode(&self, mode: AuraMode) -> Result<(), zbus::Error> {
let mut config = self.0.lock_config().await;
if let Some(effect) = config.get_effect(mode) {
self.0
.write_effect(effect)
.await
.map_err(|e| zbus::Error::Failure(format!("{e:?}")))?;
} else {
return Err(zbus::Error::Failure("Mode data does not exist".to_string()));
}
config.current_mode = mode;
config.write();
Ok(())
}
/// The current mode data
#[zbus(property)]
async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> {
// entirely possible to deadlock here, so use try instead of lock()
if let Some(config) = self.0.config.try_lock() {
let mode = config.current_mode;
match config.modes.get(&mode) {
Some(effect) => Ok(effect.clone()),
None => Err(ZbErr::Failed("Could not get the current effect".into()))
}
} else {
Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
}
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode_data(&mut self, effect: AuraEffect) -> Result<(), ZbErr> {
self.0.write_effect(&effect).await?;
let mut config = self.0.config.lock().await;
config.save_effect(effect);
config.write();
Ok(())
}
/// Get the data set for every mode available
async fn all_mode_data(&self) -> BTreeMap<AuraMode, AuraEffect> {
let config = self.0.config.lock().await;
config.modes.clone()
}
}
-64
View File
@@ -1,64 +0,0 @@
use config_traits::{StdConfig, StdConfigLoad};
use rog_slash::{DeviceState, SlashMode, SlashType};
use serde::{Deserialize, Serialize};
const CONFIG_FILE: &str = "slash.ron";
/// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug)]
pub struct SlashConfig {
#[serde(skip)]
pub slash_type: SlashType,
pub enabled: bool,
pub brightness: u8,
pub display_interval: u8,
pub display_mode: SlashMode,
pub show_on_boot: bool,
pub show_on_shutdown: bool,
pub show_on_sleep: bool,
pub show_on_battery: bool,
pub show_battery_warning: bool
}
impl Default for SlashConfig {
fn default() -> Self {
SlashConfig {
enabled: true,
brightness: 255,
display_interval: 0,
display_mode: SlashMode::Bounce,
slash_type: SlashType::Unsupported,
show_on_boot: true,
show_on_shutdown: true,
show_on_sleep: true,
show_on_battery: true,
show_battery_warning: true
}
}
}
impl StdConfig for SlashConfig {
fn new() -> Self {
Self::default()
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
fn config_dir() -> std::path::PathBuf {
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
}
}
impl StdConfigLoad for SlashConfig {}
impl From<&SlashConfig> for DeviceState {
fn from(config: &SlashConfig) -> Self {
DeviceState {
slash_enabled: config.enabled,
slash_brightness: config.brightness,
slash_interval: config.display_interval,
slash_mode: config.display_mode
}
}
}
-70
View File
@@ -1,70 +0,0 @@
use std::sync::Arc;
use config::SlashConfig;
use futures_util::lock::{Mutex, MutexGuard};
use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw;
use rog_slash::usb::{get_options_packet, pkt_set_mode, pkts_for_init};
use rog_slash::SlashType;
use crate::error::RogError;
pub mod config;
pub mod trait_impls;
#[derive(Debug, Clone)]
pub struct Slash {
hid: Option<Arc<Mutex<HidRaw>>>,
usb: Option<Arc<Mutex<USBRaw>>>,
config: Arc<Mutex<SlashConfig>>
}
impl Slash {
pub fn new(
hid: Option<Arc<Mutex<HidRaw>>>,
usb: Option<Arc<Mutex<USBRaw>>>,
config: Arc<Mutex<SlashConfig>>
) -> Self {
Self { hid, usb, config }
}
pub async fn lock_config(&self) -> MutexGuard<SlashConfig> {
self.config.lock().await
}
pub async fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
if let Some(hid) = &self.hid {
hid.lock().await.write_bytes(message)?;
} else if let Some(usb) = &self.usb {
usb.lock().await.write_bytes(message)?;
}
Ok(())
}
/// Initialise the device if required. Locks the internal config so be wary
/// of deadlocks.
pub async fn do_initialization(&self) -> Result<(), RogError> {
// Don't try to initialise these models as the asus drivers already did
let config = self.config.lock().await;
if !matches!(config.slash_type, SlashType::GA605 | SlashType::GU605) {
for pkt in &pkts_for_init(config.slash_type) {
self.write_bytes(pkt).await?;
}
}
// Apply config upon initialization
let option_packets = get_options_packet(
config.slash_type,
config.enabled,
config.brightness,
config.display_interval
);
self.write_bytes(&option_packets).await?;
let mode_packets = pkt_set_mode(config.slash_type, config.display_mode);
// self.node.write_bytes(&mode_packets[0])?;
self.write_bytes(&mode_packets[1]).await?;
Ok(())
}
}
-287
View File
@@ -1,287 +0,0 @@
use config_traits::StdConfig;
use log::{debug, error, warn};
use rog_slash::usb::{
get_battery_saver_packet, get_boot_packet, get_low_battery_packet, get_options_packet,
get_shutdown_packet, get_sleep_packet, pkt_save, pkt_set_mode
};
use rog_slash::{DeviceState, SlashMode};
use zbus::zvariant::OwnedObjectPath;
use zbus::{interface, Connection};
use super::Slash;
use crate::error::RogError;
use crate::Reloadable;
#[derive(Clone)]
pub struct SlashZbus(Slash);
impl SlashZbus {
pub fn new(slash: Slash) -> Self {
Self(slash)
}
pub async fn start_tasks(
mut self,
connection: &Connection,
path: OwnedObjectPath
) -> Result<(), RogError> {
// let task = zbus.clone();
self.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
connection
.object_server()
.at(path.clone(), self)
.await
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
.ok();
Ok(())
}
}
#[interface(name = "xyz.ljones.Slash")]
impl SlashZbus {
/// Get enabled or not
#[zbus(property)]
async fn enabled(&self) -> bool {
let lock = self.0.lock_config().await;
lock.enabled
}
/// Set enabled true or false
#[zbus(property)]
async fn set_enabled(&self, enabled: bool) {
let mut config = self.0.lock_config().await;
let brightness = if enabled && config.brightness == 0 {
0x88
} else {
config.brightness
};
self.0
.write_bytes(&get_options_packet(
config.slash_type,
enabled,
brightness,
config.display_interval
))
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
config.enabled = enabled;
config.brightness = brightness;
config.write();
}
/// Get brightness level
#[zbus(property)]
async fn brightness(&self) -> u8 {
let config = self.0.lock_config().await;
config.brightness
}
/// Set brightness level
#[zbus(property)]
async fn set_brightness(&self, brightness: u8) {
let mut config = self.0.lock_config().await;
let enabled = brightness > 0;
self.0
.write_bytes(&get_options_packet(
config.slash_type,
enabled,
brightness,
config.display_interval
))
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
config.enabled = enabled;
config.brightness = brightness;
config.write();
}
#[zbus(property)]
async fn interval(&self) -> u8 {
let config = self.0.lock_config().await;
config.display_interval
}
/// Set interval between slash animations (0-255)
#[zbus(property)]
async fn set_interval(&self, interval: u8) {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&get_options_packet(
config.slash_type, config.enabled, config.brightness, interval
))
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
config.display_interval = interval;
config.write();
}
#[zbus(property)]
async fn mode(&self) -> zbus::fdo::Result<u8> {
let config = self.0.lock_config().await;
Ok(config.display_interval)
}
/// Set interval between slash animations (0-255)
#[zbus(property)]
async fn set_mode(&self, mode: SlashMode) -> zbus::Result<()> {
let mut config = self.0.lock_config().await;
let command_packets = pkt_set_mode(config.slash_type, mode);
// self.node.write_bytes(&command_packets[0])?;
self.0.write_bytes(&command_packets[1]).await?;
self.0.write_bytes(&pkt_save(config.slash_type)).await?;
config.display_mode = mode;
config.write();
Ok(())
}
/// Get the device state as stored by asusd
// #[zbus(property)]
async fn device_state(&self) -> DeviceState {
let config = self.0.lock_config().await;
DeviceState::from(&*config)
}
#[zbus(property)]
async fn show_on_boot(&self) -> zbus::fdo::Result<bool> {
let config = self.0.lock_config().await;
Ok(config.show_on_boot)
}
#[zbus(property)]
async fn set_show_on_boot(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&get_boot_packet(config.slash_type, enable))
.await?;
config.show_on_boot = enable;
config.write();
Ok(())
}
#[zbus(property)]
async fn show_on_sleep(&self) -> zbus::fdo::Result<bool> {
let config = self.0.lock_config().await;
Ok(config.show_on_sleep)
}
#[zbus(property)]
async fn set_show_on_sleep(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&get_sleep_packet(config.slash_type, enable))
.await?;
config.show_on_sleep = enable;
config.write();
Ok(())
}
#[zbus(property)]
async fn show_on_shutdown(&self) -> zbus::fdo::Result<bool> {
let config = self.0.lock_config().await;
Ok(config.show_on_shutdown)
}
#[zbus(property)]
async fn set_show_on_shutdown(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&get_shutdown_packet(config.slash_type, enable))
.await?;
config.show_on_shutdown = enable;
config.write();
Ok(())
}
#[zbus(property)]
async fn show_on_battery(&self) -> zbus::fdo::Result<bool> {
let config = self.0.lock_config().await;
Ok(config.show_on_battery)
}
#[zbus(property)]
async fn set_show_on_battery(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&get_battery_saver_packet(config.slash_type, enable))
.await?;
config.show_on_battery = enable;
config.write();
Ok(())
}
#[zbus(property)]
async fn show_battery_warning(&self) -> zbus::fdo::Result<bool> {
let config = self.0.lock_config().await;
Ok(config.show_battery_warning)
}
#[zbus(property)]
async fn set_show_battery_warning(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&get_low_battery_packet(config.slash_type, enable))
.await?;
config.show_battery_warning = enable;
config.write();
Ok(())
}
}
impl Reloadable for SlashZbus {
async fn reload(&mut self) -> Result<(), RogError> {
debug!("reloading slash settings");
let config = self.0.lock_config().await;
self.0
.write_bytes(&get_options_packet(
config.slash_type,
config.enabled,
config.brightness,
config.display_interval
))
.await
.map_err(|err| {
warn!("set_options {}", err);
})
.ok();
macro_rules! write_bytes_with_warning {
($packet_fn:expr, $cfg:ident, $warn_msg:expr) => {
self.0
.write_bytes(&$packet_fn(config.slash_type, config.$cfg))
.await
.map_err(|err| {
warn!("{} {}", $warn_msg, err);
})
.ok();
};
}
write_bytes_with_warning!(get_boot_packet, show_on_boot, "show_on_boot");
write_bytes_with_warning!(get_sleep_packet, show_on_sleep, "show_on_sleep");
write_bytes_with_warning!(get_shutdown_packet, show_on_shutdown, "show_on_shutdown");
write_bytes_with_warning!(get_battery_saver_packet, show_on_battery, "show_on_battery");
write_bytes_with_warning!(
get_low_battery_packet,
show_battery_warning,
"show_battery_warning"
);
Ok(())
}
}
-209
View File
@@ -1,209 +0,0 @@
use std::sync::Arc;
use config_traits::{StdConfig, StdConfigLoad};
use futures_util::lock::Mutex;
use log::{debug, error, info};
use rog_anime::error::AnimeError;
use rog_anime::usb::get_anime_type;
use rog_anime::AnimeType;
use rog_aura::AuraDeviceType;
use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardBacklight;
use rog_platform::usb_raw::USBRaw;
use rog_scsi::{open_device, ScsiType};
use rog_slash::error::SlashError;
use rog_slash::SlashType;
use crate::aura_anime::config::AniMeConfig;
use crate::aura_anime::AniMe;
use crate::aura_laptop::config::AuraConfig;
use crate::aura_laptop::Aura;
use crate::aura_scsi::config::ScsiConfig;
use crate::aura_scsi::ScsiAura;
use crate::aura_slash::config::SlashConfig;
use crate::aura_slash::Slash;
use crate::error::RogError;
pub enum _DeviceHandle {
/// The AniMe devices require USBRaw as they are not HID devices
Usb(USBRaw),
HidRaw(HidRaw),
LedClass(KeyboardBacklight),
/// TODO
MulticolourLed,
None
}
#[derive(Clone)]
pub enum DeviceHandle {
Aura(Aura),
Slash(Slash),
/// The AniMe devices require USBRaw as they are not HID devices
AniMe(AniMe),
Scsi(ScsiAura),
Ally(Arc<Mutex<HidRaw>>),
OldAura(Arc<Mutex<HidRaw>>),
/// TUF laptops have an aditional set of attributes added to the LED /sysfs/
TufLedClass(Arc<Mutex<HidRaw>>),
/// TODO
MulticolourLed,
None
}
impl DeviceHandle {
/// Try Slash HID. If one exists it is initialsed and returned.
pub async fn new_slash_hid(
device: Arc<Mutex<HidRaw>>,
prod_id: &str
) -> Result<Self, RogError> {
debug!("Testing for HIDRAW Slash");
let slash_type = SlashType::from_dmi();
if matches!(slash_type, SlashType::Unsupported)
|| slash_type
.prod_id_str()
.to_lowercase()
.trim_start_matches("0x")
!= prod_id
{
log::info!("Unknown or invalid slash: {prod_id:?}, skipping");
return Err(RogError::NotFound("No slash device".to_string()));
}
info!("Found slash type {slash_type:?}: {prod_id}");
let mut config = SlashConfig::new().load();
config.slash_type = slash_type;
let slash = Slash::new(Some(device), None, Arc::new(Mutex::new(config)));
slash.do_initialization().await?;
Ok(Self::Slash(slash))
}
/// Try Slash USB. If one exists it is initialsed and returned.
pub async fn new_slash_usb() -> Result<Self, RogError> {
debug!("Testing for USB Slash");
let slash_type = SlashType::from_dmi();
if matches!(slash_type, SlashType::Unsupported) {
return Err(RogError::Slash(SlashError::NoDevice));
}
if let Ok(usb) = USBRaw::new(slash_type.prod_id()) {
info!("Found Slash USB {slash_type:?}");
let mut config = SlashConfig::new().load();
config.slash_type = slash_type;
let slash = Slash::new(
None,
Some(Arc::new(Mutex::new(usb))),
Arc::new(Mutex::new(config))
);
slash.do_initialization().await?;
Ok(Self::Slash(slash))
} else {
Err(RogError::NotFound("No slash device found".to_string()))
}
}
/// Try AniMe Matrix HID. If one exists it is initialsed and returned.
pub async fn maybe_anime_hid(
_device: Arc<Mutex<HidRaw>>,
_prod_id: &str
) -> Result<Self, RogError> {
// TODO: can't use HIDRAW for anime at the moment
Err(RogError::NotFound(
"Can't use anime over hidraw yet. Skip.".to_string()
))
// debug!("Testing for HIDRAW AniMe");
// let anime_type = AnimeType::from_dmi();
// dbg!(prod_id);
// if matches!(anime_type, AnimeType::Unsupported) || prod_id != "193b"
// { log::info!("Unknown or invalid AniMe: {prod_id:?},
// skipping"); return Err(RogError::NotFound("No
// anime-matrix device".to_string())); }
// info!("Found AniMe Matrix HIDRAW {anime_type:?}: {prod_id}");
// let mut config = AniMeConfig::new().load();
// config.anime_type = anime_type;
// let mut anime = AniMe::new(Some(device), None,
// Arc::new(Mutex::new(config))); anime.do_initialization().
// await?; Ok(Self::AniMe(anime))
}
pub async fn maybe_anime_usb() -> Result<Self, RogError> {
debug!("Testing for USB AniMe");
let anime_type = get_anime_type();
if matches!(anime_type, AnimeType::Unsupported) {
info!("No Anime Matrix capable laptop found");
return Err(RogError::Anime(AnimeError::NoDevice));
}
if let Ok(usb) = USBRaw::new(0x193b) {
info!("Found AniMe Matrix USB {anime_type:?}");
let mut config = AniMeConfig::new().load();
config.anime_type = anime_type;
let mut anime = AniMe::new(
None,
Some(Arc::new(Mutex::new(usb))),
Arc::new(Mutex::new(config))
);
anime.do_initialization().await?;
Ok(Self::AniMe(anime))
} else {
Err(RogError::NotFound(
"No AnimeMatrix device found".to_string()
))
}
}
pub async fn maybe_scsi(dev_node: &str, prod_id: &str) -> Result<Self, RogError> {
debug!("Testing for SCSI");
let prod_id = ScsiType::from(prod_id);
if prod_id == ScsiType::Unsupported {
log::info!("Unknown or invalid SCSI: {prod_id:?}, skipping");
return Err(RogError::NotFound("No SCSI device".to_string()));
}
info!("Found SCSI device {prod_id:?} on {dev_node}");
let mut config = ScsiConfig::new().load();
config.dev_type = AuraDeviceType::ScsiExtDisk;
let dev = Arc::new(Mutex::new(open_device(dev_node)?));
let scsi = ScsiAura::new(dev, Arc::new(Mutex::new(config)));
scsi.do_initialization().await?;
Ok(Self::Scsi(scsi))
}
pub async fn maybe_laptop_aura(
device: Option<Arc<Mutex<HidRaw>>>,
prod_id: &str
) -> Result<Self, RogError> {
debug!("Testing for laptop aura");
let aura_type = AuraDeviceType::from(prod_id);
if !matches!(
aura_type,
AuraDeviceType::LaptopKeyboard2021
| AuraDeviceType::LaptopKeyboardPre2021
| AuraDeviceType::LaptopKeyboardTuf
) {
log::info!("Unknown or invalid laptop aura: {prod_id:?}, skipping");
return Err(RogError::NotFound("No laptop aura device".to_string()));
}
info!("Found laptop aura type {prod_id:?}");
let backlight = KeyboardBacklight::new()
.map_err(|e| error!("Keyboard backlight error: {e:?}"))
.map_or(None, |k| {
info!("Found sysfs backlight control");
Some(Arc::new(Mutex::new(k)))
});
let mut config = AuraConfig::load_and_update_config(prod_id);
config.led_type = aura_type;
let aura = Aura {
hid: device,
backlight,
config: Arc::new(Mutex::new(config))
};
aura.do_initialization().await?;
Ok(Self::Aura(aura))
}
}
+163 -104
View File
@@ -1,88 +1,85 @@
use std::collections::HashMap; use config_traits::{StdConfig, StdConfigLoad3};
use config_traits::{StdConfig, StdConfigLoad1};
use rog_platform::asus_armoury::FirmwareAttribute;
use rog_platform::cpu::CPUEPP; use rog_platform::cpu::CPUEPP;
use rog_platform::platform::PlatformProfile; use rog_platform::platform::ThrottlePolicy;
use serde::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
const CONFIG_FILE: &str = "asusd.ron"; const CONFIG_FILE: &str = "asusd.ron";
#[derive(Default, Clone, Deserialize, Serialize, PartialEq)] #[derive(Deserialize, Serialize, Debug, PartialEq, PartialOrd)]
pub struct Tuning {
pub enabled: bool,
pub group: HashMap<FirmwareAttribute, i32>
}
type Tunings = HashMap<PlatformProfile, Tuning>;
#[derive(Deserialize, Serialize, PartialEq)]
pub struct Config { pub struct Config {
// The current charge limit applied /// Save charge limit for restoring on boot/resume
pub charge_control_end_threshold: u8, pub charge_control_end_threshold: u8,
/// Save charge limit for restoring pub panel_od: bool,
#[serde(skip)] pub boot_sound: bool,
pub base_charge_control_end_threshold: u8, pub mini_led_mode: bool,
pub disable_nvidia_powerd_on_battery: bool, pub disable_nvidia_powerd_on_battery: bool,
/// An optional command/script to run when power is changed to AC /// An optional command/script to run when power is changed to AC
pub ac_command: String, pub ac_command: String,
/// An optional command/script to run when power is changed to battery /// An optional command/script to run when power is changed to battery
pub bat_command: String, pub bat_command: String,
/// Set true if energy_performance_preference should be set if the /// Set true if energy_performance_preference should be set if the
/// platform profile is changed /// throttle/platform profile is changed
pub platform_profile_linked_epp: bool, pub throttle_policy_linked_epp: bool,
/// Which platform profile to use on battery power /// Which throttle/profile to use on battery power
pub platform_profile_on_battery: PlatformProfile, pub throttle_policy_on_battery: ThrottlePolicy,
/// Should the throttle policy be set on bat/ac change? /// Which throttle/profile to use on AC power
pub change_platform_profile_on_battery: bool, pub throttle_policy_on_ac: ThrottlePolicy,
/// Which platform profile to use on AC power /// The energy_performance_preference for this throttle/platform profile
pub platform_profile_on_ac: PlatformProfile, pub throttle_quiet_epp: CPUEPP,
/// Should the platform profile be set on bat/ac change? /// The energy_performance_preference for this throttle/platform profile
pub change_platform_profile_on_ac: bool, pub throttle_balanced_epp: CPUEPP,
/// The energy_performance_preference for this platform profile /// The energy_performance_preference for this throttle/platform profile
pub profile_quiet_epp: CPUEPP, pub throttle_performance_epp: CPUEPP,
/// The energy_performance_preference for this platform profile /// Defaults to `None` if not supported
pub profile_balanced_epp: CPUEPP, #[serde(skip_serializing_if = "Option::is_none", default)]
/// The energy_performance_preference for this platform profile pub ppt_pl1_spl: Option<u8>,
pub profile_performance_epp: CPUEPP, /// Defaults to `None` if not supported
pub ac_profile_tunings: Tunings, #[serde(skip_serializing_if = "Option::is_none", default)]
pub dc_profile_tunings: Tunings, pub ppt_pl2_sppt: Option<u8>,
pub armoury_settings: HashMap<FirmwareAttribute, i32>, /// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_fppt: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_apu_sppt: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_platform_sppt: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_dynamic_boost: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_temp_target: Option<u8>,
/// Temporary state for AC/Batt /// Temporary state for AC/Batt
#[serde(skip)] #[serde(skip)]
pub last_power_plugged: u8 pub last_power_plugged: u8,
}
impl Config {
pub fn select_tunings(&mut self, power_plugged: bool, profile: PlatformProfile) -> &mut Tuning {
let config = if power_plugged {
&mut self.ac_profile_tunings
} else {
&mut self.dc_profile_tunings
};
config.entry(profile).or_insert_with(Tuning::default)
}
} }
impl Default for Config { impl Default for Config {
fn default() -> Self { fn default() -> Self {
Self { Self {
charge_control_end_threshold: 100, charge_control_end_threshold: 100,
base_charge_control_end_threshold: 100, panel_od: false,
boot_sound: false,
mini_led_mode: false,
disable_nvidia_powerd_on_battery: true, disable_nvidia_powerd_on_battery: true,
ac_command: Default::default(), ac_command: Default::default(),
bat_command: Default::default(), bat_command: Default::default(),
platform_profile_linked_epp: true, throttle_policy_linked_epp: true,
platform_profile_on_battery: PlatformProfile::Quiet, throttle_policy_on_battery: ThrottlePolicy::Quiet,
change_platform_profile_on_battery: true, throttle_policy_on_ac: ThrottlePolicy::Performance,
platform_profile_on_ac: PlatformProfile::Performance, throttle_quiet_epp: CPUEPP::Power,
change_platform_profile_on_ac: true, throttle_balanced_epp: CPUEPP::BalancePower,
profile_quiet_epp: CPUEPP::Power, throttle_performance_epp: CPUEPP::Performance,
profile_balanced_epp: CPUEPP::BalancePower, ppt_pl1_spl: Default::default(),
profile_performance_epp: CPUEPP::Performance, ppt_pl2_sppt: Default::default(),
ac_profile_tunings: HashMap::default(), ppt_fppt: Default::default(),
dc_profile_tunings: HashMap::default(), ppt_apu_sppt: Default::default(),
armoury_settings: HashMap::default(), ppt_platform_sppt: Default::default(),
last_power_plugged: Default::default() nv_dynamic_boost: Default::default(),
nv_temp_target: Default::default(),
last_power_plugged: Default::default(),
} }
} }
} }
@@ -92,8 +89,8 @@ impl StdConfig for Config {
Config { Config {
charge_control_end_threshold: 100, charge_control_end_threshold: 100,
disable_nvidia_powerd_on_battery: true, disable_nvidia_powerd_on_battery: true,
platform_profile_on_battery: PlatformProfile::Quiet, throttle_policy_on_battery: ThrottlePolicy::Quiet,
platform_profile_on_ac: PlatformProfile::Performance, throttle_policy_on_ac: ThrottlePolicy::Performance,
ac_command: String::new(), ac_command: String::new(),
bat_command: String::new(), bat_command: String::new(),
..Default::default() ..Default::default()
@@ -109,68 +106,130 @@ impl StdConfig for Config {
} }
} }
impl StdConfigLoad1<Config601> for Config {} impl StdConfigLoad3<Config472, Config506, Config507> for Config {}
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct Config601 { pub struct Config507 {
/// Save charge limit for restoring on boot
pub charge_control_end_threshold: u8, pub charge_control_end_threshold: u8,
#[serde(skip)]
pub base_charge_control_end_threshold: u8,
pub panel_od: bool, pub panel_od: bool,
pub boot_sound: bool,
pub mini_led_mode: bool, pub mini_led_mode: bool,
pub disable_nvidia_powerd_on_battery: bool, pub disable_nvidia_powerd_on_battery: bool,
pub ac_command: String, pub ac_command: String,
pub bat_command: String, pub bat_command: String,
pub platform_profile_linked_epp: bool, pub platform_policy_linked_epp: bool,
pub platform_profile_on_battery: PlatformProfile, pub platform_policy_on_battery: ThrottlePolicy,
pub change_platform_profile_on_battery: bool, pub platform_policy_on_ac: ThrottlePolicy,
pub platform_profile_on_ac: PlatformProfile, //
pub change_platform_profile_on_ac: bool,
pub profile_quiet_epp: CPUEPP,
pub profile_balanced_epp: CPUEPP,
pub profile_performance_epp: CPUEPP,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_pl1_spl: Option<u8>, pub ppt_pl1_spl: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_pl2_sppt: Option<u8>, pub ppt_pl2_sppt: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_pl3_fppt: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_fppt: Option<u8>, pub ppt_fppt: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_apu_sppt: Option<u8>, pub ppt_apu_sppt: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_platform_sppt: Option<u8>, pub ppt_platform_sppt: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_dynamic_boost: Option<u8>, pub nv_dynamic_boost: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_temp_target: Option<u8>, pub nv_temp_target: Option<u8>,
#[serde(skip)]
pub last_power_plugged: u8
} }
impl From<Config601> for Config { impl From<Config507> for Config {
fn from(c: Config601) -> Self { fn from(c: Config507) -> Self {
Self { Self {
// Restore the base charge limit
charge_control_end_threshold: c.charge_control_end_threshold, charge_control_end_threshold: c.charge_control_end_threshold,
base_charge_control_end_threshold: c.charge_control_end_threshold, panel_od: c.panel_od,
boot_sound: false,
disable_nvidia_powerd_on_battery: c.disable_nvidia_powerd_on_battery, disable_nvidia_powerd_on_battery: c.disable_nvidia_powerd_on_battery,
ac_command: c.ac_command, ac_command: c.ac_command,
bat_command: c.bat_command, bat_command: c.bat_command,
platform_profile_linked_epp: c.platform_profile_linked_epp, mini_led_mode: c.mini_led_mode,
platform_profile_on_battery: c.platform_profile_on_battery, throttle_policy_linked_epp: true,
change_platform_profile_on_battery: c.change_platform_profile_on_battery, throttle_policy_on_battery: c.platform_policy_on_battery,
platform_profile_on_ac: c.platform_profile_on_ac, throttle_policy_on_ac: c.platform_policy_on_ac,
change_platform_profile_on_ac: c.change_platform_profile_on_ac, throttle_quiet_epp: CPUEPP::Power,
profile_quiet_epp: c.profile_quiet_epp, throttle_balanced_epp: CPUEPP::BalancePower,
profile_balanced_epp: c.profile_balanced_epp, throttle_performance_epp: CPUEPP::Performance,
profile_performance_epp: c.profile_performance_epp, ppt_pl1_spl: c.ppt_pl1_spl,
last_power_plugged: c.last_power_plugged, ppt_pl2_sppt: c.ppt_pl2_sppt,
ac_profile_tunings: HashMap::default(), ppt_fppt: c.ppt_fppt,
dc_profile_tunings: HashMap::default(), ppt_apu_sppt: c.ppt_apu_sppt,
armoury_settings: HashMap::default() ppt_platform_sppt: c.ppt_platform_sppt,
nv_dynamic_boost: c.nv_dynamic_boost,
nv_temp_target: c.nv_temp_target,
last_power_plugged: 0,
}
}
}
#[derive(Deserialize, Serialize)]
pub struct Config506 {
/// Save charge limit for restoring on boot
pub charge_control_end_threshold: u8,
pub panel_od: bool,
pub mini_led_mode: bool,
pub disable_nvidia_powerd_on_battery: bool,
pub ac_command: String,
pub bat_command: String,
/// Restored on boot as well as when power is plugged
#[serde(skip)]
pub platform_policy_to_restore: ThrottlePolicy,
pub platform_policy_on_battery: ThrottlePolicy,
pub platform_policy_on_ac: ThrottlePolicy,
//
pub ppt_pl1_spl: Option<u8>,
pub ppt_pl2_sppt: Option<u8>,
pub ppt_fppt: Option<u8>,
pub ppt_apu_sppt: Option<u8>,
pub ppt_platform_sppt: Option<u8>,
pub nv_dynamic_boost: Option<u8>,
pub nv_temp_target: Option<u8>,
}
impl From<Config506> for Config {
fn from(c: Config506) -> Self {
Self {
charge_control_end_threshold: c.charge_control_end_threshold,
panel_od: c.panel_od,
boot_sound: false,
disable_nvidia_powerd_on_battery: c.disable_nvidia_powerd_on_battery,
ac_command: c.ac_command,
bat_command: c.bat_command,
mini_led_mode: c.mini_led_mode,
throttle_policy_linked_epp: true,
throttle_policy_on_battery: c.platform_policy_on_battery,
throttle_policy_on_ac: c.platform_policy_on_ac,
throttle_quiet_epp: CPUEPP::Power,
throttle_balanced_epp: CPUEPP::BalancePower,
throttle_performance_epp: CPUEPP::Performance,
ppt_pl1_spl: c.ppt_pl1_spl,
ppt_pl2_sppt: c.ppt_pl2_sppt,
ppt_fppt: c.ppt_fppt,
ppt_apu_sppt: c.ppt_apu_sppt,
ppt_platform_sppt: c.ppt_platform_sppt,
nv_dynamic_boost: c.nv_dynamic_boost,
nv_temp_target: c.nv_temp_target,
last_power_plugged: 0,
}
}
}
#[derive(Deserialize, Serialize)]
pub struct Config472 {
/// Save charge limit for restoring on boot
pub bat_charge_limit: u8,
pub panel_od: bool,
pub mini_led_mode: bool,
pub disable_nvidia_powerd_on_battery: bool,
pub ac_command: String,
pub bat_command: String,
}
impl From<Config472> for Config {
fn from(c: Config472) -> Self {
Self {
charge_control_end_threshold: c.bat_charge_limit,
panel_od: c.panel_od,
disable_nvidia_powerd_on_battery: true,
ac_command: c.ac_command,
bat_command: c.bat_command,
..Default::default()
} }
} }
} }
+229
View File
@@ -0,0 +1,229 @@
use std::time::Duration;
use config_traits::{StdConfig, StdConfigLoad2};
use rog_anime::error::AnimeError;
use rog_anime::usb::Brightness;
use rog_anime::{
ActionData, ActionLoader, AnimTime, Animations, AnimeType, DeviceState, Fade, Vec2,
};
use serde_derive::{Deserialize, Serialize};
const CONFIG_FILE: &str = "anime.ron";
#[derive(Deserialize, Serialize)]
pub struct AnimeConfigV460 {
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
pub sleep: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>,
pub brightness: f32,
}
impl From<AnimeConfigV460> for AnimeConfig {
fn from(c: AnimeConfigV460) -> AnimeConfig {
AnimeConfig {
system: c.system,
boot: c.boot,
wake: c.wake,
shutdown: c.shutdown,
..Default::default()
}
}
}
#[derive(Deserialize, Serialize, Debug)]
pub struct AnimeConfigV472 {
pub model_override: Option<AnimeType>,
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
pub sleep: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>,
pub brightness: f32,
pub display_enabled: bool,
pub display_brightness: Brightness,
pub builtin_anims_enabled: bool,
pub builtin_anims: Animations,
}
impl From<AnimeConfigV472> for AnimeConfig {
fn from(c: AnimeConfigV472) -> AnimeConfig {
AnimeConfig {
system: c.system,
boot: c.boot,
wake: c.wake,
shutdown: c.shutdown,
model_override: c.model_override,
display_enabled: c.display_enabled,
display_brightness: c.display_brightness,
builtin_anims_enabled: c.builtin_anims_enabled,
builtin_anims: c.builtin_anims,
..Default::default()
}
}
}
#[derive(Deserialize, Serialize, Default)]
pub struct AnimeConfigCached {
pub system: Vec<ActionData>,
pub boot: Vec<ActionData>,
pub wake: Vec<ActionData>,
pub shutdown: Vec<ActionData>,
}
impl AnimeConfigCached {
pub fn init_from_config(
&mut self,
config: &AnimeConfig,
anime_type: AnimeType,
) -> Result<(), AnimeError> {
let mut sys = Vec::with_capacity(config.system.len());
for ani in &config.system {
sys.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.system = sys;
let mut boot = Vec::with_capacity(config.boot.len());
for ani in &config.boot {
boot.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.boot = boot;
let mut wake = Vec::with_capacity(config.wake.len());
for ani in &config.wake {
wake.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.wake = wake;
let mut shutdown = Vec::with_capacity(config.shutdown.len());
for ani in &config.shutdown {
shutdown.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.shutdown = shutdown;
Ok(())
}
}
/// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct AnimeConfig {
pub model_override: Option<AnimeType>,
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>,
// pub brightness: f32,
pub display_enabled: bool,
pub display_brightness: Brightness,
pub builtin_anims_enabled: bool,
pub off_when_unplugged: bool,
pub off_when_suspended: bool,
pub off_when_lid_closed: bool,
pub brightness_on_battery: Brightness,
pub builtin_anims: Animations,
}
impl Default for AnimeConfig {
fn default() -> Self {
AnimeConfig {
model_override: None,
system: Vec::new(),
boot: Vec::new(),
wake: Vec::new(),
shutdown: Vec::new(),
// brightness: 1.0,
display_enabled: true,
display_brightness: Brightness::Med,
builtin_anims_enabled: true,
off_when_unplugged: true,
off_when_suspended: true,
off_when_lid_closed: true,
brightness_on_battery: Brightness::Low,
builtin_anims: Animations::default(),
}
}
}
impl StdConfig for AnimeConfig {
fn new() -> Self {
Self::create_default()
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
fn config_dir() -> std::path::PathBuf {
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
}
}
impl StdConfigLoad2<AnimeConfigV460, AnimeConfigV472> for AnimeConfig {}
impl From<&AnimeConfig> for DeviceState {
fn from(config: &AnimeConfig) -> Self {
DeviceState {
display_enabled: config.display_enabled,
display_brightness: config.display_brightness,
builtin_anims_enabled: config.builtin_anims_enabled,
builtin_anims: config.builtin_anims,
off_when_unplugged: config.off_when_unplugged,
off_when_suspended: config.off_when_suspended,
off_when_lid_closed: config.off_when_lid_closed,
brightness_on_battery: config.brightness_on_battery,
}
}
}
impl AnimeConfig {
// fn clamp_config_brightness(mut config: &mut AnimeConfig) {
// if config.brightness < 0.0 || config.brightness > 1.0 {
// warn!(
// "Clamped brightness to [0.0 ; 1.0], was {}",
// config.brightness
// );
// config.brightness = f32::max(0.0, f32::min(1.0, config.brightness));
// }
// }
fn create_default() -> Self {
// create a default config here
AnimeConfig {
system: vec![],
boot: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
brightness: 1.0,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(2)),
Duration::from_secs(2),
)),
}],
wake: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
brightness: 1.0,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(2)),
Duration::from_secs(2),
)),
}],
shutdown: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
scale: 0.9,
angle: 0.0,
translation: Vec2::new(3.0, 2.0),
brightness: 1.0,
time: AnimTime::Infinite,
}],
..Default::default()
}
}
}
+290
View File
@@ -0,0 +1,290 @@
pub mod config;
/// Implements `CtrlTask`, Reloadable, `ZbusRun`
pub mod trait_impls;
use std::convert::TryFrom;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::sleep;
use ::zbus::export::futures_util::lock::Mutex;
use log::{error, info, warn};
use rog_anime::error::AnimeError;
use rog_anime::usb::{
get_anime_type, pkt_flush, pkt_set_brightness, pkt_set_enable_display,
pkt_set_enable_powersave_anim, pkts_for_init, Brightness,
};
use rog_anime::{ActionData, AnimeDataBuffer, AnimePacketType, AnimeType};
use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw;
use self::config::{AnimeConfig, AnimeConfigCached};
use crate::error::RogError;
enum Node {
Usb(USBRaw),
Hid(HidRaw),
}
impl Node {
pub fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
// TODO: map and pass on errors
match self {
Node::Usb(u) => {
u.write_bytes(message).ok();
}
Node::Hid(h) => {
h.write_bytes(message).ok();
}
}
Ok(())
}
pub fn set_builtins_enabled(&self, enabled: bool, bright: Brightness) -> Result<(), RogError> {
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))?;
self.write_bytes(&pkt_set_enable_display(enabled))?;
self.write_bytes(&pkt_set_brightness(bright))?;
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
}
}
pub struct CtrlAnime {
// node: HidRaw,
node: Node,
anime_type: AnimeType,
cache: AnimeConfigCached,
config: AnimeConfig,
// set to force thread to exit
thread_exit: Arc<AtomicBool>,
// Set to false when the thread exits
thread_running: Arc<AtomicBool>,
}
impl CtrlAnime {
#[inline]
pub fn new(config: AnimeConfig) -> Result<CtrlAnime, RogError> {
let usb = USBRaw::new(0x193b).ok();
let hid = HidRaw::new("193b").ok();
let node = if usb.is_some() {
info!("Anime using the USB interface");
unsafe { Node::Usb(usb.unwrap_unchecked()) }
} else if hid.is_some() {
info!("Anime using the HID interface");
unsafe { Node::Hid(hid.unwrap_unchecked()) }
} else {
return Err(RogError::Anime(AnimeError::NoDevice));
};
// TODO: something better to set wakeups disabled
// if matches!(node, Node::Usb(_)) {
// if let Ok(mut enumerator) = udev::Enumerator::new() {
// enumerator.match_subsystem("usb").ok();
// enumerator.match_attribute("idProduct", "193b").ok();
// if let Ok(mut enumer) = enumerator.scan_devices() {
// if let Some(mut dev) = enumer.next() {
// dev.set_attribute_value("power/wakeup", "disabled").ok();
// }
// }
// }
// }
let mut anime_type = get_anime_type()?;
if let AnimeType::Unknown = anime_type {
if let Some(model) = config.model_override {
warn!("Overriding the Animatrix type as {model:?}");
anime_type = model;
}
}
info!("Device has an AniMe Matrix display: {anime_type:?}");
let mut cache = AnimeConfigCached::default();
cache.init_from_config(&config, anime_type)?;
let ctrl = CtrlAnime {
node,
anime_type,
cache,
config,
thread_exit: Arc::new(AtomicBool::new(false)),
thread_running: Arc::new(AtomicBool::new(false)),
};
ctrl.do_initialization()?;
Ok(ctrl)
}
// let device = CtrlAnime::get_device(0x0b05, 0x193b)?;
/// Start an action thread. This is classed as a singleton and there should
/// be only one running - so the thread uses atomics to signal run/exit.
///
/// Because this also writes to the usb device, other write tries (display
/// only) *must* get the mutex lock and set the `thread_exit` atomic.
async fn run_thread(inner: Arc<Mutex<CtrlAnime>>, actions: Vec<ActionData>, mut once: bool) {
if actions.is_empty() {
warn!("AniMe system actions was empty");
return;
}
if let Some(lock) = inner.try_lock() {
lock.node
.write_bytes(&pkt_set_enable_powersave_anim(false))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
}
// Loop rules:
// - Lock the mutex **only when required**. That is, the lock must be held for
// the shortest duration possible.
// - An AtomicBool used for thread exit should be checked in every loop,
// including nested
// The only reason for this outer thread is to prevent blocking while waiting
// for the next spawned thread to exit
// TODO: turn this in to async task (maybe? COuld still risk blocking main
// thread)
std::thread::Builder::new()
.name("AniMe system thread start".into())
.spawn(move || {
info!("AniMe new system thread started");
// Getting copies of these Atomics is done *in* the thread to ensure
// we don't block other threads/main
let thread_exit;
let thread_running;
let anime_type;
loop {
if let Some(lock) = inner.try_lock() {
thread_exit = lock.thread_exit.clone();
thread_running = lock.thread_running.clone();
anime_type = lock.anime_type;
break;
}
}
// First two loops are to ensure we *do* aquire a lock on the mutex
// The reason the loop is required is because the USB writes can block
// for up to 10ms. We can't fail to get the atomics.
while thread_running.load(Ordering::SeqCst) {
// Make any running loop exit first
thread_exit.store(true, Ordering::SeqCst);
}
info!("AniMe no previous system thread running (now)");
thread_exit.store(false, Ordering::SeqCst);
thread_running.store(true, Ordering::SeqCst);
'main: loop {
for action in &actions {
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
match action {
ActionData::Animation(frames) => {
rog_anime::run_animation(frames, &|frame| {
if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: animation sub-loop was asked to exit");
return Ok(true); // Do safe exit
}
inner
.try_lock()
.map(|lock| {
lock.write_data_buffer(frame)
.map_err(|err| {
warn!(
"rog_anime::run_animation:callback {}",
err
);
})
.ok();
false // Don't exit yet
})
.map_or_else(
|| {
warn!("rog_anime::run_animation:callback failed");
Err(AnimeError::NoFrames)
},
Ok,
)
});
if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: sub-loop exited and main loop exiting now");
break 'main;
}
}
ActionData::Image(image) => {
once = false;
if let Some(lock) = inner.try_lock() {
lock.write_data_buffer(image.as_ref().clone())
.map_err(|e| error!("{}", e))
.ok();
}
}
ActionData::Pause(duration) => sleep(*duration),
ActionData::AudioEq
| ActionData::SystemInfo
| ActionData::TimeDate
| ActionData::Matrix => {}
}
}
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
if once || actions.is_empty() {
break 'main;
}
}
// Clear the display on exit
if let Some(lock) = inner.try_lock() {
if let Ok(data) =
AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()])
.map_err(|e| error!("{}", e))
{
lock.write_data_buffer(data)
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
}
lock.node
.write_bytes(&pkt_set_enable_powersave_anim(
lock.config.builtin_anims_enabled,
))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
}
// Loop ended, set the atmonics
thread_running.store(false, Ordering::SeqCst);
info!("AniMe system thread exited");
})
.map(|err| info!("AniMe system thread: {:?}", err))
.ok();
}
/// Write only a data packet. This will modify the leds brightness using the
/// global brightness set in config.
fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) -> Result<(), RogError> {
for led in buffer.data_mut().iter_mut() {
let mut bright = *led as f32;
if bright > 254.0 {
bright = 254.0;
}
*led = bright as u8;
}
let data = AnimePacketType::try_from(buffer)?;
for row in &data {
self.node.write_bytes(row)?;
}
self.node.write_bytes(&pkt_flush())?;
Ok(())
}
fn do_initialization(&self) -> Result<(), RogError> {
let pkts = pkts_for_init();
self.node.write_bytes(&pkts[0])?;
self.node.write_bytes(&pkts[1])?;
Ok(())
}
}
@@ -1,22 +1,23 @@
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use std::sync::Arc;
use config_traits::StdConfig; use config_traits::StdConfig;
use log::{debug, error, warn}; use log::warn;
use logind_zbus::manager::ManagerProxy; use logind_zbus::manager::ManagerProxy;
use rog_anime::usb::{ use rog_anime::usb::{
pkt_set_brightness, pkt_set_builtin_animations, pkt_set_enable_display, pkt_set_brightness, pkt_set_builtin_animations, pkt_set_enable_display,
pkt_set_enable_powersave_anim, Brightness pkt_set_enable_powersave_anim, Brightness,
}; };
use rog_anime::{Animations, AnimeDataBuffer, DeviceState}; use rog_anime::{Animations, AnimeDataBuffer, DeviceState};
use zbus::object_server::SignalEmitter; use zbus::export::futures_util::lock::Mutex;
use zbus::proxy::CacheProperties; use zbus::{interface, CacheProperties, Connection, SignalContext};
use zbus::zvariant::OwnedObjectPath;
use zbus::{interface, Connection};
use super::config::AniMeConfig; use super::config::AnimeConfig;
use super::AniMe; use super::CtrlAnime;
use crate::error::RogError; use crate::error::RogError;
use crate::Reloadable;
pub const ANIME_ZBUS_NAME: &str = "Anime";
pub const ANIME_ZBUS_PATH: &str = "/org/asuslinux";
async fn get_logind_manager<'a>() -> ManagerProxy<'a> { async fn get_logind_manager<'a>() -> ManagerProxy<'a> {
let connection = Connection::system() let connection = Connection::system()
@@ -31,117 +32,104 @@ async fn get_logind_manager<'a>() -> ManagerProxy<'a> {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct AniMeZbus(AniMe); pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>);
impl AniMeZbus { /// The struct with the main dbus methods requires this trait
pub fn new(anime: AniMe) -> Self { impl crate::ZbusRun for CtrlAnimeZbus {
Self(anime) async fn add_to_server(self, server: &mut Connection) {
} Self::add_to_server_helper(self, ANIME_ZBUS_PATH, server).await;
pub async fn start_tasks(
mut self,
connection: &Connection,
path: OwnedObjectPath
) -> Result<(), RogError> {
// let task = zbus.clone();
self.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
connection
.object_server()
.at(path.clone(), self)
.await
.map_err(|e| {
error!("Couldn't add server at path: {path}, {e:?}");
e
})?;
debug!("start_tasks was successful");
Ok(())
} }
} }
// None of these calls can be guarnateed to succeed unless we loop until okay // None of these calls can be guarnateed to succeed unless we loop until okay
// If the try_lock *does* succeed then any other thread trying to lock will not // If the try_lock *does* succeed then any other thread trying to lock will not
// grab it until we finish. // grab it until we finish.
#[interface(name = "xyz.ljones.Anime")] #[interface(name = "org.asuslinux.Anime")]
impl AniMeZbus { impl CtrlAnimeZbus {
/// Writes a data stream of length. Will force system thread to exit until /// Writes a data stream of length. Will force system thread to exit until
/// it is restarted /// it is restarted
async fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> { async fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> {
let bright = self.0.config.lock().await.display_brightness; self.0
self.0.set_builtins_enabled(false, bright).await?; .lock()
self.0.thread_exit.store(true, Ordering::SeqCst); .await
self.0.write_data_buffer(input).await.map_err(|err| { .thread_exit
warn!("ctrl_anime::run_animation:callback {}", err); .store(true, Ordering::SeqCst);
err self.0
})?; .lock()
.await
.write_data_buffer(input)
.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
err
})?;
Ok(()) Ok(())
} }
/// Set base brightness level /// Set base brightness level
#[zbus(property)] #[zbus(property)]
async fn brightness(&self) -> Brightness { async fn brightness(&self) -> Brightness {
if let Some(config) = self.0.config.try_lock() { self.0.lock().await.config.display_brightness
return config.display_brightness;
}
Brightness::Off
} }
/// Set base brightness level /// Set base brightness level
#[zbus(property)] #[zbus(property)]
async fn set_brightness(&self, brightness: Brightness) { async fn set_brightness(&self, brightness: Brightness) {
self.0 self.0
.write_bytes(&pkt_set_brightness(brightness)) .lock()
.await .await
.node
.write_bytes(&pkt_set_brightness(brightness))
.map_err(|err| { .map_err(|err| {
warn!("ctrl_anime::set_brightness {}", err); warn!("ctrl_anime::set_brightness {}", err);
}) })
.ok(); .ok();
self.0 self.0
.write_bytes(&pkt_set_enable_display(brightness != Brightness::Off)) .lock()
.await .await
.node
.write_bytes(&pkt_set_enable_display(brightness != Brightness::Off))
.map_err(|err| { .map_err(|err| {
warn!("ctrl_anime::set_brightness {}", err); warn!("ctrl_anime::set_brightness {}", err);
}) })
.ok(); .ok();
let mut config = self.0.config.lock().await; self.0.lock().await.config.display_enabled = brightness != Brightness::Off;
config.display_enabled = brightness != Brightness::Off; self.0.lock().await.config.display_brightness = brightness;
config.display_brightness = brightness; self.0.lock().await.config.write();
config.write();
} }
#[zbus(property)] #[zbus(property)]
async fn builtins_enabled(&self) -> bool { async fn builtins_enabled(&self) -> bool {
if let Some(config) = self.0.config.try_lock() { let lock = self.0.lock().await;
return config.builtin_anims_enabled; lock.config.builtin_anims_enabled
}
false
} }
/// Enable the builtin animations or not. This is quivalent to "Powersave /// Enable the builtin animations or not. This is quivalent to "Powersave
/// animations" in Armory crate /// animations" in Armory crate
#[zbus(property)] #[zbus(property)]
async fn set_builtins_enabled(&self, enabled: bool) { async fn set_builtins_enabled(&self, enabled: bool) {
let mut config = self.0.config.lock().await; let brightness = self.0.lock().await.config.display_brightness;
let brightness = config.display_brightness;
self.0 self.0
.set_builtins_enabled(enabled, brightness) .lock()
.await .await
.node
.set_builtins_enabled(enabled, brightness)
.map_err(|err| { .map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err); warn!("ctrl_anime::set_builtins_enabled {}", err);
}) })
.ok(); .ok();
if !enabled { if !enabled {
let anime_type = config.anime_type; let anime_type = self.0.lock().await.anime_type;
let data = vec![255u8; anime_type.data_length()]; let data = vec![255u8; anime_type.data_length()];
if let Ok(tmp) = AnimeDataBuffer::from_vec(anime_type, data).map_err(|err| { if let Ok(tmp) = AnimeDataBuffer::from_vec(anime_type, data).map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err); warn!("ctrl_anime::set_builtins_enabled {}", err);
}) { }) {
self.0 self.0
.write_bytes(tmp.data()) .lock()
.await .await
.node
.write_bytes(tmp.data())
.map_err(|err| { .map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err); warn!("ctrl_anime::set_builtins_enabled {}", err);
}) })
@@ -149,75 +137,77 @@ impl AniMeZbus {
} }
} }
config.builtin_anims_enabled = enabled; self.0.lock().await.config.builtin_anims_enabled = enabled;
config.write(); self.0.lock().await.config.write();
if enabled { if enabled {
self.0.thread_exit.store(true, Ordering::Release); self.0
.lock()
.await
.thread_exit
.store(true, Ordering::Release);
} }
} }
#[zbus(property)] #[zbus(property)]
async fn builtin_animations(&self) -> Animations { async fn builtin_animations(&self) -> Animations {
if let Some(config) = self.0.config.try_lock() { self.0.lock().await.config.builtin_anims
return config.builtin_anims;
}
Animations::default()
} }
/// Set which builtin animation is used for each stage /// Set which builtin animation is used for each stage
#[zbus(property)] #[zbus(property)]
async fn set_builtin_animations(&self, settings: Animations) { async fn set_builtin_animations(&self, settings: Animations) {
self.0 self.0
.write_bytes(&pkt_set_builtin_animations( .lock()
settings.boot, settings.awake, settings.sleep, settings.shutdown
))
.await .await
.node
.write_bytes(&pkt_set_builtin_animations(
settings.boot,
settings.awake,
settings.sleep,
settings.shutdown,
))
.map_err(|err| { .map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err); warn!("ctrl_anime::run_animation:callback {}", err);
}) })
.ok(); .ok();
self.0 self.0
.write_bytes(&pkt_set_enable_powersave_anim(true)) .lock()
.await .await
.node
.write_bytes(&pkt_set_enable_powersave_anim(true))
.map_err(|err| { .map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err); warn!("ctrl_anime::run_animation:callback {}", err);
}) })
.ok(); .ok();
let mut config = self.0.config.lock().await; self.0.lock().await.config.display_enabled = true;
config.display_enabled = true; self.0.lock().await.config.builtin_anims = settings;
config.builtin_anims = settings; self.0.lock().await.config.write();
config.write();
} }
#[zbus(property)] #[zbus(property)]
async fn enable_display(&self) -> bool { async fn enable_display(&self) -> bool {
if let Some(config) = self.0.config.try_lock() { self.0.lock().await.config.display_enabled
return config.display_enabled;
}
false
} }
/// Set whether the AniMe is enabled at all /// Set whether the AniMe is enabled at all
#[zbus(property)] #[zbus(property)]
async fn set_enable_display(&self, enabled: bool) { async fn set_enable_display(&self, enabled: bool) {
self.0 self.0
.write_bytes(&pkt_set_enable_display(enabled)) .lock()
.await .await
.node
.write_bytes(&pkt_set_enable_display(enabled))
.map_err(|err| { .map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err); warn!("ctrl_anime::run_animation:callback {}", err);
}) })
.ok(); .ok();
let mut config = self.0.config.lock().await; self.0.lock().await.config.display_enabled = enabled;
config.display_enabled = enabled; self.0.lock().await.config.write();
config.write();
} }
#[zbus(property)] #[zbus(property)]
async fn off_when_unplugged(&self) -> bool { async fn off_when_unplugged(&self) -> bool {
if let Some(config) = self.0.config.try_lock() { self.0.lock().await.config.off_when_unplugged
return config.off_when_unplugged;
}
false
} }
/// Set if to turn the AniMe Matrix off when external power is unplugged /// Set if to turn the AniMe Matrix off when external power is unplugged
@@ -227,40 +217,34 @@ impl AniMeZbus {
let pow = manager.on_external_power().await.unwrap_or_default(); let pow = manager.on_external_power().await.unwrap_or_default();
self.0 self.0
.write_bytes(&pkt_set_enable_display(!pow && !enabled)) .lock()
.await .await
.node
.write_bytes(&pkt_set_enable_display(!pow && !enabled))
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err); warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
}) })
.ok(); .ok();
let mut config = self.0.config.lock().await; self.0.lock().await.config.off_when_unplugged = enabled;
config.off_when_unplugged = enabled; self.0.lock().await.config.write();
config.write();
} }
#[zbus(property)] #[zbus(property)]
async fn off_when_suspended(&self) -> bool { async fn off_when_suspended(&self) -> bool {
if let Some(config) = self.0.config.try_lock() { self.0.lock().await.config.off_when_suspended
return config.off_when_suspended;
}
false
} }
/// Set if to turn the AniMe Matrix off when the laptop is suspended /// Set if to turn the AniMe Matrix off when the laptop is suspended
#[zbus(property)] #[zbus(property)]
async fn set_off_when_suspended(&self, enabled: bool) { async fn set_off_when_suspended(&self, enabled: bool) {
let mut config = self.0.config.lock().await; self.0.lock().await.config.off_when_suspended = enabled;
config.off_when_suspended = enabled; self.0.lock().await.config.write();
config.write();
} }
#[zbus(property)] #[zbus(property)]
async fn off_when_lid_closed(&self) -> bool { async fn off_when_lid_closed(&self) -> bool {
if let Some(config) = self.0.config.try_lock() { self.0.lock().await.config.off_when_lid_closed
return config.off_when_lid_closed;
}
false
} }
/// Set if to turn the AniMe Matrix off when the lid is closed /// Set if to turn the AniMe Matrix off when the lid is closed
@@ -270,40 +254,50 @@ impl AniMeZbus {
let lid = manager.lid_closed().await.unwrap_or_default(); let lid = manager.lid_closed().await.unwrap_or_default();
self.0 self.0
.write_bytes(&pkt_set_enable_display(lid && !enabled)) .lock()
.await .await
.node
.write_bytes(&pkt_set_enable_display(lid && !enabled))
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err); warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
}) })
.ok(); .ok();
let mut config = self.0.config.lock().await; self.0.lock().await.config.off_when_lid_closed = enabled;
config.off_when_lid_closed = enabled; self.0.lock().await.config.write();
config.write();
} }
/// The main loop is the base system set action if the user isn't running /// The main loop is the base system set action if the user isn't running
/// the user daemon /// the user daemon
async fn run_main_loop(&self, start: bool) { async fn run_main_loop(&self, start: bool) {
if start { if start {
self.0.thread_exit.store(true, Ordering::SeqCst); self.0
self.0.run_thread(self.0.cache.system.clone(), false).await; .lock()
.await
.thread_exit
.store(true, Ordering::SeqCst);
CtrlAnime::run_thread(
self.0.clone(),
self.0.lock().await.cache.system.clone(),
false,
)
.await;
} }
} }
/// Get the device state as stored by asusd /// Get the device state as stored by asusd
// #[zbus(property)] // #[zbus(property)]
async fn device_state(&self) -> DeviceState { async fn device_state(&self) -> DeviceState {
DeviceState::from(&*self.0.config.lock().await) DeviceState::from(&self.0.lock().await.config)
} }
} }
impl crate::CtrlTask for AniMeZbus { impl crate::CtrlTask for CtrlAnimeZbus {
fn zbus_path() -> &'static str { fn zbus_path() -> &'static str {
"ANIME_ZBUS_PATH" ANIME_ZBUS_PATH
} }
async fn create_tasks(&self, _: SignalEmitter<'static>) -> Result<(), RogError> { async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
let inner1 = self.0.clone(); let inner1 = self.0.clone();
let inner2 = self.0.clone(); let inner2 = self.0.clone();
let inner3 = self.0.clone(); let inner3 = self.0.clone();
@@ -313,15 +307,21 @@ impl crate::CtrlTask for AniMeZbus {
// on_sleep // on_sleep
let inner = inner1.clone(); let inner = inner1.clone();
async move { async move {
let config = inner.config.lock().await.clone(); let config = inner.lock().await.config.clone();
if config.display_enabled { if config.display_enabled {
inner.thread_exit.store(true, Ordering::Release); // ensure clean slate inner
.lock()
.await
.thread_exit
.store(true, Ordering::Release); // ensure clean slate
inner inner
.write_bytes(&pkt_set_enable_display( .lock()
!(sleeping && config.off_when_suspended)
))
.await .await
.node
.write_bytes(&pkt_set_enable_display(
!(sleeping && config.off_when_suspended),
))
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err); warn!("create_sys_event_tasks::off_when_suspended {}", err);
}) })
@@ -329,10 +329,12 @@ impl crate::CtrlTask for AniMeZbus {
if config.builtin_anims_enabled { if config.builtin_anims_enabled {
inner inner
.write_bytes(&pkt_set_enable_powersave_anim( .lock()
!(sleeping && config.off_when_suspended)
))
.await .await
.node
.write_bytes(&pkt_set_enable_powersave_anim(
!(sleeping && config.off_when_suspended),
))
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err); warn!("create_sys_event_tasks::off_when_suspended {}", err);
}) })
@@ -340,11 +342,18 @@ impl crate::CtrlTask for AniMeZbus {
} else if !sleeping && !config.builtin_anims_enabled { } else if !sleeping && !config.builtin_anims_enabled {
// Run custom wake animation // Run custom wake animation
inner inner
.write_bytes(&pkt_set_enable_powersave_anim(false)) .lock()
.await .await
.node
.write_bytes(&pkt_set_enable_powersave_anim(false))
.ok(); // ensure builtins are disabled .ok(); // ensure builtins are disabled
inner.run_thread(inner.cache.wake.clone(), true).await; CtrlAnime::run_thread(
inner.clone(),
inner.lock().await.cache.wake.clone(),
true,
)
.await;
} }
} }
} }
@@ -353,16 +362,26 @@ impl crate::CtrlTask for AniMeZbus {
// on_shutdown // on_shutdown
let inner = inner2.clone(); let inner = inner2.clone();
async move { async move {
let AniMeConfig { let AnimeConfig {
display_enabled, display_enabled,
builtin_anims_enabled, builtin_anims_enabled,
.. ..
} = *inner.config.lock().await; } = inner.lock().await.config;
if display_enabled && !builtin_anims_enabled { if display_enabled && !builtin_anims_enabled {
if shutting_down { if shutting_down {
inner.run_thread(inner.cache.shutdown.clone(), true).await; CtrlAnime::run_thread(
inner.clone(),
inner.lock().await.cache.shutdown.clone(),
true,
)
.await;
} else { } else {
inner.run_thread(inner.cache.boot.clone(), true).await; CtrlAnime::run_thread(
inner.clone(),
inner.lock().await.cache.boot.clone(),
true,
)
.await;
} }
} }
} }
@@ -371,24 +390,28 @@ impl crate::CtrlTask for AniMeZbus {
let inner = inner3.clone(); let inner = inner3.clone();
// on lid change // on lid change
async move { async move {
let AniMeConfig { let AnimeConfig {
off_when_lid_closed, off_when_lid_closed,
builtin_anims_enabled, builtin_anims_enabled,
.. ..
} = *inner.config.lock().await; } = inner.lock().await.config;
if off_when_lid_closed { if off_when_lid_closed {
if builtin_anims_enabled { if builtin_anims_enabled {
inner inner
.write_bytes(&pkt_set_enable_powersave_anim(!lid_closed)) .lock()
.await .await
.node
.write_bytes(&pkt_set_enable_powersave_anim(!lid_closed))
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err); warn!("create_sys_event_tasks::off_when_suspended {}", err);
}) })
.ok(); .ok();
} }
inner inner
.write_bytes(&pkt_set_enable_display(!lid_closed)) .lock()
.await .await
.node
.write_bytes(&pkt_set_enable_display(!lid_closed))
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err); warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
}) })
@@ -400,40 +423,46 @@ impl crate::CtrlTask for AniMeZbus {
let inner = inner4.clone(); let inner = inner4.clone();
// on power change // on power change
async move { async move {
let AniMeConfig { let AnimeConfig {
off_when_unplugged, off_when_unplugged,
builtin_anims_enabled, builtin_anims_enabled,
brightness_on_battery, brightness_on_battery,
.. ..
} = *inner.config.lock().await; } = inner.lock().await.config;
if off_when_unplugged { if off_when_unplugged {
if builtin_anims_enabled { if builtin_anims_enabled {
inner inner
.write_bytes(&pkt_set_enable_powersave_anim(power_plugged)) .lock()
.await .await
.node
.write_bytes(&pkt_set_enable_powersave_anim(power_plugged))
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err); warn!("create_sys_event_tasks::off_when_suspended {}", err);
}) })
.ok(); .ok();
} }
inner inner
.write_bytes(&pkt_set_enable_display(power_plugged)) .lock()
.await .await
.node
.write_bytes(&pkt_set_enable_display(power_plugged))
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_unplugged {}", err); warn!("create_sys_event_tasks::off_when_unplugged {}", err);
}) })
.ok(); .ok();
} else { } else {
inner inner
.write_bytes(&pkt_set_brightness(brightness_on_battery)) .lock()
.await .await
.node
.write_bytes(&pkt_set_brightness(brightness_on_battery))
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_unplugged {}", err); warn!("create_sys_event_tasks::off_when_unplugged {}", err);
}) })
.ok(); .ok();
} }
} }
} },
) )
.await; .await;
@@ -441,62 +470,52 @@ impl crate::CtrlTask for AniMeZbus {
} }
} }
impl crate::Reloadable for AniMeZbus { impl crate::Reloadable for CtrlAnimeZbus {
async fn reload(&mut self) -> Result<(), RogError> { async fn reload(&mut self) -> Result<(), RogError> {
let AniMeConfig { if let Some(lock) = self.0.try_lock() {
builtin_anims_enabled, let anim = &lock.config.builtin_anims;
builtin_anims, // Set builtins
display_enabled, if lock.config.builtin_anims_enabled {
display_brightness, lock.node.write_bytes(&pkt_set_builtin_animations(
off_when_lid_closed, anim.boot,
off_when_unplugged, anim.awake,
.. anim.sleep,
} = *self.0.config.lock().await; anim.shutdown,
))?;
}
// Builtins enabled or na?
lock.node.set_builtins_enabled(
lock.config.builtin_anims_enabled,
lock.config.display_brightness,
)?;
// Set builtins let manager = get_logind_manager().await;
if builtin_anims_enabled { let lid_closed = manager.lid_closed().await.unwrap_or_default();
self.0 let power_plugged = manager.on_external_power().await.unwrap_or_default();
.write_bytes(&pkt_set_builtin_animations(
builtin_anims.boot,
builtin_anims.awake,
builtin_anims.sleep,
builtin_anims.shutdown
))
.await?;
}
// Builtins enabled or na?
self.0
.set_builtins_enabled(builtin_anims_enabled, display_brightness)
.await?;
let manager = get_logind_manager().await; let turn_off = (lid_closed && lock.config.off_when_lid_closed)
let lid_closed = manager.lid_closed().await.unwrap_or_default(); || (!power_plugged && lock.config.off_when_unplugged);
let power_plugged = manager.on_external_power().await.unwrap_or_default(); lock.node
.write_bytes(&pkt_set_enable_display(!turn_off))
let turn_off = .map_err(|err| {
(lid_closed && off_when_lid_closed) || (!power_plugged && off_when_unplugged); warn!("create_sys_event_tasks::reload {}", err);
self.0 })
.write_bytes(&pkt_set_enable_display(!turn_off))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::reload {}", err);
})
.ok();
if turn_off || !display_enabled {
self.0.write_bytes(&pkt_set_enable_display(false)).await?;
// early return so we don't run animation thread
return Ok(());
}
if !builtin_anims_enabled && !self.0.cache.boot.is_empty() {
self.0
.write_bytes(&pkt_set_enable_powersave_anim(false))
.await
.ok(); .ok();
let action = self.0.cache.boot.clone(); if turn_off || !lock.config.display_enabled {
self.0.run_thread(action, true).await; lock.node.write_bytes(&pkt_set_enable_display(false))?;
// early return so we don't run animation thread
return Ok(());
}
if !lock.config.builtin_anims_enabled && !lock.cache.boot.is_empty() {
lock.node
.write_bytes(&pkt_set_enable_powersave_anim(false))
.ok();
let action = lock.cache.boot.clone();
CtrlAnime::run_thread(self.0.clone(), action, true).await;
}
} }
Ok(()) Ok(())
} }
@@ -5,22 +5,14 @@ use log::{debug, info, warn};
use rog_aura::aura_detection::LedSupportData; use rog_aura::aura_detection::LedSupportData;
use rog_aura::keyboard::LaptopAuraPower; use rog_aura::keyboard::LaptopAuraPower;
use rog_aura::{ use rog_aura::{
AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, Direction, LedBrightness, Speed, GRADIENT AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, Direction, LedBrightness, Speed, GRADIENT,
}; };
use serde::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use crate::error::RogError;
#[derive(Deserialize, Serialize, Default, Debug, Clone)] #[derive(Deserialize, Serialize, Default, Debug, Clone)]
// #[serde(default)] // #[serde(default)]
pub struct AuraConfig { pub struct AuraConfig {
#[serde(skip)]
pub led_type: AuraDeviceType,
#[serde(skip)]
pub support_data: LedSupportData,
pub config_name: String, pub config_name: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ally_fix: Option<bool>,
pub brightness: LedBrightness, pub brightness: LedBrightness,
pub current_mode: AuraModeNum, pub current_mode: AuraModeNum,
pub builtins: BTreeMap<AuraModeNum, AuraEffect>, pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
@@ -28,8 +20,6 @@ pub struct AuraConfig {
pub multizone: Option<BTreeMap<AuraModeNum, Vec<AuraEffect>>>, pub multizone: Option<BTreeMap<AuraModeNum, Vec<AuraEffect>>>,
pub multizone_on: bool, pub multizone_on: bool,
pub enabled: LaptopAuraPower, pub enabled: LaptopAuraPower,
#[serde(skip)]
pub per_key_mode_active: bool
} }
impl StdConfig for AuraConfig { impl StdConfig for AuraConfig {
@@ -64,35 +54,31 @@ impl AuraConfig {
let support_data = LedSupportData::get_data(prod_id); let support_data = LedSupportData::get_data(prod_id);
let enabled = LaptopAuraPower::new(device_type, &support_data); let enabled = LaptopAuraPower::new(device_type, &support_data);
let mut config = AuraConfig { let mut config = AuraConfig {
led_type: device_type,
support_data,
config_name: format!("aura_{prod_id}.ron"), config_name: format!("aura_{prod_id}.ron"),
ally_fix: None,
brightness: LedBrightness::Med, brightness: LedBrightness::Med,
current_mode: AuraModeNum::Static, current_mode: AuraModeNum::Static,
builtins: BTreeMap::new(), builtins: BTreeMap::new(),
multizone: None, multizone: None,
multizone_on: false, multizone_on: false,
enabled, enabled,
per_key_mode_active: false
}; };
for n in &config.support_data.basic_modes { for n in &support_data.basic_modes {
debug!("creating default for {n}"); debug!("creating default for {n}");
config config
.builtins .builtins
.insert(*n, AuraEffect::default_with_mode(*n)); .insert(*n, AuraEffect::default_with_mode(*n));
if !config.support_data.basic_zones.is_empty() { if !support_data.basic_zones.is_empty() {
let mut default = vec![]; let mut default = vec![];
for (i, tmp) in config.support_data.basic_zones.iter().enumerate() { for (i, tmp) in support_data.basic_zones.iter().enumerate() {
default.push(AuraEffect { default.push(AuraEffect {
mode: *n, mode: *n,
zone: *tmp, zone: *tmp,
colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]), colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]),
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]), colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
speed: Speed::Med, speed: Speed::Med,
direction: Direction::Left direction: Direction::Left,
}); });
} }
if let Some(m) = config.multizone.as_mut() { if let Some(m) = config.multizone.as_mut() {
@@ -144,109 +130,23 @@ impl AuraConfig {
} }
None None
} }
/// Create a default for the `current_mode` if multizone and no config
/// exists.
pub fn create_multizone_default(&mut self) -> Result<(), RogError> {
let mut default = vec![];
for (i, tmp) in self.support_data.basic_zones.iter().enumerate() {
default.push(AuraEffect {
mode: self.current_mode,
zone: *tmp,
colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]),
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
speed: Speed::Med,
direction: Direction::Left
});
}
if default.is_empty() {
return Err(RogError::AuraEffectNotSupported);
}
if let Some(multizones) = self.multizone.as_mut() {
multizones.insert(self.current_mode, default);
} else {
let mut tmp = BTreeMap::new();
tmp.insert(self.current_mode, default);
self.multizone = Some(tmp);
}
Ok(())
}
/// Reload the config from disk then verify and update it if required.
/// Always rewrites the file to disk.
pub fn load_and_update_config(prod_id: &str) -> AuraConfig {
// New loads data from the DB also
let mut config_init = AuraConfig::new(prod_id);
// config_init.set_filename(prod_id);
let mut config_loaded = config_init.clone().load();
// update the initialised data with what we loaded from disk
for mode_init in &mut config_init.builtins {
// update init values from loaded values if they exist
if let Some(loaded) = config_loaded.builtins.get(mode_init.0) {
*mode_init.1 = loaded.clone();
}
}
// Then replace just incase the initialised data contains new modes added
config_loaded.builtins = config_init.builtins;
config_loaded.support_data = config_init.support_data;
config_loaded.led_type = config_init.led_type;
config_loaded.ally_fix = config_init.ally_fix;
for enabled_init in &mut config_init.enabled.states {
for enabled in &mut config_loaded.enabled.states {
if enabled.zone == enabled_init.zone {
*enabled_init = *enabled;
break;
}
}
}
config_loaded.enabled = config_init.enabled;
if let (Some(mut multizone_init), Some(multizone_loaded)) =
(config_init.multizone, config_loaded.multizone.as_mut())
{
for mode in multizone_init.iter_mut() {
// update init values from loaded values if they exist
if let Some(loaded) = multizone_loaded.get(mode.0) {
let mut new_set = Vec::new();
let data = LedSupportData::get_data(prod_id);
// only reuse a zone mode if the mode is supported
for mode in loaded {
if data.basic_modes.contains(&mode.mode) {
new_set.push(mode.clone());
}
}
*mode.1 = new_set;
}
}
*multizone_loaded = multizone_init;
}
config_loaded.write();
config_loaded
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rog_aura::keyboard::AuraPowerState; use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour};
use rog_aura::{
AuraEffect, AuraModeNum, AuraZone, Colour, Direction, LedBrightness, PowerZones, Speed
};
use super::AuraConfig; use super::AuraConfig;
#[test] #[test]
fn set_multizone_4key_config() { fn set_multizone_4key_config() {
std::env::set_var("BOARD_NAME", "");
let mut config = AuraConfig::new("19b6"); let mut config = AuraConfig::new("19b6");
let effect = AuraEffect { let effect = AuraEffect {
colour1: Colour { colour1: Colour {
r: 0xff, r: 0xff,
g: 0x00, g: 0x00,
b: 0xff b: 0xff,
}, },
zone: AuraZone::Key1, zone: AuraZone::Key1,
..Default::default() ..Default::default()
@@ -259,7 +159,7 @@ mod tests {
colour1: Colour { colour1: Colour {
r: 0x00, r: 0x00,
g: 0xff, g: 0xff,
b: 0xff b: 0xff,
}, },
zone: AuraZone::Key2, zone: AuraZone::Key2,
..Default::default() ..Default::default()
@@ -270,7 +170,7 @@ mod tests {
colour1: Colour { colour1: Colour {
r: 0xff, r: 0xff,
g: 0xff, g: 0xff,
b: 0x00 b: 0x00,
}, },
zone: AuraZone::Key3, zone: AuraZone::Key3,
..Default::default() ..Default::default()
@@ -281,7 +181,7 @@ mod tests {
colour1: Colour { colour1: Colour {
r: 0x00, r: 0x00,
g: 0xff, g: 0xff,
b: 0x00 b: 0x00,
}, },
zone: AuraZone::Key4, zone: AuraZone::Key4,
..Default::default() ..Default::default()
@@ -294,31 +194,42 @@ mod tests {
let res = config.multizone.unwrap(); let res = config.multizone.unwrap();
let sta = res.get(&AuraModeNum::Static).unwrap(); let sta = res.get(&AuraModeNum::Static).unwrap();
assert_eq!(sta.len(), 4); assert_eq!(sta.len(), 4);
assert_eq!(sta[0].colour1, Colour { assert_eq!(
r: 0xff, sta[0].colour1,
g: 0x00, Colour {
b: 0xff r: 0xff,
}); g: 0x00,
assert_eq!(sta[1].colour1, Colour { b: 0xff
r: 0x00, }
g: 0xff, );
b: 0xff assert_eq!(
}); sta[1].colour1,
assert_eq!(sta[2].colour1, Colour { Colour {
r: 0xff, r: 0x00,
g: 0xff, g: 0xff,
b: 0x00 b: 0xff
}); }
assert_eq!(sta[3].colour1, Colour { );
r: 0x00, assert_eq!(
g: 0xff, sta[2].colour1,
b: 0x00 Colour {
}); r: 0xff,
g: 0xff,
b: 0x00
}
);
assert_eq!(
sta[3].colour1,
Colour {
r: 0x00,
g: 0xff,
b: 0x00
}
);
} }
#[test] #[test]
fn set_multizone_multimode_config() { fn set_multizone_multimode_config() {
std::env::set_var("BOARD_NAME", "");
let mut config = AuraConfig::new("19b6"); let mut config = AuraConfig::new("19b6");
let effect = AuraEffect { let effect = AuraEffect {
@@ -363,54 +274,4 @@ mod tests {
let sta = res.get(&AuraModeNum::Pulse).unwrap(); let sta = res.get(&AuraModeNum::Pulse).unwrap();
assert_eq!(sta.len(), 1); assert_eq!(sta.len(), 1);
} }
#[test]
fn verify_0x1866_g531i() {
std::env::set_var("BOARD_NAME", "G513I");
let mut config = AuraConfig::new("1866");
assert_eq!(config.brightness, LedBrightness::Med);
assert_eq!(config.builtins.len(), 5);
assert_eq!(config.builtins.first_entry().unwrap().get(), &AuraEffect {
mode: AuraModeNum::Static,
zone: AuraZone::None,
colour1: Colour { r: 166, g: 0, b: 0 },
colour2: Colour { r: 0, g: 0, b: 0 },
speed: Speed::Med,
direction: Direction::Right
});
assert_eq!(config.enabled.states.len(), 1);
assert_eq!(config.enabled.states[0], AuraPowerState {
zone: PowerZones::KeyboardAndLightbar,
boot: true,
awake: true,
sleep: true,
shutdown: true
});
}
#[test]
fn verify_0x19b6_g634j() {
std::env::set_var("BOARD_NAME", "G634J");
let mut config = AuraConfig::new("19b6");
assert_eq!(config.brightness, LedBrightness::Med);
assert_eq!(config.builtins.len(), 12);
assert_eq!(config.builtins.first_entry().unwrap().get(), &AuraEffect {
mode: AuraModeNum::Static,
zone: AuraZone::None,
colour1: Colour { r: 166, g: 0, b: 0 },
colour2: Colour { r: 0, g: 0, b: 0 },
speed: Speed::Med,
direction: Direction::Right
});
assert_eq!(config.enabled.states.len(), 4);
assert_eq!(config.enabled.states[0], AuraPowerState {
zone: PowerZones::Keyboard,
boot: true,
awake: true,
sleep: true,
shutdown: true
});
}
} }
+458
View File
@@ -0,0 +1,458 @@
use std::collections::{BTreeMap, HashSet};
use config_traits::{StdConfig, StdConfigLoad};
use inotify::Inotify;
use log::{debug, info, warn};
use rog_aura::aura_detection::LedSupportData;
use rog_aura::keyboard::{LedUsbPackets, UsbPackets};
use rog_aura::usb::{LED_APPLY, LED_SET};
use rog_aura::{
AuraDeviceType, AuraEffect, Direction, LedBrightness, Speed, GRADIENT, LED_MSG_LEN,
};
use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardLed;
use zbus::zvariant::OwnedObjectPath;
use super::config::AuraConfig;
use crate::ctrl_aura::manager::{dbus_path_for_dev, dbus_path_for_tuf};
use crate::error::RogError;
#[derive(Debug)]
pub enum LEDNode {
/// Brightness and/or TUF RGB controls
KbdLed(KeyboardLed),
/// Raw HID handle
Rog(KeyboardLed, HidRaw),
}
impl LEDNode {
// TODO: move various methods upwards to this
pub fn set_brightness(&self, value: u8) -> Result<(), RogError> {
match self {
LEDNode::KbdLed(k) => k.set_brightness(value)?,
LEDNode::Rog(k, _) => k.set_brightness(value)?,
}
Ok(())
}
pub fn get_brightness(&self) -> Result<u8, RogError> {
Ok(match self {
LEDNode::KbdLed(k) => k.get_brightness()?,
LEDNode::Rog(k, _) => k.get_brightness()?,
})
}
pub fn monitor_brightness(&self) -> Result<Inotify, RogError> {
Ok(match self {
LEDNode::KbdLed(k) => k.monitor_brightness()?,
LEDNode::Rog(k, _) => k.monitor_brightness()?,
})
}
}
/// Individual controller for one Aura device
pub struct CtrlKbdLed {
pub led_type: AuraDeviceType,
pub led_node: LEDNode,
pub supported_data: LedSupportData, // TODO: is storing this really required?
pub per_key_mode_active: bool,
pub config: AuraConfig,
pub dbus_path: OwnedObjectPath,
}
impl CtrlKbdLed {
pub fn find_all() -> Result<Vec<Self>, RogError> {
info!("Searching for all Aura devices");
let mut devices = Vec::new();
let mut found = HashSet::new(); // track and ensure we use only one hidraw per prod_id
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
err
})?;
enumerator.match_subsystem("hidraw").map_err(|err| {
warn!("{}", err);
err
})?;
for end_point in enumerator.scan_devices()? {
// usb_device gives us a product and vendor ID
if let Some(usb_device) =
end_point.parent_with_subsystem_devtype("usb", "usb_device")?
{
// The asus_wmi driver latches MCU that controls the USB endpoints
if let Some(parent) = end_point.parent() {
if let Some(driver) = parent.driver() {
// There is a tree of devices added so filter by driver
if driver != "asus" {
continue;
}
} else {
continue;
}
}
// Device is something like 002, while its parent is the MCU
// Think of it like the device is an endpoint of the USB device attached
let mut prod_id = String::new();
if let Some(usb_id) = usb_device.attribute_value("idProduct") {
prod_id = usb_id.to_string_lossy().to_string();
let aura_dev = AuraDeviceType::from(prod_id.as_str());
if aura_dev == AuraDeviceType::Unknown || found.contains(&aura_dev) {
log::debug!("Unknown or invalid device: {usb_id:?}, skipping");
continue;
}
found.insert(aura_dev);
}
let dev_node = if let Some(dev_node) = usb_device.devnode() {
dev_node
} else {
debug!("Device has no devnode, skipping");
continue;
};
info!("AuraControl found device at: {:?}", dev_node);
let dbus_path = dbus_path_for_dev(&usb_device).unwrap_or_default();
let dev = HidRaw::from_device(end_point)?;
let mut dev = Self::from_hidraw(dev, dbus_path)?;
dev.config = Self::init_config(&prod_id);
devices.push(dev);
}
}
// Check for a TUF laptop LED. Assume there is only ever one.
if let Ok(tuf_kbd) = KeyboardLed::new() {
if tuf_kbd.has_kbd_rgb_mode() {
info!("AuraControl found a TUF laptop keyboard");
let ctrl = CtrlKbdLed {
led_type: AuraDeviceType::LaptopTuf,
led_node: LEDNode::KbdLed(tuf_kbd),
supported_data: LedSupportData::get_data("tuf"),
per_key_mode_active: false,
config: Self::init_config("tuf"),
dbus_path: dbus_path_for_tuf(),
};
devices.push(ctrl);
}
}
info!("Found {} Aura devices", devices.len());
Ok(devices)
}
/// The generated data from this function has a default config. This config
/// should be overwritten. The reason for the default config is because
/// of async issues between this and udev/hidraw
pub fn from_hidraw(device: HidRaw, dbus_path: OwnedObjectPath) -> Result<Self, RogError> {
let rgb_led = KeyboardLed::new()?;
let prod_id = AuraDeviceType::from(device.prod_id());
if prod_id == AuraDeviceType::Unknown {
log::error!("{} is AuraDevice::Unknown", device.prod_id());
return Err(RogError::NoAuraNode);
}
// New loads data from the DB also
// let config = Self::init_config(prod_id, data);
let data = LedSupportData::get_data(device.prod_id());
let ctrl = CtrlKbdLed {
led_type: prod_id,
led_node: LEDNode::Rog(rgb_led, device),
supported_data: data.clone(),
per_key_mode_active: false,
config: AuraConfig::default(),
dbus_path,
};
Ok(ctrl)
}
pub fn init_config(prod_id: &str) -> AuraConfig {
// New loads data from the DB also
let mut config_init = AuraConfig::new(prod_id);
// config_init.set_filename(prod_id);
let mut config_loaded = config_init.clone().load();
// update the initialised data with what we loaded from disk
for mode in &mut config_init.builtins {
// update init values from loaded values if they exist
if let Some(loaded) = config_loaded.builtins.get(mode.0) {
*mode.1 = loaded.clone();
}
}
// Then replace just incase the initialised data contains new modes added
config_loaded.builtins = config_init.builtins;
if let (Some(mut multizone_init), Some(multizone_loaded)) =
(config_init.multizone, config_loaded.multizone.as_mut())
{
for mode in multizone_init.iter_mut() {
// update init values from loaded values if they exist
if let Some(loaded) = multizone_loaded.get(mode.0) {
let mut new_set = Vec::new();
let data = LedSupportData::get_data(prod_id);
// only reuse a zone mode if the mode is supported
for mode in loaded {
if data.basic_modes.contains(&mode.mode) {
new_set.push(mode.clone());
}
}
*mode.1 = new_set;
}
}
*multizone_loaded = multizone_init;
}
config_loaded
}
/// Set combination state for boot animation/sleep animation/all leds/keys
/// leds/side leds LED active
pub(super) fn set_power_states(&mut self) -> Result<(), RogError> {
if let LEDNode::KbdLed(_platform) = &mut self.led_node {
// TODO: tuf bool array
// if let Some(pwr) =
// AuraPowerConfig::to_tuf_bool_array(&self.config.enabled) {
// let buf = [1, pwr[1] as u8, pwr[2] as u8, pwr[3] as u8,
// pwr[4] as u8]; platform.set_kbd_rgb_state(&buf)?;
// }
} else if let LEDNode::Rog(_, hid_raw) = &self.led_node {
let bytes = self.config.enabled.to_bytes(self.led_type);
let message = [0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2], bytes[3]];
hid_raw.write_bytes(&message)?;
hid_raw.write_bytes(&LED_SET)?;
// Changes won't persist unless apply is set
hid_raw.write_bytes(&LED_APPLY)?;
}
Ok(())
}
/// Write an effect block. This is for per-key, but can be repurposed to
/// write the raw factory mode packets - when doing this it is expected that
/// only the first `Vec` (`effect[0]`) is valid.
pub fn write_effect_block(&mut self, effect: &UsbPackets) -> Result<(), RogError> {
if self.config.brightness == LedBrightness::Off {
self.config.brightness = LedBrightness::Med;
self.config.write();
}
let pkt_type = effect[0][1];
const PER_KEY_TYPE: u8 = 0xbc;
if pkt_type != PER_KEY_TYPE {
self.per_key_mode_active = false;
if let LEDNode::Rog(_, hid_raw) = &self.led_node {
hid_raw.write_bytes(&effect[0])?;
hid_raw.write_bytes(&LED_SET)?;
// hid_raw.write_bytes(&LED_APPLY)?;
}
} else {
if !self.per_key_mode_active {
if let LEDNode::Rog(_, hid_raw) = &self.led_node {
let init = LedUsbPackets::get_init_msg();
hid_raw.write_bytes(&init)?;
}
self.per_key_mode_active = true;
}
if let LEDNode::Rog(_, hid_raw) = &self.led_node {
for row in effect.iter() {
hid_raw.write_bytes(row)?;
}
} else if let LEDNode::KbdLed(tuf) = &self.led_node {
for row in effect.iter() {
let r = row[9];
let g = row[10];
let b = row[11];
tuf.set_kbd_rgb_mode(&[0, 0, r, g, b, 0])?;
}
}
}
Ok(())
}
pub fn write_mode(&mut self, mode: &AuraEffect) -> Result<(), RogError> {
if let LEDNode::KbdLed(platform) = &self.led_node {
let buf = [
1,
mode.mode as u8,
mode.colour1.r,
mode.colour1.g,
mode.colour1.b,
mode.speed as u8,
];
platform.set_kbd_rgb_mode(&buf)?;
} else if let LEDNode::Rog(_, hid_raw) = &self.led_node {
let bytes: [u8; LED_MSG_LEN] = mode.into();
hid_raw.write_bytes(&bytes)?;
hid_raw.write_bytes(&LED_SET)?;
// Changes won't persist unless apply is set
hid_raw.write_bytes(&LED_APPLY)?;
} else {
return Err(RogError::NoAuraKeyboard);
}
self.per_key_mode_active = false;
Ok(())
}
pub(super) fn write_current_config_mode(&mut self) -> Result<(), RogError> {
if self.config.multizone_on {
let mode = self.config.current_mode;
let mut create = false;
// There is no multizone config for this mode so create one here
// using the colours of rainbow if it exists, or first available
// mode, or random
if self.config.multizone.is_none() {
create = true;
} else if let Some(multizones) = self.config.multizone.as_ref() {
if !multizones.contains_key(&mode) {
create = true;
}
}
if create {
info!("No user-set config for zone founding, attempting a default");
self.create_multizone_default()?;
}
if let Some(multizones) = self.config.multizone.as_mut() {
if let Some(set) = multizones.get(&mode) {
for mode in set.clone() {
self.write_mode(&mode)?;
}
}
}
} else {
let mode = self.config.current_mode;
if let Some(effect) = self.config.builtins.get(&mode).cloned() {
self.write_mode(&effect)?;
}
}
Ok(())
}
/// Create a default for the `current_mode` if multizone and no config
/// exists.
fn create_multizone_default(&mut self) -> Result<(), RogError> {
let mut default = vec![];
for (i, tmp) in self.supported_data.basic_zones.iter().enumerate() {
default.push(AuraEffect {
mode: self.config.current_mode,
zone: *tmp,
colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]),
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
speed: Speed::Med,
direction: Direction::Left,
});
}
if default.is_empty() {
return Err(RogError::AuraEffectNotSupported);
}
if let Some(multizones) = self.config.multizone.as_mut() {
multizones.insert(self.config.current_mode, default);
} else {
let mut tmp = BTreeMap::new();
tmp.insert(self.config.current_mode, default);
self.config.multizone = Some(tmp);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use rog_aura::aura_detection::LedSupportData;
use rog_aura::{AuraDeviceType, AuraModeNum, AuraZone, PowerZones};
use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardLed;
use zbus::zvariant::OwnedObjectPath;
use super::CtrlKbdLed;
use crate::ctrl_aura::config::AuraConfig;
use crate::ctrl_aura::controller::LEDNode;
#[test]
#[ignore = "Unable to run in CI as the HIDRAW device is required"]
fn create_multizone_if_no_config() {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::new("19b6");
let supported_basic_modes = LedSupportData {
device_name: String::new(),
product_id: String::new(),
layout_name: "ga401".to_owned(),
basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![],
advanced_type: rog_aura::keyboard::AdvancedAuraType::None,
power_zones: vec![PowerZones::Keyboard, PowerZones::RearGlow],
};
let mut controller = CtrlKbdLed {
led_type: AuraDeviceType::LaptopPost2021,
led_node: LEDNode::Rog(KeyboardLed::default(), HidRaw::new("19b6").unwrap()),
supported_data: supported_basic_modes,
per_key_mode_active: false,
config,
dbus_path: OwnedObjectPath::default(),
};
assert!(controller.config.multizone.is_none());
assert!(controller.create_multizone_default().is_err());
assert!(controller.config.multizone.is_none());
controller.supported_data.basic_zones.push(AuraZone::Key1);
controller.supported_data.basic_zones.push(AuraZone::Key2);
assert!(controller.create_multizone_default().is_ok());
assert!(controller.config.multizone.is_some());
let m = controller.config.multizone.unwrap();
assert!(m.contains_key(&AuraModeNum::Static));
let e = m.get(&AuraModeNum::Static).unwrap();
assert_eq!(e.len(), 2);
assert_eq!(e[0].zone, AuraZone::Key1);
assert_eq!(e[1].zone, AuraZone::Key2);
}
#[test]
#[ignore = "Unable to run in CI as the HIDRAW device is required"]
// TODO: use sim device
fn next_mode_create_multizone_if_no_config() {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::new("19b6");
let supported_basic_modes = LedSupportData {
device_name: String::new(),
product_id: String::new(),
layout_name: "ga401".to_owned(),
basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![AuraZone::Key1, AuraZone::Key2],
advanced_type: rog_aura::keyboard::AdvancedAuraType::None,
power_zones: vec![PowerZones::Keyboard, PowerZones::RearGlow],
};
let mut controller = CtrlKbdLed {
led_type: AuraDeviceType::LaptopPost2021,
led_node: LEDNode::Rog(KeyboardLed::default(), HidRaw::new("19b6").unwrap()),
supported_data: supported_basic_modes,
per_key_mode_active: false,
config,
dbus_path: OwnedObjectPath::default(),
};
assert!(controller.config.multizone.is_none());
controller.config.multizone_on = true;
// This is called in toggle_mode. It will error here because we have no
// keyboard node in tests.
assert_eq!(
controller
.write_current_config_mode()
.unwrap_err()
.to_string(),
"No supported Aura keyboard"
);
assert!(controller.config.multizone.is_some());
let m = controller.config.multizone.unwrap();
assert!(m.contains_key(&AuraModeNum::Static));
let e = m.get(&AuraModeNum::Static).unwrap();
assert_eq!(e.len(), 2);
assert_eq!(e[0].zone, AuraZone::Key1);
assert_eq!(e[1].zone, AuraZone::Key2);
}
}
+187
View File
@@ -0,0 +1,187 @@
// Plan:
// - Manager has udev monitor on USB looking for ROG devices
// - If a device is found, add it to watch
// - Add it to Zbus server
// - If udev sees device removed then remove the zbus path
use std::collections::HashSet;
use log::{debug, error, info, warn};
use mio::{Events, Interest, Poll, Token};
use rog_aura::AuraDeviceType;
use rog_platform::hid_raw::HidRaw;
use tokio::task::spawn_blocking;
use udev::{Device, MonitorBuilder};
use zbus::object_server::SignalContext;
use zbus::zvariant::{ObjectPath, OwnedObjectPath};
use zbus::Connection;
use crate::ctrl_aura::controller::CtrlKbdLed;
use crate::ctrl_aura::trait_impls::{CtrlAuraZbus, AURA_ZBUS_PATH};
use crate::error::RogError;
use crate::{CtrlTask, Reloadable};
pub struct AuraManager {
_connection: Connection,
}
impl AuraManager {
pub async fn new(connection: Connection) -> Result<Self, RogError> {
let conn_copy = connection.clone();
let mut interfaces = HashSet::new();
// Do the initial keyboard detection:
let all = CtrlKbdLed::find_all()?;
for ctrl in all {
let path = ctrl.dbus_path.clone();
interfaces.insert(path.clone()); // ensure we record the initial stuff
let sig_ctx = CtrlAuraZbus::signal_context(&connection)?;
let sig_ctx2 = sig_ctx.clone();
let zbus = CtrlAuraZbus::new(ctrl, sig_ctx);
start_tasks(zbus, connection.clone(), sig_ctx2, path).await?;
}
let manager = Self {
_connection: connection,
};
// detect all plugged in aura devices (eventually)
// only USB devices are detected for here
spawn_blocking(move || {
let mut monitor = MonitorBuilder::new()?.match_subsystem("hidraw")?.listen()?;
let mut poll = Poll::new()?;
let mut events = Events::with_capacity(1024);
poll.registry()
.register(&mut monitor, Token(0), Interest::READABLE)?;
loop {
if poll.poll(&mut events, None).is_err() {
continue;
}
for event in monitor.iter() {
let parent = if let Some(parent) =
event.parent_with_subsystem_devtype("usb", "usb_device")?
{
parent
} else {
continue;
};
let action = if let Some(action) = event.action() {
action
} else {
continue;
};
let id_product = if let Some(id_product) = parent.attribute_value("idProduct") {
id_product.to_string_lossy()
} else {
continue;
};
let path = if let Some(path) = dbus_path_for_dev(&parent) {
path
} else {
continue;
};
let aura_device = AuraDeviceType::from(&*id_product);
if aura_device == AuraDeviceType::Unknown {
warn!("idProduct:{id_product:?} is unknown, not using");
continue;
}
if action == "remove" {
if interfaces.remove(&path) {
info!("AuraManager removing: {path:?}");
let conn_copy = conn_copy.clone();
tokio::spawn(async move {
let res = conn_copy
.object_server()
.remove::<CtrlAuraZbus, _>(&path)
.await
.map_err(|e| {
error!("Failed to remove {path:?}, {e:?}");
e
})?;
info!("AuraManager removed: {path:?}, {res}");
Ok::<(), RogError>(())
});
}
} else if action == "add" {
if interfaces.contains(&path) {
debug!("Already a ctrl at {path:?}");
continue;
}
// Need to check the driver is asus to prevent using hid_generic
if let Some(p2) = event.parent() {
if let Some(driver) = p2.driver() {
// There is a tree of devices added so filter by driver
if driver != "asus" {
debug!("{id_product:?} driver was not asus, skipping");
continue;
}
} else {
continue;
}
}
if let Some(dev_node) = event.devnode() {
if let Ok(raw) = HidRaw::from_device(event.device())
.map_err(|e| error!("device path error: {e:?}"))
{
if let Ok(mut ctrl) = CtrlKbdLed::from_hidraw(raw, path.clone()) {
ctrl.config = CtrlKbdLed::init_config(&id_product);
interfaces.insert(path.clone());
info!("AuraManager starting device at: {dev_node:?}, {path:?}");
let sig_ctx = CtrlAuraZbus::signal_context(&conn_copy)?;
let zbus = CtrlAuraZbus::new(ctrl, sig_ctx);
let sig_ctx = CtrlAuraZbus::signal_context(&conn_copy)?;
let conn_copy = conn_copy.clone();
tokio::spawn(async move {
start_tasks(zbus, conn_copy.clone(), sig_ctx, path).await
});
}
}
}
};
}
}
// Required for return type on spawn
#[allow(unreachable_code)]
Ok::<(), RogError>(())
});
Ok(manager)
}
}
pub(crate) fn dbus_path_for_dev(parent: &Device) -> Option<OwnedObjectPath> {
if let Some(filename) = super::filename_partial(parent) {
return Some(
ObjectPath::from_str_unchecked(&format!("{AURA_ZBUS_PATH}/{filename}")).into(),
);
}
None
}
pub(crate) fn dbus_path_for_tuf() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{AURA_ZBUS_PATH}/tuf")).into()
}
async fn start_tasks(
mut zbus: CtrlAuraZbus,
connection: Connection,
_signal_ctx: SignalContext<'static>,
path: OwnedObjectPath,
) -> Result<(), RogError> {
// let task = zbus.clone();
// let signal_ctx = signal_ctx.clone();
zbus.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
connection.object_server().at(path, zbus).await.unwrap();
// TODO: skip this until we keep handles to tasks so they can be killed
// task.create_tasks(signal_ctx).await
Ok(())
}
+29
View File
@@ -0,0 +1,29 @@
use udev::Device;
use zbus::zvariant::{ObjectPath, OwnedObjectPath};
pub mod config;
pub mod controller;
pub mod manager;
/// Implements `CtrlTask`, `Reloadable`, `ZbusRun`
pub mod trait_impls;
/// Returns only the Device details concatenated in a form usable for
/// adding/appending to a filename
pub(super) fn filename_partial(parent: &Device) -> Option<OwnedObjectPath> {
if let Some(id_product) = parent.attribute_value("idProduct") {
let id_product = id_product.to_string_lossy();
let path = if let Some(devnum) = parent.attribute_value("devnum") {
let devnum = devnum.to_string_lossy();
if let Some(devpath) = parent.attribute_value("devpath") {
let devpath = devpath.to_string_lossy();
format!("{id_product}_{devnum}_{devpath}")
} else {
format!("{id_product}_{devnum}")
}
} else {
format!("{id_product}")
};
return Some(ObjectPath::from_str_unchecked(&path).into());
}
None
}
+295
View File
@@ -0,0 +1,295 @@
use std::collections::BTreeMap;
use std::sync::Arc;
use config_traits::StdConfig;
use log::{debug, error, info, warn};
use rog_aura::keyboard::{LaptopAuraPower, UsbPackets};
use rog_aura::{AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, LedBrightness, PowerZones};
use zbus::export::futures_util::lock::{Mutex, MutexGuard};
use zbus::export::futures_util::StreamExt;
use zbus::fdo::Error as ZbErr;
use zbus::{interface, SignalContext};
use super::controller::CtrlKbdLed;
use crate::error::RogError;
use crate::CtrlTask;
pub const AURA_ZBUS_NAME: &str = "Aura";
pub const AURA_ZBUS_PATH: &str = "/org/asuslinux";
#[derive(Clone)]
pub struct CtrlAuraZbus(Arc<Mutex<CtrlKbdLed>>, SignalContext<'static>);
impl CtrlAuraZbus {
pub fn new(controller: CtrlKbdLed, signal: SignalContext<'static>) -> Self {
Self(Arc::new(Mutex::new(controller)), signal)
}
fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> {
let bright = lock.led_node.get_brightness()?;
lock.config.read();
lock.config.brightness = bright.into();
lock.config.write();
Ok(())
}
}
/// The main interface for changing, reading, or notfying
///
/// LED commands are split between Brightness, Modes, Per-Key
#[interface(name = "org.asuslinux.Aura")]
impl CtrlAuraZbus {
/// Return the device type for this Aura keyboard
#[zbus(property)]
async fn device_type(&self) -> AuraDeviceType {
let ctrl = self.0.lock().await;
ctrl.led_type
}
/// Return the current LED brightness
#[zbus(property)]
async fn brightness(&self) -> Result<LedBrightness, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.led_node.get_brightness().map(|n| n.into())?)
}
/// Set the keyboard brightness level (0-3)
#[zbus(property)]
async fn set_brightness(&mut self, brightness: LedBrightness) -> Result<(), ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.led_node.set_brightness(brightness.into())?)
}
/// Total levels of brightness available
#[zbus(property)]
async fn supported_brightness(&self) -> Vec<LedBrightness> {
vec![
LedBrightness::Off,
LedBrightness::Low,
LedBrightness::Med,
LedBrightness::High,
]
}
/// The total available modes
#[zbus(property)]
async fn supported_basic_modes(&self) -> Result<Vec<AuraModeNum>, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.config.builtins.keys().cloned().collect())
}
#[zbus(property)]
async fn supported_basic_zones(&self) -> Result<Vec<AuraZone>, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.supported_data.basic_zones.clone())
}
#[zbus(property)]
async fn supported_power_zones(&self) -> Result<Vec<PowerZones>, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.supported_data.power_zones.clone())
}
/// The current mode data
#[zbus(property)]
async fn led_mode(&self) -> Result<AuraModeNum, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.config.current_mode)
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode(&mut self, num: AuraModeNum) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
ctrl.config.current_mode = num;
ctrl.write_current_config_mode()?;
if ctrl.config.brightness == LedBrightness::Off {
ctrl.config.brightness = LedBrightness::Med;
}
ctrl.led_node
.set_brightness(ctrl.config.brightness.into())?;
ctrl.config.write();
self.led_mode_data_invalidate(&self.1).await.ok();
Ok(())
}
/// The current mode data
#[zbus(property)]
async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> {
let ctrl = self.0.lock().await;
let mode = ctrl.config.current_mode;
match ctrl.config.builtins.get(&mode) {
Some(effect) => Ok(effect.clone()),
None => Err(ZbErr::Failed("Could not get the current effect".into())),
}
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode_data(&mut self, effect: AuraEffect) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
if !ctrl.supported_data.basic_modes.contains(&effect.mode)
|| effect.zone != AuraZone::None
&& !ctrl.supported_data.basic_zones.contains(&effect.zone)
{
return Err(ZbErr::NotSupported(format!(
"The Aura effect is not supported: {effect:?}"
)));
}
ctrl.write_mode(&effect)?;
if ctrl.config.brightness == LedBrightness::Off {
ctrl.config.brightness = LedBrightness::Med;
}
ctrl.led_node
.set_brightness(ctrl.config.brightness.into())?;
ctrl.config.set_builtin(effect);
ctrl.config.write();
self.led_mode_invalidate(&self.1).await.ok();
Ok(())
}
/// Get the data set for every mode available
async fn all_mode_data(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
let ctrl = self.0.lock().await;
ctrl.config.builtins.clone()
}
// As property doesn't work for AuraPowerDev (complexity of serialization?)
#[zbus(property)]
async fn led_power(&self) -> LaptopAuraPower {
let ctrl = self.0.lock().await;
ctrl.config.enabled.clone()
}
/// Set a variety of states, input is array of enum.
/// `enabled` sets if the sent array should be disabled or enabled
///
/// For Modern ROG devices the "enabled" flag is ignored.
#[zbus(property)]
async fn set_led_power(&mut self, options: LaptopAuraPower) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
for opt in options.states {
let zone = opt.zone;
for config in ctrl.config.enabled.states.iter_mut() {
if config.zone == zone {
*config = opt;
}
}
}
ctrl.config.write();
Ok(ctrl.set_power_states().map_err(|e| {
warn!("{}", e);
e
})?)
}
/// On machine that have some form of either per-key keyboard or per-zone
/// this can be used to write custom effects over dbus. The input is a
/// nested `Vec<Vec<8>>` where `Vec<u8>` is a raw USB packet
async fn direct_addressing_raw(&self, data: UsbPackets) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
ctrl.write_effect_block(&data)?;
Ok(())
}
}
impl CtrlTask for CtrlAuraZbus {
fn zbus_path() -> &'static str {
"/org/asuslinux"
}
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
let load_save =
|start: bool, mut lock: MutexGuard<'_, CtrlKbdLed>| -> Result<(), RogError> {
// If waking up
if !start {
info!("CtrlKbdLedTask reloading brightness and modes");
lock.led_node
.set_brightness(lock.config.brightness.into())
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})?;
lock.write_current_config_mode().map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})?;
} else if start {
Self::update_config(&mut lock).map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})?;
}
Ok(())
};
let inner1 = self.0.clone();
let inner3 = self.0.clone();
self.create_sys_event_tasks(
move |sleeping| {
let inner1 = inner1.clone();
async move {
let lock = inner1.lock().await;
load_save(sleeping, lock).unwrap(); // unwrap as we want to
// bomb out of the task
}
},
move |_shutting_down| {
let inner3 = inner3.clone();
async move {
let lock = inner3.lock().await;
load_save(false, lock).unwrap(); // unwrap as we want to
// bomb out of the task
}
},
move |_lid_closed| {
// on lid change
async move {}
},
move |_power_plugged| {
// power change
async move {}
},
)
.await;
let ctrl2 = self.0.clone();
let ctrl = self.0.lock().await;
let watch = ctrl.led_node.monitor_brightness()?;
tokio::spawn(async move {
let mut buffer = [0; 32];
watch
.into_event_stream(&mut buffer)
.unwrap()
.for_each(|_| async {
if let Some(lock) = ctrl2.try_lock() {
load_save(true, lock).unwrap(); // unwrap as we want to
// bomb out of the task
}
})
.await;
});
Ok(())
}
}
impl crate::Reloadable for CtrlAuraZbus {
async fn reload(&mut self) -> Result<(), RogError> {
let mut ctrl = self.0.lock().await;
debug!("reloading keyboard mode");
ctrl.write_current_config_mode()?;
debug!("reloading power states");
ctrl.set_power_states().map_err(|err| warn!("{err}")).ok();
Ok(())
}
}
+43 -49
View File
@@ -3,27 +3,26 @@ use std::sync::Arc;
use config_traits::{StdConfig, StdConfigLoad}; use config_traits::{StdConfig, StdConfigLoad};
use futures_lite::StreamExt; use futures_lite::StreamExt;
use futures_util::lock::Mutex;
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use rog_platform::platform::{PlatformProfile, RogPlatform}; use rog_platform::platform::{RogPlatform, ThrottlePolicy};
use rog_profiles::error::ProfileError; use rog_profiles::error::ProfileError;
use rog_profiles::fan_curve_set::CurveData; use rog_profiles::fan_curve_set::CurveData;
use rog_profiles::{find_fan_curve_node, FanCurvePU, FanCurveProfiles}; use rog_profiles::{find_fan_curve_node, FanCurvePU, FanCurveProfiles};
use serde::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use zbus::object_server::SignalEmitter; use tokio::sync::Mutex;
use zbus::{interface, Connection}; use zbus::{interface, Connection, SignalContext};
use crate::error::RogError; use crate::error::RogError;
use crate::{CtrlTask, CONFIG_PATH_BASE}; use crate::{CtrlTask, CONFIG_PATH_BASE};
pub const FAN_CURVE_ZBUS_NAME: &str = "FanCurves"; pub const FAN_CURVE_ZBUS_NAME: &str = "FanCurves";
pub const FAN_CURVE_ZBUS_PATH: &str = "/xyz/ljones"; pub const FAN_CURVE_ZBUS_PATH: &str = "/org/asuslinux";
#[derive(Deserialize, Serialize, Debug, Default)] #[derive(Deserialize, Serialize, Debug, Default)]
pub struct FanCurveConfig { pub struct FanCurveConfig {
pub profiles: FanCurveProfiles, pub profiles: FanCurveProfiles,
#[serde(skip)] #[serde(skip)]
pub current: PlatformProfile pub current: u8,
} }
impl StdConfig for FanCurveConfig { impl StdConfig for FanCurveConfig {
@@ -47,14 +46,14 @@ impl StdConfigLoad for FanCurveConfig {}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct CtrlFanCurveZbus { pub struct CtrlFanCurveZbus {
config: Arc<Mutex<FanCurveConfig>>, config: Arc<Mutex<FanCurveConfig>>,
platform: RogPlatform platform: RogPlatform,
} }
// Non-zbus-derive impl // Non-zbus-derive impl
impl CtrlFanCurveZbus { impl CtrlFanCurveZbus {
pub fn new() -> Result<Self, RogError> { pub fn new() -> Result<Self, RogError> {
let platform = RogPlatform::new()?; let platform = RogPlatform::new()?;
if platform.has_platform_profile() { if platform.has_throttle_thermal_policy() {
info!("Device has profile control available"); info!("Device has profile control available");
find_fan_curve_node()?; find_fan_curve_node()?;
info!("Device has fan curves available"); info!("Device has fan curves available");
@@ -65,16 +64,16 @@ impl CtrlFanCurveZbus {
if config.profiles.balanced.is_empty() || !config.file_path().exists() { if config.profiles.balanced.is_empty() || !config.file_path().exists() {
info!("Fetching default fan curves"); info!("Fetching default fan curves");
let current = platform.get_platform_profile()?; let current = platform.get_throttle_thermal_policy()?;
for this in [ for this in [
PlatformProfile::Balanced, ThrottlePolicy::Balanced,
PlatformProfile::Performance, ThrottlePolicy::Performance,
PlatformProfile::Quiet ThrottlePolicy::Quiet,
] { ] {
// For each profile we need to switch to it before we // For each profile we need to switch to it before we
// can read the existing values from hardware. The ACPI method used // can read the existing values from hardware. The ACPI method used
// for this is what limits us. // for this is what limits us.
platform.set_platform_profile(this.into())?; platform.set_throttle_thermal_policy(this.into())?;
let mut dev = find_fan_curve_node()?; let mut dev = find_fan_curve_node()?;
fan_curves.set_active_curve_to_defaults(this, &mut dev)?; fan_curves.set_active_curve_to_defaults(this, &mut dev)?;
@@ -83,7 +82,7 @@ impl CtrlFanCurveZbus {
info!("{}", String::from(curve)); info!("{}", String::from(curve));
} }
} }
platform.set_platform_profile(current.as_str())?; platform.set_throttle_thermal_policy(current)?;
config.profiles = fan_curves; config.profiles = fan_curves;
config.write(); config.write();
} else { } else {
@@ -93,7 +92,7 @@ impl CtrlFanCurveZbus {
return Ok(Self { return Ok(Self {
config: Arc::new(Mutex::new(config)), config: Arc::new(Mutex::new(config)),
platform platform,
}); });
} }
@@ -101,14 +100,14 @@ impl CtrlFanCurveZbus {
} }
} }
#[interface(name = "xyz.ljones.FanCurves")] #[interface(name = "org.asuslinux.FanCurves")]
impl CtrlFanCurveZbus { impl CtrlFanCurveZbus {
/// Set all fan curves for a profile to enabled status. Will also activate a /// Set all fan curves for a profile to enabled status. Will also activate a
/// fan curve if in the same profile mode /// fan curve if in the same profile mode
async fn set_fan_curves_enabled( async fn set_fan_curves_enabled(
&mut self, &mut self,
profile: PlatformProfile, profile: ThrottlePolicy,
enabled: bool enabled: bool,
) -> zbus::fdo::Result<()> { ) -> zbus::fdo::Result<()> {
self.config self.config
.lock() .lock()
@@ -128,9 +127,9 @@ impl CtrlFanCurveZbus {
/// activate a fan curve if in the same profile mode /// activate a fan curve if in the same profile mode
async fn set_profile_fan_curve_enabled( async fn set_profile_fan_curve_enabled(
&mut self, &mut self,
profile: PlatformProfile, profile: ThrottlePolicy,
fan: FanCurvePU, fan: FanCurvePU,
enabled: bool enabled: bool,
) -> zbus::fdo::Result<()> { ) -> zbus::fdo::Result<()> {
self.config self.config
.lock() .lock()
@@ -149,7 +148,7 @@ impl CtrlFanCurveZbus {
/// Get the fan-curve data for the currently active ThrottlePolicy /// Get the fan-curve data for the currently active ThrottlePolicy
async fn fan_curve_data( async fn fan_curve_data(
&mut self, &mut self,
profile: PlatformProfile profile: ThrottlePolicy,
) -> zbus::fdo::Result<Vec<CurveData>> { ) -> zbus::fdo::Result<Vec<CurveData>> {
let curve = self let curve = self
.config .config
@@ -165,15 +164,15 @@ impl CtrlFanCurveZbus {
/// Will also activate the fan curve if the user is in the same mode. /// Will also activate the fan curve if the user is in the same mode.
async fn set_fan_curve( async fn set_fan_curve(
&mut self, &mut self,
profile: PlatformProfile, profile: ThrottlePolicy,
curve: CurveData curve: CurveData,
) -> zbus::fdo::Result<()> { ) -> zbus::fdo::Result<()> {
self.config self.config
.lock() .lock()
.await .await
.profiles .profiles
.save_fan_curve(curve, profile)?; .save_fan_curve(curve, profile)?;
let active: PlatformProfile = self.platform.get_platform_profile()?.into(); let active: ThrottlePolicy = self.platform.get_throttle_thermal_policy()?.into();
if active == profile { if active == profile {
self.config self.config
.lock() .lock()
@@ -190,15 +189,15 @@ impl CtrlFanCurveZbus {
/// ///
/// Each platform_profile has a different default and the default can be /// Each platform_profile has a different default and the default can be
/// read only for the currently active profile. /// read only for the currently active profile.
async fn set_curves_to_defaults(&mut self, profile: PlatformProfile) -> zbus::fdo::Result<()> { async fn set_curves_to_defaults(&mut self, profile: ThrottlePolicy) -> zbus::fdo::Result<()> {
let active = self.platform.get_platform_profile()?; let active = self.platform.get_throttle_thermal_policy()?;
self.platform.set_platform_profile(profile.into())?; self.platform.set_throttle_thermal_policy(profile.into())?;
self.config self.config
.lock() .lock()
.await .await
.profiles .profiles
.set_active_curve_to_defaults(profile, &mut find_fan_curve_node()?)?; .set_active_curve_to_defaults(profile, &mut find_fan_curve_node()?)?;
self.platform.set_platform_profile(active.as_str())?; self.platform.set_throttle_thermal_policy(active)?;
self.config.lock().await.write(); self.config.lock().await.write();
Ok(()) Ok(())
} }
@@ -208,16 +207,16 @@ impl CtrlFanCurveZbus {
/// ///
/// Each platform_profile has a different default and the defualt can be /// Each platform_profile has a different default and the defualt can be
/// read only for the currently active profile. /// read only for the currently active profile.
async fn reset_profile_curves(&self, profile: PlatformProfile) -> zbus::fdo::Result<()> { async fn reset_profile_curves(&self, profile: ThrottlePolicy) -> zbus::fdo::Result<()> {
let active = self.platform.get_platform_profile()?; let active = self.platform.get_throttle_thermal_policy()?;
self.platform.set_platform_profile(profile.into())?; self.platform.set_throttle_thermal_policy(profile.into())?;
self.config self.config
.lock() .lock()
.await .await
.profiles .profiles
.set_active_curve_to_defaults((&active).into(), &mut find_fan_curve_node()?)?; .set_active_curve_to_defaults(active.into(), &mut find_fan_curve_node()?)?;
self.platform.set_platform_profile(active.as_str())?; self.platform.set_throttle_thermal_policy(active)?;
self.config.lock().await.write(); self.config.lock().await.write();
Ok(()) Ok(())
@@ -235,33 +234,28 @@ impl CtrlTask for CtrlFanCurveZbus {
FAN_CURVE_ZBUS_PATH FAN_CURVE_ZBUS_PATH
} }
async fn create_tasks(&self, _signal_ctxt: SignalEmitter<'static>) -> Result<(), RogError> { async fn create_tasks(&self, _signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
let watch_platform_profile = self.platform.monitor_platform_profile()?; let watch_throttle_thermal_policy = self.platform.monitor_throttle_thermal_policy()?;
let platform = self.platform.clone(); let platform = self.platform.clone();
let config = self.config.clone(); let config = self.config.clone();
let fan_curves = self.config.clone(); let fan_curves = self.config.clone();
tokio::spawn(async move { tokio::spawn(async move {
let mut buffer = [0; 32]; let mut buffer = [0; 32];
if let Ok(mut stream) = watch_platform_profile.into_event_stream(&mut buffer) { if let Ok(mut stream) = watch_throttle_thermal_policy.into_event_stream(&mut buffer) {
while (stream.next().await).is_some() { while (stream.next().await).is_some() {
debug!("watch_platform_profile changed"); debug!("watch_throttle_thermal_policy changed");
if let Ok(profile) = if let Ok(profile) = platform.get_throttle_thermal_policy().map_err(|e| {
platform error!("get_throttle_thermal_policy error: {e}");
.get_platform_profile() }) {
.map(|p| p.into())
.map_err(|e| {
error!("get_platform_profile error: {e}");
})
{
if profile != config.lock().await.current { if profile != config.lock().await.current {
fan_curves fan_curves
.lock() .lock()
.await .await
.profiles .profiles
.write_profile_curve_to_platform( .write_profile_curve_to_platform(
profile, profile.into(),
&mut find_fan_curve_node().unwrap() &mut find_fan_curve_node().unwrap(),
) )
.map_err(|e| warn!("write_profile_curve_to_platform, {}", e)) .map_err(|e| warn!("write_profile_curve_to_platform, {}", e))
.ok(); .ok();
@@ -279,7 +273,7 @@ impl CtrlTask for CtrlFanCurveZbus {
impl crate::Reloadable for CtrlFanCurveZbus { impl crate::Reloadable for CtrlFanCurveZbus {
/// Fetch the active profile and use that to set all related components up /// Fetch the active profile and use that to set all related components up
async fn reload(&mut self) -> Result<(), RogError> { async fn reload(&mut self) -> Result<(), RogError> {
let active = self.platform.get_platform_profile()?.into(); let active = self.platform.get_throttle_thermal_policy()?.into();
let mut config = self.config.lock().await; let mut config = self.config.lock().await;
if let Ok(mut device) = find_fan_curve_node() { if let Ok(mut device) = find_fan_curve_node() {
config config
+532 -353
View File
File diff suppressed because it is too large Load Diff
+51
View File
@@ -0,0 +1,51 @@
use config_traits::{StdConfig, StdConfigLoad};
use rog_slash::{DeviceState, SlashMode};
use serde_derive::{Deserialize, Serialize};
const CONFIG_FILE: &str = "slash.ron";
/// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug)]
pub struct SlashConfig {
pub slash_enabled: bool,
pub slash_brightness: u8,
pub slash_interval: u8,
pub slash_mode: SlashMode,
}
impl Default for SlashConfig {
fn default() -> Self {
SlashConfig {
slash_enabled: true,
slash_brightness: 255,
slash_interval: 0,
slash_mode: SlashMode::Bounce,
}
}
}
impl StdConfig for SlashConfig {
fn new() -> Self {
Self::default()
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
fn config_dir() -> std::path::PathBuf {
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
}
}
impl StdConfigLoad for SlashConfig {}
impl From<&SlashConfig> for DeviceState {
fn from(config: &SlashConfig) -> Self {
DeviceState {
slash_enabled: config.slash_enabled,
slash_brightness: config.slash_brightness,
slash_interval: config.slash_interval,
slash_mode: config.slash_mode,
}
}
}
+106
View File
@@ -0,0 +1,106 @@
pub mod config;
pub mod trait_impls;
use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw;
use rog_slash::error::SlashError;
use rog_slash::usb::{get_slash_type, pkt_set_mode, pkt_set_options, pkts_for_init};
use rog_slash::{SlashMode, SlashType};
use crate::ctrl_slash::config::SlashConfig;
use crate::error::RogError;
enum Node {
Usb(USBRaw),
Hid(HidRaw),
}
impl Node {
pub fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
// TODO: map and pass on errors
match self {
Node::Usb(u) => {
u.write_bytes(message).ok();
}
Node::Hid(h) => {
h.write_bytes(message).ok();
}
}
Ok(())
}
}
pub struct CtrlSlash {
// node: HidRaw,
node: Node,
config: SlashConfig,
// slash_type: SlashType,
// // set to force thread to exit
// thread_exit: Arc<AtomicBool>,
// // Set to false when the thread exits
// thread_running: Arc<AtomicBool>,
}
impl CtrlSlash {
#[inline]
pub fn new(config: SlashConfig) -> Result<CtrlSlash, RogError> {
let slash_type = get_slash_type()?;
if matches!(slash_type, SlashType::Unknown | SlashType::Unsupported) {
return Err(RogError::Slash(SlashError::NoDevice));
}
let usb = USBRaw::new(rog_slash::usb::PROD_ID).ok();
let hid = HidRaw::new(rog_slash::usb::PROD_ID_STR).ok();
let node = if usb.is_some() {
unsafe { Node::Usb(usb.unwrap_unchecked()) }
} else if hid.is_some() {
unsafe { Node::Hid(hid.unwrap_unchecked()) }
} else {
return Err(RogError::NotSupported);
};
let ctrl = CtrlSlash {
node,
config,
// slash_type,
// thread_exit: Arc::new(AtomicBool::new(false)),
// thread_running: Arc::new(AtomicBool::new(false)),
};
ctrl.do_initialization()?;
Ok(ctrl)
}
fn do_initialization(&self) -> Result<(), RogError> {
let init_packets = pkts_for_init();
self.node.write_bytes(&init_packets[0])?;
self.node.write_bytes(&init_packets[1])?;
// Apply config upon initialization
let option_packets = pkt_set_options(
self.config.slash_enabled,
self.config.slash_brightness,
self.config.slash_interval,
);
self.node.write_bytes(&option_packets)?;
let mode_packets = pkt_set_mode(self.config.slash_mode);
self.node.write_bytes(&mode_packets[0])?;
self.node.write_bytes(&mode_packets[1])?;
Ok(())
}
pub fn set_options(&self, enabled: bool, brightness: u8, interval: u8) -> Result<(), RogError> {
let command_packets = pkt_set_options(enabled, brightness, interval);
self.node.write_bytes(&command_packets)?;
Ok(())
}
pub fn set_slash_mode(&self, slash_mode: SlashMode) -> Result<(), RogError> {
let command_packets = pkt_set_mode(slash_mode);
self.node.write_bytes(&command_packets[0])?;
self.node.write_bytes(&command_packets[1])?;
Ok(())
}
}
+161
View File
@@ -0,0 +1,161 @@
use std::sync::Arc;
use config_traits::StdConfig;
use log::warn;
use rog_slash::usb::{pkt_set_mode, pkt_set_options};
use rog_slash::{DeviceState, SlashMode};
use zbus::export::futures_util::lock::Mutex;
use zbus::{interface, Connection, SignalContext};
use crate::ctrl_slash::CtrlSlash;
use crate::error::RogError;
pub const SLASH_ZBUS_NAME: &str = "Slash";
pub const SLASH_ZBUS_PATH: &str = "/org/asuslinux";
#[derive(Clone)]
pub struct CtrlSlashZbus(pub Arc<Mutex<CtrlSlash>>);
/// The struct with the main dbus methods requires this trait
impl crate::ZbusRun for CtrlSlashZbus {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, SLASH_ZBUS_PATH, server).await;
}
}
#[interface(name = "org.asuslinux.Slash")]
impl CtrlSlashZbus {
/// Get enabled or not
#[zbus(property)]
async fn enabled(&self) -> bool {
let lock = self.0.lock().await;
lock.config.slash_enabled
}
/// Set enabled true or false
async fn set_enabled(&self, enabled: bool) {
let mut lock = self.0.lock().await;
let brightness = if enabled && lock.config.slash_brightness == 0 {
0x88
} else {
lock.config.slash_brightness
};
lock.node
.write_bytes(&pkt_set_options(
enabled,
brightness,
lock.config.slash_interval,
))
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_enabled = enabled;
lock.config.slash_brightness = brightness;
lock.config.write();
}
/// Get brightness level
#[zbus(property)]
async fn brightness(&self) -> u8 {
let lock = self.0.lock().await;
lock.config.slash_brightness
}
/// Set brightness level
async fn set_brightness(&self, brightness: u8) {
let mut lock = self.0.lock().await;
let enabled = brightness > 0;
lock.node
.write_bytes(&pkt_set_options(
enabled,
brightness,
lock.config.slash_interval,
))
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_enabled = enabled;
lock.config.slash_brightness = brightness;
lock.config.write();
}
#[zbus(property)]
async fn interval(&self) -> u8 {
let lock = self.0.lock().await;
lock.config.slash_interval
}
/// Set interval between slash animations (0-255)
async fn set_interval(&self, interval: u8) {
let mut lock = self.0.lock().await;
lock.node
.write_bytes(&pkt_set_options(
lock.config.slash_enabled,
lock.config.slash_brightness,
interval,
))
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_interval = interval;
lock.config.write();
}
#[zbus(property)]
async fn slash_mode(&self) -> u8 {
let lock = self.0.lock().await;
lock.config.slash_interval
}
/// Set interval between slash animations (0-255)
async fn set_slash_mode(&self, slash_mode: SlashMode) {
let mut lock = self.0.lock().await;
let command_packets = pkt_set_mode(slash_mode);
lock.node
.write_bytes(&command_packets[0])
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.node
.write_bytes(&command_packets[1])
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_mode = slash_mode;
lock.config.write();
}
/// Get the device state as stored by asusd
// #[zbus(property)]
async fn device_state(&self) -> DeviceState {
let lock = self.0.lock().await;
DeviceState::from(&lock.config)
}
}
impl crate::CtrlTask for CtrlSlashZbus {
fn zbus_path() -> &'static str {
SLASH_ZBUS_PATH
}
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
Ok(())
}
}
impl crate::Reloadable for CtrlSlashZbus {
async fn reload(&mut self) -> Result<(), RogError> {
Ok(())
}
}
+50 -34
View File
@@ -2,19 +2,21 @@ use std::env;
use std::error::Error; use std::error::Error;
use std::sync::Arc; use std::sync::Arc;
use ::zbus::export::futures_util::lock::Mutex;
use ::zbus::Connection; use ::zbus::Connection;
use asusd::asus_armoury::start_attributes_zbus;
use asusd::aura_manager::DeviceManager;
use asusd::config::Config; use asusd::config::Config;
use asusd::ctrl_anime::config::AnimeConfig;
use asusd::ctrl_anime::trait_impls::CtrlAnimeZbus;
use asusd::ctrl_anime::CtrlAnime;
use asusd::ctrl_aura::manager::AuraManager;
use asusd::ctrl_fancurves::CtrlFanCurveZbus; use asusd::ctrl_fancurves::CtrlFanCurveZbus;
use asusd::ctrl_platform::CtrlPlatform; use asusd::ctrl_platform::CtrlPlatform;
use asusd::ctrl_slash::config::SlashConfig;
use asusd::ctrl_slash::trait_impls::CtrlSlashZbus;
use asusd::ctrl_slash::CtrlSlash;
use asusd::{print_board_info, start_tasks, CtrlTask, DBUS_NAME}; use asusd::{print_board_info, start_tasks, CtrlTask, DBUS_NAME};
use config_traits::{StdConfig, StdConfigLoad1}; use config_traits::{StdConfig, StdConfigLoad, StdConfigLoad2, StdConfigLoad3};
use futures_util::lock::Mutex;
use log::{error, info}; use log::{error, info};
use rog_platform::asus_armoury::FirmwareAttributes;
use rog_platform::platform::RogPlatform;
use rog_platform::power::AsusPower;
use zbus::fdo::ObjectManager; use zbus::fdo::ObjectManager;
#[tokio::main] #[tokio::main]
@@ -25,12 +27,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.parse_default_env() .parse_default_env()
.target(env_logger::Target::Stdout) .target(env_logger::Target::Stdout)
.format_timestamp(None) .format_timestamp(None)
.filter_level(log::LevelFilter::Debug)
.init(); .init();
let is_service = match env::var_os("IS_SERVICE") { let is_service = match env::var_os("IS_SERVICE") {
Some(val) => val == "1", Some(val) => val == "1",
None => true None => false,
}; };
if !is_service { if !is_service {
@@ -60,30 +61,23 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
// println!("{:?}", supported.supported_functions()); // println!("{:?}", supported.supported_functions());
// Start zbus server // Start zbus server
let mut server = Connection::system().await?; let mut connection = Connection::system().await?;
server.object_server().at("/", ObjectManager).await.unwrap(); connection
.object_server()
.at("/org/asuslinux", ObjectManager)
.await
.unwrap();
let config = Config::new().load(); let config = Config::new().load();
let cfg_path = config.file_path(); let cfg_path = config.file_path();
let config = Arc::new(Mutex::new(config)); let config = Arc::new(Mutex::new(config));
// supported.add_to_server(&mut connection).await; // supported.add_to_server(&mut connection).await;
let platform = RogPlatform::new()?; // TODO: maybe needs async mutex?
let power = AsusPower::new()?; // TODO: maybe needs async mutex?
let attributes = FirmwareAttributes::new();
start_attributes_zbus(
&server,
platform.clone(),
power.clone(),
attributes.clone(),
config.clone()
)
.await?;
match CtrlFanCurveZbus::new() { match CtrlFanCurveZbus::new() {
Ok(ctrl) => { Ok(ctrl) => {
let sig_ctx = CtrlFanCurveZbus::signal_context(&server)?; let sig_ctx = CtrlFanCurveZbus::signal_context(&connection)?;
start_tasks(ctrl, &mut server, sig_ctx).await?; start_tasks(ctrl, &mut connection, sig_ctx).await?;
} }
Err(err) => { Err(err) => {
error!("FanCurves: {}", err); error!("FanCurves: {}", err);
@@ -91,30 +85,52 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
} }
match CtrlPlatform::new( match CtrlPlatform::new(
platform,
power,
attributes,
config.clone(), config.clone(),
&cfg_path, &cfg_path,
CtrlPlatform::signal_context(&server)? CtrlPlatform::signal_context(&connection)?,
) { ) {
Ok(ctrl) => { Ok(ctrl) => {
let sig_ctx = CtrlPlatform::signal_context(&server)?; let sig_ctx = CtrlPlatform::signal_context(&connection)?;
start_tasks(ctrl, &mut server, sig_ctx).await?; start_tasks(ctrl, &mut connection, sig_ctx).await?;
} }
Err(err) => { Err(err) => {
error!("CtrlPlatform: {}", err); error!("CtrlPlatform: {}", err);
} }
} }
let _ = DeviceManager::new(server.clone()).await?; match CtrlAnime::new(AnimeConfig::new().load()) {
Ok(ctrl) => {
let zbus = CtrlAnimeZbus(Arc::new(Mutex::new(ctrl)));
let sig_ctx = CtrlAnimeZbus::signal_context(&connection)?;
start_tasks(zbus, &mut connection, sig_ctx).await?;
}
Err(err) => {
info!("AniMe control: {}", err);
}
}
match CtrlSlash::new(SlashConfig::new().load()) {
Ok(ctrl) => {
let zbus = CtrlSlashZbus(Arc::new(Mutex::new(ctrl)));
// Currently, the Slash has no need for a loop watching power events, however,
// it could be cool to have the slash do some power-on/off animation
// (It has a built-in power on animation which plays when u plug in the power
// supply)
let sig_ctx = CtrlSlashZbus::signal_context(&connection)?;
start_tasks(zbus, &mut connection, sig_ctx).await?;
}
Err(err) => {
info!("AniMe control: {}", err);
}
}
let _ = AuraManager::new(connection.clone()).await?;
// Request dbus name after finishing initalizing all functions // Request dbus name after finishing initalizing all functions
server.request_name(DBUS_NAME).await?; connection.request_name(DBUS_NAME).await?;
info!("Startup success, begining dbus server loop");
loop { loop {
// This is just a blocker to idle and ensure the reator reacts // This is just a blocker to idle and ensure the reator reacts
server.executor().tick().await; connection.executor().tick().await;
} }
} }
+2 -9
View File
@@ -37,7 +37,7 @@ pub enum RogError {
SystemdUnitAction(String), SystemdUnitAction(String),
SystemdUnitWaitTimeout(String), SystemdUnitWaitTimeout(String),
Command(String, std::io::Error), Command(String, std::io::Error),
ParseRon(ron::Error) ParseRon(ron::Error),
} }
impl fmt::Display for RogError { impl fmt::Display for RogError {
@@ -87,7 +87,7 @@ impl fmt::Display for RogError {
) )
} }
RogError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error), RogError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error),
RogError::ParseRon(error) => write!(f, "Parse config error: {}", error) RogError::ParseRon(error) => write!(f, "Parse config error: {}", error),
} }
} }
} }
@@ -142,10 +142,3 @@ impl From<RogError> for zbus::fdo::Error {
zbus::fdo::Error::Failed(format!("{}", err)) zbus::fdo::Error::Failed(format!("{}", err))
} }
} }
impl From<RogError> for zbus::Error {
#[inline]
fn from(err: RogError) -> Self {
zbus::Error::Failure(format!("{}", err))
}
}
+32 -40
View File
@@ -1,18 +1,17 @@
#![deny(unused_must_use)] #![deny(unused_must_use)]
/// Configuration loading, saving /// Configuration loading, saving
pub mod config; pub mod config;
/// Control of anime matrix display
pub mod ctrl_anime;
/// Keyboard LED brightness control, RGB, and LED display modes
pub mod ctrl_aura;
/// Control platform profiles + fan-curves if available /// Control platform profiles + fan-curves if available
pub mod ctrl_fancurves; pub mod ctrl_fancurves;
/// Control ASUS bios function such as boot sound, Optimus/Dedicated gfx mode /// Control ASUS bios function such as boot sound, Optimus/Dedicated gfx mode
pub mod ctrl_platform; pub mod ctrl_platform;
/// Control of Slash led bar
pub mod ctrl_slash;
pub mod asus_armoury;
pub mod aura_anime;
pub mod aura_laptop;
pub mod aura_manager;
pub mod aura_scsi;
pub mod aura_slash;
pub mod aura_types;
pub mod error; pub mod error;
use std::future::Future; use std::future::Future;
@@ -23,19 +22,15 @@ use futures_lite::stream::StreamExt;
use log::{debug, info, warn}; use log::{debug, info, warn};
use logind_zbus::manager::ManagerProxy; use logind_zbus::manager::ManagerProxy;
use tokio::time::sleep; use tokio::time::sleep;
use zbus::object_server::{Interface, SignalEmitter};
use zbus::proxy::CacheProperties;
use zbus::zvariant::ObjectPath; use zbus::zvariant::ObjectPath;
use zbus::Connection; use zbus::{CacheProperties, Connection, SignalContext};
use crate::error::RogError; use crate::error::RogError;
const CONFIG_PATH_BASE: &str = "/etc/asusd/"; const CONFIG_PATH_BASE: &str = "/etc/asusd/";
pub const ASUS_ZBUS_PATH: &str = "/xyz/ljones"; pub static DBUS_NAME: &str = "org.asuslinux.Daemon";
pub static DBUS_PATH: &str = "/org/asuslinux/Daemon";
pub static DBUS_NAME: &str = "xyz.ljones.Asusd"; pub static DBUS_IFACE: &str = "org.asuslinux.Daemon";
pub static DBUS_PATH: &str = "/xyz/ljones/Daemon";
pub static DBUS_IFACE: &str = "xyz.ljones.Asusd";
/// This macro adds a function which spawns an `inotify` task on the passed in /// This macro adds a function which spawns an `inotify` task on the passed in
/// `Executor`. /// `Executor`.
@@ -44,7 +39,7 @@ pub static DBUS_IFACE: &str = "xyz.ljones.Asusd";
/// methods to be available: /// methods to be available:
/// - `<name>() -> SomeValue`, functionally is a getter, but is allowed to have /// - `<name>() -> SomeValue`, functionally is a getter, but is allowed to have
/// side effects. /// side effects.
/// - `notify_<name>(SignalEmitter, SomeValue)` /// - `notify_<name>(SignalContext, SomeValue)`
/// ///
/// In most cases if `SomeValue` is stored in a config then `<name>()` getter is /// In most cases if `SomeValue` is stored in a config then `<name>()` getter is
/// expected to update it. The getter should *never* write back to the path or /// expected to update it. The getter should *never* write back to the path or
@@ -61,13 +56,13 @@ pub static DBUS_IFACE: &str = "xyz.ljones.Asusd";
/// // TODO: this is kind of useless if it can't trigger some action /// // TODO: this is kind of useless if it can't trigger some action
#[macro_export] #[macro_export]
macro_rules! task_watch_item { macro_rules! task_watch_item {
($name:ident $name_str:literal $self_inner:ident) => { ($name:ident $self_inner:ident) => {
concat_idents::concat_idents!(fn_name = watch_, $name { concat_idents::concat_idents!(fn_name = watch_, $name {
async fn fn_name( async fn fn_name(
&self, &self,
signal_ctxt: SignalEmitter<'static>, signal_ctxt: SignalContext<'static>,
) -> Result<(), RogError> { ) -> Result<(), RogError> {
use futures_util::StreamExt; use zbus::export::futures_util::StreamExt;
let ctrl = self.clone(); let ctrl = self.clone();
concat_idents::concat_idents!(watch_fn = monitor_, $name { concat_idents::concat_idents!(watch_fn = monitor_, $name {
@@ -77,15 +72,12 @@ macro_rules! task_watch_item {
let mut buffer = [0; 32]; let mut buffer = [0; 32];
watch.into_event_stream(&mut buffer).unwrap().for_each(|_| async { watch.into_event_stream(&mut buffer).unwrap().for_each(|_| async {
if let Ok(value) = ctrl.$name() { // get new value from zbus method if let Ok(value) = ctrl.$name() { // get new value from zbus method
if ctrl.config.lock().await.$name != value { concat_idents::concat_idents!(notif_fn = $name, _changed {
log::debug!("{} was changed to {} externally", $name_str, value); ctrl.notif_fn(&signal_ctxt).await.ok();
concat_idents::concat_idents!(notif_fn = $name, _changed { });
ctrl.notif_fn(&signal_ctxt).await.ok(); let mut lock = ctrl.config.lock().await;
}); lock.$name = value;
let mut lock = ctrl.config.lock().await; lock.write();
lock.$name = value;
lock.write();
}
} }
}).await; }).await;
}); });
@@ -105,7 +97,7 @@ macro_rules! task_watch_item_notify {
concat_idents::concat_idents!(fn_name = watch_, $name { concat_idents::concat_idents!(fn_name = watch_, $name {
async fn fn_name( async fn fn_name(
&self, &self,
signal_ctxt: SignalEmitter<'static>, signal_ctxt: SignalContext<'static>,
) -> Result<(), RogError> { ) -> Result<(), RogError> {
use zbus::export::futures_util::StreamExt; use zbus::export::futures_util::StreamExt;
@@ -148,8 +140,8 @@ pub trait ReloadAndNotify {
fn reload_and_notify( fn reload_and_notify(
&mut self, &mut self,
signal_context: &SignalEmitter<'static>, signal_context: &SignalContext<'static>,
data: Self::Data data: Self::Data,
) -> impl Future<Output = Result<(), RogError>> + Send; ) -> impl Future<Output = Result<(), RogError>> + Send;
} }
@@ -157,9 +149,9 @@ pub trait ZbusRun {
fn add_to_server(self, server: &mut Connection) -> impl Future<Output = ()> + Send; fn add_to_server(self, server: &mut Connection) -> impl Future<Output = ()> + Send;
fn add_to_server_helper( fn add_to_server_helper(
iface: impl Interface, iface: impl zbus::Interface,
path: &str, path: &str,
server: &mut Connection server: &mut Connection,
) -> impl Future<Output = ()> + Send { ) -> impl Future<Output = ()> + Send {
async move { async move {
server server
@@ -179,8 +171,8 @@ pub trait ZbusRun {
pub trait CtrlTask { pub trait CtrlTask {
fn zbus_path() -> &'static str; fn zbus_path() -> &'static str;
fn signal_context(connection: &Connection) -> Result<SignalEmitter<'static>, zbus::Error> { fn signal_context(connection: &Connection) -> Result<SignalContext<'static>, zbus::Error> {
SignalEmitter::new(connection, Self::zbus_path()) SignalContext::new(connection, Self::zbus_path())
} }
/// Implement to set up various tasks that may be required, using the /// Implement to set up various tasks that may be required, using the
@@ -188,7 +180,7 @@ pub trait CtrlTask {
/// separate thread. /// separate thread.
fn create_tasks( fn create_tasks(
&self, &self,
signal: SignalEmitter<'static> signal: SignalContext<'static>,
) -> impl Future<Output = Result<(), RogError>> + Send; ) -> impl Future<Output = Result<(), RogError>> + Send;
// /// Create a timed repeating task // /// Create a timed repeating task
@@ -212,7 +204,7 @@ pub trait CtrlTask {
mut on_prepare_for_sleep: F1, mut on_prepare_for_sleep: F1,
mut on_prepare_for_shutdown: F2, mut on_prepare_for_shutdown: F2,
mut on_lid_change: F3, mut on_lid_change: F3,
mut on_external_power_change: F4 mut on_external_power_change: F4,
) -> impl Future<Output = ()> + Send ) -> impl Future<Output = ()> + Send
where where
F1: FnMut(bool) -> Fut1 + Send + 'static, F1: FnMut(bool) -> Fut1 + Send + 'static,
@@ -222,7 +214,7 @@ pub trait CtrlTask {
Fut1: Future<Output = ()> + Send, Fut1: Future<Output = ()> + Send,
Fut2: Future<Output = ()> + Send, Fut2: Future<Output = ()> + Send,
Fut3: Future<Output = ()> + Send, Fut3: Future<Output = ()> + Send,
Fut4: Future<Output = ()> + Send Fut4: Future<Output = ()> + Send,
{ {
async { async {
let connection = Connection::system() let connection = Connection::system()
@@ -302,10 +294,10 @@ pub trait GetSupported {
pub async fn start_tasks<T>( pub async fn start_tasks<T>(
mut zbus: T, mut zbus: T,
connection: &mut Connection, connection: &mut Connection,
signal_ctx: SignalEmitter<'static> signal_ctx: SignalContext<'static>,
) -> Result<(), RogError> ) -> Result<(), RogError>
where where
T: ZbusRun + Reloadable + CtrlTask + Clone T: ZbusRun + Reloadable + CtrlTask + Clone,
{ {
let zbus_clone = zbus.clone(); let zbus_clone = zbus.clone();
+3
View File
@@ -13,3 +13,6 @@ serde.workspace = true
ron.workspace = true ron.workspace = true
log.workspace = true log.workspace = true
[dev-dependencies]
cargo-husky.workspace = true
+7 -7
View File
@@ -19,7 +19,7 @@ use serde::Serialize;
/// implemented, the rest are intended to be free methods. /// implemented, the rest are intended to be free methods.
pub trait StdConfig pub trait StdConfig
where where
Self: Serialize + DeserializeOwned Self: Serialize + DeserializeOwned,
{ {
/// Taking over the standard `new()` to ensure things can be generic /// Taking over the standard `new()` to ensure things can be generic
fn new() -> Self; fn new() -> Self;
@@ -146,7 +146,11 @@ where
/// Renames the existing file to `<file>-old` /// Renames the existing file to `<file>-old`
fn rename_file_old(&self) { fn rename_file_old(&self) {
warn!("Renaming {} to {}-old", self.file_name(), self.file_name()); warn!(
"Renaming {} to {}-old and recreating config",
self.file_name(),
self.file_name()
);
let mut cfg_old = self.file_path().to_string_lossy().to_string(); let mut cfg_old = self.file_path().to_string_lossy().to_string();
cfg_old.push_str("-old"); cfg_old.push_str("-old");
std::fs::rename(self.file_path(), cfg_old).unwrap_or_else(|err| { std::fs::rename(self.file_path(), cfg_old).unwrap_or_else(|err| {
@@ -203,7 +207,7 @@ macro_rules! std_config_load {
/// new one created /// new one created
pub trait $trait_name<$($generic),*> pub trait $trait_name<$($generic),*>
where where
Self: $crate::StdConfig + DeserializeOwned + Serialize, Self: $crate::StdConfig +std::fmt::Debug + DeserializeOwned + Serialize,
$($generic: DeserializeOwned + Into<Self>),* $($generic: DeserializeOwned + Into<Self>),*
{ {
fn load(mut self) -> Self { fn load(mut self) -> Self {
@@ -270,8 +274,6 @@ mod tests {
} }
} }
let _ = Test {};
impl crate::StdConfigLoad1<Old1> for Test {} impl crate::StdConfigLoad1<Old1> for Test {}
} }
@@ -321,8 +323,6 @@ mod tests {
} }
} }
let _ = Test {};
impl crate::StdConfigLoad3<Old1, Old2, Old3> for Test {} impl crate::StdConfigLoad3<Old1, Old2, Old3> for Test {}
} }
} }
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "cpuctl"
license.workspace = true
version.workspace = true
readme.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
edition.workspace = true
[dependencies]
+14
View File
@@ -0,0 +1,14 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
+11 -11
View File
@@ -3,24 +3,24 @@
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig> <busconfig>
<policy group="adm"> <policy group="adm">
<allow send_destination="xyz.ljones.Asusd"/> <allow send_destination="org.asuslinux.Daemon"/>
<allow receive_sender="xyz.ljones.Asusd"/> <allow receive_sender="org.asuslinux.Daemon"/>
</policy> </policy>
<policy group="sudo"> <policy group="sudo">
<allow send_destination="xyz.ljones.Asusd"/> <allow send_destination="org.asuslinux.Daemon"/>
<allow receive_sender="xyz.ljones.Asusd"/> <allow receive_sender="org.asuslinux.Daemon"/>
</policy> </policy>
<policy group="users"> <policy group="users">
<allow send_destination="xyz.ljones.Asusd"/> <allow send_destination="org.asuslinux.Daemon"/>
<allow receive_sender="xyz.ljones.Asusd"/> <allow receive_sender="org.asuslinux.Daemon"/>
</policy> </policy>
<policy group="wheel"> <policy group="wheel">
<allow send_destination="xyz.ljones.Asusd"/> <allow send_destination="org.asuslinux.Daemon"/>
<allow receive_sender="xyz.ljones.Asusd"/> <allow receive_sender="org.asuslinux.Daemon"/>
</policy> </policy>
<policy user="root"> <policy user="root">
<allow own="xyz.ljones.Asusd"/> <allow own="org.asuslinux.Daemon"/>
<allow send_destination="xyz.ljones.Asusd"/> <allow send_destination="org.asuslinux.Daemon"/>
<allow receive_sender="xyz.ljones.Asusd"/> <allow receive_sender="org.asuslinux.Daemon"/>
</policy> </policy>
</busconfig> </busconfig>
-2
View File
@@ -7,8 +7,6 @@ ENV{DMI_FAMILY}=="*ROG*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Zephyrus*", GOTO="asusd_start" ENV{DMI_FAMILY}=="*Zephyrus*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Strix*", GOTO="asusd_start" ENV{DMI_FAMILY}=="*Strix*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Vivo*ook*", GOTO="asusd_start" ENV{DMI_FAMILY}=="*Vivo*ook*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Zenbook*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*ProArt*", GOTO="asusd_start"
# No match so # No match so
GOTO="asusd_end" GOTO="asusd_end"
+2 -3
View File
@@ -6,14 +6,13 @@ After=nvidia-powerd.service systemd-udevd.service
[Service] [Service]
Environment=IS_SERVICE=1 Environment=IS_SERVICE=1
Environment=RUST_LOG="debug" Environment=RUST_LOG="info"
# required to prevent init issues with hid_asus and MCU # required to prevent init issues with hid_asus and MCU
ExecStartPre=/bin/sleep 1 ExecStartPre=/bin/sleep 1
ExecStart=/usr/bin/asusd ExecStart=/usr/bin/asusd
Restart=on-failure Restart=on-failure
RestartSec=1 RestartSec=1
Type=dbus Type=dbus
BusName=xyz.ljones.Asusd BusName=org.asuslinux.Daemon
SELinuxContext=system_u:system_r:unconfined_t:s0 SELinuxContext=system_u:system_r:unconfined_t:s0
#SELinuxContext=system_u:object_r:modules_object_t:s0 #SELinuxContext=system_u:object_r:modules_object_t:s0
TimeoutSec=10
Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 81 KiB

+1 -1
View File
@@ -135,7 +135,7 @@ impl crate::ZbusAdd for CtrlAnimeZbus {
} }
} }
#[dbus_interface(name = "xyz.ljones.Asusd")] #[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlAnimeZbus { impl CtrlAnimeZbus {
async fn <zbus method>() { async fn <zbus method>() {
let lock = self.inner.lock().await; let lock = self.inner.lock().await;
+8 -14
View File
@@ -20,14 +20,13 @@
%global debug_package %{nil} %global debug_package %{nil}
%endif %endif
%define specrelease %{?dist} %global rpm_dkms_opt 1
%define pkg_release 3%{specrelease}
# Use hardening ldflags. # Use hardening ldflags.
%global rustflags -Clink-arg=-Wl,-z,relro,-z,now %global rustflags -Clink-arg=-Wl,-z,relro,-z,now
Name: asusctl Name: asusctl
Version: 6.0.7 Version: 4.7.0
Release: %{pkg_release} Release: 2
Summary: Control fan speeds, LEDs, graphics modes, and charge levels for ASUS notebooks Summary: Control fan speeds, LEDs, graphics modes, and charge levels for ASUS notebooks
License: MPLv2 License: MPLv2
@@ -35,8 +34,8 @@ Group: System Environment/Kernel
URL: https://gitlab.com/asus-linux/asusctl URL: https://gitlab.com/asus-linux/asusctl
Source: %{name}-%{version}.tar.gz Source: %{name}-%{version}.tar.gz
Source1: vendor_%{name}_%{version}.tar.xz Source1: vendor-%{name}-%{version}.tar.gz
Source2: cargo-config Source2: cargo_config
BuildRequires: cargo BuildRequires: cargo
BuildRequires: rust-packaging BuildRequires: rust-packaging
@@ -45,16 +44,12 @@ BuildRequires: clang-devel
BuildRequires: cmake BuildRequires: cmake
BuildRequires: rust BuildRequires: rust
BuildRequires: rust-std-static BuildRequires: rust-std-static
BuildRequires: pkgconfig(expat)
BuildRequires: pkgconfig(dbus-1) BuildRequires: pkgconfig(dbus-1)
BuildRequires: pkgconfig(libudev) BuildRequires: pkgconfig(libudev)
BuildRequires: pkgconfig(xkbcommon)
BuildRequires: pkgconfig(libzstd)
BuildRequires: pkgconfig(gtk+-3.0) BuildRequires: pkgconfig(gtk+-3.0)
BuildRequires: pkgconfig(gdk-3.0) BuildRequires: pkgconfig(gdk-3.0)
BuildRequires: desktop-file-utils BuildRequires: desktop-file-utils
Requires: libappindicator-gtk3
# expat-devel pcre2-devel
%description %description
asus-nb-ctrl is a utility for Linux to control many aspects of various asus-nb-ctrl is a utility for Linux to control many aspects of various
@@ -72,10 +67,9 @@ A one-stop-shop GUI tool for asusd/asusctl. It aims to provide most controls,
a notification service, and ability to run in the background. a notification service, and ability to run in the background.
%prep %prep
# %setup -D -T -a 1 -c -n %{name}-%{version}/vendor
# %setup -D -T -a 0 -c
%autosetup %autosetup
%setup -D -T -a 1 %setup -D -T -a 1 -c -n %{name}-%{version}/vendor
cd ..
mv Cargo.lock{,.bak} mv Cargo.lock{,.bak}
%cargo_prep %cargo_prep
+5 -3
View File
@@ -1,4 +1,4 @@
use log::warn; use log::{info, warn};
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Clone)] #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Clone)]
pub struct DMIID { pub struct DMIID {
@@ -12,7 +12,7 @@ pub struct DMIID {
pub bios_vendor: String, pub bios_vendor: String,
pub bios_version: String, pub bios_version: String,
pub product_family: String, pub product_family: String,
pub product_name: String pub product_name: String,
} }
impl DMIID { impl DMIID {
@@ -33,6 +33,8 @@ impl DMIID {
})?; })?;
if let Some(device) = (result).next() { if let Some(device) = (result).next() {
info!("Found dmi ID info at {:?}", device.sysname());
return Ok(Self { return Ok(Self {
id_model: device id_model: device
.property_value("ID_MODEL") .property_value("ID_MODEL")
@@ -77,7 +79,7 @@ impl DMIID {
product_name: device product_name: device
.attribute_value("product_name") .attribute_value("product_name")
.map(|s| s.to_string_lossy().to_string()) .map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()) .unwrap_or("Unknown".to_string()),
}); });
} }
Err("dmi not found".into()) Err("dmi not found".into())
+8
View File
@@ -28,9 +28,17 @@ gif.workspace = true
log.workspace = true log.workspace = true
serde.workspace = true serde.workspace = true
serde_derive.workspace = true
glam.workspace = true glam.workspace = true
typeshare.workspace = true
zbus = { workspace = true, optional = true } zbus = { workspace = true, optional = true }
dmi_id = { path = "../dmi-id", optional = true } dmi_id = { path = "../dmi-id", optional = true }
[dev-dependencies]
cargo-husky.workspace = true
[package.metadata.cargo-machete]
ignored = ["serde"]
+22 -37
View File
@@ -3,9 +3,9 @@ use std::str::FromStr;
use std::thread::sleep; use std::thread::sleep;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use dmi_id::DMIID;
use log::info; use log::info;
use serde::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")] #[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value}; use zbus::zvariant::{OwnedValue, Type, Value};
@@ -22,29 +22,27 @@ const BLOCK_END: usize = 634;
const PANE_LEN: usize = BLOCK_END - BLOCK_START; const PANE_LEN: usize = BLOCK_END - BLOCK_START;
/// First packet is for GA401 + GA402 /// First packet is for GA401 + GA402
pub const USB_PREFIX1: [u8; 7] = [ pub const USB_PREFIX1: [u8; 7] = [0x5e, 0xc0, 0x02, 0x01, 0x00, 0x73, 0x02];
0x5e, 0xc0, 0x02, 0x01, 0x00, 0x73, 0x02
];
/// Second packet is for GA401 + GA402 /// Second packet is for GA401 + GA402
pub const USB_PREFIX2: [u8; 7] = [ pub const USB_PREFIX2: [u8; 7] = [0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02];
0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02
];
/// Third packet is for GA402 matrix /// Third packet is for GA402 matrix
pub const USB_PREFIX3: [u8; 7] = [ pub const USB_PREFIX3: [u8; 7] = [0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02];
0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02
];
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))] #[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[typeshare]
#[derive(Default, Deserialize, PartialEq, Eq, Clone, Copy, Serialize, Debug)] #[derive(Default, Deserialize, PartialEq, Eq, Clone, Copy, Serialize, Debug)]
pub struct Animations { pub struct Animations {
pub boot: AnimBooting, pub boot: AnimBooting,
pub awake: AnimAwake, pub awake: AnimAwake,
pub sleep: AnimSleeping, pub sleep: AnimSleeping,
pub shutdown: AnimShutdown pub shutdown: AnimShutdown,
} }
// TODO: move this out // TODO: move this out
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type))] #[cfg_attr(feature = "dbus", derive(Type))]
#[typeshare]
#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] #[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub struct DeviceState { pub struct DeviceState {
pub display_enabled: bool, pub display_enabled: bool,
@@ -54,17 +52,17 @@ pub struct DeviceState {
pub off_when_unplugged: bool, pub off_when_unplugged: bool,
pub off_when_suspended: bool, pub off_when_suspended: bool,
pub off_when_lid_closed: bool, pub off_when_lid_closed: bool,
pub brightness_on_battery: Brightness pub brightness_on_battery: Brightness,
} }
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type), zvariant(signature = "s"))] #[cfg_attr(feature = "dbus", derive(Type), zvariant(signature = "s"))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize, Default)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub enum AnimeType { pub enum AnimeType {
GA401, GA401,
GA402, GA402,
GU604, GU604,
#[default] Unknown,
Unsupported
} }
impl FromStr for AnimeType { impl FromStr for AnimeType {
@@ -75,30 +73,17 @@ impl FromStr for AnimeType {
"ga401" | "GA401" => Self::GA401, "ga401" | "GA401" => Self::GA401,
"ga402" | "GA402" => Self::GA402, "ga402" | "GA402" => Self::GA402,
"gu604" | "GU604" => Self::GU604, "gu604" | "GU604" => Self::GU604,
_ => Self::Unsupported _ => Self::Unknown,
}) })
} }
} }
impl AnimeType { impl AnimeType {
pub fn from_dmi() -> Self {
let board_name = DMIID::new().unwrap_or_default().board_name.to_uppercase();
if board_name.contains("GA401I") || board_name.contains("GA401Q") {
AnimeType::GA401
} else if board_name.contains("GA402R") || board_name.contains("GA402X") {
AnimeType::GA402
} else if board_name.contains("GU604V") {
AnimeType::GU604
} else {
AnimeType::Unsupported
}
}
/// The width of diagonal images /// The width of diagonal images
pub fn width(&self) -> usize { pub fn width(&self) -> usize {
match self { match self {
AnimeType::GU604 => 70, AnimeType::GU604 => 70,
_ => 74 _ => 74,
} }
} }
@@ -107,7 +92,7 @@ impl AnimeType {
match self { match self {
AnimeType::GA401 => 36, AnimeType::GA401 => 36,
AnimeType::GU604 => 43, AnimeType::GU604 => 43,
_ => 39 _ => 39,
} }
} }
@@ -116,7 +101,7 @@ impl AnimeType {
match self { match self {
AnimeType::GA401 => PANE_LEN * 2, AnimeType::GA401 => PANE_LEN * 2,
AnimeType::GU604 => PANE_LEN * 3, AnimeType::GU604 => PANE_LEN * 3,
_ => PANE_LEN * 3 _ => PANE_LEN * 3,
} }
} }
} }
@@ -127,7 +112,7 @@ impl AnimeType {
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AnimeDataBuffer { pub struct AnimeDataBuffer {
data: Vec<u8>, data: Vec<u8>,
anime: AnimeType anime: AnimeType,
} }
impl AnimeDataBuffer { impl AnimeDataBuffer {
@@ -137,7 +122,7 @@ impl AnimeDataBuffer {
AnimeDataBuffer { AnimeDataBuffer {
data: vec![0u8; len], data: vec![0u8; len],
anime anime,
} }
} }
@@ -180,7 +165,7 @@ impl TryFrom<AnimeDataBuffer> for AnimePacketType {
let mut buffers = match anime.anime { let mut buffers = match anime.anime {
AnimeType::GA401 => vec![[0; 640]; 2], AnimeType::GA401 => vec![[0; 640]; 2],
AnimeType::GA402 | AnimeType::GU604 | AnimeType::Unsupported => vec![[0; 640]; 3] AnimeType::GA402 | AnimeType::GU604 | AnimeType::Unknown => vec![[0; 640]; 3],
}; };
for (idx, chunk) in anime.data.as_slice().chunks(PANE_LEN).enumerate() { for (idx, chunk) in anime.data.as_slice().chunks(PANE_LEN).enumerate() {
@@ -191,7 +176,7 @@ impl TryFrom<AnimeDataBuffer> for AnimePacketType {
if matches!( if matches!(
anime.anime, anime.anime,
AnimeType::GA402 | AnimeType::GU604 | AnimeType::Unsupported AnimeType::GA402 | AnimeType::GU604 | AnimeType::Unknown
) { ) {
buffers[2][..7].copy_from_slice(&USB_PREFIX3); buffers[2][..7].copy_from_slice(&USB_PREFIX3);
} }
+11 -16
View File
@@ -3,8 +3,6 @@
use std::path::Path; use std::path::Path;
use std::time::Duration; use std::time::Duration;
use log::error;
use crate::data::AnimeDataBuffer; use crate::data::AnimeDataBuffer;
use crate::error::{AnimeError, Result}; use crate::error::{AnimeError, Result};
use crate::AnimeType; use crate::AnimeType;
@@ -20,7 +18,7 @@ impl AnimeDiagonal {
Self( Self(
anime_type, anime_type,
vec![vec![0; anime_type.width()]; anime_type.height()], vec![vec![0; anime_type.width()]; anime_type.height()],
duration duration,
) )
} }
@@ -49,12 +47,9 @@ impl AnimeDiagonal {
path: &Path, path: &Path,
duration: Option<Duration>, duration: Option<Duration>,
bright: f32, bright: f32,
anime_type: AnimeType anime_type: AnimeType,
) -> Result<Self> { ) -> Result<Self> {
let data = std::fs::read(path).map_err(|e| { let data = std::fs::read(path)?;
error!("Could not open {path:?}: {e:?}");
e
})?;
let data = std::io::Cursor::new(data); let data = std::io::Cursor::new(data);
let decoder = png_pong::Decoder::new(data)?.into_steps(); let decoder = png_pong::Decoder::new(data)?.into_steps();
let png_pong::Step { raster, delay: _ } = decoder.last().ok_or(AnimeError::NoFrames)??; let png_pong::Step { raster, delay: _ } = decoder.last().ok_or(AnimeError::NoFrames)??;
@@ -86,7 +81,7 @@ impl AnimeDiagonal {
png_pong::PngRaster::Rgba16(ras) => { png_pong::PngRaster::Rgba16(ras) => {
Self::pixels_from_16bit(ras, &mut matrix, bright, false); Self::pixels_from_16bit(ras, &mut matrix, bright, false);
} }
png_pong::PngRaster::Palette(..) => return Err(AnimeError::Format) png_pong::PngRaster::Palette(..) => return Err(AnimeError::Format),
}; };
Ok(matrix) Ok(matrix)
@@ -96,9 +91,9 @@ impl AnimeDiagonal {
ras: &pix::Raster<P>, ras: &pix::Raster<P>,
matrix: &mut AnimeDiagonal, matrix: &mut AnimeDiagonal,
bright: f32, bright: f32,
grey: bool grey: bool,
) where ) where
P: pix::el::Pixel<Chan = pix::chan::Ch8> P: pix::el::Pixel<Chan = pix::chan::Ch8>,
{ {
let width = ras.width(); let width = ras.width();
for (y, row) in ras.pixels().chunks(width as usize).enumerate() { for (y, row) in ras.pixels().chunks(width as usize).enumerate() {
@@ -121,9 +116,9 @@ impl AnimeDiagonal {
ras: &pix::Raster<P>, ras: &pix::Raster<P>,
matrix: &mut AnimeDiagonal, matrix: &mut AnimeDiagonal,
bright: f32, bright: f32,
grey: bool grey: bool,
) where ) where
P: pix::el::Pixel<Chan = pix::chan::Ch16> P: pix::el::Pixel<Chan = pix::chan::Ch16>,
{ {
let width = ras.width(); let width = ras.width();
for (y, row) in ras.pixels().chunks(width as usize).enumerate() { for (y, row) in ras.pixels().chunks(width as usize).enumerate() {
@@ -146,7 +141,7 @@ impl AnimeDiagonal {
match anime_type { match anime_type {
AnimeType::GA401 => self.to_ga401_packets(), AnimeType::GA401 => self.to_ga401_packets(),
AnimeType::GU604 => self.to_gu604_packets(), AnimeType::GU604 => self.to_gu604_packets(),
_ => self.to_ga402_packets() _ => self.to_ga402_packets(),
} }
} }
@@ -224,7 +219,7 @@ impl AnimeDiagonal {
x: usize, x: usize,
y: usize, y: usize,
start_index: &mut usize, start_index: &mut usize,
len: usize len: usize,
) { ) {
buf[*start_index..*start_index + len].copy_from_slice(&anime.get_row(x, y, len)); buf[*start_index..*start_index + len].copy_from_slice(&anime.get_row(x, y, len));
*start_index += len; *start_index += len;
@@ -307,7 +302,7 @@ impl AnimeDiagonal {
x: usize, x: usize,
y: usize, y: usize,
start_index: &mut usize, start_index: &mut usize,
len: usize len: usize,
) { ) {
buf[*start_index..*start_index + len].copy_from_slice(&anime.get_row(x, y, len)); buf[*start_index..*start_index + len].copy_from_slice(&anime.get_row(x, y, len));
*start_index += len; *start_index += len;
+2 -2
View File
@@ -24,7 +24,7 @@ pub enum AnimeError {
DataBufferLength, DataBufferLength,
PixelGifWidth(usize), PixelGifWidth(usize),
PixelGifHeight(usize), PixelGifHeight(usize),
ParseError(String) ParseError(String),
} }
impl fmt::Display for AnimeError { impl fmt::Display for AnimeError {
@@ -61,7 +61,7 @@ impl fmt::Display for AnimeError {
AnimeError::PixelGifHeight(n) => write!( AnimeError::PixelGifHeight(n) => write!(
f, f,
"The gif used for pixel-perfect gif is is taller than {n}" "The gif used for pixel-perfect gif is is taller than {n}"
) ),
} }
} }
} }
+19 -25
View File
@@ -4,8 +4,7 @@ use std::path::Path;
use std::time::Duration; use std::time::Duration;
use glam::Vec2; use glam::Vec2;
use log::error; use serde_derive::{Deserialize, Serialize};
use serde::{Deserialize, Serialize};
use crate::error::{AnimeError, Result}; use crate::error::{AnimeError, Result};
use crate::{AnimeDataBuffer, AnimeDiagonal, AnimeImage, AnimeType, Pixel}; use crate::{AnimeDataBuffer, AnimeDiagonal, AnimeImage, AnimeType, Pixel};
@@ -16,7 +15,7 @@ pub struct AnimeFrame {
/// the `asusd` daemon over dbus or converted to USB packet with /// the `asusd` daemon over dbus or converted to USB packet with
/// `AnimePacketType::from(buffer)` /// `AnimePacketType::from(buffer)`
data: AnimeDataBuffer, data: AnimeDataBuffer,
delay: Duration delay: Duration,
} }
impl AnimeFrame { impl AnimeFrame {
@@ -44,7 +43,7 @@ pub enum AnimTime {
/// Run for infinite time /// Run for infinite time
Infinite, Infinite,
/// Fade in, play for, fade out /// Fade in, play for, fade out
Fade(Fade) Fade(Fade),
} }
impl Default for AnimTime { impl Default for AnimTime {
@@ -59,7 +58,7 @@ impl Default for AnimTime {
pub struct Fade { pub struct Fade {
fade_in: Duration, fade_in: Duration,
show_for: Option<Duration>, show_for: Option<Duration>,
fade_out: Duration fade_out: Duration,
} }
impl Fade { impl Fade {
@@ -67,7 +66,7 @@ impl Fade {
Self { Self {
fade_in, fade_in,
show_for, show_for,
fade_out fade_out,
} }
} }
@@ -100,7 +99,7 @@ impl AnimeGif {
file_name: &Path, file_name: &Path,
duration: AnimTime, duration: AnimTime,
brightness: f32, brightness: f32,
anime_type: AnimeType anime_type: AnimeType,
) -> Result<Self> { ) -> Result<Self> {
let mut matrix = AnimeDiagonal::new(anime_type, None); let mut matrix = AnimeDiagonal::new(anime_type, None);
@@ -108,10 +107,7 @@ impl AnimeGif {
// Configure the decoder such that it will expand the image to RGBA. // Configure the decoder such that it will expand the image to RGBA.
decoder.set_color_output(gif::ColorOutput::RGBA); decoder.set_color_output(gif::ColorOutput::RGBA);
// Read the file header // Read the file header
let file = File::open(file_name).map_err(|e| { let file = File::open(file_name)?;
error!("Could not open {file_name:?}: {e:?}");
e
})?;
let mut decoder = decoder.read_info(file)?; let mut decoder = decoder.read_info(file)?;
let mut frames = Vec::default(); let mut frames = Vec::default();
@@ -142,7 +138,7 @@ impl AnimeGif {
frames.push(AnimeFrame { frames.push(AnimeFrame {
data: matrix.into_data_buffer(anime_type)?, data: matrix.into_data_buffer(anime_type)?,
delay: Duration::from_millis(wait as u64) delay: Duration::from_millis(wait as u64),
}); });
} }
Ok(Self(frames, duration)) Ok(Self(frames, duration))
@@ -154,7 +150,7 @@ impl AnimeGif {
file_name: &Path, file_name: &Path,
anime_type: AnimeType, anime_type: AnimeType,
duration: AnimTime, duration: AnimTime,
brightness: f32 brightness: f32,
) -> Result<Self> { ) -> Result<Self> {
let image = AnimeDiagonal::from_png(file_name, None, brightness, anime_type)?; let image = AnimeDiagonal::from_png(file_name, None, brightness, anime_type)?;
@@ -170,7 +166,7 @@ impl AnimeGif {
let single = AnimeFrame { let single = AnimeFrame {
data: image.into_data_buffer(anime_type)?, data: image.into_data_buffer(anime_type)?,
delay: Duration::from_millis(30) delay: Duration::from_millis(30),
}; };
let frames = vec![single; frame_count as usize]; let frames = vec![single; frame_count as usize];
@@ -187,17 +183,15 @@ impl AnimeGif {
translation: Vec2, translation: Vec2,
duration: AnimTime, duration: AnimTime,
brightness: f32, brightness: f32,
anime_type: AnimeType anime_type: AnimeType,
) -> Result<Self> { ) -> Result<Self> {
let mut frames = Vec::new(); let mut frames = Vec::new();
let mut decoder = gif::DecodeOptions::new(); let mut decoder = gif::DecodeOptions::new();
// Configure the decoder such that it will expand the image to RGBA. // Configure the decoder such that it will expand the image to RGBA.
decoder.set_color_output(gif::ColorOutput::RGBA); decoder.set_color_output(gif::ColorOutput::RGBA);
// Read the file header // Read the file header
let file = File::open(file_name).map_err(|e| { let file = File::open(file_name)?;
error!("Could not open {file_name:?}: {e:?}");
e
})?;
let mut decoder = decoder.read_info(file)?; let mut decoder = decoder.read_info(file)?;
let height = decoder.height(); let height = decoder.height();
@@ -211,7 +205,7 @@ impl AnimeGif {
brightness, brightness,
pixels, pixels,
decoder.width() as u32, decoder.width() as u32,
anime_type anime_type,
)?; )?;
while let Some(frame) = decoder.read_next_frame()? { while let Some(frame) = decoder.read_next_frame()? {
@@ -226,7 +220,7 @@ impl AnimeGif {
brightness, brightness,
pixels, pixels,
width as u32, width as u32,
anime_type anime_type,
)?; )?;
} }
for (y, row) in frame.buffer.chunks(frame.width as usize * 4).enumerate() { for (y, row) in frame.buffer.chunks(frame.width as usize * 4).enumerate() {
@@ -239,7 +233,7 @@ impl AnimeGif {
(x + frame.left as usize) + ((y + frame.top as usize) * width as usize); (x + frame.left as usize) + ((y + frame.top as usize) * width as usize);
image.get_mut()[pos] = Pixel { image.get_mut()[pos] = Pixel {
color: ((px[0] as u32 + px[1] as u32 + px[2] as u32) / 3), color: ((px[0] as u32 + px[1] as u32 + px[2] as u32) / 3),
alpha: 1.0 alpha: 1.0,
}; };
} }
} }
@@ -247,7 +241,7 @@ impl AnimeGif {
frames.push(AnimeFrame { frames.push(AnimeFrame {
data: <AnimeDataBuffer>::try_from(&image)?, data: <AnimeDataBuffer>::try_from(&image)?,
delay: Duration::from_millis(wait as u64) delay: Duration::from_millis(wait as u64),
}); });
} }
Ok(Self(frames, duration)) Ok(Self(frames, duration))
@@ -265,7 +259,7 @@ impl AnimeGif {
translation: Vec2, translation: Vec2,
duration: AnimTime, duration: AnimTime,
brightness: f32, brightness: f32,
anime_type: AnimeType anime_type: AnimeType,
) -> Result<Self> { ) -> Result<Self> {
let image = let image =
AnimeImage::from_png(file_name, scale, angle, translation, brightness, anime_type)?; AnimeImage::from_png(file_name, scale, angle, translation, brightness, anime_type)?;
@@ -282,7 +276,7 @@ impl AnimeGif {
let single = AnimeFrame { let single = AnimeFrame {
data: <AnimeDataBuffer>::try_from(&image)?, data: <AnimeDataBuffer>::try_from(&image)?,
delay: Duration::from_millis(30) delay: Duration::from_millis(30),
}; };
let frames = vec![single; frame_count as usize]; let frames = vec![single; frame_count as usize];
+3 -3
View File
@@ -18,7 +18,7 @@ const HEIGHT: usize = 55;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AnimeGrid { pub struct AnimeGrid {
anime_type: AnimeType, anime_type: AnimeType,
data: [[u8; WIDTH]; HEIGHT] data: [[u8; WIDTH]; HEIGHT],
} }
impl AnimeGrid { impl AnimeGrid {
@@ -26,7 +26,7 @@ impl AnimeGrid {
pub fn new(anime_type: AnimeType) -> Self { pub fn new(anime_type: AnimeType) -> Self {
Self { Self {
anime_type, anime_type,
data: [[0u8; WIDTH]; HEIGHT] data: [[0u8; WIDTH]; HEIGHT],
} }
} }
@@ -174,7 +174,7 @@ mod tests {
0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0,
0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]; ];
assert_eq!(matrix.data(), &data_cmp); assert_eq!(matrix.data(), &data_cmp);
} }
+23 -29
View File
@@ -3,7 +3,6 @@ use std::path::Path;
pub use glam::Vec2; pub use glam::Vec2;
use glam::{Mat3, Vec3}; use glam::{Mat3, Vec3};
use log::error;
use crate::data::AnimeDataBuffer; use crate::data::AnimeDataBuffer;
use crate::error::{AnimeError, Result}; use crate::error::{AnimeError, Result};
@@ -13,7 +12,7 @@ use crate::AnimeType;
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct Pixel { pub struct Pixel {
pub color: u32, pub color: u32,
pub alpha: f32 pub alpha: f32,
} }
impl Default for Pixel { impl Default for Pixel {
@@ -21,7 +20,7 @@ impl Default for Pixel {
fn default() -> Self { fn default() -> Self {
Pixel { Pixel {
color: 0, color: 0,
alpha: 0.0 alpha: 0.0,
} }
} }
} }
@@ -76,7 +75,7 @@ pub struct AnimeImage {
/// The type of the display. The GA401 and GA402 use the same controller and /// The type of the display. The GA401 and GA402 use the same controller and
/// therefore same ID, so the identifier must be by laptop model in /// therefore same ID, so the identifier must be by laptop model in
/// `AnimeType`. /// `AnimeType`.
anime_type: AnimeType anime_type: AnimeType,
} }
impl AnimeImage { impl AnimeImage {
@@ -88,7 +87,7 @@ impl AnimeImage {
bright: f32, bright: f32,
pixels: Vec<Pixel>, pixels: Vec<Pixel>,
width: u32, width: u32,
anime_type: AnimeType anime_type: AnimeType,
) -> Result<Self> { ) -> Result<Self> {
if !(0.0..=1.0).contains(&bright) { if !(0.0..=1.0).contains(&bright) {
return Err(AnimeError::InvalidBrightness(bright)); return Err(AnimeError::InvalidBrightness(bright));
@@ -102,7 +101,7 @@ impl AnimeImage {
led_pos: Self::generate_image_positioning(anime_type), led_pos: Self::generate_image_positioning(anime_type),
img_pixels: pixels, img_pixels: pixels,
width, width,
anime_type anime_type,
}) })
} }
@@ -121,7 +120,7 @@ impl AnimeImage {
match anime_type { match anime_type {
AnimeType::GA401 => 0.8, AnimeType::GA401 => 0.8,
AnimeType::GU604 => 0.78, AnimeType::GU604 => 0.78,
_ => 0.77 _ => 0.77,
} }
} }
@@ -138,7 +137,7 @@ impl AnimeImage {
match anime_type { match anime_type {
AnimeType::GA401 => 0.3, AnimeType::GA401 => 0.3,
AnimeType::GU604 => 0.28, AnimeType::GU604 => 0.28,
_ => 0.283 _ => 0.283,
} }
} }
@@ -237,7 +236,7 @@ impl AnimeImage {
AnimeType::GA401 => (33.0 + 0.5) * Self::scale_x(anime_type), AnimeType::GA401 => (33.0 + 0.5) * Self::scale_x(anime_type),
AnimeType::GU604 => (38.0 + 0.5) * Self::scale_x(anime_type), AnimeType::GU604 => (38.0 + 0.5) * Self::scale_x(anime_type),
_ => (35.0 + 0.5) * Self::scale_x(anime_type) _ => (35.0 + 0.5) * Self::scale_x(anime_type),
} }
} }
@@ -246,7 +245,7 @@ impl AnimeImage {
match anime_type { match anime_type {
AnimeType::GA401 => 55, AnimeType::GA401 => 55,
AnimeType::GU604 => 62, AnimeType::GU604 => 62,
_ => 61 _ => 61,
} }
} }
@@ -257,7 +256,7 @@ impl AnimeImage {
AnimeType::GA401 => (54.0 + 1.0) * Self::scale_y(anime_type), AnimeType::GA401 => (54.0 + 1.0) * Self::scale_y(anime_type),
AnimeType::GU604 => 62.0 * Self::scale_y(anime_type), AnimeType::GU604 => 62.0 * Self::scale_y(anime_type),
// GA402 may not have dead pixels and require only the physical LED count // GA402 may not have dead pixels and require only the physical LED count
_ => 61.0 * Self::scale_y(anime_type) _ => 61.0 * Self::scale_y(anime_type),
} }
} }
@@ -267,11 +266,11 @@ impl AnimeImage {
AnimeType::GA401 => match y { AnimeType::GA401 => match y {
0 | 2 | 4 => 33, 0 | 2 | 4 => 33,
1 | 3 => 35, // Some rows are padded 1 | 3 => 35, // Some rows are padded
_ => 36 - y / 2 _ => 36 - y / 2,
}, },
AnimeType::GU604 => AnimeImage::width(anime_type, y), AnimeType::GU604 => AnimeImage::width(anime_type, y),
// GA402 does not have padding, equivalent to width // GA402 does not have padding, equivalent to width
_ => AnimeImage::width(anime_type, y) _ => AnimeImage::width(anime_type, y),
} }
} }
@@ -321,9 +320,7 @@ impl AnimeImage {
let pos = Vec3::new(led.x(), led.y(), 1.0); let pos = Vec3::new(led.x(), led.y(), 1.0);
let x0 = led_from_px.mul_vec3(pos + Vec3::new(0.0, -0.5, 0.0)); let x0 = led_from_px.mul_vec3(pos + Vec3::new(0.0, -0.5, 0.0));
const GROUP: [f32; 4] = [ const GROUP: [f32; 4] = [0.0, 0.5, 1.0, 1.5];
0.0, 0.5, 1.0, 1.5
];
for u in &GROUP { for u in &GROUP {
for v in &GROUP { for v in &GROUP {
let sample = x0 + *u * du + *v * dv; let sample = x0 + *u * du + *v * dv;
@@ -399,7 +396,7 @@ impl AnimeImage {
let led_from_cm = Mat3::from_scale(Vec2::new( let led_from_cm = Mat3::from_scale(Vec2::new(
1.0 / AnimeImage::scale_x(self.anime_type), 1.0 / AnimeImage::scale_x(self.anime_type),
1.0 / AnimeImage::scale_y(self.anime_type) 1.0 / AnimeImage::scale_y(self.anime_type),
)); ));
let transform = let transform =
@@ -422,12 +419,9 @@ impl AnimeImage {
angle: f32, angle: f32,
translation: Vec2, translation: Vec2,
bright: f32, bright: f32,
anime_type: AnimeType anime_type: AnimeType,
) -> Result<Self> { ) -> Result<Self> {
let data = std::fs::read(path).map_err(|e| { let data = std::fs::read(path)?;
error!("Could not open {path:?}: {e:?}");
e
})?;
let data = std::io::Cursor::new(data); let data = std::io::Cursor::new(data);
let decoder = png_pong::Decoder::new(data)?.into_steps(); let decoder = png_pong::Decoder::new(data)?.into_steps();
let png_pong::Step { raster, delay: _ } = decoder.last().ok_or(AnimeError::NoFrames)??; let png_pong::Step { raster, delay: _ } = decoder.last().ok_or(AnimeError::NoFrames)??;
@@ -466,7 +460,7 @@ impl AnimeImage {
width = ras.width(); width = ras.width();
Self::pixels_from_16bit(ras, false) Self::pixels_from_16bit(ras, false)
} }
png_pong::PngRaster::Palette(..) => return Err(AnimeError::Format) png_pong::PngRaster::Palette(..) => return Err(AnimeError::Format),
}; };
let mut matrix = AnimeImage::new( let mut matrix = AnimeImage::new(
@@ -476,7 +470,7 @@ impl AnimeImage {
bright, bright,
pixels, pixels,
width, width,
anime_type anime_type,
)?; )?;
matrix.update(); matrix.update();
@@ -485,7 +479,7 @@ impl AnimeImage {
fn pixels_from_8bit<P>(ras: &pix::Raster<P>, grey: bool) -> Vec<Pixel> fn pixels_from_8bit<P>(ras: &pix::Raster<P>, grey: bool) -> Vec<Pixel>
where where
P: pix::el::Pixel<Chan = pix::chan::Ch8> P: pix::el::Pixel<Chan = pix::chan::Ch8>,
{ {
ras.pixels() ras.pixels()
.iter() .iter()
@@ -497,14 +491,14 @@ impl AnimeImage {
+ (<u8>::from(px.two()) / 3) as u32 + (<u8>::from(px.two()) / 3) as u32
+ (<u8>::from(px.three()) / 3) as u32 + (<u8>::from(px.three()) / 3) as u32
}, },
alpha: <f32>::from(px.alpha()) alpha: <f32>::from(px.alpha()),
}) })
.collect() .collect()
} }
fn pixels_from_16bit<P>(ras: &pix::Raster<P>, grey: bool) -> Vec<Pixel> fn pixels_from_16bit<P>(ras: &pix::Raster<P>, grey: bool) -> Vec<Pixel>
where where
P: pix::el::Pixel<Chan = pix::chan::Ch16> P: pix::el::Pixel<Chan = pix::chan::Ch16>,
{ {
ras.pixels() ras.pixels()
.iter() .iter()
@@ -516,7 +510,7 @@ impl AnimeImage {
+ ((<u16>::from(px.two()) / 3) >> 8) as u32 + ((<u16>::from(px.two()) / 3) >> 8) as u32
+ ((<u16>::from(px.three()) / 3) >> 8) as u32 + ((<u16>::from(px.three()) / 3) >> 8) as u32
}, },
alpha: <f32>::from(px.alpha()) alpha: <f32>::from(px.alpha()),
}) })
.collect() .collect()
} }
@@ -653,7 +647,7 @@ mod tests {
Vec2::default(), Vec2::default(),
AnimTime::Infinite, AnimTime::Infinite,
1.0, 1.0,
AnimeType::GA402 AnimeType::GA402,
) )
.unwrap(); .unwrap();
matrix.frames()[0].frame(); matrix.frames()[0].frame();
+51 -22
View File
@@ -3,7 +3,7 @@ use std::path::PathBuf;
use std::time::Duration; use std::time::Duration;
use glam::Vec2; use glam::Vec2;
use serde::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use crate::error::Result; use crate::error::Result;
use crate::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType}; use crate::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType};
@@ -16,13 +16,13 @@ pub enum ActionLoader {
AsusAnimation { AsusAnimation {
file: PathBuf, file: PathBuf,
time: AnimTime, time: AnimTime,
brightness: f32 brightness: f32,
}, },
/// Image designed to be pixel perfect using the slanted template /// Image designed to be pixel perfect using the slanted template
AsusImage { AsusImage {
file: PathBuf, file: PathBuf,
time: AnimTime, time: AnimTime,
brightness: f32 brightness: f32,
}, },
/// Animated gif. If the file is a png a static gif is created using the /// Animated gif. If the file is a png a static gif is created using the
/// `time` properties /// `time` properties
@@ -32,7 +32,7 @@ pub enum ActionLoader {
angle: f32, angle: f32,
translation: Vec2, translation: Vec2,
time: AnimTime, time: AnimTime,
brightness: f32 brightness: f32,
}, },
Image { Image {
file: PathBuf, file: PathBuf,
@@ -40,10 +40,10 @@ pub enum ActionLoader {
angle: f32, angle: f32,
translation: Vec2, translation: Vec2,
time: AnimTime, time: AnimTime,
brightness: f32 brightness: f32,
}, },
/// A pause to be used between sequences /// A pause to be used between sequences
Pause(Duration) Pause(Duration),
} }
/// All the possible `AniMe` actions that can be used. The enum is intended to /// All the possible `AniMe` actions that can be used. The enum is intended to
@@ -64,7 +64,7 @@ pub enum ActionData {
/// Placeholder /// Placeholder
TimeDate, TimeDate,
/// Placeholder /// Placeholder
Matrix Matrix,
} }
impl ActionData { impl ActionData {
@@ -73,14 +73,17 @@ impl ActionData {
ActionLoader::AsusAnimation { ActionLoader::AsusAnimation {
file, file,
time, time,
brightness brightness,
} => ActionData::Animation(AnimeGif::from_diagonal_gif( } => ActionData::Animation(AnimeGif::from_diagonal_gif(
file, *time, *brightness, anime_type file,
*time,
*brightness,
anime_type,
)?), )?),
ActionLoader::AsusImage { ActionLoader::AsusImage {
file, file,
time, time,
brightness brightness,
} => match time { } => match time {
AnimTime::Infinite => { AnimTime::Infinite => {
let image = AnimeDiagonal::from_png(file, None, *brightness, anime_type)?; let image = AnimeDiagonal::from_png(file, None, *brightness, anime_type)?;
@@ -88,8 +91,11 @@ impl ActionData {
ActionData::Image(Box::new(data)) ActionData::Image(Box::new(data))
} }
_ => ActionData::Animation(AnimeGif::from_diagonal_png( _ => ActionData::Animation(AnimeGif::from_diagonal_png(
file, anime_type, *time, *brightness file,
)?) anime_type,
*time,
*brightness,
)?),
}, },
ActionLoader::ImageAnimation { ActionLoader::ImageAnimation {
file, file,
@@ -97,17 +103,29 @@ impl ActionData {
angle, angle,
translation, translation,
time, time,
brightness brightness,
} => { } => {
if let Some(ext) = file.extension() { if let Some(ext) = file.extension() {
if ext.to_string_lossy().to_lowercase() == "png" { if ext.to_string_lossy().to_lowercase() == "png" {
return Ok(ActionData::Animation(AnimeGif::from_png( return Ok(ActionData::Animation(AnimeGif::from_png(
file, *scale, *angle, *translation, *time, *brightness, anime_type file,
*scale,
*angle,
*translation,
*time,
*brightness,
anime_type,
)?)); )?));
} }
} }
ActionData::Animation(AnimeGif::from_gif( ActionData::Animation(AnimeGif::from_gif(
file, *scale, *angle, *translation, *time, *brightness, anime_type file,
*scale,
*angle,
*translation,
*time,
*brightness,
anime_type,
)?) )?)
} }
ActionLoader::Image { ActionLoader::Image {
@@ -116,23 +134,34 @@ impl ActionData {
angle, angle,
translation, translation,
brightness, brightness,
time time,
} => { } => {
match time { match time {
AnimTime::Infinite => { AnimTime::Infinite => {
// If no time then create a plain static image // If no time then create a plain static image
let image = AnimeImage::from_png( let image = AnimeImage::from_png(
file, *scale, *angle, *translation, *brightness, anime_type file,
*scale,
*angle,
*translation,
*brightness,
anime_type,
)?; )?;
let data = <AnimeDataBuffer>::try_from(&image)?; let data = <AnimeDataBuffer>::try_from(&image)?;
ActionData::Image(Box::new(data)) ActionData::Image(Box::new(data))
} }
_ => ActionData::Animation(AnimeGif::from_png( _ => ActionData::Animation(AnimeGif::from_png(
file, *scale, *angle, *translation, *time, *brightness, anime_type file,
)?) *scale,
*angle,
*translation,
*time,
*brightness,
anime_type,
)?),
} }
} }
ActionLoader::Pause(duration) => ActionData::Pause(*duration) ActionLoader::Pause(duration) => ActionData::Pause(*duration),
}; };
Ok(a) Ok(a)
} }
@@ -171,7 +200,7 @@ impl Sequences {
pub fn iter(&self) -> ActionIterator<'_> { pub fn iter(&self) -> ActionIterator<'_> {
ActionIterator { ActionIterator {
actions: self, actions: self,
next_idx: 0 next_idx: 0,
} }
} }
} }
@@ -179,7 +208,7 @@ impl Sequences {
/// Iteractor helper for iterating over all the actions in `Sequences` /// Iteractor helper for iterating over all the actions in `Sequences`
pub struct ActionIterator<'a> { pub struct ActionIterator<'a> {
actions: &'a Sequences, actions: &'a Sequences,
next_idx: usize next_idx: usize,
} }
impl<'a> Iterator for ActionIterator<'a> { impl<'a> Iterator for ActionIterator<'a> {
+34 -28
View File
@@ -11,7 +11,8 @@
use std::str::FromStr; use std::str::FromStr;
use dmi_id::DMIID; use dmi_id::DMIID;
use serde::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")] #[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value}; use zbus::zvariant::{OwnedValue, Type, Value};
@@ -28,6 +29,7 @@ pub const PROD_ID: u16 = 0x193b;
derive(Type, Value, OwnedValue), derive(Type, Value, OwnedValue),
zvariant(signature = "u") zvariant(signature = "u")
)] )]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] #[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
/// Base LED brightness of the display /// Base LED brightness of the display
pub enum Brightness { pub enum Brightness {
@@ -35,7 +37,7 @@ pub enum Brightness {
Low = 1, Low = 1,
#[default] #[default]
Med = 2, Med = 2,
High = 3 High = 3,
} }
impl FromStr for Brightness { impl FromStr for Brightness {
@@ -47,7 +49,7 @@ impl FromStr for Brightness {
"Low" | "low" => Brightness::Low, "Low" | "low" => Brightness::Low,
"Med" | "med" => Brightness::Med, "Med" | "med" => Brightness::Med,
"High" | "high" => Brightness::High, "High" | "high" => Brightness::High,
_ => Brightness::Med _ => Brightness::Med,
}) })
} }
} }
@@ -58,7 +60,7 @@ impl From<u8> for Brightness {
0 => Brightness::Off, 0 => Brightness::Off,
1 => Brightness::Low, 1 => Brightness::Low,
3 => Brightness::High, 3 => Brightness::High,
_ => Brightness::Med _ => Brightness::Med,
} }
} }
} }
@@ -80,11 +82,12 @@ impl From<Brightness> for i32 {
derive(Type, Value, OwnedValue), derive(Type, Value, OwnedValue),
zvariant(signature = "s") zvariant(signature = "s")
)] )]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] #[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub enum AnimBooting { pub enum AnimBooting {
#[default] #[default]
GlitchConstruction = 0, GlitchConstruction = 0,
StaticEmergence = 1 StaticEmergence = 1,
} }
impl FromStr for AnimBooting { impl FromStr for AnimBooting {
@@ -94,7 +97,7 @@ impl FromStr for AnimBooting {
match s { match s {
"GlitchConstruction" => Ok(Self::GlitchConstruction), "GlitchConstruction" => Ok(Self::GlitchConstruction),
"StaticEmergence" => Ok(Self::StaticEmergence), "StaticEmergence" => Ok(Self::StaticEmergence),
_ => Err(AnimeError::ParseError(s.to_owned())) _ => Err(AnimeError::ParseError(s.to_owned())),
} }
} }
} }
@@ -104,7 +107,7 @@ impl From<i32> for AnimBooting {
match value { match value {
0 => Self::GlitchConstruction, 0 => Self::GlitchConstruction,
1 => Self::StaticEmergence, 1 => Self::StaticEmergence,
_ => Self::default() _ => Self::default(),
} }
} }
} }
@@ -120,11 +123,12 @@ impl From<AnimBooting> for i32 {
derive(Type, Value, OwnedValue), derive(Type, Value, OwnedValue),
zvariant(signature = "s") zvariant(signature = "s")
)] )]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] #[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub enum AnimAwake { pub enum AnimAwake {
#[default] #[default]
BinaryBannerScroll = 0, BinaryBannerScroll = 0,
RogLogoGlitch = 1 RogLogoGlitch = 1,
} }
impl FromStr for AnimAwake { impl FromStr for AnimAwake {
@@ -134,7 +138,7 @@ impl FromStr for AnimAwake {
match s { match s {
"BinaryBannerScroll" => Ok(Self::BinaryBannerScroll), "BinaryBannerScroll" => Ok(Self::BinaryBannerScroll),
"RogLogoGlitch" => Ok(Self::RogLogoGlitch), "RogLogoGlitch" => Ok(Self::RogLogoGlitch),
_ => Err(AnimeError::ParseError(s.to_owned())) _ => Err(AnimeError::ParseError(s.to_owned())),
} }
} }
} }
@@ -144,7 +148,7 @@ impl From<i32> for AnimAwake {
match value { match value {
0 => Self::BinaryBannerScroll, 0 => Self::BinaryBannerScroll,
1 => Self::RogLogoGlitch, 1 => Self::RogLogoGlitch,
_ => Self::default() _ => Self::default(),
} }
} }
} }
@@ -160,11 +164,12 @@ impl From<AnimAwake> for i32 {
derive(Type, Value, OwnedValue), derive(Type, Value, OwnedValue),
zvariant(signature = "s") zvariant(signature = "s")
)] )]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] #[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub enum AnimSleeping { pub enum AnimSleeping {
#[default] #[default]
BannerSwipe = 0, BannerSwipe = 0,
Starfield = 1 Starfield = 1,
} }
impl FromStr for AnimSleeping { impl FromStr for AnimSleeping {
@@ -174,7 +179,7 @@ impl FromStr for AnimSleeping {
match s { match s {
"BannerSwipe" => Ok(Self::BannerSwipe), "BannerSwipe" => Ok(Self::BannerSwipe),
"Starfield" => Ok(Self::Starfield), "Starfield" => Ok(Self::Starfield),
_ => Err(AnimeError::ParseError(s.to_owned())) _ => Err(AnimeError::ParseError(s.to_owned())),
} }
} }
} }
@@ -184,7 +189,7 @@ impl From<i32> for AnimSleeping {
match value { match value {
0 => Self::BannerSwipe, 0 => Self::BannerSwipe,
1 => Self::Starfield, 1 => Self::Starfield,
_ => Self::default() _ => Self::default(),
} }
} }
} }
@@ -200,11 +205,12 @@ impl From<AnimSleeping> for i32 {
derive(Type, Value, OwnedValue), derive(Type, Value, OwnedValue),
zvariant(signature = "s") zvariant(signature = "s")
)] )]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] #[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub enum AnimShutdown { pub enum AnimShutdown {
#[default] #[default]
GlitchOut = 0, GlitchOut = 0,
SeeYa = 1 SeeYa = 1,
} }
impl FromStr for AnimShutdown { impl FromStr for AnimShutdown {
@@ -214,7 +220,7 @@ impl FromStr for AnimShutdown {
match s { match s {
"GlitchOut" => Ok(Self::GlitchOut), "GlitchOut" => Ok(Self::GlitchOut),
"SeeYa" => Ok(Self::SeeYa), "SeeYa" => Ok(Self::SeeYa),
_ => Err(AnimeError::ParseError(s.to_owned())) _ => Err(AnimeError::ParseError(s.to_owned())),
} }
} }
} }
@@ -224,7 +230,7 @@ impl From<i32> for AnimShutdown {
match value { match value {
0 => Self::GlitchOut, 0 => Self::GlitchOut,
1 => Self::SeeYa, 1 => Self::SeeYa,
_ => Self::default() _ => Self::default(),
} }
} }
} }
@@ -235,25 +241,25 @@ impl From<AnimShutdown> for i32 {
} }
} }
/// `get_maybe_anime_type` is very broad, matching on part of the laptop board /// `get_anime_type` is very broad, matching on part of the laptop board name
/// name only. For this reason `find_node()` must be used also to verify if the /// only. For this reason `find_node()` must be used also to verify if the USB
/// USB device is available. /// device is available.
/// ///
/// The currently known USB device is `19b6`. /// The currently known USB device is `19b6`.
#[inline] #[inline]
pub fn get_anime_type() -> AnimeType { pub fn get_anime_type() -> Result<AnimeType, AnimeError> {
let dmi = DMIID::new().unwrap_or_default(); let dmi = DMIID::new().map_err(|_| AnimeError::NoDevice)?; // TODO: better error
let board_name = dmi.board_name; let board_name = dmi.board_name;
if board_name.contains("GA401I") || board_name.contains("GA401Q") { if board_name.contains("GA401I") || board_name.contains("GA401Q") {
AnimeType::GA401 return Ok(AnimeType::GA401);
} else if board_name.contains("GA402R") || board_name.contains("GA402X") { } else if board_name.contains("GA402R") {
AnimeType::GA402 return Ok(AnimeType::GA402);
} else if board_name.contains("GU604V") { } else if board_name.contains("GU604V") {
AnimeType::GU604 return Ok(AnimeType::GU604);
} else {
AnimeType::Unsupported
} }
log::warn!("AniMe Matrix device found but not yet supported, will default to a GA402 layout");
Ok(AnimeType::Unknown)
} }
/// Get the two device initialization packets. These are required for device /// Get the two device initialization packets. These are required for device
@@ -326,7 +332,7 @@ pub const fn pkt_set_builtin_animations(
boot: AnimBooting, boot: AnimBooting,
awake: AnimAwake, awake: AnimAwake,
sleep: AnimSleeping, sleep: AnimSleeping,
shutdown: AnimShutdown shutdown: AnimShutdown,
) -> [u8; PACKET_SIZE] { ) -> [u8; PACKET_SIZE] {
let mut pkt = [0; PACKET_SIZE]; let mut pkt = [0; PACKET_SIZE];
pkt[0] = DEV_PAGE; pkt[0] = DEV_PAGE;
+5 -5
View File
@@ -52,7 +52,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt1_check = [ let pkt1_check = [
0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -100,7 +100,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let mut matrix = AnimeImage::new( let mut matrix = AnimeImage::new(
@@ -110,7 +110,7 @@ mod tests {
0.0, 0.0,
vec![Pixel::default(); 1000], vec![Pixel::default(); 1000],
100, 100,
AnimeType::GA401 AnimeType::GA401,
) )
.unwrap(); .unwrap();
matrix.edge_outline(); matrix.edge_outline();
@@ -175,7 +175,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt1_check = [ let pkt1_check = [
0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -223,7 +223,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+10 -10
View File
@@ -52,7 +52,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt1_check = [ let pkt1_check = [
0x5e, 0xc0, 0x2, 0x74, 0x2, 0x73, 0x2, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x2, 0x74, 0x2, 0x73, 0x2, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
@@ -100,7 +100,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt2_check = [ let pkt2_check = [
0x5e, 0xc0, 0x2, 0xe7, 0x4, 0x73, 0x2, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x2, 0xe7, 0x4, 0x73, 0x2, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00,
@@ -148,7 +148,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let mut matrix = AnimeImage::new( let mut matrix = AnimeImage::new(
@@ -158,7 +158,7 @@ mod tests {
0.0, 0.0,
vec![Pixel::default(); 1000], vec![Pixel::default(); 1000],
100, 100,
AnimeType::GA402 AnimeType::GA402,
) )
.unwrap(); .unwrap();
matrix.edge_outline(); matrix.edge_outline();
@@ -218,7 +218,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt1_check = [ let pkt1_check = [
0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00,
@@ -266,7 +266,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt2_check = [ let pkt2_check = [
0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
@@ -314,7 +314,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
@@ -378,7 +378,7 @@ mod tests {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt1_check = [ let pkt1_check = [
0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -426,7 +426,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt2_check = [ let pkt2_check = [
0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
@@ -474,7 +474,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+7 -7
View File
@@ -52,7 +52,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt1_check = [ let pkt1_check = [
0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -100,7 +100,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt2_check = [ let pkt2_check = [
0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -148,7 +148,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let mut matrix = AnimeImage::new( let mut matrix = AnimeImage::new(
@@ -158,7 +158,7 @@ mod tests {
0.0, 0.0,
vec![Pixel::default(); 1000], vec![Pixel::default(); 1000],
100, 100,
AnimeType::GU604 AnimeType::GU604,
) )
.unwrap(); .unwrap();
matrix.edge_outline(); matrix.edge_outline();
@@ -218,7 +218,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt1_check = [ let pkt1_check = [
0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -266,7 +266,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt2_check = [ let pkt2_check = [
0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -314,7 +314,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+5
View File
@@ -18,10 +18,15 @@ dbus = ["zbus"]
[dependencies] [dependencies]
serde.workspace = true serde.workspace = true
serde_derive.workspace = true
zbus = { workspace = true, optional = true } zbus = { workspace = true, optional = true }
dmi_id = { path = "../dmi-id" } dmi_id = { path = "../dmi-id" }
# cli and logging # cli and logging
log.workspace = true log.workspace = true
typeshare.workspace = true
ron = { version = "*", optional = true } ron = { version = "*", optional = true }
[dev-dependencies]
cargo-husky.workspace = true
+255 -237
View File
@@ -3,16 +3,7 @@
device_name: "FA506I", device_name: "FA506I",
product_id: "", product_id: "",
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "FA506N",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -21,7 +12,7 @@
device_name: "FA506Q", device_name: "FA506Q",
product_id: "", product_id: "",
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -30,34 +21,43 @@
device_name: "FA507", device_name: "FA507",
product_id: "", product_id: "",
layout_name: "fa507", layout_name: "fa507",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
device_name: "FA617NS", device_name: "FX505D",
product_id: "",
layout_name: "fa617ns",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "FX505",
product_id: "", product_id: "",
layout_name: "fx505d", layout_name: "fx505d",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
device_name: "FX506", device_name: "FX505G",
product_id: "",
layout_name: "fx505d",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "FX506H",
product_id: "", product_id: "",
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "FX506L",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -66,7 +66,7 @@
device_name: "FX507Z", device_name: "FX507Z",
product_id: "", product_id: "",
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -75,25 +75,7 @@
device_name: "FX516P", device_name: "FX516P",
product_id: "", product_id: "",
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Strobe],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "FX517Z",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "FX617X",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -102,79 +84,151 @@
device_name: "FX705D", device_name: "FX705D",
product_id: "", product_id: "",
layout_name: "fx505d", layout_name: "fx505d",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
device_name: "G512L", device_name: "G512",
product_id: "", product_id: "",
layout_name: "g512", layout_name: "g512",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard],
),
(
device_name: "G512LV",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
), ),
( (
device_name: "G513I", device_name: "G513I",
product_id: "", product_id: "",
layout_name: "g513i", layout_name: "g513i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]), advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]),
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
device_name: "G513Q", device_name: "G513QE",
product_id: "", product_id: "",
layout_name: "g513i", layout_name: "g513i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard],
),
(
device_name: "G513QM",
product_id: "",
layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
), ),
( (
device_name: "G513QR", device_name: "G513QR",
product_id: "", product_id: "",
layout_name: "g513i-per-key", layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "G513QY",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "G513RC",
product_id: "",
layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]),
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
device_name: "G513R", device_name: "G513RM",
product_id: "", product_id: "",
layout_name: "g513i-per-key", layout_name: "g513i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]),
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
device_name: "G513RW", device_name: "G513RW",
product_id: "", product_id: "",
layout_name: "g513i-per-key", layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard],
), ),
( (
device_name: "G513RC", device_name: "G531",
product_id: "", product_id: "",
layout_name: "g513i", layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [],
advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]), advanced_type: PerKey,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard],
), ),
( (
device_name: "G531G", device_name: "G531",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "G531GD",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "G531GT",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "G531GU",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "G531GV",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -183,7 +237,7 @@
device_name: "G531GW", device_name: "G531GW",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -192,34 +246,25 @@
device_name: "G532", device_name: "G532",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
device_name: "G533Q", device_name: "G533Q",
product_id: "1866", product_id: "",
layout_name: "g533q-per-key", layout_name: "g533q-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard],
),
(
device_name: "G533QS",
product_id: "18c6",
layout_name: "g533q-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
), ),
( (
device_name: "G533Z", device_name: "G533Z",
product_id: "", product_id: "",
layout_name: "g533q-per-key", layout_name: "g533q-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -228,34 +273,16 @@
device_name: "G614J", device_name: "G614J",
product_id: "", product_id: "",
layout_name: "g634j-per-key", layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Pulse, Strobe, Rainbow],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
(
device_name: "G614JJ",
product_id: "",
layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G614JZ",
product_id: "",
layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
),
( (
device_name: "G634J", device_name: "G634J",
product_id: "", product_id: "",
layout_name: "g634j-per-key", layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard, Lightbar, Logo, RearGlow], power_zones: [Keyboard, Lightbar, Logo, RearGlow],
@@ -264,7 +291,7 @@
device_name: "G712LI", device_name: "G712LI",
product_id: "", product_id: "",
layout_name: "gl503", layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -273,7 +300,7 @@
device_name: "G712LV", device_name: "G712LV",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -282,7 +309,7 @@
device_name: "G712LW", device_name: "G712LW",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -291,61 +318,43 @@
device_name: "G713IC", device_name: "G713IC",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [],
advanced_type: None, advanced_type: PerKey,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard],
), ),
( (
device_name: "G713P", device_name: "G713P",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard],
),
(
device_name: "G713QC",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G713QE",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
), ),
( (
device_name: "G713QM", device_name: "G713QM",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: PerKey, advanced_type: None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard],
), ),
( (
device_name: "G713QR", device_name: "G713QR",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard],
), ),
( (
device_name: "G713RC", device_name: "G713RC",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]), advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]),
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
@@ -354,7 +363,7 @@
device_name: "G713RM", device_name: "G713RM",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -363,7 +372,7 @@
device_name: "G713RS", device_name: "G713RS",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -372,25 +381,34 @@
device_name: "G713RW", device_name: "G713RW",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4, BarLeft, BarRight], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard],
), ),
( (
device_name: "G731", device_name: "G731",
product_id: "", product_id: "",
layout_name: "g533q", layout_name: "g533q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
device_name: "G731G", device_name: "G731GT",
product_id: "", product_id: "",
layout_name: "g533q", layout_name: "g533q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "G731GU",
product_id: "",
layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -399,7 +417,7 @@
device_name: "G731GV", device_name: "G731GV",
product_id: "", product_id: "",
layout_name: "g533q", layout_name: "g533q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -408,7 +426,7 @@
device_name: "G731GW", device_name: "G731GW",
product_id: "", product_id: "",
layout_name: "g533q", layout_name: "g533q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -417,16 +435,16 @@
device_name: "G733C", device_name: "G733C",
product_id: "", product_id: "",
layout_name: "g513i-per-key", layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [Logo, BarLeft, BarRight],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard, Lightbar, Logo, Lid], power_zones: [Keyboard],
), ),
( (
device_name: "G733P", device_name: "G733PZ",
product_id: "", product_id: "",
layout_name: "g733pz-per-key", layout_name: "g733pz-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
@@ -435,7 +453,7 @@
device_name: "G733Q", device_name: "G733Q",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -444,38 +462,38 @@
device_name: "G733Z", device_name: "G733Z",
product_id: "", product_id: "",
layout_name: "g513i-per-key", layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard, Lightbar, Logo, Lid], power_zones: [Keyboard],
), ),
( (
device_name: "G814J", device_name: "G814JI",
product_id: "", product_id: "",
layout_name: "g814ji-per-key", layout_name: "g814ji-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
device_name: "G834J", device_name: "G814JZ",
product_id: "", product_id: "",
layout_name: "g814ji-per-key", layout_name: "g814ji-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G834JZ",
product_id: "",
layout_name: "g814ji-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard, Lightbar, Logo, RearGlow], power_zones: [Keyboard, Lightbar, Logo, RearGlow],
), ),
(
device_name: "GA401I",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
( (
device_name: "GA401Q", device_name: "GA401Q",
product_id: "", product_id: "",
@@ -489,16 +507,7 @@
device_name: "GA402N", device_name: "GA402N",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Pulse, Rainbow, Strobe],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GA402NU-0002",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -507,7 +516,7 @@
device_name: "GA402R", device_name: "GA402R",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Pulse, Rainbow],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -516,13 +525,13 @@
device_name: "GA402X", device_name: "GA402X",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Pulse, Rainbow],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
device_name: "GA402XV-NC012", device_name: "GA402XV",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
@@ -531,7 +540,7 @@
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
device_name: "GA403U", device_name: "GA403UI",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
@@ -543,7 +552,7 @@
device_name: "GA503Q", device_name: "GA503Q",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Pulse, Rainbow, Strobe],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -561,25 +570,16 @@
device_name: "GA503R", device_name: "GA503R",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Pulse, Rainbow, Strobe],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
(
device_name: "GA605W",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
),
( (
device_name: "GL503", device_name: "GL503",
product_id: "", product_id: "",
layout_name: "gl503", layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -588,7 +588,7 @@
device_name: "GL503V", device_name: "GL503V",
product_id: "", product_id: "",
layout_name: "gl503", layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -597,7 +597,7 @@
device_name: "GL504G", device_name: "GL504G",
product_id: "", product_id: "",
layout_name: "gl503", layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4, Logo, BarLeft, BarRight], basic_zones: [Key1, Key2, Key3, Key4, Logo, BarLeft, BarRight],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -606,7 +606,7 @@
device_name: "GL531", device_name: "GL531",
product_id: "", product_id: "",
layout_name: "g512", layout_name: "g512",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -615,7 +615,7 @@
device_name: "GL553V", device_name: "GL553V",
product_id: "", product_id: "",
layout_name: "g533q", layout_name: "g533q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -624,7 +624,7 @@
device_name: "GL703G", device_name: "GL703G",
product_id: "", product_id: "",
layout_name: "gl503", layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -633,34 +633,61 @@
device_name: "GM501G", device_name: "GM501G",
product_id: "", product_id: "",
layout_name: "fa507", layout_name: "fa507",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
(
device_name: "GU502",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "GU502G",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
( (
device_name: "GU502L", device_name: "GU502L",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
(
device_name: "GU502LU",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
( (
device_name: "GU603H", device_name: "GU603H",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: Zoned([SingleZone]), advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
device_name: "GU603V", device_name: "GU603VV",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: Zoned([SingleZone]), advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -669,7 +696,7 @@
device_name: "GU603Z", device_name: "GU603Z",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: Zoned([SingleZone]), advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -678,7 +705,7 @@
device_name: "GU604V", device_name: "GU604V",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: Zoned([SingleZone]), advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -687,7 +714,7 @@
device_name: "GU605M", device_name: "GU605M",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: Zoned([SingleZone]), advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -723,7 +750,7 @@
device_name: "GV601R", device_name: "GV601R",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -741,7 +768,7 @@
device_name: "GV604V", device_name: "GV604V",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -750,7 +777,7 @@
device_name: "GX502", device_name: "GX502",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -759,7 +786,7 @@
device_name: "GX531", device_name: "GX531",
product_id: "", product_id: "",
layout_name: "gx531-per-key", layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -768,7 +795,7 @@
device_name: "GX550L", device_name: "GX550L",
product_id: "", product_id: "",
layout_name: "gx531-per-key", layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -777,7 +804,7 @@
device_name: "GX551Q", device_name: "GX551Q",
product_id: "", product_id: "",
layout_name: "gx531-per-key", layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -786,16 +813,7 @@
device_name: "GX650P", device_name: "GX650P",
product_id: "", product_id: "",
layout_name: "gx531-per-key", layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "GX650R",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -804,7 +822,7 @@
device_name: "GX701", device_name: "GX701",
product_id: "", product_id: "",
layout_name: "gx531-per-key", layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -813,7 +831,7 @@
device_name: "GX703H", device_name: "GX703H",
product_id: "", product_id: "",
layout_name: "gx531-per-key", layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -836,6 +854,15 @@
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
(
device_name: "GZ301Z",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
( (
device_name: "GZ301Z", device_name: "GZ301Z",
product_id: "18c6", product_id: "18c6",
@@ -858,18 +885,9 @@
device_name: "RC71L", device_name: "RC71L",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Ally], power_zones: [Keyboard],
),
(
device_name: "RC72L",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Ally],
), ),
]) ])
+14 -69
View File
@@ -1,8 +1,6 @@
use std::env;
use dmi_id::DMIID; use dmi_id::DMIID;
use log::{error, info, warn}; use log::{error, info, warn};
use serde::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use crate::keyboard::AdvancedAuraType; use crate::keyboard::AdvancedAuraType;
use crate::{AuraModeNum, AuraZone, PowerZones}; use crate::{AuraModeNum, AuraZone, PowerZones};
@@ -54,7 +52,7 @@ pub struct LedSupportData {
#[serde(default)] #[serde(default)]
pub advanced_type: AdvancedAuraType, pub advanced_type: AdvancedAuraType,
/// If empty will default to `Keyboard` power zone /// If empty will default to `Keyboard` power zone
pub power_zones: Vec<PowerZones> pub power_zones: Vec<PowerZones>,
} }
impl LedSupportData { impl LedSupportData {
@@ -62,20 +60,17 @@ impl LedSupportData {
/// matches against laptops first, then will proceed with matching the /// matches against laptops first, then will proceed with matching the
/// `device_name` if there are no DMI matches. /// `device_name` if there are no DMI matches.
pub fn get_data(product_id: &str) -> Self { pub fn get_data(product_id: &str) -> Self {
let mut dmi = DMIID::new().unwrap_or_default(); let dmi = DMIID::new().unwrap_or_default();
if let Ok(board_name) = env::var("BOARD_NAME") {
dmi.board_name = board_name;
}
// let prod_family = dmi.product_family().expect("Could not get // let prod_family = dmi.product_family().expect("Could not get
// product_family"); // product_family");
if let Some(data) = LedSupportFile::load_from_supoprt_db() { if let Some(data) = LedSupportFile::load_from_supoprt_db() {
return data.match_device(&dmi.board_name, product_id); if let Some(data) = data.match_device(&dmi.board_name, product_id) {
return data;
}
} }
info!("Using generic LED control for keyboard brightness only. No aura_support file found"); info!("Using generic LED control for keyboard brightness only");
let mut data = LedSupportData::default(); LedSupportData::default()
data.power_zones.push(PowerZones::Keyboard);
data
} }
} }
@@ -89,7 +84,7 @@ impl LedSupportFile {
/// The list is stored in ordered format, so the iterator must be reversed /// The list is stored in ordered format, so the iterator must be reversed
/// to ensure we match to *whole names* first before doing a glob match /// to ensure we match to *whole names* first before doing a glob match
fn match_device(&self, device_name: &str, product_id: &str) -> LedSupportData { fn match_device(&self, device_name: &str, product_id: &str) -> Option<LedSupportData> {
for config in self.0.iter().rev() { for config in self.0.iter().rev() {
if device_name.contains(&config.device_name) { if device_name.contains(&config.device_name) {
info!("Matched to {}", config.device_name); info!("Matched to {}", config.device_name);
@@ -97,27 +92,15 @@ impl LedSupportFile {
info!("Checking product ID"); info!("Checking product ID");
if config.product_id == product_id { if config.product_id == product_id {
info!("Matched to {}", config.product_id); info!("Matched to {}", config.product_id);
return config.clone(); return Some(config.clone());
} else { } else {
continue; continue;
} }
} }
return config.clone(); return Some(config.clone());
} }
} }
warn!( None
"the aura_support.ron file has no entry for this model: {device_name}, {product_id}. \
Using a default"
);
LedSupportData {
device_name: device_name.to_owned(),
product_id: product_id.to_owned(),
layout_name: "Default".to_owned(),
basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![],
advanced_type: AdvancedAuraType::None,
power_zones: vec![PowerZones::Keyboard]
}
} }
/// Load `LedSupportFile` from the `aura_support.ron` file at /// Load `LedSupportFile` from the `aura_support.ron` file at
@@ -171,7 +154,6 @@ impl LedSupportFile {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::collections::HashMap;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io::Write; use std::io::Write;
use std::path::PathBuf; use std::path::PathBuf;
@@ -191,16 +173,9 @@ mod tests {
product_id: String::new(), product_id: String::new(),
layout_name: "ga401".to_owned(), layout_name: "ga401".to_owned(),
basic_modes: vec![AuraModeNum::Static], basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![ basic_zones: vec![AuraZone::Key1, AuraZone::Logo, AuraZone::BarLeft],
AuraZone::Key1,
AuraZone::Logo,
AuraZone::BarLeft,
],
advanced_type: AdvancedAuraType::Zoned(vec![LedCode::LightbarRight]), advanced_type: AdvancedAuraType::Zoned(vec![LedCode::LightbarRight]),
power_zones: vec![ power_zones: vec![PowerZones::Keyboard, PowerZones::RearGlow],
PowerZones::Keyboard,
PowerZones::RearGlow,
]
}; };
assert!(ron::to_string(&led).is_ok()); assert!(ron::to_string(&led).is_ok());
@@ -220,9 +195,6 @@ mod tests {
let mut tmp_sort = tmp.clone(); let mut tmp_sort = tmp.clone();
tmp_sort.0.sort_by(|a, b| a.product_id.cmp(&b.product_id)); tmp_sort.0.sort_by(|a, b| a.product_id.cmp(&b.product_id));
tmp_sort.0.sort_by(|a, b| a.device_name.cmp(&b.device_name)); tmp_sort.0.sort_by(|a, b| a.device_name.cmp(&b.device_name));
for model in tmp_sort.0.iter_mut() {
model.basic_modes.sort_by_key(|a| *a as u8);
}
if tmp != tmp_sort { if tmp != tmp_sort {
let sorted = let sorted =
ron::ser::to_string_pretty(&tmp_sort, PrettyConfig::new().depth_limit(2)).unwrap(); ron::ser::to_string_pretty(&tmp_sort, PrettyConfig::new().depth_limit(2)).unwrap();
@@ -245,31 +217,4 @@ mod tests {
ron::ser::to_string_pretty(&tmp, my_config).unwrap() ron::ser::to_string_pretty(&tmp, my_config).unwrap()
); );
} }
#[test]
fn find_data_file_groups() {
let mut data = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
data.push("data/aura_support.ron");
let buf = std::fs::read_to_string(&data).unwrap();
let tmp = ron::from_str::<LedSupportFile>(&buf).unwrap();
let mut modes: HashMap<Vec<AuraModeNum>, Vec<String>> = HashMap::new();
for entry in tmp.0 {
if let Some(modes) = modes.get_mut(&entry.basic_modes) {
modes.push(entry.device_name);
} else {
modes.insert(entry.basic_modes, vec![entry.device_name]);
}
}
dbg!(modes);
// let my_config = PrettyConfig::new().depth_limit(2);
// println!(
// "RON: {}",
// ron::ser::to_string_pretty(&tmp, my_config).unwrap()
// );
}
} }
+129 -103
View File
@@ -1,13 +1,15 @@
use std::fmt::Display; use std::fmt::Display;
use std::str::FromStr; use std::str::FromStr;
use serde::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")] #[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value}; use zbus::zvariant::{OwnedValue, Type, Value};
use crate::error::Error; use crate::error::Error;
use crate::AURA_LAPTOP_LED_MSG_LEN; use crate::LED_MSG_LEN;
#[typeshare]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)] #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[cfg_attr( #[cfg_attr(
feature = "dbus", feature = "dbus",
@@ -19,7 +21,7 @@ pub enum LedBrightness {
Low = 1, Low = 1,
#[default] #[default]
Med = 2, Med = 2,
High = 3 High = 3,
} }
impl LedBrightness { impl LedBrightness {
@@ -28,7 +30,7 @@ impl LedBrightness {
Self::Off => Self::Low, Self::Off => Self::Low,
Self::Low => Self::Med, Self::Low => Self::Med,
Self::Med => Self::High, Self::Med => Self::High,
Self::High => Self::Off Self::High => Self::Off,
} }
} }
@@ -37,7 +39,7 @@ impl LedBrightness {
Self::Off => Self::High, Self::Off => Self::High,
Self::Low => Self::Off, Self::Low => Self::Off,
Self::Med => Self::Low, Self::Med => Self::Low,
Self::High => Self::Med Self::High => Self::Med,
} }
} }
} }
@@ -48,7 +50,7 @@ impl From<u8> for LedBrightness {
0 => LedBrightness::Off, 0 => LedBrightness::Off,
1 => LedBrightness::Low, 1 => LedBrightness::Low,
3 => LedBrightness::High, 3 => LedBrightness::High,
_ => LedBrightness::Med _ => LedBrightness::Med,
} }
} }
} }
@@ -72,17 +74,18 @@ impl From<i32> for LedBrightness {
1 => LedBrightness::Low, 1 => LedBrightness::Low,
2 => LedBrightness::Med, 2 => LedBrightness::Med,
3 => LedBrightness::High, 3 => LedBrightness::High,
_ => LedBrightness::Med _ => LedBrightness::Med,
} }
} }
} }
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))] #[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Debug, Clone, PartialEq, Eq, Copy, Deserialize, Serialize)] #[derive(Debug, Clone, PartialEq, Eq, Copy, Deserialize, Serialize)]
pub struct Colour { pub struct Colour {
pub r: u8, pub r: u8,
pub g: u8, pub g: u8,
pub b: u8 pub b: u8,
} }
impl Default for Colour { impl Default for Colour {
@@ -110,18 +113,14 @@ impl From<&[f32; 3]> for Colour {
Self { Self {
r: (255.0 * c[0]) as u8, r: (255.0 * c[0]) as u8,
g: (255.0 * c[1]) as u8, g: (255.0 * c[1]) as u8,
b: (255.0 * c[2]) as u8 b: (255.0 * c[2]) as u8,
} }
} }
} }
impl From<Colour> for [f32; 3] { impl From<Colour> for [f32; 3] {
fn from(c: Colour) -> Self { fn from(c: Colour) -> Self {
[ [c.r as f32 / 255.0, c.g as f32 / 255.0, c.b as f32 / 255.0]
c.r as f32 / 255.0,
c.g as f32 / 255.0,
c.b as f32 / 255.0
]
} }
} }
@@ -130,19 +129,18 @@ impl From<&[u8; 3]> for Colour {
Self { Self {
r: c[0], r: c[0],
g: c[1], g: c[1],
b: c[2] b: c[2],
} }
} }
} }
impl From<Colour> for [u8; 3] { impl From<Colour> for [u8; 3] {
fn from(c: Colour) -> Self { fn from(c: Colour) -> Self {
[ [c.r, c.g, c.b]
c.r, c.g, c.b
]
} }
} }
#[typeshare]
#[cfg_attr( #[cfg_attr(
feature = "dbus", feature = "dbus",
derive(Type, Value, OwnedValue), derive(Type, Value, OwnedValue),
@@ -153,7 +151,7 @@ pub enum Speed {
Low = 0xe1, Low = 0xe1,
#[default] #[default]
Med = 0xeb, Med = 0xeb,
High = 0xf5 High = 0xf5,
} }
impl FromStr for Speed { impl FromStr for Speed {
@@ -165,7 +163,7 @@ impl FromStr for Speed {
"low" => Ok(Speed::Low), "low" => Ok(Speed::Low),
"med" => Ok(Speed::Med), "med" => Ok(Speed::Med),
"high" => Ok(Speed::High), "high" => Ok(Speed::High),
_ => Err(Error::ParseSpeed) _ => Err(Error::ParseSpeed),
} }
} }
} }
@@ -175,7 +173,7 @@ impl From<i32> for Speed {
match value { match value {
0 => Self::Low, 0 => Self::Low,
2 => Self::High, 2 => Self::High,
_ => Self::Med _ => Self::Med,
} }
} }
} }
@@ -185,7 +183,7 @@ impl From<Speed> for i32 {
match value { match value {
Speed::Low => 0, Speed::Low => 0,
Speed::Med => 1, Speed::Med => 1,
Speed::High => 2 Speed::High => 2,
} }
} }
} }
@@ -195,13 +193,14 @@ impl From<Speed> for u8 {
match s { match s {
Speed::Low => 0, Speed::Low => 0,
Speed::Med => 1, Speed::Med => 1,
Speed::High => 2 Speed::High => 2,
} }
} }
} }
/// Used for Rainbow mode. /// Used for Rainbow mode.
/// ///
/// Enum corresponds to the required integer value /// Enum corresponds to the required integer value
#[typeshare]
#[cfg_attr( #[cfg_attr(
feature = "dbus", feature = "dbus",
derive(Type, Value, OwnedValue), derive(Type, Value, OwnedValue),
@@ -213,7 +212,7 @@ pub enum Direction {
Right = 0, Right = 0,
Left = 1, Left = 1,
Up = 2, Up = 2,
Down = 3 Down = 3,
} }
impl FromStr for Direction { impl FromStr for Direction {
@@ -226,7 +225,7 @@ impl FromStr for Direction {
"up" => Ok(Direction::Up), "up" => Ok(Direction::Up),
"down" => Ok(Direction::Down), "down" => Ok(Direction::Down),
"left" => Ok(Direction::Left), "left" => Ok(Direction::Left),
_ => Err(Error::ParseDirection) _ => Err(Error::ParseDirection),
} }
} }
} }
@@ -237,7 +236,7 @@ impl From<i32> for Direction {
1 => Self::Left, 1 => Self::Left,
2 => Self::Up, 2 => Self::Up,
3 => Self::Down, 3 => Self::Down,
_ => Self::Right _ => Self::Right,
} }
} }
} }
@@ -249,6 +248,7 @@ impl From<Direction> for i32 {
} }
/// Enum of modes that convert to the actual number required by a USB HID packet /// Enum of modes that convert to the actual number required by a USB HID packet
#[typeshare]
#[cfg_attr( #[cfg_attr(
feature = "dbus", feature = "dbus",
derive(Type, Value, OwnedValue), derive(Type, Value, OwnedValue),
@@ -261,8 +261,8 @@ pub enum AuraModeNum {
#[default] #[default]
Static = 0, Static = 0,
Breathe = 1, Breathe = 1,
RainbowCycle = 2, Strobe = 2,
RainbowWave = 3, Rainbow = 3,
Star = 4, Star = 4,
Rain = 5, Rain = 5,
Highlight = 6, Highlight = 6,
@@ -270,7 +270,7 @@ pub enum AuraModeNum {
Ripple = 8, Ripple = 8,
Pulse = 10, Pulse = 10,
Comet = 11, Comet = 11,
Flash = 12 Flash = 12,
} }
impl Display for AuraModeNum { impl Display for AuraModeNum {
@@ -290,8 +290,8 @@ impl From<&AuraModeNum> for &str {
match mode { match mode {
AuraModeNum::Static => "Static", AuraModeNum::Static => "Static",
AuraModeNum::Breathe => "Breathe", AuraModeNum::Breathe => "Breathe",
AuraModeNum::RainbowCycle => "RainbowCycle", AuraModeNum::Strobe => "Strobe",
AuraModeNum::RainbowWave => "RainbowWave", AuraModeNum::Rainbow => "Rainbow",
AuraModeNum::Star => "Stars", AuraModeNum::Star => "Stars",
AuraModeNum::Rain => "Rain", AuraModeNum::Rain => "Rain",
AuraModeNum::Highlight => "Highlight", AuraModeNum::Highlight => "Highlight",
@@ -299,7 +299,7 @@ impl From<&AuraModeNum> for &str {
AuraModeNum::Ripple => "Ripple", AuraModeNum::Ripple => "Ripple",
AuraModeNum::Pulse => "Pulse", AuraModeNum::Pulse => "Pulse",
AuraModeNum::Comet => "Comet", AuraModeNum::Comet => "Comet",
AuraModeNum::Flash => "Flash" AuraModeNum::Flash => "Flash",
} }
} }
} }
@@ -307,8 +307,8 @@ impl From<&str> for AuraModeNum {
fn from(mode: &str) -> Self { fn from(mode: &str) -> Self {
match mode { match mode {
"Breathe" => AuraModeNum::Breathe, "Breathe" => AuraModeNum::Breathe,
"RainbowCycle" => AuraModeNum::RainbowCycle, "Strobe" => AuraModeNum::Strobe,
"RainbowWave" => AuraModeNum::RainbowWave, "Rainbow" => AuraModeNum::Rainbow,
"Stars" => AuraModeNum::Star, "Stars" => AuraModeNum::Star,
"Rain" => AuraModeNum::Rain, "Rain" => AuraModeNum::Rain,
"Highlight" => AuraModeNum::Highlight, "Highlight" => AuraModeNum::Highlight,
@@ -317,7 +317,7 @@ impl From<&str> for AuraModeNum {
"Pulse" => AuraModeNum::Pulse, "Pulse" => AuraModeNum::Pulse,
"Comet" => AuraModeNum::Comet, "Comet" => AuraModeNum::Comet,
"Flash" => AuraModeNum::Flash, "Flash" => AuraModeNum::Flash,
_ => AuraModeNum::Static _ => AuraModeNum::Static,
} }
} }
} }
@@ -326,8 +326,8 @@ impl From<u8> for AuraModeNum {
fn from(mode: u8) -> Self { fn from(mode: u8) -> Self {
match mode { match mode {
1 => AuraModeNum::Breathe, 1 => AuraModeNum::Breathe,
2 => AuraModeNum::RainbowCycle, 2 => AuraModeNum::Strobe,
3 => AuraModeNum::RainbowWave, 3 => AuraModeNum::Rainbow,
4 => AuraModeNum::Star, 4 => AuraModeNum::Star,
5 => AuraModeNum::Rain, 5 => AuraModeNum::Rain,
6 => AuraModeNum::Highlight, 6 => AuraModeNum::Highlight,
@@ -336,7 +336,7 @@ impl From<u8> for AuraModeNum {
10 => AuraModeNum::Pulse, 10 => AuraModeNum::Pulse,
11 => AuraModeNum::Comet, 11 => AuraModeNum::Comet,
12 => AuraModeNum::Flash, 12 => AuraModeNum::Flash,
_ => AuraModeNum::Static _ => AuraModeNum::Static,
} }
} }
} }
@@ -360,6 +360,7 @@ impl From<AuraEffect> for AuraModeNum {
} }
/// Base effects have no zoning, while multizone is 1-4 /// Base effects have no zoning, while multizone is 1-4
#[typeshare]
#[cfg_attr( #[cfg_attr(
feature = "dbus", feature = "dbus",
derive(Type, Value, OwnedValue), derive(Type, Value, OwnedValue),
@@ -383,7 +384,7 @@ pub enum AuraZone {
/// The left part of a lightbar (typically on the front of laptop) /// The left part of a lightbar (typically on the front of laptop)
BarLeft = 6, BarLeft = 6,
/// The right part of a lightbar /// The right part of a lightbar
BarRight = 7 BarRight = 7,
} }
impl FromStr for AuraZone { impl FromStr for AuraZone {
@@ -400,7 +401,7 @@ impl FromStr for AuraZone {
"5" | "logo" => Ok(AuraZone::Logo), "5" | "logo" => Ok(AuraZone::Logo),
"6" | "lightbar-left" => Ok(AuraZone::BarLeft), "6" | "lightbar-left" => Ok(AuraZone::BarLeft),
"7" | "lightbar-right" => Ok(AuraZone::BarRight), "7" | "lightbar-right" => Ok(AuraZone::BarRight),
_ => Err(Error::ParseSpeed) _ => Err(Error::ParseSpeed),
} }
} }
} }
@@ -415,7 +416,7 @@ impl From<i32> for AuraZone {
5 => Self::Logo, 5 => Self::Logo,
6 => Self::BarLeft, 6 => Self::BarLeft,
7 => Self::BarRight, 7 => Self::BarRight,
_ => Self::default() _ => Self::default(),
} }
} }
} }
@@ -431,8 +432,9 @@ impl From<AuraZone> for i32 {
/// ```rust /// ```rust
/// // let bytes: [u8; LED_MSG_LEN] = mode.into(); /// // let bytes: [u8; LED_MSG_LEN] = mode.into();
/// ``` /// ```
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))] #[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AuraEffect { pub struct AuraEffect {
/// The effect type /// The effect type
pub mode: AuraModeNum, pub mode: AuraModeNum,
@@ -445,7 +447,7 @@ pub struct AuraEffect {
/// One of three speeds for modes that support speed (most that animate) /// One of three speeds for modes that support speed (most that animate)
pub speed: Speed, pub speed: Speed,
/// Up, down, left, right. Only Rainbow mode seems to use this /// Up, down, left, right. Only Rainbow mode seems to use this
pub direction: Direction pub direction: Direction,
} }
impl AuraEffect { impl AuraEffect {
@@ -481,7 +483,7 @@ impl Default for AuraEffect {
colour1: Colour { r: 166, g: 0, b: 0 }, colour1: Colour { r: 166, g: 0, b: 0 },
colour2: Colour { r: 0, g: 0, b: 0 }, colour2: Colour { r: 0, g: 0, b: 0 },
speed: Speed::Med, speed: Speed::Med,
direction: Direction::Right direction: Direction::Right,
} }
} }
} }
@@ -492,6 +494,56 @@ impl Display for AuraEffect {
} }
} }
pub struct AuraParameters {
pub zone: bool,
pub colour1: bool,
pub colour2: bool,
pub speed: bool,
pub direction: bool,
}
#[allow(clippy::fn_params_excessive_bools)]
impl AuraParameters {
pub const fn new(
zone: bool,
colour1: bool,
colour2: bool,
speed: bool,
direction: bool,
) -> Self {
Self {
zone,
colour1,
colour2,
speed,
direction,
}
}
}
impl AuraEffect {
/// A helper to provide detail on what effects have which parameters, e.g
/// the static factory mode accepts only one colour.
pub const fn allowed_parameters(mode: AuraModeNum) -> AuraParameters {
match mode {
AuraModeNum::Static
| AuraModeNum::Highlight
| AuraModeNum::Pulse
| AuraModeNum::Comet
| AuraModeNum::Flash => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Breathe => AuraParameters::new(true, true, true, true, false),
AuraModeNum::Strobe | AuraModeNum::Rain => {
AuraParameters::new(true, false, false, true, false)
}
AuraModeNum::Rainbow => AuraParameters::new(true, false, false, true, true),
AuraModeNum::Star => AuraParameters::new(true, true, true, true, true),
AuraModeNum::Laser | AuraModeNum::Ripple => {
AuraParameters::new(true, true, false, true, false)
}
}
}
}
/// Parses `AuraEffect` in to packet data for writing to the USB interface /// Parses `AuraEffect` in to packet data for writing to the USB interface
/// ///
/// Byte structure where colour is RGB, one byte per R, G, B: /// Byte structure where colour is RGB, one byte per R, G, B:
@@ -500,9 +552,9 @@ impl Display for AuraEffect {
/// |---|---|-----|-----|---------|------|----------|---|-----------| /// |---|---|-----|-----|---------|------|----------|---|-----------|
/// |5d |b3 |Zone |Mode |Colour 1 |Speed |Direction |00 |Colour 2 | /// |5d |b3 |Zone |Mode |Colour 1 |Speed |Direction |00 |Colour 2 |
/// ``` /// ```
impl From<&AuraEffect> for [u8; AURA_LAPTOP_LED_MSG_LEN] { impl From<&AuraEffect> for [u8; LED_MSG_LEN] {
fn from(aura: &AuraEffect) -> Self { fn from(aura: &AuraEffect) -> Self {
let mut msg = [0u8; AURA_LAPTOP_LED_MSG_LEN]; let mut msg = [0u8; LED_MSG_LEN];
msg[0] = 0x5d; msg[0] = 0x5d;
msg[1] = 0xb3; msg[1] = 0xb3;
msg[2] = aura.zone as u8; msg[2] = aura.zone as u8;
@@ -521,7 +573,7 @@ impl From<&AuraEffect> for [u8; AURA_LAPTOP_LED_MSG_LEN] {
impl From<&AuraEffect> for Vec<u8> { impl From<&AuraEffect> for Vec<u8> {
fn from(aura: &AuraEffect) -> Self { fn from(aura: &AuraEffect) -> Self {
let mut msg = vec![0u8; AURA_LAPTOP_LED_MSG_LEN]; let mut msg = vec![0u8; LED_MSG_LEN];
msg[0] = 0x5d; msg[0] = 0x5d;
msg[1] = 0xb3; msg[1] = 0xb3;
msg[2] = aura.zone as u8; msg[2] = aura.zone as u8;
@@ -540,9 +592,7 @@ impl From<&AuraEffect> for Vec<u8> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed, LED_MSG_LEN};
AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed, AURA_LAPTOP_LED_MSG_LEN
};
#[test] #[test]
fn check_led_static_packet() { fn check_led_static_packet() {
@@ -552,18 +602,18 @@ mod tests {
colour1: Colour { colour1: Colour {
r: 0xff, r: 0xff,
g: 0x11, g: 0x11,
b: 0xdd b: 0xdd,
}, },
colour2: Colour::default(), colour2: Colour::default(),
speed: Speed::Med, speed: Speed::Med,
direction: Direction::Right direction: Direction::Right,
}; };
let ar = <[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st); let ar = <[u8; LED_MSG_LEN]>::from(&st);
println!("{:02x?}", ar); println!("{:02x?}", ar);
let check = [ let check = [
0x5d, 0xb3, 0x0, 0x0, 0xff, 0x11, 0xdd, 0xeb, 0x0, 0x0, 0xa6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x0, 0x0, 0xff, 0x11, 0xdd, 0xeb, 0x0, 0x0, 0xa6, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0 0x0,
]; ];
assert_eq!(ar, check); assert_eq!(ar, check);
} }
@@ -576,119 +626,95 @@ mod tests {
colour1: Colour { colour1: Colour {
r: 0xff, r: 0xff,
g: 0, g: 0,
b: 0 b: 0,
}, },
colour2: Colour { r: 0, g: 0, b: 0 }, colour2: Colour { r: 0, g: 0, b: 0 },
speed: Speed::Low, speed: Speed::Low,
direction: Direction::Left direction: Direction::Left,
}; };
let capture = [ let capture = [
0x5d, 0xb3, 0x01, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x01, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0 0x0, 0x0,
]; ];
assert_eq!( assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.zone = AuraZone::Key2; st.zone = AuraZone::Key2;
st.colour1 = Colour { st.colour1 = Colour {
r: 0xff, r: 0xff,
g: 0xff, g: 0xff,
b: 0 b: 0,
}; };
let capture = [ let capture = [
0x5d, 0xb3, 0x02, 0x00, 0xff, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x02, 0x00, 0xff, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0 0x0, 0x0,
]; ];
assert_eq!( assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.zone = AuraZone::Key3; st.zone = AuraZone::Key3;
st.colour1 = Colour { st.colour1 = Colour {
r: 0, r: 0,
g: 0xff, g: 0xff,
b: 0xff b: 0xff,
}; };
let capture = [ let capture = [
0x5d, 0xb3, 0x03, 0x00, 0x00, 0xff, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x03, 0x00, 0x00, 0xff, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0 0x0, 0x0,
]; ];
assert_eq!( assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.zone = AuraZone::Key4; st.zone = AuraZone::Key4;
st.colour1 = Colour { st.colour1 = Colour {
r: 0xff, r: 0xff,
g: 0x00, g: 0x00,
b: 0xff b: 0xff,
}; };
let capture = [ let capture = [
0x5d, 0xb3, 0x04, 0x00, 0xff, 0x00, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x04, 0x00, 0xff, 0x00, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0 0x0, 0x0,
]; ];
assert_eq!( assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.zone = AuraZone::Logo; st.zone = AuraZone::Logo;
st.colour1 = Colour { st.colour1 = Colour {
r: 0x2c, r: 0x2c,
g: 0xff, g: 0xff,
b: 0x00 b: 0x00,
}; };
let capture = [ let capture = [
0x5d, 0xb3, 0x05, 0x00, 0x2c, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x05, 0x00, 0x2c, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0 0x0, 0x0,
]; ];
assert_eq!( assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.zone = AuraZone::BarLeft; st.zone = AuraZone::BarLeft;
st.colour1 = Colour { st.colour1 = Colour {
r: 0xff, r: 0xff,
g: 0x00, g: 0x00,
b: 0x00 b: 0x00,
}; };
let capture = [ let capture = [
0x5d, 0xb3, 0x06, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x06, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0 0x0, 0x0,
]; ];
assert_eq!( assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.zone = AuraZone::BarRight; st.zone = AuraZone::BarRight;
st.colour1 = Colour { st.colour1 = Colour {
r: 0xff, r: 0xff,
g: 0x00, g: 0x00,
b: 0xcd b: 0xcd,
}; };
let capture = [ let capture = [
0x5d, 0xb3, 0x07, 0x00, 0xff, 0x00, 0xcd, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x07, 0x00, 0xff, 0x00, 0xcd, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0 0x0, 0x0,
]; ];
assert_eq!( assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.mode = AuraModeNum::RainbowWave; st.mode = AuraModeNum::Rainbow;
let capture = [ let capture = [
0x5d, 0xb3, 0x07, 0x03, 0xff, 0x00, 0xcd, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x07, 0x03, 0xff, 0x00, 0xcd, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0 0x0, 0x0,
]; ];
assert_eq!( assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
} }
} }
+1 -2
View File
@@ -2,7 +2,6 @@ use super::{EffectState, InputForEffect};
use crate::keyboard::{KeyLayout, LedCode}; use crate::keyboard::{KeyLayout, LedCode};
use crate::Colour; use crate::Colour;
#[allow(dead_code)]
pub struct InputBased { pub struct InputBased {
led: LedCode, led: LedCode,
colour: Colour, colour: Colour,
@@ -11,7 +10,7 @@ pub struct InputBased {
/// - temperature /// - temperature
/// - fan speed /// - fan speed
/// - time /// - time
input: Box<dyn InputForEffect> input: Box<dyn InputForEffect>,
} }
impl EffectState for InputBased { impl EffectState for InputBased {
+2 -2
View File
@@ -19,7 +19,7 @@ pub struct Breathe {
#[serde(skip)] #[serde(skip)]
count_flipped: bool, count_flipped: bool,
#[serde(skip)] #[serde(skip)]
use_colour1: bool use_colour1: bool,
} }
impl Breathe { impl Breathe {
@@ -31,7 +31,7 @@ impl Breathe {
speed, speed,
colour: colour1, colour: colour1,
count_flipped: false, count_flipped: false,
use_colour1: true use_colour1: true,
} }
} }
} }
+8 -8
View File
@@ -13,7 +13,7 @@ pub struct DoomFlicker {
#[serde(skip)] #[serde(skip)]
count: u8, count: u8,
#[serde(skip)] #[serde(skip)]
colour: Colour colour: Colour,
} }
impl DoomFlicker { impl DoomFlicker {
@@ -24,7 +24,7 @@ impl DoomFlicker {
count: 4, count: 4,
max_percentage, max_percentage,
min_percentage, min_percentage,
start_colour: colour start_colour: colour,
} }
} }
} }
@@ -53,13 +53,13 @@ impl EffectState for DoomFlicker {
let max_light = Colour { let max_light = Colour {
r: (start_colour.r as f32 / 100.0 * *max_percentage as f32) as u8, r: (start_colour.r as f32 / 100.0 * *max_percentage as f32) as u8,
g: (start_colour.g as f32 / 100.0 * *max_percentage as f32) as u8, g: (start_colour.g as f32 / 100.0 * *max_percentage as f32) as u8,
b: (start_colour.b as f32 / 100.0 * *max_percentage as f32) as u8 b: (start_colour.b as f32 / 100.0 * *max_percentage as f32) as u8,
}; };
// min light is a percentage of the set colour // min light is a percentage of the set colour
let min_light = Colour { let min_light = Colour {
r: (start_colour.r as f32 / 100.0 * *min_percentage as f32) as u8, r: (start_colour.r as f32 / 100.0 * *min_percentage as f32) as u8,
g: (start_colour.g as f32 / 100.0 * *min_percentage as f32) as u8, g: (start_colour.g as f32 / 100.0 * *min_percentage as f32) as u8,
b: (start_colour.b as f32 / 100.0 * *min_percentage as f32) as u8 b: (start_colour.b as f32 / 100.0 * *min_percentage as f32) as u8,
}; };
// Convert the 255 to percentage // Convert the 255 to percentage
@@ -96,7 +96,7 @@ pub struct DoomLightFlash {
#[serde(skip)] #[serde(skip)]
count: u8, count: u8,
#[serde(skip)] #[serde(skip)]
colour: Colour colour: Colour,
} }
impl DoomLightFlash { impl DoomLightFlash {
@@ -109,7 +109,7 @@ impl DoomLightFlash {
min_percentage, min_percentage,
start_colour: colour, start_colour: colour,
max_time: 32, max_time: 32,
min_time: 7 min_time: 7,
} }
} }
} }
@@ -135,13 +135,13 @@ impl EffectState for DoomLightFlash {
let max_light = Colour { let max_light = Colour {
r: (start_colour.r as f32 / 100.0 * *max_percentage as f32) as u8, r: (start_colour.r as f32 / 100.0 * *max_percentage as f32) as u8,
g: (start_colour.g as f32 / 100.0 * *max_percentage as f32) as u8, g: (start_colour.g as f32 / 100.0 * *max_percentage as f32) as u8,
b: (start_colour.b as f32 / 100.0 * *max_percentage as f32) as u8 b: (start_colour.b as f32 / 100.0 * *max_percentage as f32) as u8,
}; };
// min light is a percentage of the set colour // min light is a percentage of the set colour
let min_light = Colour { let min_light = Colour {
r: (start_colour.r as f32 / 100.0 * *min_percentage as f32) as u8, r: (start_colour.r as f32 / 100.0 * *min_percentage as f32) as u8,
g: (start_colour.g as f32 / 100.0 * *min_percentage as f32) as u8, g: (start_colour.g as f32 / 100.0 * *min_percentage as f32) as u8,
b: (start_colour.b as f32 / 100.0 * *min_percentage as f32) as u8 b: (start_colour.b as f32 / 100.0 * *min_percentage as f32) as u8,
}; };
if *colour == max_light { if *colour == max_light {
+18 -16
View File
@@ -1,4 +1,4 @@
use serde::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
mod doom; mod doom;
pub use doom::*; pub use doom::*;
@@ -12,7 +12,7 @@ pub use breathe::*;
mod static_; mod static_;
pub use static_::*; pub use static_::*;
use crate::keyboard::{AuraLaptopUsbPackets, KeyLayout, LedCode, LedUsbPackets}; use crate::keyboard::{KeyLayout, LedCode, LedUsbPackets, UsbPackets};
use crate::Colour; use crate::Colour;
// static mut RNDINDEX: usize = 0; // static mut RNDINDEX: usize = 0;
@@ -32,7 +32,7 @@ pub const RNDTABLE: [i32; 256] = [
206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95, 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95, 28, 139, 123, 98, 125, 196, 15, 70, 194, 253,
54, 14, 109, 226, 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36, 17, 46, 52, 54, 14, 109, 226, 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36, 17, 46, 52,
231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106, 197, 242, 98, 43, 39, 175, 254, 145, 190, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106, 197, 242, 98, 43, 39, 175, 254, 145, 190,
84, 118, 222, 187, 136, 120, 163, 236, 249 84, 118, 222, 187, 136, 120, 163, 236, 249,
]; ];
pub fn p_random() -> i32 { pub fn p_random() -> i32 {
@@ -67,7 +67,7 @@ pub(crate) trait EffectState {
#[derive(Debug, Deserialize, Serialize, Default)] #[derive(Debug, Deserialize, Serialize, Default)]
pub struct AdvancedEffects { pub struct AdvancedEffects {
effects: Vec<Effect>, effects: Vec<Effect>,
zoned: bool zoned: bool,
} }
impl AdvancedEffects { impl AdvancedEffects {
@@ -75,7 +75,7 @@ impl AdvancedEffects {
pub fn new(zoned: bool) -> Self { pub fn new(zoned: bool) -> Self {
Self { Self {
effects: Default::default(), effects: Default::default(),
zoned zoned,
} }
} }
@@ -106,7 +106,7 @@ impl AdvancedEffects {
} }
} }
pub fn create_packets(&self) -> AuraLaptopUsbPackets { pub fn create_packets(&self) -> UsbPackets {
let mut usb_packets = if self.zoned { let mut usb_packets = if self.zoned {
// TODO: figure out if that single byte difference for multizone actually // TODO: figure out if that single byte difference for multizone actually
// matters // matters
@@ -186,7 +186,7 @@ pub enum Effect {
Static(Static), Static(Static),
Breathe(Breathe), Breathe(Breathe),
DoomFlicker(DoomFlicker), DoomFlicker(DoomFlicker),
DoomLightFlash(DoomLightFlash) DoomLightFlash(DoomLightFlash),
} }
impl Default for Effect { impl Default for Effect {
@@ -207,12 +207,14 @@ mod tests {
fn single_key_next_state_then_create() { fn single_key_next_state_then_create() {
let layout = KeyLayout::default_layout(); let layout = KeyLayout::default_layout();
let mut seq = AdvancedEffects::new(false); let mut seq = AdvancedEffects::new(false);
seq.effects seq.effects.push(Effect::Static(Static::new(
.push(Effect::Static(Static::new(LedCode::F, Colour { LedCode::F,
Colour {
r: 255, r: 255,
g: 127, g: 127,
b: 0 b: 0,
}))); },
)));
seq.next_state(&layout); seq.next_state(&layout);
let packets = seq.create_packets(); let packets = seq.create_packets();
@@ -232,14 +234,14 @@ mod tests {
Colour { Colour {
r: 255, r: 255,
g: 127, g: 127,
b: 0 b: 0,
}, },
Colour { Colour {
r: 127, r: 127,
g: 0, g: 0,
b: 255 b: 255,
}, },
Speed::Med Speed::Med,
))); )));
let s = let s =
@@ -274,10 +276,10 @@ mod tests {
Colour { Colour {
r: 255, r: 255,
g: 127, g: 127,
b: 80 b: 80,
}, },
100, 100,
10 10,
))); )));
seq.next_state(&layout); seq.next_state(&layout);
+2 -2
View File
@@ -8,14 +8,14 @@ use crate::{effect_state_impl, Colour};
pub struct Static { pub struct Static {
led: LedCode, led: LedCode,
/// The starting colour /// The starting colour
colour: Colour colour: Colour,
} }
impl Static { impl Static {
pub fn new(address: LedCode, colour: Colour) -> Self { pub fn new(address: LedCode, colour: Colour) -> Self {
Self { Self {
led: address, led: address,
colour colour,
} }
} }
} }
+2 -2
View File
@@ -8,7 +8,7 @@ pub enum Error {
ParseBrightness, ParseBrightness,
IoPath(String, std::io::Error), IoPath(String, std::io::Error),
Ron(ron::Error), Ron(ron::Error),
RonParse(ron::error::SpannedError) RonParse(ron::error::SpannedError),
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@@ -21,7 +21,7 @@ impl fmt::Display for Error {
Error::ParseBrightness => write!(f, "Could not parse brightness"), Error::ParseBrightness => write!(f, "Could not parse brightness"),
Error::IoPath(path, io) => write!(f, "IO Error: {path}, {io}"), Error::IoPath(path, io) => write!(f, "IO Error: {path}, {io}"),
Error::Ron(e) => write!(f, "RON Parse Error: {e}"), Error::Ron(e) => write!(f, "RON Parse Error: {e}"),
Error::RonParse(e) => write!(f, "RON Parse Error: {e}") Error::RonParse(e) => write!(f, "RON Parse Error: {e}"),
} }
} }
} }
+19 -16
View File
@@ -1,5 +1,6 @@
use log::warn; use log::warn;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")] #[cfg(feature = "dbus")]
use zbus::zvariant::Type; use zbus::zvariant::Type;
@@ -164,7 +165,7 @@ pub enum LedCode {
/// To be ignored by effects /// To be ignored by effects
Spacing, Spacing,
/// To be ignored by effects /// To be ignored by effects
Blocking Blocking,
} }
impl LedCode { impl LedCode {
@@ -193,7 +194,8 @@ impl LedCode {
} }
/// Represents the per-key raw USB packets /// Represents the per-key raw USB packets
pub type AuraLaptopUsbPackets = Vec<Vec<u8>>; #[typeshare]
pub type UsbPackets = Vec<Vec<u8>>;
/// A `UsbPackets` contains all data to change the full set of keyboard /// A `UsbPackets` contains all data to change the full set of keyboard
/// key colours individually. /// key colours individually.
@@ -202,15 +204,16 @@ pub type AuraLaptopUsbPackets = Vec<Vec<u8>>;
/// to the keyboard EC. One row controls one group of keys, these keys are not /// 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 /// necessarily all on the same row of the keyboard, with some splitting between
/// two rows. /// two rows.
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type))] #[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct LedUsbPackets { pub struct LedUsbPackets {
/// The packet data used to send data to the USB keyboard /// The packet data used to send data to the USB keyboard
usb_packets: AuraLaptopUsbPackets, usb_packets: UsbPackets,
/// Wether or not this packet collection is zoned. The determines which /// Wether or not this packet collection is zoned. The determines which
/// starting bytes are used and what the indexing is for lightbar RGB /// starting bytes are used and what the indexing is for lightbar RGB
/// colours /// colours
zoned: bool zoned: bool,
} }
impl Default for LedUsbPackets { impl Default for LedUsbPackets {
@@ -244,7 +247,7 @@ impl LedUsbPackets {
} }
Self { Self {
usb_packets: set, usb_packets: set,
zoned: false zoned: false,
} }
} }
@@ -274,7 +277,7 @@ impl LedUsbPackets {
} }
Self { Self {
usb_packets: vec![pkt], usb_packets: vec![pkt],
zoned: true zoned: true,
} }
} }
@@ -469,22 +472,22 @@ impl LedUsbPackets {
} }
#[inline] #[inline]
pub fn get(&self) -> AuraLaptopUsbPackets { pub fn get(&self) -> UsbPackets {
self.usb_packets.clone() self.usb_packets.clone()
} }
#[inline] #[inline]
pub fn get_ref(&self) -> &AuraLaptopUsbPackets { pub fn get_ref(&self) -> &UsbPackets {
&self.usb_packets &self.usb_packets
} }
#[inline] #[inline]
pub fn get_mut(&mut self) -> &mut AuraLaptopUsbPackets { pub fn get_mut(&mut self) -> &mut UsbPackets {
&mut self.usb_packets &mut self.usb_packets
} }
} }
impl From<LedUsbPackets> for AuraLaptopUsbPackets { impl From<LedUsbPackets> for UsbPackets {
fn from(k: LedUsbPackets) -> Self { fn from(k: LedUsbPackets) -> Self {
k.usb_packets k.usb_packets
} }
@@ -633,14 +636,14 @@ impl From<&LedCode> for &str {
LedCode::ZonedKbLeft => "Left Zone (zone 1)", LedCode::ZonedKbLeft => "Left Zone (zone 1)",
LedCode::ZonedKbLeftMid => "Center-left Zone (zone 2)", LedCode::ZonedKbLeftMid => "Center-left Zone (zone 2)",
LedCode::ZonedKbRightMid => "Center-right Zone (zone 3)", LedCode::ZonedKbRightMid => "Center-right Zone (zone 3)",
LedCode::ZonedKbRight => "Right Zone (zone 4)" LedCode::ZonedKbRight => "Right Zone (zone 4)",
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::keyboard::{AuraLaptopUsbPackets, LedCode, LedUsbPackets}; use crate::keyboard::{LedCode, LedUsbPackets, UsbPackets};
macro_rules! colour_check_zoned { macro_rules! colour_check_zoned {
($zone:expr, $pkt_idx_start:expr) => { ($zone:expr, $pkt_idx_start:expr) => {
@@ -650,7 +653,7 @@ mod tests {
c[1] = 255; c[1] = 255;
c[2] = 255; c[2] = 255;
let pkt: AuraLaptopUsbPackets = zone.into(); let pkt: UsbPackets = zone.into();
assert_eq!(pkt[0][$pkt_idx_start], 0xff); assert_eq!(pkt[0][$pkt_idx_start], 0xff);
assert_eq!(pkt[0][$pkt_idx_start + 1], 0xff); assert_eq!(pkt[0][$pkt_idx_start + 1], 0xff);
assert_eq!(pkt[0][$pkt_idx_start + 2], 0xff); assert_eq!(pkt[0][$pkt_idx_start + 2], 0xff);
@@ -660,7 +663,7 @@ mod tests {
#[test] #[test]
fn zone_to_packet_check() { fn zone_to_packet_check() {
let zone = LedUsbPackets::new_zoned(true); let zone = LedUsbPackets::new_zoned(true);
let pkt: AuraLaptopUsbPackets = zone.into(); let pkt: UsbPackets = zone.into();
assert_eq!(pkt[0][0], 0x5d); assert_eq!(pkt[0][0], 0x5d);
assert_eq!(pkt[0][1], 0xbc); assert_eq!(pkt[0][1], 0xbc);
assert_eq!(pkt[0][2], 0x01); assert_eq!(pkt[0][2], 0x01);
@@ -683,7 +686,7 @@ mod tests {
#[test] #[test]
fn perkey_to_packet_check() { fn perkey_to_packet_check() {
let per_key = LedUsbPackets::new_per_key(); let per_key = LedUsbPackets::new_per_key();
let pkt: AuraLaptopUsbPackets = per_key.into(); let pkt: UsbPackets = per_key.into();
assert_eq!(pkt[0][0], 0x5d); assert_eq!(pkt[0][0], 0x5d);
assert_eq!(pkt[0][1], 0xbc); assert_eq!(pkt[0][1], 0xbc);
assert_eq!(pkt[0][2], 0x00); assert_eq!(pkt[0][2], 0x00);
@@ -709,7 +712,7 @@ mod tests {
c[1] = 255; c[1] = 255;
c[2] = 255; c[2] = 255;
let pkt: AuraLaptopUsbPackets = per_key.into(); let pkt: UsbPackets = per_key.into();
assert_eq!(pkt[5][30], 0xff); // D, red assert_eq!(pkt[5][30], 0xff); // D, red
assert_eq!(pkt[5][31], 0xff); // D assert_eq!(pkt[5][31], 0xff); // D
assert_eq!(pkt[5][32], 0xff); // D assert_eq!(pkt[5][32], 0xff); // D
+126 -102
View File
@@ -26,12 +26,12 @@ pub enum KeyShape {
pad_left: f32, pad_left: f32,
pad_right: f32, pad_right: f32,
pad_top: f32, pad_top: f32,
pad_bottom: f32 pad_bottom: f32,
}, },
Blank { Blank {
width: f32, width: f32,
height: f32 height: f32,
} },
} }
impl KeyShape { impl KeyShape {
@@ -41,7 +41,7 @@ impl KeyShape {
pad_left: f32, pad_left: f32,
pad_right: f32, pad_right: f32,
pad_top: f32, pad_top: f32,
pad_bottom: f32 pad_bottom: f32,
) -> Self { ) -> Self {
Self::Led { Self::Led {
width, width,
@@ -49,7 +49,7 @@ impl KeyShape {
pad_left, pad_left,
pad_right, pad_right,
pad_top, pad_top,
pad_bottom pad_bottom,
} }
} }
@@ -66,7 +66,7 @@ impl KeyShape {
pad_left, pad_left,
pad_right, pad_right,
pad_top, pad_top,
pad_bottom pad_bottom,
} => { } => {
*width *= scale; *width *= scale;
*height *= scale; *height *= scale;
@@ -97,7 +97,7 @@ pub struct KeyRow {
row: Vec<(LedCode, String)>, row: Vec<(LedCode, String)>,
/// The final data structure merged key_shapes and rows /// The final data structure merged key_shapes and rows
#[serde(skip)] #[serde(skip)]
built_row: Vec<(LedCode, KeyShape)> built_row: Vec<(LedCode, KeyShape)>,
} }
impl KeyRow { impl KeyRow {
@@ -106,7 +106,7 @@ impl KeyRow {
pad_left, pad_left,
pad_top, pad_top,
row, row,
built_row: Default::default() built_row: Default::default(),
} }
} }
@@ -132,7 +132,7 @@ impl KeyRow {
pad_bottom, pad_bottom,
.. ..
} => height + pad_top + pad_bottom, } => height + pad_top + pad_bottom,
KeyShape::Blank { height, .. } => *height KeyShape::Blank { height, .. } => *height,
}; };
if h < height { if h < height {
@@ -156,7 +156,7 @@ impl KeyRow {
pad_right, pad_right,
.. ..
} => w += width + pad_left + pad_right, } => w += width + pad_left + pad_right,
KeyShape::Blank { width, .. } => w += width KeyShape::Blank { width, .. } => w += width,
} }
} }
w w
@@ -185,7 +185,7 @@ pub struct KeyLayout {
/// Should be copied from the `LaptopLedData` as laptops may have the same /// Should be copied from the `LaptopLedData` as laptops may have the same
/// layout, but different EC features. /// layout, but different EC features.
#[serde(skip)] #[serde(skip)]
advanced_type: AdvancedAuraType advanced_type: AdvancedAuraType,
} }
impl KeyLayout { impl KeyLayout {
@@ -195,7 +195,7 @@ impl KeyLayout {
if buf.is_empty() { if buf.is_empty() {
Err(Error::IoPath( Err(Error::IoPath(
path.to_string_lossy().to_string(), path.to_string_lossy().to_string(),
std::io::ErrorKind::InvalidData.into() std::io::ErrorKind::InvalidData.into(),
)) ))
} else { } else {
let mut data = ron::from_str::<Self>(&buf)?; let mut data = ron::from_str::<Self>(&buf)?;
@@ -332,97 +332,121 @@ impl KeyLayout {
advanced_type: AdvancedAuraType::None, advanced_type: AdvancedAuraType::None,
key_shapes: HashMap::from([( key_shapes: HashMap::from([(
"regular".to_owned(), "regular".to_owned(),
KeyShape::new_led(1.0, 1.0, 0.1, 0.1, 0.1, 0.1) KeyShape::new_led(1.0, 1.0, 0.1, 0.1, 0.1, 0.1),
)]), )]),
key_rows: vec![ key_rows: vec![
KeyRow::new(0.1, 0.1, vec![ KeyRow::new(
(LedCode::Esc, "regular".to_owned()), 0.1,
(LedCode::F1, "regular".to_owned()), 0.1,
(LedCode::F2, "regular".to_owned()), vec![
(LedCode::F3, "regular".to_owned()), (LedCode::Esc, "regular".to_owned()),
(LedCode::F4, "regular".to_owned()), (LedCode::F1, "regular".to_owned()),
// not sure which key to put here (LedCode::F2, "regular".to_owned()),
(LedCode::F5, "regular".to_owned()), (LedCode::F3, "regular".to_owned()),
(LedCode::F6, "regular".to_owned()), (LedCode::F4, "regular".to_owned()),
(LedCode::F7, "regular".to_owned()), // not sure which key to put here
(LedCode::F8, "regular".to_owned()), (LedCode::F5, "regular".to_owned()),
(LedCode::F9, "regular".to_owned()), (LedCode::F6, "regular".to_owned()),
(LedCode::F10, "regular".to_owned()), (LedCode::F7, "regular".to_owned()),
(LedCode::F11, "regular".to_owned()), (LedCode::F8, "regular".to_owned()),
(LedCode::F12, "regular".to_owned()), (LedCode::F9, "regular".to_owned()),
]), (LedCode::F10, "regular".to_owned()),
KeyRow::new(0.1, 0.1, vec![ (LedCode::F11, "regular".to_owned()),
(LedCode::Tilde, "regular".to_owned()), (LedCode::F12, "regular".to_owned()),
(LedCode::N1, "regular".to_owned()), ],
(LedCode::N2, "regular".to_owned()), ),
(LedCode::N3, "regular".to_owned()), KeyRow::new(
(LedCode::N4, "regular".to_owned()), 0.1,
(LedCode::N5, "regular".to_owned()), 0.1,
(LedCode::N6, "regular".to_owned()), vec![
(LedCode::N7, "regular".to_owned()), (LedCode::Tilde, "regular".to_owned()),
(LedCode::N8, "regular".to_owned()), (LedCode::N1, "regular".to_owned()),
(LedCode::N9, "regular".to_owned()), (LedCode::N2, "regular".to_owned()),
(LedCode::N0, "regular".to_owned()), (LedCode::N3, "regular".to_owned()),
(LedCode::Hyphen, "regular".to_owned()), (LedCode::N4, "regular".to_owned()),
(LedCode::Equals, "regular".to_owned()), (LedCode::N5, "regular".to_owned()),
(LedCode::Backspace, "regular".to_owned()), (LedCode::N6, "regular".to_owned()),
]), (LedCode::N7, "regular".to_owned()),
KeyRow::new(0.1, 0.1, vec![ (LedCode::N8, "regular".to_owned()),
(LedCode::Tab, "regular".to_owned()), (LedCode::N9, "regular".to_owned()),
(LedCode::Q, "regular".to_owned()), (LedCode::N0, "regular".to_owned()),
(LedCode::W, "regular".to_owned()), (LedCode::Hyphen, "regular".to_owned()),
(LedCode::E, "regular".to_owned()), (LedCode::Equals, "regular".to_owned()),
(LedCode::R, "regular".to_owned()), (LedCode::Backspace, "regular".to_owned()),
(LedCode::T, "regular".to_owned()), ],
(LedCode::Y, "regular".to_owned()), ),
(LedCode::U, "regular".to_owned()), KeyRow::new(
(LedCode::I, "regular".to_owned()), 0.1,
(LedCode::O, "regular".to_owned()), 0.1,
(LedCode::P, "regular".to_owned()), vec![
(LedCode::LBracket, "regular".to_owned()), (LedCode::Tab, "regular".to_owned()),
(LedCode::RBracket, "regular".to_owned()), (LedCode::Q, "regular".to_owned()),
(LedCode::BackSlash, "regular".to_owned()), (LedCode::W, "regular".to_owned()),
]), (LedCode::E, "regular".to_owned()),
KeyRow::new(0.1, 0.1, vec![ (LedCode::R, "regular".to_owned()),
(LedCode::Caps, "regular".to_owned()), (LedCode::T, "regular".to_owned()),
(LedCode::A, "regular".to_owned()), (LedCode::Y, "regular".to_owned()),
(LedCode::S, "regular".to_owned()), (LedCode::U, "regular".to_owned()),
(LedCode::D, "regular".to_owned()), (LedCode::I, "regular".to_owned()),
(LedCode::F, "regular".to_owned()), (LedCode::O, "regular".to_owned()),
(LedCode::G, "regular".to_owned()), (LedCode::P, "regular".to_owned()),
(LedCode::H, "regular".to_owned()), (LedCode::LBracket, "regular".to_owned()),
(LedCode::J, "regular".to_owned()), (LedCode::RBracket, "regular".to_owned()),
(LedCode::K, "regular".to_owned()), (LedCode::BackSlash, "regular".to_owned()),
(LedCode::L, "regular".to_owned()), ],
(LedCode::SemiColon, "regular".to_owned()), ),
(LedCode::Quote, "regular".to_owned()), KeyRow::new(
(LedCode::Return, "regular".to_owned()), 0.1,
]), 0.1,
KeyRow::new(0.1, 0.1, vec![ vec![
(LedCode::LShift, "regular".to_owned()), (LedCode::Caps, "regular".to_owned()),
(LedCode::Z, "regular".to_owned()), (LedCode::A, "regular".to_owned()),
(LedCode::X, "regular".to_owned()), (LedCode::S, "regular".to_owned()),
(LedCode::C, "regular".to_owned()), (LedCode::D, "regular".to_owned()),
(LedCode::V, "regular".to_owned()), (LedCode::F, "regular".to_owned()),
(LedCode::B, "regular".to_owned()), (LedCode::G, "regular".to_owned()),
(LedCode::N, "regular".to_owned()), (LedCode::H, "regular".to_owned()),
(LedCode::M, "regular".to_owned()), (LedCode::J, "regular".to_owned()),
(LedCode::Comma, "regular".to_owned()), (LedCode::K, "regular".to_owned()),
(LedCode::Period, "regular".to_owned()), (LedCode::L, "regular".to_owned()),
(LedCode::FwdSlash, "regular".to_owned()), (LedCode::SemiColon, "regular".to_owned()),
(LedCode::Rshift, "regular".to_owned()), (LedCode::Quote, "regular".to_owned()),
]), (LedCode::Return, "regular".to_owned()),
KeyRow::new(0.1, 0.1, vec![ ],
(LedCode::LCtrl, "regular".to_owned()), ),
(LedCode::LFn, "regular".to_owned()), KeyRow::new(
(LedCode::Meta, "regular".to_owned()), 0.1,
(LedCode::LAlt, "regular".to_owned()), 0.1,
(LedCode::Spacebar, "regular".to_owned()), vec![
(LedCode::RAlt, "regular".to_owned()), (LedCode::LShift, "regular".to_owned()),
(LedCode::PrtSc, "regular".to_owned()), (LedCode::Z, "regular".to_owned()),
(LedCode::RCtrl, "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()),
],
),
],
} }
} }
} }
@@ -464,7 +488,7 @@ mod tests {
let rows = &data.key_rows; let rows = &data.key_rows;
for row in rows { for row in rows {
for k in &row.row { for k in &row.row {
if data.key_shapes.contains_key(&k.1) { if data.key_shapes.get(&k.1).is_some() {
unused.remove(&k.1); unused.remove(&k.1);
} else { } else {
panic!("Key {:?} was missing matching shape {}", k.0, k.1); panic!("Key {:?} was missing matching shape {}", k.0, k.1);
+1 -1
View File
@@ -16,5 +16,5 @@ pub enum AdvancedAuraType {
#[default] #[default]
None, None,
Zoned(Vec<LedCode>), Zoned(Vec<LedCode>),
PerKey PerKey,
} }
+259 -338
View File
@@ -5,6 +5,7 @@ use std::ops::{BitAnd, BitOr};
use log::warn; use log::warn;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")] #[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value}; use zbus::zvariant::{OwnedValue, Type, Value};
@@ -15,6 +16,7 @@ use crate::{AuraDeviceType, PowerZones};
/// - 2021+, the struct is a single zone with 4 states /// - 2021+, the struct is a single zone with 4 states
/// - pre-2021, the struct is 1 or 2 zones and 3 states /// - pre-2021, the struct is 1 or 2 zones and 3 states
/// - Tuf, the struct is 1 zone and 3 states /// - Tuf, the struct is 1 zone and 3 states
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))] #[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuraPowerState { pub struct AuraPowerState {
@@ -23,7 +25,7 @@ pub struct AuraPowerState {
pub awake: bool, pub awake: bool,
pub sleep: bool, pub sleep: bool,
/// Ignored for pre-2021 and Tuf /// Ignored for pre-2021 and Tuf
pub shutdown: bool pub shutdown: bool,
} }
impl Default for AuraPowerState { impl Default for AuraPowerState {
@@ -34,7 +36,7 @@ impl Default for AuraPowerState {
boot: true, boot: true,
awake: true, awake: true,
sleep: true, sleep: true,
shutdown: true shutdown: true,
} }
} }
} }
@@ -46,15 +48,12 @@ impl AuraPowerState {
boot: true, boot: true,
awake: true, awake: true,
sleep: true, sleep: true,
shutdown: true shutdown: true,
} }
} }
fn tuf_to_bytes(&self) -> Vec<u8> { fn tuf_to_bytes(&self) -> Vec<u8> {
// &cmd, &boot, &awake, &sleep, &keyboard todo!("0s and 1s for bool array")
vec![
1, self.boot as u8, self.awake as u8, self.sleep as u8, 1,
]
} }
/// # Bits for older 0x1866 keyboard model /// # Bits for older 0x1866 keyboard model
@@ -102,53 +101,48 @@ impl AuraPowerState {
] ]
} }
pub fn new_to_byte(&self) -> u32 { fn new_to_byte(&self) -> u32 {
match self.zone { match self.zone {
PowerZones::Logo => { PowerZones::Logo => {
self.boot as u32 self.boot as u32
| ((self.awake as u32) << 2) | (self.awake as u32) << 2
| ((self.sleep as u32) << 4) | (self.sleep as u32) << 4
| ((self.shutdown as u32) << 6) | (self.shutdown as u32) << 6
}
PowerZones::Ally => {
(self.boot as u32)
| ((self.awake as u32) << 1)
| ((self.sleep as u32) << 2)
| ((self.shutdown as u32) << 3)
} }
PowerZones::Keyboard => { PowerZones::Keyboard => {
((self.boot as u32) << 1) (self.boot as u32) << 1
| ((self.awake as u32) << 3) | (self.awake as u32) << 3
| ((self.sleep as u32) << 5) | (self.sleep as u32) << 5
| ((self.shutdown as u32) << 7) | (self.shutdown as u32) << 7
} }
PowerZones::Lightbar => { PowerZones::Lightbar => {
((self.boot as u32) << (7 + 2)) (self.boot as u32) << (7 + 2)
| ((self.awake as u32) << (7 + 3)) | (self.awake as u32) << (7 + 3)
| ((self.sleep as u32) << (7 + 4)) | (self.sleep as u32) << (7 + 4)
| ((self.shutdown as u32) << (7 + 5)) | (self.shutdown as u32) << (7 + 5)
} }
PowerZones::Lid => { PowerZones::Lid => {
((self.boot as u32) << (15 + 1)) (self.boot as u32) << (15 + 1)
| ((self.awake as u32) << (15 + 2)) | (self.awake as u32) << (15 + 2)
| ((self.sleep as u32) << (15 + 3)) | (self.sleep as u32) << (15 + 3)
| ((self.shutdown as u32) << (15 + 4)) | (self.shutdown as u32) << (15 + 4)
} }
PowerZones::RearGlow => { PowerZones::RearGlow => {
((self.boot as u32) << (23 + 1)) (self.boot as u32) << (23 + 1)
| ((self.awake as u32) << (23 + 2)) | (self.awake as u32) << (23 + 2)
| ((self.sleep as u32) << (23 + 3)) | (self.sleep as u32) << (23 + 3)
| ((self.shutdown as u32) << (23 + 4)) | (self.shutdown as u32) << (23 + 4)
} }
PowerZones::None | PowerZones::KeyboardAndLightbar => 0 PowerZones::KeyboardAndLightbar | PowerZones::None => 0,
} }
} }
} }
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))] #[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct LaptopAuraPower { pub struct LaptopAuraPower {
pub states: Vec<AuraPowerState> pub states: Vec<AuraPowerState>,
} }
impl LaptopAuraPower { impl LaptopAuraPower {
@@ -192,70 +186,41 @@ impl LaptopAuraPower {
// TODO: use support data to setup correct zones // TODO: use support data to setup correct zones
pub fn new(aura_type: AuraDeviceType, support_data: &LedSupportData) -> Self { pub fn new(aura_type: AuraDeviceType, support_data: &LedSupportData) -> Self {
match aura_type { match aura_type {
AuraDeviceType::Unknown | AuraDeviceType::Ally | AuraDeviceType::LaptopKeyboard2021 => { AuraDeviceType::Unknown | AuraDeviceType::LaptopPost2021 => {
let mut states = Vec::new(); let mut states = Vec::new();
for zone in support_data.power_zones.iter() { for zone in support_data.power_zones.iter() {
states.push(AuraPowerState::default_for(*zone)) states.push(AuraPowerState::default_for(*zone))
} }
Self { states } Self { states }
} }
AuraDeviceType::LaptopKeyboardPre2021 => { AuraDeviceType::LaptopPre2021 => {
// The older devices are tri-state if have lightbar:
// 1. Keyboard
// 2. Lightbar
// 3. KeyboardAndLightbar
if support_data.power_zones.contains(&PowerZones::Lightbar) { if support_data.power_zones.contains(&PowerZones::Lightbar) {
Self { Self {
states: vec![AuraPowerState::default_for(PowerZones::KeyboardAndLightbar)] states: vec![AuraPowerState::default_for(PowerZones::KeyboardAndLightbar)],
} }
} else { } else {
Self { Self {
states: vec![AuraPowerState::default_for(PowerZones::Keyboard)] states: vec![AuraPowerState::default_for(PowerZones::Keyboard)],
} }
} }
} }
AuraDeviceType::LaptopKeyboardTuf => Self { AuraDeviceType::LaptopTuf => Self {
states: vec![AuraPowerState::default_for(PowerZones::Keyboard)] states: vec![AuraPowerState::default_for(PowerZones::Keyboard)],
}, },
AuraDeviceType::ScsiExtDisk => todo!(), AuraDeviceType::ScsiExtDisk => todo!(),
AuraDeviceType::AnimeOrSlash => todo!()
} }
} }
pub fn to_bytes(&self, aura_type: AuraDeviceType) -> Vec<u8> { pub fn to_bytes(&self, aura_type: AuraDeviceType) -> Vec<u8> {
if let Some(stuff) = self.states.first() {
if stuff.zone == PowerZones::Ally {
return vec![
0x5d,
0xd1,
0x09,
0x01,
stuff.new_to_byte() as u8,
];
}
}
match aura_type { match aura_type {
AuraDeviceType::LaptopKeyboard2021 | AuraDeviceType::Ally => self.new_to_bytes(), AuraDeviceType::LaptopPost2021 => self.new_to_bytes(),
AuraDeviceType::LaptopKeyboardPre2021 => { AuraDeviceType::LaptopPre2021 => self
if self.states.len() == 1 { .states
self.states .first()
.first() .cloned()
.cloned() .unwrap_or_default()
.unwrap_or_default() .old_to_bytes(),
.old_to_bytes() AuraDeviceType::LaptopTuf => self
} else {
let mut bytes: Vec<Vec<u8>> =
self.states.iter().map(|s| s.old_to_bytes()).collect();
let mut b = bytes.pop().unwrap();
for i in bytes {
for (i, n) in i.iter().enumerate() {
b[i] |= n;
}
}
b
}
}
AuraDeviceType::LaptopKeyboardTuf => self
.states .states
.first() .first()
.cloned() .cloned()
@@ -265,8 +230,7 @@ impl LaptopAuraPower {
warn!("Trying to create bytes for an unknown device"); warn!("Trying to create bytes for an unknown device");
self.new_to_bytes() self.new_to_bytes()
} }
AuraDeviceType::ScsiExtDisk => todo!("scsi disk not implemented yet"), AuraDeviceType::ScsiExtDisk => todo!(),
AuraDeviceType::AnimeOrSlash => todo!("anime/slash not implemented yet")
} }
} }
} }
@@ -285,7 +249,7 @@ enum OldAuraPower {
Boot = 0xc31209, Boot = 0xc31209,
Sleep = 0x300804, Sleep = 0x300804,
Keyboard = 0x080000, Keyboard = 0x080000,
Lightbar = 0x040500 Lightbar = 0x040500,
} }
impl BitOr<OldAuraPower> for OldAuraPower { impl BitOr<OldAuraPower> for OldAuraPower {
@@ -315,297 +279,254 @@ mod test {
use crate::keyboard::{AuraPowerState, LaptopAuraPower}; use crate::keyboard::{AuraPowerState, LaptopAuraPower};
use crate::{AuraDeviceType, PowerZones}; use crate::{AuraDeviceType, PowerZones};
fn to_binary_string_post2021(power: &LaptopAuraPower) -> String {
let bytes = power.to_bytes(AuraDeviceType::LaptopKeyboard2021);
format!(
"{:08b}, {:08b}, {:08b}, {:08b}",
bytes[0], bytes[1], bytes[2], bytes[3]
)
}
#[test] #[test]
fn check_0x1866_control_bytes() { fn check_0x1866_control_bytes() {
let power = LaptopAuraPower { let state = AuraPowerState {
states: vec![ zone: PowerZones::Keyboard,
AuraPowerState { awake: true,
zone: PowerZones::Keyboard, boot: false,
boot: false, sleep: false,
awake: true, shutdown: false,
sleep: false,
shutdown: false
},
]
}; };
let bytes = power.to_bytes(AuraDeviceType::LaptopKeyboardPre2021); let bytes = state.old_to_bytes();
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]); println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x08, 0x00, 0x02, 0x00]); assert_eq!(bytes, [0x08, 0x00, 0x02, 0x00]);
let power = LaptopAuraPower { let state = AuraPowerState {
states: vec![ zone: PowerZones::Lightbar,
AuraPowerState { awake: true,
zone: PowerZones::Lightbar, boot: false,
boot: false, sleep: false,
awake: true, shutdown: false,
sleep: false,
shutdown: false
},
]
}; };
let bytes = power.to_bytes(AuraDeviceType::LaptopKeyboardPre2021); let bytes = state.old_to_bytes();
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]); println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x04, 0x05, 0x02, 0x00]); assert_eq!(bytes, [0x04, 0x05, 0x02, 0x00]);
// let bytes = [ let bytes = AuraPowerState {
// OldAuraPower::Keyboard, zone: PowerZones::None,
// OldAuraPower::Lightbar, awake: false,
// OldAuraPower::Awake, boot: false,
// OldAuraPower::Sleep, sleep: true,
// OldAuraPower::Boot, shutdown: false,
// ];
let power = LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Keyboard,
boot: true,
awake: true,
sleep: true,
shutdown: false
},
AuraPowerState {
zone: PowerZones::Lightbar,
boot: true,
awake: true,
sleep: true,
shutdown: false
},
]
}; };
let bytes = power.to_bytes(AuraDeviceType::LaptopKeyboardPre2021); let bytes = bytes.old_to_bytes();
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x30, 0x08, 0x04, 0x00]);
let bytes = AuraPowerState {
zone: PowerZones::None,
awake: false,
boot: true,
sleep: false,
shutdown: false,
};
let bytes = bytes.old_to_bytes();
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0xc3, 0x12, 0x09, 0x00]);
let power = AuraPowerState {
zone: PowerZones::KeyboardAndLightbar,
awake: true,
boot: true,
sleep: true,
shutdown: false,
};
let bytes = power.old_to_bytes();
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]); println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0xff, 0x1f, 0x000f, 0x00]); assert_eq!(bytes, [0xff, 0x1f, 0x000f, 0x00]);
} }
#[test] #[test]
fn check_0x19b6_control_bytes_binary_rep() { fn check_0x19b6_control_bytes_binary_rep() {
let boot_logo_ = to_binary_string_post2021(&LaptopAuraPower { fn to_binary_string(power: &LaptopAuraPower) -> String {
states: vec![ let bytes = power.to_bytes(AuraDeviceType::LaptopPost2021);
AuraPowerState { format!(
zone: PowerZones::Logo, "{:08b}, {:08b}, {:08b}, {:08b}",
boot: true, bytes[0], bytes[1], bytes[2], bytes[3]
awake: false, )
sleep: false, }
shutdown: false
}, let boot_logo_ = to_binary_string(&LaptopAuraPower {
] states: vec![AuraPowerState {
zone: PowerZones::Logo,
boot: true,
awake: false,
sleep: false,
shutdown: false,
}],
}); });
let boot_keyb_ = to_binary_string_post2021(&LaptopAuraPower { let boot_keyb_ = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![AuraPowerState {
AuraPowerState { zone: PowerZones::Keyboard,
zone: PowerZones::Keyboard, boot: true,
boot: true, awake: false,
awake: false, sleep: false,
sleep: false, shutdown: false,
shutdown: false }],
},
]
}); });
let sleep_logo = to_binary_string_post2021(&LaptopAuraPower { let sleep_logo = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![AuraPowerState {
AuraPowerState { zone: PowerZones::Logo,
zone: PowerZones::Logo, boot: false,
boot: false, awake: false,
awake: false, sleep: true,
sleep: true, shutdown: false,
shutdown: false }],
},
]
}); });
let sleep_keyb = to_binary_string_post2021(&LaptopAuraPower { let sleep_keyb = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![AuraPowerState {
AuraPowerState { zone: PowerZones::Keyboard,
zone: PowerZones::Keyboard, boot: false,
boot: false, awake: false,
awake: false, sleep: true,
sleep: true, shutdown: false,
shutdown: false }],
},
]
}); });
let awake_logo = to_binary_string_post2021(&LaptopAuraPower { let awake_logo = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![AuraPowerState {
AuraPowerState { zone: PowerZones::Logo,
zone: PowerZones::Logo, boot: false,
boot: false, awake: true,
awake: true, sleep: false,
sleep: false, shutdown: false,
shutdown: false }],
},
]
}); });
let awake_keyb = to_binary_string_post2021(&LaptopAuraPower { let awake_keyb = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![AuraPowerState {
AuraPowerState { zone: PowerZones::Keyboard,
zone: PowerZones::Keyboard, boot: false,
boot: false, awake: true,
awake: true, sleep: false,
sleep: false, shutdown: false,
shutdown: false }],
},
]
}); });
let shut_logo_ = to_binary_string_post2021(&LaptopAuraPower { let shut_logo_ = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![AuraPowerState {
AuraPowerState { zone: PowerZones::Logo,
zone: PowerZones::Logo, boot: false,
boot: false, awake: false,
awake: false, sleep: false,
sleep: false, shutdown: true,
shutdown: true }],
},
]
}); });
let shut_keyb_ = to_binary_string_post2021(&LaptopAuraPower { let shut_keyb_ = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![AuraPowerState {
AuraPowerState { zone: PowerZones::Keyboard,
zone: PowerZones::Keyboard, boot: false,
boot: false, awake: false,
awake: false, sleep: false,
sleep: false, shutdown: true,
shutdown: true }],
},
]
}); });
let boot_bar__ = to_binary_string_post2021(&LaptopAuraPower { let boot_bar__ = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![AuraPowerState {
AuraPowerState { zone: PowerZones::Lightbar,
zone: PowerZones::Lightbar, boot: true,
boot: true, awake: false,
awake: false, sleep: false,
sleep: false, shutdown: false,
shutdown: false }],
},
]
}); });
let awake_bar_ = to_binary_string_post2021(&LaptopAuraPower { let awake_bar_ = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![AuraPowerState {
AuraPowerState { zone: PowerZones::Lightbar,
zone: PowerZones::Lightbar, boot: false,
boot: false, awake: true,
awake: true, sleep: false,
sleep: false, shutdown: false,
shutdown: false }],
},
]
}); });
let sleep_bar_ = to_binary_string_post2021(&LaptopAuraPower { let sleep_bar_ = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![AuraPowerState {
AuraPowerState { zone: PowerZones::Lightbar,
zone: PowerZones::Lightbar, boot: false,
boot: false, awake: false,
awake: false, sleep: true,
sleep: true, shutdown: false,
shutdown: false }],
},
]
}); });
let shut_bar__ = to_binary_string_post2021(&LaptopAuraPower { let shut_bar__ = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![AuraPowerState {
AuraPowerState { zone: PowerZones::Lightbar,
zone: PowerZones::Lightbar, boot: false,
boot: false, awake: false,
awake: false, sleep: false,
sleep: false, shutdown: true,
shutdown: true }],
},
]
}); });
let boot_lid__ = to_binary_string_post2021(&LaptopAuraPower { let boot_lid__ = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![AuraPowerState {
AuraPowerState { zone: PowerZones::Lid,
zone: PowerZones::Lid, boot: true,
boot: true, awake: false,
awake: false, sleep: false,
sleep: false, shutdown: false,
shutdown: false }],
},
]
}); });
let awake_lid_ = to_binary_string_post2021(&LaptopAuraPower { let awake_lid_ = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![AuraPowerState {
AuraPowerState { zone: PowerZones::Lid,
zone: PowerZones::Lid, boot: false,
boot: false, awake: true,
awake: true, sleep: false,
sleep: false, shutdown: false,
shutdown: false }],
},
]
}); });
let sleep_lid_ = to_binary_string_post2021(&LaptopAuraPower { let sleep_lid_ = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![AuraPowerState {
AuraPowerState { zone: PowerZones::Lid,
zone: PowerZones::Lid, boot: false,
boot: false, awake: false,
awake: false, sleep: true,
sleep: true, shutdown: false,
shutdown: false }],
},
]
}); });
let shut_lid__ = to_binary_string_post2021(&LaptopAuraPower { let shut_lid__ = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![AuraPowerState {
AuraPowerState { zone: PowerZones::Lid,
zone: PowerZones::Lid, boot: false,
boot: false, awake: false,
awake: false, sleep: false,
sleep: false, shutdown: true,
shutdown: true }],
},
]
}); });
let boot_rear_ = to_binary_string_post2021(&LaptopAuraPower { let boot_rear_ = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![AuraPowerState {
AuraPowerState { zone: PowerZones::RearGlow,
zone: PowerZones::RearGlow, boot: true,
boot: true, awake: false,
awake: false, sleep: false,
sleep: false, shutdown: false,
shutdown: false }],
},
]
}); });
let awake_rear = to_binary_string_post2021(&LaptopAuraPower { let awake_rear = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![AuraPowerState {
AuraPowerState { zone: PowerZones::RearGlow,
zone: PowerZones::RearGlow, boot: false,
boot: false, awake: true,
awake: true, sleep: false,
sleep: false, shutdown: false,
shutdown: false }],
},
]
}); });
let sleep_rear = to_binary_string_post2021(&LaptopAuraPower { let sleep_rear = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![AuraPowerState {
AuraPowerState { zone: PowerZones::RearGlow,
zone: PowerZones::RearGlow, boot: false,
boot: false, awake: false,
awake: false, sleep: true,
sleep: true, shutdown: false,
shutdown: false }],
},
]
}); });
let shut_rear_ = to_binary_string_post2021(&LaptopAuraPower { let shut_rear_ = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![AuraPowerState {
AuraPowerState { zone: PowerZones::RearGlow,
zone: PowerZones::RearGlow, boot: false,
boot: false, awake: false,
awake: false, sleep: false,
sleep: false, shutdown: true,
shutdown: true }],
},
]
}); });
assert_eq!(boot_logo_, "00000001, 00000000, 00000000, 00000000"); assert_eq!(boot_logo_, "00000001, 00000000, 00000000, 00000000");
@@ -633,7 +554,7 @@ mod test {
assert_eq!(shut_rear_, "00000000, 00000000, 00000000, 00001000"); assert_eq!(shut_rear_, "00000000, 00000000, 00000000, 00001000");
// All on // All on
let byte1 = to_binary_string_post2021(&LaptopAuraPower { let byte1 = to_binary_string(&LaptopAuraPower {
states: vec![ states: vec![
AuraPowerState { AuraPowerState {
zone: PowerZones::Keyboard, zone: PowerZones::Keyboard,
@@ -655,7 +576,7 @@ mod test {
zone: PowerZones::RearGlow, zone: PowerZones::RearGlow,
..Default::default() ..Default::default()
}, },
] ],
}); });
assert_eq!(byte1, "11111111, 00011110, 00001111, 00001111"); assert_eq!(byte1, "11111111, 00011110, 00001111, 00001111");
} }
+24 -34
View File
@@ -6,6 +6,7 @@
use std::fmt::Debug; use std::fmt::Debug;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")] #[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value}; use zbus::zvariant::{OwnedValue, Type, Value};
@@ -23,77 +24,70 @@ pub mod usb;
pub mod keyboard; pub mod keyboard;
pub const AURA_LAPTOP_LED_MSG_LEN: usize = 17; pub const LED_MSG_LEN: usize = 17;
pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub const RED: Colour = Colour { pub const RED: Colour = Colour {
r: 0xff, r: 0xff,
g: 0x00, g: 0x00,
b: 0x00 b: 0x00,
}; };
pub const GREEN: Colour = Colour { pub const GREEN: Colour = Colour {
r: 0x00, r: 0x00,
g: 0xff, g: 0xff,
b: 0x00 b: 0x00,
}; };
pub const BLUE: Colour = Colour { pub const BLUE: Colour = Colour {
r: 0x00, r: 0x00,
g: 0x00, g: 0x00,
b: 0xff b: 0xff,
}; };
pub const VIOLET: Colour = Colour { pub const VIOLET: Colour = Colour {
r: 0x9b, r: 0x9b,
g: 0x26, g: 0x26,
b: 0xb6 b: 0xb6,
}; };
pub const TEAL: Colour = Colour { pub const TEAL: Colour = Colour {
r: 0x00, r: 0x00,
g: 0x7c, g: 0x7c,
b: 0x80 b: 0x80,
}; };
pub const YELLOW: Colour = Colour { pub const YELLOW: Colour = Colour {
r: 0xff, r: 0xff,
g: 0xef, g: 0xef,
b: 0x00 b: 0x00,
}; };
pub const ORANGE: Colour = Colour { pub const ORANGE: Colour = Colour {
r: 0xff, r: 0xff,
g: 0xa4, g: 0xa4,
b: 0x00 b: 0x00,
}; };
pub const GRADIENT: [Colour; 7] = [ pub const GRADIENT: [Colour; 7] = [RED, VIOLET, BLUE, TEAL, GREEN, YELLOW, ORANGE];
RED, VIOLET, BLUE, TEAL, GREEN, YELLOW, ORANGE
];
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))] #[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum AuraDeviceType { pub enum AuraDeviceType {
/// Most new laptops /// Most new laptops
#[default] #[default]
LaptopKeyboard2021 = 0, LaptopPost2021 = 0,
LaptopKeyboardPre2021 = 1, LaptopPre2021 = 1,
LaptopKeyboardTuf = 2, LaptopTuf = 2,
ScsiExtDisk = 3, ScsiExtDisk = 3,
Ally = 4, Unknown = 255,
AnimeOrSlash = 5,
Unknown = 255
} }
impl AuraDeviceType { impl AuraDeviceType {
pub fn is_old_laptop(&self) -> bool { pub fn is_old_laptop(&self) -> bool {
*self == Self::LaptopKeyboardPre2021 *self == Self::LaptopPre2021
} }
pub fn is_tuf_laptop(&self) -> bool { pub fn is_tuf_laptop(&self) -> bool {
*self == Self::LaptopKeyboardTuf *self == Self::LaptopTuf
} }
pub fn is_new_laptop(&self) -> bool { pub fn is_new_laptop(&self) -> bool {
*self == Self::LaptopKeyboard2021 *self == Self::LaptopPost2021
}
pub fn is_ally(&self) -> bool {
*self == Self::Ally
} }
pub fn is_scsi(&self) -> bool { pub fn is_scsi(&self) -> bool {
@@ -104,18 +98,16 @@ impl AuraDeviceType {
impl From<&str> for AuraDeviceType { impl From<&str> for AuraDeviceType {
fn from(s: &str) -> Self { fn from(s: &str) -> Self {
match s.to_lowercase().trim_start_matches("0x") { match s.to_lowercase().trim_start_matches("0x") {
"tuf" => AuraDeviceType::LaptopKeyboardTuf, "tuf" => AuraDeviceType::LaptopTuf,
"1932" => AuraDeviceType::ScsiExtDisk, "1932" => AuraDeviceType::ScsiExtDisk,
"1866" | "18c6" | "1869" | "1854" => Self::LaptopKeyboardPre2021, "1866" | "18c6" | "1869" | "1854" => Self::LaptopPre2021,
"1abe" | "1b4c" => Self::Ally, _ => Self::LaptopPost2021,
"19b3" | "193b" => Self::AnimeOrSlash,
"19b6" => Self::LaptopKeyboard2021,
_ => Self::Unknown
} }
} }
} }
/// The powerr zones this laptop supports /// The powerr zones this laptop supports
#[typeshare]
#[cfg_attr( #[cfg_attr(
feature = "dbus", feature = "dbus",
derive(Type, Value, OwnedValue), derive(Type, Value, OwnedValue),
@@ -134,9 +126,7 @@ pub enum PowerZones {
Lid = 3, Lid = 3,
/// The led strip on the rear of some laptops /// The led strip on the rear of some laptops
RearGlow = 4, RearGlow = 4,
/// Exists for the older 0x1866 models /// On pre-2021 laptops there is either 1 or 2 zones used
KeyboardAndLightbar = 5, KeyboardAndLightbar = 5,
/// Ally specific for creating correct packet None = 255,
Ally = 6,
None = 255
} }
+9 -6
View File
@@ -1,7 +1,10 @@
// Only these two packets must be 17 bytes // Only these two packets must be 17 bytes
pub const AURA_LAPTOP_LED_APPLY: [u8; 17] = [ pub const LED_APPLY: [u8; 17] = [0x5d, 0xb4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
0x5d, 0xb4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 pub const LED_SET: [u8; 17] = [0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
];
pub const AURA_LAPTOP_LED_SET: [u8; 17] = [ /// Writes out the correct byte string for brightness
0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 pub const fn aura_brightness_bytes(brightness: u8) -> [u8; 17] {
]; [
0x5a, 0xba, 0xc5, 0xc4, brightness, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]
}
+11 -11
View File
@@ -9,17 +9,13 @@ homepage.workspace = true
edition.workspace = true edition.workspace = true
[features] [features]
default = [] #default = ["mocking"]
mocking = [] #mocking = []
x11 = ["slint/backend-winit-x11"]
# Requires RUSTFLAGS="--cfg tokio_unstable"
tokio-debug = ["console-subscriber"]
[dependencies] [dependencies]
console-subscriber = { version = "^0.4", optional = true } nix = { version = "^0.28.0", features = ["fs"] }
tempfile = "3.3.0"
ksni = { version = "0.3", default-features = false, features = ["async-io"] } betrayer = { version = "0.2.0" }
image = "0.25.5"
asusd = { path = "../asusd" } asusd = { path = "../asusd" }
config-traits = { path = "../config-traits" } config-traits = { path = "../config-traits" }
@@ -28,7 +24,7 @@ rog_dbus = { path = "../rog-dbus" }
rog_aura = { path = "../rog-aura" } rog_aura = { path = "../rog-aura" }
rog_profiles = { path = "../rog-profiles" } rog_profiles = { path = "../rog-profiles" }
rog_platform = { path = "../rog-platform" } rog_platform = { path = "../rog-platform" }
supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", default-features = false } supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", rev = "4eb6e97c22b68ae8d1e80500709b0c0580776ad3", default-features = false }
dmi_id = { path = "../dmi-id" } dmi_id = { path = "../dmi-id" }
gumdrop.workspace = true gumdrop.workspace = true
@@ -37,11 +33,11 @@ env_logger.workspace = true
tokio.workspace = true tokio.workspace = true
serde.workspace = true serde.workspace = true
serde_derive.workspace = true
zbus.workspace = true zbus.workspace = true
dirs.workspace = true dirs.workspace = true
notify-rust.workspace = true notify-rust.workspace = true
concat-idents.workspace = true concat-idents.workspace = true
futures-util.workspace = true
versions.workspace = true versions.workspace = true
@@ -51,6 +47,7 @@ default-features = false
features = [ features = [
"gettext", "gettext",
"compat-1-2", "compat-1-2",
"backend-linuxkms",
"backend-winit-wayland", "backend-winit-wayland",
"renderer-winit-femtovg", "renderer-winit-femtovg",
# "renderer-skia-opengl", # "renderer-skia-opengl",
@@ -58,3 +55,6 @@ features = [
[build-dependencies.slint-build] [build-dependencies.slint-build]
git = "https://github.com/slint-ui/slint.git" git = "https://github.com/slint-ui/slint.git"
[dev-dependencies]
cargo-husky.workspace = true

Some files were not shown because too many files have changed in this diff Show More