Compare commits

...

90 Commits

Author SHA1 Message Date
Luke D. Jones a7b98c67ee Prep 6.1.0-rc1 2024-12-25 19:28:32 +13:00
Luke D. Jones e7bbd99178 Stop GUI thread hogging CPU 2024-12-25 11:29:10 +13:00
Luke D. Jones e2d3f99d91 Try better to capture TUF keyboard control 2024-12-25 10:31:43 +13:00
Luke D. Jones 1cad5afc0d FirmwareAttributes: further work 2024-12-24 21:50:32 +13:00
Luke D. Jones fd31ac458d Prepare for rc1 2024-12-24 18:01:19 +13:00
Luke D. Jones 6292b3361d Re-add TUF LED support 2024-12-24 17:13:05 +13:00
Luke D. Jones ab7a4bbad3 Refactor ROGCC to use dbus to communicate with self instead of pipe file 2024-12-24 17:13:05 +13:00
Luke D. Jones 0f2d89858e SCSI support: ROG Arion external drive LED control 2024-12-24 17:13:05 +13:00
Luke D. Jones 19ffcf3376 Refactor: Make all Aura type devices use "device_manager"
Open the door to adding many other types of "aura" devices later.
2024-12-24 17:13:05 +13:00
Luke D. Jones 0ddfe76c31 Minor prep 2024-12-18 11:21:40 +13:00
Luke D. Jones c05c8ba648 Fix: prevent an aura manager deadlock 2024-12-18 10:33:14 +13:00
Luke D. Jones ccdc576319 Switch tray to knsi crate. Many things to do 2024-12-17 22:14:13 +13:00
Luke Jones 39b16ffc91 Merge branch 'one-shot-charge' into 'main'
feat: One-shot full charge charge

See merge request asus-linux/asusctl!207
2024-12-14 22:07:47 +00:00
AlbertGG 72ff1ab3ab feat: One-shot full charge charge 2024-12-14 22:07:46 +00:00
Luke D. Jones e7c4619ee9 Update deps 2024-11-28 16:32:42 +13:00
Luke Jones 71fcb382ea Merge branch 'G733ZW_Aura' into 'main'
G733Z(W) Aura Update

See merge request asus-linux/asusctl!208
2024-11-21 02:46:57 +00:00
Michael Campbell e5b2e3ef11 G733Z(W) Aura Update 2024-11-21 01:25:15 +00:00
Luke Jones dfd39522a8 Merge branch 'feature/ga605w-aura' into 'main'
feat(rog-aura): add GA605W aura support

See merge request asus-linux/asusctl!206
2024-11-13 20:13:43 +00:00
Tobias Kantusch 2759c28196 feat(rog-aura): add GA605W aura support
This adds support for aura effects on GA605W laptops.
The model is basically the same as the GU605, just with
an AMD chip, so the configuration is simply copied.
2024-11-09 23:07:59 +01:00
Luke Jones 207e199016 Merge branch 'FA506N-rainbow' into 'main'
Fa506 n rainbow

See merge request asus-linux/asusctl!203
2024-11-04 08:15:30 +00:00
Luke Jones 5f8cbdb94b Merge branch 'disable-kms' into 'main'
rog-control-center: Disable slint KMS backend

See merge request asus-linux/asusctl!204
2024-11-04 07:54:11 +00:00
Luke Jones 0853c16d5e Merge branch 'GLLM1-main-patch-64861' into 'main'
Update asusd.rules for ProArt laptops

See merge request asus-linux/asusctl!205
2024-11-04 07:51:53 +00:00
GLLM 0be04726ca Update asusd.rules for ProArt laptops (tested on P16, aka H7606W) 2024-11-03 20:14:06 +00:00
Kenny Levinsen a672a86cc4 rog-control-center: Disable slint KMS backend
The slint KMS backend is meant to run an application without a display
server and with direct, exclusive display and input control, which is a
very specific use-case for e.g. embedded applications.

Disable the KMS backend and avoid a bunch of dependencies.
2024-11-02 20:54:24 +01:00
Lu Robles c7ff3b44dc FA506N: use RainbowCycle instead of RainbowWave 2024-10-26 16:42:18 +02:00
Luke Jones f0ebda9ecd Merge branch 'fa506n' into 'main'
rog-aura: fa506n: use the correct rainbow mode

See merge request asus-linux/asusctl!202
2024-10-23 14:08:45 +00:00
Sid Pranjale b984176cd0 rog-aura: fa506n: use the correct rainbow mode 2024-10-23 17:40:29 +05:30
Luke Jones 599b1cc9ad Merge branch 'fa506n' into 'main'
rog-aura: add modes for FA506N

See merge request asus-linux/asusctl!201
2024-10-23 10:37:42 +00:00
Sid Pranjale d93df8752e rog-aura: add modes for FA506N 2024-10-23 15:23:13 +05:30
Luke Jones 8a9564bbfa Merge branch 'main' into 'main'
add install instructions for debian(light fan can work for my G614JV)

See merge request asus-linux/asusctl!182
2024-09-08 08:07:21 +00:00
Pd.ch 5455e345b2 add install instructions for debian(light fan can work for my G614JV) 2024-09-08 08:07:21 +00:00
Luke D. Jones 520101fea1 rog-platform: Adjust current_value again, read and write directly to file path 2024-09-07 17:43:31 +12:00
Luke D. Jones e866b4eeb1 rog-platform: change current_value() to &mut 2024-09-07 17:05:33 +12:00
Luke D. Jones c5c46738ee Fix ally power config 2024-09-03 12:45:07 +12:00
Luke D. Jones 27ed95bd3e Fixes to config loading and updating 2024-08-31 11:16:51 +12:00
Luke D. Jones ea9ca79a8f Point release 2024-08-30 21:32:06 +12:00
Luke D. Jones 4a97f173be Unify the aura data more. Prep for better format 2024-08-30 21:29:13 +12:00
Luke D. Jones 8f35220c5f Add Ally 1 and X as "old" style devices for power
These have a slightly different power settings data which needs to be verified.
We can use the old style for now.

Closes #542
2024-08-30 18:35:26 +12:00
Luke D. Jones c3880d055d Fix cli args for led mode 2024-08-26 19:16:11 +12:00
Luke D. Jones b661f67084 Many updates 2024-08-26 17:53:10 +12:00
Luke Jones abd2ca8601 Merge branch 'main' into 'main'
Fix vendored build with feature rog-control-center/x11

See merge request asus-linux/asusctl!197
2024-08-26 04:48:12 +00:00
Luke Jones 0905ed8ad4 Merge branch 'g614jz-aura' into 'main'
Add G614JZ/JJ  (2023 Strix G16) Aura support

See merge request asus-linux/asusctl!195
2024-08-26 04:47:42 +00:00
Luke Jones c1268d4aad Merge branch 'Chaser_1-main-patch-70824' into 'main'
Fixed a typo in README.md

See merge request asus-linux/asusctl!199
2024-08-15 10:17:59 +00:00
Christian Haser 5ed47abc32 Update README.md 2024-08-15 08:36:30 +00:00
The0919 718bb8b86f Add cargo vendor-filter all-features 2024-08-03 03:11:32 -04:00
Luke D. Jones 5ab9642b79 Update G713P 2024-07-25 23:10:57 +12:00
Luke D. Jones 14acab9a9c Add Ally X config 2024-07-25 22:52:19 +12:00
Luke Jones e4dd485dd4 Merge branch 'main' into 'main'
Add FX517Z to aura_support

See merge request asus-linux/asusctl!196
2024-07-15 22:35:05 +00:00
Carl Dunning ab1d75e5ec Add FX517Z to aura_support 2024-07-15 13:30:05 +01:00
ulville e4c5df6cca Add G614JJ Aura support 2024-07-10 20:47:18 +03:00
ulville 9ee7ee26a2 Add G614JZ (2023 Strix G16) Aura support
This laptop has a 4 zone led keyboard.
Tested on my own device. G614J model
probably also has the same 4 basic zone
support but I can't test it
2024-07-10 00:47:14 +03:00
Luke Jones e8ebdacb91 Merge branch 'fhui16776-main-patch-11114' into 'main'
Add 2023 G14 model to aura_support.ron

See merge request asus-linux/asusctl!191
2024-07-01 06:31:13 +00:00
Luke Jones b97921fea2 Merge branch 'main' into 'main'
Simplified Chinese translation

See merge request asus-linux/asusctl!194
2024-06-24 00:16:41 +00:00
UM-Li a3423195a6 Simplified Chinese translation 2024-06-19 10:41:16 +02:00
Luke Jones f55edfbae0 Merge branch 'gu605-slash-support' into 'main'
Add slash detection for GU605

Closes #523

See merge request asus-linux/asusctl!193
2024-06-17 00:35:24 +00:00
banditopazzo 3e065b6715 Add slash detection for GU605 2024-06-16 17:58:49 +02:00
Luke D. Jones f14d1ad61e Prep 6.0.11 release 2024-06-09 20:11:55 +12:00
Luke D. Jones 85d4e9cabd Update aura_support.ron 2024-06-09 12:20:32 +12:00
Luke Jones e47a9cffd7 Merge branch 'aura-G513RM' into 'main'
Aura support for G513RM

See merge request asus-linux/asusctl!192
2024-06-07 22:23:56 +00:00
GingerBreadMuncher ca93dc7215 Aura support for G513RM 2024-06-07 20:52:19 +00:00
fhui16776 fhui16776 93a646773c Add 2023 G14 model to aura_support.ron 2024-06-07 09:23:22 +00:00
Luke D. Jones 1cba693469 Update G713 aura spec 2024-06-07 09:22:59 +12:00
Luke D. Jones 166149b351 Remove a debug statement 2024-05-26 21:21:54 +12:00
Luke Jones 1b11b6d8fb Merge branch 'main' into 'main'
Resolve "AniMe Broken on GA402XZ"

Closes #512

See merge request asus-linux/asusctl!190
2024-05-26 21:19:40 +12:00
Mihir Patil 02568299df Fix AniMe on GA402XZ 2024-05-26 03:58:06 -04:00
Luke D. Jones acdc93596c Ranem rainbow/strobe effects 2024-05-24 22:43:58 +12:00
Luke D. Jones 22e26adfb6 Update lang 2024-05-24 20:01:19 +12:00
Luke D. Jones 4730e645ba Fix sortof notifs 2024-05-24 18:49:23 +12:00
Luke D. Jones d203fab70d Prep 6.0.10 2024-05-24 14:01:35 +12:00
Luke D. Jones 792fae3ed7 Enable tray and notifs without supergfx 2024-05-24 13:00:56 +12:00
Luke D. Jones e443ab00c9 Adjust anime init sequence 2024-05-21 22:36:18 +12:00
Luke D. Jones aee54f5756 Adjust G513Q support to match device specs 2024-05-21 10:11:56 +12:00
Luke D. Jones 00904e9603 Don't panic if -ENODEV on fan_curve enable 2024-05-19 22:38:45 +12:00
Luke D. Jones b1212585e2 Add GA401I to aura_support
Closes #501
2024-05-18 22:56:24 +12:00
Luke D. Jones faca084cff Prep new release 2024-05-18 13:13:35 +12:00
Luke Jones 89dc0b3501 Merge branch 'tokio_console' into 'main'
Fix GUI taking 100% of CPU core

Closes #480

See merge request asus-linux/asusctl!189
2024-05-18 01:10:33 +00:00
Luke D. Jones ea988279a8 Fix GUI taking 100% of CPU core
Closes #480
2024-05-18 12:59:26 +12:00
Luke D. Jones 219bd559b6 tokio instrument 2024-05-18 11:21:33 +12:00
Luke D. Jones ad1ef9b8a2 Update deps 2024-05-17 22:31:28 +12:00
Luke D. Jones 59795c605c Add G512LI and G513RS to aura_support.ron 2024-05-17 22:14:40 +12:00
Luke D. Jones a36ac2b6d3 Add a warning log for missing laptop model 2024-05-17 22:09:30 +12:00
Luke Jones a20837f252 Merge branch '505-invalid-paths-in-etc-asusd-anime-ron-result-in-broken-anime-control' into 'main'
Resolve "Invalid paths in /etc/asusd/anime.ron result in broken anime control"

Closes #505

See merge request asus-linux/asusctl!188
2024-05-17 10:09:12 +00:00
Luke D. Jones 1353fe3fdb Rename and recreate the default Anime config if cache setup fails 2024-05-17 21:54:37 +12:00
Luke D. Jones 770bd12a5c Add G513RS to laptop DB 2024-05-17 18:56:51 +12:00
Luke Jones af2f5592f0 Merge pull request #29 from chrnin/fix/udev-for-zenbook
add Zenbook to asusd.rules
2024-05-17 11:37:36 +12:00
Luke D. Jones 9686c41ac4 Fix pipeline 2024-05-17 11:35:36 +12:00
Luke D. Jones fbdb0514d2 Prep new release 2024-05-17 10:18:54 +12:00
Luke D. Jones 1f5650d26b Add tests, G513L laptop 2024-05-17 09:41:40 +12:00
Luke D. Jones 14db97c476 Update G513 model DB entry 2024-05-15 10:04:02 +12:00
Christophe Ninucci 7122fbaca8 add Zenbook to asusd.rules 2024-04-14 11:30:39 +02:00
119 changed files with 8888 additions and 4826 deletions
+2 -2
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 cargo test --all -- --test-threads=1
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 libinput-dev libseat-dev libudev-dev libgtk-3-dev grep llvm clang libclang-dev libsdl2-dev libsdl2-gfx-dev - apt-get update -qq && apt-get install -y -qq 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 - cargo test --all -- --test-threads=1
release: release:
only: only:
+73 -5
View File
@@ -1,12 +1,80 @@
# 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.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] ## [v6.0.7]
### Changed ### Changed
Generated
+2690 -1505
View File
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -1,6 +1,6 @@
[workspace.package] [workspace.package]
version = "6.0.7" version = "6.1.0-rc1"
rust-version = "1.77" rust-version = "1.82"
license = "MPL-2.0" license = "MPL-2.0"
readme = "README.md" readme = "README.md"
authors = ["Luke <luke@ljones.dev>"] authors = ["Luke <luke@ljones.dev>"]
@@ -25,7 +25,7 @@ members = [
"rog-profiles", "rog-profiles",
"rog-control-center", "rog-control-center",
"rog-slash", "rog-slash",
"simulators", "simulators", "rog-scsi",
] ]
default-members = [ default-members = [
"asusctl", "asusctl",
@@ -36,22 +36,22 @@ default-members = [
] ]
[workspace.dependencies] [workspace.dependencies]
tokio = { version = "^1.36.0", default-features = false, features = [ tokio = { version = "^1.39.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 = "^1.3" smol = "^2.0"
mio = "0.8.11" mio = "0.8.11"
zbus = "4.1" zbus = "5.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 = "^1.0" serde = { version = "^1.0", features = ["serde_derive"] }
serde_derive = "^1.0"
ron = "*" ron = "*"
typeshare = "1.0.0" typeshare = "1.0.0"
@@ -71,9 +71,9 @@ gif = "^0.12.0"
versions = "6.2" versions = "6.2"
notify-rust = { git = "https://github.com/flukejones/notify-rust.git", rev = "54176413b81189a3e4edbdc20a0b4f7e2e35c063", default-features = false, features = [ notify-rust = { version = "4.11.0", features = ["z", "async"] }
"z",
] } sg = {git = "https://github.com/flukejones/sg-rs.git"}
[profile.release] [profile.release]
# thin = 57s, asusd = 9.0M # thin = 57s, asusd = 9.0M
+11 -6
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
@@ -19,17 +19,22 @@ LEDCFG := aura_support.ron
SRC := Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.rs') SRC := Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.rs')
STRIP_BINARIES ?= 0 STRIP_BINARIES ?= 1
DEBUG ?= 0 DEBUG ?= 0
ifeq ($(DEBUG),0) ifeq ($(DEBUG),0)
ARGS += --release --features "rog-control-center/x11" ARGS += --release
TARGET = release TARGET = release
else else
ARGS += --profile dev --features "rog-control-center/x11" ARGS += --profile dev
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
@@ -113,7 +118,7 @@ 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 --platform x86_64-unknown-linux-gnu vendor cargo vendor-filterer --all-features --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
@@ -133,7 +138,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)
ifneq ($(STRIP_BINARIES),0) ifeq ($(STRIP_BINARIES),1)
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)
+12 -3
View File
@@ -11,7 +11,7 @@ Now includes a GUI, `rog-control-center`.
## Kernel support ## Kernel support
**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/). This is especially required for 2023+ devices and possibly some lat 2022 devices. **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/). This is especially required for 2023+ devices and possibly some late 2022 devices.
Z13 devices will need [these](https://lore.kernel.org/linux-input/20240416090402.31057-1-luke@ljones.dev/T/#t) Z13 devices will need [these](https://lore.kernel.org/linux-input/20240416090402.31057-1-luke@ljones.dev/T/#t)
@@ -84,7 +84,7 @@ Rust and cargo are required, they can be installed from [rustup.rs](https://rust
**fedora:** **fedora:**
dnf install cmake clang-devel libinput-devel libseat-devel libgbm-devel libxkbcommon-devel systemd-devel libdrm-devel expat-devel pcre2-devel libzstd-devel gtk3-devel dnf install cmake clang-devel libxkbcommon-devel systemd-devel expat-devel pcre2-devel libzstd-devel gtk3-devel
make make
sudo make install sudo make install
@@ -93,7 +93,16 @@ 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 libinput-devel libseat-devel libgbm-devel libxkbcommon-devel systemd-devel libdrm-devel expat-devel pcre2-devel libzstd-devel gtk3-devel zypper in rustup make cmake clang-devel libxkbcommon-devel systemd-devel expat-devel pcre2-devel libzstd-devel gtk3-devel
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
+1 -2
View File
@@ -10,12 +10,12 @@ 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" }
ron.workspace = true ron.workspace = true
@@ -25,4 +25,3 @@ zbus.workspace = true
[dev-dependencies] [dev-dependencies]
rog_dbus = { path = "../rog-dbus" } rog_dbus = { path = "../rog-dbus" }
cargo-husky.workspace = true
+1 -1
View File
@@ -26,7 +26,7 @@ fn main() -> Result<(), Box<dyn Error>> {
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().unwrap(); let anime_type = get_anime_type();
proxy proxy
.write(matrix.into_data_buffer(anime_type).unwrap()) .write(matrix.into_data_buffer(anime_type).unwrap())
.unwrap(); .unwrap();
+1 -1
View File
@@ -19,7 +19,7 @@ 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().unwrap(); let anime_type = get_anime_type();
let mut seq = Sequences::new(anime_type); let mut seq = Sequences::new(anime_type);
seq.insert( seq.insert(
0, 0,
+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().unwrap(); let anime_type = get_anime_type();
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().unwrap(); let anime_type = get_anime_type();
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() {
+1 -1
View File
@@ -20,7 +20,7 @@ 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(),
+1 -1
View File
@@ -23,7 +23,7 @@ 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(),
+8 -6
View File
@@ -41,6 +41,8 @@ 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)]
@@ -219,9 +221,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")]
Strobe(SingleSpeed), // 2 RainbowCycle(SingleSpeed), // 2
#[options(help = "rainbow cycling in one of four directions")] #[options(help = "rainbow cycling in one of four directions")]
Rainbow(SingleSpeedDirection), // 3 RainbowWave(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")]
@@ -312,14 +314,14 @@ impl From<&SetAuraBuiltin> for AuraEffect {
data.mode = AuraModeNum::Breathe; data.mode = AuraModeNum::Breathe;
data data
} }
SetAuraBuiltin::Strobe(x) => { SetAuraBuiltin::RainbowCycle(x) => {
let mut data: AuraEffect = x.into(); let mut data: AuraEffect = x.into();
data.mode = AuraModeNum::Strobe; data.mode = AuraModeNum::RainbowCycle;
data data
} }
SetAuraBuiltin::Rainbow(x) => { SetAuraBuiltin::RainbowWave(x) => {
let mut data: AuraEffect = x.into(); let mut data: AuraEffect = x.into();
data.mode = AuraModeNum::Rainbow; data.mode = AuraModeNum::RainbowWave;
data data
} }
SetAuraBuiltin::Stars(x) => { SetAuraBuiltin::Stars(x) => {
+10 -5
View File
@@ -4,6 +4,7 @@ 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)]
@@ -22,6 +23,8 @@ 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>,
} }
@@ -29,11 +32,11 @@ pub struct CliStart {
#[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")]
LedMode(LedModeCommand), Aura(LedModeCommand),
#[options(help = "Set the LED power states")] #[options(help = "Set the LED power states")]
LedPow1(LedPowerCommand1), AuraPowerOld(LedPowerCommand1),
#[options(help = "Set the LED power states")] #[options(help = "Set the LED power states")]
LedPow2(LedPowerCommand2), AuraPower(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")]
@@ -44,8 +47,10 @@ 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")]
Scsi(ScsiCommand),
#[options(help = "Change bios settings")] #[options(help = "Change bios settings")]
Bios(BiosCommand), Platform(PlatformCommand),
} }
#[derive(Debug, Clone, Options)] #[derive(Debug, Clone, Options)]
@@ -85,7 +90,7 @@ pub struct GraphicsCommand {
} }
#[derive(Options, Debug)] #[derive(Options, Debug)]
pub struct BiosCommand { pub struct PlatformCommand {
#[options(help = "print help message")] #[options(help = "print help message")]
pub help: bool, pub help: bool,
#[options( #[options(
+321 -221
View File
@@ -5,7 +5,6 @@ use std::process::Command;
use std::thread::sleep; use std::thread::sleep;
use anime_cli::{AnimeActions, AnimeCommand}; use anime_cli::{AnimeActions, AnimeCommand};
use asusd::ctrl_fancurves::FAN_CURVE_ZBUS_NAME;
use aura_cli::{LedPowerCommand1, LedPowerCommand2}; use aura_cli::{LedPowerCommand1, LedPowerCommand2};
use dmi_id::DMIID; use dmi_id::DMIID;
use fan_curve_cli::FanCurveCommand; use fan_curve_cli::FanCurveCommand;
@@ -15,6 +14,7 @@ use rog_anime::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage,
use rog_aura::keyboard::{AuraPowerState, LaptopAuraPower}; use rog_aura::keyboard::{AuraPowerState, LaptopAuraPower};
use rog_aura::{self, AuraDeviceType, AuraEffect, PowerZones}; use rog_aura::{self, AuraDeviceType, AuraEffect, PowerZones};
use rog_dbus::list_iface_blocking; use rog_dbus::list_iface_blocking;
use rog_dbus::scsi_aura::ScsiAuraProxyBlocking;
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::zbus_fan_curves::FanCurvesProxyBlocking; use rog_dbus::zbus_fan_curves::FanCurvesProxyBlocking;
@@ -22,8 +22,11 @@ use rog_dbus::zbus_platform::PlatformProxyBlocking;
use rog_dbus::zbus_slash::SlashProxyBlocking; use rog_dbus::zbus_slash::SlashProxyBlocking;
use rog_platform::platform::{GpuMode, Properties, ThrottlePolicy}; use rog_platform::platform::{GpuMode, Properties, ThrottlePolicy};
use rog_profiles::error::ProfileError; use rog_profiles::error::ProfileError;
use rog_scsi::AuraMode;
use rog_slash::SlashMode; use rog_slash::SlashMode;
use ron::ser::PrettyConfig; use ron::ser::PrettyConfig;
use scsi_cli::ScsiCommand;
use zbus::blocking::proxy::ProxyImpl;
use zbus::blocking::Connection; use zbus::blocking::Connection;
use crate::aura_cli::{AuraPowerStates, LedBrightness}; use crate::aura_cli::{AuraPowerStates, LedBrightness};
@@ -34,9 +37,12 @@ mod anime_cli;
mod aura_cli; mod aura_cli;
mod cli_opts; mod cli_opts;
mod fan_curve_cli; mod fan_curve_cli;
mod scsi_cli;
mod slash_cli; mod slash_cli;
fn main() { fn main() {
let self_version = env!("CARGO_PKG_VERSION");
println!("Starting version {self_version}");
let args: Vec<String> = args().skip(1).collect(); let args: Vec<String> = args().skip(1).collect();
let missing_argument_k = gumdrop::Error::missing_argument(Opt::Short('k')); let missing_argument_k = gumdrop::Error::missing_argument(Opt::Short('k'));
@@ -58,7 +64,6 @@ fn main() {
println!("\nError: {e}\n"); println!("\nError: {e}\n");
print_info(); print_info();
}) { }) {
let self_version = env!("CARGO_PKG_VERSION");
let asusd_version = platform_proxy.version().unwrap(); let asusd_version = platform_proxy.version().unwrap();
if asusd_version != self_version { if asusd_version != self_version {
println!("Version mismatch: asusctl = {self_version}, asusd = {asusd_version}"); println!("Version mismatch: asusctl = {self_version}, asusd = {asusd_version}");
@@ -122,31 +127,33 @@ fn check_service(name: &str) -> bool {
false false
} }
fn find_aura_iface() -> Result<Vec<AuraProxyBlocking<'static>>, Box<dyn std::error::Error>> { fn find_iface<T>(iface_name: &str) -> Result<Vec<T>, Box<dyn std::error::Error>>
where
T: ProxyImpl<'static> + From<zbus::Proxy<'static>>,
{
let conn = zbus::blocking::Connection::system().unwrap(); let conn = zbus::blocking::Connection::system().unwrap();
let f = let f =
zbus::blocking::fdo::ObjectManagerProxy::new(&conn, "org.asuslinux.Daemon", "/").unwrap(); zbus::blocking::fdo::ObjectManagerProxy::new(&conn, "org.asuslinux.Daemon", "/").unwrap();
let interfaces = f.get_managed_objects().unwrap(); let interfaces = f.get_managed_objects().unwrap();
let mut aura_paths = Vec::new(); let mut paths = Vec::new();
for v in interfaces.iter() { for v in interfaces.iter() {
// let o: Vec<zbus::names::OwnedInterfaceName> = v.1.keys().map(|e| // let o: Vec<zbus::names::OwnedInterfaceName> = v.1.keys().map(|e|
// e.to_owned()).collect(); println!("{}, {:?}", v.0, o); // e.to_owned()).collect(); println!("{}, {:?}", v.0, o);
for k in v.1.keys() { for k in v.1.keys() {
if k.as_str() == "org.asuslinux.Aura" { if k.as_str() == iface_name {
println!("Found aura device at {}, {}", v.0, k); println!("Found {iface_name} device at {}, {}", v.0, k);
aura_paths.push(v.0.clone()); paths.push(v.0.clone());
} }
} }
} }
if aura_paths.len() > 1 { if paths.len() > 1 {
println!("Multiple aura devices found: {aura_paths:?}"); println!("Multiple aura devices found: {paths:?}");
println!("TODO: enable selection");
} }
if !aura_paths.is_empty() { if !paths.is_empty() {
let mut ctrl = Vec::new(); let mut ctrl = Vec::new();
for path in aura_paths { for path in paths {
ctrl.push( ctrl.push(
AuraProxyBlocking::builder(&conn) T::builder(&conn)
.path(path.clone())? .path(path.clone())?
.destination("org.asuslinux.Daemon")? .destination("org.asuslinux.Daemon")?
.build()?, .build()?,
@@ -165,19 +172,20 @@ fn do_parsed(
conn: Connection, conn: Connection,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
match &parsed.command { match &parsed.command {
Some(CliCommand::LedMode(mode)) => handle_led_mode(&find_aura_iface()?, mode)?, Some(CliCommand::Aura(mode)) => handle_led_mode(mode)?,
Some(CliCommand::LedPow1(pow)) => handle_led_power1(&find_aura_iface()?, pow)?, Some(CliCommand::AuraPowerOld(pow)) => handle_led_power1(pow)?,
Some(CliCommand::LedPow2(pow)) => handle_led_power2(&find_aura_iface()?, pow)?, Some(CliCommand::AuraPower(pow)) => handle_led_power2(pow)?,
Some(CliCommand::Profile(cmd)) => { Some(CliCommand::Profile(cmd)) => {
handle_throttle_profile(&conn, supported_properties, cmd)? handle_throttle_profile(&conn, supported_properties, cmd)?
} }
Some(CliCommand::FanCurve(cmd)) => { Some(CliCommand::FanCurve(cmd)) => {
handle_fan_curve(&conn, supported_interfaces, cmd)?; handle_fan_curve(&conn, cmd)?;
} }
Some(CliCommand::Graphics(_)) => do_gfx(), Some(CliCommand::Graphics(_)) => do_gfx(),
Some(CliCommand::Anime(cmd)) => handle_anime(&conn, cmd)?, Some(CliCommand::Anime(cmd)) => handle_anime(cmd)?,
Some(CliCommand::Slash(cmd)) => handle_slash(&conn, cmd)?, Some(CliCommand::Slash(cmd)) => handle_slash(cmd)?,
Some(CliCommand::Bios(cmd)) => { Some(CliCommand::Scsi(cmd)) => handle_scsi(cmd)?,
Some(CliCommand::Platform(cmd)) => {
handle_platform_properties(&conn, supported_properties, cmd)? handle_platform_properties(&conn, supported_properties, cmd)?
} }
None => { None => {
@@ -185,22 +193,24 @@ fn do_parsed(
&& parsed.kbd_bright.is_none() && parsed.kbd_bright.is_none()
&& parsed.chg_limit.is_none() && parsed.chg_limit.is_none()
&& !parsed.next_kbd_bright && !parsed.next_kbd_bright
&& !parsed.prev_kbd_bright) && !parsed.prev_kbd_bright
&& !parsed.one_shot_chg)
|| parsed.help || parsed.help
{ {
println!("{}", CliStart::usage()); println!("{}", CliStart::usage());
println!(); println!();
if let Some(cmdlist) = CliStart::command_list() { if let Some(cmdlist) = CliStart::command_list() {
let dev_type = if let Ok(proxy) = find_aura_iface() { let dev_type =
// TODO: commands on all? if let Ok(proxy) = find_iface::<AuraProxyBlocking>("org.asuslinux.Aura") {
proxy // TODO: commands on all?
.first() proxy
.unwrap() .first()
.device_type() .unwrap()
.unwrap_or(AuraDeviceType::Unknown) .device_type()
} else { .unwrap_or(AuraDeviceType::Unknown)
AuraDeviceType::Unknown } else {
}; AuraDeviceType::Unknown
};
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_owned()).collect(); let commands: Vec<String> = cmdlist.lines().map(|s| s.to_owned()).collect();
for command in commands.iter().filter(|command| { for command in commands.iter().filter(|command| {
if command.trim().starts_with("fan-curve") if command.trim().starts_with("fan-curve")
@@ -210,6 +220,12 @@ fn do_parsed(
return false; return false;
} }
if command.trim().starts_with("aura")
&& !supported_interfaces.contains(&"org.asuslinux.Aura".to_string())
{
return false;
}
if command.trim().starts_with("anime") if command.trim().starts_with("anime")
&& !supported_interfaces.contains(&"org.asuslinux.Anime".to_string()) && !supported_interfaces.contains(&"org.asuslinux.Anime".to_string())
{ {
@@ -222,7 +238,7 @@ fn do_parsed(
return false; return false;
} }
if command.trim().starts_with("bios") if command.trim().starts_with("platform")
&& !supported_interfaces.contains(&"org.asuslinux.Platform".to_string()) && !supported_interfaces.contains(&"org.asuslinux.Platform".to_string())
{ {
return false; return false;
@@ -230,11 +246,11 @@ fn do_parsed(
if !dev_type.is_old_laptop() if !dev_type.is_old_laptop()
&& !dev_type.is_tuf_laptop() && !dev_type.is_tuf_laptop()
&& command.trim().starts_with("led-pow-1") && command.trim().starts_with("aura-power-old")
{ {
return false; return false;
} }
if !dev_type.is_new_laptop() && command.trim().starts_with("led-pow-2") { if !dev_type.is_new_laptop() && command.trim().starts_with("aura-power") {
return false; return false;
} }
true true
@@ -251,7 +267,7 @@ fn do_parsed(
} }
if let Some(brightness) = &parsed.kbd_bright { if let Some(brightness) = &parsed.kbd_bright {
if let Ok(aura) = find_aura_iface() { if let Ok(aura) = find_iface::<AuraProxyBlocking>("org.asuslinux.Aura") {
for aura in aura.iter() { for aura in aura.iter() {
match brightness.level() { match brightness.level() {
None => { None => {
@@ -267,7 +283,7 @@ fn do_parsed(
} }
if parsed.next_kbd_bright { if parsed.next_kbd_bright {
if let Ok(aura) = find_aura_iface() { if let Ok(aura) = find_iface::<AuraProxyBlocking>("org.asuslinux.Aura") {
for aura in aura.iter() { for aura in aura.iter() {
let brightness = aura.brightness()?; let brightness = aura.brightness()?;
aura.set_brightness(brightness.next())?; aura.set_brightness(brightness.next())?;
@@ -278,7 +294,7 @@ fn do_parsed(
} }
if parsed.prev_kbd_bright { if parsed.prev_kbd_bright {
if let Ok(aura) = find_aura_iface() { if let Ok(aura) = find_iface::<AuraProxyBlocking>("org.asuslinux.Aura") {
for aura in aura.iter() { for aura in aura.iter() {
let brightness = aura.brightness()?; let brightness = aura.brightness()?;
aura.set_brightness(brightness.prev())?; aura.set_brightness(brightness.prev())?;
@@ -294,7 +310,7 @@ fn do_parsed(
"Supported Platform Properties:\n{:#?}", "Supported Platform Properties:\n{:#?}",
supported_properties supported_properties
); );
if let Ok(aura) = find_aura_iface() { if let Ok(aura) = find_iface::<AuraProxyBlocking>("org.asuslinux.Aura") {
// TODO: multiple RGB check // TODO: multiple RGB check
let bright = aura.first().unwrap().supported_brightness()?; let bright = aura.first().unwrap().supported_brightness()?;
let modes = aura.first().unwrap().supported_basic_modes()?; let modes = aura.first().unwrap().supported_basic_modes()?;
@@ -314,6 +330,11 @@ fn do_parsed(
proxy.set_charge_control_end_threshold(chg_limit)?; proxy.set_charge_control_end_threshold(chg_limit)?;
} }
if parsed.one_shot_chg {
let proxy = PlatformProxyBlocking::new(&conn)?;
proxy.one_shot_full_charge()?;
}
Ok(()) Ok(())
} }
@@ -325,7 +346,7 @@ fn do_gfx() {
println!("This command will be removed in future"); println!("This command will be removed in future");
} }
fn handle_anime(conn: &Connection, cmd: &AnimeCommand) -> Result<(), Box<dyn std::error::Error>> { fn handle_anime(cmd: &AnimeCommand) -> Result<(), Box<dyn std::error::Error>> {
if (cmd.command.is_none() if (cmd.command.is_none()
&& cmd.enable_display.is_none() && cmd.enable_display.is_none()
&& cmd.enable_powersave_anim.is_none() && cmd.enable_powersave_anim.is_none()
@@ -342,165 +363,169 @@ fn handle_anime(conn: &Connection, cmd: &AnimeCommand) -> Result<(), Box<dyn std
println!("\n{}", lst); println!("\n{}", lst);
} }
} }
let proxy = AnimeProxyBlocking::new(conn)?; let animes = find_iface::<AnimeProxyBlocking>("org.asuslinux.Anime")?;
if let Some(enable) = cmd.enable_display { for proxy in animes {
proxy.set_enable_display(enable)?; if let Some(enable) = cmd.enable_display {
} proxy.set_enable_display(enable)?;
if let Some(enable) = cmd.enable_powersave_anim { }
proxy.set_builtins_enabled(enable)?; if let Some(enable) = cmd.enable_powersave_anim {
} proxy.set_builtins_enabled(enable)?;
if let Some(bright) = cmd.brightness { }
proxy.set_brightness(bright)?; if let Some(bright) = cmd.brightness {
} proxy.set_brightness(bright)?;
if let Some(enable) = cmd.off_when_lid_closed { }
proxy.set_off_when_lid_closed(enable)?; if let Some(enable) = cmd.off_when_lid_closed {
} proxy.set_off_when_lid_closed(enable)?;
if let Some(enable) = cmd.off_when_suspended { }
proxy.set_off_when_suspended(enable)?; if let Some(enable) = cmd.off_when_suspended {
} proxy.set_off_when_suspended(enable)?;
if let Some(enable) = cmd.off_when_unplugged { }
proxy.set_off_when_unplugged(enable)?; if let Some(enable) = cmd.off_when_unplugged {
} proxy.set_off_when_unplugged(enable)?;
if cmd.off_with_his_head.is_some() { }
println!("Did Alice _really_ make it back from Wonderland?"); if cmd.off_with_his_head.is_some() {
} println!("Did Alice _really_ make it back from Wonderland?");
let mut anime_type = get_anime_type()?;
if let AnimeType::Unknown = anime_type {
if let Some(model) = cmd.override_type {
anime_type = model;
} }
}
if cmd.clear { let mut anime_type = get_anime_type();
let data = vec![255u8; anime_type.data_length()]; if let AnimeType::Unsupported = anime_type {
let tmp = AnimeDataBuffer::from_vec(anime_type, data)?; if let Some(model) = cmd.override_type {
proxy.write(tmp)?; anime_type = model;
}
if let Some(action) = cmd.command.as_ref() {
match action {
AnimeActions::Image(image) => {
if image.help_requested() || image.path.is_empty() {
println!("Missing arg or command\n\n{}", image.self_usage());
if let Some(lst) = image.self_command_list() {
println!("\n{}", lst);
}
return Ok(());
}
verify_brightness(image.bright);
let matrix = AnimeImage::from_png(
Path::new(&image.path),
image.scale,
image.angle,
Vec2::new(image.x_pos, image.y_pos),
image.bright,
anime_type,
)?;
proxy.write(<AnimeDataBuffer>::try_from(&matrix)?)?;
} }
AnimeActions::PixelImage(image) => { }
if image.help_requested() || image.path.is_empty() {
println!("Missing arg or command\n\n{}", image.self_usage()); if cmd.clear {
if let Some(lst) = image.self_command_list() { let data = vec![255u8; anime_type.data_length()];
println!("\n{}", lst); let tmp = AnimeDataBuffer::from_vec(anime_type, data)?;
proxy.write(tmp)?;
}
if let Some(action) = cmd.command.as_ref() {
match action {
AnimeActions::Image(image) => {
if image.help_requested() || image.path.is_empty() {
println!("Missing arg or command\n\n{}", image.self_usage());
if let Some(lst) = image.self_command_list() {
println!("\n{}", lst);
}
return Ok(());
} }
return Ok(()); verify_brightness(image.bright);
let matrix = AnimeImage::from_png(
Path::new(&image.path),
image.scale,
image.angle,
Vec2::new(image.x_pos, image.y_pos),
image.bright,
anime_type,
)?;
proxy.write(<AnimeDataBuffer>::try_from(&matrix)?)?;
} }
verify_brightness(image.bright); AnimeActions::PixelImage(image) => {
if image.help_requested() || image.path.is_empty() {
let matrix = AnimeDiagonal::from_png( println!("Missing arg or command\n\n{}", image.self_usage());
Path::new(&image.path), if let Some(lst) = image.self_command_list() {
None, println!("\n{}", lst);
image.bright, }
anime_type, return Ok(());
)?;
proxy.write(matrix.into_data_buffer(anime_type)?)?;
}
AnimeActions::Gif(gif) => {
if gif.help_requested() || gif.path.is_empty() {
println!("Missing arg or command\n\n{}", gif.self_usage());
if let Some(lst) = gif.self_command_list() {
println!("\n{}", lst);
} }
return Ok(()); verify_brightness(image.bright);
let matrix = AnimeDiagonal::from_png(
Path::new(&image.path),
None,
image.bright,
anime_type,
)?;
proxy.write(matrix.into_data_buffer(anime_type)?)?;
} }
verify_brightness(gif.bright); AnimeActions::Gif(gif) => {
if gif.help_requested() || gif.path.is_empty() {
let matrix = AnimeGif::from_gif( println!("Missing arg or command\n\n{}", gif.self_usage());
Path::new(&gif.path), if let Some(lst) = gif.self_command_list() {
gif.scale, println!("\n{}", lst);
gif.angle, }
Vec2::new(gif.x_pos, gif.y_pos), return Ok(());
AnimTime::Count(1),
gif.bright,
anime_type,
)?;
let mut loops = gif.loops as i32;
loop {
for frame in matrix.frames() {
proxy.write(frame.frame().clone())?;
sleep(frame.delay());
} }
if loops >= 0 { verify_brightness(gif.bright);
loops -= 1;
} let matrix = AnimeGif::from_gif(
if loops == 0 { Path::new(&gif.path),
break; gif.scale,
gif.angle,
Vec2::new(gif.x_pos, gif.y_pos),
AnimTime::Count(1),
gif.bright,
anime_type,
)?;
let mut loops = gif.loops as i32;
loop {
for frame in matrix.frames() {
proxy.write(frame.frame().clone())?;
sleep(frame.delay());
}
if loops >= 0 {
loops -= 1;
}
if loops == 0 {
break;
}
} }
} }
} AnimeActions::PixelGif(gif) => {
AnimeActions::PixelGif(gif) => { if gif.help_requested() || gif.path.is_empty() {
if gif.help_requested() || gif.path.is_empty() { println!("Missing arg or command\n\n{}", gif.self_usage());
println!("Missing arg or command\n\n{}", gif.self_usage()); if let Some(lst) = gif.self_command_list() {
if let Some(lst) = gif.self_command_list() { println!("\n{}", lst);
println!("\n{}", lst); }
return Ok(());
} }
return Ok(()); verify_brightness(gif.bright);
}
verify_brightness(gif.bright);
let matrix = AnimeGif::from_diagonal_gif( let matrix = AnimeGif::from_diagonal_gif(
Path::new(&gif.path), Path::new(&gif.path),
AnimTime::Count(1), AnimTime::Count(1),
gif.bright, gif.bright,
anime_type, anime_type,
)?; )?;
let mut loops = gif.loops as i32; let mut loops = gif.loops as i32;
loop { loop {
for frame in matrix.frames() { for frame in matrix.frames() {
proxy.write(frame.frame().clone())?; proxy.write(frame.frame().clone())?;
sleep(frame.delay()); sleep(frame.delay());
} }
if loops >= 0 { if loops >= 0 {
loops -= 1; loops -= 1;
} }
if loops == 0 { if loops == 0 {
break; break;
}
} }
} }
} AnimeActions::SetBuiltins(builtins) => {
AnimeActions::SetBuiltins(builtins) => { if builtins.help_requested() || builtins.set.is_none() {
if builtins.help_requested() || builtins.set.is_none() { println!(
println!("\nAny unspecified args will be set to default (first shown var)\n"); "\nAny unspecified args will be set to default (first shown var)\n"
println!("\n{}", builtins.self_usage()); );
if let Some(lst) = builtins.self_command_list() { println!("\n{}", builtins.self_usage());
println!("\n{}", lst); if let Some(lst) = builtins.self_command_list() {
println!("\n{}", lst);
}
return Ok(());
} }
return Ok(());
}
proxy.set_builtin_animations(rog_anime::Animations { proxy.set_builtin_animations(rog_anime::Animations {
boot: builtins.boot, boot: builtins.boot,
awake: builtins.awake, awake: builtins.awake,
sleep: builtins.sleep, sleep: builtins.sleep,
shutdown: builtins.shutdown, shutdown: builtins.shutdown,
})?; })?;
}
} }
} }
} }
@@ -516,7 +541,7 @@ fn verify_brightness(brightness: f32) {
} }
} }
fn handle_slash(conn: &Connection, cmd: &SlashCommand) -> Result<(), Box<dyn std::error::Error>> { fn handle_slash(cmd: &SlashCommand) -> Result<(), Box<dyn std::error::Error>> {
if (cmd.brightness.is_none() if (cmd.brightness.is_none()
&& cmd.interval.is_none() && cmd.interval.is_none()
&& cmd.slash_mode.is_none() && cmd.slash_mode.is_none()
@@ -530,21 +555,24 @@ fn handle_slash(conn: &Connection, cmd: &SlashCommand) -> Result<(), Box<dyn std
println!("\n{}", lst); println!("\n{}", lst);
} }
} }
let proxy = SlashProxyBlocking::new(conn)?;
if cmd.enable { let slashes = find_iface::<SlashProxyBlocking>("org.asuslinux.Slash")?;
proxy.set_enabled(true)?; for proxy in slashes {
} if cmd.enable {
if cmd.disable { proxy.set_enabled(true)?;
proxy.set_enabled(false)?; }
} if cmd.disable {
if let Some(brightness) = cmd.brightness { proxy.set_enabled(false)?;
proxy.set_brightness(brightness)?; }
} if let Some(brightness) = cmd.brightness {
if let Some(interval) = cmd.interval { proxy.set_brightness(brightness)?;
proxy.set_interval(interval)?; }
} if let Some(interval) = cmd.interval {
if let Some(slash_mode) = cmd.slash_mode { proxy.set_interval(interval)?;
proxy.set_slash_mode(slash_mode)?; }
if let Some(slash_mode) = cmd.slash_mode {
proxy.set_slash_mode(slash_mode)?;
}
} }
if cmd.list { if cmd.list {
let res = SlashMode::list(); let res = SlashMode::list();
@@ -556,10 +584,80 @@ fn handle_slash(conn: &Connection, cmd: &SlashCommand) -> Result<(), Box<dyn std
Ok(()) Ok(())
} }
fn handle_led_mode( fn handle_scsi(cmd: &ScsiCommand) -> Result<(), Box<dyn std::error::Error>> {
aura: &[AuraProxyBlocking], if (!cmd.list && cmd.enable.is_none() && cmd.mode.is_none() && cmd.colours.is_empty())
mode: &LedModeCommand, || cmd.help
) -> Result<(), Box<dyn std::error::Error>> { {
println!("Missing arg or command\n\n{}", cmd.self_usage());
if let Some(lst) = cmd.self_command_list() {
println!("\n{}", lst);
}
}
let scsis = find_iface::<ScsiAuraProxyBlocking>("org.asuslinux.ScsiAura")?;
for scsi in scsis {
if let Some(enable) = cmd.enable {
scsi.set_enabled(enable)?;
}
if let Some(mode) = cmd.mode {
dbg!(mode as u8);
scsi.set_led_mode(mode).unwrap();
}
let mut mode = scsi.led_mode_data()?;
let mut do_update = false;
if !cmd.colours.is_empty() {
let mut count = 0;
for c in &cmd.colours {
if count == 0 {
mode.colour1 = *c;
}
if count == 1 {
mode.colour2 = *c;
}
if count == 2 {
mode.colour3 = *c;
}
if count == 3 {
mode.colour4 = *c;
}
count += 1;
}
do_update = true;
}
if let Some(speed) = cmd.speed {
mode.speed = speed;
do_update = true;
}
if let Some(dir) = cmd.direction {
mode.direction = dir;
do_update = true;
}
if do_update {
scsi.set_led_mode_data(mode.clone())?;
}
// let mode_ret = scsi.led_mode_data()?;
// assert_eq!(mode, mode_ret);
println!("{mode}");
}
if cmd.list {
let res = AuraMode::list();
for p in &res {
println!("{:?}", p);
}
}
Ok(())
}
fn handle_led_mode(mode: &LedModeCommand) -> Result<(), Box<dyn std::error::Error>> {
if mode.command.is_none() && !mode.prev_mode && !mode.next_mode { if mode.command.is_none() && !mode.prev_mode && !mode.next_mode {
if !mode.help { if !mode.help {
println!("Missing arg or command\n"); println!("Missing arg or command\n");
@@ -570,13 +668,15 @@ fn handle_led_mode(
if let Some(cmdlist) = LedModeCommand::command_list() { if let Some(cmdlist) = LedModeCommand::command_list() {
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_owned()).collect(); let commands: Vec<String> = cmdlist.lines().map(|s| s.to_owned()).collect();
// TODO: multiple rgb check // TODO: multiple rgb check
let aura = find_iface::<AuraProxyBlocking>("org.asuslinux.Aura")?;
let modes = aura.first().unwrap().supported_basic_modes()?; let modes = aura.first().unwrap().supported_basic_modes()?;
for command in commands.iter().filter(|command| { for command in commands.iter().filter(|command| {
for mode in &modes { for mode in &modes {
if command let mut mode = <&str>::from(mode).to_string();
.trim() if let Some(pos) = mode.chars().skip(1).position(|c| c.is_uppercase()) {
.starts_with(&<&str>::from(mode).to_lowercase()) mode.insert(pos + 1, '-');
{ }
if command.trim().starts_with(&mode.to_lowercase()) {
return true; return true;
} }
} }
@@ -598,6 +698,7 @@ fn handle_led_mode(
println!("Please specify either next or previous"); println!("Please specify either next or previous");
return Ok(()); return Ok(());
} }
let aura = find_iface::<AuraProxyBlocking>("org.asuslinux.Aura")?;
if mode.next_mode { if mode.next_mode {
for aura in aura { for aura in aura {
let mode = aura.led_mode()?; let mode = aura.led_mode()?;
@@ -633,10 +734,8 @@ fn handle_led_mode(
Ok(()) Ok(())
} }
fn handle_led_power1( fn handle_led_power1(power: &LedPowerCommand1) -> Result<(), Box<dyn std::error::Error>> {
aura: &[AuraProxyBlocking], let aura = find_iface::<AuraProxyBlocking>("org.asuslinux.Aura")?;
power: &LedPowerCommand1,
) -> Result<(), Box<dyn std::error::Error>> {
for aura in aura { for aura in aura {
let dev_type = aura.device_type()?; let dev_type = aura.device_type()?;
if !dev_type.is_old_laptop() && !dev_type.is_tuf_laptop() { if !dev_type.is_old_laptop() && !dev_type.is_tuf_laptop() {
@@ -657,7 +756,7 @@ fn handle_led_power1(
} }
if dev_type.is_old_laptop() || dev_type.is_tuf_laptop() { if dev_type.is_old_laptop() || dev_type.is_tuf_laptop() {
handle_led_power_1_do_1866(aura, power)?; handle_led_power_1_do_1866(&aura, power)?;
return Ok(()); return Ok(());
} }
} }
@@ -695,10 +794,8 @@ fn handle_led_power_1_do_1866(
Ok(()) Ok(())
} }
fn handle_led_power2( fn handle_led_power2(power: &LedPowerCommand2) -> Result<(), Box<dyn std::error::Error>> {
aura: &[AuraProxyBlocking], let aura = find_iface::<AuraProxyBlocking>("org.asuslinux.Aura")?;
power: &LedPowerCommand2,
) -> Result<(), Box<dyn std::error::Error>> {
for aura in aura { for aura in aura {
let dev_type = aura.device_type()?; let dev_type = aura.device_type()?;
if !dev_type.is_new_laptop() { if !dev_type.is_new_laptop() {
@@ -750,6 +847,7 @@ fn handle_led_power2(
aura_cli::SetAuraZoneEnabled::Lightbar(l) => set(PowerZones::Lightbar, l), aura_cli::SetAuraZoneEnabled::Lightbar(l) => set(PowerZones::Lightbar, l),
aura_cli::SetAuraZoneEnabled::Lid(l) => set(PowerZones::Lid, l), aura_cli::SetAuraZoneEnabled::Lid(l) => set(PowerZones::Lid, l),
aura_cli::SetAuraZoneEnabled::RearGlow(r) => set(PowerZones::RearGlow, r), aura_cli::SetAuraZoneEnabled::RearGlow(r) => set(PowerZones::RearGlow, r),
aura_cli::SetAuraZoneEnabled::Ally(r) => set(PowerZones::Ally, r),
} }
} }
@@ -807,13 +905,13 @@ fn handle_throttle_profile(
fn handle_fan_curve( fn handle_fan_curve(
conn: &Connection, conn: &Connection,
supported: &[String],
cmd: &FanCurveCommand, cmd: &FanCurveCommand,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
if !supported.contains(&FAN_CURVE_ZBUS_NAME.to_string()) { let Ok(fan_proxy) = FanCurvesProxyBlocking::new(conn).map_err(|e| {
println!("Fan-curves not supported by either this kernel or by the laptop."); println!("Fan-curves not supported by either this kernel or by the laptop: {e:?}");
}) else {
return Err(ProfileError::NotSupported.into()); return Err(ProfileError::NotSupported.into());
} };
if !cmd.get_enabled && !cmd.default && cmd.mod_profile.is_none() { if !cmd.get_enabled && !cmd.default && cmd.mod_profile.is_none() {
if !cmd.help { if !cmd.help {
@@ -838,7 +936,6 @@ fn handle_fan_curve(
} }
let plat_proxy = PlatformProxyBlocking::new(conn)?; let plat_proxy = PlatformProxyBlocking::new(conn)?;
let fan_proxy = FanCurvesProxyBlocking::new(conn)?;
if cmd.get_enabled { if cmd.get_enabled {
let profile = plat_proxy.throttle_thermal_policy()?; let profile = plat_proxy.throttle_thermal_policy()?;
let curves = fan_proxy.fan_curve_data(profile)?; let curves = fan_proxy.fan_curve_data(profile)?;
@@ -887,7 +984,7 @@ fn handle_fan_curve(
fn handle_platform_properties( fn handle_platform_properties(
conn: &Connection, conn: &Connection,
supported: &[Properties], supported: &[Properties],
cmd: &BiosCommand, cmd: &PlatformCommand,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
{ {
if (cmd.gpu_mux_mode_set.is_none() if (cmd.gpu_mux_mode_set.is_none()
@@ -900,7 +997,10 @@ fn handle_platform_properties(
{ {
println!("Missing arg or command\n"); println!("Missing arg or command\n");
let usage: Vec<String> = BiosCommand::usage().lines().map(|s| s.to_owned()).collect(); let usage: Vec<String> = PlatformCommand::usage()
.lines()
.map(|s| s.to_owned())
.collect();
for line in usage.iter().filter(|line| { for line in usage.iter().filter(|line| {
line.contains("sound") && supported.contains(&Properties::PostAnimationSound) line.contains("sound") && supported.contains(&Properties::PostAnimationSound)
+35
View File
@@ -0,0 +1,35 @@
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,
}
+2 -2
View File
@@ -11,9 +11,9 @@ pub struct SlashCommand {
pub disable: bool, pub disable: bool,
#[options(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-255>")] #[options(meta = "", help = "Set interval value <0-5>")]
pub interval: Option<u8>, pub interval: Option<u8>,
#[options(help = "Set SlashMode (so 'list' for all options)")] #[options(meta = "", help = "Set SlashMode (so 'list' for all options)")]
pub slash_mode: Option<SlashMode>, pub slash_mode: Option<SlashMode>,
#[options(help = "list available animations")] #[options(help = "list available animations")]
pub list: bool, pub list: bool,
-7
View File
@@ -22,7 +22,6 @@ 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" }
@@ -33,9 +32,3 @@ 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"]
+1 -1
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_derive::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::error::Error; use crate::error::Error;
+3 -3
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_derive::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use zbus::interface; use zbus::interface;
use zbus::zvariant::{ObjectPath, Type}; use zbus::zvariant::{ObjectPath, Type};
@@ -66,7 +66,7 @@ pub struct CtrlAnimeInner<'a> {
do_early_return: Arc<AtomicBool>, do_early_return: Arc<AtomicBool>,
} }
impl<'a> CtrlAnimeInner<'static> { impl CtrlAnimeInner<'static> {
pub fn new( pub fn new(
sequences: Sequences, sequences: Sequences,
client: AnimeProxyBlocking<'static>, client: AnimeProxyBlocking<'static>,
@@ -81,7 +81,7 @@ impl<'a> CtrlAnimeInner<'static> {
/// 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(&'a self) -> Result<(), Error> { pub fn run(&self) -> Result<(), Error> {
if self.do_early_return.load(Ordering::SeqCst) { if self.do_early_return.load(Ordering::SeqCst) {
return Ok(()); return Ok(());
} }
+1 -1
View File
@@ -44,7 +44,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// Set up the anime data and run loop/thread // Set up the anime data and run loop/thread
if supported.contains(&"org.asuslinux.Anime".to_string()) { if supported.contains(&"org.asuslinux.Anime".to_string()) {
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));
+1 -4
View File
@@ -18,6 +18,7 @@ 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" }
@@ -38,12 +39,8 @@ 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"]
@@ -1,81 +1,27 @@
use std::time::Duration; use std::time::Duration;
use config_traits::{StdConfig, StdConfigLoad2}; use config_traits::{StdConfig, StdConfigLoad};
use rog_anime::error::AnimeError; use rog_anime::error::AnimeError;
use rog_anime::usb::Brightness; use rog_anime::usb::Brightness;
use rog_anime::{ use rog_anime::{
ActionData, ActionLoader, AnimTime, Animations, AnimeType, DeviceState, Fade, Vec2, ActionData, ActionLoader, AnimTime, Animations, AnimeType, DeviceState, Fade, Vec2,
}; };
use serde_derive::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
const CONFIG_FILE: &str = "anime.ron"; const CONFIG_FILE: &str = "anime.ron";
#[derive(Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize, Default)]
pub struct AnimeConfigV460 { pub struct AniMeConfigCached {
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 system: Vec<ActionData>,
pub boot: Vec<ActionData>, pub boot: Vec<ActionData>,
pub wake: Vec<ActionData>, pub wake: Vec<ActionData>,
pub shutdown: Vec<ActionData>, pub shutdown: Vec<ActionData>,
} }
impl AnimeConfigCached { impl AniMeConfigCached {
pub fn init_from_config( pub fn init_from_config(
&mut self, &mut self,
config: &AnimeConfig, config: &AniMeConfig,
anime_type: AnimeType, anime_type: AnimeType,
) -> Result<(), AnimeError> { ) -> Result<(), AnimeError> {
let mut sys = Vec::with_capacity(config.system.len()); let mut sys = Vec::with_capacity(config.system.len());
@@ -107,8 +53,9 @@ impl AnimeConfigCached {
/// Config for base system actions for the anime display /// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug, Clone)] #[derive(Deserialize, Serialize, Debug, Clone)]
pub struct AnimeConfig { pub struct AniMeConfig {
pub model_override: Option<AnimeType>, #[serde(skip)]
pub anime_type: AnimeType,
pub system: Vec<ActionLoader>, pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>, pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>, pub wake: Vec<ActionLoader>,
@@ -124,10 +71,10 @@ pub struct AnimeConfig {
pub builtin_anims: Animations, pub builtin_anims: Animations,
} }
impl Default for AnimeConfig { impl Default for AniMeConfig {
fn default() -> Self { fn default() -> Self {
AnimeConfig { AniMeConfig {
model_override: None, anime_type: AnimeType::GA402,
system: Vec::new(), system: Vec::new(),
boot: Vec::new(), boot: Vec::new(),
wake: Vec::new(), wake: Vec::new(),
@@ -145,7 +92,7 @@ impl Default for AnimeConfig {
} }
} }
impl StdConfig for AnimeConfig { impl StdConfig for AniMeConfig {
fn new() -> Self { fn new() -> Self {
Self::create_default() Self::create_default()
} }
@@ -159,10 +106,10 @@ impl StdConfig for AnimeConfig {
} }
} }
impl StdConfigLoad2<AnimeConfigV460, AnimeConfigV472> for AnimeConfig {} impl StdConfigLoad for AniMeConfig {}
impl From<&AnimeConfig> for DeviceState { impl From<&AniMeConfig> for DeviceState {
fn from(config: &AnimeConfig) -> Self { fn from(config: &AniMeConfig) -> Self {
DeviceState { DeviceState {
display_enabled: config.display_enabled, display_enabled: config.display_enabled,
display_brightness: config.display_brightness, display_brightness: config.display_brightness,
@@ -176,7 +123,7 @@ impl From<&AnimeConfig> for DeviceState {
} }
} }
impl AnimeConfig { impl AniMeConfig {
// fn clamp_config_brightness(mut config: &mut AnimeConfig) { // fn clamp_config_brightness(mut config: &mut AnimeConfig) {
// if config.brightness < 0.0 || config.brightness > 1.0 { // if config.brightness < 0.0 || config.brightness > 1.0 {
// warn!( // warn!(
@@ -189,7 +136,7 @@ impl AnimeConfig {
fn create_default() -> Self { fn create_default() -> Self {
// create a default config here // create a default config here
AnimeConfig { AniMeConfig {
system: vec![], system: vec![],
boot: vec![ActionLoader::ImageAnimation { boot: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(), file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
+247
View File
@@ -0,0 +1,247 @@
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 log::{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 tokio::sync::{Mutex, MutexGuard};
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 Ok(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 {
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
}
pub async fn lock_config(&self) -> MutexGuard<AniMeConfig> {
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(())
}
/// 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_local(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();
}
inner
.write_bytes(&pkt_set_enable_powersave_anim(
inner.config.lock().await.builtin_anims_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();
}
}
@@ -1,23 +1,22 @@
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use std::sync::Arc;
use config_traits::StdConfig; use config_traits::StdConfig;
use log::warn; use log::{error, 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::export::futures_util::lock::Mutex; use zbus::object_server::SignalEmitter;
use zbus::{interface, CacheProperties, Connection, SignalContext}; use zbus::proxy::CacheProperties;
use zbus::zvariant::OwnedObjectPath;
use zbus::{interface, Connection};
use super::config::AnimeConfig; use super::config::AniMeConfig;
use super::CtrlAnime; use super::AniMe;
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()
@@ -32,12 +31,29 @@ async fn get_logind_manager<'a>() -> ManagerProxy<'a> {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>); pub struct AniMeZbus(AniMe);
/// The struct with the main dbus methods requires this trait impl AniMeZbus {
impl crate::ZbusRun for CtrlAnimeZbus { pub fn new(anime: AniMe) -> Self {
async fn add_to_server(self, server: &mut Connection) { Self(anime)
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:?}"))
.ok();
Ok(())
} }
} }
@@ -45,91 +61,84 @@ impl crate::ZbusRun for CtrlAnimeZbus {
// 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 = "org.asuslinux.Anime")] #[interface(name = "org.asuslinux.Anime")]
impl CtrlAnimeZbus { impl AniMeZbus {
/// 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<()> {
self.0 let bright = self.0.config.lock().await.display_brightness;
.lock() self.0.set_builtins_enabled(false, bright).await?;
.await self.0.thread_exit.store(true, Ordering::SeqCst);
.thread_exit self.0.write_data_buffer(input).await.map_err(|err| {
.store(true, Ordering::SeqCst); warn!("ctrl_anime::run_animation:callback {}", err);
self.0 err
.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 {
self.0.lock().await.config.display_brightness if let Ok(config) = self.0.config.try_lock() {
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
.lock()
.await
.node
.write_bytes(&pkt_set_brightness(brightness)) .write_bytes(&pkt_set_brightness(brightness))
.await
.map_err(|err| { .map_err(|err| {
warn!("ctrl_anime::set_brightness {}", err); warn!("ctrl_anime::set_brightness {}", err);
}) })
.ok(); .ok();
self.0 self.0
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(brightness != Brightness::Off)) .write_bytes(&pkt_set_enable_display(brightness != Brightness::Off))
.await
.map_err(|err| { .map_err(|err| {
warn!("ctrl_anime::set_brightness {}", err); warn!("ctrl_anime::set_brightness {}", err);
}) })
.ok(); .ok();
self.0.lock().await.config.display_enabled = brightness != Brightness::Off; let mut config = self.0.config.lock().await;
self.0.lock().await.config.display_brightness = brightness; config.display_enabled = brightness != Brightness::Off;
self.0.lock().await.config.write(); config.display_brightness = brightness;
config.write();
} }
#[zbus(property)] #[zbus(property)]
async fn builtins_enabled(&self) -> bool { async fn builtins_enabled(&self) -> bool {
let lock = self.0.lock().await; if let Ok(config) = self.0.config.try_lock() {
lock.config.builtin_anims_enabled return 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 brightness = self.0.lock().await.config.display_brightness; let mut config = self.0.config.lock().await;
let brightness = config.display_brightness;
self.0 self.0
.lock()
.await
.node
.set_builtins_enabled(enabled, brightness) .set_builtins_enabled(enabled, brightness)
.await
.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 = self.0.lock().await.anime_type; let anime_type = config.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
.lock()
.await
.node
.write_bytes(tmp.data()) .write_bytes(tmp.data())
.await
.map_err(|err| { .map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err); warn!("ctrl_anime::set_builtins_enabled {}", err);
}) })
@@ -137,77 +146,78 @@ impl CtrlAnimeZbus {
} }
} }
self.0.lock().await.config.builtin_anims_enabled = enabled; config.builtin_anims_enabled = enabled;
self.0.lock().await.config.write(); config.write();
if enabled { if enabled {
self.0 self.0.thread_exit.store(true, Ordering::Release);
.lock()
.await
.thread_exit
.store(true, Ordering::Release);
} }
} }
#[zbus(property)] #[zbus(property)]
async fn builtin_animations(&self) -> Animations { async fn builtin_animations(&self) -> Animations {
self.0.lock().await.config.builtin_anims if let Ok(config) = self.0.config.try_lock() {
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
.lock()
.await
.node
.write_bytes(&pkt_set_builtin_animations( .write_bytes(&pkt_set_builtin_animations(
settings.boot, settings.boot,
settings.awake, settings.awake,
settings.sleep, settings.sleep,
settings.shutdown, settings.shutdown,
)) ))
.await
.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
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(true)) .write_bytes(&pkt_set_enable_powersave_anim(true))
.await
.map_err(|err| { .map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err); warn!("ctrl_anime::run_animation:callback {}", err);
}) })
.ok(); .ok();
self.0.lock().await.config.display_enabled = true; let mut config = self.0.config.lock().await;
self.0.lock().await.config.builtin_anims = settings; config.display_enabled = true;
self.0.lock().await.config.write(); config.builtin_anims = settings;
config.write();
} }
#[zbus(property)] #[zbus(property)]
async fn enable_display(&self) -> bool { async fn enable_display(&self) -> bool {
self.0.lock().await.config.display_enabled if let Ok(config) = self.0.config.try_lock() {
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
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(enabled)) .write_bytes(&pkt_set_enable_display(enabled))
.await
.map_err(|err| { .map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err); warn!("ctrl_anime::run_animation:callback {}", err);
}) })
.ok(); .ok();
self.0.lock().await.config.display_enabled = enabled; let mut config = self.0.config.lock().await;
self.0.lock().await.config.write(); config.display_enabled = enabled;
config.write();
} }
#[zbus(property)] #[zbus(property)]
async fn off_when_unplugged(&self) -> bool { async fn off_when_unplugged(&self) -> bool {
self.0.lock().await.config.off_when_unplugged if let Ok(config) = self.0.config.try_lock() {
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
@@ -217,34 +227,40 @@ impl CtrlAnimeZbus {
let pow = manager.on_external_power().await.unwrap_or_default(); let pow = manager.on_external_power().await.unwrap_or_default();
self.0 self.0
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(!pow && !enabled)) .write_bytes(&pkt_set_enable_display(!pow && !enabled))
.await
.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();
self.0.lock().await.config.off_when_unplugged = enabled; let mut config = self.0.config.lock().await;
self.0.lock().await.config.write(); config.off_when_unplugged = enabled;
config.write();
} }
#[zbus(property)] #[zbus(property)]
async fn off_when_suspended(&self) -> bool { async fn off_when_suspended(&self) -> bool {
self.0.lock().await.config.off_when_suspended if let Ok(config) = self.0.config.try_lock() {
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) {
self.0.lock().await.config.off_when_suspended = enabled; let mut config = self.0.config.lock().await;
self.0.lock().await.config.write(); config.off_when_suspended = enabled;
config.write();
} }
#[zbus(property)] #[zbus(property)]
async fn off_when_lid_closed(&self) -> bool { async fn off_when_lid_closed(&self) -> bool {
self.0.lock().await.config.off_when_lid_closed if let Ok(config) = self.0.config.try_lock() {
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
@@ -254,50 +270,40 @@ impl CtrlAnimeZbus {
let lid = manager.lid_closed().await.unwrap_or_default(); let lid = manager.lid_closed().await.unwrap_or_default();
self.0 self.0
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(lid && !enabled)) .write_bytes(&pkt_set_enable_display(lid && !enabled))
.await
.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();
self.0.lock().await.config.off_when_lid_closed = enabled; let mut config = self.0.config.lock().await;
self.0.lock().await.config.write(); config.off_when_lid_closed = enabled;
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 self.0.thread_exit.store(true, Ordering::SeqCst);
.lock() self.0.run_thread(self.0.cache.system.clone(), false).await;
.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.lock().await.config) DeviceState::from(&*self.0.config.lock().await)
} }
} }
impl crate::CtrlTask for CtrlAnimeZbus { impl crate::CtrlTask for AniMeZbus {
fn zbus_path() -> &'static str { fn zbus_path() -> &'static str {
ANIME_ZBUS_PATH "ANIME_ZBUS_PATH"
} }
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> { async fn create_tasks(&self, _: SignalEmitter<'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();
@@ -307,21 +313,15 @@ impl crate::CtrlTask for CtrlAnimeZbus {
// on_sleep // on_sleep
let inner = inner1.clone(); let inner = inner1.clone();
async move { async move {
let config = inner.lock().await.config.clone(); let config = inner.config.lock().await.clone();
if config.display_enabled { if config.display_enabled {
inner inner.thread_exit.store(true, Ordering::Release); // ensure clean slate
.lock()
.await
.thread_exit
.store(true, Ordering::Release); // ensure clean slate
inner inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display( .write_bytes(&pkt_set_enable_display(
!(sleeping && config.off_when_suspended), !(sleeping && config.off_when_suspended),
)) ))
.await
.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,12 +329,10 @@ impl crate::CtrlTask for CtrlAnimeZbus {
if config.builtin_anims_enabled { if config.builtin_anims_enabled {
inner inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim( .write_bytes(&pkt_set_enable_powersave_anim(
!(sleeping && config.off_when_suspended), !(sleeping && config.off_when_suspended),
)) ))
.await
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err); warn!("create_sys_event_tasks::off_when_suspended {}", err);
}) })
@@ -342,18 +340,11 @@ impl crate::CtrlTask for CtrlAnimeZbus {
} else if !sleeping && !config.builtin_anims_enabled { } else if !sleeping && !config.builtin_anims_enabled {
// Run custom wake animation // Run custom wake animation
inner inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(false)) .write_bytes(&pkt_set_enable_powersave_anim(false))
.await
.ok(); // ensure builtins are disabled .ok(); // ensure builtins are disabled
CtrlAnime::run_thread( inner.run_thread(inner.cache.wake.clone(), true).await;
inner.clone(),
inner.lock().await.cache.wake.clone(),
true,
)
.await;
} }
} }
} }
@@ -362,26 +353,16 @@ impl crate::CtrlTask for CtrlAnimeZbus {
// 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.lock().await.config; } = *inner.config.lock().await;
if display_enabled && !builtin_anims_enabled { if display_enabled && !builtin_anims_enabled {
if shutting_down { if shutting_down {
CtrlAnime::run_thread( inner.run_thread(inner.cache.shutdown.clone(), true).await;
inner.clone(),
inner.lock().await.cache.shutdown.clone(),
true,
)
.await;
} else { } else {
CtrlAnime::run_thread( inner.run_thread(inner.cache.boot.clone(), true).await;
inner.clone(),
inner.lock().await.cache.boot.clone(),
true,
)
.await;
} }
} }
} }
@@ -390,28 +371,24 @@ impl crate::CtrlTask for CtrlAnimeZbus {
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.lock().await.config; } = *inner.config.lock().await;
if off_when_lid_closed { if off_when_lid_closed {
if builtin_anims_enabled { if builtin_anims_enabled {
inner inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(!lid_closed)) .write_bytes(&pkt_set_enable_powersave_anim(!lid_closed))
.await
.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
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(!lid_closed)) .write_bytes(&pkt_set_enable_display(!lid_closed))
.await
.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);
}) })
@@ -423,39 +400,33 @@ impl crate::CtrlTask for CtrlAnimeZbus {
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.lock().await.config; } = *inner.config.lock().await;
if off_when_unplugged { if off_when_unplugged {
if builtin_anims_enabled { if builtin_anims_enabled {
inner inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(power_plugged)) .write_bytes(&pkt_set_enable_powersave_anim(power_plugged))
.await
.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
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(power_plugged)) .write_bytes(&pkt_set_enable_display(power_plugged))
.await
.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
.lock()
.await
.node
.write_bytes(&pkt_set_brightness(brightness_on_battery)) .write_bytes(&pkt_set_brightness(brightness_on_battery))
.await
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_unplugged {}", err); warn!("create_sys_event_tasks::off_when_unplugged {}", err);
}) })
@@ -470,51 +441,54 @@ impl crate::CtrlTask for CtrlAnimeZbus {
} }
} }
impl crate::Reloadable for CtrlAnimeZbus { impl crate::Reloadable for AniMeZbus {
async fn reload(&mut self) -> Result<(), RogError> { async fn reload(&mut self) -> Result<(), RogError> {
if let Some(lock) = self.0.try_lock() { if let Ok(config) = self.0.config.try_lock() {
let anim = &lock.config.builtin_anims; let anim = &config.builtin_anims;
// Set builtins // Set builtins
if lock.config.builtin_anims_enabled { if config.builtin_anims_enabled {
lock.node.write_bytes(&pkt_set_builtin_animations( self.0
anim.boot, .write_bytes(&pkt_set_builtin_animations(
anim.awake, anim.boot,
anim.sleep, anim.awake,
anim.shutdown, anim.sleep,
))?; anim.shutdown,
))
.await?;
} }
// Builtins enabled or na? // Builtins enabled or na?
lock.node.set_builtins_enabled( self.0
lock.config.builtin_anims_enabled, .set_builtins_enabled(config.builtin_anims_enabled, config.display_brightness)
lock.config.display_brightness, .await?;
)?;
let manager = get_logind_manager().await; let manager = get_logind_manager().await;
let lid_closed = manager.lid_closed().await.unwrap_or_default(); let lid_closed = manager.lid_closed().await.unwrap_or_default();
let power_plugged = manager.on_external_power().await.unwrap_or_default(); let power_plugged = manager.on_external_power().await.unwrap_or_default();
let turn_off = (lid_closed && lock.config.off_when_lid_closed) let turn_off = (lid_closed && config.off_when_lid_closed)
|| (!power_plugged && lock.config.off_when_unplugged); || (!power_plugged && config.off_when_unplugged);
lock.node self.0
.write_bytes(&pkt_set_enable_display(!turn_off)) .write_bytes(&pkt_set_enable_display(!turn_off))
.await
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::reload {}", err); warn!("create_sys_event_tasks::reload {}", err);
}) })
.ok(); .ok();
if turn_off || !lock.config.display_enabled { if turn_off || !config.display_enabled {
lock.node.write_bytes(&pkt_set_enable_display(false))?; self.0.write_bytes(&pkt_set_enable_display(false)).await?;
// early return so we don't run animation thread // early return so we don't run animation thread
return Ok(()); return Ok(());
} }
if !lock.config.builtin_anims_enabled && !lock.cache.boot.is_empty() { if !config.builtin_anims_enabled && !self.0.cache.boot.is_empty() {
lock.node self.0
.write_bytes(&pkt_set_enable_powersave_anim(false)) .write_bytes(&pkt_set_enable_powersave_anim(false))
.await
.ok(); .ok();
let action = lock.cache.boot.clone(); let action = self.0.cache.boot.clone();
CtrlAnime::run_thread(self.0.clone(), action, true).await; self.0.run_thread(action, true).await;
} }
} }
Ok(()) Ok(())
@@ -7,12 +7,20 @@ 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_derive::{Deserialize, Serialize}; use serde::{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>,
@@ -20,6 +28,8 @@ 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 {
@@ -54,24 +64,28 @@ 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 &support_data.basic_modes { for n in &config.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 !support_data.basic_zones.is_empty() { if !config.support_data.basic_zones.is_empty() {
let mut default = vec![]; let mut default = vec![];
for (i, tmp) in support_data.basic_zones.iter().enumerate() { for (i, tmp) in config.support_data.basic_zones.iter().enumerate() {
default.push(AuraEffect { default.push(AuraEffect {
mode: *n, mode: *n,
zone: *tmp, zone: *tmp,
@@ -130,16 +144,102 @@ 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::{AuraEffect, AuraModeNum, AuraZone, Colour}; use rog_aura::keyboard::AuraPowerState;
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 {
@@ -230,6 +330,7 @@ mod tests {
#[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 {
@@ -274,4 +375,66 @@ 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
}
);
}
} }
+219
View File
@@ -0,0 +1,219 @@
use std::sync::Arc;
use config::AuraConfig;
use config_traits::StdConfig;
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 tokio::sync::{Mutex, MutexGuard};
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
@@ -0,0 +1,343 @@
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 = "/org/asuslinux";
#[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 = "org.asuslinux.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 Ok(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 Ok(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 {
"/org/asuslinux"
}
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(())
}
}
+542
View File
@@ -0,0 +1,542 @@
// 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 log::{debug, error, info, warn};
use mio::{Events, Interest, Poll, Token};
use rog_platform::error::PlatformError;
use rog_platform::hid_raw::HidRaw;
use tokio::sync::Mutex;
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;
pub const ASUS_ZBUS_PATH: &str = "/org/asuslinux";
/// 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}/{filename}")).into(),
);
}
None
}
fn dbus_path_for_tuf() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/tuf")).into()
}
fn dbus_path_for_slash() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/slash")).into()
}
fn dbus_path_for_anime() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/anime")).into()
}
fn dbus_path_for_scsi(prod_id: &str) -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{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
}
// TODO:
// - make this the HID manager (and universal)
// - *really* need to make most of this actual kernel drivers
// - LED class
// - RGB modes (how, attribute?)
// - power features (how, attribute?)
// - what about per-key stuff?
// - how would the AniMe be exposed? Just a series of LEDs?
/// 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 {
warn!("No serial for SCSI device");
}
}
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);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
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 = Arc::new(Mutex::new(Self::find_all_devices(&conn_copy).await));
let manager = Self {
_dbus_connection: connection,
};
// TODO: The /sysfs/ LEDs don't cause events, so they need to be manually
// checked for and added
// detect all plugged in aura devices (eventually)
// only USB devices are detected for here
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 {
warn!("No device for dbus path: {path:?}");
return Ok(());
};
info!("removing: {path:?}");
let dev = devices.lock().await.remove(index);
let path = path.clone();
match dev.device {
DeviceHandle::Scsi(_) => {
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
@@ -0,0 +1,114 @@
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
@@ -0,0 +1,45 @@
use std::sync::Arc;
use config::ScsiConfig;
use rog_scsi::{AuraEffect, Device, Task};
use tokio::sync::{Mutex, MutexGuard};
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
@@ -0,0 +1,116 @@
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 = "org.asuslinux.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 Ok(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()
}
}
@@ -1,12 +1,14 @@
use config_traits::{StdConfig, StdConfigLoad}; use config_traits::{StdConfig, StdConfigLoad};
use rog_slash::{DeviceState, SlashMode}; use rog_slash::{DeviceState, SlashMode, SlashType};
use serde_derive::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
const CONFIG_FILE: &str = "slash.ron"; const CONFIG_FILE: &str = "slash.ron";
/// Config for base system actions for the anime display /// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug)]
pub struct SlashConfig { pub struct SlashConfig {
#[serde(skip)]
pub slash_type: SlashType,
pub slash_enabled: bool, pub slash_enabled: bool,
pub slash_brightness: u8, pub slash_brightness: u8,
pub slash_interval: u8, pub slash_interval: u8,
@@ -20,6 +22,7 @@ impl Default for SlashConfig {
slash_brightness: 255, slash_brightness: 255,
slash_interval: 0, slash_interval: 0,
slash_mode: SlashMode::Bounce, slash_mode: SlashMode::Bounce,
slash_type: SlashType::Unsupported,
} }
} }
} }
+70
View File
@@ -0,0 +1,70 @@
use std::sync::Arc;
use config::SlashConfig;
use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw;
use rog_slash::usb::{pkt_set_mode, pkt_set_options, pkts_for_init};
use rog_slash::SlashType;
use tokio::sync::{Mutex, MutexGuard};
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 = pkt_set_options(
config.slash_type,
config.slash_enabled,
config.slash_brightness,
config.slash_interval,
);
self.write_bytes(&option_packets).await?;
let mode_packets = pkt_set_mode(config.slash_type, config.slash_mode);
// self.node.write_bytes(&mode_packets[0])?;
self.write_bytes(&mode_packets[1]).await?;
Ok(())
}
}
+190
View File
@@ -0,0 +1,190 @@
use config_traits::StdConfig;
use log::{debug, error, warn};
use rog_slash::usb::{pkt_save, pkt_set_mode, pkt_set_options};
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 = "org.asuslinux.Slash")]
impl SlashZbus {
/// Get enabled or not
#[zbus(property)]
async fn enabled(&self) -> bool {
let lock = self.0.lock_config().await;
lock.slash_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.slash_brightness == 0 {
0x88
} else {
config.slash_brightness
};
self.0
.write_bytes(&pkt_set_options(
config.slash_type,
enabled,
brightness,
config.slash_interval,
))
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
config.slash_enabled = enabled;
config.slash_brightness = brightness;
config.write();
}
/// Get brightness level
#[zbus(property)]
async fn brightness(&self) -> u8 {
let config = self.0.lock_config().await;
config.slash_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(&pkt_set_options(
config.slash_type,
enabled,
brightness,
config.slash_interval,
))
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
config.slash_enabled = enabled;
config.slash_brightness = brightness;
config.write();
}
#[zbus(property)]
async fn interval(&self) -> u8 {
let config = self.0.lock_config().await;
config.slash_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(&pkt_set_options(
config.slash_type,
config.slash_enabled,
config.slash_brightness,
interval,
))
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
config.slash_interval = interval;
config.write();
}
#[zbus(property)]
async fn slash_mode(&self) -> u8 {
let config = self.0.lock_config().await;
config.slash_interval
}
/// Set interval between slash animations (0-255)
#[zbus(property)]
async fn set_slash_mode(&self, slash_mode: SlashMode) {
let mut config = self.0.lock_config().await;
let command_packets = pkt_set_mode(config.slash_type, slash_mode);
// self.node.write_bytes(&command_packets[0])?;
self.0
.write_bytes(&command_packets[1])
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
self.0
.write_bytes(&pkt_save(config.slash_type))
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
config.slash_mode = slash_mode;
config.write();
}
/// 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)
}
}
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(&pkt_set_options(
config.slash_type,
config.slash_enabled,
config.slash_brightness,
config.slash_interval,
))
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
Ok(())
}
}
+204
View File
@@ -0,0 +1,204 @@
use std::sync::Arc;
use config_traits::{StdConfig, StdConfigLoad};
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 tokio::sync::Mutex;
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> {
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))
}
}
+9 -3
View File
@@ -1,14 +1,17 @@
use config_traits::{StdConfig, StdConfigLoad1}; use config_traits::{StdConfig, StdConfigLoad1};
use rog_platform::cpu::CPUEPP; use rog_platform::cpu::CPUEPP;
use rog_platform::platform::ThrottlePolicy; use rog_platform::platform::ThrottlePolicy;
use serde_derive::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
const CONFIG_FILE: &str = "asusd.ron"; const CONFIG_FILE: &str = "asusd.ron";
#[derive(Deserialize, Serialize, Debug, PartialEq, PartialOrd)] #[derive(Deserialize, Serialize, Debug, PartialEq, PartialOrd)]
pub struct Config { pub struct Config {
/// Save charge limit for restoring on boot/resume // The current charge limit applied
pub charge_control_end_threshold: u8, pub charge_control_end_threshold: u8,
/// Save charge limit for restoring
#[serde(skip)]
pub base_charge_control_end_threshold: u8,
pub panel_od: bool, pub panel_od: bool,
pub boot_sound: bool, pub boot_sound: bool,
pub mini_led_mode: bool, pub mini_led_mode: bool,
@@ -64,6 +67,7 @@ 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, panel_od: false,
boot_sound: false, boot_sound: false,
mini_led_mode: false, mini_led_mode: false,
@@ -116,7 +120,7 @@ impl StdConfigLoad1<Config507> for Config {}
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct Config507 { pub struct Config507 {
/// Save charge limit for restoring on boot // The current charge limit applied
pub charge_control_end_threshold: u8, pub charge_control_end_threshold: u8,
pub panel_od: bool, pub panel_od: bool,
pub mini_led_mode: bool, pub mini_led_mode: bool,
@@ -139,7 +143,9 @@ pub struct Config507 {
impl From<Config507> for Config { impl From<Config507> for Config {
fn from(c: Config507) -> 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, panel_od: c.panel_od,
boot_sound: false, 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,
-292
View File
@@ -1,292 +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 ::zbus::export::futures_util::lock::Mutex;
use config_traits::{StdConfig, StdConfigLoad2};
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() -> 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 config = AnimeConfig::new().load();
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(())
}
}
-568
View File
@@ -1,568 +0,0 @@
use std::collections::{BTreeMap, HashSet};
use config_traits::{StdConfig, StdConfigLoad};
use dmi_id::DMIID;
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::KeyboardBacklight;
use udev::Device;
use zbus::zvariant::OwnedObjectPath;
use zbus::Connection;
use super::config::AuraConfig;
use crate::ctrl_aura::manager::{dbus_path_for_dev, dbus_path_for_tuf, start_tasks};
use crate::ctrl_aura::trait_impls::CtrlAuraZbus;
use crate::error::RogError;
use crate::CtrlTask;
#[derive(Debug)]
pub enum LEDNode {
/// Brightness and/or TUF RGB controls
KbdLed(KeyboardBacklight),
/// Raw HID handle
Rog(Option<KeyboardBacklight>, 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, r) => {
if let Some(k) = k {
k.set_brightness(value)?;
let x = k.get_brightness()?;
if x != value {
debug!(
"Kernel brightness control didn't read back correct value, setting \
with raw hid"
);
r.write_bytes(&[0x5a, 0xba, 0xc5, 0xc4, value])?;
}
} else {
debug!("No brightness control found, trying raw write");
r.write_bytes(&[0x5a, 0xba, 0xc5, 0xc4, value])?;
}
}
}
Ok(())
}
pub fn get_brightness(&self) -> Result<u8, RogError> {
Ok(match self {
LEDNode::KbdLed(k) => k.get_brightness()?,
LEDNode::Rog(k, _) => {
if let Some(k) = k {
k.get_brightness()?
} else {
debug!("No brightness control found");
return Err(RogError::MissingFunction(
"No keyboard brightness control found".to_string(),
));
}
}
})
}
pub fn monitor_brightness(&self) -> Result<Inotify, RogError> {
Ok(match self {
LEDNode::KbdLed(k) => k.monitor_brightness()?,
LEDNode::Rog(k, _) => {
if let Some(k) = k {
k.monitor_brightness()?
} else {
debug!("No brightness control found");
return Err(RogError::MissingFunction(
"No keyboard brightness control found".to_string(),
));
}
}
})
}
pub fn has_brightness_control(&self) -> bool {
match self {
LEDNode::KbdLed(k) => k.has_brightness(),
LEDNode::Rog(k, _) => {
if let Some(k) = k {
k.has_brightness()
} else {
false
}
}
}
}
}
/// 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 add_to_dbus_and_start(
self,
interfaces: &mut HashSet<OwnedObjectPath>,
conn: Connection,
) -> Result<(), RogError> {
let dbus_path = self.dbus_path.clone();
let dbus_path_cpy = self.dbus_path.clone();
info!(
"AuraManager starting device at: {:?}, {:?}",
dbus_path, self.led_type
);
let conn_copy = conn.clone();
let sig_ctx1 = CtrlAuraZbus::signal_context(&conn_copy)?;
let sig_ctx2 = CtrlAuraZbus::signal_context(&conn_copy)?;
let zbus = CtrlAuraZbus::new(self, sig_ctx1);
tokio::spawn(
async move { start_tasks(zbus, conn_copy.clone(), sig_ctx2, dbus_path).await },
);
interfaces.insert(dbus_path_cpy);
Ok(())
}
/// Build and init a `CtrlKbdLed` from a udev device. Maybe.
/// This will initialise the config also.
pub fn maybe_device(
device: Device,
interfaces: &mut HashSet<OwnedObjectPath>,
) -> Result<Option<Self>, RogError> {
// usb_device gives us a product and vendor ID
if let Some(usb_device) = device.parent_with_subsystem_devtype("usb", "usb_device")? {
let dbus_path = dbus_path_for_dev(&usb_device).unwrap_or_default();
if interfaces.contains(&dbus_path) {
debug!("Already a ctrl at {dbus_path:?}, ignoring this end-point");
return Ok(None);
}
// The asus_wmi driver latches MCU that controls the USB endpoints
if let Some(parent) = device.parent() {
if let Some(driver) = parent.driver() {
// There is a tree of devices added so filter by driver
if driver != "asus" {
return Ok(None);
}
} else {
return Ok(None);
}
}
// 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_device = AuraDeviceType::from(prod_id.as_str());
if aura_device == AuraDeviceType::Unknown {
log::debug!("Unknown or invalid device: {usb_id:?}, skipping");
return Ok(None);
}
}
let dev_node = if let Some(dev_node) = usb_device.devnode() {
dev_node
} else {
debug!("Device has no devnode, skipping");
return Ok(None);
};
info!("AuraControl found device at: {:?}", dev_node);
let dev = HidRaw::from_device(device)?;
let mut controller = Self::from_hidraw(dev, dbus_path.clone())?;
controller.config = Self::init_config(&prod_id);
interfaces.insert(dbus_path);
return Ok(Some(controller));
}
Ok(None)
}
pub fn find_all() -> Result<Vec<Self>, RogError> {
info!("Searching for all Aura devices");
let mut devices = Vec::new();
let mut interfaces = 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()? {
// maybe?
if let Some(device) = Self::maybe_device(end_point, &mut interfaces)? {
devices.push(device);
}
}
// Check for a TUF laptop LED. Assume there is only ever one.
if let Ok(kbd_backlight) = KeyboardBacklight::new() {
if kbd_backlight.has_kbd_rgb_mode() {
// Extra sure double-check that this isn't a laptop with crap
// ACPI with borked return on the TUF rgb methods
let dmi = DMIID::new().unwrap_or_default();
info!("Found a TUF with product family: {}", dmi.product_family);
info!("and board name: {}", dmi.board_name);
if dmi.product_family.contains("TUF") {
info!("AuraControl found a TUF laptop keyboard");
let ctrl = CtrlKbdLed {
led_type: AuraDeviceType::LaptopTuf,
led_node: LEDNode::KbdLed(kbd_backlight),
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);
}
}
} else {
let dmi = DMIID::new().unwrap_or_default();
warn!("No asus::kbd_backlight found for {} ??", dmi.product_family);
}
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 = KeyboardBacklight::new()
.map_err(|e| {
log::error!(
"{} is missing a keyboard backlight brightness control: {e:?}",
device.prod_id()
);
})
.ok();
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
let buf = self.config.enabled.to_bytes(self.led_type);
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::KeyboardBacklight;
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(
Some(KeyboardBacklight::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(
Some(KeyboardBacklight::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);
}
}
-139
View File
@@ -1,139 +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::collections::HashSet;
use log::{error, info, warn};
use mio::{Events, Interest, Poll, Token};
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 action = event.action().unwrap_or_default();
if let Some(parent) =
event.parent_with_subsystem_devtype("usb", "usb_device")?
{
if action == "remove" {
if let Some(path) = dbus_path_for_dev(&parent) {
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 let Ok(Some(ctrl)) =
CtrlKbdLed::maybe_device(event.device(), &mut interfaces)
{
ctrl.add_to_dbus_and_start(&mut interfaces, conn_copy.clone())
.map_err(|e| {
error!("Couldn't start aura device on dbus: {e:?}")
})
.ok();
}
};
}
}
}
// 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()
}
pub 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.clone(), zbus)
.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(())
}
-34
View File
@@ -1,34 +0,0 @@
use log::warn;
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 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
}
-305
View File
@@ -1,305 +0,0 @@
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().unwrap_or_default();
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;
}
if ctrl.led_node.has_brightness_control() {
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;
}
if ctrl.led_node.has_brightness_control() {
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");
if lock.led_node.has_brightness_control() {
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;
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 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(())
}
}
+4 -3
View File
@@ -8,9 +8,10 @@ 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_derive::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use zbus::{interface, Connection, SignalContext}; use zbus::object_server::SignalEmitter;
use zbus::{interface, Connection};
use crate::error::RogError; use crate::error::RogError;
use crate::{CtrlTask, CONFIG_PATH_BASE}; use crate::{CtrlTask, CONFIG_PATH_BASE};
@@ -234,7 +235,7 @@ impl CtrlTask for CtrlFanCurveZbus {
FAN_CURVE_ZBUS_PATH FAN_CURVE_ZBUS_PATH
} }
async fn create_tasks(&self, _signal_ctxt: SignalContext<'static>) -> Result<(), RogError> { async fn create_tasks(&self, _signal_ctxt: SignalEmitter<'static>) -> Result<(), RogError> {
let watch_throttle_thermal_policy = self.platform.monitor_throttle_thermal_policy()?; 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();
+71 -8
View File
@@ -5,11 +5,13 @@ use std::sync::Arc;
use config_traits::StdConfig; use config_traits::StdConfig;
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use rog_platform::cpu::{CPUControl, CPUGovernor, CPUEPP}; use rog_platform::cpu::{CPUControl, CPUGovernor, CPUEPP};
// use rog_platform::firmware_attributes::FirmwareAttributes;
use rog_platform::platform::{GpuMode, Properties, RogPlatform, ThrottlePolicy}; use rog_platform::platform::{GpuMode, Properties, RogPlatform, ThrottlePolicy};
use rog_platform::power::AsusPower; use rog_platform::power::AsusPower;
use zbus::export::futures_util::lock::Mutex; use zbus::export::futures_util::lock::Mutex;
use zbus::fdo::Error as FdoErr; use zbus::fdo::Error as FdoErr;
use zbus::{interface, Connection, SignalContext}; use zbus::object_server::SignalEmitter;
use zbus::{interface, Connection};
use crate::config::Config; use crate::config::Config;
use crate::error::RogError; use crate::error::RogError;
@@ -43,7 +45,7 @@ macro_rules! platform_set_value {
concat_idents::concat_idents!(set = set_, $property { concat_idents::concat_idents!(set = set_, $property {
$self.platform.set($new_value).map_err(|err| { $self.platform.set($new_value).map_err(|err| {
error!("RogPlatform: {} {err}", $prop_name); error!("RogPlatform: {} {err}", $prop_name);
FdoErr::NotSupported(format!("RogPlatform: {} {err}", $prop_name)) FdoErr::Failed(format!("RogPlatform: {} {err}", $prop_name))
})?; })?;
}); });
let mut lock = $self.config.lock().await; let mut lock = $self.config.lock().await;
@@ -92,8 +94,9 @@ impl CtrlPlatform {
pub fn new( pub fn new(
config: Arc<Mutex<Config>>, config: Arc<Mutex<Config>>,
config_path: &Path, config_path: &Path,
signal_context: SignalContext<'static>, signal_context: SignalEmitter<'static>,
) -> Result<Self, RogError> { ) -> Result<Self, RogError> {
// let attrs = FirmwareAttributes::new();
let platform = RogPlatform::new()?; let platform = RogPlatform::new()?;
let power = AsusPower::new()?; let power = AsusPower::new()?;
@@ -132,13 +135,12 @@ impl CtrlPlatform {
| inotify::WatchMask::ATTRIB | inotify::WatchMask::ATTRIB
| inotify::WatchMask::CREATE, | inotify::WatchMask::CREATE,
) )
.map_err(|e| { .inspect_err(|e| {
if e.kind() == std::io::ErrorKind::NotFound { if e.kind() == std::io::ErrorKind::NotFound {
error!("Not found: {:?}", config_path); error!("Not found: {:?}", config_path);
} else { } else {
error!("Could not set asusd config inotify: {:?}", config_path); error!("Could not set asusd config inotify: {:?}", config_path);
} }
e
}) })
.ok(); .ok();
let mut events = inotify.into_event_stream(&mut buffer).unwrap(); let mut events = inotify.into_event_stream(&mut buffer).unwrap();
@@ -179,6 +181,24 @@ impl CtrlPlatform {
Ok(()) Ok(())
} }
async fn restore_charge_limit(&self) {
let limit = self.config.lock().await.base_charge_control_end_threshold;
if limit > 0
&& std::mem::replace(
&mut self.config.lock().await.charge_control_end_threshold,
limit,
) != limit
{
self.power
.set_charge_control_end_threshold(limit)
.map_err(|e| {
error!("Couldn't restore charge limit: {e}");
})
.ok();
self.config.lock().await.write();
}
}
async fn run_ac_or_bat_cmd(&self, power_plugged: bool) { async fn run_ac_or_bat_cmd(&self, power_plugged: bool) {
let prog: Vec<String> = if power_plugged { let prog: Vec<String> = if power_plugged {
// AC ONLINE // AC ONLINE
@@ -351,10 +371,24 @@ impl CtrlPlatform {
} }
self.power.set_charge_control_end_threshold(limit)?; self.power.set_charge_control_end_threshold(limit)?;
self.config.lock().await.charge_control_end_threshold = limit; self.config.lock().await.charge_control_end_threshold = limit;
self.config.lock().await.base_charge_control_end_threshold = limit;
self.config.lock().await.write(); self.config.lock().await.write();
Ok(()) Ok(())
} }
async fn one_shot_full_charge(&self) -> Result<(), FdoErr> {
let base_limit = std::mem::replace(
&mut self.config.lock().await.charge_control_end_threshold,
100,
);
if base_limit != 100 {
self.power.set_charge_control_end_threshold(100)?;
self.config.lock().await.base_charge_control_end_threshold = base_limit;
self.config.lock().await.write();
}
Ok(())
}
#[zbus(property)] #[zbus(property)]
fn gpu_mux_mode(&self) -> Result<u8, FdoErr> { fn gpu_mux_mode(&self) -> Result<u8, FdoErr> {
self.platform.get_gpu_mux_mode().map_err(|err| { self.platform.get_gpu_mux_mode().map_err(|err| {
@@ -383,7 +417,7 @@ impl CtrlPlatform {
/// If fan-curves are supported will also activate a fan curve for profile. /// If fan-curves are supported will also activate a fan curve for profile.
async fn next_throttle_thermal_policy( async fn next_throttle_thermal_policy(
&mut self, &mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>, #[zbus(signal_context)] ctxt: SignalEmitter<'_>,
) -> Result<(), FdoErr> { ) -> Result<(), FdoErr> {
let policy: ThrottlePolicy = let policy: ThrottlePolicy =
platform_get_value!(self, throttle_thermal_policy, "throttle_thermal_policy") platform_get_value!(self, throttle_thermal_policy, "throttle_thermal_policy")
@@ -714,19 +748,24 @@ impl ReloadAndNotify for CtrlPlatform {
/// Called on config file changed externally /// Called on config file changed externally
async fn reload_and_notify( async fn reload_and_notify(
&mut self, &mut self,
signal_context: &SignalContext<'static>, signal_context: &SignalEmitter<'static>,
data: Self::Data, data: Self::Data,
) -> Result<(), RogError> { ) -> Result<(), RogError> {
let mut config = self.config.lock().await; let mut config = self.config.lock().await;
if *config != data { if *config != data {
info!("asusd.ron updated externally, reloading and updating internal copy"); info!("asusd.ron updated externally, reloading and updating internal copy");
let mut base_charge_control_end_threshold = None;
if self.power.has_charge_control_end_threshold() { if self.power.has_charge_control_end_threshold() {
let limit = data.charge_control_end_threshold; let limit = data.charge_control_end_threshold;
warn!("setting charge_control_end_threshold to {limit}"); warn!("setting charge_control_end_threshold to {limit}");
self.power.set_charge_control_end_threshold(limit)?; self.power.set_charge_control_end_threshold(limit)?;
self.charge_control_end_threshold_changed(signal_context) self.charge_control_end_threshold_changed(signal_context)
.await?; .await?;
base_charge_control_end_threshold = (config.base_charge_control_end_threshold > 0)
.then_some(config.base_charge_control_end_threshold)
.or(Some(limit));
} }
if self.platform.has_throttle_thermal_policy() if self.platform.has_throttle_thermal_policy()
@@ -775,6 +814,8 @@ impl ReloadAndNotify for CtrlPlatform {
ppt_reload_and_notify!(nv_temp_target, "nv_temp_target"); ppt_reload_and_notify!(nv_temp_target, "nv_temp_target");
*config = data; *config = data;
config.base_charge_control_end_threshold =
base_charge_control_end_threshold.unwrap_or_default();
} }
Ok(()) Ok(())
@@ -785,6 +826,7 @@ impl crate::Reloadable for CtrlPlatform {
async fn reload(&mut self) -> Result<(), RogError> { async fn reload(&mut self) -> Result<(), RogError> {
info!("Begin Platform settings restore"); info!("Begin Platform settings restore");
if self.power.has_charge_control_end_threshold() { if self.power.has_charge_control_end_threshold() {
// self.restore_charge_limit().await;
let limit = self.config.lock().await.charge_control_end_threshold; let limit = self.config.lock().await.charge_control_end_threshold;
info!("reloading charge_control_end_threshold to {limit}"); info!("reloading charge_control_end_threshold to {limit}");
self.power.set_charge_control_end_threshold(limit)?; self.power.set_charge_control_end_threshold(limit)?;
@@ -877,7 +919,7 @@ impl CtrlTask for CtrlPlatform {
PLATFORM_ZBUS_PATH PLATFORM_ZBUS_PATH
} }
async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> { async fn create_tasks(&self, signal_ctxt: SignalEmitter<'static>) -> Result<(), RogError> {
let platform1 = self.clone(); let platform1 = self.clone();
let platform2 = self.clone(); let platform2 = self.clone();
let platform3 = self.clone(); let platform3 = self.clone();
@@ -945,6 +987,23 @@ impl CtrlTask for CtrlPlatform {
}) })
.ok(); .ok();
} }
if shutting_down
&& platform2.power.has_charge_control_end_threshold()
&& lock.base_charge_control_end_threshold > 0
{
info!("RogPlatform restoring charge_control_end_threshold");
platform2
.power
.set_charge_control_end_threshold(
lock.base_charge_control_end_threshold,
)
.map_err(|err| {
warn!("CtrlCharge: charge_control_end_threshold {}", err);
err
})
.ok();
}
} }
}, },
move |_lid_closed| { move |_lid_closed| {
@@ -962,6 +1021,10 @@ impl CtrlTask for CtrlPlatform {
.await; .await;
} }
platform3.run_ac_or_bat_cmd(power_plugged).await; platform3.run_ac_or_bat_cmd(power_plugged).await;
// In case one-shot charge was used, restore the old charge limit
if platform3.power.has_charge_control_end_threshold() && !power_plugged {
platform3.restore_charge_limit().await;
}
} }
}, },
) )
-98
View File
@@ -1,98 +0,0 @@
pub mod config;
pub mod trait_impls;
use config_traits::{StdConfig, StdConfigLoad};
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: Node,
config: SlashConfig,
}
impl CtrlSlash {
#[inline]
pub fn new() -> 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: SlashConfig::new().load(),
};
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(())
}
}
-165
View File
@@ -1,165 +0,0 @@
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
#[zbus(property)]
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
#[zbus(property)]
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)
#[zbus(property)]
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)
#[zbus(property)]
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(())
}
}
+4 -33
View File
@@ -4,14 +4,10 @@ use std::sync::Arc;
use ::zbus::export::futures_util::lock::Mutex; use ::zbus::export::futures_util::lock::Mutex;
use ::zbus::Connection; use ::zbus::Connection;
use asusd::aura_manager::DeviceManager;
use asusd::config::Config; use asusd::config::Config;
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::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, StdConfigLoad1};
use log::{error, info}; use log::{error, info};
@@ -25,11 +21,12 @@ 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 => false, None => true,
}; };
if !is_service { if !is_service {
@@ -96,33 +93,7 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
} }
} }
match CtrlAnime::new() { let _ = DeviceManager::new(connection.clone()).await?;
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() {
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
connection.request_name(DBUS_NAME).await?; connection.request_name(DBUS_NAME).await?;
+18 -16
View File
@@ -1,17 +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 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;
@@ -22,8 +22,10 @@ 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::{CacheProperties, Connection, SignalContext}; use zbus::Connection;
use crate::error::RogError; use crate::error::RogError;
@@ -39,7 +41,7 @@ pub static DBUS_IFACE: &str = "org.asuslinux.Daemon";
/// 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>(SignalContext, SomeValue)` /// - `notify_<name>(SignalEmitter, 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
@@ -60,7 +62,7 @@ macro_rules! task_watch_item {
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: SignalContext<'static>, signal_ctxt: SignalEmitter<'static>,
) -> Result<(), RogError> { ) -> Result<(), RogError> {
use zbus::export::futures_util::StreamExt; use zbus::export::futures_util::StreamExt;
@@ -100,7 +102,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: SignalContext<'static>, signal_ctxt: SignalEmitter<'static>,
) -> Result<(), RogError> { ) -> Result<(), RogError> {
use zbus::export::futures_util::StreamExt; use zbus::export::futures_util::StreamExt;
@@ -143,7 +145,7 @@ pub trait ReloadAndNotify {
fn reload_and_notify( fn reload_and_notify(
&mut self, &mut self,
signal_context: &SignalContext<'static>, signal_context: &SignalEmitter<'static>,
data: Self::Data, data: Self::Data,
) -> impl Future<Output = Result<(), RogError>> + Send; ) -> impl Future<Output = Result<(), RogError>> + Send;
} }
@@ -152,7 +154,7 @@ 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 zbus::Interface, iface: impl Interface,
path: &str, path: &str,
server: &mut Connection, server: &mut Connection,
) -> impl Future<Output = ()> + Send { ) -> impl Future<Output = ()> + Send {
@@ -174,8 +176,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<SignalContext<'static>, zbus::Error> { fn signal_context(connection: &Connection) -> Result<SignalEmitter<'static>, zbus::Error> {
SignalContext::new(connection, Self::zbus_path()) SignalEmitter::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
@@ -183,7 +185,7 @@ pub trait CtrlTask {
/// separate thread. /// separate thread.
fn create_tasks( fn create_tasks(
&self, &self,
signal: SignalContext<'static>, signal: SignalEmitter<'static>,
) -> impl Future<Output = Result<(), RogError>> + Send; ) -> impl Future<Output = Result<(), RogError>> + Send;
// /// Create a timed repeating task // /// Create a timed repeating task
@@ -297,7 +299,7 @@ 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: SignalContext<'static>, signal_ctx: SignalEmitter<'static>,
) -> Result<(), RogError> ) -> Result<(), RogError>
where where
T: ZbusRun + Reloadable + CtrlTask + Clone, T: ZbusRun + Reloadable + CtrlTask + Clone,
-3
View File
@@ -13,6 +13,3 @@ serde.workspace = true
ron.workspace = true ron.workspace = true
log.workspace = true log.workspace = true
[dev-dependencies]
cargo-husky.workspace = true
+5 -5
View File
@@ -146,11 +146,7 @@ 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!( warn!("Renaming {} to {}-old", self.file_name(), self.file_name());
"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| {
@@ -274,6 +270,8 @@ mod tests {
} }
} }
let _ = Test {};
impl crate::StdConfigLoad1<Old1> for Test {} impl crate::StdConfigLoad1<Old1> for Test {}
} }
@@ -323,6 +321,8 @@ mod tests {
} }
} }
let _ = Test {};
impl crate::StdConfigLoad3<Old1, Old2, Old3> for Test {} impl crate::StdConfigLoad3<Old1, Old2, Old3> for Test {}
} }
} }
+2
View File
@@ -7,6 +7,8 @@ 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"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 75 KiB

-4
View File
@@ -46,11 +46,7 @@ BuildRequires: cmake
BuildRequires: rust BuildRequires: rust
BuildRequires: rust-std-static BuildRequires: rust-std-static
BuildRequires: pkgconfig(expat) BuildRequires: pkgconfig(expat)
BuildRequires: pkgconfig(gbm)
BuildRequires: pkgconfig(dbus-1) BuildRequires: pkgconfig(dbus-1)
BuildRequires: pkgconfig(libdrm)
BuildRequires: pkgconfig(libinput)
BuildRequires: pkgconfig(libseat)
BuildRequires: pkgconfig(libudev) BuildRequires: pkgconfig(libudev)
BuildRequires: pkgconfig(xkbcommon) BuildRequires: pkgconfig(xkbcommon)
BuildRequires: pkgconfig(libzstd) BuildRequires: pkgconfig(libzstd)
+1 -3
View File
@@ -1,4 +1,4 @@
use log::{info, warn}; use log::warn;
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Clone)] #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Clone)]
pub struct DMIID { pub struct DMIID {
@@ -33,8 +33,6 @@ 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")
-7
View File
@@ -28,7 +28,6 @@ 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 typeshare.workspace = true
@@ -36,9 +35,3 @@ 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"]
+21 -6
View File
@@ -3,8 +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_derive::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use typeshare::typeshare; use typeshare::typeshare;
#[cfg(feature = "dbus")] #[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value}; use zbus::zvariant::{OwnedValue, Type, Value};
@@ -57,12 +58,13 @@ pub struct DeviceState {
#[typeshare] #[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)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize, Default)]
pub enum AnimeType { pub enum AnimeType {
GA401, GA401,
GA402, GA402,
GU604, GU604,
Unknown, #[default]
Unsupported,
} }
impl FromStr for AnimeType { impl FromStr for AnimeType {
@@ -73,12 +75,25 @@ 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::Unknown, _ => Self::Unsupported,
}) })
} }
} }
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 {
@@ -165,7 +180,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::Unknown => vec![[0; 640]; 3], AnimeType::GA402 | AnimeType::GU604 | AnimeType::Unsupported => 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() {
@@ -176,7 +191,7 @@ impl TryFrom<AnimeDataBuffer> for AnimePacketType {
if matches!( if matches!(
anime.anime, anime.anime,
AnimeType::GA402 | AnimeType::GU604 | AnimeType::Unknown AnimeType::GA402 | AnimeType::GU604 | AnimeType::Unsupported
) { ) {
buffers[2][..7].copy_from_slice(&USB_PREFIX3); buffers[2][..7].copy_from_slice(&USB_PREFIX3);
} }
+6 -1
View File
@@ -3,6 +3,8 @@
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;
@@ -49,7 +51,10 @@ impl AnimeDiagonal {
bright: f32, bright: f32,
anime_type: AnimeType, anime_type: AnimeType,
) -> Result<Self> { ) -> Result<Self> {
let data = std::fs::read(path)?; let data = std::fs::read(path).map_err(|e| {
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)??;
+10 -4
View File
@@ -4,7 +4,8 @@ use std::path::Path;
use std::time::Duration; use std::time::Duration;
use glam::Vec2; use glam::Vec2;
use serde_derive::{Deserialize, Serialize}; use log::error;
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};
@@ -107,7 +108,10 @@ 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)?; let file = File::open(file_name).map_err(|e| {
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();
@@ -186,12 +190,14 @@ impl AnimeGif {
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)?; let file = File::open(file_name).map_err(|e| {
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();
+5 -1
View File
@@ -3,6 +3,7 @@ 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};
@@ -421,7 +422,10 @@ impl AnimeImage {
bright: f32, bright: f32,
anime_type: AnimeType, anime_type: AnimeType,
) -> Result<Self> { ) -> Result<Self> {
let data = std::fs::read(path)?; let data = std::fs::read(path).map_err(|e| {
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)??;
+1 -1
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_derive::{Deserialize, Serialize}; use serde::{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};
+12 -12
View File
@@ -11,7 +11,7 @@
use std::str::FromStr; use std::str::FromStr;
use dmi_id::DMIID; use dmi_id::DMIID;
use serde_derive::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use typeshare::typeshare; use typeshare::typeshare;
#[cfg(feature = "dbus")] #[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value}; use zbus::zvariant::{OwnedValue, Type, Value};
@@ -241,25 +241,25 @@ impl From<AnimShutdown> for i32 {
} }
} }
/// `get_anime_type` is very broad, matching on part of the laptop board name /// `get_maybe_anime_type` is very broad, matching on part of the laptop board
/// only. For this reason `find_node()` must be used also to verify if the USB /// name only. For this reason `find_node()` must be used also to verify if the
/// device is available. /// USB device is available.
/// ///
/// The currently known USB device is `19b6`. /// The currently known USB device is `19b6`.
#[inline] #[inline]
pub fn get_anime_type() -> Result<AnimeType, AnimeError> { pub fn get_anime_type() -> AnimeType {
let dmi = DMIID::new().map_err(|_| AnimeError::NoDevice)?; // TODO: better error let dmi = DMIID::new().unwrap_or_default();
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") {
return Ok(AnimeType::GA401); AnimeType::GA401
} else if board_name.contains("GA402R") { } else if board_name.contains("GA402R") || board_name.contains("GA402X") {
return Ok(AnimeType::GA402); AnimeType::GA402
} else if board_name.contains("GU604V") { } else if board_name.contains("GU604V") {
return Ok(AnimeType::GU604); 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
-4
View File
@@ -18,7 +18,6 @@ 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" }
@@ -27,6 +26,3 @@ log.workspace = true
typeshare.workspace = true typeshare.workspace = true
ron = { version = "*", optional = true } ron = { version = "*", optional = true }
[dev-dependencies]
cargo-husky.workspace = true
+201 -183
View File
@@ -3,7 +3,16 @@
device_name: "FA506I", device_name: "FA506I",
product_id: "", product_id: "",
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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],
@@ -12,7 +21,7 @@
device_name: "FA506Q", device_name: "FA506Q",
product_id: "", product_id: "",
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe, Rainbow], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -21,7 +30,7 @@
device_name: "FA507", device_name: "FA507",
product_id: "", product_id: "",
layout_name: "fa507", layout_name: "fa507",
basic_modes: [Static, Breathe, Strobe, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -30,7 +39,7 @@
device_name: "FX505", device_name: "FX505",
product_id: "", product_id: "",
layout_name: "fx505d", layout_name: "fx505d",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -39,7 +48,7 @@
device_name: "FX506", device_name: "FX506",
product_id: "", product_id: "",
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -48,7 +57,7 @@
device_name: "FX507Z", device_name: "FX507Z",
product_id: "", product_id: "",
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -57,7 +66,16 @@
device_name: "FX516P", device_name: "FX516P",
product_id: "", product_id: "",
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe], basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "FX517Z",
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],
@@ -66,25 +84,25 @@
device_name: "FX705D", device_name: "FX705D",
product_id: "", product_id: "",
layout_name: "fx505d", layout_name: "fx505d",
basic_modes: [Static, Breathe, Strobe, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
device_name: "G512", device_name: "G512L",
product_id: "", product_id: "",
layout_name: "g512", layout_name: "g512",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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, Lightbar],
), ),
( (
device_name: "G513I", device_name: "G513I",
product_id: "", product_id: "",
layout_name: "g513i", layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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],
@@ -92,89 +110,44 @@
( (
device_name: "G513Q", device_name: "G513Q",
product_id: "", product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "G513QE",
product_id: "",
layout_name: "g513i", layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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, Lightbar],
), ),
( (
device_name: "G513QY", device_name: "G513QR",
product_id: "", product_id: "",
layout_name: "g513i-per-key", layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard], power_zones: [Keyboard, Lightbar],
), ),
( (
device_name: "G513R", device_name: "G513R",
product_id: "", product_id: "",
layout_name: "g513i-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: "G513RC",
product_id: "",
layout_name: "g513i", layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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],
), ),
( (
device_name: "G513RW", device_name: "G531G",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
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: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "G531GD",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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 +156,7 @@
device_name: "G531GW", device_name: "G531GW",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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,7 +165,7 @@
device_name: "G532", device_name: "G532",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], 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],
@@ -201,16 +174,16 @@
device_name: "G533Q", device_name: "G533Q",
product_id: "1866", product_id: "1866",
layout_name: "g533q-per-key", layout_name: "g533q-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], 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, 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, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], 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],
@@ -219,16 +192,34 @@
device_name: "G614J", device_name: "G614J",
product_id: "", product_id: "",
layout_name: "g634j-per-key", layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, Pulse, Strobe, Rainbow], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
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, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], 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, Lightbar, Logo, RearGlow], power_zones: [Keyboard, Lightbar, Logo, RearGlow],
@@ -237,7 +228,7 @@
device_name: "G712LI", device_name: "G712LI",
product_id: "", product_id: "",
layout_name: "gl503", layout_name: "gl503",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -246,7 +237,7 @@
device_name: "G712LV", device_name: "G712LV",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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],
@@ -255,7 +246,7 @@
device_name: "G712LW", device_name: "G712LW",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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],
@@ -264,43 +255,61 @@
device_name: "G713IC", device_name: "G713IC",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: PerKey, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard, Lightbar],
), ),
( (
device_name: "G713P", device_name: "G713P",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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, Lightbar],
),
(
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: "ga401q", layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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], power_zones: [Keyboard, Lightbar],
), ),
( (
device_name: "G713QR", device_name: "G713QR",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], 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, Lightbar],
), ),
( (
device_name: "G713RC", device_name: "G713RC",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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],
@@ -309,7 +318,7 @@
device_name: "G713RM", device_name: "G713RM",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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],
@@ -318,7 +327,7 @@
device_name: "G713RS", device_name: "G713RS",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], 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],
@@ -327,7 +336,7 @@
device_name: "G713RW", device_name: "G713RW",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4, BarLeft, BarRight], basic_zones: [Key1, Key2, Key3, Key4, BarLeft, BarRight],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
@@ -336,25 +345,16 @@
device_name: "G731", device_name: "G731",
product_id: "", product_id: "",
layout_name: "g533q", layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: "G731GT", device_name: "G731G",
product_id: "", product_id: "",
layout_name: "g533q", layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
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],
@@ -363,7 +363,7 @@
device_name: "G731GV", device_name: "G731GV",
product_id: "", product_id: "",
layout_name: "g533q", layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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],
@@ -372,7 +372,7 @@
device_name: "G731GW", device_name: "G731GW",
product_id: "", product_id: "",
layout_name: "g533q", layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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],
@@ -381,16 +381,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, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], 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, Lightbar, Logo, Lid], power_zones: [Keyboard, Lightbar, Logo, Lid],
), ),
( (
device_name: "G733PZ", device_name: "G733P",
product_id: "", product_id: "",
layout_name: "g733pz-per-key", layout_name: "g733pz-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], 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, Lightbar], power_zones: [Keyboard, Lightbar],
@@ -399,7 +399,7 @@
device_name: "G733Q", device_name: "G733Q",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], 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],
@@ -408,16 +408,16 @@
device_name: "G733Z", device_name: "G733Z",
product_id: "", product_id: "",
layout_name: "g513i-per-key", layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], 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, Lightbar, Logo, Lid],
), ),
( (
device_name: "G814J", device_name: "G814J",
product_id: "", product_id: "",
layout_name: "g814ji-per-key", layout_name: "g814ji-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], 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, Lightbar], power_zones: [Keyboard, Lightbar],
@@ -426,11 +426,20 @@
device_name: "G834J", device_name: "G834J",
product_id: "", product_id: "",
layout_name: "g814ji-per-key", layout_name: "g814ji-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], 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, 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: "",
@@ -444,7 +453,16 @@
device_name: "GA402N", device_name: "GA402N",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse, Rainbow, Strobe], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
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],
@@ -453,7 +471,7 @@
device_name: "GA402R", device_name: "GA402R",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse, Rainbow], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -462,16 +480,16 @@
device_name: "GA402X", device_name: "GA402X",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse, Rainbow], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
device_name: "GA402XV", device_name: "GA402XV-NC012",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Comet], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -489,7 +507,7 @@
device_name: "GA503Q", device_name: "GA503Q",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse, Rainbow, Strobe], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -507,16 +525,25 @@
device_name: "GA503R", device_name: "GA503R",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse, Rainbow, Strobe], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
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, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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],
@@ -525,7 +552,7 @@
device_name: "GL503V", device_name: "GL503V",
product_id: "", product_id: "",
layout_name: "gl503", layout_name: "gl503",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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],
@@ -534,7 +561,7 @@
device_name: "GL504G", device_name: "GL504G",
product_id: "", product_id: "",
layout_name: "gl503", layout_name: "gl503",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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],
@@ -543,7 +570,7 @@
device_name: "GL531", device_name: "GL531",
product_id: "", product_id: "",
layout_name: "g512", layout_name: "g512",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], 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],
@@ -552,7 +579,7 @@
device_name: "GL553V", device_name: "GL553V",
product_id: "", product_id: "",
layout_name: "g533q", layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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],
@@ -561,7 +588,7 @@
device_name: "GL703G", device_name: "GL703G",
product_id: "", product_id: "",
layout_name: "gl503", layout_name: "gl503",
basic_modes: [Static, Breathe, Strobe, Rainbow], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -570,43 +597,25 @@
device_name: "GM501G", device_name: "GM501G",
product_id: "", product_id: "",
layout_name: "fa507", layout_name: "fa507",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: "GU502L", device_name: "GU502L",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], 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],
), ),
(
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, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: Zoned([SingleZone]), advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -615,7 +624,7 @@
device_name: "GU603V", device_name: "GU603V",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: Zoned([SingleZone]), advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -624,7 +633,7 @@
device_name: "GU603Z", device_name: "GU603Z",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: Zoned([SingleZone]), advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -633,7 +642,7 @@
device_name: "GU604V", device_name: "GU604V",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: Zoned([SingleZone]), advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -642,7 +651,16 @@
device_name: "GU605M", device_name: "GU605M",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
),
(
device_name: "GA605W",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: Zoned([SingleZone]), advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -678,7 +696,7 @@
device_name: "GV601R", device_name: "GV601R",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -696,7 +714,7 @@
device_name: "GV604V", device_name: "GV604V",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -705,7 +723,7 @@
device_name: "GX502", device_name: "GX502",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], 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],
@@ -714,7 +732,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, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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],
@@ -723,7 +741,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, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], 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],
@@ -732,7 +750,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, Strobe, Rainbow, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -741,7 +759,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, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], 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],
@@ -750,7 +768,7 @@
device_name: "GX650R", device_name: "GX650R",
product_id: "", product_id: "",
layout_name: "gx531-per-key", layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], 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],
@@ -759,7 +777,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, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], 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],
@@ -768,7 +786,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, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Keyboard],
@@ -791,15 +809,6 @@
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",
@@ -822,9 +831,18 @@
device_name: "RC71L", device_name: "RC71L",
product_id: "", product_id: "",
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: None,
power_zones: [Keyboard], power_zones: [Ally],
),
(
device_name: "RC72L",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Ally],
), ),
]) ])
+58 -10
View File
@@ -1,6 +1,8 @@
use std::env;
use dmi_id::DMIID; use dmi_id::DMIID;
use log::{error, info, warn}; use log::{error, info, warn};
use serde_derive::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::keyboard::AdvancedAuraType; use crate::keyboard::AdvancedAuraType;
use crate::{AuraModeNum, AuraZone, PowerZones}; use crate::{AuraModeNum, AuraZone, PowerZones};
@@ -60,16 +62,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 dmi = DMIID::new().unwrap_or_default(); let mut 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() {
if let Some(data) = data.match_device(&dmi.board_name, product_id) { return data.match_device(&dmi.board_name, product_id);
return data;
}
} }
info!("Using generic LED control for keyboard brightness only"); info!("Using generic LED control for keyboard brightness only. No aura_support file found");
let mut data = LedSupportData::default(); let mut data = LedSupportData::default();
data.power_zones.push(PowerZones::Keyboard); data.power_zones.push(PowerZones::Keyboard);
data data
@@ -86,7 +89,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) -> Option<LedSupportData> { fn match_device(&self, device_name: &str, product_id: &str) -> 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);
@@ -94,15 +97,27 @@ 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 Some(config.clone()); return config.clone();
} else { } else {
continue; continue;
} }
} }
return Some(config.clone()); return config.clone();
} }
} }
None warn!(
"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
@@ -156,6 +171,7 @@ 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;
@@ -197,6 +213,11 @@ 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(|a, b| (*a as u8).cmp(&(*b 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();
@@ -219,4 +240,31 @@ 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()
// );
}
} }
+51 -75
View File
@@ -1,13 +1,13 @@
use std::fmt::Display; use std::fmt::Display;
use std::str::FromStr; use std::str::FromStr;
use serde_derive::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use typeshare::typeshare; 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::LED_MSG_LEN; use crate::AURA_LAPTOP_LED_MSG_LEN;
#[typeshare] #[typeshare]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)] #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
@@ -261,8 +261,8 @@ pub enum AuraModeNum {
#[default] #[default]
Static = 0, Static = 0,
Breathe = 1, Breathe = 1,
Strobe = 2, RainbowCycle = 2,
Rainbow = 3, RainbowWave = 3,
Star = 4, Star = 4,
Rain = 5, Rain = 5,
Highlight = 6, Highlight = 6,
@@ -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::Strobe => "Strobe", AuraModeNum::RainbowCycle => "RainbowCycle",
AuraModeNum::Rainbow => "Rainbow", AuraModeNum::RainbowWave => "RainbowWave",
AuraModeNum::Star => "Stars", AuraModeNum::Star => "Stars",
AuraModeNum::Rain => "Rain", AuraModeNum::Rain => "Rain",
AuraModeNum::Highlight => "Highlight", AuraModeNum::Highlight => "Highlight",
@@ -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,
"Strobe" => AuraModeNum::Strobe, "RainbowCycle" => AuraModeNum::RainbowCycle,
"Rainbow" => AuraModeNum::Rainbow, "RainbowWave" => AuraModeNum::RainbowWave,
"Stars" => AuraModeNum::Star, "Stars" => AuraModeNum::Star,
"Rain" => AuraModeNum::Rain, "Rain" => AuraModeNum::Rain,
"Highlight" => AuraModeNum::Highlight, "Highlight" => AuraModeNum::Highlight,
@@ -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::Strobe, 2 => AuraModeNum::RainbowCycle,
3 => AuraModeNum::Rainbow, 3 => AuraModeNum::RainbowWave,
4 => AuraModeNum::Star, 4 => AuraModeNum::Star,
5 => AuraModeNum::Rain, 5 => AuraModeNum::Rain,
6 => AuraModeNum::Highlight, 6 => AuraModeNum::Highlight,
@@ -434,7 +434,7 @@ impl From<AuraZone> for i32 {
/// ``` /// ```
#[typeshare] #[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))] #[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct AuraEffect { pub struct AuraEffect {
/// The effect type /// The effect type
pub mode: AuraModeNum, pub mode: AuraModeNum,
@@ -494,56 +494,6 @@ 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:
@@ -552,9 +502,9 @@ impl 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; LED_MSG_LEN] { impl From<&AuraEffect> for [u8; AURA_LAPTOP_LED_MSG_LEN] {
fn from(aura: &AuraEffect) -> Self { fn from(aura: &AuraEffect) -> Self {
let mut msg = [0u8; LED_MSG_LEN]; let mut msg = [0u8; AURA_LAPTOP_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;
@@ -573,7 +523,7 @@ impl From<&AuraEffect> for [u8; 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; LED_MSG_LEN]; let mut msg = vec![0u8; AURA_LAPTOP_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;
@@ -592,7 +542,9 @@ impl From<&AuraEffect> for Vec<u8> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed, LED_MSG_LEN}; use crate::{
AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed, AURA_LAPTOP_LED_MSG_LEN,
};
#[test] #[test]
fn check_led_static_packet() { fn check_led_static_packet() {
@@ -608,7 +560,7 @@ mod tests {
speed: Speed::Med, speed: Speed::Med,
direction: Direction::Right, direction: Direction::Right,
}; };
let ar = <[u8; LED_MSG_LEN]>::from(&st); let ar = <[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st);
println!("{:02x?}", ar); println!("{:02x?}", ar);
let check = [ let check = [
@@ -636,7 +588,10 @@ mod tests {
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!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]); assert_eq!(
<[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 {
@@ -648,7 +603,10 @@ mod tests {
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!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]); assert_eq!(
<[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 {
@@ -660,7 +618,10 @@ mod tests {
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!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]); assert_eq!(
<[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 {
@@ -672,7 +633,10 @@ mod tests {
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!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]); assert_eq!(
<[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 {
@@ -684,7 +648,10 @@ mod tests {
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!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]); assert_eq!(
<[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 {
@@ -696,7 +663,10 @@ mod tests {
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!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]); assert_eq!(
<[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 {
@@ -708,13 +678,19 @@ mod tests {
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!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]); assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.mode = AuraModeNum::Rainbow; st.mode = AuraModeNum::RainbowWave;
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!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]); assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
} }
} }
+1
View File
@@ -2,6 +2,7 @@ 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,
+3 -3
View File
@@ -1,4 +1,4 @@
use serde_derive::{Deserialize, Serialize}; use serde::{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::{KeyLayout, LedCode, LedUsbPackets, UsbPackets}; use crate::keyboard::{AuraLaptopUsbPackets, KeyLayout, LedCode, LedUsbPackets};
use crate::Colour; use crate::Colour;
// static mut RNDINDEX: usize = 0; // static mut RNDINDEX: usize = 0;
@@ -106,7 +106,7 @@ impl AdvancedEffects {
} }
} }
pub fn create_packets(&self) -> UsbPackets { pub fn create_packets(&self) -> AuraLaptopUsbPackets {
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
+11 -11
View File
@@ -195,7 +195,7 @@ impl LedCode {
/// Represents the per-key raw USB packets /// Represents the per-key raw USB packets
#[typeshare] #[typeshare]
pub type UsbPackets = Vec<Vec<u8>>; pub type AuraLaptopUsbPackets = 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.
@@ -209,7 +209,7 @@ pub type UsbPackets = Vec<Vec<u8>>;
#[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: UsbPackets, usb_packets: AuraLaptopUsbPackets,
/// 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
@@ -472,22 +472,22 @@ impl LedUsbPackets {
} }
#[inline] #[inline]
pub fn get(&self) -> UsbPackets { pub fn get(&self) -> AuraLaptopUsbPackets {
self.usb_packets.clone() self.usb_packets.clone()
} }
#[inline] #[inline]
pub fn get_ref(&self) -> &UsbPackets { pub fn get_ref(&self) -> &AuraLaptopUsbPackets {
&self.usb_packets &self.usb_packets
} }
#[inline] #[inline]
pub fn get_mut(&mut self) -> &mut UsbPackets { pub fn get_mut(&mut self) -> &mut AuraLaptopUsbPackets {
&mut self.usb_packets &mut self.usb_packets
} }
} }
impl From<LedUsbPackets> for UsbPackets { impl From<LedUsbPackets> for AuraLaptopUsbPackets {
fn from(k: LedUsbPackets) -> Self { fn from(k: LedUsbPackets) -> Self {
k.usb_packets k.usb_packets
} }
@@ -643,7 +643,7 @@ impl From<&LedCode> for &str {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::keyboard::{LedCode, LedUsbPackets, UsbPackets}; use crate::keyboard::{AuraLaptopUsbPackets, LedCode, LedUsbPackets};
macro_rules! colour_check_zoned { macro_rules! colour_check_zoned {
($zone:expr, $pkt_idx_start:expr) => { ($zone:expr, $pkt_idx_start:expr) => {
@@ -653,7 +653,7 @@ mod tests {
c[1] = 255; c[1] = 255;
c[2] = 255; c[2] = 255;
let pkt: UsbPackets = zone.into(); let pkt: AuraLaptopUsbPackets = 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);
@@ -663,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: UsbPackets = zone.into(); let pkt: AuraLaptopUsbPackets = 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);
@@ -686,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: UsbPackets = per_key.into(); let pkt: AuraLaptopUsbPackets = 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);
@@ -712,7 +712,7 @@ mod tests {
c[1] = 255; c[1] = 255;
c[2] = 255; c[2] = 255;
let pkt: UsbPackets = per_key.into(); let pkt: AuraLaptopUsbPackets = 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
+1 -1
View File
@@ -488,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.get(&k.1).is_some() { if data.key_shapes.contains_key(&k.1) {
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);
+24 -11
View File
@@ -102,7 +102,7 @@ impl AuraPowerState {
] ]
} }
fn new_to_byte(&self) -> u32 { pub fn new_to_byte(&self) -> u32 {
match self.zone { match self.zone {
PowerZones::Logo => { PowerZones::Logo => {
self.boot as u32 self.boot as u32
@@ -110,6 +110,12 @@ impl AuraPowerState {
| (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
@@ -187,14 +193,14 @@ 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::LaptopPost2021 => { AuraDeviceType::Unknown | AuraDeviceType::Ally | AuraDeviceType::LaptopKeyboard2021 => {
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::LaptopPre2021 => { AuraDeviceType::LaptopKeyboardPre2021 => {
// The older devices are tri-state if have lightbar: // The older devices are tri-state if have lightbar:
// 1. Keyboard // 1. Keyboard
// 2. Lightbar // 2. Lightbar
@@ -209,17 +215,23 @@ impl LaptopAuraPower {
} }
} }
} }
AuraDeviceType::LaptopTuf => Self { AuraDeviceType::LaptopKeyboardTuf => 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::LaptopPost2021 => self.new_to_bytes(), AuraDeviceType::LaptopKeyboard2021 | AuraDeviceType::Ally => self.new_to_bytes(),
AuraDeviceType::LaptopPre2021 => { AuraDeviceType::LaptopKeyboardPre2021 => {
if self.states.len() == 1 { if self.states.len() == 1 {
self.states self.states
.first() .first()
@@ -238,7 +250,7 @@ impl LaptopAuraPower {
b b
} }
} }
AuraDeviceType::LaptopTuf => self AuraDeviceType::LaptopKeyboardTuf => self
.states .states
.first() .first()
.cloned() .cloned()
@@ -249,6 +261,7 @@ impl LaptopAuraPower {
self.new_to_bytes() self.new_to_bytes()
} }
AuraDeviceType::ScsiExtDisk => todo!("scsi disk not implemented yet"), AuraDeviceType::ScsiExtDisk => todo!("scsi disk not implemented yet"),
AuraDeviceType::AnimeOrSlash => todo!("anime/slash not implemented yet"),
} }
} }
} }
@@ -298,7 +311,7 @@ mod test {
use crate::{AuraDeviceType, PowerZones}; use crate::{AuraDeviceType, PowerZones};
fn to_binary_string_post2021(power: &LaptopAuraPower) -> String { fn to_binary_string_post2021(power: &LaptopAuraPower) -> String {
let bytes = power.to_bytes(AuraDeviceType::LaptopPost2021); let bytes = power.to_bytes(AuraDeviceType::LaptopKeyboard2021);
format!( format!(
"{:08b}, {:08b}, {:08b}, {:08b}", "{:08b}, {:08b}, {:08b}, {:08b}",
bytes[0], bytes[1], bytes[2], bytes[3] bytes[0], bytes[1], bytes[2], bytes[3]
@@ -316,7 +329,7 @@ mod test {
shutdown: false, shutdown: false,
}], }],
}; };
let bytes = power.to_bytes(AuraDeviceType::LaptopPre2021); let bytes = power.to_bytes(AuraDeviceType::LaptopKeyboardPre2021);
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]);
@@ -329,7 +342,7 @@ mod test {
shutdown: false, shutdown: false,
}], }],
}; };
let bytes = power.to_bytes(AuraDeviceType::LaptopPre2021); let bytes = power.to_bytes(AuraDeviceType::LaptopKeyboardPre2021);
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]);
@@ -358,7 +371,7 @@ mod test {
}, },
], ],
}; };
let bytes = power.to_bytes(AuraDeviceType::LaptopPre2021); let bytes = power.to_bytes(AuraDeviceType::LaptopKeyboardPre2021);
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]);
} }
+21 -10
View File
@@ -24,7 +24,7 @@ pub mod usb;
pub mod keyboard; pub mod keyboard;
pub const LED_MSG_LEN: usize = 17; pub const AURA_LAPTOP_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 {
@@ -70,24 +70,30 @@ pub const GRADIENT: [Colour; 7] = [RED, VIOLET, BLUE, TEAL, GREEN, YELLOW, ORANG
pub enum AuraDeviceType { pub enum AuraDeviceType {
/// Most new laptops /// Most new laptops
#[default] #[default]
LaptopPost2021 = 0, LaptopKeyboard2021 = 0,
LaptopPre2021 = 1, LaptopKeyboardPre2021 = 1,
LaptopTuf = 2, LaptopKeyboardTuf = 2,
ScsiExtDisk = 3, ScsiExtDisk = 3,
Ally = 4,
AnimeOrSlash = 5,
Unknown = 255, Unknown = 255,
} }
impl AuraDeviceType { impl AuraDeviceType {
pub fn is_old_laptop(&self) -> bool { pub fn is_old_laptop(&self) -> bool {
*self == Self::LaptopPre2021 *self == Self::LaptopKeyboardPre2021
} }
pub fn is_tuf_laptop(&self) -> bool { pub fn is_tuf_laptop(&self) -> bool {
*self == Self::LaptopTuf *self == Self::LaptopKeyboardTuf
} }
pub fn is_new_laptop(&self) -> bool { pub fn is_new_laptop(&self) -> bool {
*self == Self::LaptopPost2021 *self == Self::LaptopKeyboard2021
}
pub fn is_ally(&self) -> bool {
*self == Self::Ally
} }
pub fn is_scsi(&self) -> bool { pub fn is_scsi(&self) -> bool {
@@ -98,10 +104,13 @@ 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::LaptopTuf, "tuf" => AuraDeviceType::LaptopKeyboardTuf,
"1932" => AuraDeviceType::ScsiExtDisk, "1932" => AuraDeviceType::ScsiExtDisk,
"1866" | "18c6" | "1869" | "1854" => Self::LaptopPre2021, "1866" | "18c6" | "1869" | "1854" => Self::LaptopKeyboardPre2021,
_ => Self::LaptopPost2021, "1abe" | "1b4c" => Self::Ally,
"19b3" | "193b" => Self::AnimeOrSlash,
"19b6" => Self::LaptopKeyboard2021,
_ => Self::Unknown,
} }
} }
} }
@@ -128,5 +137,7 @@ pub enum PowerZones {
RearGlow = 4, RearGlow = 4,
/// Exists for the older 0x1866 models /// Exists for the older 0x1866 models
KeyboardAndLightbar = 5, KeyboardAndLightbar = 5,
/// Ally specific for creating correct packet
Ally = 6,
None = 255, None = 255,
} }
+3 -9
View File
@@ -1,10 +1,4 @@
// Only these two packets must be 17 bytes // Only these two packets must be 17 bytes
pub const LED_APPLY: [u8; 17] = [0x5d, 0xb4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; pub const AURA_LAPTOP_LED_APPLY: [u8; 17] =
pub const LED_SET: [u8; 17] = [0x5d, 0xb5, 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 AURA_LAPTOP_LED_SET: [u8; 17] = [0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
/// Writes out the correct byte string for brightness
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,
]
}
+7 -9
View File
@@ -12,11 +12,14 @@ edition.workspace = true
default = [] default = []
mocking = [] mocking = []
x11 = ["slint/backend-winit-x11"] x11 = ["slint/backend-winit-x11"]
# Requires RUSTFLAGS="--cfg tokio_unstable"
tokio-debug = ["console-subscriber"]
[dependencies] [dependencies]
nix = { version = "^0.28.0", features = ["fs"] } console-subscriber = { version = "^0.4", optional = true }
tempfile = "3.3.0"
betrayer = { version = "0.2.0" } ksni = { version = "0.3", default-features = false, features = ["async-io"] }
image = "0.25.5"
asusd = { path = "../asusd" } asusd = { path = "../asusd" }
config-traits = { path = "../config-traits" } config-traits = { path = "../config-traits" }
@@ -25,7 +28,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", rev = "4eb6e97c22b68ae8d1e80500709b0c0580776ad3", default-features = false } supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", default-features = false }
dmi_id = { path = "../dmi-id" } dmi_id = { path = "../dmi-id" }
gumdrop.workspace = true gumdrop.workspace = true
@@ -34,7 +37,6 @@ 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
@@ -48,7 +50,6 @@ 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",
@@ -56,6 +57,3 @@ 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
+1 -1
View File
@@ -1,7 +1,7 @@
use std::fs::create_dir; use std::fs::create_dir;
use config_traits::{StdConfig, StdConfigLoad1}; use config_traits::{StdConfig, StdConfigLoad1};
use serde_derive::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::notify::EnabledNotifications; use crate::notify::EnabledNotifications;
-8
View File
@@ -5,7 +5,6 @@ pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
Io(std::io::Error), Io(std::io::Error),
Nix(nix::Error),
ConfigLoadFail, ConfigLoadFail,
ConfigLockFail, ConfigLockFail,
XdgVars, XdgVars,
@@ -18,7 +17,6 @@ impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Error::Io(err) => write!(f, "Failed to open: {}", err), Error::Io(err) => write!(f, "Failed to open: {}", err),
Error::Nix(err) => write!(f, "Error: {}", err),
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"),
@@ -36,12 +34,6 @@ impl From<std::io::Error> for Error {
} }
} }
impl From<nix::Error> for Error {
fn from(err: nix::Error) -> Self {
Error::Nix(err)
}
}
impl From<zbus::Error> for Error { impl From<zbus::Error> for Error {
fn from(err: zbus::Error) -> Self { fn from(err: zbus::Error) -> Self {
Error::Zbus(err) Error::Zbus(err)
+3 -73
View File
@@ -2,14 +2,8 @@
#![allow(clippy::redundant_clone, clippy::cmp_owned)] #![allow(clippy::redundant_clone, clippy::cmp_owned)]
slint::include_modules!(); slint::include_modules!();
// Intentionally reexport slint so that GUI consumers don't need to add to /// Intentionally reexport slint so that GUI consumers don't need to add to
// `Cargo.toml` /// `Cargo.toml`
use std::fs::{remove_dir_all, File, OpenOptions};
use std::io::{Read, Write};
use std::process::exit;
use std::thread::sleep;
use std::time::Duration;
pub use slint; pub use slint;
pub mod cli_options; pub mod cli_options;
@@ -21,11 +15,7 @@ pub mod notify;
pub mod tray; pub mod tray;
pub mod types; pub mod types;
pub mod ui; pub mod ui;
pub mod zbus;
use nix::sys::stat;
use nix::unistd;
use tempfile::TempDir;
// use log::{error, info, warn};
pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub const APP_ICON_PATH: &str = "/usr/share/icons/hicolor/512x512/apps/rog-control-center.png"; pub const APP_ICON_PATH: &str = "/usr/share/icons/hicolor/512x512/apps/rog-control-center.png";
@@ -42,10 +32,6 @@ pub fn print_versions() {
println!("rog-platform v{}", rog_platform::VERSION); println!("rog-platform v{}", rog_platform::VERSION);
} }
pub const SHOWING_GUI: u8 = 1;
pub const SHOW_GUI: u8 = 2;
pub const QUIT_APP: u8 = 3;
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(PartialEq, Eq, Clone, Copy)]
pub enum Page { pub enum Page {
AppSettings, AppSettings,
@@ -54,59 +40,3 @@ pub enum Page {
AnimeMatrix, AnimeMatrix,
FanCurves, FanCurves,
} }
/// Either exit the process, or return with a refreshed tmp-dir
pub fn on_tmp_dir_exists() -> Result<TempDir, std::io::Error> {
let mut buf = [0u8; 2];
let path = std::env::temp_dir().join("rog-gui");
if path.read_dir()?.next().is_none() {
std::fs::remove_dir_all(path)?;
return tempfile::Builder::new()
.prefix("rog-gui")
.rand_bytes(0)
.tempdir();
}
let mut ipc_file = OpenOptions::new()
.read(true)
.write(true)
.create(false)
.open(path.join("ipc.pipe"))?;
// If the app is running this ends up stacked on top of SHOWING_GUI
ipc_file.write_all(&[SHOW_GUI, 0])?;
// tiny sleep to give the app a chance to respond
sleep(Duration::from_millis(10));
ipc_file.read_exact(&mut buf).ok();
// First entry is the actual state
if buf[0] == SHOWING_GUI {
ipc_file.write_all(&[SHOWING_GUI, 0])?; // Store state again as we drained the fifo
// Early exit is not an error and we don't want to pass back a dir
#[allow(clippy::exit)]
exit(0);
} else if buf[0] == SHOW_GUI {
remove_dir_all(&path)?;
return tempfile::Builder::new()
.prefix("rog-gui")
.rand_bytes(0)
.tempdir();
}
panic!("Invalid exit or app state");
}
pub fn get_ipc_file() -> Result<File, crate::error::Error> {
let tmp_dir = std::env::temp_dir().join("rog-gui");
let fifo_path = tmp_dir.join("ipc.pipe");
if let Err(e) = unistd::mkfifo(&fifo_path, stat::Mode::S_IRWXU) {
if !matches!(e, nix::errno::Errno::EEXIST) {
Err(e)?
}
}
Ok(OpenOptions::new()
.read(true)
.write(true)
// .truncate(true)
.open(&fifo_path)?)
}
+92 -80
View File
@@ -1,6 +1,5 @@
use std::borrow::BorrowMut; use std::borrow::BorrowMut;
use std::env::args; use std::env::args;
use std::io::{Read, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::exit; use std::process::exit;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@@ -10,7 +9,7 @@ use std::time::Duration;
use config_traits::{StdConfig, StdConfigLoad1}; use config_traits::{StdConfig, StdConfigLoad1};
use dmi_id::DMIID; use dmi_id::DMIID;
use gumdrop::Options; use gumdrop::Options;
use log::{info, LevelFilter}; use log::{info, warn, LevelFilter};
use rog_control_center::cli_options::CliStart; use rog_control_center::cli_options::CliStart;
use rog_control_center::config::Config; use rog_control_center::config::Config;
use rog_control_center::error::Result; use rog_control_center::error::Result;
@@ -18,38 +17,71 @@ use rog_control_center::notify::start_notifications;
use rog_control_center::slint::ComponentHandle; use rog_control_center::slint::ComponentHandle;
use rog_control_center::tray::init_tray; use rog_control_center::tray::init_tray;
use rog_control_center::ui::setup_window; use rog_control_center::ui::setup_window;
use rog_control_center::{ use rog_control_center::zbus::{
get_ipc_file, on_tmp_dir_exists, print_versions, MainWindow, QUIT_APP, SHOWING_GUI, SHOW_GUI, AppState, ROGCCZbus, ROGCCZbusProxyBlocking, ZBUS_IFACE, ZBUS_PATH,
}; };
use rog_control_center::{print_versions, MainWindow};
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
#[tokio::main] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
let mut logger = env_logger::Builder::new();
logger
.filter_level(LevelFilter::Warn)
.parse_default_env()
.target(env_logger::Target::Stdout)
.format_timestamp(None)
.init();
// Try to open a proxy and check for app state first
{
let user_con = zbus::blocking::Connection::session()?;
if let Ok(proxy) = ROGCCZbusProxyBlocking::new(&user_con) {
if let Ok(state) = proxy.state() {
info!("App is already running: {state:?}, opening the window");
// if there is a proxy connection assume the app is already running
proxy.set_state(AppState::MainWindowShouldOpen)?;
std::process::exit(0);
}
}
}
// version checks
let self_version = env!("CARGO_PKG_VERSION"); let self_version = env!("CARGO_PKG_VERSION");
let conn = zbus::blocking::Connection::system()?; let zbus_con = zbus::blocking::Connection::system()?;
let proxy = rog_dbus::zbus_platform::PlatformProxyBlocking::new(&conn)?; let platform_proxy = rog_dbus::zbus_platform::PlatformProxyBlocking::new(&zbus_con)?;
let asusd_version = proxy.version().unwrap(); let asusd_version = platform_proxy.version().unwrap();
if asusd_version != self_version { if asusd_version != self_version {
println!("Version mismatch: asusctl = {self_version}, asusd = {asusd_version}"); println!("Version mismatch: asusctl = {self_version}, asusd = {asusd_version}");
return Ok(()); return Ok(());
} }
// start tokio
let rt = Runtime::new().expect("Unable to create Runtime");
// Enter the runtime so that `tokio::spawn` is available immediately.
let _enter = rt.enter();
#[cfg(feature = "tokio-debug")]
console_subscriber::init();
let state_zbus = ROGCCZbus::new();
let app_state = state_zbus.clone_state();
let _conn = zbus::connection::Builder::session()?
.name(ZBUS_IFACE)?
.serve_at(ZBUS_PATH, state_zbus)?
.build()
.await
.map_err(|err| {
warn!("{}: add_to_server {}", ZBUS_PATH, err);
err
})?;
let dmi = DMIID::new().unwrap_or_default(); let dmi = DMIID::new().unwrap_or_default();
let board_name = dmi.board_name; let board_name = dmi.board_name;
let prod_family = dmi.product_family; let prod_family = dmi.product_family;
info!("Running on {board_name}, product: {prod_family}"); info!("Running on {board_name}, product: {prod_family}");
let is_rog_ally = prod_family == "RC71L"; let is_rog_ally = prod_family == "RC71L";
// tmp-dir must live to the end of program life
let _tmp_dir = match tempfile::Builder::new()
.prefix("rog-gui")
.rand_bytes(0)
.tempdir()
{
Ok(tmp) => tmp,
Err(_) => on_tmp_dir_exists().unwrap(),
};
let args: Vec<String> = args().skip(1).collect(); let args: Vec<String> = args().skip(1).collect();
let cli_parsed = match CliStart::parse_args_default(&args) { let cli_parsed = match CliStart::parse_args_default(&args) {
@@ -63,21 +95,7 @@ async fn main() -> Result<()> {
return Ok(()); return Ok(());
} }
let mut logger = env_logger::Builder::new(); let supported_properties = platform_proxy.supported_properties().unwrap_or_default();
logger
.filter_level(LevelFilter::Warn)
.parse_default_env()
.target(env_logger::Target::Stdout)
.format_timestamp(None)
.init();
let supported_properties = match proxy.supported_properties() {
Ok(s) => s,
Err(_e) => {
// TODO: show an error window
Vec::default()
}
};
// Startup // Startup
let mut config = Config::new().load(); let mut config = Config::new().load();
@@ -102,8 +120,6 @@ async fn main() -> Result<()> {
if config.startup_in_background { if config.startup_in_background {
config.run_in_background = true; config.run_in_background = true;
} else {
get_ipc_file().unwrap().write_all(&[SHOW_GUI, 0]).unwrap();
} }
config.write(); config.write();
@@ -111,10 +127,6 @@ async fn main() -> Result<()> {
let startup_in_background = config.startup_in_background; let startup_in_background = config.startup_in_background;
let config = Arc::new(Mutex::new(config)); let config = Arc::new(Mutex::new(config));
// start tokio
let rt = Runtime::new().expect("Unable to create Runtime");
// Enter the runtime so that `tokio::spawn` is available immediately.
let _enter = rt.enter();
start_notifications(config.clone(), &rt)?; start_notifications(config.clone(), &rt)?;
if enable_tray_icon { if enable_tray_icon {
@@ -124,7 +136,11 @@ async fn main() -> Result<()> {
thread_local! { pub static UI: std::cell::RefCell<Option<MainWindow>> = Default::default()}; thread_local! { pub static UI: std::cell::RefCell<Option<MainWindow>> = Default::default()};
// i_slint_backend_selector::with_platform(|_| Ok(())).unwrap(); // i_slint_backend_selector::with_platform(|_| Ok(())).unwrap();
let mut do_once = !startup_in_background; if !startup_in_background {
if let Ok(mut lock) = app_state.lock() {
*lock = AppState::MainWindowShouldOpen;
}
}
if std::env::var("RUST_TRANSLATIONS").is_ok() { if std::env::var("RUST_TRANSLATIONS").is_ok() {
// don't care about content // don't care about content
@@ -136,42 +152,40 @@ async fn main() -> Result<()> {
} }
thread::spawn(move || { thread::spawn(move || {
let mut buf = [0u8; 2]; let mut state = AppState::StartingUp;
// blocks until it is read, typically the read will happen after a second
// process writes to the IPC (so there is data to actually read)
loop { loop {
if do_once { // save as a var, don't hold the lock the entire time or deadlocks happen
buf[0] = SHOW_GUI; if let Ok(lock) = app_state.lock() {
do_once = false; state = *lock;
} else {
get_ipc_file().unwrap().read_exact(&mut buf).unwrap();
} }
if buf[0] == SHOW_GUI { if state == AppState::MainWindowShouldOpen {
// There's a balancing act with read/write timing of IPC, there needs to be a if let Ok(mut lock) = app_state.lock() {
// small sleep after this to give any other process a chance to *lock = AppState::MainWindowOpen;
// read the IPC before looping }
get_ipc_file()
.unwrap()
.write_all(&[SHOWING_GUI, 0])
.unwrap();
sleep(Duration::from_millis(50)); sleep(Duration::from_millis(50));
let config_copy = config.clone(); let config_copy = config.clone();
let app_state_copy = app_state.clone();
slint::invoke_from_event_loop(move || { slint::invoke_from_event_loop(move || {
UI.with(|ui| { UI.with(|ui| {
let app_state_copy = app_state_copy.clone();
let mut ui = ui.borrow_mut(); let mut ui = ui.borrow_mut();
if let Some(ui) = ui.as_mut() { if let Some(ui) = ui.as_mut() {
ui.window().show().unwrap(); ui.window().show().unwrap();
ui.window().on_close_requested(|| { ui.window().on_close_requested(move || {
get_ipc_file().unwrap().write_all(&[0, 0]).unwrap(); if let Ok(mut lock) = app_state_copy.lock() {
*lock = AppState::MainWindowClosed;
}
slint::CloseRequestResponse::HideWindow slint::CloseRequestResponse::HideWindow
}); });
} else { } else {
let newui = setup_window(config_copy); let newui = setup_window(config_copy);
newui.window().show().unwrap(); newui.window().show().unwrap();
newui.window().on_close_requested(|| { newui.window().on_close_requested(move || {
get_ipc_file().unwrap().write_all(&[0, 0]).unwrap(); if let Ok(mut lock) = app_state_copy.lock() {
*lock = AppState::MainWindowClosed;
}
slint::CloseRequestResponse::HideWindow slint::CloseRequestResponse::HideWindow
}); });
ui.replace(newui); ui.replace(newui);
@@ -179,30 +193,28 @@ async fn main() -> Result<()> {
}); });
}) })
.unwrap(); .unwrap();
} else { } else if state == AppState::QuitApp {
if buf[1] == QUIT_APP { slint::quit_event_loop().unwrap();
slint::quit_event_loop().unwrap(); exit(0);
exit(0); } else if state != AppState::MainWindowOpen {
} if let Ok(lock) = config.lock() {
if buf[0] != SHOWING_GUI { if !lock.run_in_background {
if let Ok(lock) = config.lock() { slint::quit_event_loop().unwrap();
if !lock.run_in_background { exit(0);
slint::quit_event_loop().unwrap();
exit(0);
}
} }
slint::invoke_from_event_loop(move || {
UI.with(|ui| {
let mut ui = ui.take();
if let Some(ui) = ui.borrow_mut() {
ui.window().hide().unwrap();
}
});
})
.unwrap();
} }
slint::invoke_from_event_loop(move || {
UI.with(|ui| {
let mut ui = ui.take();
if let Some(ui) = ui.borrow_mut() {
ui.window().hide().unwrap();
}
});
})
.unwrap();
} }
sleep(Duration::from_millis(300));
} }
}); });
+115 -88
View File
@@ -9,8 +9,8 @@ use std::process::Command;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::Duration; use std::time::Duration;
use log::{error, info, warn}; use log::{debug, error, info, warn};
use notify_rust::{Hint, Notification, NotificationHandle, Urgency}; use notify_rust::{Hint, Notification, Timeout, Urgency};
use rog_dbus::zbus_platform::PlatformProxy; use rog_dbus::zbus_platform::PlatformProxy;
use rog_platform::platform::GpuMode; use rog_platform::platform::GpuMode;
use rog_platform::power::AsusPower; use rog_platform::power::AsusPower;
@@ -20,7 +20,6 @@ use supergfxctl::pci_device::{GfxMode, GfxPower};
use supergfxctl::zbus_proxy::DaemonProxy as SuperProxy; use supergfxctl::zbus_proxy::DaemonProxy as SuperProxy;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
use tokio::time::sleep;
use zbus::export::futures_util::StreamExt; use zbus::export::futures_util::StreamExt;
use crate::config::Config; use crate::config::Config;
@@ -46,6 +45,52 @@ impl Default for EnabledNotifications {
} }
} }
fn start_dpu_status_mon(config: Arc<Mutex<Config>>) {
use supergfxctl::pci_device::Device;
let dev = Device::find().unwrap_or_default();
let mut found_dgpu = false; // just for logging
for dev in dev {
if dev.is_dgpu() {
info!(
"Found dGPU: {}, starting status notifications",
dev.pci_id()
);
let enabled_notifications_copy = config.clone();
// Plain old thread is perfectly fine since most of this is potentially blocking
std::thread::spawn(move || {
let mut last_status = GfxPower::Unknown;
loop {
std::thread::sleep(Duration::from_millis(1500));
if let Ok(status) = dev.get_runtime_status() {
if status != GfxPower::Unknown && status != last_status {
if let Ok(config) = enabled_notifications_copy.lock() {
if !config.notifications.receive_notify_gfx_status
|| !config.notifications.enabled
{
continue;
}
}
// Required check because status cycles through
// active/unknown/suspended
do_gpu_status_notif("dGPU status changed:", &status)
.show()
.unwrap()
.on_close(|_| ());
debug!("dGPU status changed: {:?}", &status);
}
last_status = status;
}
}
});
found_dgpu = true;
break;
}
}
if !found_dgpu {
warn!("Did not find a dGPU on this system, dGPU status won't be avilable");
}
}
pub fn start_notifications( pub fn start_notifications(
config: Arc<Mutex<Config>>, config: Arc<Mutex<Config>>,
rt: &Runtime, rt: &Runtime,
@@ -101,23 +146,24 @@ pub fn start_notifications(
} }
}); });
let enabled_notifications_copy = config.clone();
let no_supergfx = move |e: &zbus::Error| {
error!("zbus signal: receive_notify_gfx_status: {e}");
warn!("Attempting to start plain dgpu status monitor");
start_dpu_status_mon(enabled_notifications_copy.clone());
};
// GPU MUX Mode notif // GPU MUX Mode notif
let enabled_notifications_copy = config.clone(); let enabled_notifications_copy = config.clone();
tokio::spawn(async move { tokio::spawn(async move {
let conn = zbus::Connection::system() let conn = zbus::Connection::system().await.map_err(|e| {
.await error!("zbus signal: receive_notify_gpu_mux_mode: {e}");
.map_err(|e| { e
error!("zbus signal: receive_notify_gpu_mux_mode: {e}"); })?;
e let proxy = PlatformProxy::new(&conn).await.map_err(|e| {
}) error!("zbus signal: receive_notify_gpu_mux_mode: {e}");
.unwrap(); e
let proxy = PlatformProxy::new(&conn) })?;
.await
.map_err(|e| {
error!("zbus signal: receive_notify_gpu_mux_mode: {e}");
e
})
.unwrap();
let mut actual_mux_mode = GpuMode::Error; let mut actual_mux_mode = GpuMode::Error;
if let Ok(mode) = proxy.gpu_mux_mode().await { if let Ok(mode) = proxy.gpu_mux_mode().await {
@@ -139,68 +185,25 @@ pub fn start_notifications(
do_mux_notification("Reboot required. BIOS GPU MUX mode set to", &mode).ok(); do_mux_notification("Reboot required. BIOS GPU MUX mode set to", &mode).ok();
} }
} }
Ok::<(), zbus::Error>(())
}); });
use supergfxctl::pci_device::Device; let enabled_notifications_copy = config.clone();
let dev = Device::find().unwrap_or_default();
let mut found_dgpu = false; // just for logging
for dev in dev {
if dev.is_dgpu() {
let enabled_notifications_copy = config.clone();
// Plain old thread is perfectly fine since most of this is potentially blocking
tokio::spawn(async move {
let mut last_status = GfxPower::Unknown;
loop {
if let Ok(status) = dev.get_runtime_status() {
if status != GfxPower::Unknown && status != last_status {
if let Ok(config) = enabled_notifications_copy.lock() {
if !config.notifications.receive_notify_gfx_status
|| !config.notifications.enabled
{
continue;
}
}
// Required check because status cycles through
// active/unknown/suspended
do_gpu_status_notif("dGPU status changed:", &status).ok();
}
last_status = status;
}
sleep(Duration::from_millis(500)).await;
}
});
found_dgpu = true;
break;
}
}
if !found_dgpu {
warn!("Did not find a dGPU on this system, dGPU status won't be avilable");
}
// GPU Mode change/action notif // GPU Mode change/action notif
tokio::spawn(async move { tokio::spawn(async move {
let conn = zbus::Connection::system() let conn = zbus::Connection::system().await.inspect_err(|e| {
.await no_supergfx(e);
.map_err(|e| { })?;
error!("zbus signal: receive_notify_action: {e}"); let proxy = SuperProxy::builder(&conn).build().await.inspect_err(|e| {
e no_supergfx(e);
}) })?;
.unwrap(); let _ = proxy.mode().await.inspect_err(|e| {
let proxy = SuperProxy::builder(&conn) no_supergfx(e);
.build() })?;
.await
.map_err(|e| {
error!("zbus signal: receive_notify_action: {e}");
e
})
.unwrap();
if proxy.mode().await.is_err() { let proxy_copy = proxy.clone();
info!("supergfxd not running or not responding"); let mut p = proxy.receive_notify_action().await?;
return; tokio::spawn(async move {
}
if let Ok(mut p) = proxy.receive_notify_action().await {
info!("Started zbus signal thread: receive_notify_action"); info!("Started zbus signal thread: receive_notify_action");
while let Some(e) = p.next().await { while let Some(e) = p.next().await {
if let Ok(out) = e.args() { if let Ok(out) = e.args() {
@@ -219,7 +222,36 @@ pub fn start_notifications(
.ok(); .ok();
} }
} }
}; });
let mut p = proxy_copy.receive_notify_gfx_status().await?;
tokio::spawn(async move {
info!("Started zbus signal thread: receive_notify_gfx_status");
let mut last_status = GfxPower::Unknown;
while let Some(e) = p.next().await {
if let Ok(out) = e.args() {
let status = out.status;
if status != GfxPower::Unknown && status != last_status {
if let Ok(config) = enabled_notifications_copy.lock() {
if !config.notifications.receive_notify_gfx_status
|| !config.notifications.enabled
{
continue;
}
}
// Required check because status cycles through
// active/unknown/suspended
do_gpu_status_notif("dGPU status changed:", &status)
.show_async()
.await
.unwrap()
.on_close(|_| ());
}
last_status = status;
}
}
});
Ok::<(), zbus::Error>(())
}); });
Ok(vec![blocking]) Ok(vec![blocking])
@@ -242,19 +274,15 @@ where
T: Display, T: Display,
{ {
let mut notif = Notification::new(); let mut notif = Notification::new();
notif notif
.summary(NOTIF_HEADER) .appname(NOTIF_HEADER)
.body(&format!("{message} {data}")) .summary(&format!("{message} {data}"))
.timeout(-1) .timeout(Timeout::Milliseconds(3000))
//.hint(Hint::Resident(true))
.hint(Hint::Category("device".into())); .hint(Hint::Category("device".into()));
notif notif
} }
fn do_gpu_status_notif(message: &str, data: &GfxPower) -> Result<NotificationHandle> { fn do_gpu_status_notif(message: &str, data: &GfxPower) -> Notification {
// eww
let mut notif = base_notification(message, &<&str>::from(data).to_owned()); let mut notif = base_notification(message, &<&str>::from(data).to_owned());
let icon = match data { let icon = match data {
GfxPower::Suspended => "asus_notif_blue", GfxPower::Suspended => "asus_notif_blue",
@@ -264,7 +292,7 @@ fn do_gpu_status_notif(message: &str, data: &GfxPower) -> Result<NotificationHan
GfxPower::Unknown => "gpu-integrated", GfxPower::Unknown => "gpu-integrated",
}; };
notif.icon(icon); notif.icon(icon);
Ok(Notification::show(&notif)?) notif
} }
fn do_gfx_action_notif(message: &str, action: GfxUserAction, mode: GpuMode) -> Result<()> { fn do_gfx_action_notif(message: &str, action: GfxUserAction, mode: GpuMode) -> Result<()> {
@@ -275,13 +303,12 @@ fn do_gfx_action_notif(message: &str, action: GfxUserAction, mode: GpuMode) -> R
let mut notif = Notification::new(); let mut notif = Notification::new();
notif notif
.summary(NOTIF_HEADER) .appname(NOTIF_HEADER)
.body(&format!("Changing to {mode}. {message}")) .summary(&format!("Changing to {mode}. {message}"))
.timeout(2000)
//.hint(Hint::Resident(true)) //.hint(Hint::Resident(true))
.hint(Hint::Category("device".into())) .hint(Hint::Category("device".into()))
.urgency(Urgency::Critical) .urgency(Urgency::Critical)
.timeout(-1) .timeout(Timeout::Never)
.icon("dialog-warning") .icon("dialog-warning")
.hint(Hint::Transient(true)); .hint(Hint::Transient(true));
+151 -107
View File
@@ -1,23 +1,20 @@
//! A seld-contained tray icon with menus. The control of app<->tray is done via //! A self-contained tray icon with menus.
//! commands over an MPSC channel.
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io::{Read, Write}; use std::io::Read;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::exit;
use std::sync::{Arc, Mutex, OnceLock}; use std::sync::{Arc, Mutex, OnceLock};
use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
use betrayer::{Icon, Menu, MenuItem, TrayEvent, TrayIcon, TrayIconBuilder}; use ksni::{Handle, Icon, TrayMethods};
use log::{debug, error, info, warn}; use log::{info, warn};
use rog_platform::platform::Properties; use rog_platform::platform::Properties;
use supergfxctl::pci_device::{GfxMode, GfxPower}; use supergfxctl::pci_device::{Device, GfxMode, GfxPower};
use supergfxctl::zbus_proxy::DaemonProxyBlocking as GfxProxy; use supergfxctl::zbus_proxy::DaemonProxy as GfxProxy;
use versions::Versioning; use versions::Versioning;
use crate::config::Config; use crate::config::Config;
use crate::{get_ipc_file, QUIT_APP, SHOW_GUI}; use crate::zbus::{AppState, ROGCCZbusProxyBlocking};
const TRAY_LABEL: &str = "ROG Control Center"; const TRAY_LABEL: &str = "ROG Control Center";
const TRAY_ICON_PATH: &str = "/usr/share/icons/hicolor/512x512/apps/"; const TRAY_ICON_PATH: &str = "/usr/share/icons/hicolor/512x512/apps/";
@@ -32,30 +29,6 @@ struct Icons {
static ICONS: OnceLock<Icons> = OnceLock::new(); static ICONS: OnceLock<Icons> = OnceLock::new();
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
enum TrayAction {
Open,
Quit,
}
fn open_app() {
if let Ok(mut ipc) = get_ipc_file().map_err(|e| {
error!("ROGTray: get_ipc_file: {}", e);
}) {
debug!("Tray told app to show self");
ipc.write_all(&[SHOW_GUI, 0]).ok();
}
}
fn quit_app() {
if let Ok(mut ipc) = get_ipc_file().map_err(|e| {
error!("ROGTray: get_ipc_file: {}", e);
}) {
debug!("Tray told app to show self");
ipc.write_all(&[QUIT_APP, 0]).ok();
}
}
fn read_icon(file: &Path) -> Icon { fn read_icon(file: &Path) -> Icon {
let mut path = PathBuf::from(TRAY_ICON_PATH); let mut path = PathBuf::from(TRAY_ICON_PATH);
path.push(file); path.push(file);
@@ -63,34 +36,73 @@ fn read_icon(file: &Path) -> Icon {
let mut bytes = Vec::new(); let mut bytes = Vec::new();
file.read_to_end(&mut bytes).unwrap(); file.read_to_end(&mut bytes).unwrap();
Icon::from_png_bytes(&bytes) let mut img = image::load_from_memory_with_format(&bytes, image::ImageFormat::Png)
.unwrap_or(Icon::from_rgba(vec![255u8; 32 * 32 * 4], 32, 32).unwrap()) .expect("icon not found")
} .to_rgba8();
for image::Rgba(pixel) in img.pixels_mut() {
// (╯°□°)╯︵ ┻━┻
*pixel = u32::from_be_bytes(*pixel).rotate_right(8).to_be_bytes();
}
fn build_menu() -> Menu<TrayAction> { let (width, height) = img.dimensions();
Menu::new([ Icon {
MenuItem::separator(), width: width as i32,
MenuItem::button("Open", TrayAction::Open), height: height as i32,
MenuItem::button("Quit", TrayAction::Quit), data: img.into_raw(),
])
}
fn do_action(event: TrayEvent<TrayAction>) {
if let TrayEvent::Menu(action) = event {
match action {
TrayAction::Open => open_app(),
TrayAction::Quit => {
quit_app();
exit(0);
}
}
} }
} }
fn set_tray_icon_and_tip( struct AsusTray {
current_title: String,
current_icon: Icon,
proxy: ROGCCZbusProxyBlocking<'static>,
}
impl ksni::Tray for AsusTray {
fn id(&self) -> String {
TRAY_LABEL.into()
}
fn icon_pixmap(&self) -> Vec<ksni::Icon> {
vec![self.current_icon.clone()]
}
fn title(&self) -> String {
self.current_title.clone()
}
fn status(&self) -> ksni::Status {
ksni::Status::Active
}
fn menu(&self) -> Vec<ksni::MenuItem<Self>> {
use ksni::menu::*;
vec![
StandardItem {
label: "Open ROGCC".into(),
icon_name: "rog-control-center".into(),
activate: Box::new(move |s: &mut AsusTray| {
s.proxy.set_state(AppState::MainWindowShouldOpen).ok();
}),
..Default::default()
}
.into(),
MenuItem::Separator,
StandardItem {
label: "Quit ROGCC".into(),
icon_name: "application-exit".into(),
activate: Box::new(|_| std::process::exit(0)),
..Default::default()
}
.into(),
]
}
}
async fn set_tray_icon_and_tip(
mode: GfxMode, mode: GfxMode,
power: GfxPower, power: GfxPower,
tray: &mut TrayIcon<TrayAction>, tray: &mut Handle<AsusTray>,
supergfx_active: bool, supergfx_active: bool,
) { ) {
if let Some(icons) = ICONS.get() { if let Some(icons) = ICONS.get() {
@@ -113,55 +125,77 @@ fn set_tray_icon_and_tip(
} }
} }
}; };
// *tray = TrayIconBuilder::<TrayAction>::new()
// .with_icon(icon)
// .with_tooltip(format!("ROG: gpu mode = {mode:?}, gpu power = {power:?}"))
// .with_menu(build_menu())
// .build(do_action)
// .map_err(|e| log::error!("Tray unable to be initialised: {e:?}"))
// .unwrap();
tray.set_icon(Some(icon)); tray.update(|tray: &mut AsusTray| {
tray.set_tooltip(format!("ROG: gpu mode = {mode:?}, gpu power = {power:?}")); dbg!(power);
tray.current_icon = icon;
tray.current_title = format!(
"ROG: gpu mode = {mode:?}, gpu power =
{power:?}"
);
})
.await;
} }
} }
fn find_dgpu() -> Option<Device> {
use supergfxctl::pci_device::Device;
let dev = Device::find().unwrap_or_default();
for dev in dev {
if dev.is_dgpu() {
info!("Found dGPU: {}", dev.pci_id());
// Plain old thread is perfectly fine since most of this is potentially blocking
return Some(dev);
}
}
warn!("Did not find a dGPU on this system, dGPU status won't be avilable");
None
}
/// The tray is controlled somewhat by `Arc<Mutex<SystemState>>` /// The tray is controlled somewhat by `Arc<Mutex<SystemState>>`
pub fn init_tray(_supported_properties: Vec<Properties>, config: Arc<Mutex<Config>>) { pub fn init_tray(_supported_properties: Vec<Properties>, config: Arc<Mutex<Config>>) {
std::thread::spawn(move || { tokio::spawn(async move {
let user_con = zbus::blocking::Connection::session().unwrap();
let proxy = ROGCCZbusProxyBlocking::new(&user_con).unwrap();
let rog_red = read_icon(&PathBuf::from("asus_notif_red.png")); let rog_red = read_icon(&PathBuf::from("asus_notif_red.png"));
if let Ok(mut tray) = TrayIconBuilder::<TrayAction>::new() let tray = AsusTray {
.with_icon(rog_red.clone()) current_title: TRAY_LABEL.to_string(),
.with_tooltip(TRAY_LABEL) current_icon: rog_red.clone(),
.with_menu(build_menu()) proxy,
.build(do_action) };
let mut tray = tray
.spawn_without_dbus_name()
.await
.map_err(|e| { .map_err(|e| {
log::error!( log::error!(
"Tray unable to be initialised: {e:?}. Do you have a system tray enabled?" "Tray unable to be initialised: {e:?}. Do you have a system tray enabled?"
) )
}) })
{ .unwrap();
info!("Tray started");
let rog_blue = read_icon(&PathBuf::from("asus_notif_blue.png"));
let rog_green = read_icon(&PathBuf::from("asus_notif_green.png"));
let rog_white = read_icon(&PathBuf::from("asus_notif_white.png"));
let gpu_integrated = read_icon(&PathBuf::from("rog-control-center.png"));
ICONS.get_or_init(|| Icons {
rog_blue,
rog_red: rog_red.clone(),
rog_green,
rog_white,
gpu_integrated,
});
let mut has_supergfx = true; info!("Tray started");
let conn = zbus::blocking::Connection::system().unwrap(); let rog_blue = read_icon(&PathBuf::from("asus_notif_blue.png"));
if let Ok(gfx_proxy) = GfxProxy::new(&conn) { let rog_green = read_icon(&PathBuf::from("asus_notif_green.png"));
let mut supergfx_active = false; let rog_white = read_icon(&PathBuf::from("asus_notif_white.png"));
if gfx_proxy.mode().is_ok() { let gpu_integrated = read_icon(&PathBuf::from("rog-control-center.png"));
supergfx_active = true; ICONS.get_or_init(|| Icons {
if let Ok(version) = gfx_proxy.version() { rog_blue,
rog_red: rog_red.clone(),
rog_green,
rog_white,
gpu_integrated,
});
let mut has_supergfx = false;
let conn = zbus::Connection::system().await.unwrap();
if let Ok(gfx_proxy) = GfxProxy::new(&conn).await {
match gfx_proxy.mode().await {
Ok(_) => {
has_supergfx = true;
if let Ok(version) = gfx_proxy.version().await {
if let Some(version) = Versioning::new(&version) { if let Some(version) = Versioning::new(&version) {
let curr_gfx = Versioning::new("5.2.0").unwrap(); let curr_gfx = Versioning::new("5.2.0").unwrap();
warn!("supergfxd version = {version}"); warn!("supergfxd version = {version}");
@@ -172,25 +206,35 @@ pub fn init_tray(_supported_properties: Vec<Properties>, config: Arc<Mutex<Confi
} }
} }
} }
}; }
Err(e) => warn!("Couldn't get mode form supergfxd: {e:?}"),
}
info!("Started ROGTray"); info!("Started ROGTray");
let mut last_power = GfxPower::Unknown; let mut last_power = GfxPower::Unknown;
loop { let dev = find_dgpu();
sleep(Duration::from_millis(1000)); loop {
if let Ok(lock) = config.try_lock() { tokio::time::sleep(Duration::from_millis(1000)).await;
if !lock.enable_tray_icon { if let Ok(lock) = config.try_lock() {
return; if !lock.enable_tray_icon {
return;
}
}
if has_supergfx {
if let Ok(mode) = gfx_proxy.mode().await {
if let Ok(power) = gfx_proxy.power().await {
if last_power != power {
set_tray_icon_and_tip(mode, power, &mut tray, has_supergfx).await;
last_power = power;
}
} }
} }
if has_supergfx { } else if let Some(dev) = dev.as_ref() {
if let Ok(mode) = gfx_proxy.mode() { if let Ok(power) = dev.get_runtime_status() {
if let Ok(power) = gfx_proxy.power() { if last_power != power {
if last_power != power { set_tray_icon_and_tip(GfxMode::Hybrid, power, &mut tray, has_supergfx)
set_tray_icon_and_tip(mode, power, &mut tray, supergfx_active); .await;
last_power = power; last_power = power;
}
}
} }
} }
} }
+12 -6
View File
@@ -65,6 +65,7 @@ impl From<PowerZones> for SlintPowerZones {
PowerZones::Lid => SlintPowerZones::Lid, PowerZones::Lid => SlintPowerZones::Lid,
PowerZones::RearGlow => SlintPowerZones::RearGlow, PowerZones::RearGlow => SlintPowerZones::RearGlow,
PowerZones::KeyboardAndLightbar => SlintPowerZones::KeyboardAndLightbar, PowerZones::KeyboardAndLightbar => SlintPowerZones::KeyboardAndLightbar,
PowerZones::Ally => SlintPowerZones::Ally,
PowerZones::None => SlintPowerZones::Keyboard, PowerZones::None => SlintPowerZones::Keyboard,
} }
} }
@@ -79,6 +80,7 @@ impl From<SlintPowerZones> for PowerZones {
SlintPowerZones::Lid => PowerZones::Lid, SlintPowerZones::Lid => PowerZones::Lid,
SlintPowerZones::RearGlow => PowerZones::RearGlow, SlintPowerZones::RearGlow => PowerZones::RearGlow,
SlintPowerZones::KeyboardAndLightbar => PowerZones::KeyboardAndLightbar, SlintPowerZones::KeyboardAndLightbar => PowerZones::KeyboardAndLightbar,
SlintPowerZones::Ally => PowerZones::Ally,
} }
} }
} }
@@ -146,11 +148,13 @@ impl From<LaptopAuraPower> for SlintLaptopAuraPower {
impl From<SlintDeviceType> for AuraDeviceType { impl From<SlintDeviceType> for AuraDeviceType {
fn from(value: SlintDeviceType) -> Self { fn from(value: SlintDeviceType) -> Self {
match value { match value {
SlintDeviceType::New => Self::LaptopPost2021, SlintDeviceType::New => Self::LaptopKeyboard2021,
SlintDeviceType::Old => Self::LaptopPre2021, SlintDeviceType::Old => Self::LaptopKeyboardPre2021,
SlintDeviceType::Tuf => Self::LaptopTuf, SlintDeviceType::Tuf => Self::LaptopKeyboardTuf,
SlintDeviceType::ScsiExtDisk => Self::ScsiExtDisk, SlintDeviceType::ScsiExtDisk => Self::ScsiExtDisk,
SlintDeviceType::Unknown => Self::Unknown, SlintDeviceType::Unknown => Self::Unknown,
SlintDeviceType::Ally => Self::Ally,
SlintDeviceType::AnimeOrSlash => Self::AnimeOrSlash,
} }
} }
} }
@@ -158,11 +162,13 @@ impl From<SlintDeviceType> for AuraDeviceType {
impl From<AuraDeviceType> for SlintDeviceType { impl From<AuraDeviceType> for SlintDeviceType {
fn from(value: AuraDeviceType) -> Self { fn from(value: AuraDeviceType) -> Self {
match value { match value {
AuraDeviceType::LaptopPost2021 => SlintDeviceType::New, AuraDeviceType::LaptopKeyboard2021 => SlintDeviceType::New,
AuraDeviceType::LaptopPre2021 => SlintDeviceType::Old, AuraDeviceType::LaptopKeyboardPre2021 => SlintDeviceType::Old,
AuraDeviceType::LaptopTuf => SlintDeviceType::Tuf, AuraDeviceType::LaptopKeyboardTuf => SlintDeviceType::Tuf,
AuraDeviceType::ScsiExtDisk => SlintDeviceType::ScsiExtDisk, AuraDeviceType::ScsiExtDisk => SlintDeviceType::ScsiExtDisk,
AuraDeviceType::Unknown => SlintDeviceType::Unknown, AuraDeviceType::Unknown => SlintDeviceType::Unknown,
AuraDeviceType::Ally => SlintDeviceType::Ally,
AuraDeviceType::AnimeOrSlash => SlintDeviceType::AnimeOrSlash,
} }
} }
} }
+31 -12
View File
@@ -1,7 +1,8 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use log::{error, info}; use log::{debug, error, info};
use rog_aura::keyboard::LaptopAuraPower; use rog_aura::keyboard::LaptopAuraPower;
use rog_aura::{AuraDeviceType, PowerZones};
use rog_dbus::zbus_aura::AuraProxy; use rog_dbus::zbus_aura::AuraProxy;
use slint::{ComponentHandle, Model, RgbaColor, SharedString}; use slint::{ComponentHandle, Model, RgbaColor, SharedString};
@@ -75,7 +76,7 @@ pub fn setup_aura_page(ui: &MainWindow, _states: Arc<Mutex<Config>>) {
tokio::spawn(async move { tokio::spawn(async move {
let Ok(aura) = find_aura_iface().await else { let Ok(aura) = find_aura_iface().await else {
info!("This device appears to have no aura interfaces"); info!("This device appears to have no aura interfaces");
return; return Ok::<(), zbus::Error>(());
}; };
set_ui_props_async!(handle, aura, AuraPageData, brightness); set_ui_props_async!(handle, aura, AuraPageData, brightness);
@@ -84,24 +85,40 @@ pub fn setup_aura_page(ui: &MainWindow, _states: Arc<Mutex<Config>>) {
set_ui_props_async!(handle, aura, AuraPageData, led_power); set_ui_props_async!(handle, aura, AuraPageData, led_power);
set_ui_props_async!(handle, aura, AuraPageData, device_type); set_ui_props_async!(handle, aura, AuraPageData, device_type);
if let Ok(pow3r) = aura.supported_power_zones().await { if let Ok(mut pow3r) = aura.supported_power_zones().await {
let dev_type = aura
.device_type()
.await
.unwrap_or(AuraDeviceType::LaptopKeyboard2021);
log::debug!("Available LED power modes {pow3r:?}"); log::debug!("Available LED power modes {pow3r:?}");
handle handle
.upgrade_in_event_loop(move |handle| { .upgrade_in_event_loop(move |handle| {
let power: Vec<SlintPowerZones> = pow3r.iter().map(|p| (*p).into()).collect();
let names: Vec<SharedString> = handle let names: Vec<SharedString> = handle
.global::<AuraPageData>() .global::<AuraPageData>()
.get_power_zone_names() .get_power_zone_names()
.iter() .iter()
.collect(); .collect();
let names: Vec<SharedString> =
pow3r.iter().map(|n| names[(*n) as usize].clone()).collect(); if dev_type.is_old_laptop() {
handle // Need to add the specific KeyboardAndLightbar
.global::<AuraPageData>() if pow3r.contains(&PowerZones::Keyboard)
.set_supported_power_zones(power.as_slice().into()); && pow3r.contains(&PowerZones::Lightbar)
handle {
.global::<AuraPageData>() pow3r.push(PowerZones::KeyboardAndLightbar);
.set_power_zone_names_old(names.as_slice().into()); }
let names: Vec<SharedString> =
pow3r.iter().map(|n| names[(*n) as usize].clone()).collect();
handle
.global::<AuraPageData>()
.set_power_zone_names_old(names.as_slice().into());
} else {
let power: Vec<SlintPowerZones> =
pow3r.iter().map(|p| (*p).into()).collect();
handle
.global::<AuraPageData>()
.set_supported_power_zones(power.as_slice().into());
}
}) })
.ok(); .ok();
} }
@@ -209,5 +226,7 @@ pub fn setup_aura_page(ui: &MainWindow, _states: Arc<Mutex<Config>>) {
} }
} }
}); });
debug!("Aura setup tasks complete");
Ok(())
}); });
} }
+68
View File
@@ -0,0 +1,68 @@
use std::sync::{Arc, Mutex};
use zbus::zvariant::{OwnedValue, Type, Value};
use zbus::{interface, proxy};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Type, Value, OwnedValue)]
#[zvariant(signature = "u")]
pub enum AppState {
MainWindowOpen = 0,
/// If the app is running, open the main window
MainWindowShouldOpen = 1,
MainWindowClosed = 2,
StartingUp = 3,
QuitApp = 4,
LockFailed = 5,
}
pub struct ROGCCZbus {
state: Arc<Mutex<AppState>>,
}
impl ROGCCZbus {
pub fn new() -> Self {
Self {
state: Arc::new(Mutex::new(AppState::StartingUp)),
}
}
pub fn clone_state(&self) -> Arc<Mutex<AppState>> {
self.state.clone()
}
}
pub const ZBUS_PATH: &str = "/xyz/ljones/rogcc";
pub const ZBUS_IFACE: &str = "xyz.ljones.rogcc";
#[interface(name = "xyz.ljones.rogcc")]
impl ROGCCZbus {
/// Return the device type for this Aura keyboard
#[zbus(property)]
async fn state(&self) -> AppState {
if let Ok(lock) = self.state.try_lock() {
return *lock;
}
AppState::LockFailed
}
#[zbus(property)]
async fn set_state(&self, state: AppState) {
if let Ok(mut lock) = self.state.try_lock() {
*lock = state;
}
}
}
#[proxy(
interface = "xyz.ljones.rogcc",
default_service = "xyz.ljones.rogcc",
default_path = "/xyz/ljones/rogcc"
)]
pub trait ROGCCZbus {
/// EnableDisplay property
#[zbus(property)]
fn state(&self) -> zbus::Result<AppState>;
#[zbus(property)]
fn set_state(&self, state: AppState) -> zbus::Result<()>;
}
@@ -2,7 +2,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2024-05-14 05:08+0000\n" "POT-Creation-Date: 2024-12-24 22:29+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -12,6 +12,206 @@ msgstr ""
"Language: \n" "Language: \n"
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
#: rog-control-center/ui/pages/system.slint:26
msgctxt "SystemPageData"
msgid "Balanced"
msgstr ""
#: rog-control-center/ui/pages/system.slint:26 rog-control-center/ui/pages/system.slint:30
msgctxt "SystemPageData"
msgid "Performance"
msgstr ""
#: rog-control-center/ui/pages/system.slint:26
msgctxt "SystemPageData"
msgid "Quiet"
msgstr ""
#: rog-control-center/ui/pages/system.slint:29
msgctxt "SystemPageData"
msgid "Default"
msgstr ""
#: rog-control-center/ui/pages/system.slint:31
msgctxt "SystemPageData"
msgid "BalancePerformance"
msgstr ""
#: rog-control-center/ui/pages/system.slint:32
msgctxt "SystemPageData"
msgid "BalancePower"
msgstr ""
#: rog-control-center/ui/pages/system.slint:33
msgctxt "SystemPageData"
msgid "Power"
msgstr ""
#: rog-control-center/ui/pages/system.slint:110
msgctxt "PageSystem"
msgid "Base system settings"
msgstr ""
#: rog-control-center/ui/pages/system.slint:115
msgctxt "PageSystem"
msgid "Charge limit"
msgstr ""
#: rog-control-center/ui/pages/system.slint:127
msgctxt "PageSystem"
msgid "Throttle Policy"
msgstr ""
#: rog-control-center/ui/pages/system.slint:137
msgctxt "PageSystem"
msgid "Advanced"
msgstr ""
#: rog-control-center/ui/pages/system.slint:149
msgctxt "PageSystem"
msgid "Panel Overdrive"
msgstr ""
#: rog-control-center/ui/pages/system.slint:157
msgctxt "PageSystem"
msgid "MiniLED Mode"
msgstr ""
#: rog-control-center/ui/pages/system.slint:165
msgctxt "PageSystem"
msgid "POST boot sound"
msgstr ""
#: rog-control-center/ui/pages/system.slint:183
msgctxt "PageSystem"
msgid "System performance settings"
msgstr ""
#: rog-control-center/ui/pages/system.slint:188
msgctxt "ppt_pl1_spl"
msgid "PL1, sustained power limit"
msgstr ""
#: rog-control-center/ui/pages/system.slint:198
msgctxt "ppt_pl2_sppt"
msgid "PL2, turbo power limit"
msgstr ""
#: rog-control-center/ui/pages/system.slint:208
msgctxt "ppt_fppt"
msgid "FPPT, Fast Power Limit"
msgstr ""
#: rog-control-center/ui/pages/system.slint:218
msgctxt "ppt_apu_sppt"
msgid "SPPT, APU slow power limit"
msgstr ""
#: rog-control-center/ui/pages/system.slint:228
msgctxt "ppt_platform_sppt"
msgid "Slow package power tracking limit"
msgstr ""
#: rog-control-center/ui/pages/system.slint:238
msgctxt "nv_dynamic_boost"
msgid "dGPU boost overclock"
msgstr ""
#: rog-control-center/ui/pages/system.slint:248
msgctxt "nv_temp_target"
msgid "dGPU temperature max"
msgstr ""
#: rog-control-center/ui/pages/system.slint:294
msgctxt "PageSystem"
msgid "Energy Performance Preference linked to Throttle Policy"
msgstr ""
#: rog-control-center/ui/pages/system.slint:298
msgctxt "PageSystem"
msgid "Change EPP based on Throttle Policy"
msgstr ""
#: rog-control-center/ui/pages/system.slint:306
msgctxt "PageSystem"
msgid "EPP for Balanced Policy"
msgstr ""
#: rog-control-center/ui/pages/system.slint:316
msgctxt "PageSystem"
msgid "EPP for Performance Policy"
msgstr ""
#: rog-control-center/ui/pages/system.slint:326
msgctxt "PageSystem"
msgid "EPP for Quiet Policy"
msgstr ""
#: rog-control-center/ui/pages/system.slint:344
msgctxt "PageSystem"
msgid "Throttle Policy for power state"
msgstr ""
#: rog-control-center/ui/pages/system.slint:350
msgctxt "PageSystem"
msgid "Throttle Policy on Battery"
msgstr ""
#: rog-control-center/ui/pages/system.slint:360 rog-control-center/ui/pages/system.slint:381
msgctxt "PageSystem"
msgid "Enabled"
msgstr ""
#: rog-control-center/ui/pages/system.slint:371
msgctxt "PageSystem"
msgid "Throttle Policy on AC"
msgstr ""
#: rog-control-center/ui/pages/aura.slint:28
msgctxt "PageAura"
msgid "Brightness"
msgstr ""
#: rog-control-center/ui/pages/aura.slint:39
msgctxt "PageAura"
msgid "Aura mode"
msgstr ""
#: rog-control-center/ui/pages/aura.slint:59
msgctxt "PageAura"
msgid "Colour 1"
msgstr ""
#: rog-control-center/ui/pages/aura.slint:85
msgctxt "PageAura"
msgid "Colour 2"
msgstr ""
#: rog-control-center/ui/pages/aura.slint:119
msgctxt "PageAura"
msgid "Zone"
msgstr ""
#: rog-control-center/ui/pages/aura.slint:142
msgctxt "PageAura"
msgid "Direction"
msgstr ""
#: rog-control-center/ui/pages/aura.slint:164
msgctxt "PageAura"
msgid "Speed"
msgstr ""
#: rog-control-center/ui/pages/aura.slint:185
msgctxt "PageAura"
msgid "Power Settings"
msgstr ""
#: rog-control-center/ui/pages/aura.slint:270
msgctxt "PageAura"
msgid "Power Zones"
msgstr ""
#: rog-control-center/ui/pages/anime.slint:6 #: rog-control-center/ui/pages/anime.slint:6
msgctxt "Anime Brightness" msgctxt "Anime Brightness"
msgid "Off" msgid "Off"
@@ -212,392 +412,197 @@ msgctxt "PageAppSettings"
msgid "Enable dGPU notifications" msgid "Enable dGPU notifications"
msgstr "" msgstr ""
#: rog-control-center/ui/pages/aura.slint:28 #: rog-control-center/ui/types/aura_types.slint:52
msgctxt "PageAura"
msgid "Brightness"
msgstr ""
#: rog-control-center/ui/pages/aura.slint:39
msgctxt "PageAura"
msgid "Aura mode"
msgstr ""
#: rog-control-center/ui/pages/aura.slint:59
msgctxt "PageAura"
msgid "Colour 1"
msgstr ""
#: rog-control-center/ui/pages/aura.slint:85
msgctxt "PageAura"
msgid "Colour 2"
msgstr ""
#: rog-control-center/ui/pages/aura.slint:119
msgctxt "PageAura"
msgid "Zone"
msgstr ""
#: rog-control-center/ui/pages/aura.slint:142
msgctxt "PageAura"
msgid "Direction"
msgstr ""
#: rog-control-center/ui/pages/aura.slint:164
msgctxt "PageAura"
msgid "Speed"
msgstr ""
#: rog-control-center/ui/pages/aura.slint:185
msgctxt "PageAura"
msgid "Power Settings"
msgstr ""
#: rog-control-center/ui/pages/aura.slint:270
msgctxt "PageAura"
msgid "Power Zones"
msgstr ""
#: rog-control-center/ui/pages/system.slint:26
msgctxt "SystemPageData"
msgid "Balanced"
msgstr ""
#: rog-control-center/ui/pages/system.slint:26 rog-control-center/ui/pages/system.slint:30
msgctxt "SystemPageData"
msgid "Performance"
msgstr ""
#: rog-control-center/ui/pages/system.slint:26
msgctxt "SystemPageData"
msgid "Quiet"
msgstr ""
#: rog-control-center/ui/pages/system.slint:29
msgctxt "SystemPageData"
msgid "Default"
msgstr ""
#: rog-control-center/ui/pages/system.slint:31
msgctxt "SystemPageData"
msgid "BalancePerformance"
msgstr ""
#: rog-control-center/ui/pages/system.slint:32
msgctxt "SystemPageData"
msgid "BalancePower"
msgstr ""
#: rog-control-center/ui/pages/system.slint:33
msgctxt "SystemPageData"
msgid "Power"
msgstr ""
#: rog-control-center/ui/pages/system.slint:110
msgctxt "PageSystem"
msgid "Base system settings"
msgstr ""
#: rog-control-center/ui/pages/system.slint:115
msgctxt "PageSystem"
msgid "Charge limit"
msgstr ""
#: rog-control-center/ui/pages/system.slint:127
msgctxt "PageSystem"
msgid "Throttle Policy"
msgstr ""
#: rog-control-center/ui/pages/system.slint:137
msgctxt "PageSystem"
msgid "Advanced"
msgstr ""
#: rog-control-center/ui/pages/system.slint:149
msgctxt "PageSystem"
msgid "Panel Overdrive"
msgstr ""
#: rog-control-center/ui/pages/system.slint:157
msgctxt "PageSystem"
msgid "MiniLED Mode"
msgstr ""
#: rog-control-center/ui/pages/system.slint:165
msgctxt "PageSystem"
msgid "POST boot sound"
msgstr ""
#: rog-control-center/ui/pages/system.slint:183
msgctxt "PageSystem"
msgid "System performance settings"
msgstr ""
#: rog-control-center/ui/pages/system.slint:188
msgctxt "ppt_pl1_spl"
msgid "ppt_pl1_spl"
msgstr ""
#: rog-control-center/ui/pages/system.slint:198
msgctxt "ppt_pl2_sppt"
msgid "ppt_pl2_sppt"
msgstr ""
#: rog-control-center/ui/pages/system.slint:208
msgctxt "ppt_fppt"
msgid "ppt_fppt"
msgstr ""
#: rog-control-center/ui/pages/system.slint:218
msgctxt "ppt_apu_sppt"
msgid "ppt_apu_sppt"
msgstr ""
#: rog-control-center/ui/pages/system.slint:228
msgctxt "ppt_platform_sppt"
msgid "ppt_platform_sppt"
msgstr ""
#: rog-control-center/ui/pages/system.slint:238
msgctxt "nv_dynamic_boost"
msgid "nv_dynamic_boost"
msgstr ""
#: rog-control-center/ui/pages/system.slint:248
msgctxt "nv_temp_target"
msgid "nv_temp_target"
msgstr ""
#: rog-control-center/ui/pages/system.slint:294
msgctxt "PageSystem"
msgid "Energy Performance Preference linked to Throttle Policy"
msgstr ""
#: rog-control-center/ui/pages/system.slint:298
msgctxt "PageSystem"
msgid "Change EPP based on Throttle Policy"
msgstr ""
#: rog-control-center/ui/pages/system.slint:306
msgctxt "PageSystem"
msgid "EPP for Balanced Policy"
msgstr ""
#: rog-control-center/ui/pages/system.slint:316
msgctxt "PageSystem"
msgid "EPP for Performance Policy"
msgstr ""
#: rog-control-center/ui/pages/system.slint:326
msgctxt "PageSystem"
msgid "EPP for Quiet Policy"
msgstr ""
#: rog-control-center/ui/pages/system.slint:344
msgctxt "PageSystem"
msgid "Throttle Policy for power state"
msgstr ""
#: rog-control-center/ui/pages/system.slint:350
msgctxt "PageSystem"
msgid "Throttle Policy on Battery"
msgstr ""
#: rog-control-center/ui/pages/system.slint:360 rog-control-center/ui/pages/system.slint:381
msgctxt "PageSystem"
msgid "Enabled"
msgstr ""
#: rog-control-center/ui/pages/system.slint:371
msgctxt "PageSystem"
msgid "Throttle Policy on AC"
msgstr ""
#: rog-control-center/ui/types/aura_types.slint:49
msgctxt "Aura power zone" msgctxt "Aura power zone"
msgid "Logo" msgid "Logo"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:50 rog-control-center/ui/types/aura_types.slint:59 #: rog-control-center/ui/types/aura_types.slint:53 rog-control-center/ui/types/aura_types.slint:63
msgctxt "Aura power zone" msgctxt "Aura power zone"
msgid "Keyboard" msgid "Keyboard"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:51 rog-control-center/ui/types/aura_types.slint:60 #: rog-control-center/ui/types/aura_types.slint:54 rog-control-center/ui/types/aura_types.slint:64
msgctxt "Aura power zone" msgctxt "Aura power zone"
msgid "Lightbar" msgid "Lightbar"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:52 #: rog-control-center/ui/types/aura_types.slint:55
msgctxt "Aura power zone" msgctxt "Aura power zone"
msgid "Lid" msgid "Lid"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:53 #: rog-control-center/ui/types/aura_types.slint:56
msgctxt "Aura power zone" msgctxt "Aura power zone"
msgid "Rear Glow" msgid "Rear Glow"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:54 rog-control-center/ui/types/aura_types.slint:61 #: rog-control-center/ui/types/aura_types.slint:57 rog-control-center/ui/types/aura_types.slint:65
msgctxt "Aura power zone" msgctxt "Aura power zone"
msgid "Keyboard and Lightbar" msgid "Keyboard and Lightbar"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:64 #: rog-control-center/ui/types/aura_types.slint:58
msgctxt "Aura power zone"
msgid "Ally"
msgstr ""
#: rog-control-center/ui/types/aura_types.slint:68
msgctxt "Aura brightness" msgctxt "Aura brightness"
msgid "Off" msgid "Off"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:65 #: rog-control-center/ui/types/aura_types.slint:69
msgctxt "Aura brightness" msgctxt "Aura brightness"
msgid "Low" msgid "Low"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:66 #: rog-control-center/ui/types/aura_types.slint:70
msgctxt "Aura brightness" msgctxt "Aura brightness"
msgid "Med" msgid "Med"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:67 #: rog-control-center/ui/types/aura_types.slint:71
msgctxt "Aura brightness" msgctxt "Aura brightness"
msgid "High" msgid "High"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:72 rog-control-center/ui/types/aura_types.slint:87 #: rog-control-center/ui/types/aura_types.slint:76 rog-control-center/ui/types/aura_types.slint:91
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Static" msgid "Static"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:73 rog-control-center/ui/types/aura_types.slint:88 #: rog-control-center/ui/types/aura_types.slint:77 rog-control-center/ui/types/aura_types.slint:92
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Breathe" msgid "Breathe"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:74 rog-control-center/ui/types/aura_types.slint:89 #: rog-control-center/ui/types/aura_types.slint:78 rog-control-center/ui/types/aura_types.slint:93
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Strobe" msgid "Strobe"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:75 #: rog-control-center/ui/types/aura_types.slint:79
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Rainbow" msgid "Rainbow"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:76 #: rog-control-center/ui/types/aura_types.slint:80
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Star" msgid "Star"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:77 #: rog-control-center/ui/types/aura_types.slint:81
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Rain" msgid "Rain"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:78 #: rog-control-center/ui/types/aura_types.slint:82
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Highlight" msgid "Highlight"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:79 #: rog-control-center/ui/types/aura_types.slint:83
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Laser" msgid "Laser"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:80 #: rog-control-center/ui/types/aura_types.slint:84
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Ripple" msgid "Ripple"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:81 #: rog-control-center/ui/types/aura_types.slint:85
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Nothing" msgid "Nothing"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:82 #: rog-control-center/ui/types/aura_types.slint:86
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Pulse" msgid "Pulse"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:83 #: rog-control-center/ui/types/aura_types.slint:87
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Comet" msgid "Comet"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:84 #: rog-control-center/ui/types/aura_types.slint:88
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Flash" msgid "Flash"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:96 #: rog-control-center/ui/types/aura_types.slint:100
msgctxt "Aura zone" msgctxt "Aura zone"
msgid "None" msgid "None"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:97 #: rog-control-center/ui/types/aura_types.slint:101
msgctxt "Aura zone" msgctxt "Aura zone"
msgid "Key1" msgid "Key1"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:98 #: rog-control-center/ui/types/aura_types.slint:102
msgctxt "Aura zone" msgctxt "Aura zone"
msgid "Key2" msgid "Key2"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:99 #: rog-control-center/ui/types/aura_types.slint:103
msgctxt "Aura zone" msgctxt "Aura zone"
msgid "Key3" msgid "Key3"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:100 #: rog-control-center/ui/types/aura_types.slint:104
msgctxt "Aura zone" msgctxt "Aura zone"
msgid "Key4" msgid "Key4"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:101 #: rog-control-center/ui/types/aura_types.slint:105
msgctxt "Aura zone" msgctxt "Aura zone"
msgid "Logo" msgid "Logo"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:102 #: rog-control-center/ui/types/aura_types.slint:106
msgctxt "Aura zone" msgctxt "Aura zone"
msgid "Lightbar Left" msgid "Lightbar Left"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:103 #: rog-control-center/ui/types/aura_types.slint:107
msgctxt "Aura zone" msgctxt "Aura zone"
msgid "Lightbar Right" msgid "Lightbar Right"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:107 #: rog-control-center/ui/types/aura_types.slint:111
msgctxt "Aura direction" msgctxt "Aura direction"
msgid "Right" msgid "Right"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:108 #: rog-control-center/ui/types/aura_types.slint:112
msgctxt "Aura direction" msgctxt "Aura direction"
msgid "Left" msgid "Left"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:109 #: rog-control-center/ui/types/aura_types.slint:113
msgctxt "Aura direction" msgctxt "Aura direction"
msgid "Up" msgid "Up"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:110 #: rog-control-center/ui/types/aura_types.slint:114
msgctxt "Aura direction" msgctxt "Aura direction"
msgid "Down" msgid "Down"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:114 #: rog-control-center/ui/types/aura_types.slint:118
msgctxt "Aura speed" msgctxt "Aura speed"
msgid "Low" msgid "Low"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:115 #: rog-control-center/ui/types/aura_types.slint:119
msgctxt "Aura speed" msgctxt "Aura speed"
msgid "Medium" msgid "Medium"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:116 #: rog-control-center/ui/types/aura_types.slint:120
msgctxt "Aura speed" msgctxt "Aura speed"
msgid "High" msgid "High"
msgstr "" msgstr ""
@@ -642,43 +647,43 @@ msgctxt "AuraPowerGroupOld"
msgid "Sleep" msgid "Sleep"
msgstr "" msgstr ""
#: rog-control-center/ui/main_window.slint:50 #: rog-control-center/ui/main_window.slint:51
msgctxt "MainWindow" msgctxt "MainWindow"
msgid "ROG" msgid "ROG"
msgstr "" msgstr ""
#: rog-control-center/ui/main_window.slint:52 #: rog-control-center/ui/main_window.slint:53
msgctxt "Menu1" msgctxt "Menu1"
msgid "System Control" msgid "System Control"
msgstr "" msgstr ""
#: rog-control-center/ui/main_window.slint:53 #: rog-control-center/ui/main_window.slint:54
msgctxt "Menu2" msgctxt "Menu2"
msgid "Keyboard Aura" msgid "Keyboard Aura"
msgstr "" msgstr ""
#: rog-control-center/ui/main_window.slint:54 #: rog-control-center/ui/main_window.slint:55
msgctxt "Menu3" msgctxt "Menu3"
msgid "AniMe Matrix" msgid "AniMe Matrix"
msgstr "" msgstr ""
#: rog-control-center/ui/main_window.slint:55 #: rog-control-center/ui/main_window.slint:56
msgctxt "Menu4" msgctxt "Menu4"
msgid "Fan Curves" msgid "Fan Curves"
msgstr "" msgstr ""
#: rog-control-center/ui/main_window.slint:56 #: rog-control-center/ui/main_window.slint:57
msgctxt "Menu5" msgctxt "Menu5"
msgid "App Settings" msgid "App Settings"
msgstr "" msgstr ""
#: rog-control-center/ui/main_window.slint:57 #: rog-control-center/ui/main_window.slint:58
msgctxt "Menu6" msgctxt "Menu6"
msgid "About" msgid "About"
msgstr "" msgstr ""
#: rog-control-center/ui/main_window.slint:69 #: rog-control-center/ui/main_window.slint:70
msgctxt "MainWindow" msgctxt "MainWindow"
msgid "Quit" msgid "Quit App"
msgstr "" msgstr ""
@@ -0,0 +1,700 @@
# SPDX-FileCopyrightText: 2024 UM-Li <um-li@tuta.io>
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2024-06-09 00:20+0000\n"
"PO-Revision-Date: 2024-06-19 10:37+0200\n"
"Last-Translator: UM-Li <um-li@tuta.io>\n"
"Language-Team: Chinese <kde-i18n-doc@kde.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh_CN\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Lokalize 24.05.1\n"
#: rog-control-center/ui/pages/anime.slint:6
msgctxt "Anime Brightness"
msgid "Off"
msgstr "关"
#: rog-control-center/ui/pages/anime.slint:7
msgctxt "Anime Brightness"
msgid "Low"
msgstr "暗"
#: rog-control-center/ui/pages/anime.slint:8
msgctxt "Anime Brightness"
msgid "Med"
msgstr "中"
#: rog-control-center/ui/pages/anime.slint:9
msgctxt "Anime Brightness"
msgid "High"
msgstr "亮"
#: rog-control-center/ui/pages/anime.slint:23
msgctxt "AnimePageData"
msgid "Glitch Construction"
msgstr ""
#: rog-control-center/ui/pages/anime.slint:23
msgctxt "AnimePageData"
msgid "Static Emergence"
msgstr ""
#: rog-control-center/ui/pages/anime.slint:25
msgctxt "AnimePageData"
msgid "Binary Banner Scroll"
msgstr ""
#: rog-control-center/ui/pages/anime.slint:25
msgctxt "AnimePageData"
msgid "Rog Logo Glitch"
msgstr ""
#: rog-control-center/ui/pages/anime.slint:27
msgctxt "AnimePageData"
msgid "Banner Swipe"
msgstr ""
#: rog-control-center/ui/pages/anime.slint:27
msgctxt "AnimePageData"
msgid "Starfield"
msgstr ""
#: rog-control-center/ui/pages/anime.slint:29
msgctxt "AnimePageData"
msgid "Glitch Out"
msgstr ""
#: rog-control-center/ui/pages/anime.slint:29
msgctxt "AnimePageData"
msgid "See Ya"
msgstr ""
#: rog-control-center/ui/pages/anime.slint:50
msgctxt "Anime Brightness"
msgid "Brightness"
msgstr "亮度"
#: rog-control-center/ui/pages/anime.slint:66
msgctxt "PageAnime"
msgid "Enable display"
msgstr "启用"
#: rog-control-center/ui/pages/anime.slint:74 rog-control-center/ui/pages/anime.slint:97
msgctxt "PageAnime"
msgid "Advanced"
msgstr "高级"
#: rog-control-center/ui/pages/anime.slint:89
msgctxt "PageAnime"
msgid "Use built-in animations"
msgstr "使用内置动效"
#: rog-control-center/ui/pages/anime.slint:146
msgctxt "PageAnime"
msgid "Set which builtin animations are played"
msgstr "选择内置动效"
#: rog-control-center/ui/pages/anime.slint:150
msgctxt "Anime built-in selection"
msgid "Boot Animation"
msgstr "开机"
#: rog-control-center/ui/pages/anime.slint:160
msgctxt "Anime built-in selection"
msgid "Running Animation"
msgstr "运行"
#: rog-control-center/ui/pages/anime.slint:170
msgctxt "Anime built-in selection"
msgid "Sleep Animation"
msgstr "睡眠"
#: rog-control-center/ui/pages/anime.slint:180
msgctxt "Anime built-in selection"
msgid "Shutdown Animation"
msgstr "关机"
#: rog-control-center/ui/pages/anime.slint:220
msgctxt "PageAnime"
msgid "Advanced Display Settings"
msgstr "高级显示设置"
#: rog-control-center/ui/pages/anime.slint:225
msgctxt "PageAnime"
msgid "Off when lid closed"
msgstr "合上盖子时关闭"
#: rog-control-center/ui/pages/anime.slint:234
msgctxt "PageAnime"
msgid "Off when suspended"
msgstr "睡眠时关闭"
#: rog-control-center/ui/pages/anime.slint:243
msgctxt "PageAnime"
msgid "Off when on battery"
msgstr "电池供电时关闭"
#: rog-control-center/ui/pages/app_settings.slint:26
msgctxt "PageAppSettings"
msgid "Run in background after closing"
msgstr "保持在后台运行"
#: rog-control-center/ui/pages/app_settings.slint:34
msgctxt "PageAppSettings"
msgid "Start app in background (UI closed)"
msgstr "启动至后台"
#: rog-control-center/ui/pages/app_settings.slint:42
msgctxt "PageAppSettings"
msgid "Enable system tray icon"
msgstr "启用托盘图标"
#: rog-control-center/ui/pages/app_settings.slint:50
msgctxt "PageAppSettings"
msgid "Enable dGPU notifications"
msgstr "启用独显通知"
#: rog-control-center/ui/pages/aura.slint:28
msgctxt "PageAura"
msgid "Brightness"
msgstr "亮度"
#: rog-control-center/ui/pages/aura.slint:39
msgctxt "PageAura"
msgid "Aura mode"
msgstr "Aura 模式"
#: rog-control-center/ui/pages/aura.slint:59
msgctxt "PageAura"
msgid "Colour 1"
msgstr "颜色 1"
#: rog-control-center/ui/pages/aura.slint:85
msgctxt "PageAura"
msgid "Colour 2"
msgstr "颜色 2"
#: rog-control-center/ui/pages/aura.slint:119
msgctxt "PageAura"
msgid "Zone"
msgstr "区域"
#: rog-control-center/ui/pages/aura.slint:142
msgctxt "PageAura"
msgid "Direction"
msgstr "方向"
#: rog-control-center/ui/pages/aura.slint:164
msgctxt "PageAura"
msgid "Speed"
msgstr "速度"
#: rog-control-center/ui/pages/aura.slint:185
msgctxt "PageAura"
msgid "Power Settings"
msgstr "电源设置"
#: rog-control-center/ui/pages/aura.slint:270
msgctxt "PageAura"
msgid "Power Zones"
msgstr "区域供电"
#: rog-control-center/ui/pages/fans.slint:26
msgctxt "FanTab"
msgid "This fan is not avilable on this machine"
msgstr "该风扇在本机不可用"
#: rog-control-center/ui/pages/fans.slint:34
msgctxt "FanTab"
msgid "Enabled"
msgstr "启用"
#: rog-control-center/ui/pages/fans.slint:43
msgctxt "FanTab"
msgid "Apply"
msgstr "应用"
#: rog-control-center/ui/pages/fans.slint:51
msgctxt "FanTab"
msgid "Cancel"
msgstr "取消"
#: rog-control-center/ui/pages/fans.slint:59
msgctxt "FanTab"
msgid "Factory Default (all fans)"
msgstr "全部恢复出厂设置"
#: rog-control-center/ui/pages/fans.slint:72
msgctxt "PageFans"
msgid "Balanced"
msgstr "平衡"
#: rog-control-center/ui/pages/fans.slint:75 rog-control-center/ui/pages/fans.slint:134 rog-control-center/ui/pages/fans.slint:193
msgctxt "PageFans"
msgid "CPU"
msgstr "CPU"
#: rog-control-center/ui/pages/fans.slint:93 rog-control-center/ui/pages/fans.slint:152 rog-control-center/ui/pages/fans.slint:211
msgctxt "PageFans"
msgid "Mid"
msgstr "中部"
#: rog-control-center/ui/pages/fans.slint:111 rog-control-center/ui/pages/fans.slint:170 rog-control-center/ui/pages/fans.slint:229
msgctxt "PageFans"
msgid "GPU"
msgstr "GPU"
#: rog-control-center/ui/pages/fans.slint:131
msgctxt "PageFans"
msgid "Performance"
msgstr "性能"
#: rog-control-center/ui/pages/fans.slint:190
msgctxt "PageFans"
msgid "Quiet"
msgstr "省电"
#: rog-control-center/ui/pages/system.slint:26
msgctxt "SystemPageData"
msgid "Balanced"
msgstr "平衡"
#: rog-control-center/ui/pages/system.slint:26 rog-control-center/ui/pages/system.slint:30
msgctxt "SystemPageData"
msgid "Performance"
msgstr "性能"
#: rog-control-center/ui/pages/system.slint:26
msgctxt "SystemPageData"
msgid "Quiet"
msgstr "省电"
#: rog-control-center/ui/pages/system.slint:29
msgctxt "SystemPageData"
msgid "Default"
msgstr "默认"
#: rog-control-center/ui/pages/system.slint:31
msgctxt "SystemPageData"
msgid "BalancePerformance"
msgstr "平衡(侧重性能)"
#: rog-control-center/ui/pages/system.slint:32
msgctxt "SystemPageData"
msgid "BalancePower"
msgstr "平衡(侧重省电)"
#: rog-control-center/ui/pages/system.slint:33
msgctxt "SystemPageData"
msgid "Power"
msgstr "省电"
#: rog-control-center/ui/pages/system.slint:110
msgctxt "PageSystem"
msgid "Base system settings"
msgstr "基本设置"
#: rog-control-center/ui/pages/system.slint:115
msgctxt "PageSystem"
msgid "Charge limit"
msgstr "充电上限"
#: rog-control-center/ui/pages/system.slint:127
msgctxt "PageSystem"
msgid "Throttle Policy"
msgstr "电源管理方案"
#: rog-control-center/ui/pages/system.slint:137
msgctxt "PageSystem"
msgid "Advanced"
msgstr "高级"
#: rog-control-center/ui/pages/system.slint:149
msgctxt "PageSystem"
msgid "Panel Overdrive"
msgstr "屏幕快显"
#: rog-control-center/ui/pages/system.slint:157
msgctxt "PageSystem"
msgid "MiniLED Mode"
msgstr "MiniLED 模式"
#: rog-control-center/ui/pages/system.slint:165
msgctxt "PageSystem"
msgid "POST boot sound"
msgstr "POST 开机音效"
#: rog-control-center/ui/pages/system.slint:183
msgctxt "PageSystem"
msgid "System performance settings"
msgstr "性能设置"
#: rog-control-center/ui/pages/system.slint:188
msgctxt "ppt_pl1_spl"
msgid "PL1, sustained power limit"
msgstr ""
#: rog-control-center/ui/pages/system.slint:198
msgctxt "ppt_pl2_sppt"
msgid "PL2, turbo power limit"
msgstr ""
#: rog-control-center/ui/pages/system.slint:208
msgctxt "ppt_fppt"
msgid "FPPT, Fast Power Limit"
msgstr ""
#: rog-control-center/ui/pages/system.slint:218
msgctxt "ppt_apu_sppt"
msgid "SPPT, APU slow power limit"
msgstr ""
#: rog-control-center/ui/pages/system.slint:228
msgctxt "ppt_platform_sppt"
msgid "Slow package power tracking limit"
msgstr ""
#: rog-control-center/ui/pages/system.slint:238
#, fuzzy
msgctxt "nv_dynamic_boost"
msgid "dGPU boost overclock"
msgstr "独显动态增强"
#: rog-control-center/ui/pages/system.slint:248
msgctxt "nv_temp_target"
msgid "dGPU temperature max"
msgstr "独显最高温度"
#: rog-control-center/ui/pages/system.slint:294
msgctxt "PageSystem"
msgid "Energy Performance Preference linked to Throttle Policy"
msgstr "绑定至电源管理方案的 EPP"
#: rog-control-center/ui/pages/system.slint:298
msgctxt "PageSystem"
msgid "Change EPP based on Throttle Policy"
msgstr "基于电源管理方案更改 EPP"
#: rog-control-center/ui/pages/system.slint:306
msgctxt "PageSystem"
msgid "EPP for Balanced Policy"
msgstr "平衡"
#: rog-control-center/ui/pages/system.slint:316
msgctxt "PageSystem"
msgid "EPP for Performance Policy"
msgstr "性能"
#: rog-control-center/ui/pages/system.slint:326
msgctxt "PageSystem"
msgid "EPP for Quiet Policy"
msgstr "省电"
#: rog-control-center/ui/pages/system.slint:344
msgctxt "PageSystem"
msgid "Throttle Policy for power state"
msgstr "基于供电状态的电源管理方案"
#: rog-control-center/ui/pages/system.slint:350
msgctxt "PageSystem"
msgid "Throttle Policy on Battery"
msgstr "电池供电"
#: rog-control-center/ui/pages/system.slint:360 rog-control-center/ui/pages/system.slint:381
msgctxt "PageSystem"
msgid "Enabled"
msgstr "启用"
#: rog-control-center/ui/pages/system.slint:371
msgctxt "PageSystem"
msgid "Throttle Policy on AC"
msgstr "交流供电"
#: rog-control-center/ui/types/aura_types.slint:49
msgctxt "Aura power zone"
msgid "Logo"
msgstr "徽标"
#: rog-control-center/ui/types/aura_types.slint:50 rog-control-center/ui/types/aura_types.slint:59
msgctxt "Aura power zone"
msgid "Keyboard"
msgstr "键盘"
#: rog-control-center/ui/types/aura_types.slint:51 rog-control-center/ui/types/aura_types.slint:60
msgctxt "Aura power zone"
msgid "Lightbar"
msgstr "灯带"
#: rog-control-center/ui/types/aura_types.slint:52
msgctxt "Aura power zone"
msgid "Lid"
msgstr "盖子"
#: rog-control-center/ui/types/aura_types.slint:53
msgctxt "Aura power zone"
msgid "Rear Glow"
msgstr "后灯"
#: rog-control-center/ui/types/aura_types.slint:54 rog-control-center/ui/types/aura_types.slint:61
msgctxt "Aura power zone"
msgid "Keyboard and Lightbar"
msgstr "键盘和灯带"
#: rog-control-center/ui/types/aura_types.slint:64
msgctxt "Aura brightness"
msgid "Off"
msgstr "关"
#: rog-control-center/ui/types/aura_types.slint:65
msgctxt "Aura brightness"
msgid "Low"
msgstr "暗"
#: rog-control-center/ui/types/aura_types.slint:66
msgctxt "Aura brightness"
msgid "Med"
msgstr "中"
#: rog-control-center/ui/types/aura_types.slint:67
msgctxt "Aura brightness"
msgid "High"
msgstr "亮"
#: rog-control-center/ui/types/aura_types.slint:72 rog-control-center/ui/types/aura_types.slint:87
#, fuzzy
msgctxt "Basic aura mode"
msgid "Static"
msgstr "恒亮"
#: rog-control-center/ui/types/aura_types.slint:73 rog-control-center/ui/types/aura_types.slint:88
#, fuzzy
msgctxt "Basic aura mode"
msgid "Breathe"
msgstr "呼吸"
#: rog-control-center/ui/types/aura_types.slint:74 rog-control-center/ui/types/aura_types.slint:89
#, fuzzy
msgctxt "Basic aura mode"
msgid "Strobe"
msgstr "频谱"
#: rog-control-center/ui/types/aura_types.slint:75
#, fuzzy
msgctxt "Basic aura mode"
msgid "Rainbow"
msgstr "彩虹"
#: rog-control-center/ui/types/aura_types.slint:76
#, fuzzy
msgctxt "Basic aura mode"
msgid "Star"
msgstr "星光"
#: rog-control-center/ui/types/aura_types.slint:77
#, fuzzy
msgctxt "Basic aura mode"
msgid "Rain"
msgstr "落雨"
#: rog-control-center/ui/types/aura_types.slint:78
#, fuzzy
msgctxt "Basic aura mode"
msgid "Highlight"
msgstr "高亮"
#: rog-control-center/ui/types/aura_types.slint:79
#, fuzzy
msgctxt "Basic aura mode"
msgid "Laser"
msgstr "激光"
#: rog-control-center/ui/types/aura_types.slint:80
#, fuzzy
msgctxt "Basic aura mode"
msgid "Ripple"
msgstr "涟漪"
#: rog-control-center/ui/types/aura_types.slint:81
#, fuzzy
msgctxt "Basic aura mode"
msgid "Nothing"
msgstr "无"
#: rog-control-center/ui/types/aura_types.slint:82
#, fuzzy
msgctxt "Basic aura mode"
msgid "Pulse"
msgstr "脉冲"
#: rog-control-center/ui/types/aura_types.slint:83
#, fuzzy
msgctxt "Basic aura mode"
msgid "Comet"
msgstr "彗星"
#: rog-control-center/ui/types/aura_types.slint:84
#, fuzzy
msgctxt "Basic aura mode"
msgid "Flash"
msgstr "闪烁"
#: rog-control-center/ui/types/aura_types.slint:96
msgctxt "Aura zone"
msgid "None"
msgstr "无"
#: rog-control-center/ui/types/aura_types.slint:97
msgctxt "Aura zone"
msgid "Key1"
msgstr "按键 1"
#: rog-control-center/ui/types/aura_types.slint:98
msgctxt "Aura zone"
msgid "Key2"
msgstr "按键 2"
#: rog-control-center/ui/types/aura_types.slint:99
msgctxt "Aura zone"
msgid "Key3"
msgstr "按键 3"
#: rog-control-center/ui/types/aura_types.slint:100
msgctxt "Aura zone"
msgid "Key4"
msgstr "按键 4"
#: rog-control-center/ui/types/aura_types.slint:101
msgctxt "Aura zone"
msgid "Logo"
msgstr "徽标"
#: rog-control-center/ui/types/aura_types.slint:102
msgctxt "Aura zone"
msgid "Lightbar Left"
msgstr "左灯带"
#: rog-control-center/ui/types/aura_types.slint:103
msgctxt "Aura zone"
msgid "Lightbar Right"
msgstr "右灯带"
#: rog-control-center/ui/types/aura_types.slint:107
msgctxt "Aura direction"
msgid "Right"
msgstr "右"
#: rog-control-center/ui/types/aura_types.slint:108
msgctxt "Aura direction"
msgid "Left"
msgstr "左"
#: rog-control-center/ui/types/aura_types.slint:109
msgctxt "Aura direction"
msgid "Up"
msgstr "上"
#: rog-control-center/ui/types/aura_types.slint:110
msgctxt "Aura direction"
msgid "Down"
msgstr "下"
#: rog-control-center/ui/types/aura_types.slint:114
msgctxt "Aura speed"
msgid "Low"
msgstr "慢"
#: rog-control-center/ui/types/aura_types.slint:115
msgctxt "Aura speed"
msgid "Medium"
msgstr "中"
#: rog-control-center/ui/types/aura_types.slint:116
msgctxt "Aura speed"
msgid "High"
msgstr "快"
#: rog-control-center/ui/widgets/aura_power.slint:33
msgctxt "AuraPowerGroup"
msgid "Boot"
msgstr "开机"
#: rog-control-center/ui/widgets/aura_power.slint:43
msgctxt "AuraPowerGroup"
msgid "Awake"
msgstr "唤醒"
#: rog-control-center/ui/widgets/aura_power.slint:53
msgctxt "AuraPowerGroup"
msgid "Sleep"
msgstr "睡眠"
#: rog-control-center/ui/widgets/aura_power.slint:63
msgctxt "AuraPowerGroup"
msgid "Shutdown"
msgstr "关机"
#: rog-control-center/ui/widgets/aura_power.slint:102
msgctxt "AuraPowerGroupOld"
msgid "Zone Selection"
msgstr "区域"
#: rog-control-center/ui/widgets/aura_power.slint:114
msgctxt "AuraPowerGroupOld"
msgid "Boot"
msgstr "开机"
#: rog-control-center/ui/widgets/aura_power.slint:124
msgctxt "AuraPowerGroupOld"
msgid "Awake"
msgstr "唤醒"
#: rog-control-center/ui/widgets/aura_power.slint:134
msgctxt "AuraPowerGroupOld"
msgid "Sleep"
msgstr "睡眠"
#: rog-control-center/ui/main_window.slint:51
msgctxt "MainWindow"
msgid "ROG"
msgstr "ROG"
#: rog-control-center/ui/main_window.slint:53
msgctxt "Menu1"
msgid "System Control"
msgstr "系统设置"
#: rog-control-center/ui/main_window.slint:54
msgctxt "Menu2"
msgid "Keyboard Aura"
msgstr "Aura 键盘灯效"
#: rog-control-center/ui/main_window.slint:55
msgctxt "Menu3"
msgid "AniMe Matrix"
msgstr "AniMe 光显矩阵"
#: rog-control-center/ui/main_window.slint:56
msgctxt "Menu4"
msgid "Fan Curves"
msgstr "风扇曲线"
#: rog-control-center/ui/main_window.slint:57
msgctxt "Menu5"
msgid "App Settings"
msgstr "应用设置"
#: rog-control-center/ui/main_window.slint:58
msgctxt "Menu6"
msgid "About"
msgstr "关于"
#: rog-control-center/ui/main_window.slint:70
msgctxt "MainWindow"
msgid "Quit App"
msgstr "退出"
+37 -1
View File
@@ -5,6 +5,7 @@ import { SideBar } from "widgets/sidebar.slint";
import { PageAbout } from "pages/about.slint"; import { PageAbout } from "pages/about.slint";
import { PageFans } from "pages/fans.slint"; import { PageFans } from "pages/fans.slint";
import { PageAnime, AnimePageData } from "pages/anime.slint"; import { PageAnime, AnimePageData } from "pages/anime.slint";
import { RogItem } from "widgets/common.slint";
import { PageAura } from "pages/aura.slint"; import { PageAura } from "pages/aura.slint";
import { Node } from "widgets/graph.slint"; import { Node } from "widgets/graph.slint";
export { Node } export { Node }
@@ -66,7 +67,7 @@ export component MainWindow inherits Window {
Text { Text {
vertical-alignment: center; vertical-alignment: center;
horizontal-alignment: center; horizontal-alignment: center;
text: @tr("Quit"); text: @tr("Quit App");
} }
TouchArea { TouchArea {
@@ -178,4 +179,39 @@ export component MainWindow inherits Window {
} }
} }
} }
if SomeError.error_message != "": Rectangle {
x: 0px;
y: 0px;
width: root.width;
height: root.height;
padding: 10px;
background: Palette.background;
border-color: Palette.border;
border-width: 3px;
border-radius: 10px;
VerticalBox {
RogItem {
min-height: 50px;
max-height: 100px;
Text {
text <=> SomeError.error_message;
font-size: 18px;
}
}
Text {
text <=> SomeError.error_help;
horizontal-alignment: TextHorizontalAlignment.center;
vertical-alignment: TextVerticalAlignment.center;
}
}
}
}
export global SomeError {
in property <string> error_message: "";
in property <string> error_help: "";
} }
+7 -7
View File
@@ -185,7 +185,7 @@ export component PageSystem inherits Rectangle {
} }
if SystemPageData.available.ppt-pl1-spl: SystemSlider { if SystemPageData.available.ppt-pl1-spl: SystemSlider {
text: @tr("ppt_pl1_spl" => "ppt_pl1_spl"); text: @tr("ppt_pl1_spl" => "PL1, sustained power limit");
minimum: 5; minimum: 5;
maximum: 250; maximum: 250;
value <=> SystemPageData.ppt_pl1_spl; value <=> SystemPageData.ppt_pl1_spl;
@@ -195,7 +195,7 @@ export component PageSystem inherits Rectangle {
} }
if SystemPageData.available.ppt-pl2-sppt: SystemSlider { if SystemPageData.available.ppt-pl2-sppt: SystemSlider {
text: @tr("ppt_pl2_sppt" => "ppt_pl2_sppt"); text: @tr("ppt_pl2_sppt" => "PL2, turbo power limit");
minimum: 5; minimum: 5;
maximum: 250; maximum: 250;
value <=> SystemPageData.ppt_pl2_sppt; value <=> SystemPageData.ppt_pl2_sppt;
@@ -205,7 +205,7 @@ export component PageSystem inherits Rectangle {
} }
if SystemPageData.available.ppt-fppt: SystemSlider { if SystemPageData.available.ppt-fppt: SystemSlider {
text: @tr("ppt_fppt" => "ppt_fppt"); text: @tr("ppt_fppt" => "FPPT, Fast Power Limit");
minimum: 5; minimum: 5;
maximum: 250; maximum: 250;
value <=> SystemPageData.ppt_fppt; value <=> SystemPageData.ppt_fppt;
@@ -215,7 +215,7 @@ export component PageSystem inherits Rectangle {
} }
if SystemPageData.available.ppt-apu-sppt: SystemSlider { if SystemPageData.available.ppt-apu-sppt: SystemSlider {
text: @tr("ppt_apu_sppt" => "ppt_apu_sppt"); text: @tr("ppt_apu_sppt" => "SPPT, APU slow power limit");
minimum: 5; minimum: 5;
maximum: 130; maximum: 130;
value <=> SystemPageData.ppt_apu_sppt; value <=> SystemPageData.ppt_apu_sppt;
@@ -225,7 +225,7 @@ export component PageSystem inherits Rectangle {
} }
if SystemPageData.available.ppt-platform-sppt: SystemSlider { if SystemPageData.available.ppt-platform-sppt: SystemSlider {
text: @tr("ppt_platform_sppt" => "ppt_platform_sppt"); text: @tr("ppt_platform_sppt" => "Slow package power tracking limit");
maximum: 130; maximum: 130;
minimum: 5; minimum: 5;
value <=> SystemPageData.ppt_platform_sppt; value <=> SystemPageData.ppt_platform_sppt;
@@ -235,7 +235,7 @@ export component PageSystem inherits Rectangle {
} }
if SystemPageData.available.nv-dynamic-boost: SystemSlider { if SystemPageData.available.nv-dynamic-boost: SystemSlider {
text: @tr("nv_dynamic_boost" => "nv_dynamic_boost"); text: @tr("nv_dynamic_boost" => "dGPU boost overclock");
minimum: 5; minimum: 5;
maximum: 25; maximum: 25;
value <=> SystemPageData.nv_dynamic_boost; value <=> SystemPageData.nv_dynamic_boost;
@@ -245,7 +245,7 @@ export component PageSystem inherits Rectangle {
} }
if SystemPageData.available.nv-temp-target: SystemSlider { if SystemPageData.available.nv-temp-target: SystemSlider {
text: @tr("nv_temp_target" => "nv_temp_target"); text: @tr("nv_temp_target" => "dGPU temperature max");
minimum: 75; minimum: 75;
maximum: 87; maximum: 87;
value <=> SystemPageData.nv_temp_target; value <=> SystemPageData.nv_temp_target;
@@ -4,6 +4,8 @@ export enum AuraDevType {
Tuf, Tuf,
ScsiExtDisk, ScsiExtDisk,
Unknown, Unknown,
Ally,
AnimeOrSlash,
} }
export struct AuraEffect { export struct AuraEffect {
@@ -28,6 +30,7 @@ export enum PowerZones {
Lid, Lid,
RearGlow, RearGlow,
KeyboardAndLightbar, KeyboardAndLightbar,
Ally,
} }
export struct AuraPowerState { export struct AuraPowerState {
@@ -52,6 +55,7 @@ export global AuraPageData {
@tr("Aura power zone" => "Lid"), @tr("Aura power zone" => "Lid"),
@tr("Aura power zone" => "Rear Glow"), @tr("Aura power zone" => "Rear Glow"),
@tr("Aura power zone" => "Keyboard and Lightbar"), @tr("Aura power zone" => "Keyboard and Lightbar"),
@tr("Aura power zone" => "Ally"),
]; ];
// Exists only for the older 0x1866 keybaords. On page setup it must // Exists only for the older 0x1866 keybaords. On page setup it must
// be set to match the supported_power_zones // be set to match the supported_power_zones
+1 -1
View File
@@ -17,7 +17,7 @@ export component SystemSlider inherits RogItem {
callback released(int); callback released(int);
HorizontalLayout { HorizontalLayout {
HorizontalLayout { HorizontalLayout {
width: 30%; width: 50%;
alignment: LayoutAlignment.space-between; alignment: LayoutAlignment.space-between;
padding-left: 10px; padding-left: 10px;
Text { Text {
+1 -2
View File
@@ -13,10 +13,9 @@ description = "dbus interface methods for asusctl"
asusd = { path = "../asusd" } asusd = { path = "../asusd" }
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_scsi = { path = "../rog-scsi", features = ["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" }
zbus.workspace = true zbus.workspace = true
[dev-dependencies]
cargo-husky.workspace = true
+1
View File
@@ -1,5 +1,6 @@
pub use asusd::{DBUS_IFACE, DBUS_NAME, DBUS_PATH}; pub use asusd::{DBUS_IFACE, DBUS_NAME, DBUS_PATH};
pub mod scsi_aura;
pub mod zbus_anime; pub mod zbus_anime;
pub mod zbus_aura; pub mod zbus_aura;
pub mod zbus_fan_curves; pub mod zbus_fan_curves;
+56
View File
@@ -0,0 +1,56 @@
//! # D-Bus interface proxy for: `org.asuslinux.ScsiAura`
//!
//! This code was generated by `zbus-xmlgen` `5.0.1` from D-Bus introspection
//! data. Source: `Interface '/org/asuslinux/M3D0AP048745_scsi' from service
//! 'org.asuslinux.Daemon' on system bus`.
//!
//! You may prefer to adapt it, instead of using it verbatim.
//!
//! More information can be found in the [Writing a client proxy] section of the
//! zbus documentation.
//!
//! This type implements the [D-Bus standard interfaces],
//! (`org.freedesktop.DBus.*`) for which the following zbus API can be used:
//!
//! * [`zbus::fdo::PeerProxy`]
//! * [`zbus::fdo::PropertiesProxy`]
//! * [`zbus::fdo::IntrospectableProxy`]
//!
//! Consequently `zbus-xmlgen` did not generate code for the above interfaces.
//!
//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html
//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces,
use rog_scsi::{AuraEffect, AuraMode};
use zbus::proxy;
#[proxy(
interface = "org.asuslinux.ScsiAura",
default_service = "org.asuslinux.Daemon",
default_path = "/org/asuslinux"
)]
pub trait ScsiAura {
/// AllModeData method
#[allow(clippy::type_complexity)]
fn all_mode_data(&self) -> zbus::Result<std::collections::HashMap<AuraMode, AuraEffect>>;
/// DeviceType property
#[zbus(property)]
fn device_type(&self) -> zbus::Result<u32>;
/// Enabled property
#[zbus(property)]
fn enabled(&self) -> zbus::Result<bool>;
#[zbus(property)]
fn set_enabled(&self, value: bool) -> zbus::Result<()>;
/// LedMode property
#[zbus(property)]
fn led_mode(&self) -> zbus::Result<u8>;
#[zbus(property)]
fn set_led_mode(&self, mode: AuraMode) -> zbus::Result<()>;
/// LedModeData property
#[zbus(property)]
fn led_mode_data(&self) -> zbus::Result<AuraEffect>;
#[zbus(property)]
fn set_led_mode_data(&self, effect: AuraEffect) -> zbus::Result<()>;
}
+1 -1
View File
@@ -7,7 +7,7 @@ use zbus::proxy;
default_service = "org.asuslinux.Daemon", default_service = "org.asuslinux.Daemon",
default_path = "/org/asuslinux" default_path = "/org/asuslinux"
)] )]
trait Anime { pub trait Anime {
/// DeviceState method /// DeviceState method
fn device_state(&self) -> zbus::Result<AnimeDeviceState>; fn device_state(&self) -> zbus::Result<AnimeDeviceState>;
+4 -4
View File
@@ -22,7 +22,7 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use rog_aura::keyboard::{LaptopAuraPower, UsbPackets}; use rog_aura::keyboard::{AuraLaptopUsbPackets, LaptopAuraPower};
use rog_aura::{AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, LedBrightness, PowerZones}; use rog_aura::{AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, LedBrightness, PowerZones};
use zbus::blocking::Connection; use zbus::blocking::Connection;
use zbus::{proxy, Result}; use zbus::{proxy, Result};
@@ -34,12 +34,12 @@ const BLOCKING_TIME: u64 = 33; // 100ms = 10 FPS, max 50ms = 20 FPS, 40ms = 25 F
default_service = "org.asuslinux.Daemon", default_service = "org.asuslinux.Daemon",
default_path = "/org/asuslinux/Aura" default_path = "/org/asuslinux/Aura"
)] )]
trait Aura { pub trait Aura {
/// AllModeData method /// AllModeData method
fn all_mode_data(&self) -> zbus::Result<BTreeMap<AuraModeNum, AuraEffect>>; fn all_mode_data(&self) -> zbus::Result<BTreeMap<AuraModeNum, AuraEffect>>;
/// DirectAddressingRaw method /// DirectAddressingRaw method
fn direct_addressing_raw(&self, data: UsbPackets) -> zbus::Result<()>; fn direct_addressing_raw(&self, data: AuraLaptopUsbPackets) -> zbus::Result<()>;
/// Brightness property /// Brightness property
#[zbus(property)] #[zbus(property)]
@@ -104,7 +104,7 @@ impl<'a> AuraProxyPerkey<'a> {
/// Intentionally blocks for 10ms after sending to allow the block to /// Intentionally blocks for 10ms after sending to allow the block to
/// be written to the keyboard EC. This should not be async. /// be written to the keyboard EC. This should not be async.
#[inline] #[inline]
pub fn direct_addressing_raw(&self, direct_raw: UsbPackets) -> Result<()> { pub fn direct_addressing_raw(&self, direct_raw: AuraLaptopUsbPackets) -> Result<()> {
self.0.direct_addressing_raw(direct_raw)?; self.0.direct_addressing_raw(direct_raw)?;
std::thread::sleep(std::time::Duration::from_millis(BLOCKING_TIME)); std::thread::sleep(std::time::Duration::from_millis(BLOCKING_TIME));
Ok(()) Ok(())
+1 -1
View File
@@ -30,7 +30,7 @@ use zbus::proxy;
default_service = "org.asuslinux.Daemon", default_service = "org.asuslinux.Daemon",
default_path = "/org/asuslinux" default_path = "/org/asuslinux"
)] )]
trait FanCurves { pub trait FanCurves {
/// Get the fan-curve data for the currently active PlatformProfile /// Get the fan-curve data for the currently active PlatformProfile
fn fan_curve_data(&self, profile: ThrottlePolicy) -> zbus::Result<Vec<CurveData>>; fn fan_curve_data(&self, profile: ThrottlePolicy) -> zbus::Result<Vec<CurveData>>;
+4 -1
View File
@@ -29,7 +29,7 @@ use zbus::proxy;
default_service = "org.asuslinux.Daemon", default_service = "org.asuslinux.Daemon",
default_path = "/org/asuslinux" default_path = "/org/asuslinux"
)] )]
trait Platform { pub trait Platform {
#[zbus(property)] #[zbus(property)]
fn version(&self) -> zbus::Result<String>; fn version(&self) -> zbus::Result<String>;
@@ -45,6 +45,9 @@ trait Platform {
#[zbus(property)] #[zbus(property)]
fn set_charge_control_end_threshold(&self, value: u8) -> zbus::Result<()>; fn set_charge_control_end_threshold(&self, value: u8) -> zbus::Result<()>;
// Toggle one-shot charge to 100%
fn one_shot_full_charge(&self) -> zbus::Result<()>;
/// DgpuDisable property /// DgpuDisable property
#[zbus(property)] #[zbus(property)]
fn dgpu_disable(&self) -> zbus::Result<bool>; fn dgpu_disable(&self) -> zbus::Result<bool>;
+1 -1
View File
@@ -6,7 +6,7 @@ use zbus::proxy;
default_service = "org.asuslinux.Daemon", default_service = "org.asuslinux.Daemon",
default_path = "/org/asuslinux" default_path = "/org/asuslinux"
)] )]
trait Slash { pub trait Slash {
/// EnableDisplay property /// EnableDisplay property
#[zbus(property)] #[zbus(property)]
fn enabled(&self) -> zbus::Result<bool>; fn enabled(&self) -> zbus::Result<bool>;

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