Compare commits

...

137 Commits

Author SHA1 Message Date
Denis Benato
5e5cebc781 Revert "Fix: continue -> return in set_config_or_default"
This reverts commit ad051bd7b8.
2025-11-06 22:53:21 +01:00
Denis Benato
def691f9d0 Fix: sliders not updating the value 2025-11-06 22:53:09 +01:00
Denis Benato
80eeafd9e1 Feat: config reload and do not apply properties if max==min 2025-11-06 21:06:44 +01:00
Denis Benato
bf173300e0 Feat: start in tray mode 2025-11-06 17:29:12 +01:00
Denis Benato
07874d7452 tests: load bundled aura_support.ron when system files missing to make unit tests reliable in CI 2025-11-06 17:24:01 +01:00
Denis Benato
894a0d2b11 Chore: update CHANGELOG.md 2025-11-06 16:12:44 +01:00
Denis Benato
11dc02612e Fix clippy warnings: alias complex types, allow some clippy lints, collapse else-if, avoid is_multiple_of usage 2025-11-06 16:05:38 +01:00
Denis Benato
fccb233e9e Fix: stop rust from silly complains about is_multiple_of 2025-11-06 15:58:30 +01:00
Denis Benato
c13612e02c Fix: simplify an if 2025-11-06 15:52:43 +01:00
Denis Benato
80147966a9 Chore: use stable 2025-11-06 15:49:05 +01:00
Denis Benato
a1815ac40c Chore: improve tests 2025-11-06 15:34:30 +01:00
Denis Benato
ad051bd7b8 Fix: continue -> return in set_config_or_default 2025-11-06 15:07:31 +01:00
Denis Benato
90676b390e Chore: update CHANGELOG.md 2025-11-06 15:04:59 +01:00
Denis Benato
974f2acafa Feat: better handling of nv_ properties 2025-11-06 14:53:57 +01:00
Denis Benato
6ae3ae5284 Feat: make nvidia dGPU tunables power-profile dependant 2025-11-06 14:26:03 +01:00
Denis Benato
34699a7021 Fix: notifications not respecting settings 2025-11-06 02:57:20 +01:00
Denis Benato
ef311689ec Fix: do not stall the boot process 2025-11-06 00:55:20 +01:00
Denis Benato
8ee0281b4f Fix: fedora build 2025-11-05 22:40:58 +01:00
Denis Benato
d8504b5430 Changelog for 6.1.17 2025-11-05 21:14:21 +01:00
Denis Benato
5c4d833fbd Prepare for 6.1.17 2025-11-05 21:09:18 +01:00
Denis Benato
698999e828 cargo fmt and README.md restyle 2025-11-05 20:02:43 +01:00
Denis Benato
0eae9e55c6 Merge branch 'devel' 2025-11-05 19:54:21 +01:00
Denis Benato
07171888a1 Merge branch 'main' into 'main'
feat: Allow setting default profile for ac and battery

See merge request asus-linux/asusctl!229
2025-11-05 18:53:53 +00:00
matszwe02
9321fde6af feat: Allow setting default profile for ac and battery 2025-11-05 01:44:31 +01:00
Denis Benato
f90d0a6673 Merge branch 'devel' into 'devel'
Add install-data-asusd_user to install-data in Makefile

See merge request asus-linux/asusctl!228
2025-10-29 18:49:50 +00:00
Synby
bbd03c128d Edit Makefile
add install-data-asusd_user to install-data
2025-10-29 17:23:29 +00:00
Denis Benato
132a2f3665 Chore: complete the switch back to stable 2025-10-23 14:50:40 +02:00
Denis Benato
180566e5f1 Fix: share a single HID device
Avoid opening multiple handles to the same device whenever possible.
2025-10-22 22:05:17 +02:00
Denis Benato
c9e76f3273 Chore: prepare for 6.1.16 release 2025-10-20 19:15:36 +02:00
Denis Benato
2997ae83ff Merge branch 'devel' 2025-10-20 04:08:20 +02:00
Denis Benato
151d681e16 feat: expose MCU Powersave toggle on rogcc 2025-10-20 03:47:09 +02:00
Denis Benato
90b3f43a36 Chore: add more debugging 2025-10-20 02:49:47 +02:00
Denis Benato
a345b09ce1 Chore: type abttery -> battery 2025-10-20 02:46:07 +02:00
Denis Benato
f26b0d8de5 Merge branch 'profile-match' into 'main'
Allow flexible profile cli input for LowPower

See merge request asus-linux/asusctl!226
2025-10-20 00:40:54 +00:00
yanganto
9366b0ec04 Allow flexible profile cli input for LowPower 2025-10-20 00:40:54 +00:00
Denis Benato
415712143b Feat: add screen_auto_brightness 2025-10-20 02:08:59 +02:00
Denis Benato
60fce30a06 Chore: flush out requirement for nightly 2025-10-20 00:39:17 +02:00
Denis Benato
d8f06230fa Chore: Added support for FX607J 2025-10-19 23:02:45 +02:00
Denis Benato
834464a527 Fix: fixed ac_command/bat_command 2025-10-19 23:00:23 +02:00
Denis Benato
4faa96298a Chore: Added supprot for G614JIR 2025-10-19 22:32:05 +02:00
Denis Benato
78c574b761 chore_ add supprot for GU605CR 2025-10-19 21:27:33 +02:00
Denis Benato
9785eafd53 Modified to README.md to not mislead users attempting to compile it 2025-10-19 20:45:49 +02:00
Denis Benato
51cad9ea7e chore: make the startup more verbose 2025-10-16 15:17:27 +02:00
Denis Benato
319373faea chore: Prepare for 6.1.15 release 2025-10-14 01:57:03 +02:00
Denis Benato
f6aa3e3d01 Merge branch 'devel' 2025-10-14 01:50:20 +02:00
Denis Benato
d11fc20bab feat: apply the proper configuration depending on the plug status 2025-10-13 21:36:01 +02:00
Denis Benato
b0e1b21e4b chore: cargo clippy fix 2025-10-12 22:34:20 +02:00
Denis Benato
1c1daaa6d2 chore: reimplement default for FanCurveCPU 2025-10-12 19:47:26 +02:00
HiFiPhile
c3b5de843f Add TX Air rule 2025-10-12 19:44:30 +02:00
Denis Benato
09dcfb4065 Fix multiple warnings 2025-10-12 19:43:07 +02:00
Denis Benato
b2e7211bbe chore: cargo fmt 2025-10-12 19:43:05 +02:00
Denis Benato
1ab1adf937 Merge branch 'tx_air' into 'main'
Add TX Air (TUF Chinese version) udev rule

See merge request asus-linux/asusctl!227
2025-10-12 17:42:09 +00:00
HiFiPhile
a21bf779b0 Add TX Air rule 2025-10-12 13:20:07 +02:00
Denis Benato
0dba22529c feat: change limits dynamically 2025-10-11 20:51:57 +02:00
Denis Benato
180d63620b Fix multiple warnings 2025-10-08 01:19:38 +02:00
Denis Benato
daea1f538c chore: cargo fmt 2025-10-08 01:01:43 +02:00
Denis Benato
f5e2484797 chore: release 6.1.14 2025-10-08 00:56:25 +02:00
Denis Benato
7105ae40c6 chore: attempt to fix tests 2025-10-08 00:35:09 +02:00
Denis Benato
33f9900ef9 chore: fix formatting 2025-10-08 00:19:43 +02:00
Denis Benato
1aa1c62e40 chore: update spec file version to 6.1.13 2025-10-07 23:47:09 +02:00
Denis Benato
3a18ef4c7b chore: Prepare for 6.1.13 release 2025-10-07 23:47:02 +02:00
Denis Benato
9dcce77302 chore: fix a warning 2025-10-06 19:37:30 +02:00
Denis Benato
995df9b51b fix: fix building due to a double-borrow 2025-10-06 19:29:29 +02:00
Denis Benato
84c8babdb7 fix: make the startup path more robust 2025-10-06 19:19:31 +02:00
Denis Benato
1014f97b6f fix: apply panel overdrive and other properties on asusd startup 2025-10-05 23:13:41 +02:00
Denis Benato
3c023be57d chore: update changelog for the upcoming release 2025-10-05 17:29:51 +02:00
Denis Benato
eff20c84d6 chore: add ubuntu install instructions 2025-10-05 17:29:12 +02:00
Denis Benato
f8984eb7e9 chore: update crates 2025-10-05 17:18:24 +02:00
Denis Benato
2ffd2a1e1f chore: compile packages on make install 2025-10-05 16:58:03 +02:00
Denis Benato
341bd081f8 chore: fix makefile 2025-10-05 16:45:29 +02:00
Denis Benato
52af4203a1 Merge remote-tracking branch 'github/main' 2025-10-05 16:15:36 +02:00
Denis Benato
e5a6088392 Merge pull request #84 from evertvorster/patch-1
Update asusd.rules to have absolute path
2025-10-05 16:15:20 +02:00
Denis Benato
6ee5dfb352 chore: require power-profile-daemon on fedora 2025-10-05 16:07:58 +02:00
Evert Vorster
3f8336fc5e Update asusd.rules
Added the absolute path for systemctl. No modern distrobution installs it anywhere else.
2025-10-05 16:02:48 +02:00
Denis Benato
8fc7e8f3a7 Merge remote-tracking branch 'gitlab/main' 2025-10-05 16:02:08 +02:00
Denis Benato
098b1f2668 chore: add *.patch to gitignore 2025-10-05 15:57:20 +02:00
Denis Benato
20df3ad2f2 Merge remote-tracking branch 'github/main' 2025-10-05 15:52:50 +02:00
Denis Benato
7aaadad6da Merge pull request #39 from rashadgasimli/main
Add Azerbaijani language
2025-10-05 15:48:37 +02:00
Luke Jones
be60c1ba02 Merge pull request #69 from kxxt/tx
Add udev match for TX Gaming
2025-06-10 12:28:47 +12:00
kxxt
698a8e8677 Add udev match for TX Gaming 2025-05-29 20:38:05 +08:00
fluke
ce6420eeac Merge branch 'aura-support-fx706heb' into 'main'
Add AURA support definition for 2021 TUF F17

See merge request asus-linux/asusctl!225
2025-05-19 08:53:03 +00:00
fluke
f5f5e4f720 Edit FX706HEB to become FX706H and match more models 2025-05-19 08:52:11 +00:00
Brandon Tolbird
c08503826b Add AURA support definition for 2021 TUF F17
(FX706HEB)
2025-05-18 22:20:42 +00:00
Luke Jones
685345d656 chore: update spec file version to 6.1.12 2025-04-06 13:51:55 +12:00
Luke Jones
59aab24a4a Fix unbouneded loop and prep new version 2025-04-06 13:26:53 +12:00
Luke Jones
c3f0e61ebc chore: update spec file version to 6.1.11 2025-04-05 21:51:35 +13:00
Luke Jones
c143536cd0 Prep new release 2025-04-06 07:55:06 +12:00
Luke Jones
2a168e93d3 chore: update translations 2025-04-06 07:36:21 +12:00
Luke Jones
c3570a23f1 feature: add UI controls fro screenpad 2025-04-06 07:36:21 +12:00
Luke Jones
9db6cb5545 feature: watch primary backlight and sync screenpad to it 2025-04-06 02:41:46 +12:00
Luke Jones
836575c0a8 feature: screenpad settings config store 2025-04-06 02:09:45 +13:00
Luke Jones
61f2216c25 fix: minor clippy fix 2025-04-06 01:04:03 +13:00
Luke Jones
7f5b3ef376 feature: add support for screenpad brightness 2025-04-06 01:04:03 +13:00
fluke
257471a36c Merge branch 'fix/anime-flicker' into 'main'
Fix anime flickering when repeatedly setting images in a tight loop

See merge request asus-linux/asusctl!223
2025-03-27 21:13:25 +00:00
I-Al-Istannen
568f3e848f Fix anime flickering when repeatedly setting images in a tight loop 2025-03-27 21:05:23 +01:00
fluke
0c9b58755f Merge branch 'pt_BR-translation' into 'main'
Include pt_BR translations file.

See merge request asus-linux/asusctl!222
2025-03-25 01:18:33 +00:00
PabloKiryu
1b47fb7873 Include pt_BR translations file. 2025-03-18 00:32:25 -03:00
Denis Benato
df93209839 chore: Install LICENSE file 2025-03-13 01:28:06 +01:00
Denis Benato
11ee7827e9 chore: Add packaging instructions for deb
This commits adds .deb packaging support for all applications in this repository.
2025-03-12 18:18:08 +00:00
Denis Benato
c337de5139 chore(Makefile): split up install by package 2025-03-12 16:54:35 +01:00
Luke Jones
d55c2befed chore: update spec file version to 6.1.10 2025-03-04 09:21:53 +13:00
Luke Jones
4cd9918e1a asusd: single line fix for profile switching 2025-03-04 09:21:52 +13:00
Luke Jones
3a900f23fe ROGCC: better handling of fan curve profile 2025-03-03 20:20:09 +13:00
Luke Jones
aee465aced chore: update spec file version to 6.1.9 2025-03-03 19:58:37 +13:00
Luke Jones
192e5ccaa3 ROGCC: better handling of platform profile 2025-03-03 18:22:26 +13:00
Luke Jones
f164583792 ROGCC: fix fancurve quiet fans incorrect enablement 2025-03-02 20:50:57 +13:00
Luke Jones
b4d657b866 ROGCC: fix PPT sliders 2025-03-02 20:39:16 +13:00
Luke Jones
f7bf7aeef9 ROGCC: fix displaying ppt-toggle switch 2025-03-02 16:24:17 +13:00
Luke Jones
2cd4c4850f Extra debug output in ROGCC 2025-03-02 15:15:06 +13:00
Luke Jones
805ccfe451 Merge pull request #51 from sergik776/main
Added translation files for rog-control-center (RU)
2025-03-01 19:47:04 +13:00
Luke Jones
5655f63dff Cleanup 2025-03-01 16:27:50 +13:00
sergik776
a2795e4d78 Full translation into Russian 2025-02-26 10:51:33 +02:00
sergik776
0684c16bf5 Full translation into Russian 2025-02-26 10:24:17 +02:00
Luke Jones
a08ca3af98 chore: update spec file version to 6.1.8 2025-02-22 12:45:20 +13:00
Luke Jones
efa379e778 Add opensuse CI 2025-02-21 17:12:47 +13:00
Luke Jones
5cbf0816fe chore: update translations 2025-02-18 22:12:56 +13:00
Luke Jones
2951d3926c Update git hooks 2025-02-18 22:12:54 +13:00
Luke Jones
eb19d59d52 Update distro packaging 2025-02-18 22:12:53 +13:00
guylamar2006
3e4d594b05 Fix thread 'main' panicked at asusctl/src/main.rs:85:14: 2025-02-17 21:13:42 +00:00
Luke Jones
ae8ce83583 Fix slash enable 2025-02-17 11:38:29 +13:00
Luke Jones
5c3348a9f5 Add small env fixes for Ally
Signed-off-by: Luke Jones <luke@ljones.dev>
2025-02-16 23:18:58 +13:00
Luke Jones
f299ffeb6e Disable skia again, new release 2025-02-16 21:46:04 +13:00
Luke Jones
21c468cf02 Update supergfx
Signed-off-by: Luke Jones <luke@ljones.dev>
2025-02-16 11:50:56 +13:00
Luke Jones
7f12f62ad5 Temp 2025-02-16 09:38:33 +13:00
Luke Jones
5fb0e26331 Fix git losing the last set of fixes. Prep new release
Signed-off-by: Luke Jones <luke@ljones.dev>
2025-02-14 22:23:11 +13:00
Luke Jones
4dd29952c8 Fix the handling of of the kernel change from "quiet" to "low-power"
A coming kernel change will convert "quiet" to "low-power" due to how
platform_profile can now have multiple registered handlers.
(kernel 6.14 est)

Fixes #609
2025-02-14 20:02:08 +13:00
Luke Jones
2c006699f2 Reformat with trailing comma 2025-02-14 20:00:11 +13:00
Luke Jones
0bdf0474c9 Small error message fix 2025-02-14 20:00:11 +13:00
Kamo Kuma
66196ceb17 systemctl is-enabled can return 'linked' even if service is enabled. 2025-02-10 21:00:21 -05:00
Luke Jones
b2726f3a67 Fix: charge control 2025-02-10 22:18:34 +13:00
Luke Jones
663f87d5e2 Checkpoint 2025-02-10 13:56:26 +13:00
Luke Jones
377bb4d6ad Merge branch 'private/Rohitrajak1807/G513RW-ledfix' into 'main'
Add ROG G513RW led cfgs

See merge request asus-linux/asusctl!217
2025-02-10 00:55:33 +00:00
Rohit Rajak
98e60db2da Add ROG G513RW led cfgs 2025-02-10 00:55:33 +00:00
sergik776
a206591e97 Added translation files for rog-control-center (RU) 2025-01-28 20:09:20 +02:00
Rəşad Qasımlı
c5c5a9ac67 Update rog-control-center.mo 2024-07-20 14:35:45 +04:00
Rəşad Qasımlı
b84bc61f3d Update the information about translator 2024-07-20 00:59:47 +04:00
Rəşad Qasımlı
b4e38e0814 Add Azerbaijani language 2024-07-19 23:47:41 +04:00
156 changed files with 9173 additions and 3930 deletions

53
.cargo-husky/hooks/post-commit Executable file
View File

@@ -0,0 +1,53 @@
#!/bin/sh
set -e
ROOT_DIR=$(git rev-parse --show-toplevel)
AURA_DATA="${ROOT_DIR}/rog-aura/data/aura_support.ron"
SPEC_FILE="${ROOT_DIR}/distro-packaging/asusctl.spec"
TRANSLATION="${ROOT_DIR}/rog-control-center/translations/en/rog-control-center.po"
VERSION=$(grep -Pm1 'version = "(\d+.\d+.\d+.*)"' "${ROOT_DIR}/Cargo.toml" | cut -d'"' -f2)
if [ -z "$VERSION" ]; then
echo "Error: Could not extract version from Cargo.toml"
exit 1
fi
if [ ! -f "$SPEC_FILE" ]; then
echo "Error: Spec file not found at ${SPEC_FILE}"
exit 1
fi
# Update spec file
sed -i "s/^%define version.*/%define version ${VERSION}/" "$SPEC_FILE"
if git diff --quiet "$SPEC_FILE"; then
echo "No changes to spec file"
else
git add "$SPEC_FILE"
git commit --no-verify -m "chore: update spec file version to ${VERSION}"
echo "Updated spec file version to ${VERSION}"
fi
# Update translations only if UI files changed
if git diff-tree -r HEAD@{1} HEAD --name-only | grep -q "^rog-control-center/ui/"; then
echo 'find -name \*.slint | xargs slint-tr-extractor -o ${TRANSLATION}'
find -name \*.slint | xargs slint-tr-extractor -o $TRANSLATION
if git diff --quiet "$TRANSLATION"; then
echo "No changes to translation file"
else
git add "$TRANSLATION"
git commit --no-verify -m "chore: update translations"
echo "Updated ${TRANSLATION}"
fi
else
echo "No changes in rog-control-center/ui/, skipping translation update"
fi
# Update aura data
cargo test --package rog_aura --lib -- aura_detection::tests::check_data_file_parse --exact
cargo test --package rog_aura --lib -- aura_detection::tests::find_data_file_groups --exact
if git diff --quiet "$AURA_DATA"; then
echo "No changes to aura data file"
else
git add "$AURA_DATA"
git commit --no-verify -m "chore: update aura data"
echo "Updated $AURA_DATA"
fi

View File

@@ -1,18 +1,6 @@
#!/bin/sh #!/bin/sh
set -e set -e
echo 'find -name \*.slint | xargs slint-tr-extractor -o rog-control-center/translations/en/rog-control-center.po' echo '+cargo fmt --all -- --check'
find -name \*.slint | xargs slint-tr-extractor -o rog-control-center/translations/en/rog-control-center.po cargo fmt --all -- --check
git add -u
echo '+cargo +nightly fmt --all -- --check'
cargo +nightly fmt --all -- --check
echo '+cargo clippy --all -- -D warnings'
cargo clippy --all -- -D warnings
echo '+cargo test --all'
cargo test --all -- --test-threads=1
echo '+cargo cranky'
cargo cranky

View File

@@ -2,8 +2,8 @@
set -e set -e
echo '+cargo +nightly fmt --all -- --check' echo '+cargo fmt --all -- --check'
cargo +nightly fmt --all -- --check cargo fmt --all -- --check
echo '+cargo clippy --all -- -D warnings' echo '+cargo clippy --all -- -D warnings'
cargo clippy --all -- -D warnings cargo clippy --all -- -D warnings
echo '+cargo cranky' echo '+cargo cranky'

1
.gitignore vendored
View File

@@ -9,6 +9,7 @@ vendor_*
.vscode .vscode
.~lock.* .~lock.*
*.ods# *.ods#
*.patch
# gnome extension # gnome extension
node-modules node-modules

View File

@@ -31,8 +31,7 @@ format:
- tags - tags
<<: *rust_cache <<: *rust_cache
script: script:
- echo "nightly" > rust-toolchain - rustup component add rustfmt || true
- rustup component add rustfmt
- cargo fmt --check - cargo fmt --check
check: check:
@@ -40,8 +39,8 @@ check:
- tags - tags
<<: *rust_cache <<: *rust_cache
script: script:
- rustup component add clippy - rustup component add clippy || true
- cargo check - cargo check --locked --workspace
# deny currently catches too much # deny currently catches too much
#- cargo install cargo-deny && cargo deny #- cargo install cargo-deny && cargo deny
- cargo install cargo-cranky && cargo cranky - cargo install cargo-cranky && cargo cranky
@@ -52,7 +51,7 @@ test:
<<: *rust_cache <<: *rust_cache
script: script:
- mkdir -p .git/hooks > /dev/null - mkdir -p .git/hooks > /dev/null
- cargo test --all -- --test-threads=1 - cargo test --locked --all
release: release:
only: only:
@@ -60,7 +59,7 @@ release:
<<: *rust_cache <<: *rust_cache
script: script:
- cargo install cargo-vendor-filterer - cargo install cargo-vendor-filterer
- make && make vendor - make FROZEN=1 && make vendor
artifacts: artifacts:
paths: paths:
- vendor_asusctl*.tar.xz - vendor_asusctl*.tar.xz
@@ -72,7 +71,7 @@ pages:
- tags - tags
<<: *rust_cache <<: *rust_cache
script: script:
- cargo doc --document-private-items --no-deps --workspace - cargo doc --locked --document-private-items --no-deps --workspace
- rm -rf public - rm -rf public
- mkdir public - mkdir public
- cp -R target/doc/* public - cp -R target/doc/* public

View File

@@ -2,8 +2,110 @@
## [Unreleased] ## [Unreleased]
### Changed
- Make the boot process more reliable
- tie nv_ properties to power profiles
- Better support nv_tgp
## [v6.1.17]
### Changed
- Fix Makefile
- Share a single HID device
## [v6.1.16]
### Changed
- Expose more properties via rog-control-center
- Add support for a few more models
## [v6.1.15]
### Changed
- Reflect the current asus-armoury status on AC plug connection status change
## [v6.1.14]
### Changed
- Fix formatting
- Attempt to fix tests
## [v6.1.13]
### Changed
- Fix a problem in reloading the service (@evertvorster)
- Add Azerbaijani language (@rashadgasimli)
- Add Ubuntu installation instructions
## [v6.1.12]
### Changed
- Fix an unbounded event loop caused by other processes causing a "modify" event on the screen backlight brightness.
## [v6.1.11]
### Changed
- Fix anime flickering issue when using custom anims (@I-Al-Istannen)
- Include pt_BR translations file (@PabloKiryu)
## Added
- Support for the screenpad brightness on some Laptops. This includes syncing to the primary screen brightness, and a gamma adjustment to set brightness scaling.
- Add asusctl CLI options
- Add UI options
- Add a fake gamma correction (`asusctl backlight --sync-screenpad-brightness`, 1.5 for example sets screenpad low brightness lower than primary, and scales upwards)
### Changed
- asusd: single line fix for profile switching
## [v6.1.9]
### Changed
- ROGCC: better handling of platform profiles
## [v6.1.8]
### Changed
- Testing CI for opensuse RPM build
- ROGCC: Fixes to showing the PPT enablement toggle
- ROGCC: Fixes to how PPT and NV sliders work and enable/disable
- RGOCC: Fix quiet fan-curves availability
## [v6.1.7]
### Changed
- Fix Slash display enable
## [v6.1.6]
### Changed
- Disable skia bindings for UI again. It causes failures in build pipelines and requires extra dependencies.
## [v6.1.5]
### Changed
- Update dependencies
- Fix fan-curve proxy type signatures
## [v6.1.4]
### Changed
- Fix git doing me a dirty
## [v6.1.3]
### Changed
- Many small bugfixes such as for platform profile switching
## [v6.1.2]
### Changed
- Try a slightly different tact to fix charge control slider
## [v6.1.1]
### Changed ### Changed
- Fix aura data matching - Fix aura data matching
- Fix charge control slider
## [v6.1.0] ## [v6.1.0]

3493
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
[workspace.package] [workspace.package]
version = "6.1.0" version = "6.1.17"
rust-version = "1.82" rust-version = "1.82"
license = "MPL-2.0" license = "MPL-2.0"
readme = "README.md" readme = "README.md"
@@ -43,8 +43,9 @@ dirs = "^4.0"
smol = "^2.0" smol = "^2.0"
mio = "0.8.11" mio = "0.8.11"
zbus = "5.1" futures-util = "0.3.31"
logind-zbus = { version = "5.0.0" } #, default-features = false, features = ["non_blocking"] } zbus = "5.5.0"
logind-zbus = { version = "5.2.0" } #, default-features = false, features = ["non_blocking"] }
serde = { version = "^1.0", features = ["serde_derive"] } serde = { version = "^1.0", features = ["serde_derive"] }
ron = "*" ron = "*"
@@ -65,7 +66,7 @@ gif = "^0.12.0"
versions = "6.2" versions = "6.2"
notify-rust = { version = "4.11.0", features = ["z", "async"] } notify-rust = { version = "4.11.5", features = ["z", "async"] }
sg = { git = "https://github.com/flukejones/sg-rs.git" } sg = { git = "https://github.com/flukejones/sg-rs.git" }
@@ -76,15 +77,15 @@ lto = "fat"
debug = false debug = false
opt-level = 3 opt-level = 3
panic = "abort" panic = "abort"
codegen-units = 1 # codegen-units = 1
[profile.dev] [profile.dev]
opt-level = 1 opt-level = 1
codegen-units = 16 # codegen-units = 1
[profile.dev.package."*"] [profile.dev.package."*"]
opt-level = 1 opt-level = 1
codegen-units = 16 # codegen-units = 1
[profile.bench] [profile.bench]
debug = false debug = false

View File

@@ -48,7 +48,7 @@ The LED controller (e.g, aura) enables setting many of the factory modes availab
#### Supported laptops #### Supported laptops
There are over 60 supported laptops as of 01-01-2023. Please see [the rog-aura crate readme for further details](/rog-aura/README.md). There are over 80 supported laptops as of 01-01-2023. Please see [the rog-aura crate readme for further details](/rog-aura/README.md).
### Charge control ### Charge control
@@ -420,13 +420,13 @@ To switch to next/previous Aura modes you will need to bind both the aura keys (
**Next** **Next**
``` ```
asusctl led-mode -n asusctl aura -n
``` ```
**Previous** **Previous**
``` ```
asusctl led-mode -p asusctl aura -p
``` ```
To switch Fan/Thermal profiles you need to bind the Fn+F5 key to `asusctl profile -n`. To switch Fan/Thermal profiles you need to bind the Fn+F5 key to `asusctl profile -n`.

View File

@@ -17,6 +17,8 @@ BIN_D := asusd
BIN_U := asusd-user BIN_U := asusd-user
LEDCFG := aura_support.ron LEDCFG := aura_support.ron
DESTDIR_REALPATH = $(shell realpath $(DESTDIR))
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 ?= 0
@@ -35,6 +37,15 @@ ifeq ($(X11),1)
ARGS += --features "rog-control-center/x11" ARGS += --features "rog-control-center/x11"
endif endif
# Always use the versions in Cargo.lock by default
ARGS += --locked
# Allow optionally freezing the build to avoid any network access and enforce Cargo.lock strictly
FROZEN ?= 0
ifeq ($(FROZEN),1)
ARGS += --frozen
endif
VENDORED ?= 0 VENDORED ?= 0
ifeq ($(VENDORED),1) ifeq ($(VENDORED),1)
ARGS += --frozen ARGS += --frozen
@@ -48,24 +59,31 @@ clean:
distclean: distclean:
rm -rf .cargo vendor vendor.tar.xz rm -rf .cargo vendor vendor.tar.xz
install-program: target/$(TARGET)/$(BIN_D): build
$(INSTALL_PROGRAM) "./target/$(TARGET)/$(BIN_ROG)" "$(DESTDIR)$(bindir)/$(BIN_ROG)" target/$(TARGET)/$(BIN_C): build
target/$(TARGET)/$(BIN_U): build
target/$(TARGET)/$(BIN_ROG): build
$(INSTALL_PROGRAM) "./target/$(TARGET)/$(BIN_C)" "$(DESTDIR)$(bindir)/$(BIN_C)" install-asusd: target/$(TARGET)/$(BIN_D)
$(INSTALL_PROGRAM) "./target/$(TARGET)/$(BIN_D)" "$(DESTDIR)$(bindir)/$(BIN_D)" $(INSTALL_PROGRAM) "./target/$(TARGET)/$(BIN_D)" "$(DESTDIR)$(bindir)/$(BIN_D)"
install-asusctl: target/$(TARGET)/$(BIN_C)
$(INSTALL_PROGRAM) "./target/$(TARGET)/$(BIN_C)" "$(DESTDIR)$(bindir)/$(BIN_C)"
install-asusd_user: target/$(TARGET)/$(BIN_U)
$(INSTALL_PROGRAM) "./target/$(TARGET)/$(BIN_U)" "$(DESTDIR)$(bindir)/$(BIN_U)" $(INSTALL_PROGRAM) "./target/$(TARGET)/$(BIN_U)" "$(DESTDIR)$(bindir)/$(BIN_U)"
install-data: install-rog_gui: target/$(TARGET)/$(BIN_ROG)
$(INSTALL_PROGRAM) "./target/$(TARGET)/$(BIN_ROG)" "$(DESTDIR)$(bindir)/$(BIN_ROG)"
.PHONY: install-asusd install-asusctl install-asusd_user install-rog_gui
install-program: install-asusd install-asusctl install-asusd_user install-rog_gui
install-data-rog_gui:
$(INSTALL_DATA) "./rog-control-center/data/$(BIN_ROG).desktop" "$(DESTDIR)$(datarootdir)/applications/$(BIN_ROG).desktop" $(INSTALL_DATA) "./rog-control-center/data/$(BIN_ROG).desktop" "$(DESTDIR)$(datarootdir)/applications/$(BIN_ROG).desktop"
$(INSTALL_DATA) "./rog-control-center/data/$(BIN_ROG).png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/$(BIN_ROG).png" $(INSTALL_DATA) "./rog-control-center/data/$(BIN_ROG).png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/$(BIN_ROG).png"
cd rog-aura/data/layouts && find . -type f -name "*.ron" -exec $(INSTALL_DATA) "{}" "$(DESTDIR)$(datarootdir)/rog-gui/layouts/{}" \; cd rog-aura/data/layouts && find . -type f -name "*.ron" -exec $(INSTALL_DATA) "{}" "$(DESTDIR_REALPATH)$(datarootdir)/rog-gui/layouts/{}" \;
$(INSTALL_DATA) "./data/$(BIN_D).rules" "$(DESTDIR)$(libdir)/udev/rules.d/99-$(BIN_D).rules"
$(INSTALL_DATA) "./rog-aura/data/$(LEDCFG)" "$(DESTDIR)$(datarootdir)/asusd/$(LEDCFG)"
$(INSTALL_DATA) "./data/$(BIN_D).conf" "$(DESTDIR)$(datarootdir)/dbus-1/system.d/$(BIN_D).conf"
$(INSTALL_DATA) "./data/$(BIN_D).service" "$(DESTDIR)$(libdir)/systemd/system/$(BIN_D).service"
$(INSTALL_DATA) "./data/$(BIN_U).service" "$(DESTDIR)$(libdir)/systemd/user/$(BIN_U).service"
$(INSTALL_DATA) "./data/icons/asus_notif_yellow.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_yellow.png" $(INSTALL_DATA) "./data/icons/asus_notif_yellow.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_yellow.png"
$(INSTALL_DATA) "./data/icons/asus_notif_green.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_green.png" $(INSTALL_DATA) "./data/icons/asus_notif_green.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_green.png"
@@ -81,9 +99,24 @@ install-data:
$(INSTALL_DATA) "./data/icons/scalable/gpu-vfio.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-vfio.svg" $(INSTALL_DATA) "./data/icons/scalable/gpu-vfio.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-vfio.svg"
$(INSTALL_DATA) "./data/icons/scalable/notification-reboot.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/notification-reboot.svg" $(INSTALL_DATA) "./data/icons/scalable/notification-reboot.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/notification-reboot.svg"
cd rog-anime/data && find "./anime" -type f -exec $(INSTALL_DATA) "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \; install-data-asusd:
$(INSTALL_DATA) "./data/$(BIN_D).rules" "$(DESTDIR)$(libdir)/udev/rules.d/99-$(BIN_D).rules"
$(INSTALL_DATA) "./rog-aura/data/$(LEDCFG)" "$(DESTDIR)$(datarootdir)/asusd/$(LEDCFG)"
$(INSTALL_DATA) "./data/$(BIN_D).conf" "$(DESTDIR)$(datarootdir)/dbus-1/system.d/$(BIN_D).conf"
$(INSTALL_DATA) "./data/$(BIN_D).service" "$(DESTDIR)$(libdir)/systemd/system/$(BIN_D).service"
cd rog-anime/data && find "./anime" -type f -exec $(INSTALL_DATA) "{}" "$(DESTDIR_REALPATH)$(datarootdir)/asusd/{}" \;
install-data-asusd_user:
$(INSTALL_DATA) "./data/$(BIN_U).service" "$(DESTDIR)$(libdir)/systemd/user/$(BIN_U).service"
.PHONY: install-data-asusd install-data-asusd_user
install-data: install-data-asusd install-data-asusd_user install-data-rog_gui
install: install-program install-data install: install-program install-data
$(INSTALL_DATA) "./LICENSE" "$(DESTDIR)$(datarootdir)/asusctl/LICENSE"
uninstall: uninstall:
rm -f "$(DESTDIR)$(bindir)/$(BIN_ROG)" rm -f "$(DESTDIR)$(bindir)/$(BIN_ROG)"

View File

@@ -46,13 +46,13 @@ See the [rog-aura readme](./rog-aura/README.md) for more details.
Most ASUS gaming laptops that have a USB keyboard. If `lsusb` shows something similar Most ASUS gaming laptops that have a USB keyboard. If `lsusb` shows something similar
to this: to this:
``` ```plain
Bus 001 Device 002: ID 0b05:1866 ASUSTek Computer, Inc. N-KEY Device Bus 001 Device 002: ID 0b05:1866 ASUSTek Computer, Inc. N-KEY Device
``` ```
or or
``` ```plain
Bus 003 Device 002: ID 0b05:19b6 ASUSTek Computer, Inc. [unknown] Bus 003 Device 002: ID 0b05:19b6 ASUSTek Computer, Inc. [unknown]
``` ```
@@ -74,43 +74,56 @@ The list is a bit outdated as many features have been enabled in the Linux kerne
- [x] Toggle bios setting for boot/POST sound - [x] Toggle bios setting for boot/POST sound
- [x] Toggle GPU MUX (g-sync, or called MUX on 2022+ laptops) - [x] Toggle GPU MUX (g-sync, or called MUX on 2022+ laptops)
# GUI ## GUI
A gui is now in the repo - ROG Control Center. At this time it is still a WIP, but it has almost all features in place already. A gui is now in the repo - ROG Control Center. At this time it is still a WIP, but it has almost all features in place already.
**NOTE**: Xorg is not supported. **NOTE**: Xorg is not supported.
# BUILDING ## BUILDING
Rust and cargo are required, they can be installed from [rustup.rs](https://rustup.rs/) or from the distro repos if newer than 1.75. Rust and cargo are required, they can be installed from [rustup.rs](https://rustup.rs/).
Distro packaging should work with the stable toolchain. If your distro does not provide a recent Rust toolchain, install rustup and use the stable toolchain.
**fedora:** **fedora:**
dnf install cmake clang-devel libxkbcommon-devel systemd-devel expat-devel pcre2-devel libzstd-devel gtk3-devel ```sh
make dnf install cmake clang-devel libxkbcommon-devel systemd-devel expat-devel pcre2-devel libzstd-devel gtk3-devel
sudo make install make
sudo make install
```
**openSUSE:** **openSUSE:**
Works with KDE Plasma (without GTK packages) Works with KDE Plasma (without GTK packages)
zypper in -t pattern devel_basis ```sh
zypper in rustup make cmake clang-devel libxkbcommon-devel systemd-devel expat-devel pcre2-devel libzstd-devel gtk3-devel zypper in -t pattern devel_basis
make zypper in rustup make cmake clang-devel libxkbcommon-devel systemd-devel expat-devel pcre2-devel libzstd-devel gtk3-devel
sudo make install make
sudo make install
```
**Debian(unsuported):** **Debian(unsuported):**
officially unsuported,but you can still try and test it by yourself(some features may not be available). 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 ```sh
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh sudo apt install libclang-dev libudev-dev libfontconfig-dev build-essential cmake libxkbcommon-dev
make curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
sudo make install make
sudo make install
```
**Ubuntu, Popos (unsuported):** **Ubuntu, Popos (unsuported):**
instructions removed as outdated ```sh
sudo apt install make cargo gcc pkg-config openssl libasound2-dev cmake build-essential python3 libfreetype6-dev libexpat1-dev libxcb-composite0-dev libssl-dev libx11-dev libfontconfig1-dev curl libclang-dev libudev-dev checkinstall libseat-dev libinput-dev libxkbcommon-dev libgbm-dev
make
sudo make install
```
## Installing ## Installing
@@ -128,15 +141,15 @@ You may also need to activate the service for debian install. If running Pop!\_O
If you are upgrading from a previous installed version, you will need to restart the service or reboot. If you are upgrading from a previous installed version, you will need to restart the service or reboot.
``` ```sh
$ systemctl daemon-reload && systemctl restart asusd systemctl daemon-reload && systemctl restart asusd
``` ```
## Uninstalling ## Uninstalling
Run `sudo make uninstall` in the source repo, and remove `/etc/asusd/`. Run `sudo make uninstall` in the source repo, and remove `/etc/asusd/`.
# Contributing ## Contributing
See `CONTRIBUTING.md`. Additionally, also do `cargo clean` and `cargo test` on first checkout to ensure the commit hooks are used (via `cargo-husky`). See `CONTRIBUTING.md`. Additionally, also do `cargo clean` and `cargo test` on first checkout to ensure the commit hooks are used (via `cargo-husky`).
@@ -144,17 +157,17 @@ Generation of the bindings with `make bindings` requires `typeshare` to be insta
Dbus introsepction XML requires with `make introspection` requires `anime_sim` to be running before starting `asusd`. Dbus introsepction XML requires with `make introspection` requires `anime_sim` to be running before starting `asusd`.
# OTHER ## OTHER
## AniMe Matrix simulator ### AniMe Matrix simulator
A simulator using SDL2 can be built using `cargo build --package rog_simulators` and run with `./target/debug/anime_sim`. Once started `asusd` will need restarting to pick it up. If running this sim on a laptop _with_ the display, the simulated display will be used instead of the physical display. A simulator using SDL2 can be built using `cargo build --package rog_simulators` and run with `./target/debug/anime_sim`. Once started `asusd` will need restarting to pick it up. If running this sim on a laptop _with_ the display, the simulated display will be used instead of the physical display.
## Supporting more laptops ### Supporting more laptops
Please file a support request. Please file a support request.
# License & Trademarks ## License & Trademarks
Mozilla Public License 2 (MPL-2.0) Mozilla Public License 2 (MPL-2.0)

View File

@@ -27,3 +27,15 @@ zbus.workspace = true
[dev-dependencies] [dev-dependencies]
rog_dbus = { path = "../rog-dbus" } rog_dbus = { path = "../rog-dbus" }
[package.metadata.deb]
license-file = ["../LICENSE", "4"]
extended-description = """\
An utility for Linux to control many aspects of various ASUS laptops
but can also be used with non-asus laptops with reduced features."""
depends = "$auto"
section = "utility"
priority = "optional"
assets = [
["target/release/asusctl", "usr/bin/", "755"],
]

View File

@@ -23,7 +23,7 @@ fn main() -> Result<(), Box<dyn Error>> {
Path::new(&args[1]), Path::new(&args[1]),
None, None,
args[2].parse::<f32>().unwrap(), args[2].parse::<f32>().unwrap(),
AnimeType::GA401 AnimeType::GA401,
)?; )?;
let anime_type = get_anime_type(); let anime_type = get_anime_type();

View File

@@ -21,11 +21,14 @@ fn main() {
let brightness = args[2].parse::<f32>().unwrap(); let brightness = args[2].parse::<f32>().unwrap();
let anime_type = get_anime_type(); let anime_type = get_anime_type();
let mut seq = Sequences::new(anime_type); let mut seq = Sequences::new(anime_type);
seq.insert(0, &ActionLoader::AsusAnimation { seq.insert(
file: path.into(), 0,
time: rog_anime::AnimTime::Infinite, &ActionLoader::AsusAnimation {
brightness file: path.into(),
}) time: rog_anime::AnimTime::Infinite,
brightness,
},
)
.unwrap(); .unwrap();
loop { loop {

View File

@@ -27,10 +27,10 @@ fn main() -> Result<(), Box<dyn Error>> {
args[3].parse::<f32>().unwrap(), args[3].parse::<f32>().unwrap(),
Vec2::new( Vec2::new(
args[4].parse::<f32>().unwrap(), args[4].parse::<f32>().unwrap(),
args[5].parse::<f32>().unwrap() args[5].parse::<f32>().unwrap(),
), ),
args[6].parse::<f32>().unwrap(), args[6].parse::<f32>().unwrap(),
anime_type anime_type,
)?; )?;
proxy.write(<AnimeDataBuffer>::try_from(&matrix)?).unwrap(); proxy.write(<AnimeDataBuffer>::try_from(&matrix)?).unwrap();

View File

@@ -30,10 +30,10 @@ fn main() -> Result<(), Box<dyn Error>> {
args[3].parse::<f32>().unwrap(), args[3].parse::<f32>().unwrap(),
Vec2::new( Vec2::new(
args[4].parse::<f32>().unwrap(), args[4].parse::<f32>().unwrap(),
args[5].parse::<f32>().unwrap() args[5].parse::<f32>().unwrap(),
), ),
args[6].parse::<f32>().unwrap(), args[6].parse::<f32>().unwrap(),
anime_type anime_type,
)?; )?;
loop { loop {

View File

@@ -36,10 +36,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Colour { Colour {
r: 200, r: 200,
g: 110, g: 110,
b: 0 b: 0,
}, },
100, 100,
10 10,
)); ));
seq.push(zone); seq.push(zone);

View File

@@ -40,7 +40,7 @@ pub struct AnimeCommand {
#[options(no_short, meta = "", help = "Off with his head!!!")] #[options(no_short, meta = "", help = "Off with his head!!!")]
pub off_with_his_head: Option<bool>, pub off_with_his_head: Option<bool>,
#[options(command)] #[options(command)]
pub command: Option<AnimeActions> pub command: Option<AnimeActions>,
} }
#[derive(Options)] #[derive(Options)]
@@ -54,7 +54,7 @@ pub enum AnimeActions {
#[options(help = "display an animated diagonal/pixel-perfect GIF")] #[options(help = "display an animated diagonal/pixel-perfect GIF")]
PixelGif(AnimeGifDiagonal), PixelGif(AnimeGifDiagonal),
#[options(help = "change which builtin animations are shown")] #[options(help = "change which builtin animations are shown")]
SetBuiltins(Builtins) SetBuiltins(Builtins),
} }
#[derive(Options)] #[derive(Options)]
@@ -82,7 +82,7 @@ pub struct Builtins {
)] )]
pub shutdown: AnimShutdown, pub shutdown: AnimShutdown,
#[options(meta = "", help = "set/apply the animations <true/false>")] #[options(meta = "", help = "set/apply the animations <true/false>")]
pub set: Option<bool> pub set: Option<bool>,
} }
#[derive(Options)] #[derive(Options)]
@@ -100,7 +100,7 @@ pub struct AnimeImage {
#[options(meta = "", default = "0.0", help = "the angle in radians")] #[options(meta = "", default = "0.0", help = "the angle in radians")]
pub angle: f32, pub angle: f32,
#[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")] #[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")]
pub bright: f32 pub bright: f32,
} }
#[derive(Options)] #[derive(Options)]
@@ -110,7 +110,7 @@ pub struct AnimeImageDiagonal {
#[options(meta = "", help = "full path to the png to display")] #[options(meta = "", help = "full path to the png to display")]
pub path: String, pub path: String,
#[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")] #[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")]
pub bright: f32 pub bright: f32,
} }
#[derive(Options)] #[derive(Options)]
@@ -134,7 +134,7 @@ pub struct AnimeGif {
default = "1", default = "1",
help = "how many loops to play - 0 is infinite" help = "how many loops to play - 0 is infinite"
)] )]
pub loops: u32 pub loops: u32,
} }
#[derive(Options)] #[derive(Options)]
@@ -150,5 +150,5 @@ pub struct AnimeGifDiagonal {
default = "1", default = "1",
help = "how many loops to play - 0 is infinite" help = "how many loops to play - 0 is infinite"
)] )]
pub loops: u32 pub loops: u32,
} }

View File

@@ -17,7 +17,7 @@ pub struct LedPowerCommand1 {
#[options(meta = "", help = "Control boot animations <true/false>")] #[options(meta = "", help = "Control boot animations <true/false>")]
pub boot: Option<bool>, pub boot: Option<bool>,
#[options(meta = "", help = "Control suspend animations <true/false>")] #[options(meta = "", help = "Control suspend animations <true/false>")]
pub sleep: Option<bool> pub sleep: Option<bool>,
} }
#[derive(Options, Debug)] #[derive(Options, Debug)]
@@ -25,7 +25,7 @@ pub struct LedPowerCommand2 {
#[options(help = "print help message")] #[options(help = "print help message")]
pub help: bool, pub help: bool,
#[options(command)] #[options(command)]
pub command: Option<SetAuraZoneEnabled> pub command: Option<SetAuraZoneEnabled>,
} }
#[derive(Options, Debug)] #[derive(Options, Debug)]
@@ -42,7 +42,7 @@ pub enum SetAuraZoneEnabled {
#[options(help = "")] #[options(help = "")]
RearGlow(AuraPowerStates), RearGlow(AuraPowerStates),
#[options(help = "")] #[options(help = "")]
Ally(AuraPowerStates) Ally(AuraPowerStates),
} }
#[derive(Debug, Clone, Options)] #[derive(Debug, Clone, Options)]
@@ -56,12 +56,12 @@ pub struct AuraPowerStates {
#[options(help = "defaults to false if option unused")] #[options(help = "defaults to false if option unused")]
pub sleep: bool, pub sleep: bool,
#[options(help = "defaults to false if option unused")] #[options(help = "defaults to false if option unused")]
pub shutdown: bool pub shutdown: bool,
} }
#[derive(Options)] #[derive(Options)]
pub struct LedBrightness { pub struct LedBrightness {
level: Option<u8> level: Option<u8>,
} }
impl LedBrightness { impl LedBrightness {
pub fn new(level: Option<u8>) -> Self { pub fn new(level: Option<u8>) -> Self {
@@ -96,7 +96,7 @@ impl ToString for LedBrightness {
Some(0x00) => "low", Some(0x00) => "low",
Some(0x01) => "med", Some(0x01) => "med",
Some(0x02) => "high", Some(0x02) => "high",
_ => "unknown" _ => "unknown",
}; };
s.to_owned() s.to_owned()
} }
@@ -113,7 +113,7 @@ pub struct SingleSpeed {
meta = "", meta = "",
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left" help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
)] )]
pub zone: AuraZone pub zone: AuraZone,
} }
#[derive(Debug, Clone, Options, Default)] #[derive(Debug, Clone, Options, Default)]
@@ -129,7 +129,7 @@ pub struct SingleSpeedDirection {
meta = "", meta = "",
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left" help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
)] )]
pub zone: AuraZone pub zone: AuraZone,
} }
#[derive(Debug, Clone, Default, Options)] #[derive(Debug, Clone, Default, Options)]
@@ -143,7 +143,7 @@ pub struct SingleColour {
meta = "", meta = "",
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left" help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
)] )]
pub zone: AuraZone pub zone: AuraZone,
} }
#[derive(Debug, Clone, Default, Options)] #[derive(Debug, Clone, Default, Options)]
@@ -159,7 +159,7 @@ pub struct SingleColourSpeed {
meta = "", meta = "",
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left" help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
)] )]
pub zone: AuraZone pub zone: AuraZone,
} }
#[derive(Debug, Clone, Options, Default)] #[derive(Debug, Clone, Options, Default)]
@@ -177,9 +177,10 @@ pub struct TwoColourSpeed {
meta = "", meta = "",
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left" help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
)] )]
pub zone: AuraZone pub zone: AuraZone,
} }
#[allow(dead_code)]
#[derive(Debug, Clone, Default, Options)] #[derive(Debug, Clone, Default, Options)]
pub struct MultiZone { pub struct MultiZone {
#[options(help = "print help message")] #[options(help = "print help message")]
@@ -191,9 +192,10 @@ pub struct MultiZone {
#[options(short = "c", meta = "", help = "set the RGB value e.g, ff00ff")] #[options(short = "c", meta = "", help = "set the RGB value e.g, ff00ff")]
pub colour3: Colour, pub colour3: Colour,
#[options(short = "d", meta = "", help = "set the RGB value e.g, ff00ff")] #[options(short = "d", meta = "", help = "set the RGB value e.g, ff00ff")]
pub colour4: Colour pub colour4: Colour,
} }
#[allow(dead_code)]
#[derive(Debug, Clone, Default, Options)] #[derive(Debug, Clone, Default, Options)]
pub struct MultiColourSpeed { pub struct MultiColourSpeed {
#[options(help = "print help message")] #[options(help = "print help message")]
@@ -207,7 +209,7 @@ pub struct MultiColourSpeed {
#[options(short = "d", meta = "", help = "set the RGB value e.g, ff00ff")] #[options(short = "d", meta = "", help = "set the RGB value e.g, ff00ff")]
pub colour4: Colour, pub colour4: Colour,
#[options(no_long, meta = "", help = "set the speed: low, med, high")] #[options(no_long, meta = "", help = "set the speed: low, med, high")]
pub speed: Speed pub speed: Speed,
} }
/// Byte value for setting the built-in mode. /// Byte value for setting the built-in mode.
@@ -239,7 +241,7 @@ pub enum SetAuraBuiltin {
#[options(help = "set a vertical line zooming from left")] #[options(help = "set a vertical line zooming from left")]
Comet(SingleColour), // 11 Comet(SingleColour), // 11
#[options(help = "set a wide vertical line zooming from left")] #[options(help = "set a wide vertical line zooming from left")]
Flash(SingleColour) // 12 Flash(SingleColour), // 12
} }
impl Default for SetAuraBuiltin { impl Default for SetAuraBuiltin {

View File

@@ -26,7 +26,7 @@ pub struct CliStart {
#[options(help = "Toggle one-shot battery charge to 100%")] #[options(help = "Toggle one-shot battery charge to 100%")]
pub one_shot_chg: bool, pub one_shot_chg: bool,
#[options(command)] #[options(command)]
pub command: Option<CliCommand> pub command: Option<CliCommand>,
} }
#[derive(Options)] #[derive(Options)]
@@ -53,7 +53,9 @@ pub enum CliCommand {
help = "Change platform settings. This is a new interface exposed by the asus-armoury \ help = "Change platform settings. This is a new interface exposed by the asus-armoury \
driver, some of the settings will be the same as the older platform interface" driver, some of the settings will be the same as the older platform interface"
)] )]
Armoury(ArmouryCommand) Armoury(ArmouryCommand),
#[options(name = "backlight", help = "Set screen backlight levels")]
Backlight(BacklightCommand),
} }
#[derive(Debug, Clone, Options)] #[derive(Debug, Clone, Options)]
@@ -71,7 +73,17 @@ pub struct ProfileCommand {
pub profile_get: bool, pub profile_get: bool,
#[options(meta = "", help = "set the active profile")] #[options(meta = "", help = "set the active profile")]
pub profile_set: Option<PlatformProfile> pub profile_set: Option<PlatformProfile>,
#[options(short = "a", meta = "", help = "set the profile to use on AC power")]
pub profile_set_ac: Option<PlatformProfile>,
#[options(
short = "b",
meta = "",
help = "set the profile to use on battery power"
)]
pub profile_set_bat: Option<PlatformProfile>,
} }
#[derive(Options)] #[derive(Options)]
@@ -83,13 +95,13 @@ pub struct LedModeCommand {
#[options(help = "switch to previous aura mode")] #[options(help = "switch to previous aura mode")]
pub prev_mode: bool, pub prev_mode: bool,
#[options(command)] #[options(command)]
pub command: Option<SetAuraBuiltin> pub command: Option<SetAuraBuiltin>,
} }
#[derive(Options)] #[derive(Options)]
pub struct GraphicsCommand { pub struct GraphicsCommand {
#[options(help = "print help message")] #[options(help = "print help message")]
pub help: bool pub help: bool,
} }
#[derive(Options, Debug)] #[derive(Options, Debug)]
@@ -100,5 +112,23 @@ pub struct ArmouryCommand {
free, free,
help = "append each value name followed by the value to set. `-1` sets to default" help = "append each value name followed by the value to set. `-1` sets to default"
)] )]
pub free: Vec<String> pub free: Vec<String>,
}
#[derive(Options)]
pub struct BacklightCommand {
#[options(help = "print help message")]
pub help: bool,
#[options(meta = "", help = "Set screen brightness <0-100>")]
pub screenpad_brightness: Option<i32>,
#[options(
meta = "",
help = "Set screenpad gamma brightness 0.5 - 2.2, 1.0 == linear"
)]
pub screenpad_gamma: Option<f32>,
#[options(
meta = "",
help = "Set screenpad brightness to sync with primary display"
)]
pub sync_screenpad_brightness: Option<bool>,
} }

View File

@@ -45,5 +45,5 @@ pub struct FanCurveCommand {
help = "data format = 30c:1%,49c:2%,59c:3%,69c:4%,79c:31%,89c:49%,99c:56%,109c:58%. \ help = "data format = 30c:1%,49c:2%,59c:3%,69c:4%,79c:31%,89c:49%,99c:56%,109c:58%. \
`--mod-profile` required. If '%' is omitted the fan range is 0-255" `--mod-profile` required. If '%' is omitted the fan range is 0-255"
)] )]
pub data: Option<CurveData> pub data: Option<CurveData>,
} }

View File

@@ -19,6 +19,7 @@ use rog_dbus::list_iface_blocking;
use rog_dbus::scsi_aura::ScsiAuraProxyBlocking; 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_backlight::BacklightProxyBlocking;
use rog_dbus::zbus_fan_curves::FanCurvesProxyBlocking; use rog_dbus::zbus_fan_curves::FanCurvesProxyBlocking;
use rog_dbus::zbus_platform::PlatformProxyBlocking; use rog_dbus::zbus_platform::PlatformProxyBlocking;
use rog_dbus::zbus_slash::SlashProxyBlocking; use rog_dbus::zbus_slash::SlashProxyBlocking;
@@ -74,22 +75,36 @@ fn main() {
println!("\nError: {e}\n"); println!("\nError: {e}\n");
print_info(); print_info();
}) { }) {
let asusd_version = platform_proxy let asusd_version = match platform_proxy.version() {
.version() Ok(version) => version,
.map_err(|e| { Err(e) => {
error!( error!(
"Could not get asusd version: {e:?}\nIs asusd.service running? {}", "Could not get asusd version: {e:?}\nIs asusd.service running? {}",
check_service("asusd") check_service("asusd")
); );
}) return;
.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; return;
} }
let supported_properties = platform_proxy.supported_properties().unwrap(); let supported_properties = match platform_proxy.supported_properties() {
let supported_interfaces = list_iface_blocking().unwrap(); Ok(props) => props,
Err(e) => {
error!("Could not get supported properties: {e:?}");
return;
}
};
let supported_interfaces = match list_iface_blocking() {
Ok(ifaces) => ifaces,
Err(e) => {
error!("Could not get supported interfaces: {e:?}");
return;
}
};
if parsed.version { if parsed.version {
println!("asusctl v{}", env!("CARGO_PKG_VERSION")); println!("asusctl v{}", env!("CARGO_PKG_VERSION"));
@@ -106,7 +121,7 @@ fn main() {
fn print_error_help( fn print_error_help(
err: &dyn std::error::Error, err: &dyn std::error::Error,
supported_interfaces: &[String], supported_interfaces: &[String],
supported_properties: &[Properties] supported_properties: &[Properties],
) { ) {
check_service("asusd"); check_service("asusd");
println!("\nError: {}\n", err); println!("\nError: {}\n", err);
@@ -147,7 +162,7 @@ fn check_service(name: &str) -> bool {
fn find_iface<T>(iface_name: &str) -> Result<Vec<T>, Box<dyn std::error::Error>> fn find_iface<T>(iface_name: &str) -> Result<Vec<T>, Box<dyn std::error::Error>>
where where
T: ProxyImpl<'static> + From<zbus::Proxy<'static>> T: ProxyImpl<'static> + From<zbus::Proxy<'static>>,
{ {
let conn = zbus::blocking::Connection::system().unwrap(); let conn = zbus::blocking::Connection::system().unwrap();
let f = zbus::blocking::fdo::ObjectManagerProxy::new(&conn, "xyz.ljones.Asusd", "/").unwrap(); let f = zbus::blocking::fdo::ObjectManagerProxy::new(&conn, "xyz.ljones.Asusd", "/").unwrap();
@@ -174,7 +189,7 @@ where
T::builder(&conn) T::builder(&conn)
.path(path.clone())? .path(path.clone())?
.destination("xyz.ljones.Asusd")? .destination("xyz.ljones.Asusd")?
.build()? .build()?,
); );
} }
return Ok(ctrl); return Ok(ctrl);
@@ -187,7 +202,7 @@ fn do_parsed(
parsed: &CliStart, parsed: &CliStart,
supported_interfaces: &[String], supported_interfaces: &[String],
supported_properties: &[Properties], supported_properties: &[Properties],
conn: Connection conn: Connection,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
match &parsed.command { match &parsed.command {
Some(CliCommand::Aura(mode)) => handle_led_mode(mode)?, Some(CliCommand::Aura(mode)) => handle_led_mode(mode)?,
@@ -204,6 +219,7 @@ fn do_parsed(
Some(CliCommand::Slash(cmd)) => handle_slash(cmd)?, Some(CliCommand::Slash(cmd)) => handle_slash(cmd)?,
Some(CliCommand::Scsi(cmd)) => handle_scsi(cmd)?, Some(CliCommand::Scsi(cmd)) => handle_scsi(cmd)?,
Some(CliCommand::Armoury(cmd)) => handle_armoury_command(cmd)?, Some(CliCommand::Armoury(cmd)) => handle_armoury_command(cmd)?,
Some(CliCommand::Backlight(cmd)) => handle_backlight(cmd)?,
None => { None => {
if (!parsed.show_supported if (!parsed.show_supported
&& parsed.kbd_bright.is_none() && parsed.kbd_bright.is_none()
@@ -259,12 +275,18 @@ fn do_parsed(
return false; return false;
} }
if command.trim().starts_with("platform") if command.trim().starts_with("armoury")
&& !supported_interfaces.contains(&"xyz.ljones.AsusArmoury".to_string()) && !supported_interfaces.contains(&"xyz.ljones.AsusArmoury".to_string())
{ {
return false; return false;
} }
if command.trim().starts_with("backlight")
&& !supported_interfaces.contains(&"xyz.ljones.Backlight".to_string())
{
return false;
}
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("aura-power-old") && command.trim().starts_with("aura-power-old")
@@ -295,7 +317,7 @@ fn do_parsed(
let level = aura.brightness()?; let level = aura.brightness()?;
println!("Current keyboard led brightness: {level:?}"); println!("Current keyboard led brightness: {level:?}");
} }
Some(level) => aura.set_brightness(rog_aura::LedBrightness::from(level))? Some(level) => aura.set_brightness(rog_aura::LedBrightness::from(level))?,
} }
} }
} else { } else {
@@ -367,6 +389,46 @@ fn do_gfx() {
println!("This command will be removed in future"); println!("This command will be removed in future");
} }
fn handle_backlight(cmd: &BacklightCommand) -> Result<(), Box<dyn std::error::Error>> {
if (cmd.screenpad_brightness.is_none()
&& cmd.screenpad_gamma.is_none()
&& cmd.sync_screenpad_brightness.is_none())
|| cmd.help
{
println!("Missing arg or command\n\n{}", cmd.self_usage());
let backlights = find_iface::<BacklightProxyBlocking>("xyz.ljones.Backlight")?;
for backlight in backlights {
println!("Current screenpad settings:");
println!(" Brightness: {}", backlight.screenpad_brightness()?);
println!(" Gamma: {}", backlight.screenpad_gamma()?);
println!(
" Sync with primary: {}",
backlight.screenpad_sync_with_primary()?
);
}
return Ok(());
}
let backlights = find_iface::<BacklightProxyBlocking>("xyz.ljones.Backlight")?;
for backlight in backlights {
if let Some(brightness) = cmd.screenpad_brightness {
backlight.set_screenpad_brightness(brightness)?;
}
if let Some(gamma) = cmd.screenpad_gamma {
backlight.set_screenpad_gamma(gamma.to_string().as_str())?;
}
if let Some(sync) = cmd.sync_screenpad_brightness {
backlight.set_screenpad_sync_with_primary(sync)?;
}
}
Ok(())
}
fn handle_anime(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()
@@ -444,7 +506,7 @@ fn handle_anime(cmd: &AnimeCommand) -> Result<(), Box<dyn std::error::Error>> {
image.angle, image.angle,
Vec2::new(image.x_pos, image.y_pos), Vec2::new(image.x_pos, image.y_pos),
image.bright, image.bright,
anime_type anime_type,
)?; )?;
proxy.write(<AnimeDataBuffer>::try_from(&matrix)?)?; proxy.write(<AnimeDataBuffer>::try_from(&matrix)?)?;
@@ -463,7 +525,7 @@ fn handle_anime(cmd: &AnimeCommand) -> Result<(), Box<dyn std::error::Error>> {
Path::new(&image.path), Path::new(&image.path),
None, None,
image.bright, image.bright,
anime_type anime_type,
)?; )?;
proxy.write(matrix.into_data_buffer(anime_type)?)?; proxy.write(matrix.into_data_buffer(anime_type)?)?;
@@ -485,7 +547,7 @@ fn handle_anime(cmd: &AnimeCommand) -> Result<(), Box<dyn std::error::Error>> {
Vec2::new(gif.x_pos, gif.y_pos), Vec2::new(gif.x_pos, gif.y_pos),
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;
@@ -516,7 +578,7 @@ fn handle_anime(cmd: &AnimeCommand) -> Result<(), Box<dyn std::error::Error>> {
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;
@@ -549,7 +611,7 @@ fn handle_anime(cmd: &AnimeCommand) -> Result<(), Box<dyn std::error::Error>> {
boot: builtins.boot, boot: builtins.boot,
awake: builtins.awake, awake: builtins.awake,
sleep: builtins.sleep, sleep: builtins.sleep,
shutdown: builtins.shutdown shutdown: builtins.shutdown,
})?; })?;
} }
} }
@@ -575,6 +637,7 @@ fn handle_slash(cmd: &SlashCommand) -> Result<(), Box<dyn std::error::Error>> {
&& cmd.show_on_sleep.is_none() && cmd.show_on_sleep.is_none()
&& cmd.show_on_battery.is_none() && cmd.show_on_battery.is_none()
&& cmd.show_battery_warning.is_none() && cmd.show_battery_warning.is_none()
// && cmd.show_on_lid_closed.is_none()
&& cmd.mode.is_none() && cmd.mode.is_none()
&& !cmd.list && !cmd.list
&& !cmd.enable && !cmd.enable
@@ -620,6 +683,9 @@ fn handle_slash(cmd: &SlashCommand) -> Result<(), Box<dyn std::error::Error>> {
if let Some(show) = cmd.show_battery_warning { if let Some(show) = cmd.show_battery_warning {
proxy.set_show_battery_warning(show)?; proxy.set_show_battery_warning(show)?;
} }
// if let Some(show) = cmd.show_on_lid_closed {
// proxy.set_show_on_lid_closed(show)?;
// }
} }
if cmd.list { if cmd.list {
let res = SlashMode::list(); let res = SlashMode::list();
@@ -812,7 +878,7 @@ fn handle_led_power1(power: &LedPowerCommand1) -> Result<(), Box<dyn std::error:
fn handle_led_power_1_do_1866( fn handle_led_power_1_do_1866(
aura: &AuraProxyBlocking, aura: &AuraProxyBlocking,
power: &LedPowerCommand1 power: &LedPowerCommand1,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
let mut states = Vec::new(); let mut states = Vec::new();
if power.keyboard { if power.keyboard {
@@ -821,7 +887,7 @@ fn handle_led_power_1_do_1866(
boot: power.boot.unwrap_or_default(), boot: power.boot.unwrap_or_default(),
awake: power.awake.unwrap_or_default(), awake: power.awake.unwrap_or_default(),
sleep: power.sleep.unwrap_or_default(), sleep: power.sleep.unwrap_or_default(),
shutdown: false shutdown: false,
}); });
} }
if power.lightbar { if power.lightbar {
@@ -830,7 +896,7 @@ fn handle_led_power_1_do_1866(
boot: power.boot.unwrap_or_default(), boot: power.boot.unwrap_or_default(),
awake: power.awake.unwrap_or_default(), awake: power.awake.unwrap_or_default(),
sleep: power.sleep.unwrap_or_default(), sleep: power.sleep.unwrap_or_default(),
shutdown: false shutdown: false,
}); });
} }
@@ -892,7 +958,7 @@ fn handle_led_power2(power: &LedPowerCommand2) -> Result<(), Box<dyn std::error:
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) aura_cli::SetAuraZoneEnabled::Ally(r) => set(PowerZones::Ally, r),
} }
} }
@@ -906,14 +972,20 @@ fn handle_led_power2(power: &LedPowerCommand2) -> Result<(), Box<dyn std::error:
fn handle_throttle_profile( fn handle_throttle_profile(
conn: &Connection, conn: &Connection,
supported: &[Properties], supported: &[Properties],
cmd: &ProfileCommand cmd: &ProfileCommand,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
if !supported.contains(&Properties::ThrottlePolicy) { if !supported.contains(&Properties::ThrottlePolicy) {
println!("Profiles not supported by either this kernel or by the laptop."); println!("Profiles not supported by either this kernel or by the laptop.");
return Err(ProfileError::NotSupported.into()); return Err(ProfileError::NotSupported.into());
} }
if !cmd.next && !cmd.list && cmd.profile_set.is_none() && !cmd.profile_get { if !cmd.next
&& !cmd.list
&& cmd.profile_set.is_none()
&& !cmd.profile_get
&& cmd.profile_set_ac.is_none()
&& cmd.profile_set_bat.is_none()
{
if !cmd.help { if !cmd.help {
println!("Missing arg or command\n"); println!("Missing arg or command\n");
} }
@@ -927,22 +999,31 @@ fn handle_throttle_profile(
let proxy = PlatformProxyBlocking::new(conn)?; let proxy = PlatformProxyBlocking::new(conn)?;
let current = proxy.platform_profile()?; let current = proxy.platform_profile()?;
let choices = proxy.platform_profile_choices()?;
if cmd.next { if cmd.next {
proxy.set_platform_profile(current.next())?; proxy.set_platform_profile(PlatformProfile::next(current, &choices))?;
} else if let Some(profile) = cmd.profile_set { } else if let Some(profile) = cmd.profile_set {
proxy.set_platform_profile(profile)?; proxy.set_platform_profile(profile)?;
} else if let Some(profile) = cmd.profile_set_ac {
proxy.set_platform_profile_on_ac(profile)?;
} else if let Some(profile) = cmd.profile_set_bat {
proxy.set_platform_profile_on_battery(profile)?;
} }
if cmd.list { if cmd.list {
let res = PlatformProfile::list(); for p in &choices {
for p in &res {
println!("{:?}", p); println!("{:?}", p);
} }
} }
if cmd.profile_get { if cmd.profile_get {
println!("Active profile is {current:?}"); println!("Active profile is {current:?}");
println!("Profile on AC is {:?}", proxy.platform_profile_on_ac()?);
println!(
"Profile on Battery is {:?}",
proxy.platform_profile_on_battery()?
);
} }
Ok(()) Ok(())
@@ -950,7 +1031,7 @@ fn handle_throttle_profile(
fn handle_fan_curve( fn handle_fan_curve(
conn: &Connection, conn: &Connection,
cmd: &FanCurveCommand cmd: &FanCurveCommand,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
let Ok(fan_proxy) = FanCurvesProxyBlocking::new(conn).map_err(|e| { let Ok(fan_proxy) = FanCurvesProxyBlocking::new(conn).map_err(|e| {
println!("Fan-curves not supported by either this kernel or by the laptop: {e:?}"); println!("Fan-curves not supported by either this kernel or by the laptop: {e:?}");
@@ -1045,7 +1126,7 @@ fn check_systemd_unit_enabled(name: &str) -> bool {
.output() .output()
{ {
let buf = String::from_utf8_lossy(&out.stdout); let buf = String::from_utf8_lossy(&out.stdout);
return buf.contains("enabled"); return buf.contains("enabled") || buf.contains("linked");
} }
false false
} }
@@ -1103,11 +1184,15 @@ fn print_firmware_attr(attr: &AsusArmouryProxyBlocking) -> Result<(), Box<dyn st
Ok(()) Ok(())
} }
#[allow(clippy::manual_is_multiple_of)]
fn handle_armoury_command(cmd: &ArmouryCommand) -> Result<(), Box<dyn std::error::Error>> { fn handle_armoury_command(cmd: &ArmouryCommand) -> Result<(), Box<dyn std::error::Error>> {
{ {
if cmd.free.is_empty() || cmd.free.len() % 2 != 0 || cmd.help { // Avoid using `.is_multiple_of(2)` to satisfy the request. Use modulus check
// and simplify the boolean expression.
let odd_len = cmd.free.len() % 2 != 0;
if cmd.free.is_empty() || odd_len || cmd.help {
const USAGE: &str = "Usage: asusctl platform panel_overdrive 1 nv_dynamic_boost 5"; const USAGE: &str = "Usage: asusctl platform panel_overdrive 1 nv_dynamic_boost 5";
if cmd.free.len() % 2 != 0 { if odd_len {
println!( println!(
"Incorrect number of args, each attribute label must be paired with a setting:" "Incorrect number of args, each attribute label must be paired with a setting:"
); );

View File

@@ -31,5 +31,5 @@ pub struct ScsiCommand {
pub colours: Vec<Colour>, pub colours: Vec<Colour>,
#[options(help = "list available animations")] #[options(help = "list available animations")]
pub list: bool pub list: bool,
} }

View File

@@ -26,10 +26,12 @@ pub struct SlashCommand {
pub show_on_sleep: Option<bool>, pub show_on_sleep: Option<bool>,
#[options(short = "b", meta = "", help = "Show the animation on battery")] #[options(short = "b", meta = "", help = "Show the animation on battery")]
pub show_on_battery: Option<bool>, pub show_on_battery: Option<bool>,
// #[options(short = "L", meta = "", help = "Show the animation on lid closed")]
// pub show_on_lid_closed: Option<bool>,
#[options( #[options(
short = "w", short = "w",
meta = "", meta = "",
help = "Show the low-battery warning animation" help = "Show the low-battery warning animation"
)] )]
pub show_battery_warning: Option<bool> pub show_battery_warning: Option<bool>,
} }

View File

@@ -32,3 +32,16 @@ config-traits = { path = "../config-traits" }
zbus.workspace = true zbus.workspace = true
env_logger.workspace = true env_logger.workspace = true
[package.metadata.deb]
license-file = ["../LICENSE", "4"]
extended-description = """\
An user utility for Linux to control fancy things on various ASUS laptops
like keyboard effects or anime matrix animation cycles."""
depends = "$auto"
section = "utility"
priority = "optional"
assets = [
["target/release/asusd-user", "usr/bin/", "755"],
["../asusd_user-fakeinstall/usr/lib/systemd/user/*", "usr/lib/systemd/user/", "644"],
]

View File

@@ -21,7 +21,7 @@ fn root_conf_dir() -> PathBuf {
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct ConfigAnime { pub struct ConfigAnime {
pub name: String, pub name: String,
pub anime: Vec<ActionLoader> pub anime: Vec<ActionLoader>,
} }
impl ConfigAnime { impl ConfigAnime {
@@ -52,8 +52,8 @@ impl Default for ConfigAnime {
time: AnimTime::Fade(Fade::new( time: AnimTime::Fade(Fade::new(
Duration::from_secs(2), Duration::from_secs(2),
None, None,
Duration::from_secs(2) Duration::from_secs(2),
)) )),
}, },
ActionLoader::AsusAnimation { ActionLoader::AsusAnimation {
file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(), file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(),
@@ -61,8 +61,8 @@ impl Default for ConfigAnime {
time: AnimTime::Fade(Fade::new( time: AnimTime::Fade(Fade::new(
Duration::from_secs(6), Duration::from_secs(6),
None, None,
Duration::from_secs(3) Duration::from_secs(3),
)) )),
}, },
ActionLoader::ImageAnimation { ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(), file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
@@ -73,8 +73,8 @@ impl Default for ConfigAnime {
time: AnimTime::Fade(Fade::new( time: AnimTime::Fade(Fade::new(
Duration::from_secs(2), Duration::from_secs(2),
Some(Duration::from_secs(2)), Some(Duration::from_secs(2)),
Duration::from_secs(2) Duration::from_secs(2),
)) )),
}, },
ActionLoader::Image { ActionLoader::Image {
file: "/usr/share/asusd/anime/custom/rust.png".into(), file: "/usr/share/asusd/anime/custom/rust.png".into(),
@@ -84,9 +84,9 @@ impl Default for ConfigAnime {
time: AnimTime::Fade(Fade::new( time: AnimTime::Fade(Fade::new(
Duration::from_secs(2), Duration::from_secs(2),
Some(Duration::from_secs(1)), Some(Duration::from_secs(1)),
Duration::from_secs(2) Duration::from_secs(2),
)), )),
brightness: 0.6 brightness: 0.6,
}, },
ActionLoader::Pause(Duration::from_secs(1)), ActionLoader::Pause(Duration::from_secs(1)),
ActionLoader::ImageAnimation { ActionLoader::ImageAnimation {
@@ -95,9 +95,9 @@ impl Default for ConfigAnime {
angle: 0.0, angle: 0.0,
translation: Vec2::new(3.0, 2.0), translation: Vec2::new(3.0, 2.0),
brightness: 0.5, brightness: 0.5,
time: AnimTime::Count(2) time: AnimTime::Count(2),
}, },
] ],
} }
} }
} }
@@ -121,7 +121,7 @@ impl StdConfigLoad for ConfigAnime {}
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct ConfigAura { pub struct ConfigAura {
pub name: String, pub name: String,
pub aura: AuraSequences pub aura: AuraSequences,
} }
impl ConfigAura { impl ConfigAura {
@@ -139,14 +139,14 @@ impl Default for ConfigAura {
Colour { Colour {
r: 255, r: 255,
g: 0, g: 0,
b: 20 b: 20,
}, },
Colour { Colour {
r: 20, r: 20,
g: 255, g: 255,
b: 0 b: 0,
}, },
Speed::Low Speed::Low,
)); ));
seq.push(key.clone()); seq.push(key.clone());
@@ -161,7 +161,7 @@ impl Default for ConfigAura {
LedCode::F, LedCode::F,
Colour { r: 255, g: 0, b: 0 }, Colour { r: 255, g: 0, b: 0 },
Colour { r: 255, g: 0, b: 0 }, Colour { r: 255, g: 0, b: 0 },
Speed::High Speed::High,
)); ));
seq.push(key); seq.push(key);
@@ -176,13 +176,13 @@ impl Default for ConfigAura {
LedCode::N9, LedCode::N9,
Colour { r: 0, g: 0, b: 255 }, Colour { r: 0, g: 0, b: 255 },
80, 80,
40 40,
)); ));
seq.push(key); seq.push(key);
Self { Self {
name: "aura-default".to_owned(), name: "aura-default".to_owned(),
aura: seq aura: seq,
} }
} }
} }
@@ -209,14 +209,14 @@ pub struct ConfigBase {
/// Name of active anime config file in the user config directory /// Name of active anime config file in the user config directory
pub active_anime: Option<String>, pub active_anime: Option<String>,
/// Name of active aura config file in the user config directory /// Name of active aura config file in the user config directory
pub active_aura: Option<String> pub active_aura: Option<String>,
} }
impl StdConfig for ConfigBase { impl StdConfig for ConfigBase {
fn new() -> Self { fn new() -> Self {
Self { Self {
active_anime: Some("anime-default".to_owned()), active_anime: Some("anime-default".to_owned()),
active_aura: Some("aura-default".to_owned()) active_aura: Some("aura-default".to_owned()),
} }
} }

View File

@@ -25,7 +25,7 @@ pub struct Timer {
/// Used only for `TimeType::Timer`, milliseonds to fade the image in for /// Used only for `TimeType::Timer`, milliseonds to fade the image in for
fade_in: u64, fade_in: u64,
/// Used only for `TimeType::Timer`, milliseonds to fade the image out for /// Used only for `TimeType::Timer`, milliseonds to fade the image out for
fade_out: u64 fade_out: u64,
} }
impl From<Timer> for AnimTime { impl From<Timer> for AnimTime {
@@ -46,7 +46,7 @@ impl From<Timer> for AnimTime {
} }
} }
TimeType::Count => AnimTime::Count(time.count as u32), TimeType::Count => AnimTime::Count(time.count as u32),
TimeType::Infinite => AnimTime::Infinite TimeType::Infinite => AnimTime::Infinite,
} }
} }
} }
@@ -55,7 +55,7 @@ impl From<Timer> for AnimTime {
pub enum TimeType { pub enum TimeType {
Timer, Timer,
Count, Count,
Infinite Infinite,
} }
/// The inner object exists to allow the zbus proxy to share it with a runner /// The inner object exists to allow the zbus proxy to share it with a runner
@@ -63,19 +63,19 @@ pub enum TimeType {
pub struct CtrlAnimeInner<'a> { pub struct CtrlAnimeInner<'a> {
sequences: Sequences, sequences: Sequences,
client: AnimeProxyBlocking<'a>, client: AnimeProxyBlocking<'a>,
do_early_return: Arc<AtomicBool> do_early_return: Arc<AtomicBool>,
} }
impl CtrlAnimeInner<'static> { impl CtrlAnimeInner<'static> {
pub fn new( pub fn new(
sequences: Sequences, sequences: Sequences,
client: AnimeProxyBlocking<'static>, client: AnimeProxyBlocking<'static>,
do_early_return: Arc<AtomicBool> do_early_return: Arc<AtomicBool>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
Ok(Self { Ok(Self {
sequences, sequences,
client, client,
do_early_return do_early_return,
}) })
} }
@@ -130,7 +130,7 @@ pub struct CtrlAnime<'a> {
client: AnimeProxyBlocking<'a>, client: AnimeProxyBlocking<'a>,
inner: Arc<Mutex<CtrlAnimeInner<'a>>>, inner: Arc<Mutex<CtrlAnimeInner<'a>>>,
/// Must be the same Atomic as in CtrlAnimeInner /// Must be the same Atomic as in CtrlAnimeInner
inner_early_return: Arc<AtomicBool> inner_early_return: Arc<AtomicBool>,
} }
impl CtrlAnime<'static> { impl CtrlAnime<'static> {
@@ -138,13 +138,13 @@ impl CtrlAnime<'static> {
config: Arc<Mutex<ConfigAnime>>, config: Arc<Mutex<ConfigAnime>>,
inner: Arc<Mutex<CtrlAnimeInner<'static>>>, inner: Arc<Mutex<CtrlAnimeInner<'static>>>,
client: AnimeProxyBlocking<'static>, client: AnimeProxyBlocking<'static>,
inner_early_return: Arc<AtomicBool> inner_early_return: Arc<AtomicBool>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
Ok(CtrlAnime { Ok(CtrlAnime {
config, config,
client, client,
inner, inner,
inner_early_return inner_early_return,
}) })
} }
@@ -174,7 +174,7 @@ impl CtrlAnime<'static> {
index: u32, index: u32,
file: &str, file: &str,
time: Timer, time: Timer,
brightness: f32 brightness: f32,
) -> zbus::fdo::Result<String> { ) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() { if let Ok(mut config) = self.config.try_lock() {
let time: AnimTime = time.into(); let time: AnimTime = time.into();
@@ -182,7 +182,7 @@ impl CtrlAnime<'static> {
let action = ActionLoader::AsusAnimation { let action = ActionLoader::AsusAnimation {
file: file.into(), file: file.into(),
brightness, brightness,
time time,
}; };
// Must make the inner run loop return early // Must make the inner run loop return early
@@ -216,7 +216,7 @@ impl CtrlAnime<'static> {
angle: f32, angle: f32,
xy: (f32, f32), xy: (f32, f32),
time: Timer, time: Timer,
brightness: f32 brightness: f32,
) -> zbus::fdo::Result<String> { ) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() { if let Ok(mut config) = self.config.try_lock() {
let time: AnimTime = time.into(); let time: AnimTime = time.into();
@@ -228,7 +228,7 @@ impl CtrlAnime<'static> {
angle, angle,
translation, translation,
brightness, brightness,
time time,
}; };
// Must make the inner run loop return early // Must make the inner run loop return early
@@ -261,7 +261,7 @@ impl CtrlAnime<'static> {
angle: f32, angle: f32,
xy: (f32, f32), xy: (f32, f32),
time: Timer, time: Timer,
brightness: f32 brightness: f32,
) -> zbus::fdo::Result<String> { ) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() { if let Ok(mut config) = self.config.try_lock() {
let file = Path::new(&file); let file = Path::new(&file);
@@ -272,7 +272,7 @@ impl CtrlAnime<'static> {
angle, angle,
translation: Vec2::new(xy.0, xy.1), translation: Vec2::new(xy.0, xy.1),
brightness, brightness,
time time,
}; };
// Must make the inner run loop return early // Must make the inner run loop return early

View File

@@ -61,16 +61,16 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
CtrlAnimeInner::new( CtrlAnimeInner::new(
anime, anime,
anime_proxy_blocking.clone(), anime_proxy_blocking.clone(),
early_return.clone() early_return.clone(),
) )
.unwrap() .unwrap(),
)); ));
// Need new client object for dbus control part // Need new client object for dbus control part
let anime_control = CtrlAnime::new( let anime_control = CtrlAnime::new(
anime_config, anime_config,
inner.clone(), inner.clone(),
anime_proxy_blocking, anime_proxy_blocking,
early_return early_return,
) )
.unwrap(); .unwrap();
anime_control.add_to_server(&mut connection).await; anime_control.add_to_server(&mut connection).await;

View File

@@ -8,7 +8,7 @@ pub enum Error {
ConfigLoadFail, ConfigLoadFail,
ConfigLockFail, ConfigLockFail,
XdgVars, XdgVars,
Anime(AnimeError) Anime(AnimeError),
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@@ -19,7 +19,7 @@ impl fmt::Display for Error {
Error::ConfigLoadFail => write!(f, "Failed to load user config"), Error::ConfigLoadFail => write!(f, "Failed to load user config"),
Error::ConfigLockFail => write!(f, "Failed to lock user config"), Error::ConfigLockFail => write!(f, "Failed to lock user config"),
Error::XdgVars => write!(f, "XDG environment vars appear unset"), Error::XdgVars => write!(f, "XDG environment vars appear unset"),
Error::Anime(err) => write!(f, "Anime error: {}", err) Error::Anime(err) => write!(f, "Anime error: {}", err),
} }
} }
} }

View File

@@ -32,7 +32,7 @@ trait Daemon {
file: &str, file: &str,
time: u32, time: u32,
count: u32, count: u32,
brightness: f64 brightness: f64,
) -> zbus::Result<String>; ) -> zbus::Result<String>;
/// InsertImage method /// InsertImage method
@@ -43,7 +43,7 @@ trait Daemon {
scale: f64, scale: f64,
angle: f64, angle: f64,
xy: &(f64, f64), xy: &(f64, f64),
brightness: f64 brightness: f64,
) -> zbus::Result<String>; ) -> zbus::Result<String>;
/// InsertImageGif method /// InsertImageGif method
@@ -56,7 +56,7 @@ trait Daemon {
xy: &(f64, f64), xy: &(f64, f64),
time: u32, time: u32,
count: u32, count: u32,
brightness: f64 brightness: f64,
) -> zbus::Result<String>; ) -> zbus::Result<String>;
/// InsertPause method /// InsertPause method

View File

@@ -34,6 +34,7 @@ tokio.workspace = true
log.workspace = true log.workspace = true
env_logger.workspace = true env_logger.workspace = true
futures-util.workspace = true
zbus.workspace = true zbus.workspace = true
logind-zbus.workspace = true logind-zbus.workspace = true
@@ -44,3 +45,19 @@ concat-idents.workspace = true
[dev-dependencies] [dev-dependencies]
cargo-husky.workspace = true cargo-husky.workspace = true
tempfile = "3"
[package.metadata.deb]
license-file = ["../LICENSE", "4"]
extended-description = """\
The dbus server for asusctl and rog-control-center applications."""
depends = "$auto"
section = "utility"
priority = "optional"
assets = [
["target/release/asusd", "usr/bin/", "755"],
["../asusd-fakeinstall/usr/lib/systemd/system/*", "usr/lib/systemd/system/", "644"],
["../asusd-fakeinstall/usr/lib/udev/rules.d/*", "usr/lib/udev/rules.d/", "644"],
["../asusd-fakeinstall/usr/share/asusd/*", "usr/share/share/asusd/", "644"],
["../asusd-fakeinstall/usr/share/dbus-1/system.d/*", "usr/share/dbus-1/system.d/", "644"],
]

View File

@@ -1,12 +1,12 @@
use std::sync::Arc; use std::sync::Arc;
use ::zbus::export::futures_util::lock::Mutex;
use config_traits::StdConfig; use config_traits::StdConfig;
use log::{debug, error, info}; use log::{debug, error, info};
use rog_platform::asus_armoury::{AttrValue, Attribute, FirmwareAttribute, FirmwareAttributes}; use rog_platform::asus_armoury::{AttrValue, Attribute, FirmwareAttribute, FirmwareAttributes};
use rog_platform::platform::{PlatformProfile, RogPlatform}; use rog_platform::platform::{PlatformProfile, RogPlatform};
use rog_platform::power::AsusPower; use rog_platform::power::AsusPower;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::sync::Mutex;
use zbus::object_server::SignalEmitter; use zbus::object_server::SignalEmitter;
use zbus::zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Type, Value}; use zbus::zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Type, Value};
use zbus::{fdo, interface, Connection}; use zbus::{fdo, interface, Connection};
@@ -20,20 +20,42 @@ const MOD_NAME: &str = "asus_armoury";
#[derive(Debug, Default, Clone, Deserialize, Serialize, Type, Value, OwnedValue)] #[derive(Debug, Default, Clone, Deserialize, Serialize, Type, Value, OwnedValue)]
pub struct PossibleValues { pub struct PossibleValues {
strings: Vec<String>, strings: Vec<String>,
nums: Vec<i32> nums: Vec<i32>,
} }
fn dbus_path_for_attr(attr_name: &str) -> OwnedObjectPath { fn dbus_path_for_attr(attr_name: &str) -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{attr_name}")).into() ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{attr_name}")).into()
} }
// Helper: return true when the attribute is effectively unsupported for
// the current power mode/device. We consider an attribute unsupported when
// its resolved min and max are equal (no available range), which indicates
// writes would be invalid. When true, callers should avoid attempting writes.
fn attr_unsupported(attr: &Attribute) -> bool {
let min = attr
.refresh_min_value()
.or(match attr.min_value() {
AttrValue::Integer(i) => Some(*i),
_ => None,
})
.unwrap_or(-1);
let max = attr
.refresh_max_value()
.or(match attr.max_value() {
AttrValue::Integer(i) => Some(*i),
_ => None,
})
.unwrap_or(-2); // different default so equality is false unless both present
min == max
}
#[derive(Clone)] #[derive(Clone)]
pub struct AsusArmouryAttribute { pub struct AsusArmouryAttribute {
attr: Attribute, attr: Attribute,
config: Arc<Mutex<Config>>, config: Arc<Mutex<Config>>,
/// platform control required here for access to PPD or Throttle profile /// platform control required here for access to PPD or Throttle profile
platform: RogPlatform, platform: RogPlatform,
power: AsusPower power: AsusPower,
} }
impl AsusArmouryAttribute { impl AsusArmouryAttribute {
@@ -41,16 +63,39 @@ impl AsusArmouryAttribute {
attr: Attribute, attr: Attribute,
platform: RogPlatform, platform: RogPlatform,
power: AsusPower, power: AsusPower,
config: Arc<Mutex<Config>> config: Arc<Mutex<Config>>,
) -> Self { ) -> Self {
Self { Self {
attr, attr,
config, config,
platform, platform,
power power,
} }
} }
pub fn attribute_name(&self) -> String {
String::from(self.attr.name())
}
fn resolve_i32_value(refreshed: Option<i32>, cached: &AttrValue) -> i32 {
refreshed
.or(match cached {
AttrValue::Integer(i) => Some(*i),
_ => None,
})
.unwrap_or(-1)
}
pub async fn emit_limits(&self, connection: &Connection) -> Result<(), RogError> {
let path = dbus_path_for_attr(self.attr.name());
let signal = SignalEmitter::new(connection, path)?;
self.min_value_changed(&signal).await?;
self.max_value_changed(&signal).await?;
self.scalar_increment_changed(&signal).await?;
self.current_value_changed(&signal).await?;
Ok(())
}
pub async fn move_to_zbus(self, connection: &Connection) -> Result<(), RogError> { pub async fn move_to_zbus(self, connection: &Connection) -> Result<(), RogError> {
let path = dbus_path_for_attr(self.attr.name()); let path = dbus_path_for_attr(self.attr.name());
connection connection
@@ -64,9 +109,9 @@ impl AsusArmouryAttribute {
async fn watch_and_notify( async fn watch_and_notify(
&mut self, &mut self,
signal_ctxt: SignalEmitter<'static> signal_ctxt: SignalEmitter<'static>,
) -> Result<(), RogError> { ) -> Result<(), RogError> {
use zbus::export::futures_util::StreamExt; use futures_util::StreamExt;
let name = self.name(); let name = self.name();
macro_rules! watch_value_notify { macro_rules! watch_value_notify {
@@ -78,21 +123,27 @@ impl AsusArmouryAttribute {
let sig = signal_ctxt.clone(); let sig = signal_ctxt.clone();
tokio::spawn(async move { tokio::spawn(async move {
let mut buffer = [0; 32]; let mut buffer = [0; 32];
watch if let Ok(stream) = watch.into_event_stream(&mut buffer) {
.into_event_stream(&mut buffer) stream
.unwrap() .for_each(|_| async {
.for_each(|_| async { debug!("{} changed", name);
debug!("{} changed", name); ctrl.$fn_prop_changed(&sig).await.ok();
ctrl.$fn_prop_changed(&sig).await.ok(); })
}) .await;
.await; } else {
info!(
"inotify event stream failed for {} ({}). You can ignore this \
if unsupported",
name, $attr_str
);
}
}); });
} }
Err(e) => info!( Err(e) => info!(
"inotify watch failed: {}. You can ignore this if your device does not \ "inotify watch failed: {}. You can ignore this if your device does not \
support the feature", support the feature",
e e
) ),
} }
}; };
} }
@@ -107,36 +158,109 @@ impl AsusArmouryAttribute {
} }
} }
#[derive(Clone, Default)]
pub struct ArmouryAttributeRegistry {
attrs: Vec<AsusArmouryAttribute>,
}
impl ArmouryAttributeRegistry {
pub fn push(&mut self, attr: AsusArmouryAttribute) {
self.attrs.push(attr);
}
pub fn is_empty(&self) -> bool {
self.attrs.is_empty()
}
pub fn iter(&self) -> std::slice::Iter<'_, AsusArmouryAttribute> {
self.attrs.iter()
}
pub async fn emit_limits(&self, connection: &Connection) -> Result<(), RogError> {
let mut last_err: Option<RogError> = None;
for attr in &self.attrs {
if let Err(e) = attr.emit_limits(connection).await {
error!(
"Failed to emit updated limits for attribute '{}': {e:?}",
attr.attribute_name()
);
last_err = Some(e);
}
}
if let Some(err) = last_err {
Err(err)
} else {
Ok(())
}
}
}
impl crate::Reloadable for AsusArmouryAttribute { impl crate::Reloadable for AsusArmouryAttribute {
async fn reload(&mut self) -> Result<(), RogError> { async fn reload(&mut self) -> Result<(), RogError> {
info!("Reloading {}", self.attr.name()); info!("Reloading {}", self.attr.name());
let profile: PlatformProfile = self.platform.get_platform_profile()?.into(); let name: FirmwareAttribute = self.attr.name().into();
let power_plugged = self
.power if name.is_ppt() || name.is_dgpu() {
.get_online() let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
.map_err(|e| { let power_plugged = self
error!("Could not get power status: {e:?}"); .power
e .get_online()
}) .map_err(|e| {
.unwrap_or_default(); error!("Could not get power status: {e:?}");
let config = if power_plugged == 1 { e
&self.config.lock().await.ac_profile_tunings })
} else { .unwrap_or_default()
&self.config.lock().await.dc_profile_tunings == 1;
};
if let Some(tuning) = config.get(&profile) { let apply_value = {
if tuning.enabled { let config = self.config.lock().await;
if let Some(tune) = tuning.group.get(&self.name()) { config
.select_tunings_ref(power_plugged, profile)
.and_then(|tuning| {
if tuning.enabled {
tuning.group.get(&self.name()).copied()
} else {
None
}
})
};
if let Some(tune) = apply_value {
// Don't attempt writes for attributes that report min==0 and max==0
// (commonly means not supported in this power mode/device); skip
// applying stored tune in that case.
if self.attr.base_path_exists() && !attr_unsupported(&self.attr) {
self.attr self.attr
.set_current_value(&AttrValue::Integer(*tune)) .set_current_value(&AttrValue::Integer(tune))
.map_err(|e| { .map_err(|e| {
error!("Could not set {} value: {e:?}", self.attr.name()); error!("Could not set {} value: {e:?}", self.attr.name());
self.attr.base_path_exists(); self.attr.base_path_exists();
e e
})?; })?;
info!("Set {} to {:?}", self.attr.name(), tune); info!("Set {} to {:?}", self.attr.name(), tune);
} else {
debug!(
"Skipping apply for {} because attribute missing or unsupported (min==max)",
self.attr.name()
);
} }
} }
} else {
// Handle non-PPT attributes (boolean and other settings)
if let Some(saved_value) = self.config.lock().await.armoury_settings.get(&name) {
self.attr
.set_current_value(&AttrValue::Integer(*saved_value))
.map_err(|e| {
error!("Could not set {} value: {e:?}", self.attr.name());
self.attr.base_path_exists();
e
})?;
info!(
"Restored armoury setting {} to {:?}",
self.attr.name(),
saved_value
);
}
} }
Ok(()) Ok(())
@@ -187,13 +311,13 @@ impl AsusArmouryAttribute {
async fn default_value(&self) -> i32 { async fn default_value(&self) -> i32 {
match self.attr.default_value() { match self.attr.default_value() {
AttrValue::Integer(i) => *i, AttrValue::Integer(i) => *i,
_ => -1 _ => -1,
} }
} }
async fn restore_default(&self) -> fdo::Result<()> { async fn restore_default(&self) -> fdo::Result<()> {
self.attr.restore_default()?; self.attr.restore_default()?;
if self.name().is_ppt() { if self.name().is_ppt() || self.name().is_dgpu() {
let profile: PlatformProfile = self.platform.get_platform_profile()?.into(); let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self let power_plugged = self
.power .power
@@ -226,39 +350,33 @@ impl AsusArmouryAttribute {
#[zbus(property)] #[zbus(property)]
async fn min_value(&self) -> i32 { async fn min_value(&self) -> i32 {
match self.attr.min_value() { Self::resolve_i32_value(self.attr.refresh_min_value(), self.attr.min_value())
AttrValue::Integer(i) => *i,
_ => -1
}
} }
#[zbus(property)] #[zbus(property)]
async fn max_value(&self) -> i32 { async fn max_value(&self) -> i32 {
match self.attr.max_value() { Self::resolve_i32_value(self.attr.refresh_max_value(), self.attr.max_value())
AttrValue::Integer(i) => *i,
_ => -1
}
} }
#[zbus(property)] #[zbus(property)]
async fn scalar_increment(&self) -> i32 { async fn scalar_increment(&self) -> i32 {
match self.attr.scalar_increment() { Self::resolve_i32_value(
AttrValue::Integer(i) => *i, self.attr.refresh_scalar_increment(),
_ => -1 self.attr.scalar_increment(),
} )
} }
#[zbus(property)] #[zbus(property)]
async fn possible_values(&self) -> Vec<i32> { async fn possible_values(&self) -> Vec<i32> {
match self.attr.possible_values() { match self.attr.possible_values() {
AttrValue::EnumInt(i) => i.clone(), AttrValue::EnumInt(i) => i.clone(),
_ => Vec::default() _ => Vec::default(),
} }
} }
#[zbus(property)] #[zbus(property)]
async fn current_value(&self) -> fdo::Result<i32> { async fn current_value(&self) -> fdo::Result<i32> {
if self.name().is_ppt() { if self.name().is_ppt() || self.name().is_dgpu() {
let profile: PlatformProfile = self.platform.get_platform_profile()?.into(); let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self let power_plugged = self
.power .power
@@ -267,16 +385,19 @@ impl AsusArmouryAttribute {
error!("Could not get power status: {e:?}"); error!("Could not get power status: {e:?}");
e e
}) })
.unwrap_or_default(); .unwrap_or_default()
let mut config = self.config.lock().await; == 1;
let tuning = config.select_tunings(power_plugged == 1, profile); let config = self.config.lock().await;
if let Some(tune) = tuning.group.get(&self.name()) { if let Some(tuning) = config.select_tunings_ref(power_plugged, profile) {
return Ok(*tune); if let Some(tune) = tuning.group.get(&self.name()) {
} else if let AttrValue::Integer(i) = self.attr.default_value() { return Ok(*tune);
}
}
if let AttrValue::Integer(i) = self.attr.default_value() {
return Ok(*i); return Ok(*i);
} }
return Err(fdo::Error::Failed( return Err(fdo::Error::Failed(
"Could not read current value".to_string() "Could not read current value".to_string(),
)); ));
} }
@@ -284,13 +405,101 @@ impl AsusArmouryAttribute {
return Ok(i); return Ok(i);
} }
Err(fdo::Error::Failed( Err(fdo::Error::Failed(
"Could not read current value".to_string() "Could not read current value".to_string(),
)) ))
} }
async fn stored_value_for_power(&self, on_ac: bool) -> fdo::Result<i32> {
if !(self.name().is_ppt() || self.name().is_dgpu()) {
return Err(fdo::Error::NotSupported(
"Stored values are only available for PPT/dGPU tunable attributes".to_string(),
));
}
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let config = self.config.lock().await;
if let Some(tuning) = config.select_tunings_ref(on_ac, profile) {
if let Some(tune) = tuning.group.get(&self.name()) {
return Ok(*tune);
}
}
if let AttrValue::Integer(i) = self.attr.default_value() {
return Ok(*i);
}
Err(fdo::Error::Failed(
"Could not read stored value".to_string(),
))
}
async fn set_value_for_power(&mut self, on_ac: bool, value: i32) -> fdo::Result<()> {
if !(self.name().is_ppt() || self.name().is_dgpu()) {
return Err(fdo::Error::NotSupported(
"Setting stored values is only supported for PPT/dGPU tunable attributes"
.to_string(),
));
}
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let apply_now;
{
let mut config = self.config.lock().await;
let tuning = config.select_tunings(on_ac, profile);
if let Some(tune) = tuning.group.get_mut(&self.name()) {
*tune = value;
} else {
tuning.group.insert(self.name(), value);
debug!(
"Store {} value for {} power = {}",
self.attr.name(),
if on_ac { "AC" } else { "DC" },
value
);
}
apply_now = tuning.enabled;
config.write();
}
if apply_now {
let power_plugged = self
.power
.get_online()
.map_err(|e| {
error!("Could not get power status: {e:?}");
e
})
.unwrap_or_default()
!= 0;
// Don't attempt writes for attributes that report min==0 and max==0
// (commonly means not supported in this power mode/device); skip
// applying stored value in that case.
if self.attr.base_path_exists() && !attr_unsupported(&self.attr) {
if power_plugged == on_ac {
self.attr
.set_current_value(&AttrValue::Integer(value))
.map_err(|e| {
error!("Could not set value: {e:?}");
e
})?;
}
} else {
debug!(
"Skipping immediate apply for {} because attribute missing or unsupported (min==max)",
self.attr.name()
);
}
}
Ok(())
}
#[zbus(property)] #[zbus(property)]
async fn set_current_value(&mut self, value: i32) -> fdo::Result<()> { async fn set_current_value(&mut self, value: i32) -> fdo::Result<()> {
if self.name().is_ppt() { if self.name().is_ppt() || self.name().is_dgpu() {
let profile: PlatformProfile = self.platform.get_platform_profile()?.into(); let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self let power_plugged = self
.power .power
@@ -311,12 +520,22 @@ impl AsusArmouryAttribute {
debug!("Store tuning config for {} = {:?}", self.attr.name(), value); debug!("Store tuning config for {} = {:?}", self.attr.name(), value);
} }
if tuning.enabled { if tuning.enabled {
self.attr // Don't attempt writes for attributes that report min==0 and max==0
.set_current_value(&AttrValue::Integer(value)) // (commonly means not supported in this power mode/device); skip
.map_err(|e| { // applying stored value in that case.
error!("Could not set value: {e:?}"); if self.attr.base_path_exists() && !attr_unsupported(&self.attr) {
e self.attr
})?; .set_current_value(&AttrValue::Integer(value))
.map_err(|e| {
error!("Could not set value: {e:?}");
e
})?;
} else {
debug!(
"Skipping apply for {} on set_current_value because attribute missing or unsupported (min==max)",
self.attr.name()
);
}
} }
} else { } else {
self.attr self.attr
@@ -357,69 +576,278 @@ impl AsusArmouryAttribute {
} }
} }
#[allow(clippy::too_many_arguments)]
pub async fn start_attributes_zbus( pub async fn start_attributes_zbus(
conn: &Connection, conn: Option<&Connection>,
platform: RogPlatform, platform: RogPlatform,
power: AsusPower, power: AsusPower,
attributes: FirmwareAttributes, attributes: FirmwareAttributes,
config: Arc<Mutex<Config>> config: Arc<Mutex<Config>>,
) -> Result<(), RogError> { enable_zbus: bool,
profile_override: Option<rog_platform::platform::PlatformProfile>,
power_plugged_override: Option<bool>,
) -> Result<ArmouryAttributeRegistry, RogError> {
let mut registry = ArmouryAttributeRegistry::default();
for attr in attributes.attributes() { for attr in attributes.attributes() {
let mut attr = AsusArmouryAttribute::new( let mut attr = AsusArmouryAttribute::new(
attr.clone(), attr.clone(),
platform.clone(), platform.clone(),
power.clone(), power.clone(),
config.clone() config.clone(),
); );
attr.reload().await?;
let path = dbus_path_for_attr(attr.attr.name()); let registry_attr = attr.clone();
let sig = zbus::object_server::SignalEmitter::new(conn, path)?;
attr.watch_and_notify(sig).await?;
attr.move_to_zbus(conn).await?; // Only perform the full reload (which may query the platform/power sysfs)
// when zbus is enabled. Tests using the no-zbus mode skip the reload and
// emulate the reload/apply behavior to avoid depending on udev/sysfs.
if enable_zbus {
if let Err(e) = attr.reload().await {
error!(
"Skipping attribute '{}' due to reload error: {e:?}",
attr.attr.name()
);
// continue with others
continue;
}
}
let attr_name = attr.attribute_name();
// If zbus is enabled and a connection is provided, create the SignalEmitter,
// start watchers and register the object on zbus. Tests can call this function
// with enable_zbus=false and conn=None to skip DBus registration and watchers.
if !enable_zbus {
// Emulate reload logic but prefer overrides when provided to avoid dependency on udev/sysfs in tests
let name: rog_platform::asus_armoury::FirmwareAttribute = attr.attr.name().into();
if name.is_ppt() || name.is_dgpu() {
// determine profile
let profile = if let Some(p) = profile_override {
p
} else {
match attr.platform.get_platform_profile() {
Ok(p) => p.into(),
Err(_) => rog_platform::platform::PlatformProfile::Balanced,
}
};
// determine power plugged
let power_plugged = if let Some(v) = power_plugged_override {
v
} else {
match attr.power.get_online() {
Ok(v) => v == 1,
Err(_) => false,
}
};
let apply_value = {
let config = attr.config.lock().await;
config
.select_tunings_ref(power_plugged, profile)
.and_then(|tuning| {
if tuning.enabled {
tuning.group.get(&attr.name()).copied()
} else {
None
}
})
};
if let Some(tune) = apply_value {
attr.attr
.set_current_value(&AttrValue::Integer(tune))
.map_err(|e| {
error!("Could not set {} value: {e:?}", attr.attr.name());
e
})?;
}
} else if let Some(saved_value) = attr.config.lock().await.armoury_settings.get(&name) {
attr.attr
.set_current_value(&AttrValue::Integer(*saved_value))
.map_err(|e| {
error!("Could not set {} value: {e:?}", attr.attr.name());
e
})?;
info!(
"Restored armoury setting {} to {:?}",
attr.attr.name(),
saved_value
);
}
registry.push(registry_attr);
continue;
}
// If zbus is enabled and a connection is provided, create the SignalEmitter,
// start watchers and register the object on zbus. Tests can call this function
// with enable_zbus=false and conn=None to skip DBus registration and watchers.
if enable_zbus {
if let Some(connection) = conn {
let path = dbus_path_for_attr(attr_name.as_str());
match zbus::object_server::SignalEmitter::new(connection, path) {
Ok(sig) => {
if let Err(e) = attr.watch_and_notify(sig).await {
error!("Failed to start watcher for '{}': {e:?}", attr.attr.name());
}
}
Err(e) => {
error!(
"Failed to create SignalEmitter for '{}': {e:?}",
attr.attr.name()
);
}
}
if let Err(e) = attr.move_to_zbus(connection).await {
error!("Failed to register attribute '{attr_name}' on zbus: {e:?}");
continue;
}
} else {
error!("zbus enabled but no Connection provided for attribute registration");
}
}
registry.push(registry_attr);
} }
Ok(()) Ok(registry)
} }
pub async fn set_config_or_default( pub async fn set_config_or_default(
attrs: &FirmwareAttributes, attrs: &FirmwareAttributes,
config: &mut Config, config: &mut Config,
power_plugged: bool, power_plugged: bool,
profile: PlatformProfile profile: PlatformProfile,
) { ) {
for attr in attrs.attributes().iter() { for attr in attrs.attributes().iter() {
let name: FirmwareAttribute = attr.name().into(); let name: FirmwareAttribute = attr.name().into();
if name.is_ppt() { if name.is_ppt() || name.is_dgpu() {
let tuning = config.select_tunings(power_plugged, profile); let tuning = config.select_tunings(power_plugged, profile);
if !tuning.enabled { if !tuning.enabled {
debug!("Tuning group is not enabled, skipping"); debug!("Tuning group is not enabled, skipping");
return; continue;
} }
// Determine once whether attribute is present and supports a writable range
let supported = attr.base_path_exists() && !attr_unsupported(attr);
if let Some(tune) = tuning.group.get(&name) { if let Some(tune) = tuning.group.get(&name) {
attr.set_current_value(&AttrValue::Integer(*tune)) if supported {
.map_err(|e| { attr.set_current_value(&AttrValue::Integer(*tune))
error!("Failed to set {}: {e}", <&str>::from(name)); .map_err(|e| {
}) error!("Failed to set {}: {e}", <&str>::from(name));
.ok(); })
} else { .ok();
let default = attr.default_value(); } else {
attr.set_current_value(default) debug!(
.map_err(|e| { "Skipping apply for {} in set_config_or_default because attribute missing or unsupported",
error!("Failed to set {}: {e}", <&str>::from(name)); <&str>::from(name)
})
.ok();
if let AttrValue::Integer(i) = default {
tuning.group.insert(name, *i);
info!(
"Set default tuning config for {} = {:?}",
<&str>::from(name),
i
); );
// config.write();
} }
} else {
// Only attempt to apply defaults when the attribute supports a range
if supported {
let default = attr.default_value();
attr.set_current_value(default)
.map_err(|e| {
error!("Failed to set {}: {e}", <&str>::from(name));
})
.ok();
if let AttrValue::Integer(i) = default {
tuning.group.insert(name, *i);
info!(
"Set default tuning config for {} = {:?}",
<&str>::from(name),
i
);
// config.write();
}
} else {
debug!(
"Skipping default apply for {} in set_config_or_default because attribute missing or unsupported",
<&str>::from(name)
);
}
}
} else {
// Handle non-PPT attributes (boolean and other settings)
if let Some(saved_value) = config.armoury_settings.get(&name) {
attr.set_current_value(&AttrValue::Integer(*saved_value))
.map_err(|e| {
error!("Failed to set {}: {e}", <&str>::from(name));
})
.ok();
info!(
"Restored armoury setting for {} = {:?}",
<&str>::from(name),
saved_value
);
} }
} }
} }
} }
// Internal helper to store a tuning value into the correct per-profile, per-power map.
// This centralizes the behavior so tests can validate storage semantics.
#[allow(dead_code)]
fn insert_tuning_value(
config: &mut Config,
on_ac: bool,
profile: PlatformProfile,
name: rog_platform::asus_armoury::FirmwareAttribute,
value: i32,
) {
let tuning = config.select_tunings(on_ac, profile);
if let Some(t) = tuning.group.get_mut(&name) {
*t = value;
} else {
tuning.group.insert(name, value);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::Config;
use rog_platform::asus_armoury::FirmwareAttribute;
use rog_platform::platform::PlatformProfile;
#[test]
fn insert_nv_tuning_is_per_profile_and_power() {
let mut cfg = Config::default();
let profile = PlatformProfile::Performance;
// Insert value for AC
insert_tuning_value(
&mut cfg,
true,
profile,
FirmwareAttribute::NvDynamicBoost,
7,
);
// Value should be present in ac_profile_tunings
let t_ac = cfg.select_tunings_ref(true, profile).unwrap();
assert_eq!(t_ac.group.get(&FirmwareAttribute::NvDynamicBoost), Some(&7));
// Insert separate value for DC
insert_tuning_value(
&mut cfg,
false,
profile,
FirmwareAttribute::NvDynamicBoost,
3,
);
let t_dc = cfg.select_tunings_ref(false, profile).unwrap();
assert_eq!(t_dc.group.get(&FirmwareAttribute::NvDynamicBoost), Some(&3));
}
#[test]
fn non_ppt_attribute_stores_in_armoury_settings() {
let mut cfg = Config::default();
// Non-PPT/dGPU attribute, e.g., BootSound
let name = FirmwareAttribute::BootSound;
// Simulate setting armoury setting
cfg.armoury_settings.insert(name, 1);
assert_eq!(cfg.armoury_settings.get(&name), Some(&1));
}
}

View File

@@ -4,7 +4,7 @@ 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::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -15,14 +15,14 @@ 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());
for ani in &config.system { for ani in &config.system {
@@ -68,7 +68,7 @@ pub struct AniMeConfig {
pub off_when_suspended: bool, pub off_when_suspended: bool,
pub off_when_lid_closed: bool, pub off_when_lid_closed: bool,
pub brightness_on_battery: Brightness, pub brightness_on_battery: Brightness,
pub builtin_anims: Animations pub builtin_anims: Animations,
} }
impl Default for AniMeConfig { impl Default for AniMeConfig {
@@ -87,7 +87,7 @@ impl Default for AniMeConfig {
off_when_suspended: true, off_when_suspended: true,
off_when_lid_closed: true, off_when_lid_closed: true,
brightness_on_battery: Brightness::Low, brightness_on_battery: Brightness::Low,
builtin_anims: Animations::default() builtin_anims: Animations::default(),
} }
} }
} }
@@ -118,7 +118,7 @@ impl From<&AniMeConfig> for DeviceState {
off_when_unplugged: config.off_when_unplugged, off_when_unplugged: config.off_when_unplugged,
off_when_suspended: config.off_when_suspended, off_when_suspended: config.off_when_suspended,
off_when_lid_closed: config.off_when_lid_closed, off_when_lid_closed: config.off_when_lid_closed,
brightness_on_battery: config.brightness_on_battery brightness_on_battery: config.brightness_on_battery,
} }
} }
} }
@@ -148,8 +148,8 @@ impl AniMeConfig {
time: AnimTime::Fade(Fade::new( time: AnimTime::Fade(Fade::new(
Duration::from_secs(2), Duration::from_secs(2),
Some(Duration::from_secs(2)), Some(Duration::from_secs(2)),
Duration::from_secs(2) Duration::from_secs(2),
)) )),
}, },
], ],
wake: vec![ wake: vec![
@@ -162,8 +162,8 @@ impl AniMeConfig {
time: AnimTime::Fade(Fade::new( time: AnimTime::Fade(Fade::new(
Duration::from_secs(2), Duration::from_secs(2),
Some(Duration::from_secs(2)), Some(Duration::from_secs(2)),
Duration::from_secs(2) Duration::from_secs(2),
)) )),
}, },
], ],
shutdown: vec![ shutdown: vec![
@@ -173,7 +173,7 @@ impl AniMeConfig {
angle: 0.0, angle: 0.0,
translation: Vec2::new(3.0, 2.0), translation: Vec2::new(3.0, 2.0),
brightness: 1.0, brightness: 1.0,
time: AnimTime::Infinite time: AnimTime::Infinite,
}, },
], ],
..Default::default() ..Default::default()

View File

@@ -11,7 +11,7 @@ use config_traits::StdConfig;
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use rog_anime::usb::{ use rog_anime::usb::{
pkt_flush, pkt_set_brightness, pkt_set_enable_display, pkt_set_enable_powersave_anim, pkt_flush, pkt_set_brightness, pkt_set_enable_display, pkt_set_enable_powersave_anim,
pkts_for_init, Brightness pkts_for_init, Brightness,
}; };
use rog_anime::{ActionData, AnimeDataBuffer, AnimePacketType}; use rog_anime::{ActionData, AnimeDataBuffer, AnimePacketType};
use rog_platform::hid_raw::HidRaw; use rog_platform::hid_raw::HidRaw;
@@ -30,14 +30,14 @@ pub struct AniMe {
// set to force thread to exit // set to force thread to exit
thread_exit: Arc<AtomicBool>, thread_exit: Arc<AtomicBool>,
// Set to false when the thread exits // Set to false when the thread exits
thread_running: Arc<AtomicBool> thread_running: Arc<AtomicBool>,
} }
impl AniMe { impl AniMe {
pub fn new( pub fn new(
hid: Option<Arc<Mutex<HidRaw>>>, hid: Option<Arc<Mutex<HidRaw>>>,
usb: Option<Arc<Mutex<USBRaw>>>, usb: Option<Arc<Mutex<USBRaw>>>,
config: Arc<Mutex<AniMeConfig>> config: Arc<Mutex<AniMeConfig>>,
) -> Self { ) -> Self {
Self { Self {
hid, hid,
@@ -45,7 +45,7 @@ impl AniMe {
config, config,
cache: AniMeConfigCached::default(), cache: AniMeConfigCached::default(),
thread_exit: Arc::new(AtomicBool::new(false)), thread_exit: Arc::new(AtomicBool::new(false)),
thread_running: Arc::new(AtomicBool::new(false)) thread_running: Arc::new(AtomicBool::new(false)),
} }
} }
@@ -106,7 +106,7 @@ impl AniMe {
pub async fn set_builtins_enabled( pub async fn set_builtins_enabled(
&self, &self,
enabled: bool, enabled: bool,
bright: Brightness bright: Brightness,
) -> Result<(), RogError> { ) -> Result<(), RogError> {
self.write_bytes(&pkt_set_enable_powersave_anim(enabled)) self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
.await?; .await?;

View File

@@ -5,7 +5,7 @@ use log::{debug, 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::object_server::SignalEmitter; use zbus::object_server::SignalEmitter;
@@ -41,7 +41,7 @@ impl AniMeZbus {
pub async fn start_tasks( pub async fn start_tasks(
mut self, mut self,
connection: &Connection, connection: &Connection,
path: OwnedObjectPath path: OwnedObjectPath,
) -> Result<(), RogError> { ) -> Result<(), RogError> {
// let task = zbus.clone(); // let task = zbus.clone();
self.reload() self.reload()
@@ -69,7 +69,11 @@ impl AniMeZbus {
/// it is restarted /// it is restarted
async fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> { async fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> {
let bright = self.0.config.lock().await.display_brightness; let bright = self.0.config.lock().await.display_brightness;
self.0.set_builtins_enabled(false, bright).await?; if self.0.config.lock().await.builtin_anims_enabled {
// This clears the display, causing flickers if done indiscriminately on every
// write. Therefore, we guard it behind a config check.
self.0.set_builtins_enabled(false, bright).await?;
}
self.0.thread_exit.store(true, Ordering::SeqCst); self.0.thread_exit.store(true, Ordering::SeqCst);
self.0.write_data_buffer(input).await.map_err(|err| { self.0.write_data_buffer(input).await.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err); warn!("ctrl_anime::run_animation:callback {}", err);
@@ -169,7 +173,7 @@ impl AniMeZbus {
async fn set_builtin_animations(&self, settings: Animations) { async fn set_builtin_animations(&self, settings: Animations) {
self.0 self.0
.write_bytes(&pkt_set_builtin_animations( .write_bytes(&pkt_set_builtin_animations(
settings.boot, settings.awake, settings.sleep, settings.shutdown settings.boot, settings.awake, settings.sleep, settings.shutdown,
)) ))
.await .await
.map_err(|err| { .map_err(|err| {
@@ -319,7 +323,7 @@ impl crate::CtrlTask for AniMeZbus {
inner inner
.write_bytes(&pkt_set_enable_display( .write_bytes(&pkt_set_enable_display(
!(sleeping && config.off_when_suspended) !(sleeping && config.off_when_suspended),
)) ))
.await .await
.map_err(|err| { .map_err(|err| {
@@ -330,7 +334,7 @@ impl crate::CtrlTask for AniMeZbus {
if config.builtin_anims_enabled { if config.builtin_anims_enabled {
inner inner
.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 .await
.map_err(|err| { .map_err(|err| {
@@ -433,7 +437,7 @@ impl crate::CtrlTask for AniMeZbus {
.ok(); .ok();
} }
} }
} },
) )
.await; .await;
@@ -460,7 +464,7 @@ impl crate::Reloadable for AniMeZbus {
builtin_anims.boot, builtin_anims.boot,
builtin_anims.awake, builtin_anims.awake,
builtin_anims.sleep, builtin_anims.sleep,
builtin_anims.shutdown builtin_anims.shutdown,
)) ))
.await?; .await?;
} }

View File

@@ -5,7 +5,7 @@ use log::{debug, info, warn};
use rog_aura::aura_detection::LedSupportData; use rog_aura::aura_detection::LedSupportData;
use rog_aura::keyboard::LaptopAuraPower; use rog_aura::keyboard::LaptopAuraPower;
use rog_aura::{ use rog_aura::{
AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, Direction, LedBrightness, Speed, GRADIENT AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, Direction, LedBrightness, Speed, GRADIENT,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -29,7 +29,7 @@ pub struct AuraConfig {
pub multizone_on: bool, pub multizone_on: bool,
pub enabled: LaptopAuraPower, pub enabled: LaptopAuraPower,
#[serde(skip)] #[serde(skip)]
pub per_key_mode_active: bool pub per_key_mode_active: bool,
} }
impl StdConfig for AuraConfig { impl StdConfig for AuraConfig {
@@ -74,7 +74,7 @@ impl AuraConfig {
multizone: None, multizone: None,
multizone_on: false, multizone_on: false,
enabled, enabled,
per_key_mode_active: false per_key_mode_active: false,
}; };
for n in &config.support_data.basic_modes { for n in &config.support_data.basic_modes {
@@ -82,8 +82,9 @@ impl AuraConfig {
config config
.builtins .builtins
.insert(*n, AuraEffect::default_with_mode(*n)); .insert(*n, AuraEffect::default_with_mode(*n));
}
if !config.support_data.basic_zones.is_empty() { if !config.support_data.basic_zones.is_empty() {
for n in &config.support_data.basic_modes {
let mut default = vec![]; let mut default = vec![];
for (i, tmp) in config.support_data.basic_zones.iter().enumerate() { for (i, tmp) in config.support_data.basic_zones.iter().enumerate() {
default.push(AuraEffect { default.push(AuraEffect {
@@ -92,7 +93,7 @@ impl AuraConfig {
colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]), colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]),
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]), colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
speed: Speed::Med, speed: Speed::Med,
direction: Direction::Left direction: Direction::Left,
}); });
} }
if let Some(m) = config.multizone.as_mut() { if let Some(m) = config.multizone.as_mut() {
@@ -118,14 +119,14 @@ impl AuraConfig {
self.multizone_on = false; self.multizone_on = false;
} else { } else {
if let Some(multi) = self.multizone.as_mut() { if let Some(multi) = self.multizone.as_mut() {
if let Some(fx) = multi.get_mut(effect.mode()) { if let Some(fx_vec) = multi.get_mut(effect.mode()) {
for fx in fx.iter_mut() { for fx in fx_vec.iter_mut() {
if fx.zone == effect.zone { if fx.zone == effect.zone {
*fx = effect; *fx = effect;
return; return;
} }
} }
fx.push(effect); fx_vec.push(effect);
} else { } else {
multi.insert(*effect.mode(), vec![effect]); multi.insert(*effect.mode(), vec![effect]);
} }
@@ -156,7 +157,7 @@ impl AuraConfig {
colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]), colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]),
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]), colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
speed: Speed::Med, speed: Speed::Med,
direction: Direction::Left direction: Direction::Left,
}); });
} }
if default.is_empty() { if default.is_empty() {
@@ -230,15 +231,29 @@ impl AuraConfig {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::sync::{Mutex, MutexGuard, OnceLock};
use rog_aura::keyboard::AuraPowerState; use rog_aura::keyboard::AuraPowerState;
use rog_aura::{ use rog_aura::{
AuraEffect, AuraModeNum, AuraZone, Colour, Direction, LedBrightness, PowerZones, Speed AuraEffect, AuraModeNum, AuraZone, Colour, Direction, LedBrightness, PowerZones, Speed,
}; };
use super::AuraConfig; use super::AuraConfig;
// Global mutex to serialize tests that rely on process-wide environment
// variables
static TEST_MUTEX: OnceLock<Mutex<()>> = OnceLock::new();
fn test_lock() -> MutexGuard<'static, ()> {
TEST_MUTEX
.get_or_init(|| Mutex::new(()))
.lock()
.expect("TEST_MUTEX poisoned")
}
#[test] #[test]
fn set_multizone_4key_config() { fn set_multizone_4key_config() {
let _guard = test_lock();
std::env::set_var("BOARD_NAME", ""); std::env::set_var("BOARD_NAME", "");
let mut config = AuraConfig::new("19b6"); let mut config = AuraConfig::new("19b6");
@@ -246,7 +261,7 @@ mod tests {
colour1: Colour { colour1: Colour {
r: 0xff, r: 0xff,
g: 0x00, g: 0x00,
b: 0xff b: 0xff,
}, },
zone: AuraZone::Key1, zone: AuraZone::Key1,
..Default::default() ..Default::default()
@@ -259,7 +274,7 @@ mod tests {
colour1: Colour { colour1: Colour {
r: 0x00, r: 0x00,
g: 0xff, g: 0xff,
b: 0xff b: 0xff,
}, },
zone: AuraZone::Key2, zone: AuraZone::Key2,
..Default::default() ..Default::default()
@@ -270,7 +285,7 @@ mod tests {
colour1: Colour { colour1: Colour {
r: 0xff, r: 0xff,
g: 0xff, g: 0xff,
b: 0x00 b: 0x00,
}, },
zone: AuraZone::Key3, zone: AuraZone::Key3,
..Default::default() ..Default::default()
@@ -281,7 +296,7 @@ mod tests {
colour1: Colour { colour1: Colour {
r: 0x00, r: 0x00,
g: 0xff, g: 0xff,
b: 0x00 b: 0x00,
}, },
zone: AuraZone::Key4, zone: AuraZone::Key4,
..Default::default() ..Default::default()
@@ -294,30 +309,43 @@ mod tests {
let res = config.multizone.unwrap(); let res = config.multizone.unwrap();
let sta = res.get(&AuraModeNum::Static).unwrap(); let sta = res.get(&AuraModeNum::Static).unwrap();
assert_eq!(sta.len(), 4); assert_eq!(sta.len(), 4);
assert_eq!(sta[0].colour1, Colour { assert_eq!(
r: 0xff, sta[0].colour1,
g: 0x00, Colour {
b: 0xff r: 0xff,
}); g: 0x00,
assert_eq!(sta[1].colour1, Colour { b: 0xff
r: 0x00, }
g: 0xff, );
b: 0xff assert_eq!(
}); sta[1].colour1,
assert_eq!(sta[2].colour1, Colour { Colour {
r: 0xff, r: 0x00,
g: 0xff, g: 0xff,
b: 0x00 b: 0xff
}); }
assert_eq!(sta[3].colour1, Colour { );
r: 0x00, assert_eq!(
g: 0xff, sta[2].colour1,
b: 0x00 Colour {
}); r: 0xff,
g: 0xff,
b: 0x00
}
);
assert_eq!(
sta[3].colour1,
Colour {
r: 0x00,
g: 0xff,
b: 0x00
}
);
} }
#[test] #[test]
fn set_multizone_multimode_config() { fn set_multizone_multimode_config() {
let _guard = test_lock();
std::env::set_var("BOARD_NAME", ""); std::env::set_var("BOARD_NAME", "");
let mut config = AuraConfig::new("19b6"); let mut config = AuraConfig::new("19b6");
@@ -366,51 +394,65 @@ mod tests {
#[test] #[test]
fn verify_0x1866_g531i() { fn verify_0x1866_g531i() {
let _guard = test_lock();
std::env::set_var("BOARD_NAME", "G513I"); std::env::set_var("BOARD_NAME", "G513I");
let mut config = AuraConfig::new("1866"); let mut config = AuraConfig::new("1866");
assert_eq!(config.brightness, LedBrightness::Med); assert_eq!(config.brightness, LedBrightness::Med);
assert_eq!(config.builtins.len(), 5); assert_eq!(config.builtins.len(), 5);
assert_eq!(config.builtins.first_entry().unwrap().get(), &AuraEffect { assert_eq!(
mode: AuraModeNum::Static, config.builtins.first_entry().unwrap().get(),
zone: AuraZone::None, &AuraEffect {
colour1: Colour { r: 166, g: 0, b: 0 }, mode: AuraModeNum::Static,
colour2: Colour { r: 0, g: 0, b: 0 }, zone: AuraZone::None,
speed: Speed::Med, colour1: Colour { r: 166, g: 0, b: 0 },
direction: Direction::Right 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.len(), 1);
assert_eq!(config.enabled.states[0], AuraPowerState { assert_eq!(
zone: PowerZones::KeyboardAndLightbar, config.enabled.states[0],
boot: true, AuraPowerState {
awake: true, zone: PowerZones::KeyboardAndLightbar,
sleep: true, boot: true,
shutdown: true awake: true,
}); sleep: true,
shutdown: true
}
);
} }
#[test] #[test]
fn verify_0x19b6_g634j() { fn verify_0x19b6_g634j() {
let _guard = test_lock();
std::env::set_var("BOARD_NAME", "G634J"); std::env::set_var("BOARD_NAME", "G634J");
let mut config = AuraConfig::new("19b6"); let mut config = AuraConfig::new("19b6");
assert_eq!(config.brightness, LedBrightness::Med); assert_eq!(config.brightness, LedBrightness::Med);
assert_eq!(config.builtins.len(), 12); assert_eq!(config.builtins.len(), 12);
assert_eq!(config.builtins.first_entry().unwrap().get(), &AuraEffect { assert_eq!(
mode: AuraModeNum::Static, config.builtins.first_entry().unwrap().get(),
zone: AuraZone::None, &AuraEffect {
colour1: Colour { r: 166, g: 0, b: 0 }, mode: AuraModeNum::Static,
colour2: Colour { r: 0, g: 0, b: 0 }, zone: AuraZone::None,
speed: Speed::Med, colour1: Colour { r: 166, g: 0, b: 0 },
direction: Direction::Right 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.len(), 4);
assert_eq!(config.enabled.states[0], AuraPowerState { assert_eq!(
zone: PowerZones::Keyboard, config.enabled.states[0],
boot: true, AuraPowerState {
awake: true, zone: PowerZones::Keyboard,
sleep: true, boot: true,
shutdown: true awake: true,
}); sleep: true,
shutdown: true
}
);
} }
} }

View File

@@ -19,7 +19,7 @@ pub mod trait_impls;
pub struct Aura { pub struct Aura {
pub hid: Option<Arc<Mutex<HidRaw>>>, pub hid: Option<Arc<Mutex<HidRaw>>>,
pub backlight: Option<Arc<Mutex<KeyboardBacklight>>>, pub backlight: Option<Arc<Mutex<KeyboardBacklight>>>,
pub config: Arc<Mutex<AuraConfig>> pub config: Arc<Mutex<AuraConfig>>,
} }
impl Aura { impl Aura {
@@ -28,7 +28,7 @@ impl Aura {
Ok(()) Ok(())
} }
pub async fn lock_config(&self) -> MutexGuard<AuraConfig> { pub async fn lock_config(&self) -> MutexGuard<'_, AuraConfig> {
self.config.lock().await self.config.lock().await
} }
@@ -91,13 +91,13 @@ impl Aura {
pub async fn write_effect_and_apply( pub async fn write_effect_and_apply(
&self, &self,
dev_type: AuraDeviceType, dev_type: AuraDeviceType,
mode: &AuraEffect mode: &AuraEffect,
) -> Result<(), RogError> { ) -> Result<(), RogError> {
if matches!(dev_type, AuraDeviceType::LaptopKeyboardTuf) { if matches!(dev_type, AuraDeviceType::LaptopKeyboardTuf) {
if let Some(platform) = &self.backlight { if let Some(platform) = &self.backlight {
let buf = [ let buf = [
1, mode.mode as u8, mode.colour1.r, mode.colour1.g, mode.colour1.b, 1, mode.mode as u8, mode.colour1.r, mode.colour1.g, mode.colour1.b,
mode.speed as u8 mode.speed as u8,
]; ];
platform.lock().await.set_kbd_rgb_mode(&buf)?; platform.lock().await.set_kbd_rgb_mode(&buf)?;
} }
@@ -121,7 +121,7 @@ impl Aura {
return Ok(()); return Ok(());
} }
Err(RogError::MissingFunction( Err(RogError::MissingFunction(
"No LED backlight control available".to_string() "No LED backlight control available".to_string(),
)) ))
} }
@@ -145,7 +145,7 @@ impl Aura {
0x01, 0x01,
p.new_to_byte() as u8, p.new_to_byte() as u8,
0x0, 0x0,
0x0 0x0,
]; ];
hid_raw.write_bytes(&msg)?; hid_raw.write_bytes(&msg)?;
return Ok(()); return Ok(());
@@ -154,7 +154,7 @@ impl Aura {
let bytes = config.enabled.to_bytes(config.led_type); let bytes = config.enabled.to_bytes(config.led_type);
let msg = [ let msg = [
0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2], bytes[3] 0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2], bytes[3],
]; ];
hid_raw.write_bytes(&msg)?; hid_raw.write_bytes(&msg)?;
} }
@@ -167,7 +167,7 @@ impl Aura {
pub async fn write_effect_block( pub async fn write_effect_block(
&self, &self,
config: &mut AuraConfig, config: &mut AuraConfig,
effect: &AuraLaptopUsbPackets effect: &AuraLaptopUsbPackets,
) -> Result<(), RogError> { ) -> Result<(), RogError> {
if config.brightness == LedBrightness::Off { if config.brightness == LedBrightness::Off {
config.brightness = LedBrightness::Med; config.brightness = LedBrightness::Med;
@@ -201,7 +201,7 @@ impl Aura {
let g = row[10]; let g = row[10];
let b = row[11]; let b = row[11];
tuf.lock().await.set_kbd_rgb_mode(&[ tuf.lock().await.set_kbd_rgb_mode(&[
0, 0, r, g, b, 0 0, 0, r, g, b, 0,
])?; ])?;
} }
} }
@@ -215,7 +215,7 @@ impl Aura {
let mut config = self.config.lock().await; let mut config = self.config.lock().await;
if config.ally_fix.is_none() { if config.ally_fix.is_none() {
let msg = [ let msg = [
0x5d, 0xbd, 0x01, 0xff, 0xff, 0xff, 0xff 0x5d, 0xbd, 0x01, 0xff, 0xff, 0xff, 0xff,
]; ];
hid_raw.lock().await.write_bytes(&msg)?; hid_raw.lock().await.write_bytes(&msg)?;
info!("Reset Ally power settings to base"); info!("Reset Ally power settings to base");

View File

@@ -28,7 +28,7 @@ impl AuraZbus {
mut self, mut self,
connection: &Connection, connection: &Connection,
// _signal_ctx: SignalEmitter<'static>, // _signal_ctx: SignalEmitter<'static>,
path: OwnedObjectPath path: OwnedObjectPath,
) -> Result<(), RogError> { ) -> Result<(), RogError> {
// let task = zbus.clone(); // let task = zbus.clone();
// let signal_ctx = signal_ctx.clone(); // let signal_ctx = signal_ctx.clone();
@@ -144,7 +144,7 @@ impl AuraZbus {
let mode = config.current_mode; let mode = config.current_mode;
match config.builtins.get(&mode) { match config.builtins.get(&mode) {
Some(effect) => Ok(effect.clone()), Some(effect) => Ok(effect.clone()),
None => Err(ZbErr::Failed("Could not get the current effect".into())) None => Err(ZbErr::Failed("Could not get the current effect".into())),
} }
} else { } else {
Err(ZbErr::Failed("Aura control couldn't lock self".to_string())) Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
@@ -297,7 +297,7 @@ impl CtrlTask for AuraZbus {
move |_power_plugged| { move |_power_plugged| {
// power change // power change
async move {} async move {}
} },
) )
.await; .await;

View File

@@ -4,6 +4,7 @@
// - Add it to Zbus server // - Add it to Zbus server
// - If udev sees device removed then remove the zbus path // - If udev sees device removed then remove the zbus path
use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use dmi_id::DMIID; use dmi_id::DMIID;
@@ -56,7 +57,7 @@ fn dbus_path_for_dev(parent: &Device) -> Option<OwnedObjectPath> {
if let Some(filename) = filename_partial(parent) { if let Some(filename) = filename_partial(parent) {
return Some( return Some(
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{filename}")) ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{filename}"))
.into() .into(),
); );
} }
None None
@@ -91,17 +92,42 @@ fn dev_prop_matches(dev: &Device, prop: &str, value: &str) -> bool {
/// required. /// required.
pub struct AsusDevice { pub struct AsusDevice {
device: DeviceHandle, device: DeviceHandle,
dbus_path: OwnedObjectPath dbus_path: OwnedObjectPath,
hid_key: Option<String>,
} }
/// Shared alias for the HidRaw handle map used throughout this module.
type HidHandleMap = Arc<Mutex<HashMap<String, Arc<Mutex<HidRaw>>>>>;
pub struct DeviceManager { pub struct DeviceManager {
_dbus_connection: Connection _dbus_connection: Connection,
_hid_handles: HidHandleMap,
} }
impl DeviceManager { impl DeviceManager {
async fn get_or_create_hid_handle(
handles: &HidHandleMap,
endpoint: &Device,
) -> Result<(Arc<Mutex<HidRaw>>, String), RogError> {
let dev_node = endpoint
.devnode()
.ok_or_else(|| RogError::MissingFunction("hidraw devnode missing".to_string()))?;
let key = dev_node.to_string_lossy().to_string();
if let Some(existing) = handles.lock().await.get(&key).cloned() {
return Ok((existing, key));
}
let hidraw = HidRaw::from_device(endpoint.clone())?;
let handle = Arc::new(Mutex::new(hidraw));
handles.lock().await.insert(key.clone(), handle.clone());
Ok((handle, key))
}
async fn init_hid_devices( async fn init_hid_devices(
connection: &Connection, connection: &Connection,
device: Device device: Device,
handles: HidHandleMap,
) -> Result<Vec<AsusDevice>, RogError> { ) -> Result<Vec<AsusDevice>, RogError> {
let mut devices = Vec::new(); let mut devices = Vec::new();
if let Some(usb_device) = device.parent_with_subsystem_devtype("usb", "usb_device")? { if let Some(usb_device) = device.parent_with_subsystem_devtype("usb", "usb_device")? {
@@ -116,13 +142,14 @@ impl DeviceManager {
// 1. Generate an interface path // 1. Generate an interface path
// 2. Create the device // 2. Create the device
// Use the top-level endpoint, not the parent // Use the top-level endpoint, not the parent
if let Ok(hidraw) = HidRaw::from_device(device) { if let Ok((dev, hid_key)) =
Self::get_or_create_hid_handle(&handles, &device).await
{
debug!("Testing device {usb_id:?}"); debug!("Testing device {usb_id:?}");
let dev = Arc::new(Mutex::new(hidraw));
// SLASH DEVICE // SLASH DEVICE
if let Ok(dev_type) = DeviceHandle::new_slash_hid( if let Ok(dev_type) = DeviceHandle::new_slash_hid(
dev.clone(), dev.clone(),
usb_id.to_str().unwrap_or_default() usb_id.to_str().unwrap_or_default(),
) )
.await .await
{ {
@@ -133,14 +160,15 @@ impl DeviceManager {
ctrl.start_tasks(connection, path.clone()).await.unwrap(); ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice { devices.push(AsusDevice {
device: dev_type, device: dev_type,
dbus_path: path dbus_path: path,
hid_key: Some(hid_key.clone()),
}); });
} }
} }
// ANIME MATRIX DEVICE // ANIME MATRIX DEVICE
if let Ok(dev_type) = DeviceHandle::maybe_anime_hid( if let Ok(dev_type) = DeviceHandle::maybe_anime_hid(
dev.clone(), dev.clone(),
usb_id.to_str().unwrap_or_default() usb_id.to_str().unwrap_or_default(),
) )
.await .await
{ {
@@ -151,14 +179,15 @@ impl DeviceManager {
ctrl.start_tasks(connection, path.clone()).await.unwrap(); ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice { devices.push(AsusDevice {
device: dev_type, device: dev_type,
dbus_path: path dbus_path: path,
hid_key: Some(hid_key.clone()),
}); });
} }
} }
// AURA LAPTOP DEVICE // AURA LAPTOP DEVICE
if let Ok(dev_type) = DeviceHandle::maybe_laptop_aura( if let Ok(dev_type) = DeviceHandle::maybe_laptop_aura(
Some(dev), Some(dev),
usb_id.to_str().unwrap_or_default() usb_id.to_str().unwrap_or_default(),
) )
.await .await
{ {
@@ -169,10 +198,13 @@ impl DeviceManager {
ctrl.start_tasks(connection, path.clone()).await.unwrap(); ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice { devices.push(AsusDevice {
device: dev_type, device: dev_type,
dbus_path: path dbus_path: path,
hid_key: Some(hid_key),
}); });
} }
} }
} else {
warn!("Failed to initialise shared hid handle for {usb_id:?}");
} }
} }
} }
@@ -181,7 +213,10 @@ impl DeviceManager {
} }
/// To be called on daemon startup /// To be called on daemon startup
async fn init_all_hid(connection: &Connection) -> Result<Vec<AsusDevice>, RogError> { async fn init_all_hid(
connection: &Connection,
handles: HidHandleMap,
) -> Result<Vec<AsusDevice>, RogError> {
// track and ensure we use only one hidraw per prod_id // track and ensure we use only one hidraw per prod_id
// let mut interfaces = HashSet::new(); // let mut interfaces = HashSet::new();
let mut devices: Vec<AsusDevice> = Vec::new(); let mut devices: Vec<AsusDevice> = Vec::new();
@@ -200,7 +235,7 @@ impl DeviceManager {
.scan_devices() .scan_devices()
.map_err(|e| PlatformError::IoPath("enumerator".to_owned(), e))? .map_err(|e| PlatformError::IoPath("enumerator".to_owned(), e))?
{ {
devices.append(&mut Self::init_hid_devices(connection, device).await?); devices.append(&mut Self::init_hid_devices(connection, device, handles.clone()).await?);
} }
Ok(devices) Ok(devices)
@@ -209,7 +244,7 @@ impl DeviceManager {
async fn init_scsi( async fn init_scsi(
connection: &Connection, connection: &Connection,
device: &Device, device: &Device,
path: OwnedObjectPath path: OwnedObjectPath,
) -> Option<AsusDevice> { ) -> Option<AsusDevice> {
// "ID_MODEL_ID" "1932" // "ID_MODEL_ID" "1932"
// "ID_VENDOR_ID" "0b05" // "ID_VENDOR_ID" "0b05"
@@ -227,7 +262,8 @@ impl DeviceManager {
ctrl.start_tasks(connection, path.clone()).await.unwrap(); ctrl.start_tasks(connection, path.clone()).await.unwrap();
return Some(AsusDevice { return Some(AsusDevice {
device: dev_type, device: dev_type,
dbus_path: path dbus_path: path,
hid_key: None,
}); });
} }
} }
@@ -275,10 +311,13 @@ impl DeviceManager {
Ok(devices) Ok(devices)
} }
pub async fn find_all_devices(connection: &Connection) -> Vec<AsusDevice> { pub async fn find_all_devices(
connection: &Connection,
handles: Arc<Mutex<HashMap<String, Arc<Mutex<HidRaw>>>>>,
) -> Vec<AsusDevice> {
let mut devices: Vec<AsusDevice> = Vec::new(); let mut devices: Vec<AsusDevice> = Vec::new();
// HID first, always // HID first, always
if let Ok(devs) = &mut Self::init_all_hid(connection).await { if let Ok(devs) = &mut Self::init_all_hid(connection, handles.clone()).await {
devices.append(devs); devices.append(devs);
} }
// USB after, need to check if HID picked something up and if so, skip it // USB after, need to check if HID picked something up and if so, skip it
@@ -305,7 +344,8 @@ impl DeviceManager {
ctrl.start_tasks(connection, path.clone()).await.unwrap(); ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice { devices.push(AsusDevice {
device: dev_type, device: dev_type,
dbus_path: path dbus_path: path,
hid_key: None,
}); });
} }
} else { } else {
@@ -327,7 +367,8 @@ impl DeviceManager {
{ {
devices.push(AsusDevice { devices.push(AsusDevice {
device: dev_type, device: dev_type,
dbus_path: path dbus_path: path,
hid_key: None,
}); });
} }
} }
@@ -354,7 +395,8 @@ impl DeviceManager {
ctrl.start_tasks(connection, path.clone()).await.unwrap(); ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice { devices.push(AsusDevice {
device: dev_type, device: dev_type,
dbus_path: path dbus_path: path,
hid_key: None,
}); });
} }
} }
@@ -370,16 +412,19 @@ impl DeviceManager {
pub async fn new(connection: Connection) -> Result<Self, RogError> { pub async fn new(connection: Connection) -> Result<Self, RogError> {
let conn_copy = connection.clone(); let conn_copy = connection.clone();
let devices = Self::find_all_devices(&conn_copy).await; let hid_handles = Arc::new(Mutex::new(HashMap::new()));
let devices = Self::find_all_devices(&conn_copy, hid_handles.clone()).await;
info!("Found {} valid devices on startup", devices.len()); info!("Found {} valid devices on startup", devices.len());
let devices = Arc::new(Mutex::new(devices)); let devices = Arc::new(Mutex::new(devices));
let manager = Self { let manager = Self {
_dbus_connection: connection _dbus_connection: connection,
_hid_handles: hid_handles.clone(),
}; };
// TODO: The /sysfs/ LEDs don't cause events, so they need to be manually // TODO: The /sysfs/ LEDs don't cause events, so they need to be manually
// checked for and added // checked for and added
let hid_handles_thread = hid_handles.clone();
std::thread::spawn(move || { std::thread::spawn(move || {
let mut monitor = MonitorBuilder::new()?.listen()?; let mut monitor = MonitorBuilder::new()?.listen()?;
let mut poll = Poll::new()?; let mut poll = Poll::new()?;
@@ -408,6 +453,7 @@ impl DeviceManager {
let devices = devices.clone(); let devices = devices.clone();
let conn_copy = conn_copy.clone(); let conn_copy = conn_copy.clone();
let hid_handles = hid_handles_thread.clone();
block_on(async move { block_on(async move {
// SCSCI devs // SCSCI devs
if subsys == "block" { if subsys == "block" {
@@ -483,6 +529,7 @@ impl DeviceManager {
// Iter in reverse so as to not screw up indexing // Iter in reverse so as to not screw up indexing
for index in removals.iter().rev() { for index in removals.iter().rev() {
let dev = devices.lock().await.remove(*index); let dev = devices.lock().await.remove(*index);
let hid_key = dev.hid_key.clone();
let path = path.clone(); let path = path.clone();
let res = match dev.device { let res = match dev.device {
DeviceHandle::Aura(_) => { DeviceHandle::Aura(_) => {
@@ -509,17 +556,23 @@ impl DeviceManager {
.remove::<ScsiZbus, _>(&path) .remove::<ScsiZbus, _>(&path)
.await? .await?
} }
_ => todo!() _ => todo!(),
}; };
info!("AuraManager removed: {path:?}, {res}"); info!("AuraManager removed: {path:?}, {res}");
if let Some(key) = hid_key {
hid_handles.lock().await.remove(&key);
}
} }
} }
} else if action == "add" { } else if action == "add" {
let evdev = event.device(); let evdev = event.device();
if let Ok(mut new_devs) = if let Ok(mut new_devs) = Self::init_hid_devices(
Self::init_hid_devices(&conn_copy, evdev) &conn_copy,
.await evdev,
.map_err(|e| error!("Couldn't add new device: {e:?}")) hid_handles.clone(),
)
.await
.map_err(|e| error!("Couldn't add new device: {e:?}"))
{ {
devices.lock().await.append(&mut new_devs); devices.lock().await.append(&mut new_devs);
} }

View File

@@ -14,7 +14,7 @@ pub struct ScsiConfig {
pub dev_type: AuraDeviceType, pub dev_type: AuraDeviceType,
pub enabled: bool, pub enabled: bool,
pub current_mode: AuraMode, pub current_mode: AuraMode,
pub modes: BTreeMap<AuraMode, AuraEffect> pub modes: BTreeMap<AuraMode, AuraEffect>,
} }
impl ScsiConfig { impl ScsiConfig {
@@ -38,61 +38,61 @@ impl Default for ScsiConfig {
(AuraMode::Off, AuraEffect::default_with_mode(AuraMode::Off)), (AuraMode::Off, AuraEffect::default_with_mode(AuraMode::Off)),
( (
AuraMode::Static, AuraMode::Static,
AuraEffect::default_with_mode(AuraMode::Static) AuraEffect::default_with_mode(AuraMode::Static),
), ),
( (
AuraMode::Breathe, AuraMode::Breathe,
AuraEffect::default_with_mode(AuraMode::Breathe) AuraEffect::default_with_mode(AuraMode::Breathe),
), ),
( (
AuraMode::Flashing, AuraMode::Flashing,
AuraEffect::default_with_mode(AuraMode::Flashing) AuraEffect::default_with_mode(AuraMode::Flashing),
), ),
( (
AuraMode::RainbowCycle, AuraMode::RainbowCycle,
AuraEffect::default_with_mode(AuraMode::RainbowCycle) AuraEffect::default_with_mode(AuraMode::RainbowCycle),
), ),
( (
AuraMode::RainbowWave, AuraMode::RainbowWave,
AuraEffect::default_with_mode(AuraMode::RainbowWave) AuraEffect::default_with_mode(AuraMode::RainbowWave),
), ),
( (
AuraMode::RainbowCycleBreathe, AuraMode::RainbowCycleBreathe,
AuraEffect::default_with_mode(AuraMode::RainbowCycleBreathe) AuraEffect::default_with_mode(AuraMode::RainbowCycleBreathe),
), ),
( (
AuraMode::ChaseFade, AuraMode::ChaseFade,
AuraEffect::default_with_mode(AuraMode::ChaseFade) AuraEffect::default_with_mode(AuraMode::ChaseFade),
), ),
( (
AuraMode::RainbowCycleChaseFade, AuraMode::RainbowCycleChaseFade,
AuraEffect::default_with_mode(AuraMode::RainbowCycleChaseFade) AuraEffect::default_with_mode(AuraMode::RainbowCycleChaseFade),
), ),
( (
AuraMode::Chase, AuraMode::Chase,
AuraEffect::default_with_mode(AuraMode::Chase) AuraEffect::default_with_mode(AuraMode::Chase),
), ),
( (
AuraMode::RainbowCycleChase, AuraMode::RainbowCycleChase,
AuraEffect::default_with_mode(AuraMode::RainbowCycleChase) AuraEffect::default_with_mode(AuraMode::RainbowCycleChase),
), ),
( (
AuraMode::RainbowCycleWave, AuraMode::RainbowCycleWave,
AuraEffect::default_with_mode(AuraMode::RainbowCycleWave) AuraEffect::default_with_mode(AuraMode::RainbowCycleWave),
), ),
( (
AuraMode::RainbowPulseChase, AuraMode::RainbowPulseChase,
AuraEffect::default_with_mode(AuraMode::RainbowPulseChase) AuraEffect::default_with_mode(AuraMode::RainbowPulseChase),
), ),
( (
AuraMode::RandomFlicker, AuraMode::RandomFlicker,
AuraEffect::default_with_mode(AuraMode::RandomFlicker) AuraEffect::default_with_mode(AuraMode::RandomFlicker),
), ),
( (
AuraMode::DoubleFade, AuraMode::DoubleFade,
AuraEffect::default_with_mode(AuraMode::DoubleFade) AuraEffect::default_with_mode(AuraMode::DoubleFade),
) ),
]) ]),
} }
} }
} }

View File

@@ -12,7 +12,7 @@ pub mod trait_impls;
#[derive(Clone)] #[derive(Clone)]
pub struct ScsiAura { pub struct ScsiAura {
device: Arc<Mutex<Device>>, device: Arc<Mutex<Device>>,
config: Arc<Mutex<ScsiConfig>> config: Arc<Mutex<ScsiConfig>>,
} }
impl ScsiAura { impl ScsiAura {
@@ -20,7 +20,7 @@ impl ScsiAura {
Self { device, config } Self { device, config }
} }
pub async fn lock_config(&self) -> MutexGuard<ScsiConfig> { pub async fn lock_config(&self) -> MutexGuard<'_, ScsiConfig> {
self.config.lock().await self.config.lock().await
} }

View File

@@ -22,7 +22,7 @@ impl ScsiZbus {
pub async fn start_tasks( pub async fn start_tasks(
self, self,
connection: &Connection, connection: &Connection,
path: OwnedObjectPath path: OwnedObjectPath,
) -> Result<(), RogError> { ) -> Result<(), RogError> {
connection connection
.object_server() .object_server()
@@ -87,7 +87,7 @@ impl ScsiZbus {
let mode = config.current_mode; let mode = config.current_mode;
match config.modes.get(&mode) { match config.modes.get(&mode) {
Some(effect) => Ok(effect.clone()), Some(effect) => Ok(effect.clone()),
None => Err(ZbErr::Failed("Could not get the current effect".into())) None => Err(ZbErr::Failed("Could not get the current effect".into())),
} }
} else { } else {
Err(ZbErr::Failed("Aura control couldn't lock self".to_string())) Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))

View File

@@ -17,7 +17,8 @@ pub struct SlashConfig {
pub show_on_shutdown: bool, pub show_on_shutdown: bool,
pub show_on_sleep: bool, pub show_on_sleep: bool,
pub show_on_battery: bool, pub show_on_battery: bool,
pub show_battery_warning: bool pub show_battery_warning: bool,
pub show_on_lid_closed: bool,
} }
impl Default for SlashConfig { impl Default for SlashConfig {
@@ -32,7 +33,8 @@ impl Default for SlashConfig {
show_on_shutdown: true, show_on_shutdown: true,
show_on_sleep: true, show_on_sleep: true,
show_on_battery: true, show_on_battery: true,
show_battery_warning: true show_battery_warning: true,
show_on_lid_closed: true,
} }
} }
} }
@@ -58,7 +60,7 @@ impl From<&SlashConfig> for DeviceState {
slash_enabled: config.enabled, slash_enabled: config.enabled,
slash_brightness: config.brightness, slash_brightness: config.brightness,
slash_interval: config.display_interval, slash_interval: config.display_interval,
slash_mode: config.display_mode slash_mode: config.display_mode,
} }
} }
} }

View File

@@ -3,8 +3,7 @@ use std::sync::Arc;
use config::SlashConfig; use config::SlashConfig;
use rog_platform::hid_raw::HidRaw; use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw; use rog_platform::usb_raw::USBRaw;
use rog_slash::usb::{get_options_packet, pkt_set_mode, pkts_for_init}; use rog_slash::usb::{slash_pkt_enable, slash_pkt_init, slash_pkt_options, slash_pkt_set_mode};
use rog_slash::SlashType;
use tokio::sync::{Mutex, MutexGuard}; use tokio::sync::{Mutex, MutexGuard};
use crate::error::RogError; use crate::error::RogError;
@@ -16,19 +15,19 @@ pub mod trait_impls;
pub struct Slash { pub struct Slash {
hid: Option<Arc<Mutex<HidRaw>>>, hid: Option<Arc<Mutex<HidRaw>>>,
usb: Option<Arc<Mutex<USBRaw>>>, usb: Option<Arc<Mutex<USBRaw>>>,
config: Arc<Mutex<SlashConfig>> config: Arc<Mutex<SlashConfig>>,
} }
impl Slash { impl Slash {
pub fn new( pub fn new(
hid: Option<Arc<Mutex<HidRaw>>>, hid: Option<Arc<Mutex<HidRaw>>>,
usb: Option<Arc<Mutex<USBRaw>>>, usb: Option<Arc<Mutex<USBRaw>>>,
config: Arc<Mutex<SlashConfig>> config: Arc<Mutex<SlashConfig>>,
) -> Self { ) -> Self {
Self { hid, usb, config } Self { hid, usb, config }
} }
pub async fn lock_config(&self) -> MutexGuard<SlashConfig> { pub async fn lock_config(&self) -> MutexGuard<'_, SlashConfig> {
self.config.lock().await self.config.lock().await
} }
@@ -46,22 +45,22 @@ impl Slash {
pub async fn do_initialization(&self) -> Result<(), RogError> { pub async fn do_initialization(&self) -> Result<(), RogError> {
// Don't try to initialise these models as the asus drivers already did // Don't try to initialise these models as the asus drivers already did
let config = self.config.lock().await; let config = self.config.lock().await;
if !matches!(config.slash_type, SlashType::GA605 | SlashType::GU605) { for pkt in &slash_pkt_init(config.slash_type) {
for pkt in &pkts_for_init(config.slash_type) { self.write_bytes(pkt).await?;
self.write_bytes(pkt).await?;
}
} }
self.write_bytes(&slash_pkt_enable(config.slash_type, config.enabled))
.await?;
// Apply config upon initialization // Apply config upon initialization
let option_packets = get_options_packet( let option_packets = slash_pkt_options(
config.slash_type, config.slash_type,
config.enabled, config.enabled,
config.brightness, config.brightness,
config.display_interval config.display_interval,
); );
self.write_bytes(&option_packets).await?; self.write_bytes(&option_packets).await?;
let mode_packets = pkt_set_mode(config.slash_type, config.display_mode); let mode_packets = slash_pkt_set_mode(config.slash_type, config.display_mode);
// self.node.write_bytes(&mode_packets[0])?; // self.node.write_bytes(&mode_packets[0])?;
self.write_bytes(&mode_packets[1]).await?; self.write_bytes(&mode_packets[1]).await?;

View File

@@ -1,8 +1,9 @@
use config_traits::StdConfig; use config_traits::StdConfig;
use log::{debug, error, warn}; use log::{debug, error, warn};
use rog_slash::usb::{ use rog_slash::usb::{
get_battery_saver_packet, get_boot_packet, get_low_battery_packet, get_options_packet, slash_pkt_battery_saver, slash_pkt_boot, slash_pkt_enable, slash_pkt_lid_closed,
get_shutdown_packet, get_sleep_packet, pkt_save, pkt_set_mode slash_pkt_low_battery, slash_pkt_options, slash_pkt_save, slash_pkt_set_mode,
slash_pkt_shutdown, slash_pkt_sleep,
}; };
use rog_slash::{DeviceState, SlashMode}; use rog_slash::{DeviceState, SlashMode};
use zbus::zvariant::OwnedObjectPath; use zbus::zvariant::OwnedObjectPath;
@@ -23,7 +24,7 @@ impl SlashZbus {
pub async fn start_tasks( pub async fn start_tasks(
mut self, mut self,
connection: &Connection, connection: &Connection,
path: OwnedObjectPath path: OwnedObjectPath,
) -> Result<(), RogError> { ) -> Result<(), RogError> {
// let task = zbus.clone(); // let task = zbus.clone();
self.reload() self.reload()
@@ -58,11 +59,18 @@ impl SlashZbus {
config.brightness config.brightness
}; };
self.0 self.0
.write_bytes(&get_options_packet( .write_bytes(&slash_pkt_enable(config.slash_type, enabled))
.await
.map_err(|err| {
warn!("ctrl_slash::enable {}", err);
})
.ok();
self.0
.write_bytes(&slash_pkt_options(
config.slash_type, config.slash_type,
enabled, enabled,
brightness, brightness,
config.display_interval config.display_interval,
)) ))
.await .await
.map_err(|err| { .map_err(|err| {
@@ -88,11 +96,11 @@ impl SlashZbus {
let mut config = self.0.lock_config().await; let mut config = self.0.lock_config().await;
let enabled = brightness > 0; let enabled = brightness > 0;
self.0 self.0
.write_bytes(&get_options_packet( .write_bytes(&slash_pkt_options(
config.slash_type, config.slash_type,
enabled, enabled,
brightness, brightness,
config.display_interval config.display_interval,
)) ))
.await .await
.map_err(|err| { .map_err(|err| {
@@ -116,8 +124,8 @@ impl SlashZbus {
async fn set_interval(&self, interval: u8) { async fn set_interval(&self, interval: u8) {
let mut config = self.0.lock_config().await; let mut config = self.0.lock_config().await;
self.0 self.0
.write_bytes(&get_options_packet( .write_bytes(&slash_pkt_options(
config.slash_type, config.enabled, config.brightness, interval config.slash_type, config.enabled, config.brightness, interval,
)) ))
.await .await
.map_err(|err| { .map_err(|err| {
@@ -140,10 +148,12 @@ impl SlashZbus {
async fn set_mode(&self, mode: SlashMode) -> zbus::Result<()> { async fn set_mode(&self, mode: SlashMode) -> zbus::Result<()> {
let mut config = self.0.lock_config().await; let mut config = self.0.lock_config().await;
let command_packets = pkt_set_mode(config.slash_type, mode); let command_packets = slash_pkt_set_mode(config.slash_type, mode);
// self.node.write_bytes(&command_packets[0])?; // self.node.write_bytes(&command_packets[0])?;
self.0.write_bytes(&command_packets[1]).await?; self.0.write_bytes(&command_packets[1]).await?;
self.0.write_bytes(&pkt_save(config.slash_type)).await?; self.0
.write_bytes(&slash_pkt_save(config.slash_type))
.await?;
config.display_mode = mode; config.display_mode = mode;
config.write(); config.write();
@@ -167,7 +177,7 @@ impl SlashZbus {
async fn set_show_on_boot(&self, enable: bool) -> zbus::Result<()> { async fn set_show_on_boot(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await; let mut config = self.0.lock_config().await;
self.0 self.0
.write_bytes(&get_boot_packet(config.slash_type, enable)) .write_bytes(&slash_pkt_boot(config.slash_type, enable))
.await?; .await?;
config.show_on_boot = enable; config.show_on_boot = enable;
config.write(); config.write();
@@ -184,7 +194,7 @@ impl SlashZbus {
async fn set_show_on_sleep(&self, enable: bool) -> zbus::Result<()> { async fn set_show_on_sleep(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await; let mut config = self.0.lock_config().await;
self.0 self.0
.write_bytes(&get_sleep_packet(config.slash_type, enable)) .write_bytes(&slash_pkt_sleep(config.slash_type, enable))
.await?; .await?;
config.show_on_sleep = enable; config.show_on_sleep = enable;
config.write(); config.write();
@@ -201,7 +211,7 @@ impl SlashZbus {
async fn set_show_on_shutdown(&self, enable: bool) -> zbus::Result<()> { async fn set_show_on_shutdown(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await; let mut config = self.0.lock_config().await;
self.0 self.0
.write_bytes(&get_shutdown_packet(config.slash_type, enable)) .write_bytes(&slash_pkt_shutdown(config.slash_type, enable))
.await?; .await?;
config.show_on_shutdown = enable; config.show_on_shutdown = enable;
config.write(); config.write();
@@ -218,7 +228,7 @@ impl SlashZbus {
async fn set_show_on_battery(&self, enable: bool) -> zbus::Result<()> { async fn set_show_on_battery(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await; let mut config = self.0.lock_config().await;
self.0 self.0
.write_bytes(&get_battery_saver_packet(config.slash_type, enable)) .write_bytes(&slash_pkt_battery_saver(config.slash_type, enable))
.await?; .await?;
config.show_on_battery = enable; config.show_on_battery = enable;
config.write(); config.write();
@@ -235,12 +245,32 @@ impl SlashZbus {
async fn set_show_battery_warning(&self, enable: bool) -> zbus::Result<()> { async fn set_show_battery_warning(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await; let mut config = self.0.lock_config().await;
self.0 self.0
.write_bytes(&get_low_battery_packet(config.slash_type, enable)) .write_bytes(&slash_pkt_low_battery(config.slash_type, enable))
.await?; .await?;
config.show_battery_warning = enable; config.show_battery_warning = enable;
config.write(); config.write();
Ok(()) Ok(())
} }
#[zbus(property)]
async fn show_on_lid_closed(&self) -> zbus::fdo::Result<bool> {
let config = self.0.lock_config().await;
Ok(config.show_on_lid_closed)
}
#[zbus(property)]
async fn set_show_on_lid_closed(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&slash_pkt_lid_closed(config.slash_type, enable))
.await?;
self.0
.write_bytes(&slash_pkt_save(config.slash_type))
.await?;
config.show_on_lid_closed = enable;
config.write();
Ok(())
}
} }
impl Reloadable for SlashZbus { impl Reloadable for SlashZbus {
@@ -248,11 +278,11 @@ impl Reloadable for SlashZbus {
debug!("reloading slash settings"); debug!("reloading slash settings");
let config = self.0.lock_config().await; let config = self.0.lock_config().await;
self.0 self.0
.write_bytes(&get_options_packet( .write_bytes(&slash_pkt_options(
config.slash_type, config.slash_type,
config.enabled, config.enabled,
config.brightness, config.brightness,
config.display_interval config.display_interval,
)) ))
.await .await
.map_err(|err| { .map_err(|err| {
@@ -272,12 +302,12 @@ impl Reloadable for SlashZbus {
}; };
} }
write_bytes_with_warning!(get_boot_packet, show_on_boot, "show_on_boot"); write_bytes_with_warning!(slash_pkt_boot, show_on_boot, "show_on_boot");
write_bytes_with_warning!(get_sleep_packet, show_on_sleep, "show_on_sleep"); write_bytes_with_warning!(slash_pkt_sleep, show_on_sleep, "show_on_sleep");
write_bytes_with_warning!(get_shutdown_packet, show_on_shutdown, "show_on_shutdown"); write_bytes_with_warning!(slash_pkt_shutdown, show_on_shutdown, "show_on_shutdown");
write_bytes_with_warning!(get_battery_saver_packet, show_on_battery, "show_on_battery"); write_bytes_with_warning!(slash_pkt_battery_saver, show_on_battery, "show_on_battery");
write_bytes_with_warning!( write_bytes_with_warning!(
get_low_battery_packet, slash_pkt_low_battery,
show_battery_warning, show_battery_warning,
"show_battery_warning" "show_battery_warning"
); );

View File

@@ -31,7 +31,7 @@ pub enum _DeviceHandle {
LedClass(KeyboardBacklight), LedClass(KeyboardBacklight),
/// TODO /// TODO
MulticolourLed, MulticolourLed,
None None,
} }
#[derive(Clone)] #[derive(Clone)]
@@ -47,14 +47,14 @@ pub enum DeviceHandle {
TufLedClass(Arc<Mutex<HidRaw>>), TufLedClass(Arc<Mutex<HidRaw>>),
/// TODO /// TODO
MulticolourLed, MulticolourLed,
None None,
} }
impl DeviceHandle { impl DeviceHandle {
/// Try Slash HID. If one exists it is initialsed and returned. /// Try Slash HID. If one exists it is initialsed and returned.
pub async fn new_slash_hid( pub async fn new_slash_hid(
device: Arc<Mutex<HidRaw>>, device: Arc<Mutex<HidRaw>>,
prod_id: &str prod_id: &str,
) -> Result<Self, RogError> { ) -> Result<Self, RogError> {
debug!("Testing for HIDRAW Slash"); debug!("Testing for HIDRAW Slash");
let slash_type = SlashType::from_dmi(); let slash_type = SlashType::from_dmi();
@@ -93,7 +93,7 @@ impl DeviceHandle {
let slash = Slash::new( let slash = Slash::new(
None, None,
Some(Arc::new(Mutex::new(usb))), Some(Arc::new(Mutex::new(usb))),
Arc::new(Mutex::new(config)) Arc::new(Mutex::new(config)),
); );
slash.do_initialization().await?; slash.do_initialization().await?;
Ok(Self::Slash(slash)) Ok(Self::Slash(slash))
@@ -105,11 +105,11 @@ impl DeviceHandle {
/// Try AniMe Matrix HID. If one exists it is initialsed and returned. /// Try AniMe Matrix HID. If one exists it is initialsed and returned.
pub async fn maybe_anime_hid( pub async fn maybe_anime_hid(
_device: Arc<Mutex<HidRaw>>, _device: Arc<Mutex<HidRaw>>,
_prod_id: &str _prod_id: &str,
) -> Result<Self, RogError> { ) -> Result<Self, RogError> {
// TODO: can't use HIDRAW for anime at the moment // TODO: can't use HIDRAW for anime at the moment
Err(RogError::NotFound( Err(RogError::NotFound(
"Can't use anime over hidraw yet. Skip.".to_string() "Can't use anime over hidraw yet. Skip.".to_string(),
)) ))
// debug!("Testing for HIDRAW AniMe"); // debug!("Testing for HIDRAW AniMe");
@@ -144,13 +144,13 @@ impl DeviceHandle {
let mut anime = AniMe::new( let mut anime = AniMe::new(
None, None,
Some(Arc::new(Mutex::new(usb))), Some(Arc::new(Mutex::new(usb))),
Arc::new(Mutex::new(config)) Arc::new(Mutex::new(config)),
); );
anime.do_initialization().await?; anime.do_initialization().await?;
Ok(Self::AniMe(anime)) Ok(Self::AniMe(anime))
} else { } else {
Err(RogError::NotFound( Err(RogError::NotFound(
"No AnimeMatrix device found".to_string() "No AnimeMatrix device found".to_string(),
)) ))
} }
} }
@@ -174,7 +174,7 @@ impl DeviceHandle {
pub async fn maybe_laptop_aura( pub async fn maybe_laptop_aura(
device: Option<Arc<Mutex<HidRaw>>>, device: Option<Arc<Mutex<HidRaw>>>,
prod_id: &str prod_id: &str,
) -> Result<Self, RogError> { ) -> Result<Self, RogError> {
debug!("Testing for laptop aura"); debug!("Testing for laptop aura");
let aura_type = AuraDeviceType::from(prod_id); let aura_type = AuraDeviceType::from(prod_id);
@@ -201,7 +201,7 @@ impl DeviceHandle {
let aura = Aura { let aura = Aura {
hid: device, hid: device,
backlight, backlight,
config: Arc::new(Mutex::new(config)) config: Arc::new(Mutex::new(config)),
}; };
aura.do_initialization().await?; aura.do_initialization().await?;
Ok(Self::Aura(aura)) Ok(Self::Aura(aura))

View File

@@ -1,6 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use config_traits::{StdConfig, StdConfigLoad1}; use config_traits::{StdConfig, StdConfigLoad2};
use rog_platform::asus_armoury::FirmwareAttribute; use rog_platform::asus_armoury::FirmwareAttribute;
use rog_platform::cpu::CPUEPP; use rog_platform::cpu::CPUEPP;
use rog_platform::platform::PlatformProfile; use rog_platform::platform::PlatformProfile;
@@ -11,7 +11,7 @@ const CONFIG_FILE: &str = "asusd.ron";
#[derive(Default, Clone, Deserialize, Serialize, PartialEq)] #[derive(Default, Clone, Deserialize, Serialize, PartialEq)]
pub struct Tuning { pub struct Tuning {
pub enabled: bool, pub enabled: bool,
pub group: HashMap<FirmwareAttribute, i32> pub group: HashMap<FirmwareAttribute, i32>,
} }
type Tunings = HashMap<PlatformProfile, Tuning>; type Tunings = HashMap<PlatformProfile, Tuning>;
@@ -43,13 +43,19 @@ pub struct Config {
/// The energy_performance_preference for this platform profile /// The energy_performance_preference for this platform profile
pub profile_balanced_epp: CPUEPP, pub profile_balanced_epp: CPUEPP,
/// The energy_performance_preference for this platform profile /// The energy_performance_preference for this platform profile
pub profile_custom_epp: CPUEPP,
/// The energy_performance_preference for this platform profile
pub profile_performance_epp: CPUEPP, pub profile_performance_epp: CPUEPP,
pub ac_profile_tunings: Tunings, pub ac_profile_tunings: Tunings,
pub dc_profile_tunings: Tunings, pub dc_profile_tunings: Tunings,
pub armoury_settings: HashMap<FirmwareAttribute, i32>, pub armoury_settings: HashMap<FirmwareAttribute, i32>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub screenpad_gamma: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub screenpad_sync_primary: Option<bool>,
/// Temporary state for AC/Batt /// Temporary state for AC/Batt
#[serde(skip)] #[serde(skip)]
pub last_power_plugged: u8 pub last_power_plugged: u8,
} }
impl Config { impl Config {
@@ -61,6 +67,19 @@ impl Config {
}; };
config.entry(profile).or_insert_with(Tuning::default) config.entry(profile).or_insert_with(Tuning::default)
} }
pub fn select_tunings_ref(
&self,
power_plugged: bool,
profile: PlatformProfile,
) -> Option<&Tuning> {
let config = if power_plugged {
&self.ac_profile_tunings
} else {
&self.dc_profile_tunings
};
config.get(&profile)
}
} }
impl Default for Config { impl Default for Config {
@@ -79,10 +98,13 @@ impl Default for Config {
profile_quiet_epp: CPUEPP::Power, profile_quiet_epp: CPUEPP::Power,
profile_balanced_epp: CPUEPP::BalancePower, profile_balanced_epp: CPUEPP::BalancePower,
profile_performance_epp: CPUEPP::Performance, profile_performance_epp: CPUEPP::Performance,
profile_custom_epp: CPUEPP::Performance,
ac_profile_tunings: HashMap::default(), ac_profile_tunings: HashMap::default(),
dc_profile_tunings: HashMap::default(), dc_profile_tunings: HashMap::default(),
armoury_settings: HashMap::default(), armoury_settings: HashMap::default(),
last_power_plugged: Default::default() last_power_plugged: Default::default(),
screenpad_gamma: Default::default(),
screenpad_sync_primary: Default::default(),
} }
} }
} }
@@ -109,7 +131,64 @@ impl StdConfig for Config {
} }
} }
impl StdConfigLoad1<Config601> for Config {} impl StdConfigLoad2<Config611, Config601> for Config {}
#[derive(Deserialize, Serialize)]
pub struct Config611 {
pub charge_control_end_threshold: u8,
#[serde(skip)]
pub base_charge_control_end_threshold: u8,
pub disable_nvidia_powerd_on_battery: bool,
pub ac_command: String,
pub bat_command: String,
pub platform_profile_linked_epp: bool,
pub platform_profile_on_battery: PlatformProfile,
pub change_platform_profile_on_battery: bool,
pub platform_profile_on_ac: PlatformProfile,
pub change_platform_profile_on_ac: bool,
pub profile_quiet_epp: CPUEPP,
pub profile_balanced_epp: CPUEPP,
pub profile_custom_epp: CPUEPP,
pub profile_performance_epp: CPUEPP,
pub ac_profile_tunings: Tunings,
pub dc_profile_tunings: Tunings,
pub armoury_settings: HashMap<FirmwareAttribute, i32>,
#[serde(skip)]
pub last_power_plugged: u8,
}
impl From<Config611> for Config {
fn from(c: Config611) -> Self {
let mut config = Self {
// Restore the base charge limit
charge_control_end_threshold: c.charge_control_end_threshold,
base_charge_control_end_threshold: c.charge_control_end_threshold,
disable_nvidia_powerd_on_battery: c.disable_nvidia_powerd_on_battery,
ac_command: c.ac_command,
bat_command: c.bat_command,
platform_profile_linked_epp: c.platform_profile_linked_epp,
platform_profile_on_battery: c.platform_profile_on_battery,
change_platform_profile_on_battery: c.change_platform_profile_on_battery,
platform_profile_on_ac: c.platform_profile_on_ac,
change_platform_profile_on_ac: c.change_platform_profile_on_ac,
profile_quiet_epp: c.profile_quiet_epp,
profile_balanced_epp: c.profile_balanced_epp,
profile_performance_epp: c.profile_performance_epp,
profile_custom_epp: c.profile_performance_epp,
last_power_plugged: c.last_power_plugged,
ac_profile_tunings: HashMap::default(),
dc_profile_tunings: HashMap::default(),
armoury_settings: HashMap::default(),
screenpad_gamma: None,
screenpad_sync_primary: Default::default(),
};
config.ac_profile_tunings = c.ac_profile_tunings;
config.dc_profile_tunings = c.dc_profile_tunings;
config.armoury_settings = c.armoury_settings;
config
}
}
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct Config601 { pub struct Config601 {
@@ -146,8 +225,10 @@ pub struct Config601 {
pub nv_dynamic_boost: Option<u8>, pub nv_dynamic_boost: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)] #[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_temp_target: Option<u8>, pub nv_temp_target: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_tgp: Option<u8>,
#[serde(skip)] #[serde(skip)]
pub last_power_plugged: u8 pub last_power_plugged: u8,
} }
impl From<Config601> for Config { impl From<Config601> for Config {
@@ -167,10 +248,13 @@ impl From<Config601> for Config {
profile_quiet_epp: c.profile_quiet_epp, profile_quiet_epp: c.profile_quiet_epp,
profile_balanced_epp: c.profile_balanced_epp, profile_balanced_epp: c.profile_balanced_epp,
profile_performance_epp: c.profile_performance_epp, profile_performance_epp: c.profile_performance_epp,
profile_custom_epp: c.profile_performance_epp,
last_power_plugged: c.last_power_plugged, last_power_plugged: c.last_power_plugged,
ac_profile_tunings: HashMap::default(), ac_profile_tunings: HashMap::default(),
dc_profile_tunings: HashMap::default(), dc_profile_tunings: HashMap::default(),
armoury_settings: HashMap::default() armoury_settings: HashMap::default(),
screenpad_gamma: None,
screenpad_sync_primary: Default::default(),
} }
} }
} }

348
asusd/src/ctrl_backlight.rs Normal file
View File

@@ -0,0 +1,348 @@
use std::sync::Arc;
use std::time::Duration;
use config_traits::StdConfig;
use log::{info, warn};
use rog_platform::backlight::{Backlight, BacklightType};
use tokio::sync::Mutex;
use zbus::fdo::Error as FdoErr;
use zbus::object_server::SignalEmitter;
use zbus::{interface, Connection};
use crate::config::Config;
use crate::error::RogError;
use crate::ASUS_ZBUS_PATH;
#[derive(Clone)]
pub struct CtrlBacklight {
backlights: Vec<Backlight>,
config: Arc<Mutex<Config>>,
}
impl CtrlBacklight {
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
let mut backlights = Vec::new();
if let Ok(primary) = Backlight::new(BacklightType::Primary) {
info!("Found primary display backlight");
backlights.push(primary);
}
if let Ok(screenpad) = Backlight::new(BacklightType::Screenpad) {
info!("Found screenpad backlight");
backlights.push(screenpad);
}
if backlights.is_empty() {
return Err(RogError::MissingFunction("No backlights found".into()));
}
Ok(Self { backlights, config })
}
fn get_backlight(&self, device_type: &BacklightType) -> Option<&Backlight> {
self.backlights
.iter()
.find(|b| b.device_type() == device_type)
}
async fn set_brightness_with_sync(
&self,
device_type: &BacklightType,
level: i32,
) -> Result<(), FdoErr> {
let sync = self
.config
.lock()
.await
.screenpad_sync_primary
.unwrap_or_default();
// If sync is enabled and we're setting screenpad brightness, set primary first
if sync && *device_type == BacklightType::Screenpad {
if let Some(primary) = self.get_backlight(&BacklightType::Primary) {
if let Ok(primary_max) = primary.get_max_brightness() {
let primary_scaled = level * primary_max / 100;
let _ = primary.set_brightness(primary_scaled);
}
}
}
if let Some(backlight) = self.get_backlight(device_type) {
let max = backlight.get_max_brightness().map_err(|e| {
warn!("Failed to get max brightness: {}", e);
FdoErr::Failed(format!("Failed to get max brightness: {}", e))
})?;
let gamma = self.config.lock().await.screenpad_gamma.unwrap_or(1.0);
let scaled = if *device_type == BacklightType::Screenpad {
// Apply non-linear scaling with the configurable gamma value only for Screenpad
let normalized_level = level as f32 / 100.0;
let gamma_corrected = normalized_level.powf(gamma);
(gamma_corrected * max as f32) as i32
} else {
// Linear scaling for other devices
level * max / 100
};
backlight.set_brightness(scaled).map_err(|e| {
warn!("Failed to set brightness: {}", e);
FdoErr::Failed(format!("Failed to set brightness: {}", e))
})?;
// If sync is enabled and we're setting primary brightness, set screenpad
// afterward
if sync && *device_type == BacklightType::Primary {
for other in self
.backlights
.iter()
.filter(|b| b.device_type() != device_type)
{
if let Ok(other_max) = other.get_max_brightness() {
let other_scaled = if other.device_type() == &BacklightType::Screenpad {
// Apply gamma only to Screenpad
let normalized_level = level as f32 / 100.0;
let gamma_corrected = normalized_level.powf(gamma);
(gamma_corrected * other_max as f32) as i32
} else {
// Linear scaling for other devices
level * other_max / 100
};
let _ = other.set_brightness(other_scaled);
}
}
}
Ok(())
} else {
Err(FdoErr::NotSupported(format!(
"Backlight {:?} not found",
device_type
)))
}
}
async fn get_brightness_percent(&self, device_type: &BacklightType) -> Result<i32, FdoErr> {
if let Some(backlight) = self.get_backlight(device_type) {
let brightness = backlight.get_brightness().map_err(|e| {
warn!("Failed to get brightness: {}", e);
FdoErr::Failed(format!("Failed to get brightness: {}", e))
})?;
let max = backlight.get_max_brightness().map_err(|e| {
warn!("Failed to get max brightness: {}", e);
FdoErr::Failed(format!("Failed to get max brightness: {}", e))
})?;
if *device_type == BacklightType::Screenpad {
let gamma = self.config.lock().await.screenpad_gamma.unwrap_or(1.0);
let normalized = brightness as f32 / max as f32;
let corrected = normalized.powf(1.0 / gamma);
Ok((corrected * 100.0).round() as i32)
} else {
Ok(brightness * 100 / max)
}
} else {
Err(FdoErr::NotSupported(format!(
"Backlight {:?} not found",
device_type
)))
}
}
pub async fn start_watch_primary(&self) -> Result<(), RogError> {
if self.get_backlight(&BacklightType::Screenpad).is_none() {
return Ok(());
}
if let Some(sync) = self.config.lock().await.screenpad_sync_primary {
if !sync {
return Ok(());
}
}
if let Some(backlight) = self.get_backlight(&BacklightType::Primary) {
let watch = backlight.monitor_brightness()?;
let backlights = self.clone();
tokio::spawn(async move {
let mut last_level = 0;
let mut buffer = [0; 32];
use futures_lite::StreamExt;
if let Ok(mut stream) = watch.into_event_stream(&mut buffer) {
loop {
let _ = stream.next().await;
let sync = backlights.config.lock().await.screenpad_sync_primary;
if let Some(sync) = sync {
if !sync {
continue;
}
} else if backlights
.config
.lock()
.await
.screenpad_sync_primary
.is_none()
{
continue;
}
let level = backlights
.get_brightness_percent(&BacklightType::Primary)
.await
.unwrap_or(60);
if last_level != level {
last_level = level;
backlights
.set_brightness_with_sync(&BacklightType::Screenpad, level)
.await
.ok();
}
// other processes cause "MODIFY" event and make this spin 100%, so sleep
tokio::time::sleep(Duration::from_millis(300)).await;
}
// watch
// .into_event_stream(&mut buffer)
// .unwrap()
// .for_each(|_| async {})
// .await;
}
});
}
Ok(())
}
}
#[interface(name = "xyz.ljones.Backlight")]
impl CtrlBacklight {
#[zbus(property)]
async fn screenpad_sync_with_primary(&self) -> bool {
self.config
.lock()
.await
.screenpad_sync_primary
.unwrap_or_default()
}
#[zbus(property)]
async fn set_screenpad_sync_with_primary(&self, sync: bool) -> Result<(), zbus::Error> {
self.config.lock().await.screenpad_sync_primary = Some(sync);
self.config.lock().await.write();
Ok(())
}
#[zbus(property)]
async fn screenpad_gamma(&self) -> String {
(self.config.lock().await.screenpad_gamma.unwrap_or(1.0)).to_string()
}
#[zbus(property)]
async fn set_screenpad_gamma(&self, value: &str) -> Result<(), zbus::Error> {
let gamma: f32 = value
.parse()
.map_err(|_| FdoErr::Failed("Invalid gamma value, must be a valid number".into()))?;
if gamma < 0.1 {
return Err(FdoErr::Failed("Gamma value must be greater than 0".into()).into());
}
if gamma > 2.0 {
return Err(FdoErr::Failed("Gamma value must be 2.0 or less".into()).into());
}
self.config.lock().await.screenpad_gamma = Some(gamma);
self.config.lock().await.write();
Ok(())
}
#[zbus(property)]
async fn primary_brightness(&self) -> Result<i32, FdoErr> {
self.get_brightness_percent(&BacklightType::Primary).await
}
#[zbus(property)]
async fn set_primary_brightness(
&self,
#[zbus(signal_context)] ctxt: SignalEmitter<'_>,
level: i32,
) -> Result<(), zbus::Error> {
if level > 100 {
return Err(FdoErr::Failed("Brightness level must be 0-100".into()).into());
}
self.set_brightness_with_sync(&BacklightType::Primary, level)
.await?;
self.primary_brightness_changed(&ctxt).await?;
Ok(())
}
#[zbus(property)]
async fn screenpad_brightness(&self) -> Result<i32, FdoErr> {
self.get_brightness_percent(&BacklightType::Screenpad).await
}
#[zbus(property)]
async fn set_screenpad_brightness(
&self,
// #[zbus(signal_context)] ctxt: SignalEmitter<'_>,
level: i32,
) -> Result<(), zbus::Error> {
if level > 100 {
return Err(FdoErr::Failed("Brightness level must be 0-100".into()).into());
}
self.set_brightness_with_sync(&BacklightType::Screenpad, level)
.await?;
// self.screenpad_brightness_changed(&ctxt).await?;
Ok(())
}
#[zbus(property)]
async fn screenpad_power(&self) -> Result<bool, FdoErr> {
if let Some(backlight) = self.get_backlight(&BacklightType::Screenpad) {
let power = backlight.get_bl_power().map_err(|e| {
warn!("Failed to get backlight power: {}", e);
FdoErr::Failed(format!("Failed to get backlight power: {}", e))
})?;
Ok(power == 0)
} else {
Err(FdoErr::NotSupported("Screenpad backlight not found".into()))
}
}
#[zbus(property)]
async fn set_screenpad_power(
&self,
#[zbus(signal_context)] ctxt: SignalEmitter<'_>,
power: bool,
) -> Result<(), zbus::Error> {
if let Some(backlight) = self.get_backlight(&BacklightType::Screenpad) {
backlight
.set_bl_power(if power { 0 } else { 1 })
.map_err(|e| {
warn!("Failed to set backlight power: {}", e);
FdoErr::Failed(format!("Failed to set backlight power: {}", e))
})?;
self.screenpad_power_changed(&ctxt).await?;
Ok(())
} else {
Err(FdoErr::NotSupported("Screenpad backlight not found".into()).into())
}
}
}
impl crate::ZbusRun for CtrlBacklight {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, ASUS_ZBUS_PATH, server).await;
}
}
impl crate::Reloadable for CtrlBacklight {
async fn reload(&mut self) -> Result<(), RogError> {
info!("Reloading backlight settings");
Ok(())
}
}

View File

@@ -23,7 +23,7 @@ pub const FAN_CURVE_ZBUS_PATH: &str = "/xyz/ljones";
pub struct FanCurveConfig { pub struct FanCurveConfig {
pub profiles: FanCurveProfiles, pub profiles: FanCurveProfiles,
#[serde(skip)] #[serde(skip)]
pub current: PlatformProfile pub current: PlatformProfile,
} }
impl StdConfig for FanCurveConfig { impl StdConfig for FanCurveConfig {
@@ -47,7 +47,7 @@ impl StdConfigLoad for FanCurveConfig {}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct CtrlFanCurveZbus { pub struct CtrlFanCurveZbus {
config: Arc<Mutex<FanCurveConfig>>, config: Arc<Mutex<FanCurveConfig>>,
platform: RogPlatform platform: RogPlatform,
} }
// Non-zbus-derive impl // Non-zbus-derive impl
@@ -66,11 +66,8 @@ impl CtrlFanCurveZbus {
info!("Fetching default fan curves"); info!("Fetching default fan curves");
let current = platform.get_platform_profile()?; let current = platform.get_platform_profile()?;
for this in [ let profiles = platform.get_platform_profile_choices()?;
PlatformProfile::Balanced, for this in profiles {
PlatformProfile::Performance,
PlatformProfile::Quiet
] {
// For each profile we need to switch to it before we // For each profile we need to switch to it before we
// can read the existing values from hardware. The ACPI method used // can read the existing values from hardware. The ACPI method used
// for this is what limits us. // for this is what limits us.
@@ -93,7 +90,7 @@ impl CtrlFanCurveZbus {
return Ok(Self { return Ok(Self {
config: Arc::new(Mutex::new(config)), config: Arc::new(Mutex::new(config)),
platform platform,
}); });
} }
@@ -108,7 +105,7 @@ impl CtrlFanCurveZbus {
async fn set_fan_curves_enabled( async fn set_fan_curves_enabled(
&mut self, &mut self,
profile: PlatformProfile, profile: PlatformProfile,
enabled: bool enabled: bool,
) -> zbus::fdo::Result<()> { ) -> zbus::fdo::Result<()> {
self.config self.config
.lock() .lock()
@@ -130,7 +127,7 @@ impl CtrlFanCurveZbus {
&mut self, &mut self,
profile: PlatformProfile, profile: PlatformProfile,
fan: FanCurvePU, fan: FanCurvePU,
enabled: bool enabled: bool,
) -> zbus::fdo::Result<()> { ) -> zbus::fdo::Result<()> {
self.config self.config
.lock() .lock()
@@ -149,7 +146,7 @@ impl CtrlFanCurveZbus {
/// Get the fan-curve data for the currently active ThrottlePolicy /// Get the fan-curve data for the currently active ThrottlePolicy
async fn fan_curve_data( async fn fan_curve_data(
&mut self, &mut self,
profile: PlatformProfile profile: PlatformProfile,
) -> zbus::fdo::Result<Vec<CurveData>> { ) -> zbus::fdo::Result<Vec<CurveData>> {
let curve = self let curve = self
.config .config
@@ -166,7 +163,7 @@ impl CtrlFanCurveZbus {
async fn set_fan_curve( async fn set_fan_curve(
&mut self, &mut self,
profile: PlatformProfile, profile: PlatformProfile,
curve: CurveData curve: CurveData,
) -> zbus::fdo::Result<()> { ) -> zbus::fdo::Result<()> {
self.config self.config
.lock() .lock()
@@ -216,7 +213,7 @@ impl CtrlFanCurveZbus {
.lock() .lock()
.await .await
.profiles .profiles
.set_active_curve_to_defaults((&active).into(), &mut find_fan_curve_node()?)?; .set_active_curve_to_defaults(active.as_str().into(), &mut find_fan_curve_node()?)?;
self.platform.set_platform_profile(active.as_str())?; self.platform.set_platform_profile(active.as_str())?;
self.config.lock().await.write(); self.config.lock().await.write();
@@ -261,7 +258,7 @@ impl CtrlTask for CtrlFanCurveZbus {
.profiles .profiles
.write_profile_curve_to_platform( .write_profile_curve_to_platform(
profile, profile,
&mut find_fan_curve_node().unwrap() &mut find_fan_curve_node().unwrap(),
) )
.map_err(|e| warn!("write_profile_curve_to_platform, {}", e)) .map_err(|e| warn!("write_profile_curve_to_platform, {}", e))
.ok(); .ok();

View File

@@ -8,12 +8,12 @@ use rog_platform::asus_armoury::{AttrValue, FirmwareAttribute, FirmwareAttribute
use rog_platform::cpu::{CPUControl, CPUGovernor, CPUEPP}; use rog_platform::cpu::{CPUControl, CPUGovernor, CPUEPP};
use rog_platform::platform::{PlatformProfile, Properties, RogPlatform}; use rog_platform::platform::{PlatformProfile, Properties, RogPlatform};
use rog_platform::power::AsusPower; use rog_platform::power::AsusPower;
use zbus::export::futures_util::lock::Mutex; use tokio::sync::Mutex;
use zbus::fdo::Error as FdoErr; use zbus::fdo::Error as FdoErr;
use zbus::object_server::SignalEmitter; use zbus::object_server::SignalEmitter;
use zbus::{interface, Connection}; use zbus::{interface, Connection};
use crate::asus_armoury::set_config_or_default; use crate::asus_armoury::{set_config_or_default, ArmouryAttributeRegistry};
use crate::config::Config; use crate::config::Config;
use crate::error::RogError; use crate::error::RogError;
use crate::{task_watch_item, CtrlTask, ReloadAndNotify}; use crate::{task_watch_item, CtrlTask, ReloadAndNotify};
@@ -45,17 +45,22 @@ pub struct CtrlPlatform {
platform: RogPlatform, platform: RogPlatform,
attributes: FirmwareAttributes, attributes: FirmwareAttributes,
cpu_control: Option<CPUControl>, cpu_control: Option<CPUControl>,
config: Arc<Mutex<Config>> config: Arc<Mutex<Config>>,
connection: Connection,
armoury_registry: ArmouryAttributeRegistry,
} }
impl CtrlPlatform { impl CtrlPlatform {
#[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
platform: RogPlatform, platform: RogPlatform,
power: AsusPower, power: AsusPower,
attributes: FirmwareAttributes, attributes: FirmwareAttributes,
config: Arc<Mutex<Config>>, config: Arc<Mutex<Config>>,
config_path: &Path, config_path: &Path,
signal_context: SignalEmitter<'static> signal_context: SignalEmitter<'static>,
connection: Connection,
armoury_registry: ArmouryAttributeRegistry,
) -> Result<Self, RogError> { ) -> Result<Self, RogError> {
let config1 = config.clone(); let config1 = config.clone();
let config_path = config_path.to_owned(); let config_path = config_path.to_owned();
@@ -67,12 +72,14 @@ impl CtrlPlatform {
config, config,
cpu_control: CPUControl::new() cpu_control: CPUControl::new()
.map_err(|e| error!("Couldn't get CPU control sysfs: {e}")) .map_err(|e| error!("Couldn't get CPU control sysfs: {e}"))
.ok() .ok(),
connection,
armoury_registry,
}; };
let mut inotify_self = ret_self.clone(); let mut inotify_self = ret_self.clone();
tokio::spawn(async move { tokio::spawn(async move {
use zbus::export::futures_util::StreamExt; use futures_util::StreamExt;
info!("Starting inotify watch for asusd config file"); info!("Starting inotify watch for asusd config file");
let mut buffer = [0; 32]; let mut buffer = [0; 32];
@@ -86,7 +93,7 @@ impl CtrlPlatform {
inotify::WatchMask::MODIFY inotify::WatchMask::MODIFY
| inotify::WatchMask::CLOSE_WRITE | inotify::WatchMask::CLOSE_WRITE
| inotify::WatchMask::ATTRIB | inotify::WatchMask::ATTRIB
| inotify::WatchMask::CREATE | inotify::WatchMask::CREATE,
) )
.inspect_err(|e| { .inspect_err(|e| {
if e.kind() == std::io::ErrorKind::NotFound { if e.kind() == std::io::ErrorKind::NotFound {
@@ -128,7 +135,7 @@ impl CtrlPlatform {
if limit > 0 if limit > 0
&& std::mem::replace( && std::mem::replace(
&mut self.config.lock().await.charge_control_end_threshold, &mut self.config.lock().await.charge_control_end_threshold,
limit limit,
) != limit ) != limit
{ {
self.power self.power
@@ -161,7 +168,7 @@ impl CtrlPlatform {
.map(|s| s.to_string()) .map(|s| s.to_string())
.collect() .collect()
}; };
if prog.len() > 1 { if (!prog.is_empty()) && (!prog[0].is_empty()) {
let mut cmd = Command::new(&prog[0]); let mut cmd = Command::new(&prog[0]);
for arg in prog.iter().skip(1) { for arg in prog.iter().skip(1) {
cmd.arg(arg); cmd.arg(arg);
@@ -210,7 +217,9 @@ impl CtrlPlatform {
match throttle { match throttle {
PlatformProfile::Balanced => self.config.lock().await.profile_balanced_epp, PlatformProfile::Balanced => self.config.lock().await.profile_balanced_epp,
PlatformProfile::Performance => self.config.lock().await.profile_performance_epp, PlatformProfile::Performance => self.config.lock().await.profile_performance_epp,
PlatformProfile::Quiet => self.config.lock().await.profile_quiet_epp PlatformProfile::Quiet => self.config.lock().await.profile_quiet_epp,
PlatformProfile::LowPower => self.config.lock().await.profile_quiet_epp,
PlatformProfile::Custom => self.config.lock().await.profile_custom_epp,
} }
} }
@@ -305,7 +314,7 @@ impl CtrlPlatform {
async fn one_shot_full_charge(&self) -> Result<(), FdoErr> { async fn one_shot_full_charge(&self) -> Result<(), FdoErr> {
let base_limit = std::mem::replace( let base_limit = std::mem::replace(
&mut self.config.lock().await.charge_control_end_threshold, &mut self.config.lock().await.charge_control_end_threshold,
100 100,
); );
if base_limit != 100 { if base_limit != 100 {
self.power.set_charge_control_end_threshold(100)?; self.power.set_charge_control_end_threshold(100)?;
@@ -319,11 +328,13 @@ 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_platform_profile( async fn next_platform_profile(
&mut self, &mut self,
#[zbus(signal_context)] ctxt: SignalEmitter<'_> #[zbus(signal_context)] ctxt: SignalEmitter<'_>,
) -> Result<(), FdoErr> { ) -> Result<(), FdoErr> {
let policy: PlatformProfile = let policy: PlatformProfile =
platform_get_value!(self, platform_profile, "platform_profile").map(|n| n.into())?; platform_get_value!(self, platform_profile, "platform_profile").map(|n| n.into())?;
let policy = PlatformProfile::next(policy); let choices =
platform_get_value!(self, platform_profile_choices, "platform_profile_choices")?;
let policy = PlatformProfile::next(policy, &choices);
if self.platform.has_platform_profile() { if self.platform.has_platform_profile() {
let change_epp = self.config.lock().await.platform_profile_linked_epp; let change_epp = self.config.lock().await.platform_profile_linked_epp;
@@ -339,21 +350,27 @@ impl CtrlPlatform {
Ok(self.platform_profile_changed(&ctxt).await?) Ok(self.platform_profile_changed(&ctxt).await?)
} else { } else {
Err(FdoErr::NotSupported( Err(FdoErr::NotSupported(
"RogPlatform: platform_profile not supported".to_owned() "RogPlatform: platform_profile not supported".to_owned(),
)) ))
} }
} }
#[zbus(property)]
fn platform_profile_choices(&self) -> Result<Vec<PlatformProfile>, FdoErr> {
platform_get_value!(self, platform_profile_choices, "platform_profile_choices")
}
#[zbus(property)] #[zbus(property)]
fn platform_profile(&self) -> Result<PlatformProfile, FdoErr> { fn platform_profile(&self) -> Result<PlatformProfile, FdoErr> {
platform_get_value!(self, platform_profile, "platform_profile").map(|n| n.into()) let policy: PlatformProfile = self.platform.get_platform_profile()?.as_str().into();
Ok(policy)
} }
#[zbus(property)] #[zbus(property)]
async fn set_platform_profile( async fn set_platform_profile(
&mut self, &mut self,
#[zbus(signal_context)] ctxt: SignalEmitter<'_>, #[zbus(signal_context)] ctxt: SignalEmitter<'_>,
policy: PlatformProfile policy: PlatformProfile,
) -> Result<(), FdoErr> { ) -> Result<(), FdoErr> {
// TODO: watch for external changes // TODO: watch for external changes
if self.platform.has_platform_profile() { if self.platform.has_platform_profile() {
@@ -362,7 +379,15 @@ impl CtrlPlatform {
self.check_and_set_epp(epp, change_epp); self.check_and_set_epp(epp, change_epp);
self.config.lock().await.write(); self.config.lock().await.write();
// TODO: Need to get supported profiles here and ensure we translate to one
let choices = self.platform.get_platform_profile_choices()?;
if !choices.contains(&policy) {
return Err(FdoErr::NotSupported(format!(
"RogPlatform: platform_profile: {} not supported",
policy
)));
}
self.platform self.platform
.set_platform_profile(policy.into()) .set_platform_profile(policy.into())
.map_err(|err| { .map_err(|err| {
@@ -373,7 +398,7 @@ impl CtrlPlatform {
Ok(()) Ok(())
} else { } else {
Err(FdoErr::NotSupported( Err(FdoErr::NotSupported(
"RogPlatform: platform_profile not supported".to_owned() "RogPlatform: platform_profile not supported".to_owned(),
)) ))
} }
} }
@@ -399,7 +424,7 @@ impl CtrlPlatform {
async fn set_platform_profile_on_battery( async fn set_platform_profile_on_battery(
&mut self, &mut self,
#[zbus(signal_context)] ctxt: SignalEmitter<'_>, #[zbus(signal_context)] ctxt: SignalEmitter<'_>,
policy: PlatformProfile policy: PlatformProfile,
) -> Result<(), FdoErr> { ) -> Result<(), FdoErr> {
self.config.lock().await.platform_profile_on_battery = policy; self.config.lock().await.platform_profile_on_battery = policy;
self.set_platform_profile(ctxt, policy).await?; self.set_platform_profile(ctxt, policy).await?;
@@ -428,7 +453,7 @@ impl CtrlPlatform {
async fn set_platform_profile_on_ac( async fn set_platform_profile_on_ac(
&mut self, &mut self,
#[zbus(signal_context)] ctxt: SignalEmitter<'_>, #[zbus(signal_context)] ctxt: SignalEmitter<'_>,
policy: PlatformProfile policy: PlatformProfile,
) -> Result<(), FdoErr> { ) -> Result<(), FdoErr> {
self.config.lock().await.platform_profile_on_ac = policy; self.config.lock().await.platform_profile_on_ac = policy;
self.set_platform_profile(ctxt, policy).await?; self.set_platform_profile(ctxt, policy).await?;
@@ -541,7 +566,7 @@ impl CtrlPlatform {
for attr in self.attributes.attributes() { for attr in self.attributes.attributes() {
let name: FirmwareAttribute = attr.name().into(); let name: FirmwareAttribute = attr.name().into();
if name.is_ppt() { if name.is_ppt() || name.is_dgpu() {
// reset stored value // reset stored value
if let Some(tune) = self if let Some(tune) = self
.config .config
@@ -593,7 +618,7 @@ impl ReloadAndNotify for CtrlPlatform {
async fn reload_and_notify( async fn reload_and_notify(
&mut self, &mut self,
signal_context: &SignalEmitter<'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 {
@@ -622,7 +647,9 @@ impl ReloadAndNotify for CtrlPlatform {
let epp = match profile { let epp = match profile {
PlatformProfile::Balanced => data.profile_balanced_epp, PlatformProfile::Balanced => data.profile_balanced_epp,
PlatformProfile::Performance => data.profile_performance_epp, PlatformProfile::Performance => data.profile_performance_epp,
PlatformProfile::Quiet => data.profile_quiet_epp PlatformProfile::Quiet => data.profile_quiet_epp,
PlatformProfile::LowPower => data.profile_quiet_epp,
PlatformProfile::Custom => data.profile_custom_epp,
}; };
warn!("setting epp to {epp:?}"); warn!("setting epp to {epp:?}");
self.check_and_set_epp(epp, true); self.check_and_set_epp(epp, true);
@@ -694,7 +721,7 @@ impl CtrlTask for CtrlPlatform {
platform1 platform1
.power .power
.set_charge_control_end_threshold( .set_charge_control_end_threshold(
platform1.config.lock().await.charge_control_end_threshold platform1.config.lock().await.charge_control_end_threshold,
) )
.ok(); .ok();
} }
@@ -709,6 +736,31 @@ impl CtrlTask for CtrlPlatform {
} }
if !sleeping { if !sleeping {
platform1.run_ac_or_bat_cmd(power_plugged > 0).await; platform1.run_ac_or_bat_cmd(power_plugged > 0).await;
if let Ok(profile) =
platform1.platform.get_platform_profile().map(|p| p.into())
{
let attrs = FirmwareAttributes::new();
{
let mut cfg = platform1.config.lock().await;
set_config_or_default(
&attrs,
&mut cfg,
power_plugged > 0,
profile,
)
.await;
}
if let Err(e) = platform1
.armoury_registry
.emit_limits(&platform1.connection)
.await
{
error!(
"Failed to emit armoury updates after power change: \
{e:?}"
);
}
}
} }
platform1.config.lock().await.last_power_plugged = power_plugged; platform1.config.lock().await.last_power_plugged = power_plugged;
} }
@@ -728,7 +780,7 @@ impl CtrlTask for CtrlPlatform {
platform2 platform2
.power .power
.set_charge_control_end_threshold( .set_charge_control_end_threshold(
lock.base_charge_control_end_threshold lock.base_charge_control_end_threshold,
) )
.map_err(|err| { .map_err(|err| {
warn!("CtrlCharge: charge_control_end_threshold {}", err); warn!("CtrlCharge: charge_control_end_threshold {}", err);
@@ -769,20 +821,24 @@ impl CtrlTask for CtrlPlatform {
{ {
// TODO: manage this better, shouldn't need to create every time // TODO: manage this better, shouldn't need to create every time
let attrs = FirmwareAttributes::new(); let attrs = FirmwareAttributes::new();
set_config_or_default( {
&attrs, let mut cfg = platform3.config.lock().await;
&mut *platform3.config.lock().await, set_config_or_default(&attrs, &mut cfg, power_plugged, profile).await;
power_plugged, }
profile if let Err(e) = platform3
) .armoury_registry
.await; .emit_limits(&platform3.connection)
.await
{
error!("Failed to emit armoury updates after AC/DC toggle: {e:?}");
}
platform3 platform3
.enable_ppt_group_changed(&signal_ctxt_copy) .enable_ppt_group_changed(&signal_ctxt_copy)
.await .await
.ok(); .ok();
} }
} }
} },
) )
.await; .await;
@@ -829,9 +885,12 @@ impl CtrlTask for CtrlPlatform {
&attrs, &attrs,
&mut *ctrl.config.lock().await, &mut *ctrl.config.lock().await,
power_plugged == 1, power_plugged == 1,
profile profile,
) )
.await; .await;
if let Err(e) = ctrl.armoury_registry.emit_limits(&ctrl.connection).await {
error!("Failed to emit armoury updates after profile change: {e:?}");
}
} }
} }
} }

View File

@@ -2,23 +2,26 @@ use std::env;
use std::error::Error; use std::error::Error;
use std::sync::Arc; use std::sync::Arc;
use ::zbus::export::futures_util::lock::Mutex;
use ::zbus::Connection; use ::zbus::Connection;
use asusd::asus_armoury::start_attributes_zbus; use asusd::asus_armoury::{start_attributes_zbus, ArmouryAttributeRegistry};
use asusd::aura_manager::DeviceManager; use asusd::aura_manager::DeviceManager;
use asusd::config::Config; use asusd::config::Config;
use asusd::ctrl_backlight::CtrlBacklight;
use asusd::ctrl_fancurves::CtrlFanCurveZbus; use asusd::ctrl_fancurves::CtrlFanCurveZbus;
use asusd::ctrl_platform::CtrlPlatform; use asusd::ctrl_platform::CtrlPlatform;
use asusd::{print_board_info, start_tasks, CtrlTask, DBUS_NAME}; use asusd::{print_board_info, start_tasks, CtrlTask, ZbusRun, DBUS_NAME};
use config_traits::{StdConfig, StdConfigLoad1}; use config_traits::{StdConfig, StdConfigLoad2};
use log::{error, info}; use log::{error, info};
use rog_platform::asus_armoury::FirmwareAttributes; use rog_platform::asus_armoury::FirmwareAttributes;
use rog_platform::platform::RogPlatform; use rog_platform::platform::RogPlatform;
use rog_platform::power::AsusPower; use rog_platform::power::AsusPower;
use tokio::sync::Mutex;
use zbus::fdo::ObjectManager; use zbus::fdo::ObjectManager;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Starting asusd daemon...");
// console_subscriber::init(); // console_subscriber::init();
let mut logger = env_logger::Builder::new(); let mut logger = env_logger::Builder::new();
logger logger
@@ -30,7 +33,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let is_service = match env::var_os("IS_SERVICE") { let is_service = match env::var_os("IS_SERVICE") {
Some(val) => val == "1", Some(val) => val == "1",
None => true None => true,
}; };
if !is_service { if !is_service {
@@ -61,7 +64,9 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
// Start zbus server // Start zbus server
let mut server = Connection::system().await?; let mut server = Connection::system().await?;
server.object_server().at("/", ObjectManager).await.unwrap(); if let Err(e) = server.object_server().at("/", ObjectManager).await {
error!("Failed to register ObjectManager at root '/': {e:?}");
}
let config = Config::new().load(); let config = Config::new().load();
let cfg_path = config.file_path(); let cfg_path = config.file_path();
@@ -71,36 +76,67 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
let platform = RogPlatform::new()?; // TODO: maybe needs async mutex? let platform = RogPlatform::new()?; // TODO: maybe needs async mutex?
let power = AsusPower::new()?; // TODO: maybe needs async mutex? let power = AsusPower::new()?; // TODO: maybe needs async mutex?
let attributes = FirmwareAttributes::new(); let attributes = FirmwareAttributes::new();
start_attributes_zbus( let armoury_registry = match start_attributes_zbus(
&server, Some(&server),
platform.clone(), platform.clone(),
power.clone(), power.clone(),
attributes.clone(), attributes.clone(),
config.clone() config.clone(),
true,
None,
None,
) )
.await?; .await
{
Ok(registry) => {
info!("attribute on zbus initialized");
registry
}
Err(e) => {
error!("Failed to initialize firmware attributes over zbus: {e:?}");
ArmouryAttributeRegistry::default()
}
};
match CtrlFanCurveZbus::new() { match CtrlFanCurveZbus::new() {
Ok(ctrl) => { Ok(ctrl) => {
info!("FanCurves: found supported fancurves");
let sig_ctx = CtrlFanCurveZbus::signal_context(&server)?; let sig_ctx = CtrlFanCurveZbus::signal_context(&server)?;
start_tasks(ctrl, &mut server, sig_ctx).await?; start_tasks(ctrl, &mut server, sig_ctx).await?;
info!("FanCurves: initialized");
} }
Err(err) => { Err(err) => {
error!("FanCurves: {}", err); error!("FanCurves: {}", err);
} }
} }
match CtrlBacklight::new(config.clone()) {
Ok(backlight) => {
info!("Backlight: found supported backlight");
backlight.start_watch_primary().await?;
backlight.add_to_server(&mut server).await;
info!("Backlight: initialized");
}
Err(err) => {
error!("Backlight: {}", err);
}
}
match CtrlPlatform::new( match CtrlPlatform::new(
platform, platform,
power, power,
attributes, attributes,
config.clone(), config.clone(),
&cfg_path, &cfg_path,
CtrlPlatform::signal_context(&server)? CtrlPlatform::signal_context(&server)?,
server.clone(),
armoury_registry,
) { ) {
Ok(ctrl) => { Ok(ctrl) => {
info!("CtrlPlatform: initialized");
let sig_ctx = CtrlPlatform::signal_context(&server)?; let sig_ctx = CtrlPlatform::signal_context(&server)?;
start_tasks(ctrl, &mut server, sig_ctx).await?; start_tasks(ctrl, &mut server, sig_ctx).await?;
info!("CtrlPlatform: tasks started");
} }
Err(err) => { Err(err) => {
error!("CtrlPlatform: {}", err); error!("CtrlPlatform: {}", err);
@@ -109,10 +145,12 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
let _ = DeviceManager::new(server.clone()).await?; let _ = DeviceManager::new(server.clone()).await?;
info!("DeviceManager initialized");
// Request dbus name after finishing initalizing all functions // Request dbus name after finishing initalizing all functions
server.request_name(DBUS_NAME).await?; server.request_name(DBUS_NAME).await?;
info!("Startup success, begining dbus server loop"); info!("Startup success on dbus name {DBUS_NAME}: begining dbus server loop");
loop { loop {
// This is just a blocker to idle and ensure the reator reacts // This is just a blocker to idle and ensure the reator reacts
server.executor().tick().await; server.executor().tick().await;

View File

@@ -37,7 +37,7 @@ pub enum RogError {
SystemdUnitAction(String), SystemdUnitAction(String),
SystemdUnitWaitTimeout(String), SystemdUnitWaitTimeout(String),
Command(String, std::io::Error), Command(String, std::io::Error),
ParseRon(ron::Error) ParseRon(ron::Error),
} }
impl fmt::Display for RogError { impl fmt::Display for RogError {
@@ -87,7 +87,7 @@ impl fmt::Display for RogError {
) )
} }
RogError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error), RogError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error),
RogError::ParseRon(error) => write!(f, "Parse config error: {}", error) RogError::ParseRon(error) => write!(f, "Parse config error: {}", error),
} }
} }
} }

View File

@@ -1,6 +1,7 @@
#![deny(unused_must_use)] #![deny(unused_must_use)]
/// Configuration loading, saving /// Configuration loading, saving
pub mod config; pub mod config;
pub mod ctrl_backlight;
/// 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
@@ -67,7 +68,7 @@ macro_rules! task_watch_item {
&self, &self,
signal_ctxt: SignalEmitter<'static>, signal_ctxt: SignalEmitter<'static>,
) -> Result<(), RogError> { ) -> Result<(), RogError> {
use zbus::export::futures_util::StreamExt; use futures_util::StreamExt;
let ctrl = self.clone(); let ctrl = self.clone();
concat_idents::concat_idents!(watch_fn = monitor_, $name { concat_idents::concat_idents!(watch_fn = monitor_, $name {
@@ -107,7 +108,7 @@ macro_rules! task_watch_item_notify {
&self, &self,
signal_ctxt: SignalEmitter<'static>, signal_ctxt: SignalEmitter<'static>,
) -> Result<(), RogError> { ) -> Result<(), RogError> {
use zbus::export::futures_util::StreamExt; use futures_util::StreamExt;
let ctrl = self.clone(); let ctrl = self.clone();
concat_idents::concat_idents!(watch_fn = monitor_, $name { concat_idents::concat_idents!(watch_fn = monitor_, $name {
@@ -149,7 +150,7 @@ pub trait ReloadAndNotify {
fn reload_and_notify( fn reload_and_notify(
&mut self, &mut self,
signal_context: &SignalEmitter<'static>, signal_context: &SignalEmitter<'static>,
data: Self::Data data: Self::Data,
) -> impl Future<Output = Result<(), RogError>> + Send; ) -> impl Future<Output = Result<(), RogError>> + Send;
} }
@@ -159,7 +160,7 @@ pub trait ZbusRun {
fn add_to_server_helper( fn add_to_server_helper(
iface: impl Interface, iface: impl Interface,
path: &str, path: &str,
server: &mut Connection server: &mut Connection,
) -> impl Future<Output = ()> + Send { ) -> impl Future<Output = ()> + Send {
async move { async move {
server server
@@ -188,7 +189,7 @@ pub trait CtrlTask {
/// separate thread. /// separate thread.
fn create_tasks( fn create_tasks(
&self, &self,
signal: SignalEmitter<'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
@@ -212,7 +213,7 @@ pub trait CtrlTask {
mut on_prepare_for_sleep: F1, mut on_prepare_for_sleep: F1,
mut on_prepare_for_shutdown: F2, mut on_prepare_for_shutdown: F2,
mut on_lid_change: F3, mut on_lid_change: F3,
mut on_external_power_change: F4 mut on_external_power_change: F4,
) -> impl Future<Output = ()> + Send ) -> impl Future<Output = ()> + Send
where where
F1: FnMut(bool) -> Fut1 + Send + 'static, F1: FnMut(bool) -> Fut1 + Send + 'static,
@@ -222,7 +223,7 @@ pub trait CtrlTask {
Fut1: Future<Output = ()> + Send, Fut1: Future<Output = ()> + Send,
Fut2: Future<Output = ()> + Send, Fut2: Future<Output = ()> + Send,
Fut3: Future<Output = ()> + Send, Fut3: Future<Output = ()> + Send,
Fut4: Future<Output = ()> + Send Fut4: Future<Output = ()> + Send,
{ {
async { async {
let connection = Connection::system() let connection = Connection::system()
@@ -302,10 +303,10 @@ pub trait GetSupported {
pub async fn start_tasks<T>( pub async fn start_tasks<T>(
mut zbus: T, mut zbus: T,
connection: &mut Connection, connection: &mut Connection,
signal_ctx: SignalEmitter<'static> signal_ctx: SignalEmitter<'static>,
) -> Result<(), RogError> ) -> Result<(), RogError>
where where
T: ZbusRun + Reloadable + CtrlTask + Clone T: ZbusRun + Reloadable + CtrlTask + Clone,
{ {
let zbus_clone = zbus.clone(); let zbus_clone = zbus.clone();

View File

@@ -0,0 +1,68 @@
use std::fs::{create_dir_all, File};
use std::io::Write;
use std::path::PathBuf;
use tempfile::tempdir;
use asusd::asus_armoury::set_config_or_default;
use asusd::config::Config;
use rog_platform::asus_armoury::FirmwareAttributes;
use rog_platform::platform::PlatformProfile;
fn write_attr_dir_with_min_max(base: &PathBuf, name: &str, default: &str, min: &str, max: &str) {
let attr_dir = base.join(name);
create_dir_all(&attr_dir).unwrap();
let mut f = File::create(attr_dir.join("default_value")).unwrap();
write!(f, "{}", default).unwrap();
let mut f = File::create(attr_dir.join("display_name")).unwrap();
write!(f, "{}", name).unwrap();
// create current_value file so set_current_value can open for write
let mut f = File::create(attr_dir.join("current_value")).unwrap();
write!(f, "{}", default).unwrap();
// write explicit min and max
let mut f = File::create(attr_dir.join("min_value")).unwrap();
write!(f, "{}", min).unwrap();
let mut f = File::create(attr_dir.join("max_value")).unwrap();
write!(f, "{}", max).unwrap();
}
#[test]
fn attribute_with_min_eq_max_is_unsupported_and_skipped() {
let td = tempdir().unwrap();
let base = td.path().join("attributes");
create_dir_all(&base).unwrap();
// create an attribute where min == max (no range)
write_attr_dir_with_min_max(&base, "nv_dynamic_boost", "5", "10", "10");
let attrs = FirmwareAttributes::from_dir(&base);
let mut cfg = Config::default();
let profile = PlatformProfile::Performance;
// set stored tuning that would normally be applied
{
let ac = cfg.select_tunings(true, profile);
ac.enabled = true;
ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::NvDynamicBoost,
9,
);
}
let rt = tokio::runtime::Runtime::new().unwrap();
// apply AC
rt.block_on(async {
set_config_or_default(&attrs, &mut cfg, true, profile).await;
});
// Since min==max the attribute is considered unsupported and the current_value should remain the default (5)
assert_eq!(
std::fs::read_to_string(base.join("nv_dynamic_boost").join("current_value"))
.unwrap()
.trim(),
"5"
);
}

View File

@@ -0,0 +1,173 @@
use std::fs::{create_dir_all, File};
use std::io::Write;
use std::path::PathBuf;
use std::sync::Arc;
use tempfile::tempdir;
use asusd::asus_armoury::start_attributes_zbus;
use asusd::config::Config;
use rog_platform::asus_armoury::FirmwareAttributes;
use rog_platform::platform::PlatformProfile;
use rog_platform::platform::RogPlatform;
use rog_platform::power::AsusPower;
use tokio::runtime::Runtime;
use tokio::sync::Mutex as TokioMutex;
fn write_attr_dir(base: &PathBuf, name: &str, default: &str, display: &str) {
let attr_dir = base.join(name);
create_dir_all(&attr_dir).unwrap();
let mut f = File::create(attr_dir.join("default_value")).unwrap();
write!(f, "{}", default).unwrap();
let mut f = File::create(attr_dir.join("display_name")).unwrap();
write!(f, "{}", display).unwrap();
// create current_value file so set_current_value can open for write
let mut f = File::create(attr_dir.join("current_value")).unwrap();
write!(f, "{}", default).unwrap();
}
#[test]
fn full_service_handles_boot_sound_and_nv_tgp() {
let td = tempdir().unwrap();
let base = td.path().join("attributes");
create_dir_all(&base).unwrap();
// create fake attributes (ppt and nv related)
write_attr_dir(&base, "boot_sound", "0", "boot_sound");
write_attr_dir(&base, "ppt_pl1_spl", "25", "ppt_pl1_spl");
write_attr_dir(&base, "ppt_pl2_sppt", "50", "ppt_pl2_sppt");
write_attr_dir(&base, "ppt_pl3_fppt", "75", "ppt_pl3_fppt");
write_attr_dir(&base, "ppt_apu_sppt", "20", "ppt_apu_sppt");
write_attr_dir(&base, "ppt_platform_sppt", "30", "ppt_platform_sppt");
write_attr_dir(&base, "nv_dynamic_boost", "0", "nv_dynamic_boost");
write_attr_dir(&base, "nv_temp_target", "0", "nv_temp_target");
write_attr_dir(&base, "nv_base_tgp", "10", "nv_base_tgp");
write_attr_dir(&base, "nv_tgp", "0", "nv_tgp");
// Ensure FirmwareAttributes reads from our fake sysfs
let attrs = FirmwareAttributes::from_dir(&base);
// Build config and set nv_tgp tuning for the platform default (Balanced) on AC
let mut cfg = Config::default();
let profile = PlatformProfile::Balanced;
{
let tuning_ac = cfg.select_tunings(true, profile);
tuning_ac.enabled = true;
tuning_ac
.group
.insert(rog_platform::asus_armoury::FirmwareAttribute::PptPl1Spl, 42);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl2Sppt,
43,
);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl3Fppt,
44,
);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptApuSppt,
45,
);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPlatformSppt,
46,
);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::NvDynamicBoost,
11,
);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::NvTempTarget,
66,
);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::DgpuBaseTgp,
12,
);
tuning_ac
.group
.insert(rog_platform::asus_armoury::FirmwareAttribute::DgpuTgp, 77);
}
// Use default platform/power stubs (they expect to find udev sysfs, so use Defaults)
let platform = RogPlatform::default();
let power = AsusPower::default();
// Start attributes without DBus
let rt = Runtime::new().unwrap();
let cfg_arc = Arc::new(TokioMutex::new(cfg));
let attrs_clone = attrs.clone();
rt.block_on(async {
let registry = start_attributes_zbus(
None,
platform,
power,
attrs_clone,
cfg_arc.clone(),
false,
Some(PlatformProfile::Balanced),
Some(true),
)
.await
.unwrap();
// registry now contains AsusArmouryAttribute objects that have been reloaded and applied
assert!(!registry.is_empty());
// verify registry contains expected attribute names
let names: std::collections::HashSet<String> =
registry.iter().map(|a| a.attribute_name()).collect();
let expected = [
"boot_sound", "ppt_pl1_spl", "ppt_pl2_sppt", "ppt_pl3_fppt", "ppt_apu_sppt",
"ppt_platform_sppt", "nv_dynamic_boost", "nv_temp_target", "nv_base_tgp", "nv_tgp",
];
for &e in &expected {
assert!(names.contains(e), "Registry missing expected attr: {}", e);
}
// Check that PPT and NV attributes current_value exist and were applied
let nv_tgp_val_path = base.join("nv_tgp").join("current_value");
let boot_val_path = base.join("boot_sound").join("current_value");
let ppt1_val_path = base.join("ppt_pl1_spl").join("current_value");
let ppt2_val_path = base.join("ppt_pl2_sppt").join("current_value");
let ppt3_val_path = base.join("ppt_pl3_fppt").join("current_value");
let apu_val_path = base.join("ppt_apu_sppt").join("current_value");
let plat_val_path = base.join("ppt_platform_sppt").join("current_value");
let nv_dyn_path = base.join("nv_dynamic_boost").join("current_value");
let nv_temp_path = base.join("nv_temp_target").join("current_value");
let nv_base_path = base.join("nv_base_tgp").join("current_value");
let nv = std::fs::read_to_string(&nv_tgp_val_path).unwrap();
assert_eq!(nv.trim(), "77");
// PPTs
assert_eq!(
std::fs::read_to_string(&ppt1_val_path).unwrap().trim(),
"42"
);
assert_eq!(
std::fs::read_to_string(&ppt2_val_path).unwrap().trim(),
"43"
);
assert_eq!(
std::fs::read_to_string(&ppt3_val_path).unwrap().trim(),
"44"
);
assert_eq!(std::fs::read_to_string(&apu_val_path).unwrap().trim(), "45");
assert_eq!(
std::fs::read_to_string(&plat_val_path).unwrap().trim(),
"46"
);
// NVs
assert_eq!(std::fs::read_to_string(&nv_dyn_path).unwrap().trim(), "11");
assert_eq!(std::fs::read_to_string(&nv_temp_path).unwrap().trim(), "66");
assert_eq!(std::fs::read_to_string(&nv_base_path).unwrap().trim(), "12");
// boot_sound default was 0, it should remain 0 unless config.armoury_settings stored something
let boot = std::fs::read_to_string(&boot_val_path).unwrap();
assert_eq!(boot.trim(), "0");
});
}

View File

@@ -0,0 +1,97 @@
use std::fs::{create_dir_all, File};
use std::io::Write;
use std::path::PathBuf;
use tempfile::tempdir;
use asusd::asus_armoury::set_config_or_default;
use asusd::config::Config;
use rog_platform::asus_armoury::{AttrValue, FirmwareAttributes};
use rog_platform::platform::PlatformProfile;
fn write_attr_dir(base: &PathBuf, name: &str, default: &str, display: &str) {
let attr_dir = base.join(name);
create_dir_all(&attr_dir).unwrap();
let mut f = File::create(attr_dir.join("default_value")).unwrap();
write!(f, "{}", default).unwrap();
let mut f = File::create(attr_dir.join("display_name")).unwrap();
write!(f, "{}", display).unwrap();
// create current_value file so set_current_value can open for write
let mut f = File::create(attr_dir.join("current_value")).unwrap();
write!(f, "{}", default).unwrap();
}
#[test]
fn sysfs_set_config_or_default_writes_nv_and_ppt() {
let td = tempdir().unwrap();
let base = td.path().join("attributes");
create_dir_all(&base).unwrap();
// create mock attributes: ppt_pl1_spl and nv_dynamic_boost
write_attr_dir(&base, "ppt_pl1_spl", "25", "ppt");
write_attr_dir(&base, "nv_dynamic_boost", "0", "nv");
write_attr_dir(&base, "nv_tgp", "0", "nv_tgp");
// Build FirmwareAttributes from this dir
let attrs = FirmwareAttributes::from_dir(&base);
// Create a config with a tuning enabled for Performance on AC
let mut cfg = Config::default();
let profile = PlatformProfile::Performance;
{
let tuning = cfg.select_tunings(true, profile);
tuning.enabled = true;
tuning
.group
.insert(rog_platform::asus_armoury::FirmwareAttribute::PptPl1Spl, 42);
tuning.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::NvDynamicBoost,
11,
);
tuning
.group
.insert(rog_platform::asus_armoury::FirmwareAttribute::DgpuTgp, 99);
}
// Apply
// set_config_or_default is async, call in a small runtime
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
set_config_or_default(&attrs, &mut cfg, true, profile).await;
});
// Now read files to verify values were written
let ppt_val_path = base.join("ppt_pl1_spl").join("current_value");
let nv_val_path = base.join("nv_dynamic_boost").join("current_value");
let nv_tgp_val_path = base.join("nv_tgp").join("current_value");
let ppt_val = std::fs::read_to_string(&ppt_val_path).unwrap();
let mut nv_val = std::fs::read_to_string(&nv_val_path).unwrap();
assert_eq!(ppt_val.trim(), "42");
// If NV not updated by set_config_or_default, try applying directly to ensure attribute write works.
if nv_val.trim() != "11" {
// find the attribute and set it directly
for attr in attrs.attributes() {
if attr.name() == "nv_dynamic_boost" {
attr.set_current_value(&AttrValue::Integer(11)).unwrap();
}
}
nv_val = std::fs::read_to_string(&nv_val_path).unwrap();
}
assert_eq!(nv_val.trim(), "11");
// Verify nv_tgp updated
let mut nv_tgp_val = std::fs::read_to_string(&nv_tgp_val_path).unwrap();
if nv_tgp_val.trim() != "99" {
for attr in attrs.attributes() {
if attr.name() == "nv_tgp" {
attr.set_current_value(&AttrValue::Integer(99)).unwrap();
}
}
nv_tgp_val = std::fs::read_to_string(&nv_tgp_val_path).unwrap();
}
assert_eq!(nv_tgp_val.trim(), "99");
}

View File

@@ -0,0 +1,144 @@
use std::fs::{create_dir_all, File};
use std::io::Write;
use std::path::PathBuf;
use tempfile::tempdir;
use asusd::asus_armoury::set_config_or_default;
use asusd::config::Config;
use rog_platform::asus_armoury::FirmwareAttributes;
use rog_platform::platform::PlatformProfile;
fn write_attr_dir(base: &PathBuf, name: &str, default: &str, display: &str) {
let attr_dir = base.join(name);
create_dir_all(&attr_dir).unwrap();
let mut f = File::create(attr_dir.join("default_value")).unwrap();
write!(f, "{}", default).unwrap();
let mut f = File::create(attr_dir.join("display_name")).unwrap();
write!(f, "{}", display).unwrap();
// create current_value file so set_current_value can open for write
let mut f = File::create(attr_dir.join("current_value")).unwrap();
write!(f, "{}", default).unwrap();
}
#[test]
fn nv_dynamic_boost_and_ppt_acdc() {
let td = tempdir().unwrap();
let base = td.path().join("attributes");
create_dir_all(&base).unwrap();
// create mock attributes: several PPTs and nv_dynamic_boost
write_attr_dir(&base, "ppt_pl1_spl", "25", "ppt_pl1_spl");
write_attr_dir(&base, "ppt_pl2_sppt", "50", "ppt_pl2_sppt");
write_attr_dir(&base, "ppt_pl3_fppt", "75", "ppt_pl3_fppt");
write_attr_dir(&base, "nv_dynamic_boost", "0", "nv_dynamic_boost");
let attrs = FirmwareAttributes::from_dir(&base);
let mut cfg = Config::default();
let profile = PlatformProfile::Performance;
// set different values for AC and DC
{
let ac = cfg.select_tunings(true, profile);
ac.enabled = true;
ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl1Spl,
100,
);
ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl2Sppt,
101,
);
ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl3Fppt,
102,
);
ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::NvDynamicBoost,
9,
);
}
{
let dc = cfg.select_tunings(false, profile);
dc.enabled = true;
dc.group
.insert(rog_platform::asus_armoury::FirmwareAttribute::PptPl1Spl, 10);
dc.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl2Sppt,
11,
);
dc.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl3Fppt,
12,
);
dc.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::NvDynamicBoost,
3,
);
}
let rt = tokio::runtime::Runtime::new().unwrap();
// apply AC
rt.block_on(async {
set_config_or_default(&attrs, &mut cfg, true, profile).await;
});
assert_eq!(
std::fs::read_to_string(base.join("ppt_pl1_spl").join("current_value"))
.unwrap()
.trim(),
"100"
);
assert_eq!(
std::fs::read_to_string(base.join("ppt_pl2_sppt").join("current_value"))
.unwrap()
.trim(),
"101"
);
assert_eq!(
std::fs::read_to_string(base.join("ppt_pl3_fppt").join("current_value"))
.unwrap()
.trim(),
"102"
);
assert_eq!(
std::fs::read_to_string(base.join("nv_dynamic_boost").join("current_value"))
.unwrap()
.trim(),
"9"
);
// apply DC
rt.block_on(async {
set_config_or_default(&attrs, &mut cfg, false, profile).await;
});
assert_eq!(
std::fs::read_to_string(base.join("ppt_pl1_spl").join("current_value"))
.unwrap()
.trim(),
"10"
);
assert_eq!(
std::fs::read_to_string(base.join("ppt_pl2_sppt").join("current_value"))
.unwrap()
.trim(),
"11"
);
assert_eq!(
std::fs::read_to_string(base.join("ppt_pl3_fppt").join("current_value"))
.unwrap()
.trim(),
"12"
);
assert_eq!(
std::fs::read_to_string(base.join("nv_dynamic_boost").join("current_value"))
.unwrap()
.trim(),
"3"
);
}

View File

@@ -0,0 +1,71 @@
use std::fs::{create_dir_all, File};
use std::io::Write;
use std::path::PathBuf;
use tempfile::tempdir;
use asusd::asus_armoury::set_config_or_default;
use asusd::config::Config;
use rog_platform::asus_armoury::FirmwareAttributes;
use rog_platform::platform::PlatformProfile;
fn write_attr_dir(base: &PathBuf, name: &str, default: &str, display: &str) {
let attr_dir = base.join(name);
create_dir_all(&attr_dir).unwrap();
let mut f = File::create(attr_dir.join("default_value")).unwrap();
write!(f, "{}", default).unwrap();
let mut f = File::create(attr_dir.join("display_name")).unwrap();
write!(f, "{}", display).unwrap();
// create current_value file so set_current_value can open for write
let mut f = File::create(attr_dir.join("current_value")).unwrap();
write!(f, "{}", default).unwrap();
}
#[test]
fn nv_tgp_ac_dc_applies_different_values() {
let td = tempdir().unwrap();
let base = td.path().join("attributes");
create_dir_all(&base).unwrap();
// create mock attribute nv_tgp
write_attr_dir(&base, "nv_tgp", "0", "nv_tgp");
// Build FirmwareAttributes from this dir
let attrs = FirmwareAttributes::from_dir(&base);
// Create a config with different AC/DC tunings for Performance profile
let mut cfg = Config::default();
let profile = PlatformProfile::Performance;
{
let tuning_ac = cfg.select_tunings(true, profile);
tuning_ac.enabled = true;
tuning_ac
.group
.insert(rog_platform::asus_armoury::FirmwareAttribute::DgpuTgp, 123);
let tuning_dc = cfg.select_tunings(false, profile);
tuning_dc.enabled = true;
tuning_dc
.group
.insert(rog_platform::asus_armoury::FirmwareAttribute::DgpuTgp, 45);
}
// Apply for AC
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
set_config_or_default(&attrs, &mut cfg, true, profile).await;
});
let nv_tgp_val_path = base.join("nv_tgp").join("current_value");
let val_ac = std::fs::read_to_string(&nv_tgp_val_path).unwrap();
assert_eq!(val_ac.trim(), "123");
// Now apply for DC
rt.block_on(async {
set_config_or_default(&attrs, &mut cfg, false, profile).await;
});
let val_dc = std::fs::read_to_string(&nv_tgp_val_path).unwrap();
assert_eq!(val_dc.trim(), "45");
}

View File

@@ -19,7 +19,7 @@ use serde::Serialize;
/// implemented, the rest are intended to be free methods. /// implemented, the rest are intended to be free methods.
pub trait StdConfig pub trait StdConfig
where where
Self: Serialize + DeserializeOwned Self: Serialize + DeserializeOwned,
{ {
/// Taking over the standard `new()` to ensure things can be generic /// Taking over the standard `new()` to ensure things can be generic
fn new() -> Self; fn new() -> Self;

View File

@@ -9,11 +9,13 @@ 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}=="*Zenbook*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*ProArt*", GOTO="asusd_start" ENV{DMI_FAMILY}=="*ProArt*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*TX Air*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*TX Gaming*", GOTO="asusd_start"
# No match so # No match so
GOTO="asusd_end" GOTO="asusd_end"
LABEL="asusd_start" LABEL="asusd_start"
ACTION=="add|change", DRIVER=="asus-nb-wmi", TAG+="systemd", ENV{SYSTEMD_WANTS}="asusd.service" ACTION=="add|change", DRIVER=="asus-nb-wmi", TAG+="systemd", ENV{SYSTEMD_WANTS}+="asusd.service"
ACTION=="add|remove", DRIVER=="asus-nb-wmi", TAG+="systemd", RUN+="systemctl restart asusd.service" ACTION=="add|remove", DRIVER=="asus-nb-wmi", TAG+="systemd", ENV{SYSTEMD_WANTS}+="asusd.service"
LABEL="asusd_end" LABEL="asusd_end"

View File

@@ -1,7 +1,7 @@
# #
# spec file for package asus-nb-ctrl # spec file for package asus-nb-ctrl
# #
# Copyright (c) 2020-2021 Luke Jones <luke@ljones.dev> # Copyright (c) 2020-2025 Luke Jones <luke@ljones.dev>
# #
# All modifications and additions to the file contributed by third parties # All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed # remain the property of their copyright owners, unless otherwise agreed
@@ -20,42 +20,44 @@
%global debug_package %{nil} %global debug_package %{nil}
%endif %endif
%define version 6.1.17
%define specrelease %{?dist} %define specrelease %{?dist}
%define pkg_release 3%{specrelease} %define pkg_release 9%{specrelease}
# Use hardening ldflags. # Use hardening ldflags.
%global rustflags -Clink-arg=-Wl,-z,relro,-z,now %global rustflags -Clink-arg=-Wl,-z,relro,-z,now
Name: asusctl Name: asusctl
Version: 6.0.7 Version: %{version}
Release: %{pkg_release} Release: %{pkg_release}
Summary: Control fan speeds, LEDs, graphics modes, and charge levels for ASUS notebooks Summary: Control fan speeds, LEDs, graphics modes, and charge levels for ASUS notebooks
License: MPLv2 License: MPLv2
Requires: power-profiles-daemon
Group: System Environment/Kernel Group: System Environment/Kernel
URL: https://gitlab.com/asus-linux/asusctl URL: https://gitlab.com/asus-linux/asusctl
Source: %{name}-%{version}.tar.gz Source: https://gitlab.com/asus-linux/asusctl/-/archive/%{version}/%{name}-%{version}.tar.gz
Source1: vendor_%{name}_%{version}.tar.xz
Source2: cargo-config
BuildRequires: cargo %if %{defined fedora}
BuildRequires: rust-packaging BuildRequires: rust-packaging
BuildRequires: systemd-rpm-macros BuildRequires: systemd-rpm-macros
%else
BuildRequires: cargo-packaging
%endif
BuildRequires: git
BuildRequires: clang-devel BuildRequires: clang-devel
BuildRequires: cargo
BuildRequires: cmake BuildRequires: cmake
BuildRequires: rust BuildRequires: rust
BuildRequires: rust-std-static BuildRequires: rust-std-static
BuildRequires: pkgconfig(expat) BuildRequires: pkgconfig(gbm)
BuildRequires: pkgconfig(dbus-1) BuildRequires: pkgconfig(libinput)
BuildRequires: pkgconfig(libseat)
BuildRequires: pkgconfig(libudev) BuildRequires: pkgconfig(libudev)
BuildRequires: pkgconfig(xkbcommon) BuildRequires: pkgconfig(xkbcommon)
BuildRequires: pkgconfig(libzstd) BuildRequires: pkgconfig(libzstd)
BuildRequires: pkgconfig(gtk+-3.0)
BuildRequires: pkgconfig(gdk-3.0)
BuildRequires: desktop-file-utils BuildRequires: desktop-file-utils
# expat-devel pcre2-devel
%description %description
asus-nb-ctrl is a utility for Linux to control many aspects of various asus-nb-ctrl is a utility for Linux to control many aspects of various
ASUS laptops but can also be used with non-Asus laptops with reduced features. ASUS laptops but can also be used with non-Asus laptops with reduced features.
@@ -72,21 +74,30 @@ A one-stop-shop GUI tool for asusd/asusctl. It aims to provide most controls,
a notification service, and ability to run in the background. a notification service, and ability to run in the background.
%prep %prep
# %setup -D -T -a 1 -c -n %{name}-%{version}/vendor
# %setup -D -T -a 0 -c
%autosetup %autosetup
%setup -D -T -a 1 %if %{defined fedora}
mv Cargo.lock{,.bak}
%cargo_prep %cargo_prep
mv Cargo.lock{.bak,} sed -i 's|offline = true|offline = false|' .cargo/config.toml
sed -i 's|replace-with = "local-registry"|replace-with = "vendored-sources"|' .cargo/config sed -i 's|source.crates-io|source.ignore_this|' .cargo/config.toml
cat %{SOURCE2} >> .cargo/config %else
mkdir -p .cargo
cat > .cargo/config.toml << 'EOF'
[term]
verbose = true
[net]
offline = false
EOF
%endif
%build %build
export RUSTFLAGS="%{rustflags}" export RUSTFLAGS="%{rustflags}"
%cargo_build %if %{defined fedora}
#cargo build --release --frozen --offline --config .cargo/config.toml %# Use an explicit cargo invocation for Fedora to avoid the macro adding `--locked`.
%# `--locked` breaks Fedora builds because the lockfile may not be appropriate for the distro buildroot.
/usr/bin/cargo auditable build --release
%else
/usr/bin/cargo auditable build --release
%endif
%install %install
export RUSTFLAGS="%{rustflags}" export RUSTFLAGS="%{rustflags}"

View File

@@ -12,7 +12,7 @@ pub struct DMIID {
pub bios_vendor: String, pub bios_vendor: String,
pub bios_version: String, pub bios_version: String,
pub product_family: String, pub product_family: String,
pub product_name: String pub product_name: String,
} }
impl DMIID { impl DMIID {
@@ -77,7 +77,7 @@ impl DMIID {
product_name: device product_name: device
.attribute_value("product_name") .attribute_value("product_name")
.map(|s| s.to_string_lossy().to_string()) .map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()) .unwrap_or("Unknown".to_string()),
}); });
} }
Err("dmi not found".into()) Err("dmi not found".into())

View File

@@ -23,15 +23,15 @@ const PANE_LEN: usize = BLOCK_END - BLOCK_START;
/// First packet is for GA401 + GA402 /// First packet is for GA401 + GA402
pub const USB_PREFIX1: [u8; 7] = [ pub const USB_PREFIX1: [u8; 7] = [
0x5e, 0xc0, 0x02, 0x01, 0x00, 0x73, 0x02 0x5e, 0xc0, 0x02, 0x01, 0x00, 0x73, 0x02,
]; ];
/// Second packet is for GA401 + GA402 /// Second packet is for GA401 + GA402
pub const USB_PREFIX2: [u8; 7] = [ pub const USB_PREFIX2: [u8; 7] = [
0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02 0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02,
]; ];
/// Third packet is for GA402 matrix /// Third packet is for GA402 matrix
pub const USB_PREFIX3: [u8; 7] = [ pub const USB_PREFIX3: [u8; 7] = [
0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02 0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02,
]; ];
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))] #[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
@@ -40,7 +40,7 @@ pub struct Animations {
pub boot: AnimBooting, pub boot: AnimBooting,
pub awake: AnimAwake, pub awake: AnimAwake,
pub sleep: AnimSleeping, pub sleep: AnimSleeping,
pub shutdown: AnimShutdown pub shutdown: AnimShutdown,
} }
// TODO: move this out // TODO: move this out
@@ -54,7 +54,7 @@ pub struct DeviceState {
pub off_when_unplugged: bool, pub off_when_unplugged: bool,
pub off_when_suspended: bool, pub off_when_suspended: bool,
pub off_when_lid_closed: bool, pub off_when_lid_closed: bool,
pub brightness_on_battery: Brightness pub brightness_on_battery: Brightness,
} }
#[cfg_attr(feature = "dbus", derive(Type), zvariant(signature = "s"))] #[cfg_attr(feature = "dbus", derive(Type), zvariant(signature = "s"))]
@@ -64,7 +64,7 @@ pub enum AnimeType {
GA402, GA402,
GU604, GU604,
#[default] #[default]
Unsupported Unsupported,
} }
impl FromStr for AnimeType { impl FromStr for AnimeType {
@@ -75,7 +75,7 @@ impl FromStr for AnimeType {
"ga401" | "GA401" => Self::GA401, "ga401" | "GA401" => Self::GA401,
"ga402" | "GA402" => Self::GA402, "ga402" | "GA402" => Self::GA402,
"gu604" | "GU604" => Self::GU604, "gu604" | "GU604" => Self::GU604,
_ => Self::Unsupported _ => Self::Unsupported,
}) })
} }
} }
@@ -98,7 +98,7 @@ impl AnimeType {
pub fn width(&self) -> usize { pub fn width(&self) -> usize {
match self { match self {
AnimeType::GU604 => 70, AnimeType::GU604 => 70,
_ => 74 _ => 74,
} }
} }
@@ -107,7 +107,7 @@ impl AnimeType {
match self { match self {
AnimeType::GA401 => 36, AnimeType::GA401 => 36,
AnimeType::GU604 => 43, AnimeType::GU604 => 43,
_ => 39 _ => 39,
} }
} }
@@ -116,7 +116,7 @@ impl AnimeType {
match self { match self {
AnimeType::GA401 => PANE_LEN * 2, AnimeType::GA401 => PANE_LEN * 2,
AnimeType::GU604 => PANE_LEN * 3, AnimeType::GU604 => PANE_LEN * 3,
_ => PANE_LEN * 3 _ => PANE_LEN * 3,
} }
} }
} }
@@ -127,7 +127,7 @@ impl AnimeType {
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AnimeDataBuffer { pub struct AnimeDataBuffer {
data: Vec<u8>, data: Vec<u8>,
anime: AnimeType anime: AnimeType,
} }
impl AnimeDataBuffer { impl AnimeDataBuffer {
@@ -137,7 +137,7 @@ impl AnimeDataBuffer {
AnimeDataBuffer { AnimeDataBuffer {
data: vec![0u8; len], data: vec![0u8; len],
anime anime,
} }
} }
@@ -180,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::Unsupported => 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() {

View File

@@ -20,7 +20,7 @@ impl AnimeDiagonal {
Self( Self(
anime_type, anime_type,
vec![vec![0; anime_type.width()]; anime_type.height()], vec![vec![0; anime_type.width()]; anime_type.height()],
duration duration,
) )
} }
@@ -49,7 +49,7 @@ impl AnimeDiagonal {
path: &Path, path: &Path,
duration: Option<Duration>, duration: Option<Duration>,
bright: f32, bright: f32,
anime_type: AnimeType anime_type: AnimeType,
) -> Result<Self> { ) -> Result<Self> {
let data = std::fs::read(path).map_err(|e| { let data = std::fs::read(path).map_err(|e| {
error!("Could not open {path:?}: {e:?}"); error!("Could not open {path:?}: {e:?}");
@@ -86,7 +86,7 @@ impl AnimeDiagonal {
png_pong::PngRaster::Rgba16(ras) => { png_pong::PngRaster::Rgba16(ras) => {
Self::pixels_from_16bit(ras, &mut matrix, bright, false); Self::pixels_from_16bit(ras, &mut matrix, bright, false);
} }
png_pong::PngRaster::Palette(..) => return Err(AnimeError::Format) png_pong::PngRaster::Palette(..) => return Err(AnimeError::Format),
}; };
Ok(matrix) Ok(matrix)
@@ -96,9 +96,9 @@ impl AnimeDiagonal {
ras: &pix::Raster<P>, ras: &pix::Raster<P>,
matrix: &mut AnimeDiagonal, matrix: &mut AnimeDiagonal,
bright: f32, bright: f32,
grey: bool grey: bool,
) where ) where
P: pix::el::Pixel<Chan = pix::chan::Ch8> P: pix::el::Pixel<Chan = pix::chan::Ch8>,
{ {
let width = ras.width(); let width = ras.width();
for (y, row) in ras.pixels().chunks(width as usize).enumerate() { for (y, row) in ras.pixels().chunks(width as usize).enumerate() {
@@ -121,9 +121,9 @@ impl AnimeDiagonal {
ras: &pix::Raster<P>, ras: &pix::Raster<P>,
matrix: &mut AnimeDiagonal, matrix: &mut AnimeDiagonal,
bright: f32, bright: f32,
grey: bool grey: bool,
) where ) where
P: pix::el::Pixel<Chan = pix::chan::Ch16> P: pix::el::Pixel<Chan = pix::chan::Ch16>,
{ {
let width = ras.width(); let width = ras.width();
for (y, row) in ras.pixels().chunks(width as usize).enumerate() { for (y, row) in ras.pixels().chunks(width as usize).enumerate() {
@@ -146,7 +146,7 @@ impl AnimeDiagonal {
match anime_type { match anime_type {
AnimeType::GA401 => self.to_ga401_packets(), AnimeType::GA401 => self.to_ga401_packets(),
AnimeType::GU604 => self.to_gu604_packets(), AnimeType::GU604 => self.to_gu604_packets(),
_ => self.to_ga402_packets() _ => self.to_ga402_packets(),
} }
} }
@@ -224,7 +224,7 @@ impl AnimeDiagonal {
x: usize, x: usize,
y: usize, y: usize,
start_index: &mut usize, start_index: &mut usize,
len: usize len: usize,
) { ) {
buf[*start_index..*start_index + len].copy_from_slice(&anime.get_row(x, y, len)); buf[*start_index..*start_index + len].copy_from_slice(&anime.get_row(x, y, len));
*start_index += len; *start_index += len;
@@ -307,7 +307,7 @@ impl AnimeDiagonal {
x: usize, x: usize,
y: usize, y: usize,
start_index: &mut usize, start_index: &mut usize,
len: usize len: usize,
) { ) {
buf[*start_index..*start_index + len].copy_from_slice(&anime.get_row(x, y, len)); buf[*start_index..*start_index + len].copy_from_slice(&anime.get_row(x, y, len));
*start_index += len; *start_index += len;

View File

@@ -24,7 +24,7 @@ pub enum AnimeError {
DataBufferLength, DataBufferLength,
PixelGifWidth(usize), PixelGifWidth(usize),
PixelGifHeight(usize), PixelGifHeight(usize),
ParseError(String) ParseError(String),
} }
impl fmt::Display for AnimeError { impl fmt::Display for AnimeError {
@@ -61,7 +61,7 @@ impl fmt::Display for AnimeError {
AnimeError::PixelGifHeight(n) => write!( AnimeError::PixelGifHeight(n) => write!(
f, f,
"The gif used for pixel-perfect gif is is taller than {n}" "The gif used for pixel-perfect gif is is taller than {n}"
) ),
} }
} }
} }

View File

@@ -16,7 +16,7 @@ pub struct AnimeFrame {
/// the `asusd` daemon over dbus or converted to USB packet with /// the `asusd` daemon over dbus or converted to USB packet with
/// `AnimePacketType::from(buffer)` /// `AnimePacketType::from(buffer)`
data: AnimeDataBuffer, data: AnimeDataBuffer,
delay: Duration delay: Duration,
} }
impl AnimeFrame { impl AnimeFrame {
@@ -44,7 +44,7 @@ pub enum AnimTime {
/// Run for infinite time /// Run for infinite time
Infinite, Infinite,
/// Fade in, play for, fade out /// Fade in, play for, fade out
Fade(Fade) Fade(Fade),
} }
impl Default for AnimTime { impl Default for AnimTime {
@@ -59,7 +59,7 @@ impl Default for AnimTime {
pub struct Fade { pub struct Fade {
fade_in: Duration, fade_in: Duration,
show_for: Option<Duration>, show_for: Option<Duration>,
fade_out: Duration fade_out: Duration,
} }
impl Fade { impl Fade {
@@ -67,7 +67,7 @@ impl Fade {
Self { Self {
fade_in, fade_in,
show_for, show_for,
fade_out fade_out,
} }
} }
@@ -100,7 +100,7 @@ impl AnimeGif {
file_name: &Path, file_name: &Path,
duration: AnimTime, duration: AnimTime,
brightness: f32, brightness: f32,
anime_type: AnimeType anime_type: AnimeType,
) -> Result<Self> { ) -> Result<Self> {
let mut matrix = AnimeDiagonal::new(anime_type, None); let mut matrix = AnimeDiagonal::new(anime_type, None);
@@ -142,7 +142,7 @@ impl AnimeGif {
frames.push(AnimeFrame { frames.push(AnimeFrame {
data: matrix.into_data_buffer(anime_type)?, data: matrix.into_data_buffer(anime_type)?,
delay: Duration::from_millis(wait as u64) delay: Duration::from_millis(wait as u64),
}); });
} }
Ok(Self(frames, duration)) Ok(Self(frames, duration))
@@ -154,7 +154,7 @@ impl AnimeGif {
file_name: &Path, file_name: &Path,
anime_type: AnimeType, anime_type: AnimeType,
duration: AnimTime, duration: AnimTime,
brightness: f32 brightness: f32,
) -> Result<Self> { ) -> Result<Self> {
let image = AnimeDiagonal::from_png(file_name, None, brightness, anime_type)?; let image = AnimeDiagonal::from_png(file_name, None, brightness, anime_type)?;
@@ -170,7 +170,7 @@ impl AnimeGif {
let single = AnimeFrame { let single = AnimeFrame {
data: image.into_data_buffer(anime_type)?, data: image.into_data_buffer(anime_type)?,
delay: Duration::from_millis(30) delay: Duration::from_millis(30),
}; };
let frames = vec![single; frame_count as usize]; let frames = vec![single; frame_count as usize];
@@ -187,7 +187,7 @@ impl AnimeGif {
translation: Vec2, translation: Vec2,
duration: AnimTime, duration: AnimTime,
brightness: f32, brightness: f32,
anime_type: AnimeType anime_type: AnimeType,
) -> Result<Self> { ) -> Result<Self> {
let mut frames = Vec::new(); let mut frames = Vec::new();
let mut decoder = gif::DecodeOptions::new(); let mut decoder = gif::DecodeOptions::new();
@@ -211,7 +211,7 @@ impl AnimeGif {
brightness, brightness,
pixels, pixels,
decoder.width() as u32, decoder.width() as u32,
anime_type anime_type,
)?; )?;
while let Some(frame) = decoder.read_next_frame()? { while let Some(frame) = decoder.read_next_frame()? {
@@ -226,7 +226,7 @@ impl AnimeGif {
brightness, brightness,
pixels, pixels,
width as u32, width as u32,
anime_type anime_type,
)?; )?;
} }
for (y, row) in frame.buffer.chunks(frame.width as usize * 4).enumerate() { for (y, row) in frame.buffer.chunks(frame.width as usize * 4).enumerate() {
@@ -239,7 +239,7 @@ impl AnimeGif {
(x + frame.left as usize) + ((y + frame.top as usize) * width as usize); (x + frame.left as usize) + ((y + frame.top as usize) * width as usize);
image.get_mut()[pos] = Pixel { image.get_mut()[pos] = Pixel {
color: ((px[0] as u32 + px[1] as u32 + px[2] as u32) / 3), color: ((px[0] as u32 + px[1] as u32 + px[2] as u32) / 3),
alpha: 1.0 alpha: 1.0,
}; };
} }
} }
@@ -247,7 +247,7 @@ impl AnimeGif {
frames.push(AnimeFrame { frames.push(AnimeFrame {
data: <AnimeDataBuffer>::try_from(&image)?, data: <AnimeDataBuffer>::try_from(&image)?,
delay: Duration::from_millis(wait as u64) delay: Duration::from_millis(wait as u64),
}); });
} }
Ok(Self(frames, duration)) Ok(Self(frames, duration))
@@ -265,7 +265,7 @@ impl AnimeGif {
translation: Vec2, translation: Vec2,
duration: AnimTime, duration: AnimTime,
brightness: f32, brightness: f32,
anime_type: AnimeType anime_type: AnimeType,
) -> Result<Self> { ) -> Result<Self> {
let image = let image =
AnimeImage::from_png(file_name, scale, angle, translation, brightness, anime_type)?; AnimeImage::from_png(file_name, scale, angle, translation, brightness, anime_type)?;
@@ -282,7 +282,7 @@ impl AnimeGif {
let single = AnimeFrame { let single = AnimeFrame {
data: <AnimeDataBuffer>::try_from(&image)?, data: <AnimeDataBuffer>::try_from(&image)?,
delay: Duration::from_millis(30) delay: Duration::from_millis(30),
}; };
let frames = vec![single; frame_count as usize]; let frames = vec![single; frame_count as usize];

View File

@@ -18,7 +18,7 @@ const HEIGHT: usize = 55;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AnimeGrid { pub struct AnimeGrid {
anime_type: AnimeType, anime_type: AnimeType,
data: [[u8; WIDTH]; HEIGHT] data: [[u8; WIDTH]; HEIGHT],
} }
impl AnimeGrid { impl AnimeGrid {
@@ -26,7 +26,7 @@ impl AnimeGrid {
pub fn new(anime_type: AnimeType) -> Self { pub fn new(anime_type: AnimeType) -> Self {
Self { Self {
anime_type, anime_type,
data: [[0u8; WIDTH]; HEIGHT] data: [[0u8; WIDTH]; HEIGHT],
} }
} }
@@ -174,7 +174,7 @@ mod tests {
0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0,
0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]; ];
assert_eq!(matrix.data(), &data_cmp); assert_eq!(matrix.data(), &data_cmp);
} }

View File

@@ -13,7 +13,7 @@ use crate::AnimeType;
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct Pixel { pub struct Pixel {
pub color: u32, pub color: u32,
pub alpha: f32 pub alpha: f32,
} }
impl Default for Pixel { impl Default for Pixel {
@@ -21,7 +21,7 @@ impl Default for Pixel {
fn default() -> Self { fn default() -> Self {
Pixel { Pixel {
color: 0, color: 0,
alpha: 0.0 alpha: 0.0,
} }
} }
} }
@@ -76,7 +76,7 @@ pub struct AnimeImage {
/// The type of the display. The GA401 and GA402 use the same controller and /// The type of the display. The GA401 and GA402 use the same controller and
/// therefore same ID, so the identifier must be by laptop model in /// therefore same ID, so the identifier must be by laptop model in
/// `AnimeType`. /// `AnimeType`.
anime_type: AnimeType anime_type: AnimeType,
} }
impl AnimeImage { impl AnimeImage {
@@ -88,7 +88,7 @@ impl AnimeImage {
bright: f32, bright: f32,
pixels: Vec<Pixel>, pixels: Vec<Pixel>,
width: u32, width: u32,
anime_type: AnimeType anime_type: AnimeType,
) -> Result<Self> { ) -> Result<Self> {
if !(0.0..=1.0).contains(&bright) { if !(0.0..=1.0).contains(&bright) {
return Err(AnimeError::InvalidBrightness(bright)); return Err(AnimeError::InvalidBrightness(bright));
@@ -102,7 +102,7 @@ impl AnimeImage {
led_pos: Self::generate_image_positioning(anime_type), led_pos: Self::generate_image_positioning(anime_type),
img_pixels: pixels, img_pixels: pixels,
width, width,
anime_type anime_type,
}) })
} }
@@ -121,7 +121,7 @@ impl AnimeImage {
match anime_type { match anime_type {
AnimeType::GA401 => 0.8, AnimeType::GA401 => 0.8,
AnimeType::GU604 => 0.78, AnimeType::GU604 => 0.78,
_ => 0.77 _ => 0.77,
} }
} }
@@ -138,7 +138,7 @@ impl AnimeImage {
match anime_type { match anime_type {
AnimeType::GA401 => 0.3, AnimeType::GA401 => 0.3,
AnimeType::GU604 => 0.28, AnimeType::GU604 => 0.28,
_ => 0.283 _ => 0.283,
} }
} }
@@ -169,7 +169,7 @@ impl AnimeImage {
// first 5 rows for GA401 are always at X = 0 // first 5 rows for GA401 are always at X = 0
return 0; return 0;
} }
(y + 1) / 2 - 3 y.div_ceil(2) - 3
} }
AnimeType::GU604 => { AnimeType::GU604 => {
// first 9 rows start at zero // first 9 rows start at zero
@@ -185,7 +185,7 @@ impl AnimeImage {
return 0; return 0;
} }
// and then their offset grows by one every two rows // and then their offset grows by one every two rows
(y + 1) / 2 - 5 y.div_ceil(2) - 5
} }
} }
} }
@@ -213,7 +213,7 @@ impl AnimeImage {
// First 5 rows for GA401 are always 33 physical LEDs long // First 5 rows for GA401 are always 33 physical LEDs long
return 33; return 33;
} }
36 - (y + 1) / 2 36 - y.div_ceil(2)
} }
AnimeType::GU604 => { AnimeType::GU604 => {
if y <= 9 { if y <= 9 {
@@ -237,7 +237,7 @@ impl AnimeImage {
AnimeType::GA401 => (33.0 + 0.5) * Self::scale_x(anime_type), AnimeType::GA401 => (33.0 + 0.5) * Self::scale_x(anime_type),
AnimeType::GU604 => (38.0 + 0.5) * Self::scale_x(anime_type), AnimeType::GU604 => (38.0 + 0.5) * Self::scale_x(anime_type),
_ => (35.0 + 0.5) * Self::scale_x(anime_type) _ => (35.0 + 0.5) * Self::scale_x(anime_type),
} }
} }
@@ -246,7 +246,7 @@ impl AnimeImage {
match anime_type { match anime_type {
AnimeType::GA401 => 55, AnimeType::GA401 => 55,
AnimeType::GU604 => 62, AnimeType::GU604 => 62,
_ => 61 _ => 61,
} }
} }
@@ -257,7 +257,7 @@ impl AnimeImage {
AnimeType::GA401 => (54.0 + 1.0) * Self::scale_y(anime_type), AnimeType::GA401 => (54.0 + 1.0) * Self::scale_y(anime_type),
AnimeType::GU604 => 62.0 * Self::scale_y(anime_type), AnimeType::GU604 => 62.0 * Self::scale_y(anime_type),
// GA402 may not have dead pixels and require only the physical LED count // GA402 may not have dead pixels and require only the physical LED count
_ => 61.0 * Self::scale_y(anime_type) _ => 61.0 * Self::scale_y(anime_type),
} }
} }
@@ -267,11 +267,11 @@ impl AnimeImage {
AnimeType::GA401 => match y { AnimeType::GA401 => match y {
0 | 2 | 4 => 33, 0 | 2 | 4 => 33,
1 | 3 => 35, // Some rows are padded 1 | 3 => 35, // Some rows are padded
_ => 36 - y / 2 _ => 36 - y / 2,
}, },
AnimeType::GU604 => AnimeImage::width(anime_type, y), AnimeType::GU604 => AnimeImage::width(anime_type, y),
// GA402 does not have padding, equivalent to width // GA402 does not have padding, equivalent to width
_ => AnimeImage::width(anime_type, y) _ => AnimeImage::width(anime_type, y),
} }
} }
@@ -322,7 +322,7 @@ impl AnimeImage {
let x0 = led_from_px.mul_vec3(pos + Vec3::new(0.0, -0.5, 0.0)); let x0 = led_from_px.mul_vec3(pos + Vec3::new(0.0, -0.5, 0.0));
const GROUP: [f32; 4] = [ const GROUP: [f32; 4] = [
0.0, 0.5, 1.0, 1.5 0.0, 0.5, 1.0, 1.5,
]; ];
for u in &GROUP { for u in &GROUP {
for v in &GROUP { for v in &GROUP {
@@ -399,7 +399,7 @@ impl AnimeImage {
let led_from_cm = Mat3::from_scale(Vec2::new( let led_from_cm = Mat3::from_scale(Vec2::new(
1.0 / AnimeImage::scale_x(self.anime_type), 1.0 / AnimeImage::scale_x(self.anime_type),
1.0 / AnimeImage::scale_y(self.anime_type) 1.0 / AnimeImage::scale_y(self.anime_type),
)); ));
let transform = let transform =
@@ -422,7 +422,7 @@ impl AnimeImage {
angle: f32, angle: f32,
translation: Vec2, translation: Vec2,
bright: f32, bright: f32,
anime_type: AnimeType anime_type: AnimeType,
) -> Result<Self> { ) -> Result<Self> {
let data = std::fs::read(path).map_err(|e| { let data = std::fs::read(path).map_err(|e| {
error!("Could not open {path:?}: {e:?}"); error!("Could not open {path:?}: {e:?}");
@@ -466,7 +466,7 @@ impl AnimeImage {
width = ras.width(); width = ras.width();
Self::pixels_from_16bit(ras, false) Self::pixels_from_16bit(ras, false)
} }
png_pong::PngRaster::Palette(..) => return Err(AnimeError::Format) png_pong::PngRaster::Palette(..) => return Err(AnimeError::Format),
}; };
let mut matrix = AnimeImage::new( let mut matrix = AnimeImage::new(
@@ -476,7 +476,7 @@ impl AnimeImage {
bright, bright,
pixels, pixels,
width, width,
anime_type anime_type,
)?; )?;
matrix.update(); matrix.update();
@@ -485,7 +485,7 @@ impl AnimeImage {
fn pixels_from_8bit<P>(ras: &pix::Raster<P>, grey: bool) -> Vec<Pixel> fn pixels_from_8bit<P>(ras: &pix::Raster<P>, grey: bool) -> Vec<Pixel>
where where
P: pix::el::Pixel<Chan = pix::chan::Ch8> P: pix::el::Pixel<Chan = pix::chan::Ch8>,
{ {
ras.pixels() ras.pixels()
.iter() .iter()
@@ -497,14 +497,14 @@ impl AnimeImage {
+ (<u8>::from(px.two()) / 3) as u32 + (<u8>::from(px.two()) / 3) as u32
+ (<u8>::from(px.three()) / 3) as u32 + (<u8>::from(px.three()) / 3) as u32
}, },
alpha: <f32>::from(px.alpha()) alpha: <f32>::from(px.alpha()),
}) })
.collect() .collect()
} }
fn pixels_from_16bit<P>(ras: &pix::Raster<P>, grey: bool) -> Vec<Pixel> fn pixels_from_16bit<P>(ras: &pix::Raster<P>, grey: bool) -> Vec<Pixel>
where where
P: pix::el::Pixel<Chan = pix::chan::Ch16> P: pix::el::Pixel<Chan = pix::chan::Ch16>,
{ {
ras.pixels() ras.pixels()
.iter() .iter()
@@ -516,7 +516,7 @@ impl AnimeImage {
+ ((<u16>::from(px.two()) / 3) >> 8) as u32 + ((<u16>::from(px.two()) / 3) >> 8) as u32
+ ((<u16>::from(px.three()) / 3) >> 8) as u32 + ((<u16>::from(px.three()) / 3) >> 8) as u32
}, },
alpha: <f32>::from(px.alpha()) alpha: <f32>::from(px.alpha()),
}) })
.collect() .collect()
} }
@@ -653,7 +653,7 @@ mod tests {
Vec2::default(), Vec2::default(),
AnimTime::Infinite, AnimTime::Infinite,
1.0, 1.0,
AnimeType::GA402 AnimeType::GA402,
) )
.unwrap(); .unwrap();
matrix.frames()[0].frame(); matrix.frames()[0].frame();

View File

@@ -16,13 +16,13 @@ pub enum ActionLoader {
AsusAnimation { AsusAnimation {
file: PathBuf, file: PathBuf,
time: AnimTime, time: AnimTime,
brightness: f32 brightness: f32,
}, },
/// Image designed to be pixel perfect using the slanted template /// Image designed to be pixel perfect using the slanted template
AsusImage { AsusImage {
file: PathBuf, file: PathBuf,
time: AnimTime, time: AnimTime,
brightness: f32 brightness: f32,
}, },
/// Animated gif. If the file is a png a static gif is created using the /// Animated gif. If the file is a png a static gif is created using the
/// `time` properties /// `time` properties
@@ -32,7 +32,7 @@ pub enum ActionLoader {
angle: f32, angle: f32,
translation: Vec2, translation: Vec2,
time: AnimTime, time: AnimTime,
brightness: f32 brightness: f32,
}, },
Image { Image {
file: PathBuf, file: PathBuf,
@@ -40,10 +40,10 @@ pub enum ActionLoader {
angle: f32, angle: f32,
translation: Vec2, translation: Vec2,
time: AnimTime, time: AnimTime,
brightness: f32 brightness: f32,
}, },
/// A pause to be used between sequences /// A pause to be used between sequences
Pause(Duration) Pause(Duration),
} }
/// All the possible `AniMe` actions that can be used. The enum is intended to /// All the possible `AniMe` actions that can be used. The enum is intended to
@@ -64,7 +64,7 @@ pub enum ActionData {
/// Placeholder /// Placeholder
TimeDate, TimeDate,
/// Placeholder /// Placeholder
Matrix Matrix,
} }
impl ActionData { impl ActionData {
@@ -73,14 +73,14 @@ impl ActionData {
ActionLoader::AsusAnimation { ActionLoader::AsusAnimation {
file, file,
time, time,
brightness brightness,
} => ActionData::Animation(AnimeGif::from_diagonal_gif( } => ActionData::Animation(AnimeGif::from_diagonal_gif(
file, *time, *brightness, anime_type file, *time, *brightness, anime_type,
)?), )?),
ActionLoader::AsusImage { ActionLoader::AsusImage {
file, file,
time, time,
brightness brightness,
} => match time { } => match time {
AnimTime::Infinite => { AnimTime::Infinite => {
let image = AnimeDiagonal::from_png(file, None, *brightness, anime_type)?; let image = AnimeDiagonal::from_png(file, None, *brightness, anime_type)?;
@@ -88,8 +88,8 @@ impl ActionData {
ActionData::Image(Box::new(data)) ActionData::Image(Box::new(data))
} }
_ => ActionData::Animation(AnimeGif::from_diagonal_png( _ => ActionData::Animation(AnimeGif::from_diagonal_png(
file, anime_type, *time, *brightness file, anime_type, *time, *brightness,
)?) )?),
}, },
ActionLoader::ImageAnimation { ActionLoader::ImageAnimation {
file, file,
@@ -97,17 +97,17 @@ impl ActionData {
angle, angle,
translation, translation,
time, time,
brightness brightness,
} => { } => {
if let Some(ext) = file.extension() { if let Some(ext) = file.extension() {
if ext.to_string_lossy().to_lowercase() == "png" { if ext.to_string_lossy().to_lowercase() == "png" {
return Ok(ActionData::Animation(AnimeGif::from_png( return Ok(ActionData::Animation(AnimeGif::from_png(
file, *scale, *angle, *translation, *time, *brightness, anime_type file, *scale, *angle, *translation, *time, *brightness, anime_type,
)?)); )?));
} }
} }
ActionData::Animation(AnimeGif::from_gif( ActionData::Animation(AnimeGif::from_gif(
file, *scale, *angle, *translation, *time, *brightness, anime_type file, *scale, *angle, *translation, *time, *brightness, anime_type,
)?) )?)
} }
ActionLoader::Image { ActionLoader::Image {
@@ -116,23 +116,23 @@ impl ActionData {
angle, angle,
translation, translation,
brightness, brightness,
time time,
} => { } => {
match time { match time {
AnimTime::Infinite => { AnimTime::Infinite => {
// If no time then create a plain static image // If no time then create a plain static image
let image = AnimeImage::from_png( let image = AnimeImage::from_png(
file, *scale, *angle, *translation, *brightness, anime_type file, *scale, *angle, *translation, *brightness, anime_type,
)?; )?;
let data = <AnimeDataBuffer>::try_from(&image)?; let data = <AnimeDataBuffer>::try_from(&image)?;
ActionData::Image(Box::new(data)) ActionData::Image(Box::new(data))
} }
_ => ActionData::Animation(AnimeGif::from_png( _ => ActionData::Animation(AnimeGif::from_png(
file, *scale, *angle, *translation, *time, *brightness, anime_type file, *scale, *angle, *translation, *time, *brightness, anime_type,
)?) )?),
} }
} }
ActionLoader::Pause(duration) => ActionData::Pause(*duration) ActionLoader::Pause(duration) => ActionData::Pause(*duration),
}; };
Ok(a) Ok(a)
} }
@@ -171,7 +171,7 @@ impl Sequences {
pub fn iter(&self) -> ActionIterator<'_> { pub fn iter(&self) -> ActionIterator<'_> {
ActionIterator { ActionIterator {
actions: self, actions: self,
next_idx: 0 next_idx: 0,
} }
} }
} }
@@ -179,7 +179,7 @@ impl Sequences {
/// Iteractor helper for iterating over all the actions in `Sequences` /// Iteractor helper for iterating over all the actions in `Sequences`
pub struct ActionIterator<'a> { pub struct ActionIterator<'a> {
actions: &'a Sequences, actions: &'a Sequences,
next_idx: usize next_idx: usize,
} }
impl<'a> Iterator for ActionIterator<'a> { impl<'a> Iterator for ActionIterator<'a> {

View File

@@ -35,7 +35,7 @@ pub enum Brightness {
Low = 1, Low = 1,
#[default] #[default]
Med = 2, Med = 2,
High = 3 High = 3,
} }
impl FromStr for Brightness { impl FromStr for Brightness {
@@ -47,7 +47,7 @@ impl FromStr for Brightness {
"Low" | "low" => Brightness::Low, "Low" | "low" => Brightness::Low,
"Med" | "med" => Brightness::Med, "Med" | "med" => Brightness::Med,
"High" | "high" => Brightness::High, "High" | "high" => Brightness::High,
_ => Brightness::Med _ => Brightness::Med,
}) })
} }
} }
@@ -58,7 +58,7 @@ impl From<u8> for Brightness {
0 => Brightness::Off, 0 => Brightness::Off,
1 => Brightness::Low, 1 => Brightness::Low,
3 => Brightness::High, 3 => Brightness::High,
_ => Brightness::Med _ => Brightness::Med,
} }
} }
} }
@@ -84,7 +84,7 @@ impl From<Brightness> for i32 {
pub enum AnimBooting { pub enum AnimBooting {
#[default] #[default]
GlitchConstruction = 0, GlitchConstruction = 0,
StaticEmergence = 1 StaticEmergence = 1,
} }
impl FromStr for AnimBooting { impl FromStr for AnimBooting {
@@ -94,7 +94,7 @@ impl FromStr for AnimBooting {
match s { match s {
"GlitchConstruction" => Ok(Self::GlitchConstruction), "GlitchConstruction" => Ok(Self::GlitchConstruction),
"StaticEmergence" => Ok(Self::StaticEmergence), "StaticEmergence" => Ok(Self::StaticEmergence),
_ => Err(AnimeError::ParseError(s.to_owned())) _ => Err(AnimeError::ParseError(s.to_owned())),
} }
} }
} }
@@ -104,7 +104,7 @@ impl From<i32> for AnimBooting {
match value { match value {
0 => Self::GlitchConstruction, 0 => Self::GlitchConstruction,
1 => Self::StaticEmergence, 1 => Self::StaticEmergence,
_ => Self::default() _ => Self::default(),
} }
} }
} }
@@ -124,7 +124,7 @@ impl From<AnimBooting> for i32 {
pub enum AnimAwake { pub enum AnimAwake {
#[default] #[default]
BinaryBannerScroll = 0, BinaryBannerScroll = 0,
RogLogoGlitch = 1 RogLogoGlitch = 1,
} }
impl FromStr for AnimAwake { impl FromStr for AnimAwake {
@@ -134,7 +134,7 @@ impl FromStr for AnimAwake {
match s { match s {
"BinaryBannerScroll" => Ok(Self::BinaryBannerScroll), "BinaryBannerScroll" => Ok(Self::BinaryBannerScroll),
"RogLogoGlitch" => Ok(Self::RogLogoGlitch), "RogLogoGlitch" => Ok(Self::RogLogoGlitch),
_ => Err(AnimeError::ParseError(s.to_owned())) _ => Err(AnimeError::ParseError(s.to_owned())),
} }
} }
} }
@@ -144,7 +144,7 @@ impl From<i32> for AnimAwake {
match value { match value {
0 => Self::BinaryBannerScroll, 0 => Self::BinaryBannerScroll,
1 => Self::RogLogoGlitch, 1 => Self::RogLogoGlitch,
_ => Self::default() _ => Self::default(),
} }
} }
} }
@@ -164,7 +164,7 @@ impl From<AnimAwake> for i32 {
pub enum AnimSleeping { pub enum AnimSleeping {
#[default] #[default]
BannerSwipe = 0, BannerSwipe = 0,
Starfield = 1 Starfield = 1,
} }
impl FromStr for AnimSleeping { impl FromStr for AnimSleeping {
@@ -174,7 +174,7 @@ impl FromStr for AnimSleeping {
match s { match s {
"BannerSwipe" => Ok(Self::BannerSwipe), "BannerSwipe" => Ok(Self::BannerSwipe),
"Starfield" => Ok(Self::Starfield), "Starfield" => Ok(Self::Starfield),
_ => Err(AnimeError::ParseError(s.to_owned())) _ => Err(AnimeError::ParseError(s.to_owned())),
} }
} }
} }
@@ -184,7 +184,7 @@ impl From<i32> for AnimSleeping {
match value { match value {
0 => Self::BannerSwipe, 0 => Self::BannerSwipe,
1 => Self::Starfield, 1 => Self::Starfield,
_ => Self::default() _ => Self::default(),
} }
} }
} }
@@ -204,7 +204,7 @@ impl From<AnimSleeping> for i32 {
pub enum AnimShutdown { pub enum AnimShutdown {
#[default] #[default]
GlitchOut = 0, GlitchOut = 0,
SeeYa = 1 SeeYa = 1,
} }
impl FromStr for AnimShutdown { impl FromStr for AnimShutdown {
@@ -214,7 +214,7 @@ impl FromStr for AnimShutdown {
match s { match s {
"GlitchOut" => Ok(Self::GlitchOut), "GlitchOut" => Ok(Self::GlitchOut),
"SeeYa" => Ok(Self::SeeYa), "SeeYa" => Ok(Self::SeeYa),
_ => Err(AnimeError::ParseError(s.to_owned())) _ => Err(AnimeError::ParseError(s.to_owned())),
} }
} }
} }
@@ -224,7 +224,7 @@ impl From<i32> for AnimShutdown {
match value { match value {
0 => Self::GlitchOut, 0 => Self::GlitchOut,
1 => Self::SeeYa, 1 => Self::SeeYa,
_ => Self::default() _ => Self::default(),
} }
} }
} }
@@ -326,7 +326,7 @@ pub const fn pkt_set_builtin_animations(
boot: AnimBooting, boot: AnimBooting,
awake: AnimAwake, awake: AnimAwake,
sleep: AnimSleeping, sleep: AnimSleeping,
shutdown: AnimShutdown shutdown: AnimShutdown,
) -> [u8; PACKET_SIZE] { ) -> [u8; PACKET_SIZE] {
let mut pkt = [0; PACKET_SIZE]; let mut pkt = [0; PACKET_SIZE];
pkt[0] = DEV_PAGE; pkt[0] = DEV_PAGE;

View File

@@ -52,7 +52,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt1_check = [ let pkt1_check = [
0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -100,7 +100,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let mut matrix = AnimeImage::new( let mut matrix = AnimeImage::new(
@@ -110,7 +110,7 @@ mod tests {
0.0, 0.0,
vec![Pixel::default(); 1000], vec![Pixel::default(); 1000],
100, 100,
AnimeType::GA401 AnimeType::GA401,
) )
.unwrap(); .unwrap();
matrix.edge_outline(); matrix.edge_outline();
@@ -175,7 +175,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt1_check = [ let pkt1_check = [
0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -223,7 +223,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));

View File

@@ -52,7 +52,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt1_check = [ let pkt1_check = [
0x5e, 0xc0, 0x2, 0x74, 0x2, 0x73, 0x2, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x2, 0x74, 0x2, 0x73, 0x2, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
@@ -100,7 +100,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt2_check = [ let pkt2_check = [
0x5e, 0xc0, 0x2, 0xe7, 0x4, 0x73, 0x2, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x2, 0xe7, 0x4, 0x73, 0x2, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00,
@@ -148,7 +148,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let mut matrix = AnimeImage::new( let mut matrix = AnimeImage::new(
@@ -158,7 +158,7 @@ mod tests {
0.0, 0.0,
vec![Pixel::default(); 1000], vec![Pixel::default(); 1000],
100, 100,
AnimeType::GA402 AnimeType::GA402,
) )
.unwrap(); .unwrap();
matrix.edge_outline(); matrix.edge_outline();
@@ -218,7 +218,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt1_check = [ let pkt1_check = [
0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00,
@@ -266,7 +266,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt2_check = [ let pkt2_check = [
0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
@@ -314,7 +314,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
@@ -378,7 +378,7 @@ mod tests {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt1_check = [ let pkt1_check = [
0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -426,7 +426,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt2_check = [ let pkt2_check = [
0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
@@ -474,7 +474,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));

View File

@@ -52,7 +52,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt1_check = [ let pkt1_check = [
0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -100,7 +100,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt2_check = [ let pkt2_check = [
0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -148,7 +148,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let mut matrix = AnimeImage::new( let mut matrix = AnimeImage::new(
@@ -158,7 +158,7 @@ mod tests {
0.0, 0.0,
vec![Pixel::default(); 1000], vec![Pixel::default(); 1000],
100, 100,
AnimeType::GU604 AnimeType::GU604,
) )
.unwrap(); .unwrap();
matrix.edge_outline(); matrix.edge_outline();
@@ -218,7 +218,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt1_check = [ let pkt1_check = [
0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -266,7 +266,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let pkt2_check = [ let pkt2_check = [
0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -314,7 +314,7 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));

View File

@@ -5,7 +5,7 @@
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -14,7 +14,7 @@
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle], basic_modes: [Static, Breathe, RainbowCycle],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -23,7 +23,7 @@
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -32,16 +32,16 @@
layout_name: "fa507", layout_name: "fa507",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
device_name: "FA617NS", device_name: "FA617NS",
product_id: "", product_id: "",
layout_name: "fa617ns", layout_name: "fx505d",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -50,7 +50,7 @@
layout_name: "fx505d", layout_name: "fx505d",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -59,7 +59,7 @@
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -68,7 +68,7 @@
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -77,7 +77,7 @@
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -86,7 +86,16 @@
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard],
),
(
device_name: "FX607J",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -95,7 +104,7 @@
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -104,7 +113,16 @@
layout_name: "fx505d", layout_name: "fx505d",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard],
),
(
device_name: "FX706H",
product_id: "",
layout_name: "fx505d",
basic_modes: [Static, Breathe, RainbowCycle],
basic_zones: [],
advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -113,7 +131,7 @@
layout_name: "g512", layout_name: "g512",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -131,7 +149,7 @@
layout_name: "g513i", layout_name: "g513i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -161,13 +179,22 @@
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",
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: "G531G", device_name: "G531G",
product_id: "", product_id: "",
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -176,7 +203,7 @@
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -221,7 +248,16 @@
layout_name: "g634j-per-key", layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G614JIR",
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], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -230,7 +266,7 @@
layout_name: "g634j-per-key", layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -239,7 +275,7 @@
layout_name: "g634j-per-key", layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -257,7 +293,7 @@
layout_name: "gl503", layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -266,7 +302,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -275,7 +311,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -284,7 +320,7 @@
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -293,7 +329,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -302,7 +338,7 @@
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -311,7 +347,7 @@
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -347,7 +383,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -365,7 +401,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -383,7 +419,7 @@
layout_name: "g533q", layout_name: "g533q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -392,7 +428,7 @@
layout_name: "g533q", layout_name: "g533q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -401,7 +437,7 @@
layout_name: "g533q", layout_name: "g533q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -464,7 +500,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -473,7 +509,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -482,7 +518,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -491,7 +527,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -500,7 +536,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -509,7 +545,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -518,7 +554,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -527,7 +563,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -536,7 +572,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -545,7 +581,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -554,7 +590,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -572,7 +608,7 @@
layout_name: "gl503", layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -581,7 +617,7 @@
layout_name: "gl503", layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -590,7 +626,7 @@
layout_name: "gl503", layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -608,7 +644,7 @@
layout_name: "g533q", layout_name: "g533q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -617,7 +653,7 @@
layout_name: "gl503", layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -626,7 +662,7 @@
layout_name: "fa507", layout_name: "fa507",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -674,6 +710,15 @@
advanced_type: Zoned([SingleZone]), advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
(
device_name: "GU605C",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
),
( (
device_name: "GU605M", device_name: "GU605M",
product_id: "", product_id: "",
@@ -689,7 +734,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -698,7 +743,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -707,7 +752,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -716,7 +761,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -734,7 +779,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -752,7 +797,7 @@
layout_name: "gx531-per-key", layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, 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: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -806,7 +851,7 @@
layout_name: "gx531-per-key", layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -815,7 +860,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -824,7 +869,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -833,8 +878,8 @@
layout_name: "", layout_name: "",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [None], power_zones: [r#None],
), ),
( (
device_name: "GZ301Z", device_name: "GZ301Z",
@@ -842,7 +887,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -851,7 +896,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Ally], power_zones: [Ally],
), ),
( (
@@ -860,7 +905,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Ally], power_zones: [Ally],
), ),
]) ])

View File

@@ -54,7 +54,7 @@ pub struct LedSupportData {
#[serde(default)] #[serde(default)]
pub advanced_type: AdvancedAuraType, pub advanced_type: AdvancedAuraType,
/// If empty will default to `Keyboard` power zone /// If empty will default to `Keyboard` power zone
pub power_zones: Vec<PowerZones> pub power_zones: Vec<PowerZones>,
} }
impl LedSupportData { impl LedSupportData {
@@ -116,7 +116,7 @@ impl LedSupportFile {
basic_modes: vec![AuraModeNum::Static], basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![], basic_zones: vec![],
advanced_type: AdvancedAuraType::None, advanced_type: AdvancedAuraType::None,
power_zones: vec![PowerZones::Keyboard] power_zones: vec![PowerZones::Keyboard],
} }
} }
@@ -164,7 +164,29 @@ impl LedSupportFile {
return Some(data); return Some(data);
} }
warn!("Does {} exist?", ASUS_LED_MODE_USER_CONF); // If the system-wide support files were not available (e.g. running
// tests in CI or a development environment) try to load the
// bundled test data shipped with the crate under `data/aura_support.ron`.
// This allows unit tests to run without requiring files to be installed
// to `/usr/share/asusd`.
let mut bundled = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
bundled.push("data/aura_support.ron");
if let Ok(buf) = std::fs::read_to_string(&bundled) {
if let Ok(mut tmp) = ron::from_str::<LedSupportFile>(&buf) {
data.0.append(&mut tmp.0);
data.0.sort_by(|a, b| a.device_name.cmp(&b.device_name));
info!("Loaded bundled LED support data from {}", bundled.display());
return Some(data);
} else {
warn!(
"Bundled aura_support.ron present but failed to parse: {}",
bundled.display()
);
}
} else {
warn!("Does {} exist?", ASUS_LED_MODE_USER_CONF);
}
None None
} }
} }
@@ -200,7 +222,7 @@ mod tests {
power_zones: vec![ power_zones: vec![
PowerZones::Keyboard, PowerZones::Keyboard,
PowerZones::RearGlow, PowerZones::RearGlow,
] ],
}; };
assert!(ron::to_string(&led).is_ok()); assert!(ron::to_string(&led).is_ok());

View File

@@ -19,7 +19,7 @@ pub enum LedBrightness {
Low = 1, Low = 1,
#[default] #[default]
Med = 2, Med = 2,
High = 3 High = 3,
} }
impl LedBrightness { impl LedBrightness {
@@ -28,7 +28,7 @@ impl LedBrightness {
Self::Off => Self::Low, Self::Off => Self::Low,
Self::Low => Self::Med, Self::Low => Self::Med,
Self::Med => Self::High, Self::Med => Self::High,
Self::High => Self::Off Self::High => Self::Off,
} }
} }
@@ -37,7 +37,7 @@ impl LedBrightness {
Self::Off => Self::High, Self::Off => Self::High,
Self::Low => Self::Off, Self::Low => Self::Off,
Self::Med => Self::Low, Self::Med => Self::Low,
Self::High => Self::Med Self::High => Self::Med,
} }
} }
} }
@@ -48,7 +48,7 @@ impl From<u8> for LedBrightness {
0 => LedBrightness::Off, 0 => LedBrightness::Off,
1 => LedBrightness::Low, 1 => LedBrightness::Low,
3 => LedBrightness::High, 3 => LedBrightness::High,
_ => LedBrightness::Med _ => LedBrightness::Med,
} }
} }
} }
@@ -72,7 +72,7 @@ impl From<i32> for LedBrightness {
1 => LedBrightness::Low, 1 => LedBrightness::Low,
2 => LedBrightness::Med, 2 => LedBrightness::Med,
3 => LedBrightness::High, 3 => LedBrightness::High,
_ => LedBrightness::Med _ => LedBrightness::Med,
} }
} }
} }
@@ -82,7 +82,7 @@ impl From<i32> for LedBrightness {
pub struct Colour { pub struct Colour {
pub r: u8, pub r: u8,
pub g: u8, pub g: u8,
pub b: u8 pub b: u8,
} }
impl Default for Colour { impl Default for Colour {
@@ -110,7 +110,7 @@ impl From<&[f32; 3]> for Colour {
Self { Self {
r: (255.0 * c[0]) as u8, r: (255.0 * c[0]) as u8,
g: (255.0 * c[1]) as u8, g: (255.0 * c[1]) as u8,
b: (255.0 * c[2]) as u8 b: (255.0 * c[2]) as u8,
} }
} }
} }
@@ -120,7 +120,7 @@ impl From<Colour> for [f32; 3] {
[ [
c.r as f32 / 255.0, c.r as f32 / 255.0,
c.g as f32 / 255.0, c.g as f32 / 255.0,
c.b as f32 / 255.0 c.b as f32 / 255.0,
] ]
} }
} }
@@ -130,7 +130,7 @@ impl From<&[u8; 3]> for Colour {
Self { Self {
r: c[0], r: c[0],
g: c[1], g: c[1],
b: c[2] b: c[2],
} }
} }
} }
@@ -138,7 +138,7 @@ impl From<&[u8; 3]> for Colour {
impl From<Colour> for [u8; 3] { impl From<Colour> for [u8; 3] {
fn from(c: Colour) -> Self { fn from(c: Colour) -> Self {
[ [
c.r, c.g, c.b c.r, c.g, c.b,
] ]
} }
} }
@@ -153,7 +153,7 @@ pub enum Speed {
Low = 0xe1, Low = 0xe1,
#[default] #[default]
Med = 0xeb, Med = 0xeb,
High = 0xf5 High = 0xf5,
} }
impl FromStr for Speed { impl FromStr for Speed {
@@ -165,7 +165,7 @@ impl FromStr for Speed {
"low" => Ok(Speed::Low), "low" => Ok(Speed::Low),
"med" => Ok(Speed::Med), "med" => Ok(Speed::Med),
"high" => Ok(Speed::High), "high" => Ok(Speed::High),
_ => Err(Error::ParseSpeed) _ => Err(Error::ParseSpeed),
} }
} }
} }
@@ -175,7 +175,7 @@ impl From<i32> for Speed {
match value { match value {
0 => Self::Low, 0 => Self::Low,
2 => Self::High, 2 => Self::High,
_ => Self::Med _ => Self::Med,
} }
} }
} }
@@ -185,7 +185,7 @@ impl From<Speed> for i32 {
match value { match value {
Speed::Low => 0, Speed::Low => 0,
Speed::Med => 1, Speed::Med => 1,
Speed::High => 2 Speed::High => 2,
} }
} }
} }
@@ -195,7 +195,7 @@ impl From<Speed> for u8 {
match s { match s {
Speed::Low => 0, Speed::Low => 0,
Speed::Med => 1, Speed::Med => 1,
Speed::High => 2 Speed::High => 2,
} }
} }
} }
@@ -213,7 +213,7 @@ pub enum Direction {
Right = 0, Right = 0,
Left = 1, Left = 1,
Up = 2, Up = 2,
Down = 3 Down = 3,
} }
impl FromStr for Direction { impl FromStr for Direction {
@@ -226,7 +226,7 @@ impl FromStr for Direction {
"up" => Ok(Direction::Up), "up" => Ok(Direction::Up),
"down" => Ok(Direction::Down), "down" => Ok(Direction::Down),
"left" => Ok(Direction::Left), "left" => Ok(Direction::Left),
_ => Err(Error::ParseDirection) _ => Err(Error::ParseDirection),
} }
} }
} }
@@ -237,7 +237,7 @@ impl From<i32> for Direction {
1 => Self::Left, 1 => Self::Left,
2 => Self::Up, 2 => Self::Up,
3 => Self::Down, 3 => Self::Down,
_ => Self::Right _ => Self::Right,
} }
} }
} }
@@ -270,7 +270,7 @@ pub enum AuraModeNum {
Ripple = 8, Ripple = 8,
Pulse = 10, Pulse = 10,
Comet = 11, Comet = 11,
Flash = 12 Flash = 12,
} }
impl Display for AuraModeNum { impl Display for AuraModeNum {
@@ -299,7 +299,7 @@ impl From<&AuraModeNum> for &str {
AuraModeNum::Ripple => "Ripple", AuraModeNum::Ripple => "Ripple",
AuraModeNum::Pulse => "Pulse", AuraModeNum::Pulse => "Pulse",
AuraModeNum::Comet => "Comet", AuraModeNum::Comet => "Comet",
AuraModeNum::Flash => "Flash" AuraModeNum::Flash => "Flash",
} }
} }
} }
@@ -317,7 +317,7 @@ impl From<&str> for AuraModeNum {
"Pulse" => AuraModeNum::Pulse, "Pulse" => AuraModeNum::Pulse,
"Comet" => AuraModeNum::Comet, "Comet" => AuraModeNum::Comet,
"Flash" => AuraModeNum::Flash, "Flash" => AuraModeNum::Flash,
_ => AuraModeNum::Static _ => AuraModeNum::Static,
} }
} }
} }
@@ -336,7 +336,7 @@ impl From<u8> for AuraModeNum {
10 => AuraModeNum::Pulse, 10 => AuraModeNum::Pulse,
11 => AuraModeNum::Comet, 11 => AuraModeNum::Comet,
12 => AuraModeNum::Flash, 12 => AuraModeNum::Flash,
_ => AuraModeNum::Static _ => AuraModeNum::Static,
} }
} }
} }
@@ -383,7 +383,7 @@ pub enum AuraZone {
/// The left part of a lightbar (typically on the front of laptop) /// The left part of a lightbar (typically on the front of laptop)
BarLeft = 6, BarLeft = 6,
/// The right part of a lightbar /// The right part of a lightbar
BarRight = 7 BarRight = 7,
} }
impl FromStr for AuraZone { impl FromStr for AuraZone {
@@ -400,7 +400,7 @@ impl FromStr for AuraZone {
"5" | "logo" => Ok(AuraZone::Logo), "5" | "logo" => Ok(AuraZone::Logo),
"6" | "lightbar-left" => Ok(AuraZone::BarLeft), "6" | "lightbar-left" => Ok(AuraZone::BarLeft),
"7" | "lightbar-right" => Ok(AuraZone::BarRight), "7" | "lightbar-right" => Ok(AuraZone::BarRight),
_ => Err(Error::ParseSpeed) _ => Err(Error::ParseSpeed),
} }
} }
} }
@@ -415,7 +415,7 @@ impl From<i32> for AuraZone {
5 => Self::Logo, 5 => Self::Logo,
6 => Self::BarLeft, 6 => Self::BarLeft,
7 => Self::BarRight, 7 => Self::BarRight,
_ => Self::default() _ => Self::default(),
} }
} }
} }
@@ -445,7 +445,7 @@ pub struct AuraEffect {
/// One of three speeds for modes that support speed (most that animate) /// One of three speeds for modes that support speed (most that animate)
pub speed: Speed, pub speed: Speed,
/// Up, down, left, right. Only Rainbow mode seems to use this /// Up, down, left, right. Only Rainbow mode seems to use this
pub direction: Direction pub direction: Direction,
} }
impl AuraEffect { impl AuraEffect {
@@ -481,7 +481,7 @@ impl Default for AuraEffect {
colour1: Colour { r: 166, g: 0, b: 0 }, colour1: Colour { r: 166, g: 0, b: 0 },
colour2: Colour { r: 0, g: 0, b: 0 }, colour2: Colour { r: 0, g: 0, b: 0 },
speed: Speed::Med, speed: Speed::Med,
direction: Direction::Right direction: Direction::Right,
} }
} }
} }
@@ -541,7 +541,7 @@ impl From<&AuraEffect> for Vec<u8> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed, AURA_LAPTOP_LED_MSG_LEN AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed, AURA_LAPTOP_LED_MSG_LEN,
}; };
#[test] #[test]
@@ -552,18 +552,18 @@ mod tests {
colour1: Colour { colour1: Colour {
r: 0xff, r: 0xff,
g: 0x11, g: 0x11,
b: 0xdd b: 0xdd,
}, },
colour2: Colour::default(), colour2: Colour::default(),
speed: Speed::Med, speed: Speed::Med,
direction: Direction::Right direction: Direction::Right,
}; };
let ar = <[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st); let ar = <[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st);
println!("{:02x?}", ar); println!("{:02x?}", ar);
let check = [ let check = [
0x5d, 0xb3, 0x0, 0x0, 0xff, 0x11, 0xdd, 0xeb, 0x0, 0x0, 0xa6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x0, 0x0, 0xff, 0x11, 0xdd, 0xeb, 0x0, 0x0, 0xa6, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0 0x0,
]; ];
assert_eq!(ar, check); assert_eq!(ar, check);
} }
@@ -576,15 +576,15 @@ mod tests {
colour1: Colour { colour1: Colour {
r: 0xff, r: 0xff,
g: 0, g: 0,
b: 0 b: 0,
}, },
colour2: Colour { r: 0, g: 0, b: 0 }, colour2: Colour { r: 0, g: 0, b: 0 },
speed: Speed::Low, speed: Speed::Low,
direction: Direction::Left direction: Direction::Left,
}; };
let capture = [ let capture = [
0x5d, 0xb3, 0x01, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x01, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0 0x0, 0x0,
]; ];
assert_eq!( assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9], <[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
@@ -595,11 +595,11 @@ mod tests {
st.colour1 = Colour { st.colour1 = Colour {
r: 0xff, r: 0xff,
g: 0xff, g: 0xff,
b: 0 b: 0,
}; };
let capture = [ let capture = [
0x5d, 0xb3, 0x02, 0x00, 0xff, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x02, 0x00, 0xff, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0 0x0, 0x0,
]; ];
assert_eq!( assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9], <[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
@@ -610,11 +610,11 @@ mod tests {
st.colour1 = Colour { st.colour1 = Colour {
r: 0, r: 0,
g: 0xff, g: 0xff,
b: 0xff b: 0xff,
}; };
let capture = [ let capture = [
0x5d, 0xb3, 0x03, 0x00, 0x00, 0xff, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x03, 0x00, 0x00, 0xff, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0 0x0, 0x0,
]; ];
assert_eq!( assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9], <[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
@@ -625,11 +625,11 @@ mod tests {
st.colour1 = Colour { st.colour1 = Colour {
r: 0xff, r: 0xff,
g: 0x00, g: 0x00,
b: 0xff b: 0xff,
}; };
let capture = [ let capture = [
0x5d, 0xb3, 0x04, 0x00, 0xff, 0x00, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x04, 0x00, 0xff, 0x00, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0 0x0, 0x0,
]; ];
assert_eq!( assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9], <[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
@@ -640,11 +640,11 @@ mod tests {
st.colour1 = Colour { st.colour1 = Colour {
r: 0x2c, r: 0x2c,
g: 0xff, g: 0xff,
b: 0x00 b: 0x00,
}; };
let capture = [ let capture = [
0x5d, 0xb3, 0x05, 0x00, 0x2c, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x05, 0x00, 0x2c, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0 0x0, 0x0,
]; ];
assert_eq!( assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9], <[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
@@ -655,11 +655,11 @@ mod tests {
st.colour1 = Colour { st.colour1 = Colour {
r: 0xff, r: 0xff,
g: 0x00, g: 0x00,
b: 0x00 b: 0x00,
}; };
let capture = [ let capture = [
0x5d, 0xb3, 0x06, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x06, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0 0x0, 0x0,
]; ];
assert_eq!( assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9], <[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
@@ -670,11 +670,11 @@ mod tests {
st.colour1 = Colour { st.colour1 = Colour {
r: 0xff, r: 0xff,
g: 0x00, g: 0x00,
b: 0xcd b: 0xcd,
}; };
let capture = [ let capture = [
0x5d, 0xb3, 0x07, 0x00, 0xff, 0x00, 0xcd, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x07, 0x00, 0xff, 0x00, 0xcd, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0 0x0, 0x0,
]; ];
assert_eq!( assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9], <[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
@@ -684,7 +684,7 @@ mod tests {
st.mode = AuraModeNum::RainbowWave; 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!( assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9], <[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],

View File

@@ -11,7 +11,7 @@ pub struct InputBased {
/// - temperature /// - temperature
/// - fan speed /// - fan speed
/// - time /// - time
input: Box<dyn InputForEffect> input: Box<dyn InputForEffect>,
} }
impl EffectState for InputBased { impl EffectState for InputBased {

View File

@@ -19,7 +19,7 @@ pub struct Breathe {
#[serde(skip)] #[serde(skip)]
count_flipped: bool, count_flipped: bool,
#[serde(skip)] #[serde(skip)]
use_colour1: bool use_colour1: bool,
} }
impl Breathe { impl Breathe {
@@ -31,7 +31,7 @@ impl Breathe {
speed, speed,
colour: colour1, colour: colour1,
count_flipped: false, count_flipped: false,
use_colour1: true use_colour1: true,
} }
} }
} }

View File

@@ -13,7 +13,7 @@ pub struct DoomFlicker {
#[serde(skip)] #[serde(skip)]
count: u8, count: u8,
#[serde(skip)] #[serde(skip)]
colour: Colour colour: Colour,
} }
impl DoomFlicker { impl DoomFlicker {
@@ -24,7 +24,7 @@ impl DoomFlicker {
count: 4, count: 4,
max_percentage, max_percentage,
min_percentage, min_percentage,
start_colour: colour start_colour: colour,
} }
} }
} }
@@ -53,13 +53,13 @@ impl EffectState for DoomFlicker {
let max_light = Colour { let max_light = Colour {
r: (start_colour.r as f32 / 100.0 * *max_percentage as f32) as u8, r: (start_colour.r as f32 / 100.0 * *max_percentage as f32) as u8,
g: (start_colour.g as f32 / 100.0 * *max_percentage as f32) as u8, g: (start_colour.g as f32 / 100.0 * *max_percentage as f32) as u8,
b: (start_colour.b as f32 / 100.0 * *max_percentage as f32) as u8 b: (start_colour.b as f32 / 100.0 * *max_percentage as f32) as u8,
}; };
// min light is a percentage of the set colour // min light is a percentage of the set colour
let min_light = Colour { let min_light = Colour {
r: (start_colour.r as f32 / 100.0 * *min_percentage as f32) as u8, r: (start_colour.r as f32 / 100.0 * *min_percentage as f32) as u8,
g: (start_colour.g as f32 / 100.0 * *min_percentage as f32) as u8, g: (start_colour.g as f32 / 100.0 * *min_percentage as f32) as u8,
b: (start_colour.b as f32 / 100.0 * *min_percentage as f32) as u8 b: (start_colour.b as f32 / 100.0 * *min_percentage as f32) as u8,
}; };
// Convert the 255 to percentage // Convert the 255 to percentage
@@ -96,7 +96,7 @@ pub struct DoomLightFlash {
#[serde(skip)] #[serde(skip)]
count: u8, count: u8,
#[serde(skip)] #[serde(skip)]
colour: Colour colour: Colour,
} }
impl DoomLightFlash { impl DoomLightFlash {
@@ -109,7 +109,7 @@ impl DoomLightFlash {
min_percentage, min_percentage,
start_colour: colour, start_colour: colour,
max_time: 32, max_time: 32,
min_time: 7 min_time: 7,
} }
} }
} }
@@ -135,13 +135,13 @@ impl EffectState for DoomLightFlash {
let max_light = Colour { let max_light = Colour {
r: (start_colour.r as f32 / 100.0 * *max_percentage as f32) as u8, r: (start_colour.r as f32 / 100.0 * *max_percentage as f32) as u8,
g: (start_colour.g as f32 / 100.0 * *max_percentage as f32) as u8, g: (start_colour.g as f32 / 100.0 * *max_percentage as f32) as u8,
b: (start_colour.b as f32 / 100.0 * *max_percentage as f32) as u8 b: (start_colour.b as f32 / 100.0 * *max_percentage as f32) as u8,
}; };
// min light is a percentage of the set colour // min light is a percentage of the set colour
let min_light = Colour { let min_light = Colour {
r: (start_colour.r as f32 / 100.0 * *min_percentage as f32) as u8, r: (start_colour.r as f32 / 100.0 * *min_percentage as f32) as u8,
g: (start_colour.g as f32 / 100.0 * *min_percentage as f32) as u8, g: (start_colour.g as f32 / 100.0 * *min_percentage as f32) as u8,
b: (start_colour.b as f32 / 100.0 * *min_percentage as f32) as u8 b: (start_colour.b as f32 / 100.0 * *min_percentage as f32) as u8,
}; };
if *colour == max_light { if *colour == max_light {

View File

@@ -32,7 +32,7 @@ pub const RNDTABLE: [i32; 256] = [
206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95, 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95, 28, 139, 123, 98, 125, 196, 15, 70, 194, 253,
54, 14, 109, 226, 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36, 17, 46, 52, 54, 14, 109, 226, 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36, 17, 46, 52,
231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106, 197, 242, 98, 43, 39, 175, 254, 145, 190, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106, 197, 242, 98, 43, 39, 175, 254, 145, 190,
84, 118, 222, 187, 136, 120, 163, 236, 249 84, 118, 222, 187, 136, 120, 163, 236, 249,
]; ];
pub fn p_random() -> i32 { pub fn p_random() -> i32 {
@@ -67,7 +67,7 @@ pub(crate) trait EffectState {
#[derive(Debug, Deserialize, Serialize, Default)] #[derive(Debug, Deserialize, Serialize, Default)]
pub struct AdvancedEffects { pub struct AdvancedEffects {
effects: Vec<Effect>, effects: Vec<Effect>,
zoned: bool zoned: bool,
} }
impl AdvancedEffects { impl AdvancedEffects {
@@ -75,7 +75,7 @@ impl AdvancedEffects {
pub fn new(zoned: bool) -> Self { pub fn new(zoned: bool) -> Self {
Self { Self {
effects: Default::default(), effects: Default::default(),
zoned zoned,
} }
} }
@@ -186,7 +186,7 @@ pub enum Effect {
Static(Static), Static(Static),
Breathe(Breathe), Breathe(Breathe),
DoomFlicker(DoomFlicker), DoomFlicker(DoomFlicker),
DoomLightFlash(DoomLightFlash) DoomLightFlash(DoomLightFlash),
} }
impl Default for Effect { impl Default for Effect {
@@ -207,12 +207,14 @@ mod tests {
fn single_key_next_state_then_create() { fn single_key_next_state_then_create() {
let layout = KeyLayout::default_layout(); let layout = KeyLayout::default_layout();
let mut seq = AdvancedEffects::new(false); let mut seq = AdvancedEffects::new(false);
seq.effects seq.effects.push(Effect::Static(Static::new(
.push(Effect::Static(Static::new(LedCode::F, Colour { LedCode::F,
Colour {
r: 255, r: 255,
g: 127, g: 127,
b: 0 b: 0,
}))); },
)));
seq.next_state(&layout); seq.next_state(&layout);
let packets = seq.create_packets(); let packets = seq.create_packets();
@@ -232,14 +234,14 @@ mod tests {
Colour { Colour {
r: 255, r: 255,
g: 127, g: 127,
b: 0 b: 0,
}, },
Colour { Colour {
r: 127, r: 127,
g: 0, g: 0,
b: 255 b: 255,
}, },
Speed::Med Speed::Med,
))); )));
let s = let s =
@@ -274,10 +276,10 @@ mod tests {
Colour { Colour {
r: 255, r: 255,
g: 127, g: 127,
b: 80 b: 80,
}, },
100, 100,
10 10,
))); )));
seq.next_state(&layout); seq.next_state(&layout);

View File

@@ -8,14 +8,14 @@ use crate::{effect_state_impl, Colour};
pub struct Static { pub struct Static {
led: LedCode, led: LedCode,
/// The starting colour /// The starting colour
colour: Colour colour: Colour,
} }
impl Static { impl Static {
pub fn new(address: LedCode, colour: Colour) -> Self { pub fn new(address: LedCode, colour: Colour) -> Self {
Self { Self {
led: address, led: address,
colour colour,
} }
} }
} }

View File

@@ -8,7 +8,7 @@ pub enum Error {
ParseBrightness, ParseBrightness,
IoPath(String, std::io::Error), IoPath(String, std::io::Error),
Ron(ron::Error), Ron(ron::Error),
RonParse(ron::error::SpannedError) RonParse(ron::error::SpannedError),
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@@ -21,7 +21,7 @@ impl fmt::Display for Error {
Error::ParseBrightness => write!(f, "Could not parse brightness"), Error::ParseBrightness => write!(f, "Could not parse brightness"),
Error::IoPath(path, io) => write!(f, "IO Error: {path}, {io}"), Error::IoPath(path, io) => write!(f, "IO Error: {path}, {io}"),
Error::Ron(e) => write!(f, "RON Parse Error: {e}"), Error::Ron(e) => write!(f, "RON Parse Error: {e}"),
Error::RonParse(e) => write!(f, "RON Parse Error: {e}") Error::RonParse(e) => write!(f, "RON Parse Error: {e}"),
} }
} }
} }

View File

@@ -164,7 +164,7 @@ pub enum LedCode {
/// To be ignored by effects /// To be ignored by effects
Spacing, Spacing,
/// To be ignored by effects /// To be ignored by effects
Blocking Blocking,
} }
impl LedCode { impl LedCode {
@@ -210,7 +210,7 @@ pub struct LedUsbPackets {
/// Wether or not this packet collection is zoned. The determines which /// Wether or not this packet collection is zoned. The determines which
/// starting bytes are used and what the indexing is for lightbar RGB /// starting bytes are used and what the indexing is for lightbar RGB
/// colours /// colours
zoned: bool zoned: bool,
} }
impl Default for LedUsbPackets { impl Default for LedUsbPackets {
@@ -244,7 +244,7 @@ impl LedUsbPackets {
} }
Self { Self {
usb_packets: set, usb_packets: set,
zoned: false zoned: false,
} }
} }
@@ -274,7 +274,7 @@ impl LedUsbPackets {
} }
Self { Self {
usb_packets: vec![pkt], usb_packets: vec![pkt],
zoned: true zoned: true,
} }
} }
@@ -633,7 +633,7 @@ impl From<&LedCode> for &str {
LedCode::ZonedKbLeft => "Left Zone (zone 1)", LedCode::ZonedKbLeft => "Left Zone (zone 1)",
LedCode::ZonedKbLeftMid => "Center-left Zone (zone 2)", LedCode::ZonedKbLeftMid => "Center-left Zone (zone 2)",
LedCode::ZonedKbRightMid => "Center-right Zone (zone 3)", LedCode::ZonedKbRightMid => "Center-right Zone (zone 3)",
LedCode::ZonedKbRight => "Right Zone (zone 4)" LedCode::ZonedKbRight => "Right Zone (zone 4)",
} }
} }
} }

View File

@@ -26,12 +26,12 @@ pub enum KeyShape {
pad_left: f32, pad_left: f32,
pad_right: f32, pad_right: f32,
pad_top: f32, pad_top: f32,
pad_bottom: f32 pad_bottom: f32,
}, },
Blank { Blank {
width: f32, width: f32,
height: f32 height: f32,
} },
} }
impl KeyShape { impl KeyShape {
@@ -41,7 +41,7 @@ impl KeyShape {
pad_left: f32, pad_left: f32,
pad_right: f32, pad_right: f32,
pad_top: f32, pad_top: f32,
pad_bottom: f32 pad_bottom: f32,
) -> Self { ) -> Self {
Self::Led { Self::Led {
width, width,
@@ -49,7 +49,7 @@ impl KeyShape {
pad_left, pad_left,
pad_right, pad_right,
pad_top, pad_top,
pad_bottom pad_bottom,
} }
} }
@@ -66,7 +66,7 @@ impl KeyShape {
pad_left, pad_left,
pad_right, pad_right,
pad_top, pad_top,
pad_bottom pad_bottom,
} => { } => {
*width *= scale; *width *= scale;
*height *= scale; *height *= scale;
@@ -97,7 +97,7 @@ pub struct KeyRow {
row: Vec<(LedCode, String)>, row: Vec<(LedCode, String)>,
/// The final data structure merged key_shapes and rows /// The final data structure merged key_shapes and rows
#[serde(skip)] #[serde(skip)]
built_row: Vec<(LedCode, KeyShape)> built_row: Vec<(LedCode, KeyShape)>,
} }
impl KeyRow { impl KeyRow {
@@ -106,7 +106,7 @@ impl KeyRow {
pad_left, pad_left,
pad_top, pad_top,
row, row,
built_row: Default::default() built_row: Default::default(),
} }
} }
@@ -132,7 +132,7 @@ impl KeyRow {
pad_bottom, pad_bottom,
.. ..
} => height + pad_top + pad_bottom, } => height + pad_top + pad_bottom,
KeyShape::Blank { height, .. } => *height KeyShape::Blank { height, .. } => *height,
}; };
if h < height { if h < height {
@@ -156,7 +156,7 @@ impl KeyRow {
pad_right, pad_right,
.. ..
} => w += width + pad_left + pad_right, } => w += width + pad_left + pad_right,
KeyShape::Blank { width, .. } => w += width KeyShape::Blank { width, .. } => w += width,
} }
} }
w w
@@ -185,7 +185,7 @@ pub struct KeyLayout {
/// Should be copied from the `LaptopLedData` as laptops may have the same /// Should be copied from the `LaptopLedData` as laptops may have the same
/// layout, but different EC features. /// layout, but different EC features.
#[serde(skip)] #[serde(skip)]
advanced_type: AdvancedAuraType advanced_type: AdvancedAuraType,
} }
impl KeyLayout { impl KeyLayout {
@@ -195,7 +195,7 @@ impl KeyLayout {
if buf.is_empty() { if buf.is_empty() {
Err(Error::IoPath( Err(Error::IoPath(
path.to_string_lossy().to_string(), path.to_string_lossy().to_string(),
std::io::ErrorKind::InvalidData.into() std::io::ErrorKind::InvalidData.into(),
)) ))
} else { } else {
let mut data = ron::from_str::<Self>(&buf)?; let mut data = ron::from_str::<Self>(&buf)?;
@@ -332,97 +332,121 @@ impl KeyLayout {
advanced_type: AdvancedAuraType::None, advanced_type: AdvancedAuraType::None,
key_shapes: HashMap::from([( key_shapes: HashMap::from([(
"regular".to_owned(), "regular".to_owned(),
KeyShape::new_led(1.0, 1.0, 0.1, 0.1, 0.1, 0.1) KeyShape::new_led(1.0, 1.0, 0.1, 0.1, 0.1, 0.1),
)]), )]),
key_rows: vec![ key_rows: vec![
KeyRow::new(0.1, 0.1, vec![ KeyRow::new(
(LedCode::Esc, "regular".to_owned()), 0.1,
(LedCode::F1, "regular".to_owned()), 0.1,
(LedCode::F2, "regular".to_owned()), vec![
(LedCode::F3, "regular".to_owned()), (LedCode::Esc, "regular".to_owned()),
(LedCode::F4, "regular".to_owned()), (LedCode::F1, "regular".to_owned()),
// not sure which key to put here (LedCode::F2, "regular".to_owned()),
(LedCode::F5, "regular".to_owned()), (LedCode::F3, "regular".to_owned()),
(LedCode::F6, "regular".to_owned()), (LedCode::F4, "regular".to_owned()),
(LedCode::F7, "regular".to_owned()), // not sure which key to put here
(LedCode::F8, "regular".to_owned()), (LedCode::F5, "regular".to_owned()),
(LedCode::F9, "regular".to_owned()), (LedCode::F6, "regular".to_owned()),
(LedCode::F10, "regular".to_owned()), (LedCode::F7, "regular".to_owned()),
(LedCode::F11, "regular".to_owned()), (LedCode::F8, "regular".to_owned()),
(LedCode::F12, "regular".to_owned()), (LedCode::F9, "regular".to_owned()),
]), (LedCode::F10, "regular".to_owned()),
KeyRow::new(0.1, 0.1, vec![ (LedCode::F11, "regular".to_owned()),
(LedCode::Tilde, "regular".to_owned()), (LedCode::F12, "regular".to_owned()),
(LedCode::N1, "regular".to_owned()), ],
(LedCode::N2, "regular".to_owned()), ),
(LedCode::N3, "regular".to_owned()), KeyRow::new(
(LedCode::N4, "regular".to_owned()), 0.1,
(LedCode::N5, "regular".to_owned()), 0.1,
(LedCode::N6, "regular".to_owned()), vec![
(LedCode::N7, "regular".to_owned()), (LedCode::Tilde, "regular".to_owned()),
(LedCode::N8, "regular".to_owned()), (LedCode::N1, "regular".to_owned()),
(LedCode::N9, "regular".to_owned()), (LedCode::N2, "regular".to_owned()),
(LedCode::N0, "regular".to_owned()), (LedCode::N3, "regular".to_owned()),
(LedCode::Hyphen, "regular".to_owned()), (LedCode::N4, "regular".to_owned()),
(LedCode::Equals, "regular".to_owned()), (LedCode::N5, "regular".to_owned()),
(LedCode::Backspace, "regular".to_owned()), (LedCode::N6, "regular".to_owned()),
]), (LedCode::N7, "regular".to_owned()),
KeyRow::new(0.1, 0.1, vec![ (LedCode::N8, "regular".to_owned()),
(LedCode::Tab, "regular".to_owned()), (LedCode::N9, "regular".to_owned()),
(LedCode::Q, "regular".to_owned()), (LedCode::N0, "regular".to_owned()),
(LedCode::W, "regular".to_owned()), (LedCode::Hyphen, "regular".to_owned()),
(LedCode::E, "regular".to_owned()), (LedCode::Equals, "regular".to_owned()),
(LedCode::R, "regular".to_owned()), (LedCode::Backspace, "regular".to_owned()),
(LedCode::T, "regular".to_owned()), ],
(LedCode::Y, "regular".to_owned()), ),
(LedCode::U, "regular".to_owned()), KeyRow::new(
(LedCode::I, "regular".to_owned()), 0.1,
(LedCode::O, "regular".to_owned()), 0.1,
(LedCode::P, "regular".to_owned()), vec![
(LedCode::LBracket, "regular".to_owned()), (LedCode::Tab, "regular".to_owned()),
(LedCode::RBracket, "regular".to_owned()), (LedCode::Q, "regular".to_owned()),
(LedCode::BackSlash, "regular".to_owned()), (LedCode::W, "regular".to_owned()),
]), (LedCode::E, "regular".to_owned()),
KeyRow::new(0.1, 0.1, vec![ (LedCode::R, "regular".to_owned()),
(LedCode::Caps, "regular".to_owned()), (LedCode::T, "regular".to_owned()),
(LedCode::A, "regular".to_owned()), (LedCode::Y, "regular".to_owned()),
(LedCode::S, "regular".to_owned()), (LedCode::U, "regular".to_owned()),
(LedCode::D, "regular".to_owned()), (LedCode::I, "regular".to_owned()),
(LedCode::F, "regular".to_owned()), (LedCode::O, "regular".to_owned()),
(LedCode::G, "regular".to_owned()), (LedCode::P, "regular".to_owned()),
(LedCode::H, "regular".to_owned()), (LedCode::LBracket, "regular".to_owned()),
(LedCode::J, "regular".to_owned()), (LedCode::RBracket, "regular".to_owned()),
(LedCode::K, "regular".to_owned()), (LedCode::BackSlash, "regular".to_owned()),
(LedCode::L, "regular".to_owned()), ],
(LedCode::SemiColon, "regular".to_owned()), ),
(LedCode::Quote, "regular".to_owned()), KeyRow::new(
(LedCode::Return, "regular".to_owned()), 0.1,
]), 0.1,
KeyRow::new(0.1, 0.1, vec![ vec![
(LedCode::LShift, "regular".to_owned()), (LedCode::Caps, "regular".to_owned()),
(LedCode::Z, "regular".to_owned()), (LedCode::A, "regular".to_owned()),
(LedCode::X, "regular".to_owned()), (LedCode::S, "regular".to_owned()),
(LedCode::C, "regular".to_owned()), (LedCode::D, "regular".to_owned()),
(LedCode::V, "regular".to_owned()), (LedCode::F, "regular".to_owned()),
(LedCode::B, "regular".to_owned()), (LedCode::G, "regular".to_owned()),
(LedCode::N, "regular".to_owned()), (LedCode::H, "regular".to_owned()),
(LedCode::M, "regular".to_owned()), (LedCode::J, "regular".to_owned()),
(LedCode::Comma, "regular".to_owned()), (LedCode::K, "regular".to_owned()),
(LedCode::Period, "regular".to_owned()), (LedCode::L, "regular".to_owned()),
(LedCode::FwdSlash, "regular".to_owned()), (LedCode::SemiColon, "regular".to_owned()),
(LedCode::Rshift, "regular".to_owned()), (LedCode::Quote, "regular".to_owned()),
]), (LedCode::Return, "regular".to_owned()),
KeyRow::new(0.1, 0.1, vec![ ],
(LedCode::LCtrl, "regular".to_owned()), ),
(LedCode::LFn, "regular".to_owned()), KeyRow::new(
(LedCode::Meta, "regular".to_owned()), 0.1,
(LedCode::LAlt, "regular".to_owned()), 0.1,
(LedCode::Spacebar, "regular".to_owned()), vec![
(LedCode::RAlt, "regular".to_owned()), (LedCode::LShift, "regular".to_owned()),
(LedCode::PrtSc, "regular".to_owned()), (LedCode::Z, "regular".to_owned()),
(LedCode::RCtrl, "regular".to_owned()), (LedCode::X, "regular".to_owned()),
]), (LedCode::C, "regular".to_owned()),
] (LedCode::V, "regular".to_owned()),
(LedCode::B, "regular".to_owned()),
(LedCode::N, "regular".to_owned()),
(LedCode::M, "regular".to_owned()),
(LedCode::Comma, "regular".to_owned()),
(LedCode::Period, "regular".to_owned()),
(LedCode::FwdSlash, "regular".to_owned()),
(LedCode::Rshift, "regular".to_owned()),
],
),
KeyRow::new(
0.1,
0.1,
vec![
(LedCode::LCtrl, "regular".to_owned()),
(LedCode::LFn, "regular".to_owned()),
(LedCode::Meta, "regular".to_owned()),
(LedCode::LAlt, "regular".to_owned()),
(LedCode::Spacebar, "regular".to_owned()),
(LedCode::RAlt, "regular".to_owned()),
(LedCode::PrtSc, "regular".to_owned()),
(LedCode::RCtrl, "regular".to_owned()),
],
),
],
} }
} }
} }

View File

@@ -16,5 +16,5 @@ pub enum AdvancedAuraType {
#[default] #[default]
None, None,
Zoned(Vec<LedCode>), Zoned(Vec<LedCode>),
PerKey PerKey,
} }

View File

@@ -23,7 +23,7 @@ pub struct AuraPowerState {
pub awake: bool, pub awake: bool,
pub sleep: bool, pub sleep: bool,
/// Ignored for pre-2021 and Tuf /// Ignored for pre-2021 and Tuf
pub shutdown: bool pub shutdown: bool,
} }
impl Default for AuraPowerState { impl Default for AuraPowerState {
@@ -34,7 +34,7 @@ impl Default for AuraPowerState {
boot: true, boot: true,
awake: true, awake: true,
sleep: true, sleep: true,
shutdown: true shutdown: true,
} }
} }
} }
@@ -46,7 +46,7 @@ impl AuraPowerState {
boot: true, boot: true,
awake: true, awake: true,
sleep: true, sleep: true,
shutdown: true shutdown: true,
} }
} }
@@ -140,7 +140,7 @@ impl AuraPowerState {
| ((self.sleep as u32) << (23 + 3)) | ((self.sleep as u32) << (23 + 3))
| ((self.shutdown as u32) << (23 + 4)) | ((self.shutdown as u32) << (23 + 4))
} }
PowerZones::None | PowerZones::KeyboardAndLightbar => 0 PowerZones::None | PowerZones::KeyboardAndLightbar => 0,
} }
} }
} }
@@ -148,7 +148,7 @@ impl AuraPowerState {
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))] #[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct LaptopAuraPower { pub struct LaptopAuraPower {
pub states: Vec<AuraPowerState> pub states: Vec<AuraPowerState>,
} }
impl LaptopAuraPower { impl LaptopAuraPower {
@@ -206,19 +206,19 @@ impl LaptopAuraPower {
// 3. KeyboardAndLightbar // 3. KeyboardAndLightbar
if support_data.power_zones.contains(&PowerZones::Lightbar) { if support_data.power_zones.contains(&PowerZones::Lightbar) {
Self { Self {
states: vec![AuraPowerState::default_for(PowerZones::KeyboardAndLightbar)] states: vec![AuraPowerState::default_for(PowerZones::KeyboardAndLightbar)],
} }
} else { } else {
Self { Self {
states: vec![AuraPowerState::default_for(PowerZones::Keyboard)] states: vec![AuraPowerState::default_for(PowerZones::Keyboard)],
} }
} }
} }
AuraDeviceType::LaptopKeyboardTuf => Self { AuraDeviceType::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!() AuraDeviceType::AnimeOrSlash => todo!(),
} }
} }
@@ -266,7 +266,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") AuraDeviceType::AnimeOrSlash => todo!("anime/slash not implemented yet"),
} }
} }
} }
@@ -285,7 +285,7 @@ enum OldAuraPower {
Boot = 0xc31209, Boot = 0xc31209,
Sleep = 0x300804, Sleep = 0x300804,
Keyboard = 0x080000, Keyboard = 0x080000,
Lightbar = 0x040500 Lightbar = 0x040500,
} }
impl BitOr<OldAuraPower> for OldAuraPower { impl BitOr<OldAuraPower> for OldAuraPower {
@@ -332,9 +332,9 @@ mod test {
boot: false, boot: false,
awake: true, awake: true,
sleep: false, sleep: false,
shutdown: false shutdown: false,
}, },
] ],
}; };
let bytes = power.to_bytes(AuraDeviceType::LaptopKeyboardPre2021); 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]);
@@ -347,9 +347,9 @@ mod test {
boot: false, boot: false,
awake: true, awake: true,
sleep: false, sleep: false,
shutdown: false shutdown: false,
}, },
] ],
}; };
let bytes = power.to_bytes(AuraDeviceType::LaptopKeyboardPre2021); 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]);
@@ -369,16 +369,16 @@ mod test {
boot: true, boot: true,
awake: true, awake: true,
sleep: true, sleep: true,
shutdown: false shutdown: false,
}, },
AuraPowerState { AuraPowerState {
zone: PowerZones::Lightbar, zone: PowerZones::Lightbar,
boot: true, boot: true,
awake: true, awake: true,
sleep: true, sleep: true,
shutdown: false shutdown: false,
}, },
] ],
}; };
let bytes = power.to_bytes(AuraDeviceType::LaptopKeyboardPre2021); 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]);
@@ -394,9 +394,9 @@ mod test {
boot: true, boot: true,
awake: false, awake: false,
sleep: false, sleep: false,
shutdown: false shutdown: false,
}, },
] ],
}); });
let boot_keyb_ = to_binary_string_post2021(&LaptopAuraPower { let boot_keyb_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![ states: vec![
@@ -405,9 +405,9 @@ mod test {
boot: true, boot: true,
awake: false, awake: false,
sleep: false, sleep: false,
shutdown: false shutdown: false,
}, },
] ],
}); });
let sleep_logo = to_binary_string_post2021(&LaptopAuraPower { let sleep_logo = to_binary_string_post2021(&LaptopAuraPower {
states: vec![ states: vec![
@@ -416,9 +416,9 @@ mod test {
boot: false, boot: false,
awake: false, awake: false,
sleep: true, sleep: true,
shutdown: false shutdown: false,
}, },
] ],
}); });
let sleep_keyb = to_binary_string_post2021(&LaptopAuraPower { let sleep_keyb = to_binary_string_post2021(&LaptopAuraPower {
states: vec![ states: vec![
@@ -427,9 +427,9 @@ mod test {
boot: false, boot: false,
awake: false, awake: false,
sleep: true, sleep: true,
shutdown: false shutdown: false,
}, },
] ],
}); });
let awake_logo = to_binary_string_post2021(&LaptopAuraPower { let awake_logo = to_binary_string_post2021(&LaptopAuraPower {
states: vec![ states: vec![
@@ -438,9 +438,9 @@ mod test {
boot: false, boot: false,
awake: true, awake: true,
sleep: false, sleep: false,
shutdown: false shutdown: false,
}, },
] ],
}); });
let awake_keyb = to_binary_string_post2021(&LaptopAuraPower { let awake_keyb = to_binary_string_post2021(&LaptopAuraPower {
states: vec![ states: vec![
@@ -449,9 +449,9 @@ mod test {
boot: false, boot: false,
awake: true, awake: true,
sleep: false, sleep: false,
shutdown: false shutdown: false,
}, },
] ],
}); });
let shut_logo_ = to_binary_string_post2021(&LaptopAuraPower { let shut_logo_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![ states: vec![
@@ -460,9 +460,9 @@ mod test {
boot: false, boot: false,
awake: false, awake: false,
sleep: false, sleep: false,
shutdown: true shutdown: true,
}, },
] ],
}); });
let shut_keyb_ = to_binary_string_post2021(&LaptopAuraPower { let shut_keyb_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![ states: vec![
@@ -471,9 +471,9 @@ mod test {
boot: false, boot: false,
awake: false, awake: false,
sleep: false, sleep: false,
shutdown: true shutdown: true,
}, },
] ],
}); });
let boot_bar__ = to_binary_string_post2021(&LaptopAuraPower { let boot_bar__ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![ states: vec![
@@ -482,9 +482,9 @@ mod test {
boot: true, boot: true,
awake: false, awake: false,
sleep: false, sleep: false,
shutdown: false shutdown: false,
}, },
] ],
}); });
let awake_bar_ = to_binary_string_post2021(&LaptopAuraPower { let awake_bar_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![ states: vec![
@@ -493,9 +493,9 @@ mod test {
boot: false, boot: false,
awake: true, awake: true,
sleep: false, sleep: false,
shutdown: false shutdown: false,
}, },
] ],
}); });
let sleep_bar_ = to_binary_string_post2021(&LaptopAuraPower { let sleep_bar_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![ states: vec![
@@ -504,9 +504,9 @@ mod test {
boot: false, boot: false,
awake: false, awake: false,
sleep: true, sleep: true,
shutdown: false shutdown: false,
}, },
] ],
}); });
let shut_bar__ = to_binary_string_post2021(&LaptopAuraPower { let shut_bar__ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![ states: vec![
@@ -515,9 +515,9 @@ mod test {
boot: false, boot: false,
awake: false, awake: false,
sleep: false, sleep: false,
shutdown: true shutdown: true,
}, },
] ],
}); });
let boot_lid__ = to_binary_string_post2021(&LaptopAuraPower { let boot_lid__ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![ states: vec![
@@ -526,9 +526,9 @@ mod test {
boot: true, boot: true,
awake: false, awake: false,
sleep: false, sleep: false,
shutdown: false shutdown: false,
}, },
] ],
}); });
let awake_lid_ = to_binary_string_post2021(&LaptopAuraPower { let awake_lid_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![ states: vec![
@@ -537,9 +537,9 @@ mod test {
boot: false, boot: false,
awake: true, awake: true,
sleep: false, sleep: false,
shutdown: false shutdown: false,
}, },
] ],
}); });
let sleep_lid_ = to_binary_string_post2021(&LaptopAuraPower { let sleep_lid_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![ states: vec![
@@ -548,9 +548,9 @@ mod test {
boot: false, boot: false,
awake: false, awake: false,
sleep: true, sleep: true,
shutdown: false shutdown: false,
}, },
] ],
}); });
let shut_lid__ = to_binary_string_post2021(&LaptopAuraPower { let shut_lid__ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![ states: vec![
@@ -559,9 +559,9 @@ mod test {
boot: false, boot: false,
awake: false, awake: false,
sleep: false, sleep: false,
shutdown: true shutdown: true,
}, },
] ],
}); });
let boot_rear_ = to_binary_string_post2021(&LaptopAuraPower { let boot_rear_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![ states: vec![
@@ -570,9 +570,9 @@ mod test {
boot: true, boot: true,
awake: false, awake: false,
sleep: false, sleep: false,
shutdown: false shutdown: false,
}, },
] ],
}); });
let awake_rear = to_binary_string_post2021(&LaptopAuraPower { let awake_rear = to_binary_string_post2021(&LaptopAuraPower {
states: vec![ states: vec![
@@ -581,9 +581,9 @@ mod test {
boot: false, boot: false,
awake: true, awake: true,
sleep: false, sleep: false,
shutdown: false shutdown: false,
}, },
] ],
}); });
let sleep_rear = to_binary_string_post2021(&LaptopAuraPower { let sleep_rear = to_binary_string_post2021(&LaptopAuraPower {
states: vec![ states: vec![
@@ -592,9 +592,9 @@ mod test {
boot: false, boot: false,
awake: false, awake: false,
sleep: true, sleep: true,
shutdown: false shutdown: false,
}, },
] ],
}); });
let shut_rear_ = to_binary_string_post2021(&LaptopAuraPower { let shut_rear_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![ states: vec![
@@ -603,9 +603,9 @@ mod test {
boot: false, boot: false,
awake: false, awake: false,
sleep: false, sleep: false,
shutdown: true shutdown: true,
}, },
] ],
}); });
assert_eq!(boot_logo_, "00000001, 00000000, 00000000, 00000000"); assert_eq!(boot_logo_, "00000001, 00000000, 00000000, 00000000");
@@ -655,7 +655,7 @@ mod test {
zone: PowerZones::RearGlow, zone: PowerZones::RearGlow,
..Default::default() ..Default::default()
}, },
] ],
}); });
assert_eq!(byte1, "11111111, 00011110, 00001111, 00001111"); assert_eq!(byte1, "11111111, 00011110, 00001111, 00001111");
} }

View File

@@ -29,40 +29,40 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub const RED: Colour = Colour { pub const RED: Colour = Colour {
r: 0xff, r: 0xff,
g: 0x00, g: 0x00,
b: 0x00 b: 0x00,
}; };
pub const GREEN: Colour = Colour { pub const GREEN: Colour = Colour {
r: 0x00, r: 0x00,
g: 0xff, g: 0xff,
b: 0x00 b: 0x00,
}; };
pub const BLUE: Colour = Colour { pub const BLUE: Colour = Colour {
r: 0x00, r: 0x00,
g: 0x00, g: 0x00,
b: 0xff b: 0xff,
}; };
pub const VIOLET: Colour = Colour { pub const VIOLET: Colour = Colour {
r: 0x9b, r: 0x9b,
g: 0x26, g: 0x26,
b: 0xb6 b: 0xb6,
}; };
pub const TEAL: Colour = Colour { pub const TEAL: Colour = Colour {
r: 0x00, r: 0x00,
g: 0x7c, g: 0x7c,
b: 0x80 b: 0x80,
}; };
pub const YELLOW: Colour = Colour { pub const YELLOW: Colour = Colour {
r: 0xff, r: 0xff,
g: 0xef, g: 0xef,
b: 0x00 b: 0x00,
}; };
pub const ORANGE: Colour = Colour { pub const ORANGE: Colour = Colour {
r: 0xff, r: 0xff,
g: 0xa4, g: 0xa4,
b: 0x00 b: 0x00,
}; };
pub const GRADIENT: [Colour; 7] = [ pub const GRADIENT: [Colour; 7] = [
RED, VIOLET, BLUE, TEAL, GREEN, YELLOW, ORANGE RED, VIOLET, BLUE, TEAL, GREEN, YELLOW, ORANGE,
]; ];
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))] #[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
@@ -76,7 +76,7 @@ pub enum AuraDeviceType {
ScsiExtDisk = 3, ScsiExtDisk = 3,
Ally = 4, Ally = 4,
AnimeOrSlash = 5, AnimeOrSlash = 5,
Unknown = 255 Unknown = 255,
} }
impl AuraDeviceType { impl AuraDeviceType {
@@ -110,7 +110,7 @@ impl From<&str> for AuraDeviceType {
"1abe" | "1b4c" => Self::Ally, "1abe" | "1b4c" => Self::Ally,
"19b3" | "193b" => Self::AnimeOrSlash, "19b3" | "193b" => Self::AnimeOrSlash,
"19b6" => Self::LaptopKeyboard2021, "19b6" => Self::LaptopKeyboard2021,
_ => Self::Unknown _ => Self::Unknown,
} }
} }
} }
@@ -138,5 +138,5 @@ pub enum PowerZones {
KeyboardAndLightbar = 5, KeyboardAndLightbar = 5,
/// Ally specific for creating correct packet /// Ally specific for creating correct packet
Ally = 6, Ally = 6,
None = 255 None = 255,
} }

View File

@@ -1,7 +1,7 @@
// Only these two packets must be 17 bytes // Only these two packets must be 17 bytes
pub const AURA_LAPTOP_LED_APPLY: [u8; 17] = [ pub const AURA_LAPTOP_LED_APPLY: [u8; 17] = [
0x5d, 0xb4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0x5d, 0xb4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]; ];
pub const AURA_LAPTOP_LED_SET: [u8; 17] = [ 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 0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]; ];

View File

@@ -12,7 +12,7 @@ edition.workspace = true
default = [] default = []
mocking = [] mocking = []
x11 = ["slint/backend-winit-x11"] x11 = ["slint/backend-winit-x11"]
# Requires RUSTFLAGS="--cfg tokio_unstable" # Optional tokio debug feature does not require nightly; remove RUSTFLAGS note.
tokio-debug = ["console-subscriber"] tokio-debug = ["console-subscriber"]
[dependencies] [dependencies]
@@ -41,6 +41,7 @@ zbus.workspace = true
dirs.workspace = true dirs.workspace = true
notify-rust.workspace = true notify-rust.workspace = true
concat-idents.workspace = true concat-idents.workspace = true
futures-util.workspace = true
versions.workspace = true versions.workspace = true
@@ -48,12 +49,26 @@ versions.workspace = true
git = "https://github.com/slint-ui/slint.git" git = "https://github.com/slint-ui/slint.git"
default-features = false default-features = false
features = [ features = [
"gettext",
"compat-1-2", "compat-1-2",
"gettext",
"backend-winit-wayland", "backend-winit-wayland",
"renderer-winit-femtovg", "renderer-femtovg",
# "renderer-skia-opengl", # "renderer-skia-opengl",
] ]
[build-dependencies.slint-build] [build-dependencies.slint-build]
git = "https://github.com/slint-ui/slint.git" git = "https://github.com/slint-ui/slint.git"
[package.metadata.deb]
license-file = ["../LICENSE", "4"]
extended-description = """\
The dbus server for asusctl and rog-control-center applications."""
depends = "$auto"
section = "utility"
priority = "optional"
assets = [
["target/release/rog-control-center", "usr/bin/", "755"],
["../rog_gui-fakeinstall/usr/share/applications/*", "usr/share/share/applications/", "644"],
["../rog_gui-fakeinstall/usr/share/icons/hicolor/512x512/apps/*", "usr/share/icons/hicolor/512x512/apps", "644"],
["../rog_gui-fakeinstall/usr/share/icons/hicolor/scalable/status/*", "usr/share/icons/hicolor/scalable/status", "644"],
]

View File

@@ -18,7 +18,7 @@ fn main() {
CompilerConfiguration::new() CompilerConfiguration::new()
// .embed_resources(EmbedResourcesKind::EmbedFiles) // .embed_resources(EmbedResourcesKind::EmbedFiles)
.with_include_paths(vec![include]) .with_include_paths(vec![include])
.with_style("fluent".into()) .with_style("fluent".into()),
) )
.unwrap(); .unwrap();
} }

View File

@@ -23,5 +23,7 @@ pub struct CliStart {
help = "put ROGCC in layout viewing mode - this is helpful for finding existing layouts \ help = "put ROGCC in layout viewing mode - this is helpful for finding existing layouts \
that might match your laptop" that might match your laptop"
)] )]
pub layout_viewing: bool pub layout_viewing: bool,
#[options(help = "start in tray mode - main window hidden")]
pub tray_mode: bool,
} }

View File

@@ -21,7 +21,7 @@ pub struct Config {
pub fullscreen_width: u32, pub fullscreen_width: u32,
pub fullscreen_height: u32, pub fullscreen_height: u32,
// This field must be last // This field must be last
pub notifications: EnabledNotifications pub notifications: EnabledNotifications,
} }
impl Default for Config { impl Default for Config {
@@ -36,7 +36,7 @@ impl Default for Config {
fullscreen_height: 1080, fullscreen_height: 1080,
notifications: EnabledNotifications::default(), notifications: EnabledNotifications::default(),
ac_command: String::new(), ac_command: String::new(),
bat_command: String::new() bat_command: String::new(),
} }
} }
} }
@@ -77,7 +77,7 @@ pub struct Config461 {
pub enable_dgpu_notifications: bool, pub enable_dgpu_notifications: bool,
pub dark_mode: bool, pub dark_mode: bool,
// This field must be last // This field must be last
pub enabled_notifications: EnabledNotifications pub enabled_notifications: EnabledNotifications,
} }
impl From<Config461> for Config { impl From<Config461> for Config {
@@ -92,7 +92,7 @@ impl From<Config461> for Config {
start_fullscreen: false, start_fullscreen: false,
fullscreen_width: 1920, fullscreen_width: 1920,
fullscreen_height: 1080, fullscreen_height: 1080,
notifications: c.enabled_notifications notifications: c.enabled_notifications,
} }
} }
} }

View File

@@ -9,7 +9,8 @@ pub enum Error {
ConfigLockFail, ConfigLockFail,
XdgVars, XdgVars,
Zbus(zbus::Error), Zbus(zbus::Error),
Notification(notify_rust::error::Error) ZbusFdo(zbus::fdo::Error),
Notification(notify_rust::error::Error),
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@@ -21,7 +22,8 @@ impl fmt::Display for Error {
Error::ConfigLockFail => write!(f, "Failed to lock user config"), Error::ConfigLockFail => write!(f, "Failed to lock user config"),
Error::XdgVars => write!(f, "XDG environment vars appear unset"), Error::XdgVars => write!(f, "XDG environment vars appear unset"),
Error::Zbus(err) => write!(f, "Error: {}", err), Error::Zbus(err) => write!(f, "Error: {}", err),
Error::Notification(err) => write!(f, "Notification Error: {}", err) Error::ZbusFdo(err) => write!(f, "Error: {}", err),
Error::Notification(err) => write!(f, "Notification Error: {}", err),
} }
} }
} }
@@ -40,6 +42,12 @@ impl From<zbus::Error> for Error {
} }
} }
impl From<zbus::fdo::Error> for Error {
fn from(err: zbus::fdo::Error) -> Self {
Error::ZbusFdo(err)
}
}
impl From<notify_rust::error::Error> for Error { impl From<notify_rust::error::Error> for Error {
fn from(err: notify_rust::error::Error) -> Self { fn from(err: notify_rust::error::Error) -> Self {
Error::Notification(err) Error::Notification(err)

View File

@@ -38,5 +38,5 @@ pub enum Page {
System, System,
AuraEffects, AuraEffects,
AnimeMatrix, AnimeMatrix,
FanCurves FanCurves,
} }

View File

@@ -8,7 +8,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, warn, LevelFilter}; use log::{debug, 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;
@@ -17,7 +17,7 @@ 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::zbus_proxies::{ use rog_control_center::zbus_proxies::{
AppState, ROGCCZbus, ROGCCZbusProxyBlocking, ZBUS_IFACE, ZBUS_PATH AppState, ROGCCZbus, ROGCCZbusProxyBlocking, ZBUS_IFACE, ZBUS_PATH,
}; };
use rog_control_center::{print_versions, MainWindow}; use rog_control_center::{print_versions, MainWindow};
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
@@ -35,7 +35,19 @@ async fn main() -> Result<()> {
// If we're running under gamescope we have to set WAYLAND_DISPLAY for winit to // If we're running under gamescope we have to set WAYLAND_DISPLAY for winit to
// use // use
if let Ok(gamescope) = env::var("GAMESCOPE_WAYLAND_DISPLAY") { if let Ok(gamescope) = env::var("GAMESCOPE_WAYLAND_DISPLAY") {
env::set_var("WAYLAND_DISPLAY", gamescope); debug!("Gamescope detected");
if !gamescope.is_empty() {
debug!("Setting WAYLAND_DISPLAY to {}", gamescope);
env::set_var("WAYLAND_DISPLAY", gamescope);
}
// gamescope-0
else if let Ok(wayland) = env::var("WAYLAND_DISPLAY") {
debug!("Wayland display detected");
if wayland.is_empty() {
debug!("Setting WAYLAND_DISPLAY to gamescope-0");
env::set_var("WAYLAND_DISPLAY", "gamescope-0");
}
}
} }
// Try to open a proxy and check for app state first // Try to open a proxy and check for app state first
@@ -63,7 +75,7 @@ async fn main() -> Result<()> {
.unwrap(); .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 // start tokio
@@ -90,7 +102,7 @@ async fn main() -> Result<()> {
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" || prod_family == "RC72L"; let is_rog_ally = board_name == "RC71L" || board_name == "RC72L" || prod_family == "ROG Ally";
let args: Vec<String> = args().skip(1).collect(); let args: Vec<String> = args().skip(1).collect();
@@ -128,12 +140,22 @@ async fn main() -> Result<()> {
config.startup_in_background = false; config.startup_in_background = false;
config.start_fullscreen = true; config.start_fullscreen = true;
} }
if cli_parsed.tray_mode {
config.enable_tray_icon = true;
config.run_in_background = true;
config.startup_in_background = true;
}
config.write(); config.write();
let enable_tray_icon = config.enable_tray_icon; let enable_tray_icon = config.enable_tray_icon;
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));
// shared weak handle to the UI so other threads can request UI updates
let ui_handle: Arc<Mutex<Option<slint::Weak<MainWindow>>>> = Arc::new(Mutex::new(None));
start_notifications(config.clone(), &rt)?; start_notifications(config.clone(), &rt)?;
if enable_tray_icon { if enable_tray_icon {
@@ -158,87 +180,183 @@ async fn main() -> Result<()> {
slint::init_translations!(concat!(env!("CARGO_MANIFEST_DIR"), "/translations/")); slint::init_translations!(concat!(env!("CARGO_MANIFEST_DIR"), "/translations/"));
} }
let config_for_ui = config.clone();
let ui_handle_for_thread = ui_handle.clone();
thread::spawn(move || { thread::spawn(move || {
let mut state = AppState::StartingUp; let mut state = AppState::StartingUp;
loop { loop {
// save as a var, don't hold the lock the entire time or deadlocks happen if is_rog_ally {
if let Ok(app_state) = app_state.lock() { let config_copy_2 = config_for_ui.clone();
state = *app_state; let newui = setup_window(config_for_ui.clone());
} newui.window().on_close_requested(move || {
exit(0);
});
// This sleep is required to give the event loop time to react let ui_copy = newui.as_weak();
sleep(Duration::from_millis(300)); newui
if state == AppState::MainWindowShouldOpen { .window()
if let Ok(mut app_state) = app_state.lock() { .set_rendering_notifier(move |s, _| {
*app_state = AppState::MainWindowOpen; if let slint::RenderingState::BeforeRendering = s {
} let config = config_copy_2.clone();
ui_copy
let config_copy = config.clone(); .upgrade_in_event_loop(move |w| {
let app_state_copy = app_state.clone(); let fullscreen =
slint::invoke_from_event_loop(move || { config.lock().is_ok_and(|c| c.start_fullscreen);
UI.with(|ui| { if fullscreen && !w.window().is_fullscreen() {
let app_state_copy = app_state_copy.clone(); w.window().set_fullscreen(fullscreen);
let mut ui = ui.borrow_mut();
if let Some(ui) = ui.as_mut() {
ui.window().show().unwrap();
ui.window().on_close_requested(move || {
if let Ok(mut app_state) = app_state_copy.lock() {
*app_state = AppState::MainWindowClosed;
}
slint::CloseRequestResponse::HideWindow
});
} else {
let config_copy_2 = config_copy.clone();
let newui = setup_window(config_copy);
newui.window().on_close_requested(move || {
if let Ok(mut app_state) = app_state_copy.lock() {
*app_state = AppState::MainWindowClosed;
}
slint::CloseRequestResponse::HideWindow
});
let ui_copy = newui.as_weak();
newui
.window()
.set_rendering_notifier(move |s, _| {
if let slint::RenderingState::RenderingSetup = s {
let config = config_copy_2.clone();
ui_copy
.upgrade_in_event_loop(move |w| {
let fullscreen =
config.lock().is_ok_and(|c| c.start_fullscreen);
if fullscreen && !w.window().is_fullscreen() {
w.window().set_fullscreen(fullscreen);
}
})
.ok();
} }
}) })
.ok(); .ok();
ui.replace(newui);
} }
}); })
}) .ok();
.unwrap(); } else {
} else if state == AppState::QuitApp { // save as a var, don't hold the lock the entire time or deadlocks happen
slint::quit_event_loop().unwrap(); if let Ok(app_state) = app_state.lock() {
exit(0); state = *app_state;
} else if state != AppState::MainWindowOpen { }
if let Ok(config) = config.lock() {
if !config.run_in_background { // This sleep is required to give the event loop time to react
slint::quit_event_loop().unwrap(); sleep(Duration::from_millis(300));
exit(0); if state == AppState::MainWindowShouldOpen {
if let Ok(mut app_state) = app_state.lock() {
*app_state = AppState::MainWindowOpen;
}
let config_copy = config_for_ui.clone();
let app_state_copy = app_state.clone();
let ui_handle_for_ui = ui_handle_for_thread.clone();
slint::invoke_from_event_loop(move || {
let ui_handle_for_ui = ui_handle_for_ui.clone();
UI.with(|ui| {
let app_state_copy = app_state_copy.clone();
let mut ui = ui.borrow_mut();
if let Some(ui) = ui.as_mut() {
// store weak handle so other threads can update UI globals
if let Ok(mut h) = ui_handle_for_ui.lock() {
*h = Some(ui.as_weak());
}
ui.window().show().unwrap();
ui.window().on_close_requested(move || {
if let Ok(mut app_state) = app_state_copy.lock() {
*app_state = AppState::MainWindowClosed;
}
slint::CloseRequestResponse::HideWindow
});
} else {
let config_copy_2 = config_copy.clone();
let newui = setup_window(config_copy);
// store weak handle for the newly created UI
if let Ok(mut h) = ui_handle_for_ui.lock() {
*h = Some(newui.as_weak());
}
newui.window().on_close_requested(move || {
if let Ok(mut app_state) = app_state_copy.lock() {
*app_state = AppState::MainWindowClosed;
}
slint::CloseRequestResponse::HideWindow
});
let ui_copy = newui.as_weak();
newui
.window()
.set_rendering_notifier(move |s, _| {
if let slint::RenderingState::RenderingSetup = s {
let config = config_copy_2.clone();
ui_copy
.upgrade_in_event_loop(move |w| {
let fullscreen = config
.lock()
.is_ok_and(|c| c.start_fullscreen);
if fullscreen && !w.window().is_fullscreen() {
w.window().set_fullscreen(fullscreen);
}
})
.ok();
}
})
.ok();
ui.replace(newui);
}
});
})
.unwrap();
} else if state == AppState::QuitApp {
slint::quit_event_loop().unwrap();
exit(0);
} else if state != AppState::MainWindowOpen {
if let Ok(cfg) = config_for_ui.lock() {
if !cfg.run_in_background {
slint::quit_event_loop().unwrap();
exit(0);
}
} }
} }
} }
} }
}); });
// start config watcher to pick up external edits
spawn_config_watcher(config.clone(), ui_handle.clone());
slint::run_event_loop_until_quit().unwrap(); slint::run_event_loop_until_quit().unwrap();
rt.shutdown_background(); rt.shutdown_background();
Ok(()) Ok(())
} }
// Spawn a watcher thread that polls the config file and reloads it when modified.
// This keeps the running rogcc instance in sync with manual edits to the config file.
fn spawn_config_watcher(
config: Arc<Mutex<Config>>,
ui_handle: Arc<Mutex<Option<slint::Weak<MainWindow>>>>,
) {
std::thread::spawn(move || {
use std::time::SystemTime;
let cfg_path = Config::config_dir().join(Config::new().file_name());
let mut last_mtime: Option<SystemTime> = None;
loop {
if let Ok(meta) = std::fs::metadata(&cfg_path) {
if let Ok(m) = meta.modified() {
if last_mtime.is_none() {
last_mtime = Some(m);
} else if last_mtime.is_some_and(|t| t < m) {
// file changed, reload
last_mtime = Some(m);
let new_cfg = Config::new().load();
if let Ok(mut lock) = config.lock() {
*lock = new_cfg.clone();
}
// Update UI app settings globals if UI is present
if let Ok(maybe_weak) = ui_handle.lock() {
if let Some(weak) = maybe_weak.clone() {
let config_for_ui = config.clone();
weak.upgrade_in_event_loop(move |w| {
if let Ok(lock) = config_for_ui.lock() {
let cfg = lock.clone();
w.global::<rog_control_center::AppSettingsPageData>()
.set_run_in_background(cfg.run_in_background);
w.global::<rog_control_center::AppSettingsPageData>()
.set_startup_in_background(cfg.startup_in_background);
w.global::<rog_control_center::AppSettingsPageData>()
.set_enable_tray_icon(cfg.enable_tray_icon);
w.global::<rog_control_center::AppSettingsPageData>()
.set_enable_dgpu_notifications(
cfg.notifications.enabled,
);
}
})
.ok();
}
}
}
}
}
std::thread::sleep(std::time::Duration::from_secs(2));
}
});
}
fn do_cli_help(parsed: &CliStart) -> bool { fn do_cli_help(parsed: &CliStart) -> bool {
if parsed.help { if parsed.help {
println!("{}", CliStart::usage()); println!("{}", CliStart::usage());

View File

@@ -5,7 +5,7 @@ use rog_aura::{AuraEffect, AuraModeNum, AuraZone};
use rog_platform::platform::GpuMode; use rog_platform::platform::GpuMode;
use rog_platform::supported::{ use rog_platform::supported::{
AdvancedAura, AnimeSupportedFunctions, ChargeSupportedFunctions, LedSupportedFunctions, AdvancedAura, AnimeSupportedFunctions, ChargeSupportedFunctions, LedSupportedFunctions,
PlatformProfileFunctions, RogBiosSupportedFunctions, SupportedFunctions PlatformProfileFunctions, RogBiosSupportedFunctions, SupportedFunctions,
}; };
use rog_profiles::fan_curve_set::{CurveData, FanCurveSet}; use rog_profiles::fan_curve_set::{CurveData, FanCurveSet};
use supergfxctl::pci_device::{GfxMode, GfxPower}; use supergfxctl::pci_device::{GfxMode, GfxPower};
@@ -16,7 +16,7 @@ const NOPE: &str = "";
#[derive(Default)] #[derive(Default)]
pub struct DaemonProxyBlocking<'a> { pub struct DaemonProxyBlocking<'a> {
_phantom: &'a str _phantom: &'a str,
} }
impl<'a> DaemonProxyBlocking<'a> { impl<'a> DaemonProxyBlocking<'a> {
@@ -35,7 +35,7 @@ impl<'a> DaemonProxyBlocking<'a> {
#[derive(Default)] #[derive(Default)]
pub struct RogDbusClientBlocking<'a> { pub struct RogDbusClientBlocking<'a> {
_phantom: &'a str _phantom: &'a str,
} }
impl<'a> RogDbusClientBlocking<'a> { impl<'a> RogDbusClientBlocking<'a> {
@@ -100,6 +100,31 @@ impl Bios {
pub fn set_panel_od(&self, _b: bool) -> Result<()> { pub fn set_panel_od(&self, _b: bool) -> Result<()> {
Ok(()) Ok(())
} }
// Mock NV/dGPU tunables
pub fn nv_dynamic_boost(&self) -> Result<i16> {
Ok(0)
}
pub fn set_nv_dynamic_boost(&self, _v: i16) -> Result<()> {
Ok(())
}
pub fn nv_temp_target(&self) -> Result<i16> {
Ok(0)
}
pub fn set_nv_temp_target(&self, _v: i16) -> Result<()> {
Ok(())
}
pub fn nv_tgp(&self) -> Result<i16> {
Ok(0)
}
pub fn set_nv_tgp(&self, _v: i16) -> Result<()> {
Ok(())
}
} }
pub struct Profile; pub struct Profile;
@@ -126,16 +151,16 @@ impl Profile {
pub fn fan_curve_data(&self, _p: rog_profiles::Profile) -> Result<FanCurveSet> { pub fn fan_curve_data(&self, _p: rog_profiles::Profile) -> Result<FanCurveSet> {
let mut curve = FanCurveSet::default(); let mut curve = FanCurveSet::default();
curve.cpu.pwm = [ curve.cpu.pwm = [
30, 40, 60, 100, 140, 180, 200, 250 30, 40, 60, 100, 140, 180, 200, 250,
]; ];
curve.cpu.temp = [ curve.cpu.temp = [
20, 30, 40, 50, 70, 80, 90, 100 20, 30, 40, 50, 70, 80, 90, 100,
]; ];
curve.gpu.pwm = [ curve.gpu.pwm = [
40, 80, 100, 140, 170, 200, 230, 250 40, 80, 100, 140, 170, 200, 230, 250,
]; ];
curve.gpu.temp = [ curve.gpu.temp = [
20, 30, 40, 50, 70, 80, 90, 100 20, 30, 40, 50, 70, 80, 90, 100,
]; ];
Ok(curve) Ok(curve)
} }
@@ -204,7 +229,7 @@ impl Led {
AuraDevRog2::AwakeKeyb, AuraDevRog2::AwakeKeyb,
AuraDevRog2::SleepLogo, AuraDevRog2::SleepLogo,
AuraDevRog2::AwakeLogo, AuraDevRog2::AwakeLogo,
] ],
}) })
} }
@@ -242,11 +267,11 @@ impl Supported {
Ok(SupportedFunctions { Ok(SupportedFunctions {
anime_ctrl: AnimeSupportedFunctions(true), anime_ctrl: AnimeSupportedFunctions(true),
charge_ctrl: ChargeSupportedFunctions { charge_ctrl: ChargeSupportedFunctions {
charge_level_set: true charge_level_set: true,
}, },
platform_profile: PlatformProfileFunctions { platform_profile: PlatformProfileFunctions {
platform_profile: true, platform_profile: true,
fan_curves: true fan_curves: true,
}, },
keyboard_led: LedSupportedFunctions { keyboard_led: LedSupportedFunctions {
dev_id: AuraDevice::X19b6, dev_id: AuraDevice::X19b6,
@@ -267,7 +292,7 @@ impl Supported {
AuraZone::BarRight, AuraZone::BarRight,
AuraZone::Logo, AuraZone::Logo,
], ],
advanced_type: AdvancedAura::PerKey advanced_type: AdvancedAura::PerKey,
}, },
rog_bios_ctrl: RogBiosSupportedFunctions { rog_bios_ctrl: RogBiosSupportedFunctions {
post_sound: true, post_sound: true,
@@ -275,8 +300,8 @@ impl Supported {
panel_overdrive: true, panel_overdrive: true,
dgpu_disable: true, dgpu_disable: true,
mini_led_mode: true, mini_led_mode: true,
egpu_enable: true egpu_enable: true,
} },
}) })
} }
} }

View File

@@ -9,6 +9,7 @@ use std::process::Command;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::Duration; use std::time::Duration;
use futures_util::StreamExt;
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use notify_rust::{Hint, Notification, Timeout, Urgency}; use notify_rust::{Hint, Notification, Timeout, Urgency};
use rog_platform::platform::GpuMode; use rog_platform::platform::GpuMode;
@@ -19,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 zbus::export::futures_util::StreamExt;
use crate::config::Config; use crate::config::Config;
use crate::error::Result; use crate::error::Result;
@@ -31,7 +31,7 @@ const NOTIF_HEADER: &str = "ROG Control";
pub struct EnabledNotifications { pub struct EnabledNotifications {
pub enabled: bool, pub enabled: bool,
pub receive_notify_gfx: bool, pub receive_notify_gfx: bool,
pub receive_notify_gfx_status: bool pub receive_notify_gfx_status: bool,
} }
impl Default for EnabledNotifications { impl Default for EnabledNotifications {
@@ -39,7 +39,7 @@ impl Default for EnabledNotifications {
Self { Self {
enabled: true, enabled: true,
receive_notify_gfx: true, receive_notify_gfx: true,
receive_notify_gfx_status: true receive_notify_gfx_status: true,
} }
} }
} }
@@ -92,7 +92,7 @@ fn start_dpu_status_mon(config: Arc<Mutex<Config>>) {
pub fn start_notifications( pub fn start_notifications(
config: Arc<Mutex<Config>>, config: Arc<Mutex<Config>>,
rt: &Runtime rt: &Runtime,
) -> Result<Vec<JoinHandle<()>>> { ) -> Result<Vec<JoinHandle<()>>> {
// Setup the AC/BAT commands that will run on power status change // Setup the AC/BAT commands that will run on power status change
let config_copy = config.clone(); let config_copy = config.clone();
@@ -116,26 +116,26 @@ pub fn start_notifications(
if p == 0 && p != last_state { if p == 0 && p != last_state {
let prog: Vec<&str> = bat.split_whitespace().collect(); let prog: Vec<&str> = bat.split_whitespace().collect();
if prog.len() > 1 { if (!prog.is_empty()) && (!prog[0].is_empty()) {
let mut cmd = Command::new(prog[0]); let mut cmd = Command::new(prog[0]);
for arg in prog.iter().skip(1) { for arg in prog.iter().skip(1) {
cmd.arg(*arg); cmd.arg(*arg);
} }
cmd.spawn() cmd.spawn()
.map_err(|e| error!("AC command error: {e:?}")) .map_err(|e| error!("Battery power command error: {e:?}"))
.ok(); .ok();
} }
} else if p != last_state { } else if p != last_state {
let prog: Vec<&str> = ac.split_whitespace().collect(); let prog: Vec<&str> = ac.split_whitespace().collect();
if prog.len() > 1 { if (!prog.is_empty()) && (!prog[0].is_empty()) {
let mut cmd = Command::new(prog[0]); let mut cmd = Command::new(prog[0]);
for arg in prog.iter().skip(1) { for arg in prog.iter().skip(1) {
cmd.arg(*arg); cmd.arg(*arg);
} }
cmd.spawn() cmd.spawn()
.map_err(|e| error!("AC command error: {e:?}")) .map_err(|e| error!("AC power command error: {e:?}"))
.ok(); .ok();
} }
} }
@@ -203,18 +203,26 @@ pub fn start_notifications(
})?; })?;
let proxy_copy = proxy.clone(); let proxy_copy = proxy.clone();
let enabled_notifications_copy_action = enabled_notifications_copy.clone();
let mut p = proxy.receive_notify_action().await?; let mut p = proxy.receive_notify_action().await?;
tokio::spawn(async move { tokio::spawn(async move {
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() {
// Respect user notification settings for gpu actions
if let Ok(cfg) = enabled_notifications_copy_action.lock() {
if !cfg.notifications.enabled || !cfg.notifications.receive_notify_gfx {
continue;
}
}
let action = out.action(); let action = out.action();
let mode = convert_gfx_mode(proxy.mode().await.unwrap_or_default()); let mode = convert_gfx_mode(proxy.mode().await.unwrap_or_default());
match action { match action {
supergfxctl::actions::UserActionRequired::Reboot => { supergfxctl::actions::UserActionRequired::Reboot => {
do_mux_notification("Graphics mode change requires reboot", &mode) do_mux_notification("Graphics mode change requires reboot", &mode)
} }
_ => do_gfx_action_notif(<&str>::from(action), *action, mode) _ => do_gfx_action_notif(<&str>::from(action), *action, mode),
} }
.map_err(|e| { .map_err(|e| {
error!("zbus signal: do_gfx_action_notif: {e}"); error!("zbus signal: do_gfx_action_notif: {e}");
@@ -266,13 +274,13 @@ fn convert_gfx_mode(gfx: GfxMode) -> GpuMode {
GfxMode::Vfio => GpuMode::Vfio, GfxMode::Vfio => GpuMode::Vfio,
GfxMode::AsusEgpu => GpuMode::Egpu, GfxMode::AsusEgpu => GpuMode::Egpu,
GfxMode::AsusMuxDgpu => GpuMode::Ultimate, GfxMode::AsusMuxDgpu => GpuMode::Ultimate,
GfxMode::None => GpuMode::Error GfxMode::None => GpuMode::Error,
} }
} }
fn base_notification<T>(message: &str, data: &T) -> Notification fn base_notification<T>(message: &str, data: &T) -> Notification
where where
T: Display T: Display,
{ {
let mut notif = Notification::new(); let mut notif = Notification::new();
notif notif
@@ -290,7 +298,7 @@ fn do_gpu_status_notif(message: &str, data: &GfxPower) -> Notification {
GfxPower::Off => "asus_notif_green", GfxPower::Off => "asus_notif_green",
GfxPower::AsusDisabled => "asus_notif_white", GfxPower::AsusDisabled => "asus_notif_white",
GfxPower::AsusMuxDiscreet | GfxPower::Active => "asus_notif_red", GfxPower::AsusMuxDiscreet | GfxPower::Active => "asus_notif_red",
GfxPower::Unknown => "gpu-integrated" GfxPower::Unknown => "gpu-integrated",
}; };
notif.icon(icon); notif.icon(icon);
notif notif
@@ -309,7 +317,9 @@ fn do_gfx_action_notif(message: &str, action: GfxUserAction, mode: GpuMode) -> R
//.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(Timeout::Never) // For user-action notifications keep them visible if they require interaction
// but for non-interactive actions we prefer they auto-hide like other notifs.
.timeout(Timeout::Milliseconds(6000))
.icon("dialog-warning") .icon("dialog-warning")
.hint(Hint::Transient(true)); .hint(Hint::Transient(true));
@@ -331,7 +341,7 @@ fn do_gfx_action_notif(message: &str, action: GfxUserAction, mode: GpuMode) -> R
if id == "gfx-mode-session-action" { if id == "gfx-mode-session-action" {
let mut cmd = Command::new("qdbus"); let mut cmd = Command::new("qdbus");
cmd.args([ cmd.args([
"org.kde.ksmserver", "/KSMServer", "logout", "1", "0", "0" "org.kde.ksmserver", "/KSMServer", "logout", "1", "0", "0",
]); ]);
cmd.spawn().ok(); cmd.spawn().ok();
} else if id == "__closed" { } else if id == "__closed" {
@@ -375,7 +385,7 @@ fn do_mux_notification(message: &str, m: &GpuMode) -> Result<()> {
if id == "gfx-mode-session-action" { if id == "gfx-mode-session-action" {
let mut cmd = Command::new("qdbus"); let mut cmd = Command::new("qdbus");
cmd.args([ cmd.args([
"org.kde.ksmserver", "/KSMServer", "logout", "1", "1", "0" "org.kde.ksmserver", "/KSMServer", "logout", "1", "1", "0",
]); ]);
cmd.spawn().ok(); cmd.spawn().ok();
} else if id == "__closed" { } else if id == "__closed" {

View File

@@ -24,7 +24,7 @@ struct Icons {
rog_red: Icon, rog_red: Icon,
rog_green: Icon, rog_green: Icon,
rog_white: Icon, rog_white: Icon,
gpu_integrated: Icon gpu_integrated: Icon,
} }
static ICONS: OnceLock<Icons> = OnceLock::new(); static ICONS: OnceLock<Icons> = OnceLock::new();
@@ -32,7 +32,10 @@ static ICONS: OnceLock<Icons> = OnceLock::new();
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);
let mut file = OpenOptions::new().read(true).open(path).unwrap(); let mut file = OpenOptions::new()
.read(true)
.open(&path)
.unwrap_or_else(|_| panic!("Missing icon: {:?}", path));
let mut bytes = Vec::new(); let mut bytes = Vec::new();
file.read_to_end(&mut bytes).unwrap(); file.read_to_end(&mut bytes).unwrap();
@@ -48,14 +51,14 @@ fn read_icon(file: &Path) -> Icon {
Icon { Icon {
width: width as i32, width: width as i32,
height: height as i32, height: height as i32,
data: img.into_raw() data: img.into_raw(),
} }
} }
struct AsusTray { struct AsusTray {
current_title: String, current_title: String,
current_icon: Icon, current_icon: Icon,
proxy: ROGCCZbusProxyBlocking<'static> proxy: ROGCCZbusProxyBlocking<'static>,
} }
impl ksni::Tray for AsusTray { impl ksni::Tray for AsusTray {
@@ -103,7 +106,7 @@ async fn set_tray_icon_and_tip(
mode: GfxMode, mode: GfxMode,
power: GfxPower, power: GfxPower,
tray: &mut Handle<AsusTray>, tray: &mut Handle<AsusTray>,
supergfx_active: bool supergfx_active: bool,
) { ) {
if let Some(icons) = ICONS.get() { if let Some(icons) = ICONS.get() {
let icon = match power { let icon = match power {
@@ -162,7 +165,7 @@ pub fn init_tray(_supported_properties: Vec<Properties>, config: Arc<Mutex<Confi
let tray_init = AsusTray { let tray_init = AsusTray {
current_title: TRAY_LABEL.to_string(), current_title: TRAY_LABEL.to_string(),
current_icon: rog_red.clone(), current_icon: rog_red.clone(),
proxy proxy,
}; };
// TODO: return an error to the UI // TODO: return an error to the UI
@@ -187,7 +190,7 @@ pub fn init_tray(_supported_properties: Vec<Properties>, config: Arc<Mutex<Confi
rog_red: rog_red.clone(), rog_red: rog_red.clone(),
rog_green, rog_green,
rog_white, rog_white,
gpu_integrated gpu_integrated,
}); });
let mut has_supergfx = false; let mut has_supergfx = false;
@@ -215,8 +218,8 @@ pub fn init_tray(_supported_properties: Vec<Properties>, config: Arc<Mutex<Confi
may not be running or installed" may not be running or installed"
) )
} }
_ => warn!("Couldn't get mode from supergfxd: {e:?}") _ => warn!("Couldn't get mode from supergfxd: {e:?}"),
} },
} }
info!("Started ROGTray"); info!("Started ROGTray");

View File

@@ -1,6 +1,6 @@
use crate::slint_generatedMainWindow::{ use crate::slint_generatedMainWindow::{
AuraDevType as SlintDeviceType, AuraPowerState as SlintAuraPowerState, AuraDevType as SlintDeviceType, AuraPowerState as SlintAuraPowerState,
LaptopAuraPower as SlintLaptopAuraPower LaptopAuraPower as SlintLaptopAuraPower,
}; };
impl From<rog_aura::AuraEffect> for crate::slint_generatedMainWindow::AuraEffect { impl From<rog_aura::AuraEffect> for crate::slint_generatedMainWindow::AuraEffect {
@@ -10,20 +10,20 @@ impl From<rog_aura::AuraEffect> for crate::slint_generatedMainWindow::AuraEffect
red: m.colour1.r, red: m.colour1.r,
green: m.colour1.g, green: m.colour1.g,
blue: m.colour1.b, blue: m.colour1.b,
alpha: 255 alpha: 255,
} }
.into(), .into(),
colour2: RgbaColor { colour2: RgbaColor {
red: m.colour2.r, red: m.colour2.r,
green: m.colour2.g, green: m.colour2.g,
blue: m.colour2.b, blue: m.colour2.b,
alpha: 255 alpha: 255,
} }
.into(), .into(),
direction: m.direction.into(), direction: m.direction.into(),
mode: m.mode.into(), mode: m.mode.into(),
speed: m.speed.into(), speed: m.speed.into(),
zone: m.zone.into() zone: m.zone.into(),
} }
} }
} }
@@ -36,17 +36,17 @@ impl From<crate::slint_generatedMainWindow::AuraEffect> for rog_aura::AuraEffect
colour1: rog_aura::Colour { colour1: rog_aura::Colour {
r: c1.red, r: c1.red,
g: c1.green, g: c1.green,
b: c1.blue b: c1.blue,
}, },
colour2: rog_aura::Colour { colour2: rog_aura::Colour {
r: c2.red, r: c2.red,
g: c2.green, g: c2.green,
b: c2.blue b: c2.blue,
}, },
direction: m.direction.into(), direction: m.direction.into(),
mode: m.mode.into(), mode: m.mode.into(),
speed: m.speed.into(), speed: m.speed.into(),
zone: m.zone.into() zone: m.zone.into(),
} }
} }
} }
@@ -66,7 +66,7 @@ impl From<PowerZones> for SlintPowerZones {
PowerZones::RearGlow => SlintPowerZones::RearGlow, PowerZones::RearGlow => SlintPowerZones::RearGlow,
PowerZones::KeyboardAndLightbar => SlintPowerZones::KeyboardAndLightbar, PowerZones::KeyboardAndLightbar => SlintPowerZones::KeyboardAndLightbar,
PowerZones::Ally => SlintPowerZones::Ally, PowerZones::Ally => SlintPowerZones::Ally,
PowerZones::None => SlintPowerZones::Keyboard PowerZones::None => SlintPowerZones::Keyboard,
} }
} }
} }
@@ -80,7 +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 SlintPowerZones::Ally => PowerZones::Ally,
} }
} }
} }
@@ -92,7 +92,7 @@ impl From<SlintAuraPowerState> for AuraPowerState {
boot: value.boot, boot: value.boot,
awake: value.awake, awake: value.awake,
sleep: value.sleep, sleep: value.sleep,
shutdown: value.shutdown shutdown: value.shutdown,
} }
} }
} }
@@ -106,7 +106,7 @@ impl From<AuraPowerState> for SlintAuraPowerState {
sleep: value.sleep, sleep: value.sleep,
shutdown: value.shutdown, shutdown: value.shutdown,
zone, zone,
zone_name_idx: zone as i32 zone_name_idx: zone as i32,
} }
} }
} }
@@ -120,7 +120,7 @@ impl From<&AuraPowerState> for SlintAuraPowerState {
sleep: value.sleep, sleep: value.sleep,
shutdown: value.shutdown, shutdown: value.shutdown,
zone, zone,
zone_name_idx: zone as i32 zone_name_idx: zone as i32,
} }
} }
} }
@@ -140,7 +140,7 @@ impl From<LaptopAuraPower> for SlintLaptopAuraPower {
let converted: Vec<SlintAuraPowerState> = let converted: Vec<SlintAuraPowerState> =
value.states.iter().map(SlintAuraPowerState::from).collect(); value.states.iter().map(SlintAuraPowerState::from).collect();
Self { Self {
states: ModelRc::from(converted.as_slice()) states: ModelRc::from(converted.as_slice()),
} }
} }
} }
@@ -154,7 +154,7 @@ impl From<SlintDeviceType> for AuraDeviceType {
SlintDeviceType::ScsiExtDisk => Self::ScsiExtDisk, SlintDeviceType::ScsiExtDisk => Self::ScsiExtDisk,
SlintDeviceType::Unknown => Self::Unknown, SlintDeviceType::Unknown => Self::Unknown,
SlintDeviceType::Ally => Self::Ally, SlintDeviceType::Ally => Self::Ally,
SlintDeviceType::AnimeOrSlash => Self::AnimeOrSlash SlintDeviceType::AnimeOrSlash => Self::AnimeOrSlash,
} }
} }
} }
@@ -168,7 +168,7 @@ impl From<AuraDeviceType> for SlintDeviceType {
AuraDeviceType::ScsiExtDisk => SlintDeviceType::ScsiExtDisk, AuraDeviceType::ScsiExtDisk => SlintDeviceType::ScsiExtDisk,
AuraDeviceType::Unknown => SlintDeviceType::Unknown, AuraDeviceType::Unknown => SlintDeviceType::Unknown,
AuraDeviceType::Ally => SlintDeviceType::Ally, AuraDeviceType::Ally => SlintDeviceType::Ally,
AuraDeviceType::AnimeOrSlash => SlintDeviceType::AnimeOrSlash AuraDeviceType::AnimeOrSlash => SlintDeviceType::AnimeOrSlash,
} }
} }
} }

View File

@@ -8,7 +8,9 @@ impl From<Profile> for PlatformProfile {
match value { match value {
Profile::Balanced => PlatformProfile::Balanced, Profile::Balanced => PlatformProfile::Balanced,
Profile::Performance => PlatformProfile::Performance, Profile::Performance => PlatformProfile::Performance,
Profile::Quiet => PlatformProfile::Quiet Profile::Quiet => PlatformProfile::Quiet,
Profile::LowPower => PlatformProfile::LowPower,
Profile::Custom => PlatformProfile::Custom,
} }
} }
} }
@@ -18,7 +20,9 @@ impl From<PlatformProfile> for Profile {
match value { match value {
PlatformProfile::Balanced => Profile::Balanced, PlatformProfile::Balanced => Profile::Balanced,
PlatformProfile::Performance => Profile::Performance, PlatformProfile::Performance => Profile::Performance,
PlatformProfile::Quiet => Profile::Quiet PlatformProfile::Quiet => Profile::Quiet,
PlatformProfile::LowPower => Profile::LowPower,
PlatformProfile::Custom => Profile::Custom,
} }
} }
} }
@@ -28,7 +32,7 @@ impl From<FanType> for FanCurvePU {
match value { match value {
FanType::CPU => FanCurvePU::CPU, FanType::CPU => FanCurvePU::CPU,
FanType::Middle => FanCurvePU::MID, FanType::Middle => FanCurvePU::MID,
FanType::GPU => FanCurvePU::GPU FanType::GPU => FanCurvePU::GPU,
} }
} }
} }
@@ -38,7 +42,7 @@ impl From<FanCurvePU> for FanType {
match value { match value {
FanCurvePU::CPU => FanType::CPU, FanCurvePU::CPU => FanType::CPU,
FanCurvePU::GPU => FanType::GPU, FanCurvePU::GPU => FanType::GPU,
FanCurvePU::MID => FanType::Middle FanCurvePU::MID => FanType::Middle,
} }
} }
} }

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