Compare commits

...

167 Commits
6.0.4 ... 6.1.2

Author SHA1 Message Date
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
Luke Jones
11ac46df11 Edit README.md 2025-02-08 02:39:30 +00:00
Luke Jones
05bec93644 Edit README.md 2025-02-08 02:33:08 +00:00
Luke Jones
c77e7cf1ce Fix: correction to wrong matching function used for aura data find
Closes #607
2025-02-07 09:45:05 +13:00
Luke Jones
d30f7dc2ea Merge branch 'main' into 'main'
feat (rog-aura): ASUS TUF FA617NS AURA Support

See merge request asus-linux/asusctl!214
2025-02-05 02:58:05 +00:00
Luke Jones
759606fca3 Merge branch 'aura' into 'main'
Update 'led-mode' -> 'aura' in example command.

See merge request asus-linux/asusctl!215
2025-02-05 02:57:38 +00:00
Luke Jones
25ed8bdeed Fix spelling mistake 2025-02-05 14:35:17 +13:00
Luke Jones
de61a15b7a Prep 6.1.0 2025-02-04 18:52:55 +13:00
Luke Jones
16700e55f4 ROGCC: refactor many more parts of the PPT settings 2025-02-04 18:50:48 +13:00
Luke Jones
5833a011ce Merge branch 'g513rc-zonefix' into 'main'
Add basic zones for G513RC

See merge request asus-linux/asusctl!216
2025-02-03 04:08:29 +00:00
loystonpais
62577ee0e4 Add basic zones for G513RC 2025-02-03 07:36:32 +05:30
Luke Jones
dac35996b1 Enable use of GAMESCOPE_WAYLAND_DISPLAY 2025-02-02 20:02:51 +13:00
Luke Jones
5c2bcad7c6 Various UI fixes 2025-02-01 20:31:09 +13:00
armdebug
81f6c18a24 Update 'led-mode' -> 'aura' in example command.
led-mode is renamed to aura in 19ffcf3376
2025-01-28 02:18:56 +05:30
riarumoda
bcc3d42789 feat (rog-aura): ASUS TUF FA617NS AURA Support 2025-01-23 15:14:52 +08:00
Luke Jones
36c34cb3dd Merge branch 'fix/anime-animation-crash' into 'main'
fix: Use spawn instead of spawn_local in anime animation callback

Closes #588

See merge request asus-linux/asusctl!213
2025-01-22 02:05:04 +00:00
I-Al-Istannen
9b82b875f1 fix: Use spawn instead of spawn_local in anime animation callback
Closes #588
2025-01-21 21:04:20 +01:00
Luke D. Jones
bddccc696f Update deps 2025-01-21 19:49:20 +13:00
Luke D. Jones
51e9f10087 Prep 6.1.0-rc7 2025-01-21 16:44:58 +13:00
Luke D. Jones
911ff8690e feature: rework PPT tuning handling more
1. Per profile, per-ac/dc
2. Do not apply unless group is enabled
3. Better reset/disable handling
4. Selecting a profile defaults PPT to off/disabled
2025-01-21 16:39:34 +13:00
Luke D. Jones
25823dc6b7 asusd: anime: don't cause async deadlocks damnit
Same old song, an async mutex lock being held for a wide scope.
In particular being held for a scope that includes a function call which
also tries to get the lock.

Fix it by copy/clone of the config interior values required.

Fixes #588
2025-01-21 12:24:24 +13:00
Luke D. Jones
cba8e1a473 Add extra debug logging to anime path 2025-01-20 13:43:38 +13:00
Luke D. Jones
fb98827a1a Prep 6.1.0-rc6 2025-01-19 23:35:32 +13:00
Luke D. Jones
b9296862df Move entirely to using only platform-profile
throttle_thermal_policy is not ideal anymore and may be
removed from kernel in the future.
2025-01-19 21:52:32 +13:00
Luke D. Jones
450205f9a9 Bug fix: correctly set charge limit from UI 2025-01-19 17:29:36 +13:00
Luke D. Jones
82431ee25b Prep 6.1.0-rc5 2025-01-19 16:11:48 +13:00
Luke D. Jones
f11aea02a8 Add help and reset to UI for ppt values 2025-01-19 16:01:57 +13:00
Luke D. Jones
2d6d669c22 PPT restor defaults (WIP) 2025-01-19 12:02:22 +13:00
Luke D. Jones
f9cebf9221 Per-AC/DC per-profile tunings enabled 2025-01-19 11:33:48 +13:00
Luke D. Jones
a00808313e Prep 6.1.0-rc4 2025-01-18 23:11:46 +13:00
Luke D. Jones
3426591d32 Finalise per-profile PPT settings 2025-01-18 22:46:50 +13:00
Luke D. Jones
ef3b6636f5 Allow each performance profile to have different PPT values 2025-01-18 21:54:37 +13:00
Luke D. Jones
ee9e0a1e31 Fix: ROGCC: fix anime matrix settings 2025-01-18 21:27:57 +13:00
Luke D. Jones
9e84997cbf Fix: ROGCC: don't crash out if no tray area available 2025-01-18 20:54:25 +13:00
Luke D. Jones
2b22f82b72 Cleanup unsafe sysfs interfaces. Bugfixes for UI 2025-01-16 23:56:12 +13:00
Luke D. Jones
7a1b45071d Correct changelog rc3 tag 2025-01-15 22:25:24 +13:00
Luke D. Jones
ad63c429cb Bugfix: urgent small fixes 2025-01-15 22:19:46 +13:00
Luke D. Jones
a790d9a499 Remove dangerous use of ppt* in platform, add use of ppt_pl3_fppt in asus_armoury handler 2025-01-13 23:18:19 +13:00
Luke D. Jones
be51a1ab77 Disable strip in Makefile 2025-01-13 22:38:30 +13:00
Luke D. Jones
d9a88e7cc3 Notify user via message in UI if asus-armoury not loaded 2025-01-13 14:32:13 +13:00
Luke D. Jones
d785e17f95 Allow version in makefile to have '-rc*' 2025-01-12 18:52:45 +13:00
Luke D. Jones
efe64119c6 Prep 6.1.0-rc2 2025-01-12 17:53:32 +13:00
Luke D. Jones
128bc3fce1 Update readme, slash configs 2025-01-12 17:51:43 +13:00
Luke D. Jones
2123f369ad Small clippy cleanups 2025-01-04 20:04:07 +13:00
Luke D. Jones
2b7a2a5be3 Remove typeshare use 2025-01-03 16:37:22 +13:00
Luke D. Jones
1d9e89ef3d Update deps 2025-01-02 19:08:48 +13:00
Luke D. Jones
4011b3ebd4 ROGCC: begin using the new asus_armoury API 2025-01-01 14:47:08 +13:00
Luke D. Jones
f7456f0144 Allow changing of platform values with cli 2024-12-29 12:03:50 +13:00
Luke D. Jones
2ed2d82e03 Better print of firmware attributes in CLI 2024-12-29 11:36:22 +13:00
Luke D. Jones
d40f4733e2 Fix: prevent event loop error in ROGCC
Leftover code from parts of the refactor and tray crate change were
causing the app to crash due to the UI trying to issue a command on the
slint thread when the slint thread had not been created yet.

Closes #579
2024-12-28 22:06:16 +13:00
Luke Jones
98dc155e41 Merge branch 'feat/add-g533qs' into 'main'
feat(rog-aura): 🎸 Add G533QS aura support

See merge request asus-linux/asusctl!210
2024-12-28 04:32:17 +00:00
Luke D. Jones
fd3384decc Minor test of platform attributes 2024-12-28 10:56:34 +13:00
Luke D. Jones
a1a9c7077a Rename dbus. Add asus_armoury client source 2024-12-26 21:36:07 +13:00
Luke D. Jones
62aa1fe04f Add initial dbus draft of asus_armoury attrs 2024-12-26 16:41:17 +13:00
Luke D. Jones
a7b98c67ee Prep 6.1.0-rc1 2024-12-25 19:28:32 +13:00
Luke D. Jones
e7bbd99178 Stop GUI thread hogging CPU 2024-12-25 11:29:10 +13:00
Luke D. Jones
e2d3f99d91 Try better to capture TUF keyboard control 2024-12-25 10:31:43 +13:00
Luke D. Jones
1cad5afc0d FirmwareAttributes: further work 2024-12-24 21:50:32 +13:00
Luke D. Jones
fd31ac458d Prepare for rc1 2024-12-24 18:01:19 +13:00
Luke D. Jones
6292b3361d Re-add TUF LED support 2024-12-24 17:13:05 +13:00
Luke D. Jones
ab7a4bbad3 Refactor ROGCC to use dbus to communicate with self instead of pipe file 2024-12-24 17:13:05 +13:00
Luke D. Jones
0f2d89858e SCSI support: ROG Arion external drive LED control 2024-12-24 17:13:05 +13:00
Luke D. Jones
19ffcf3376 Refactor: Make all Aura type devices use "device_manager"
Open the door to adding many other types of "aura" devices later.
2024-12-24 17:13:05 +13:00
kamack38
72c0b24ead feat(rog-aura): 🎸 Add G533QS aura support 2024-12-23 00:18:46 +01:00
Luke D. Jones
0ddfe76c31 Minor prep 2024-12-18 11:21:40 +13:00
Luke D. Jones
c05c8ba648 Fix: prevent an aura manager deadlock 2024-12-18 10:33:14 +13:00
Luke D. Jones
ccdc576319 Switch tray to knsi crate. Many things to do 2024-12-17 22:14:13 +13:00
Luke Jones
39b16ffc91 Merge branch 'one-shot-charge' into 'main'
feat: One-shot full charge charge

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Closes #523

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

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

Closes #512

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

Closes #480

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

Closes #505

See merge request asus-linux/asusctl!188
2024-05-17 10:09:12 +00:00
Luke D. Jones
1353fe3fdb Rename and recreate the default Anime config if cache setup fails 2024-05-17 21:54:37 +12:00
Luke D. Jones
770bd12a5c Add G513RS to laptop DB 2024-05-17 18:56:51 +12:00
Luke Jones
af2f5592f0 Merge pull request #29 from chrnin/fix/udev-for-zenbook
add Zenbook to asusd.rules
2024-05-17 11:37:36 +12:00
Luke D. Jones
9686c41ac4 Fix pipeline 2024-05-17 11:35:36 +12:00
Luke D. Jones
fbdb0514d2 Prep new release 2024-05-17 10:18:54 +12:00
Luke D. Jones
1f5650d26b Add tests, G513L laptop 2024-05-17 09:41:40 +12:00
Luke D. Jones
14db97c476 Update G513 model DB entry 2024-05-15 10:04:02 +12:00
Luke D. Jones
66a501ecf6 Prepare new release 2024-05-14 17:35:44 +12:00
Luke D. Jones
6b129763d4 Reimplement the 0x1866 keyboard power settings 2024-05-14 10:18:56 +12:00
Luke D. Jones
a0368d4345 Temporary fix of old keyboard+lightbar 2024-05-13 23:53:25 +12:00
Luke D. Jones
f2f090a88f Use backup hidraw brightness write if read-back value fails 2024-05-13 16:18:00 +12:00
Luke D. Jones
6c7e1a6467 Add option for enable/disable apply AC/Bat policy on change 2024-05-12 21:37:04 +12:00
Luke Jones
92ca7bc70d Merge branch 'main' into 'main'
rog-control-center Turkish language support added

See merge request asus-linux/asusctl!185
2024-05-11 08:31:20 +00:00
Winfried
dd1e6b845b rog-control-center Turkish language support added 2024-05-11 10:52:19 +03:00
Luke D. Jones
882fa9bed8 don't setup page data if unsupported 2024-05-11 11:52:26 +12:00
Luke D. Jones
9c7dfb4030 Small fixups 2024-05-11 11:09:02 +12:00
Luke D. Jones
f855908c82 Better everything 2024-05-11 11:03:13 +12:00
Luke D. Jones
293a087b8a Fixups 2024-05-10 23:14:48 +12:00
Luke D. Jones
fd72a04bb8 Sanitize the dbus path for aura devices (remove '.' chars) 2024-05-10 19:37:38 +12:00
Luke D. Jones
f131a3fa70 More logigng. Adjust the aura init process. Fix TUF led power 2024-05-10 18:58:05 +12:00
Luke D. Jones
ccf8d8df91 Prep 6.0.5 version 2024-05-10 12:44:46 +12:00
Luke D. Jones
b970d364f7 Fix test 2024-05-10 12:36:34 +12:00
Luke D. Jones
1da68ea69d Try to mitigate the lack of kbd_brightness on some laptops 2024-05-10 12:22:36 +12:00
Luke D. Jones
e62e7e8eca Cleanup sys config 2024-05-10 10:26:32 +12:00
Luke D. Jones
1b1d10c461 Try to better handle pre-2021 laptops with lightbar 2024-05-10 10:04:51 +12:00
Stefan Boca
14ea0f6d83 Update GA402XV supported aura modes 2024-05-08 19:20:17 -07:00
Christophe Ninucci
7122fbaca8 add Zenbook to asusd.rules 2024-04-14 11:30:39 +02:00
179 changed files with 15317 additions and 8098 deletions

View File

@@ -12,7 +12,7 @@ echo '+cargo clippy --all -- -D warnings'
cargo clippy --all -- -D warnings
echo '+cargo test --all'
cargo test --all
cargo test --all -- --test-threads=1
echo '+cargo cranky'
cargo cranky
cargo cranky

View File

@@ -17,7 +17,7 @@ image: rust:latest
- target/release/.cargo-lock
before_script:
- apt-get update -qq && apt-get install -y -qq libinput-dev libseat-dev libudev-dev libgtk-3-dev grep llvm clang libclang-dev libsdl2-dev libsdl2-gfx-dev
- apt-get update -qq && apt-get install -y -qq libudev-dev libgtk-3-dev grep llvm clang libclang-dev libsdl2-dev libsdl2-gfx-dev
stages:
- format
@@ -52,7 +52,7 @@ test:
<<: *rust_cache
script:
- mkdir -p .git/hooks > /dev/null
- cargo test --all
- cargo test --all -- --test-threads=1
release:
only:

View File

@@ -1,12 +1,178 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [v6.1.2]
### Changed
- Try a slightly different tact to fix charge control slider
## [v6.1.1]
### Changed
- Fix aura data matching
- Fix charge control slider
## [v6.1.0]
### Changed
- Update deps
- Add support for G513RC RGB modes
- Many UI fixes
- Fixes to PPT settings in UI
## [v6.1.0-rc7]
- Refactor PPT handling more:
1. Per profile, per-ac/dc
2. Do not apply unless group is enabled
3. Better reset/disable handling
4. Selecting a profile defaults PPT to off/disabled
- Bugfix: prevent an AniMe thread async deadlock
## [v6.1.0-rc6]
### Changed
- Two small fixes, one for `low-power` profile name, and one for base gpu tdp
- Move to using platform_profile api only (no throttle_thermal_policy)
## [v6.1.0-rc5]
### Changed
- Per-AC/DC, per-profile tunings enabled (Battery vs AC power + platform profile)
- Add ability to restore PPT defaults
- Add PPT help dialogue to UI
- Bug fix: correctly set charge limit from UI
## [v6.1.0-rc4]
### Changed
- Bug fix: UI was setting incorrect value for FPPT
- Bug fix: Re-add callbacks for the throttle and epp settings in UI
- Bug fix: Fix UI settigns for AniMe Matrix display
- Bug fix: better handle missing tray (for example gnome)
- Strip out all outdated and unsafe tuning stuff
- Allow each performance profile to have different PPT settings
## [v6.1.0-rc3]
### Changed
- Bug fixes
- Partial support for per-profile CPU tunings (WIP)
## [v6.1.0-rc2]
### Added
- asus-armoury driver support. WIP, will be adjusted/changed further
- More "Slash" display controls
## [v6.1.0-rc1]
### Added
- ROG Arion external driver LED support
- Add GA605W LED layout
- Add GA605 + GU605 Slash support
### Changed
- Fix attribute writes. At some point the kernel API seems to have changed.
- Extremely large refactor of Aura device handling. Should enable easy add of different kinds now.
- Rename CLI args for aura related properties. This will likely change further as more devices are added
## [v6.0.12]
### Changed
- Add Ally X aura config
- Fixes to Ally led power configs
- Fix CLI led modes
- Add FX517Z to aura_support
- Add G614JJ Aura support
- Add G614JZ (2023 Strix G16) Aura support
## [v6.0.11]
### Changed
- Renamed `Strobe` effect to `RainbowCycle` to prevent confusion over what it is
- Ranamed `Rainbow` effect to `RainbowWave`
- Cleaned up serde crate deps
- Fixed AniMe on GA402XZ
- Update some of the G713 device aura features
- Update some of the G513 device aura features
## [v6.0.10]
### Added
- Add the GA401I model to aura_support.
### Changed
- Aura support return a default aura definition instead of nothing
- Minor updates in aura controller to ensure configs are updated if the support file changes
- Don't panic if -ENODEV on fan_curve enable
- Adjust the G513Q support to match what is on the asus website.
- Adjust init sequence of anime to prevent accidental use of Slash as Anime
- Enable notifs and tray icon without supergfx
## [v6.0.9]
### Added
- Add G513RS to laptop DB.
- Add G512LI to laptop DB.
### Changed
- Rename and recreate the default Anime config if cache setup fails
### Fixed
- Nuke the issue of GUI taking 100% of a CPU core
## [v6.0.8]
### Added
- Add G512L laptop DB entry
### Changed
- Add more tests to verify things
### Fix
- asusctl incorrectly assumes fan-curves unsupported. Now fixed.
- try to fix ROGCC using CPU time.
## [v6.0.7]
### Changed
- Add a config option to set if throttle policy is changed on ac/bat change (UI only)
- Allow X11 GUI. This is *not* supported. Please see readme.
- Fixes to some GUI widget layouts and sizing
- Do a backup HID raw write fro brightness if the read-back value does not match. This is a temporary solve for some G14 and G16 until the kernel patch is ready.
- Reimplement the older 0x1866 MCU keyboard control power bits plus UI control for it. If you had a keyboard affected by Lightbar issues and it is older than a couple of years this should help. If not, please file a bug.
## [v6.0.6]
### Added
- Add GX650R laptop to aura DB
### Changed
- Further tweaks to aura init
- More logging
- Fix TUF laptop led power
- Sanitize the dbus path for aura devices (remove `.` chars)
- Even more error handling (gracefully)
- Better checking for dbus interfaces
- Remove dbus `supported_interfaces`
- dbus ObjectManager is now at root `/`
## [v6.0.5]
### Changed
- Better more robust error handling in ROGCC
- Try to better handle pre-2021 laptops with lightbar
- Add more logging to try and trace the charge_control_end_threshold issue
- Make `kbd_brightness` optional to work around issues on some laptops that seem to lack it. Likely this will all need a refactor *again* if work proceeds in kernel for better RGB support.
## [v6.0.4]
### Changed

4862
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +1,6 @@
[workspace]
members = [
"asusctl",
"asusd",
"asusd-user",
"config-traits",
"cpuctl",
"dmi-id",
"rog-platform",
"rog-dbus",
"rog-anime",
"rog-aura",
"rog-profiles",
"rog-control-center",
"rog-slash",
"simulators",
]
default-members = [
"asusctl",
"asusd",
"asusd-user",
"cpuctl",
"rog-control-center",
]
resolver = "2"
[workspace.package]
version = "6.0.4"
rust-version = "1.77"
version = "6.1.2"
rust-version = "1.82"
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
@@ -35,25 +9,46 @@ homepage = "https://gitlab.com/asus-linux/asusctl"
description = "Laptop feature control for ASUS ROG laptops and others"
edition = "2021"
[workspace]
resolver = "2"
members = [
"asusctl",
"asusd",
"asusd-user",
"config-traits",
"dmi-id",
"rog-platform",
"rog-dbus",
"rog-anime",
"rog-aura",
"rog-profiles",
"rog-control-center",
"rog-slash",
"simulators",
"rog-scsi",
]
default-members = ["asusctl", "asusd", "asusd-user", "rog-control-center"]
[workspace.dependencies]
tokio = { version = "^1.36.0", default-features = false, features = [
"macros",
"sync",
"time",
"rt-multi-thread",
tokio = { version = "^1.39.0", default-features = false, features = [
"macros",
"sync",
"time",
"rt",
"rt-multi-thread",
] }
concat-idents = "^1.1"
dirs = "^4.0"
smol = "^1.3"
smol = "^2.0"
mio = "0.8.11"
zbus = "4.1"
logind-zbus = { version = "4.0.2" } #, default-features = false, features = ["non_blocking"] }
futures-util = "0.3.31"
zbus = "5.1.1"
logind-zbus = { version = "5.0.0" } #, default-features = false, features = ["non_blocking"] }
serde = "^1.0"
serde_derive = "^1.0"
serde = { version = "^1.0", features = ["serde_derive"] }
ron = "*"
typeshare = "1.0.0"
log = "^0.4"
env_logger = "^0.10.0"
@@ -71,26 +66,26 @@ gif = "^0.12.0"
versions = "6.2"
notify-rust = { git = "https://github.com/flukejones/notify-rust.git", rev = "54176413b81189a3e4edbdc20a0b4f7e2e35c063", default-features = false, features = [
"z",
] }
notify-rust = { version = "4.11.4", features = ["z", "async"] }
sg = { git = "https://github.com/flukejones/sg-rs.git" }
[profile.release]
# thin = 57s, asusd = 9.0M
# fat = 72s, asusd = 6.4M
lto = "fat"
lto = "thin"
debug = false
opt-level = 3
panic = "abort"
codegen-units = 1
# codegen-units = 1
[profile.dev]
opt-level = 1
codegen-units = 16
# codegen-units = 1
[profile.dev.package."*"]
opt-level = 1
codegen-units = 16
# codegen-units = 1
[profile.bench]
debug = false

View File

@@ -1,4 +1,4 @@
VERSION := $(shell /usr/bin/grep -Pm1 'version = "(\d.\d.\d)"' Cargo.toml | cut -d'"' -f2)
VERSION := $(shell /usr/bin/grep -Pm1 'version = "(\d+.\d+.\d+.*)"' Cargo.toml | cut -d'"' -f2)
INSTALL = install
INSTALL_PROGRAM = ${INSTALL} -D -m 0755
@@ -30,6 +30,11 @@ else
TARGET = debug
endif
X11 ?= 0
ifeq ($(X11),1)
ARGS += --features "rog-control-center/x11"
endif
VENDORED ?= 0
ifeq ($(VENDORED),1)
ARGS += --frozen
@@ -113,16 +118,10 @@ vendor:
mv .cargo/config ./cargo-config
rm -rf .cargo
rm -rf vendor
cargo vendor-filterer --platform x86_64-unknown-linux-gnu vendor
cargo vendor-filterer --all-features --platform x86_64-unknown-linux-gnu vendor
tar pcfJ vendor_asusctl_$(VERSION).tar.xz vendor
rm -rf vendor
bindings:
typeshare ./rog-anime/src/ --lang=typescript --output-file=bindings/ts/anime.ts
typeshare ./rog-aura/src/ --lang=typescript --output-file=bindings/ts/aura.ts
typeshare ./rog-profiles/src/ --lang=typescript --output-file=bindings/ts/profiles.ts
typeshare ./rog-platform/src/ --lang=typescript --output-file=bindings/ts/platform.ts
translate:
find -name \*.slint | xargs slint-tr-extractor -o rog-control-center/translations/en/rog-control-center.po
@@ -133,7 +132,7 @@ ifeq ($(VENDORED),1)
tar pxf vendor_asusctl_$(VERSION).tar.xz
endif
cargo build $(ARGS)
ifneq ($(STRIP_BINARIES),0)
ifeq ($(STRIP_BINARIES),1)
strip -s ./target/$(TARGET)/$(BIN_C)
strip -s ./target/$(TARGET)/$(BIN_D)
strip -s ./target/$(TARGET)/$(BIN_U)

View File

@@ -1,6 +1,6 @@
# `asusctl` for ASUS ROG
[Become a Patron!](https://www.patreon.com/bePatron?u=7602281) - [Asus Linux Website](https://asus-linux.org/)
[![Become a Patron!](https://github.com/codebard/patron-button-and-widgets-by-codebard/blob/master/images/become_a_patron_button.png?raw=true)](https://www.patreon.com/bePatron?u=7602281) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/V7V5CLU67) - [Asus Linux Website](https://asus-linux.org/)
**WARNING:** Many features are developed in tandem with kernel patches. If you see a feature is missing you either need a patched kernel or latest release.
@@ -11,10 +11,16 @@ Now includes a GUI, `rog-control-center`.
## Kernel support
**The minimum supported kernel version is 6.10**, which will contain the patches from [here](https://lore.kernel.org/platform-driver-x86/20240404001652.86207-1-luke@ljones.dev/). This is especially required for 2023+ devices and possibly some lat 2022 devices.
Due to on-going driver work the minimum suggested kernel version is always **the latest*, as improvements and fixes are continuous.
Support for some new features is not avilable unless you run a patched kernel with the work I am doing [in this github repo](https://github.com/flukejones/linux/tree/wip/ally-6.13). Use the linked branch, or `wip/ally-6.12`. Everything that is done here is upstreamed eventually (a long process).
Z13 devices will need [these](https://lore.kernel.org/linux-input/20240416090402.31057-1-luke@ljones.dev/T/#t)
## X11 support
X11 is not supported at all, as in I will not help you with X11 issues if there are any due to limited time and it being unmaintained itself. You can however build `rog-control-center` with it enabled `cargo build --features "rog-control-center/x11"`.
## Goals
The main goal of this work is to provide a safe and easy to use abstraction over various laptop features via DBUS, and to provide some helpful defaults and other behaviour such as toggling throttle/profile on AC/battery change.
@@ -33,7 +39,7 @@ See the [rog-aura readme](./rog-aura/README.md) for more details.
## Discord
[![Discord](https://img.shields.io/badge/Discord-7289DA?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/z8y99XqPb7)
[![Discord](https://img.shields.io/badge/Discord-7289DA?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/B8GftRW2Hd)
## SUPPORTED LAPTOPS
@@ -72,13 +78,15 @@ The list is a bit outdated as many features have been enabled in the Linux kerne
A gui is now in the repo - ROG Control Center. At this time it is still a WIP, but it has almost all features in place already.
**NOTE**: Xorg is not supported.
# 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.
**fedora:**
dnf install cmake clang-devel libinput-devel libseat-devel libgbm-devel libxkbcommon-devel systemd-devel libdrm-devel expat-devel pcre2-devel libzstd-devel gtk3-devel
dnf install cmake clang-devel libxkbcommon-devel systemd-devel expat-devel pcre2-devel libzstd-devel gtk3-devel
make
sudo make install
@@ -87,7 +95,16 @@ Rust and cargo are required, they can be installed from [rustup.rs](https://rust
Works with KDE Plasma (without GTK packages)
zypper in -t pattern devel_basis
zypper in rustup make cmake clang-devel libinput-devel libseat-devel libgbm-devel libxkbcommon-devel systemd-devel libdrm-devel expat-devel pcre2-devel libzstd-devel gtk3-devel
zypper in rustup make cmake clang-devel libxkbcommon-devel systemd-devel expat-devel pcre2-devel libzstd-devel gtk3-devel
make
sudo make install
**Debian(unsuported):**
officially unsuported,but you can still try and test it by yourself(some features may not be available).
sudo apt install libclang-dev libudev-dev libfontconfig-dev build-essential cmake libxkbcommon-dev
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
make
sudo make install

View File

@@ -10,19 +10,20 @@ edition.workspace = true
[dependencies]
rog_anime = { path = "../rog-anime" }
rog_scsi = { path = "../rog-scsi" }
rog_slash = { path = "../rog-slash" }
rog_aura = { path = "../rog-aura" }
rog_dbus = { path = "../rog-dbus" }
rog_profiles = { path = "../rog-profiles" }
rog_platform = { path = "../rog-platform" }
asusd = { path = "../asusd" }
dmi_id = { path = "../dmi-id" }
log.workspace = true
env_logger.workspace = true
ron.workspace = true
gumdrop.workspace = true
zbus.workspace = true
[dev-dependencies]
rog_dbus = { path = "../rog-dbus" }
cargo-husky.workspace = true

View File

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

View File

@@ -29,7 +29,7 @@ fn main() {
}
}
let anime_type = get_anime_type().unwrap();
let anime_type = get_anime_type();
proxy
.write(matrix.into_data_buffer(anime_type).unwrap())
.unwrap();

View File

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

View File

@@ -14,7 +14,7 @@ fn main() {
let conn = Connection::system().unwrap();
let proxy = AnimeProxyBlocking::new(&conn).unwrap();
let anime_type = get_anime_type().unwrap();
let anime_type = get_anime_type();
let mut matrix = AnimeGrid::new(anime_type);
let tmp = matrix.get_mut();

View File

@@ -9,7 +9,7 @@ use zbus::blocking::Connection;
fn main() {
let conn = Connection::system().unwrap();
let proxy = AnimeProxyBlocking::new(&conn).unwrap();
let anime_type = get_anime_type().unwrap();
let anime_type = get_anime_type();
let mut matrix = AnimeDataBuffer::new(anime_type);
matrix.data_mut()[1] = 100; // start = 1
for n in matrix.data_mut()[2..32].iter_mut() {

View File

@@ -20,17 +20,17 @@ fn main() -> Result<(), Box<dyn Error>> {
exit(-1);
}
let anime_type = get_anime_type()?;
let anime_type = get_anime_type();
let matrix = AnimeImage::from_png(
Path::new(&args[1]),
args[2].parse::<f32>().unwrap(),
args[3].parse::<f32>().unwrap(),
Vec2::new(
args[4].parse::<f32>().unwrap(),
args[5].parse::<f32>().unwrap(),
args[5].parse::<f32>().unwrap()
),
args[6].parse::<f32>().unwrap(),
anime_type,
anime_type
)?;
proxy.write(<AnimeDataBuffer>::try_from(&matrix)?).unwrap();

View File

@@ -23,17 +23,17 @@ fn main() -> Result<(), Box<dyn Error>> {
exit(-1);
}
let anime_type = get_anime_type()?;
let anime_type = get_anime_type();
let mut matrix = AnimeImage::from_png(
Path::new(&args[1]),
args[2].parse::<f32>().unwrap(),
args[3].parse::<f32>().unwrap(),
Vec2::new(
args[4].parse::<f32>().unwrap(),
args[5].parse::<f32>().unwrap(),
args[5].parse::<f32>().unwrap()
),
args[6].parse::<f32>().unwrap(),
anime_type,
anime_type
)?;
loop {

View File

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

View File

@@ -40,7 +40,7 @@ pub struct AnimeCommand {
#[options(no_short, meta = "", help = "Off with his head!!!")]
pub off_with_his_head: Option<bool>,
#[options(command)]
pub command: Option<AnimeActions>,
pub command: Option<AnimeActions>
}
#[derive(Options)]
@@ -54,7 +54,7 @@ pub enum AnimeActions {
#[options(help = "display an animated diagonal/pixel-perfect GIF")]
PixelGif(AnimeGifDiagonal),
#[options(help = "change which builtin animations are shown")]
SetBuiltins(Builtins),
SetBuiltins(Builtins)
}
#[derive(Options)]
@@ -82,7 +82,7 @@ pub struct Builtins {
)]
pub shutdown: AnimShutdown,
#[options(meta = "", help = "set/apply the animations <true/false>")]
pub set: Option<bool>,
pub set: Option<bool>
}
#[derive(Options)]
@@ -100,7 +100,7 @@ pub struct AnimeImage {
#[options(meta = "", default = "0.0", help = "the angle in radians")]
pub angle: f32,
#[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")]
pub bright: f32,
pub bright: f32
}
#[derive(Options)]
@@ -110,7 +110,7 @@ pub struct AnimeImageDiagonal {
#[options(meta = "", help = "full path to the png to display")]
pub path: String,
#[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")]
pub bright: f32,
pub bright: f32
}
#[derive(Options)]
@@ -134,7 +134,7 @@ pub struct AnimeGif {
default = "1",
help = "how many loops to play - 0 is infinite"
)]
pub loops: u32,
pub loops: u32
}
#[derive(Options)]
@@ -150,5 +150,5 @@ pub struct AnimeGifDiagonal {
default = "1",
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>")]
pub boot: Option<bool>,
#[options(meta = "", help = "Control suspend animations <true/false>")]
pub sleep: Option<bool>,
pub sleep: Option<bool>
}
#[derive(Options, Debug)]
@@ -25,7 +25,7 @@ pub struct LedPowerCommand2 {
#[options(help = "print help message")]
pub help: bool,
#[options(command)]
pub command: Option<SetAuraZoneEnabled>,
pub command: Option<SetAuraZoneEnabled>
}
#[derive(Options, Debug)]
@@ -41,6 +41,8 @@ pub enum SetAuraZoneEnabled {
Lid(AuraPowerStates),
#[options(help = "")]
RearGlow(AuraPowerStates),
#[options(help = "")]
Ally(AuraPowerStates)
}
#[derive(Debug, Clone, Options)]
@@ -54,12 +56,12 @@ pub struct AuraPowerStates {
#[options(help = "defaults to false if option unused")]
pub sleep: bool,
#[options(help = "defaults to false if option unused")]
pub shutdown: bool,
pub shutdown: bool
}
#[derive(Options)]
pub struct LedBrightness {
level: Option<u8>,
level: Option<u8>
}
impl LedBrightness {
pub fn new(level: Option<u8>) -> Self {
@@ -94,7 +96,7 @@ impl ToString for LedBrightness {
Some(0x00) => "low",
Some(0x01) => "med",
Some(0x02) => "high",
_ => "unknown",
_ => "unknown"
};
s.to_owned()
}
@@ -111,7 +113,7 @@ pub struct SingleSpeed {
meta = "",
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)]
@@ -127,7 +129,7 @@ pub struct SingleSpeedDirection {
meta = "",
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)]
@@ -141,7 +143,7 @@ pub struct SingleColour {
meta = "",
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)]
@@ -157,7 +159,7 @@ pub struct SingleColourSpeed {
meta = "",
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)]
@@ -175,7 +177,7 @@ pub struct TwoColourSpeed {
meta = "",
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)]
@@ -189,7 +191,7 @@ pub struct MultiZone {
#[options(short = "c", meta = "", help = "set the RGB value e.g, ff00ff")]
pub colour3: Colour,
#[options(short = "d", meta = "", help = "set the RGB value e.g, ff00ff")]
pub colour4: Colour,
pub colour4: Colour
}
#[derive(Debug, Clone, Default, Options)]
@@ -205,7 +207,7 @@ pub struct MultiColourSpeed {
#[options(short = "d", meta = "", help = "set the RGB value e.g, ff00ff")]
pub colour4: Colour,
#[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.
@@ -219,9 +221,9 @@ pub enum SetAuraBuiltin {
#[options(help = "pulse between one or two colours")]
Breathe(TwoColourSpeed), // 1
#[options(help = "strobe through all colours")]
Strobe(SingleSpeed), // 2
RainbowCycle(SingleSpeed), // 2
#[options(help = "rainbow cycling in one of four directions")]
Rainbow(SingleSpeedDirection), // 3
RainbowWave(SingleSpeedDirection), // 3
#[options(help = "rain pattern mimicking raindrops")]
Stars(TwoColourSpeed), // 4
#[options(help = "rain pattern of three preset colours")]
@@ -237,7 +239,7 @@ pub enum SetAuraBuiltin {
#[options(help = "set a vertical line zooming from left")]
Comet(SingleColour), // 11
#[options(help = "set a wide vertical line zooming from left")]
Flash(SingleColour), // 12
Flash(SingleColour) // 12
}
impl Default for SetAuraBuiltin {
@@ -312,14 +314,14 @@ impl From<&SetAuraBuiltin> for AuraEffect {
data.mode = AuraModeNum::Breathe;
data
}
SetAuraBuiltin::Strobe(x) => {
SetAuraBuiltin::RainbowCycle(x) => {
let mut data: AuraEffect = x.into();
data.mode = AuraModeNum::Strobe;
data.mode = AuraModeNum::RainbowCycle;
data
}
SetAuraBuiltin::Rainbow(x) => {
SetAuraBuiltin::RainbowWave(x) => {
let mut data: AuraEffect = x.into();
data.mode = AuraModeNum::Rainbow;
data.mode = AuraModeNum::RainbowWave;
data
}
SetAuraBuiltin::Stars(x) => {

View File

@@ -1,9 +1,10 @@
use gumdrop::Options;
use rog_platform::platform::ThrottlePolicy;
use rog_platform::platform::PlatformProfile;
use crate::anime_cli::AnimeCommand;
use crate::aura_cli::{LedBrightness, LedPowerCommand1, LedPowerCommand2, SetAuraBuiltin};
use crate::fan_curve_cli::FanCurveCommand;
use crate::scsi_cli::ScsiCommand;
use crate::slash_cli::SlashCommand;
#[derive(Default, Options)]
@@ -22,18 +23,20 @@ pub struct CliStart {
pub prev_kbd_bright: bool,
#[options(meta = "", help = "Set your battery charge limit <20-100>")]
pub chg_limit: Option<u8>,
#[options(help = "Toggle one-shot battery charge to 100%")]
pub one_shot_chg: bool,
#[options(command)]
pub command: Option<CliCommand>,
pub command: Option<CliCommand>
}
#[derive(Options)]
pub enum CliCommand {
#[options(help = "Set the keyboard lighting from built-in modes")]
LedMode(LedModeCommand),
Aura(LedModeCommand),
#[options(help = "Set the LED power states")]
LedPow1(LedPowerCommand1),
AuraPowerOld(LedPowerCommand1),
#[options(help = "Set the LED power states")]
LedPow2(LedPowerCommand2),
AuraPower(LedPowerCommand2),
#[options(help = "Set or select platform_profile")]
Profile(ProfileCommand),
#[options(help = "Set, select, or modify fan curves if supported")]
@@ -44,8 +47,13 @@ pub enum CliCommand {
Anime(AnimeCommand),
#[options(name = "slash", help = "Manage Slash Ledbar")]
Slash(SlashCommand),
#[options(help = "Change bios settings")]
Bios(BiosCommand),
#[options(name = "scsi", help = "Manage SCSI external drive")]
Scsi(ScsiCommand),
#[options(
help = "Change platform settings. This is a new interface exposed by the asus-armoury \
driver, some of the settings will be the same as the older platform interface"
)]
Armoury(ArmouryCommand)
}
#[derive(Debug, Clone, Options)]
@@ -63,7 +71,7 @@ pub struct ProfileCommand {
pub profile_get: bool,
#[options(meta = "", help = "set the active profile")]
pub profile_set: Option<ThrottlePolicy>,
pub profile_set: Option<PlatformProfile>
}
#[derive(Options)]
@@ -75,44 +83,22 @@ pub struct LedModeCommand {
#[options(help = "switch to previous aura mode")]
pub prev_mode: bool,
#[options(command)]
pub command: Option<SetAuraBuiltin>,
pub command: Option<SetAuraBuiltin>
}
#[derive(Options)]
pub struct GraphicsCommand {
#[options(help = "print help message")]
pub help: bool,
pub help: bool
}
#[derive(Options, Debug)]
pub struct BiosCommand {
pub struct ArmouryCommand {
#[options(help = "print help message")]
pub help: bool,
#[options(
meta = "",
short = "S",
no_long,
help = "set bios POST sound: asusctl -S <true/false>"
free,
help = "append each value name followed by the value to set. `-1` sets to default"
)]
pub post_sound_set: Option<bool>,
#[options(no_long, short = "s", help = "read bios POST sound")]
pub post_sound_get: bool,
#[options(
meta = "",
short = "D",
no_long,
help = "Switch GPU MUX mode: 0 = Discrete, 1 = Optimus, reboot required"
)]
pub gpu_mux_mode_set: Option<u8>,
#[options(no_long, short = "d", help = "get GPU mode")]
pub gpu_mux_mode_get: bool,
#[options(
meta = "",
short = "O",
no_long,
help = "Set device panel overdrive <true/false>"
)]
pub panel_overdrive_set: Option<bool>,
#[options(no_long, short = "o", help = "get panel overdrive")]
pub panel_overdrive_get: bool,
pub free: Vec<String>
}

View File

@@ -1,5 +1,5 @@
use gumdrop::Options;
use rog_platform::platform::ThrottlePolicy;
use rog_platform::platform::PlatformProfile;
use rog_profiles::fan_curve_set::CurveData;
use rog_profiles::FanCurvePU;
@@ -18,7 +18,7 @@ pub struct FanCurveCommand {
meta = "",
help = "profile to modify fan-curve for. Shows data if no options provided"
)]
pub mod_profile: Option<ThrottlePolicy>,
pub mod_profile: Option<PlatformProfile>,
#[options(
meta = "",
@@ -45,5 +45,5 @@ pub struct FanCurveCommand {
help = "data format = 30c:1%,49c:2%,59c:3%,69c:4%,79c:31%,89c:49%,99c:56%,109c:58%. \
`--mod-profile` required. If '%' is omitted the fan range is 0-255"
)]
pub data: Option<CurveData>,
pub data: Option<CurveData>
}

File diff suppressed because it is too large Load Diff

35
asusctl/src/scsi_cli.rs Normal file
View File

@@ -0,0 +1,35 @@
use gumdrop::Options;
use rog_scsi::{AuraMode, Colour, Direction, Speed};
#[derive(Options)]
pub struct ScsiCommand {
#[options(help = "print help message")]
pub help: bool,
#[options(help = "Enable the SCSI drive LEDs")]
pub enable: Option<bool>,
#[options(meta = "", help = "Set LED mode (so 'list' for all options)")]
pub mode: Option<AuraMode>,
#[options(
meta = "",
help = "Set LED mode speed <slowest, slow, med, fast, fastest> (does not apply to all)"
)]
pub speed: Option<Speed>,
#[options(
meta = "",
help = "Set LED mode direction <forward, reverse> (does not apply to all)"
)]
pub direction: Option<Direction>,
#[options(
meta = "",
help = "Set LED colours <hex>, specify up to 4 with repeated arg"
)]
pub colours: Vec<Colour>,
#[options(help = "list available animations")]
pub list: bool
}

View File

@@ -7,14 +7,29 @@ pub struct SlashCommand {
pub help: bool,
#[options(help = "Enable the Slash Ledbar")]
pub enable: bool,
#[options(help = "Ddisable the Slash Ledbar")]
#[options(help = "Disable the Slash Ledbar")]
pub disable: bool,
#[options(meta = "", help = "Set brightness value <0-255>")]
#[options(short = "l", meta = "", help = "Set brightness value <0-255>")]
pub brightness: Option<u8>,
#[options(meta = "", help = "Set interval value <0-255>")]
#[options(meta = "", help = "Set interval value <0-5>")]
pub interval: Option<u8>,
#[options(help = "Set SlashMode (so 'list' for all options)")]
pub slash_mode: Option<SlashMode>,
#[options(meta = "", help = "Set SlashMode (so 'list' for all options)")]
pub mode: Option<SlashMode>,
#[options(help = "list available animations")]
pub list: bool,
#[options(short = "B", meta = "", help = "Show the animation on boot")]
pub show_on_boot: Option<bool>,
#[options(short = "S", meta = "", help = "Show the animation on shutdown")]
pub show_on_shutdown: Option<bool>,
#[options(short = "s", meta = "", help = "Show the animation on sleep")]
pub show_on_sleep: Option<bool>,
#[options(short = "b", meta = "", help = "Show the animation on battery")]
pub show_on_battery: Option<bool>,
#[options(
short = "w",
meta = "",
help = "Show the low-battery warning animation"
)]
pub show_battery_warning: Option<bool>
}

View File

@@ -12,13 +12,16 @@ edition.workspace = true
name = "asusd-user"
path = "src/daemon.rs"
[features]
default = []
local_data = []
[dependencies]
dirs.workspace = true
smol.workspace = true
# serialisation
serde.workspace = true
serde_derive.workspace = true
ron.workspace = true
rog_anime = { path = "../rog-anime" }
@@ -29,9 +32,3 @@ config-traits = { path = "../config-traits" }
zbus.workspace = true
env_logger.workspace = true
[dev-dependencies]
cargo-husky.workspace = true
[package.metadata.cargo-machete]
ignored = ["serde"]

View File

@@ -6,7 +6,7 @@ use rog_anime::{ActionLoader, AnimTime, AnimeType, Fade, Sequences as AnimeSeque
use rog_aura::effects::{AdvancedEffects as AuraSequences, Breathe, DoomFlicker, Effect, Static};
use rog_aura::keyboard::LedCode;
use rog_aura::{Colour, Speed};
use serde_derive::{Deserialize, Serialize};
use serde::{Deserialize, Serialize};
use crate::error::Error;
@@ -21,7 +21,7 @@ fn root_conf_dir() -> PathBuf {
#[derive(Debug, Deserialize, Serialize)]
pub struct ConfigAnime {
pub name: String,
pub anime: Vec<ActionLoader>,
pub anime: Vec<ActionLoader>
}
impl ConfigAnime {
@@ -52,8 +52,8 @@ impl Default for ConfigAnime {
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
None,
Duration::from_secs(2),
)),
Duration::from_secs(2)
))
},
ActionLoader::AsusAnimation {
file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(),
@@ -61,8 +61,8 @@ impl Default for ConfigAnime {
time: AnimTime::Fade(Fade::new(
Duration::from_secs(6),
None,
Duration::from_secs(3),
)),
Duration::from_secs(3)
))
},
ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
@@ -73,8 +73,8 @@ impl Default for ConfigAnime {
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(2)),
Duration::from_secs(2),
)),
Duration::from_secs(2)
))
},
ActionLoader::Image {
file: "/usr/share/asusd/anime/custom/rust.png".into(),
@@ -84,9 +84,9 @@ impl Default for ConfigAnime {
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
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::ImageAnimation {
@@ -95,9 +95,9 @@ impl Default for ConfigAnime {
angle: 0.0,
translation: Vec2::new(3.0, 2.0),
brightness: 0.5,
time: AnimTime::Count(2),
time: AnimTime::Count(2)
},
],
]
}
}
}
@@ -121,7 +121,7 @@ impl StdConfigLoad for ConfigAnime {}
#[derive(Debug, Deserialize, Serialize)]
pub struct ConfigAura {
pub name: String,
pub aura: AuraSequences,
pub aura: AuraSequences
}
impl ConfigAura {
@@ -139,14 +139,14 @@ impl Default for ConfigAura {
Colour {
r: 255,
g: 0,
b: 20,
b: 20
},
Colour {
r: 20,
g: 255,
b: 0,
b: 0
},
Speed::Low,
Speed::Low
));
seq.push(key.clone());
@@ -161,7 +161,7 @@ impl Default for ConfigAura {
LedCode::F,
Colour { r: 255, g: 0, b: 0 },
Colour { r: 255, g: 0, b: 0 },
Speed::High,
Speed::High
));
seq.push(key);
@@ -176,13 +176,13 @@ impl Default for ConfigAura {
LedCode::N9,
Colour { r: 0, g: 0, b: 255 },
80,
40,
40
));
seq.push(key);
Self {
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
pub active_anime: Option<String>,
/// 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 {
fn new() -> Self {
Self {
active_anime: Some("anime-default".to_owned()),
active_aura: Some("aura-default".to_owned()),
active_aura: Some("aura-default".to_owned())
}
}

View File

@@ -9,7 +9,7 @@ use rog_anime::error::AnimeError;
use rog_anime::{ActionData, ActionLoader, AnimTime, Fade, Sequences, Vec2};
use rog_dbus::zbus_anime::AnimeProxyBlocking;
use ron::ser::PrettyConfig;
use serde_derive::{Deserialize, Serialize};
use serde::{Deserialize, Serialize};
use zbus::interface;
use zbus::zvariant::{ObjectPath, Type};
@@ -25,7 +25,7 @@ pub struct Timer {
/// Used only for `TimeType::Timer`, milliseonds to fade the image in for
fade_in: u64,
/// Used only for `TimeType::Timer`, milliseonds to fade the image out for
fade_out: u64,
fade_out: u64
}
impl From<Timer> for AnimTime {
@@ -46,7 +46,7 @@ impl From<Timer> for AnimTime {
}
}
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 {
Timer,
Count,
Infinite,
Infinite
}
/// The inner object exists to allow the zbus proxy to share it with a runner
@@ -63,25 +63,25 @@ pub enum TimeType {
pub struct CtrlAnimeInner<'a> {
sequences: Sequences,
client: AnimeProxyBlocking<'a>,
do_early_return: Arc<AtomicBool>,
do_early_return: Arc<AtomicBool>
}
impl<'a> CtrlAnimeInner<'static> {
impl CtrlAnimeInner<'static> {
pub fn new(
sequences: Sequences,
client: AnimeProxyBlocking<'static>,
do_early_return: Arc<AtomicBool>,
do_early_return: Arc<AtomicBool>
) -> Result<Self, Error> {
Ok(Self {
sequences,
client,
do_early_return,
do_early_return
})
}
/// To be called on each main loop iteration to pump out commands to the
/// anime
pub fn run(&'a self) -> Result<(), Error> {
pub fn run(&self) -> Result<(), Error> {
if self.do_early_return.load(Ordering::SeqCst) {
return Ok(());
}
@@ -130,7 +130,7 @@ pub struct CtrlAnime<'a> {
client: AnimeProxyBlocking<'a>,
inner: Arc<Mutex<CtrlAnimeInner<'a>>>,
/// Must be the same Atomic as in CtrlAnimeInner
inner_early_return: Arc<AtomicBool>,
inner_early_return: Arc<AtomicBool>
}
impl CtrlAnime<'static> {
@@ -138,23 +138,20 @@ impl CtrlAnime<'static> {
config: Arc<Mutex<ConfigAnime>>,
inner: Arc<Mutex<CtrlAnimeInner<'static>>>,
client: AnimeProxyBlocking<'static>,
inner_early_return: Arc<AtomicBool>,
inner_early_return: Arc<AtomicBool>
) -> Result<Self, Error> {
Ok(CtrlAnime {
config,
client,
inner,
inner_early_return,
inner_early_return
})
}
pub async fn add_to_server(self, server: &mut zbus::Connection) {
server
.object_server()
.at(
&ObjectPath::from_str_unchecked("/org/asuslinux/Anime"),
self,
)
.at(&ObjectPath::from_str_unchecked("/xyz/ljones/Anime"), self)
.await
.map_err(|err| {
println!("CtrlAnime: add_to_server {}", err);
@@ -170,14 +167,14 @@ impl CtrlAnime<'static> {
// - Do actions
// - Write config if required
// - Unset inner_early_return
#[interface(name = "org.asuslinux.Daemon")]
#[interface(name = "xyz.ljones.Asusd")]
impl CtrlAnime<'static> {
pub fn insert_asus_gif(
&mut self,
index: u32,
file: &str,
time: Timer,
brightness: f32,
brightness: f32
) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() {
let time: AnimTime = time.into();
@@ -185,7 +182,7 @@ impl CtrlAnime<'static> {
let action = ActionLoader::AsusAnimation {
file: file.into(),
brightness,
time,
time
};
// Must make the inner run loop return early
@@ -219,7 +216,7 @@ impl CtrlAnime<'static> {
angle: f32,
xy: (f32, f32),
time: Timer,
brightness: f32,
brightness: f32
) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() {
let time: AnimTime = time.into();
@@ -231,7 +228,7 @@ impl CtrlAnime<'static> {
angle,
translation,
brightness,
time,
time
};
// Must make the inner run loop return early
@@ -264,7 +261,7 @@ impl CtrlAnime<'static> {
angle: f32,
xy: (f32, f32),
time: Timer,
brightness: f32,
brightness: f32
) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() {
let file = Path::new(&file);
@@ -275,7 +272,7 @@ impl CtrlAnime<'static> {
angle,
translation: Vec2::new(xy.0, xy.1),
brightness,
time,
time
};
// Must make the inner run loop return early

View File

@@ -11,8 +11,7 @@ use rog_aura::aura_detection::LedSupportData;
use rog_aura::keyboard::KeyLayout;
use rog_dbus::zbus_anime::AnimeProxyBlocking;
use rog_dbus::zbus_aura::AuraProxyBlocking;
use rog_dbus::zbus_platform::PlatformProxyBlocking;
use rog_dbus::DBUS_NAME;
use rog_dbus::{list_iface_blocking, DBUS_NAME};
use smol::Executor;
use zbus::Connection;
@@ -36,20 +35,16 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("rog-platform v{}", rog_platform::VERSION);
let conn = zbus::blocking::Connection::system().unwrap();
let platform_proxy = PlatformProxyBlocking::new(&conn).unwrap();
let supported = platform_proxy
.supported_interfaces()
.unwrap_or_default()
.contains(&"Anime".to_string());
let supported = list_iface_blocking()?;
let config = ConfigBase::new().load();
let executor = Executor::new();
let early_return = Arc::new(AtomicBool::new(false));
// Set up the anime data and run loop/thread
if supported {
if supported.contains(&"xyz.ljones.Anime".to_string()) {
if let Some(cfg) = config.active_anime {
let anime_type = get_anime_type()?;
let anime_type = get_anime_type();
let anime_config = ConfigAnime::new().set_name(cfg).load();
let anime = anime_config.create(anime_type)?;
let anime_config = Arc::new(Mutex::new(anime_config));
@@ -66,16 +61,16 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
CtrlAnimeInner::new(
anime,
anime_proxy_blocking.clone(),
early_return.clone(),
early_return.clone()
)
.unwrap(),
.unwrap()
));
// Need new client object for dbus control part
let anime_control = CtrlAnime::new(
anime_config,
inner.clone(),
anime_proxy_blocking,
early_return,
early_return
)
.unwrap();
anime_control.add_to_server(&mut connection).await;

View File

@@ -8,7 +8,7 @@ pub enum Error {
ConfigLoadFail,
ConfigLockFail,
XdgVars,
Anime(AnimeError),
Anime(AnimeError)
}
impl fmt::Display for Error {
@@ -19,7 +19,7 @@ impl fmt::Display for Error {
Error::ConfigLoadFail => write!(f, "Failed to load user config"),
Error::ConfigLockFail => write!(f, "Failed to lock user config"),
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

@@ -1,8 +1,8 @@
//! # `DBus` interface proxy for: `org.asuslinux.Daemon`
//! # `DBus` interface proxy for: `xyz.ljones.Asusd`
//!
//! This code was generated by `zbus-xmlgen` `1.0.0` from `DBus` introspection
//! data. Source: `Interface '/org/asuslinux/Anime' from service
//! 'org.asuslinux.Daemon' on session bus`.
//! data. Source: `Interface '/xyz/ljones/Anime' from service
//! 'xyz.ljones.Asusd' on session bus`.
//!
//! You may prefer to adapt it, instead of using it verbatim.
//!
@@ -23,10 +23,7 @@
use zbus::proxy;
#[proxy(
interface = "org.asuslinux.Daemon",
default_path = "/org/asuslinux/Anime"
)]
#[proxy(interface = "xyz.ljones.Asusd", default_path = "/xyz/ljones/Anime")]
trait Daemon {
/// InsertAsusGif method
fn insert_asus_gif(
@@ -35,7 +32,7 @@ trait Daemon {
file: &str,
time: u32,
count: u32,
brightness: f64,
brightness: f64
) -> zbus::Result<String>;
/// InsertImage method
@@ -46,7 +43,7 @@ trait Daemon {
scale: f64,
angle: f64,
xy: &(f64, f64),
brightness: f64,
brightness: f64
) -> zbus::Result<String>;
/// InsertImageGif method
@@ -59,7 +56,7 @@ trait Daemon {
xy: &(f64, f64),
time: u32,
count: u32,
brightness: f64,
brightness: f64
) -> zbus::Result<String>;
/// InsertPause method

View File

@@ -18,6 +18,7 @@ config-traits = { path = "../config-traits" }
rog_anime = { path = "../rog-anime", features = ["dbus"] }
rog_slash = { path = "../rog-slash", features = ["dbus"] }
rog_aura = { path = "../rog-aura", features = ["dbus"] }
rog_scsi = { path = "../rog-scsi", features = ["dbus"] }
rog_platform = { path = "../rog-platform" }
rog_profiles = { path = "../rog-profiles" }
dmi_id = { path = "../dmi-id" }
@@ -33,17 +34,14 @@ tokio.workspace = true
log.workspace = true
env_logger.workspace = true
futures-util.workspace = true
zbus.workspace = true
logind-zbus.workspace = true
# serialisation
serde.workspace = true
serde_derive.workspace = true
concat-idents.workspace = true
[dev-dependencies]
cargo-husky.workspace = true
[package.metadata.cargo-machete]
ignored = ["serde"]

425
asusd/src/asus_armoury.rs Normal file
View File

@@ -0,0 +1,425 @@
use std::sync::Arc;
use config_traits::StdConfig;
use futures_util::lock::Mutex;
use log::{debug, error, info};
use rog_platform::asus_armoury::{AttrValue, Attribute, FirmwareAttribute, FirmwareAttributes};
use rog_platform::platform::{PlatformProfile, RogPlatform};
use rog_platform::power::AsusPower;
use serde::{Deserialize, Serialize};
use zbus::object_server::SignalEmitter;
use zbus::zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Type, Value};
use zbus::{fdo, interface, Connection};
use crate::config::Config;
use crate::error::RogError;
use crate::{Reloadable, ASUS_ZBUS_PATH};
const MOD_NAME: &str = "asus_armoury";
#[derive(Debug, Default, Clone, Deserialize, Serialize, Type, Value, OwnedValue)]
pub struct PossibleValues {
strings: Vec<String>,
nums: Vec<i32>
}
fn dbus_path_for_attr(attr_name: &str) -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{attr_name}")).into()
}
#[derive(Clone)]
pub struct AsusArmouryAttribute {
attr: Attribute,
config: Arc<Mutex<Config>>,
/// platform control required here for access to PPD or Throttle profile
platform: RogPlatform,
power: AsusPower
}
impl AsusArmouryAttribute {
pub fn new(
attr: Attribute,
platform: RogPlatform,
power: AsusPower,
config: Arc<Mutex<Config>>
) -> Self {
Self {
attr,
config,
platform,
power
}
}
pub async fn move_to_zbus(self, connection: &Connection) -> Result<(), RogError> {
let path = dbus_path_for_attr(self.attr.name());
connection
.object_server()
.at(path.clone(), self)
.await
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
.ok();
Ok(())
}
async fn watch_and_notify(
&mut self,
signal_ctxt: SignalEmitter<'static>
) -> Result<(), RogError> {
use futures_util::StreamExt;
let name = self.name();
macro_rules! watch_value_notify {
($attr_str:expr, $fn_prop_changed:ident) => {
match self.attr.get_watcher($attr_str) {
Ok(watch) => {
let name = <&str>::from(name);
let ctrl = self.clone();
let sig = signal_ctxt.clone();
tokio::spawn(async move {
let mut buffer = [0; 32];
watch
.into_event_stream(&mut buffer)
.unwrap()
.for_each(|_| async {
debug!("{} changed", name);
ctrl.$fn_prop_changed(&sig).await.ok();
})
.await;
});
}
Err(e) => info!(
"inotify watch failed: {}. You can ignore this if your device does not \
support the feature",
e
)
}
};
}
// "current_value", "default_value", "min_value", "max_value"
watch_value_notify!("current_value", current_value_changed);
watch_value_notify!("default_value", default_value_changed);
watch_value_notify!("min_value", min_value_changed);
watch_value_notify!("max_value", max_value_changed);
Ok(())
}
}
impl crate::Reloadable for AsusArmouryAttribute {
async fn reload(&mut self) -> Result<(), RogError> {
info!("Reloading {}", self.attr.name());
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self
.power
.get_online()
.map_err(|e| {
error!("Could not get power status: {e:?}");
e
})
.unwrap_or_default();
let config = if power_plugged == 1 {
&self.config.lock().await.ac_profile_tunings
} else {
&self.config.lock().await.dc_profile_tunings
};
if let Some(tuning) = config.get(&profile) {
if tuning.enabled {
if let Some(tune) = tuning.group.get(&self.name()) {
self.attr
.set_current_value(&AttrValue::Integer(*tune))
.map_err(|e| {
error!("Could not set {} value: {e:?}", self.attr.name());
self.attr.base_path_exists();
e
})?;
info!("Set {} to {:?}", self.attr.name(), tune);
}
}
}
Ok(())
}
}
/// If return is `-1` on a property then there is avilable value for that
/// property
#[interface(name = "xyz.ljones.AsusArmoury")]
impl AsusArmouryAttribute {
#[zbus(property)]
fn name(&self) -> FirmwareAttribute {
self.attr.name().into()
}
#[zbus(property)]
async fn available_attrs(&self) -> Vec<String> {
let mut attrs = Vec::new();
if !matches!(self.attr.default_value(), AttrValue::None) {
attrs.push("default_value".to_string());
}
if !matches!(self.attr.min_value(), AttrValue::None) {
attrs.push("min_value".to_string());
}
if !matches!(self.attr.max_value(), AttrValue::None) {
attrs.push("max_value".to_string());
}
if !matches!(self.attr.scalar_increment(), AttrValue::None) {
attrs.push("scalar_increment".to_string());
}
if !matches!(self.attr.possible_values(), AttrValue::None) {
attrs.push("possible_values".to_string());
}
// TODO: Don't unwrap, use error
if let Ok(value) = self.attr.current_value().map_err(|e| {
error!("Failed to read: {e:?}");
e
}) {
if !matches!(value, AttrValue::None) {
attrs.push("current_value".to_string());
}
}
attrs
}
/// If return is `-1` then there is no default value
#[zbus(property)]
async fn default_value(&self) -> i32 {
match self.attr.default_value() {
AttrValue::Integer(i) => *i,
_ => -1
}
}
async fn restore_default(&self) -> fdo::Result<()> {
self.attr.restore_default()?;
if self.name().is_ppt() {
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self
.power
.get_online()
.map_err(|e| {
error!("Could not get power status: {e:?}");
e
})
.unwrap_or_default();
let mut config = self.config.lock().await;
let tuning = config.select_tunings(power_plugged == 1, profile);
if let Some(tune) = tuning.group.get_mut(&self.name()) {
if let AttrValue::Integer(i) = self.attr.default_value() {
*tune = *i;
}
}
if tuning.enabled {
self.attr
.set_current_value(self.attr.default_value())
.map_err(|e| {
error!("Could not set value: {e:?}");
e
})?;
}
config.write();
}
Ok(())
}
#[zbus(property)]
async fn min_value(&self) -> i32 {
match self.attr.min_value() {
AttrValue::Integer(i) => *i,
_ => -1
}
}
#[zbus(property)]
async fn max_value(&self) -> i32 {
match self.attr.max_value() {
AttrValue::Integer(i) => *i,
_ => -1
}
}
#[zbus(property)]
async fn scalar_increment(&self) -> i32 {
match self.attr.scalar_increment() {
AttrValue::Integer(i) => *i,
_ => -1
}
}
#[zbus(property)]
async fn possible_values(&self) -> Vec<i32> {
match self.attr.possible_values() {
AttrValue::EnumInt(i) => i.clone(),
_ => Vec::default()
}
}
#[zbus(property)]
async fn current_value(&self) -> fdo::Result<i32> {
if self.name().is_ppt() {
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self
.power
.get_online()
.map_err(|e| {
error!("Could not get power status: {e:?}");
e
})
.unwrap_or_default();
let mut config = self.config.lock().await;
let tuning = config.select_tunings(power_plugged == 1, profile);
if let Some(tune) = tuning.group.get(&self.name()) {
return Ok(*tune);
} else if let AttrValue::Integer(i) = self.attr.default_value() {
return Ok(*i);
}
return Err(fdo::Error::Failed(
"Could not read current value".to_string()
));
}
if let Ok(AttrValue::Integer(i)) = self.attr.current_value() {
return Ok(i);
}
Err(fdo::Error::Failed(
"Could not read current value".to_string()
))
}
#[zbus(property)]
async fn set_current_value(&mut self, value: i32) -> fdo::Result<()> {
if self.name().is_ppt() {
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self
.power
.get_online()
.map_err(|e| {
error!("Could not get power status: {e:?}");
e
})
.unwrap_or_default();
let mut config = self.config.lock().await;
let tuning = config.select_tunings(power_plugged == 1, profile);
if let Some(tune) = tuning.group.get_mut(&self.name()) {
*tune = value;
} else {
tuning.group.insert(self.name(), value);
debug!("Store tuning config for {} = {:?}", self.attr.name(), value);
}
if tuning.enabled {
self.attr
.set_current_value(&AttrValue::Integer(value))
.map_err(|e| {
error!("Could not set value: {e:?}");
e
})?;
}
} else {
self.attr
.set_current_value(&AttrValue::Integer(value))
.map_err(|e| {
error!("Could not set value: {e:?}");
e
})?;
let has_attr = self
.config
.lock()
.await
.armoury_settings
.contains_key(&self.name());
if has_attr {
if let Some(setting) = self
.config
.lock()
.await
.armoury_settings
.get_mut(&self.name())
{
*setting = value
}
} else {
debug!("Adding config for {}", self.attr.name());
self.config
.lock()
.await
.armoury_settings
.insert(self.name(), value);
debug!("Set config for {} = {:?}", self.attr.name(), value);
}
}
self.config.lock().await.write();
Ok(())
}
}
pub async fn start_attributes_zbus(
conn: &Connection,
platform: RogPlatform,
power: AsusPower,
attributes: FirmwareAttributes,
config: Arc<Mutex<Config>>
) -> Result<(), RogError> {
for attr in attributes.attributes() {
let mut attr = AsusArmouryAttribute::new(
attr.clone(),
platform.clone(),
power.clone(),
config.clone()
);
attr.reload().await?;
let path = dbus_path_for_attr(attr.attr.name());
let sig = zbus::object_server::SignalEmitter::new(conn, path)?;
attr.watch_and_notify(sig).await?;
attr.move_to_zbus(conn).await?;
}
Ok(())
}
pub async fn set_config_or_default(
attrs: &FirmwareAttributes,
config: &mut Config,
power_plugged: bool,
profile: PlatformProfile
) {
for attr in attrs.attributes().iter() {
let name: FirmwareAttribute = attr.name().into();
if name.is_ppt() {
let tuning = config.select_tunings(power_plugged, profile);
if !tuning.enabled {
debug!("Tuning group is not enabled, skipping");
return;
}
if let Some(tune) = tuning.group.get(&name) {
attr.set_current_value(&AttrValue::Integer(*tune))
.map_err(|e| {
error!("Failed to set {}: {e}", <&str>::from(name));
})
.ok();
} else {
let default = attr.default_value();
attr.set_current_value(default)
.map_err(|e| {
error!("Failed to set {}: {e}", <&str>::from(name));
})
.ok();
if let AttrValue::Integer(i) = default {
tuning.group.insert(name, *i);
info!(
"Set default tuning config for {} = {:?}",
<&str>::from(name),
i
);
// config.write();
}
}
}
}
}

View File

@@ -0,0 +1,182 @@
use std::time::Duration;
use config_traits::{StdConfig, StdConfigLoad};
use rog_anime::error::AnimeError;
use rog_anime::usb::Brightness;
use rog_anime::{
ActionData, ActionLoader, AnimTime, Animations, AnimeType, DeviceState, Fade, Vec2
};
use serde::{Deserialize, Serialize};
const CONFIG_FILE: &str = "anime.ron";
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
pub struct AniMeConfigCached {
pub system: Vec<ActionData>,
pub boot: Vec<ActionData>,
pub wake: Vec<ActionData>,
pub shutdown: Vec<ActionData>
}
impl AniMeConfigCached {
pub fn init_from_config(
&mut self,
config: &AniMeConfig,
anime_type: AnimeType
) -> Result<(), AnimeError> {
let mut sys = Vec::with_capacity(config.system.len());
for ani in &config.system {
sys.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.system = sys;
let mut boot = Vec::with_capacity(config.boot.len());
for ani in &config.boot {
boot.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.boot = boot;
let mut wake = Vec::with_capacity(config.wake.len());
for ani in &config.wake {
wake.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.wake = wake;
let mut shutdown = Vec::with_capacity(config.shutdown.len());
for ani in &config.shutdown {
shutdown.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.shutdown = shutdown;
Ok(())
}
}
/// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct AniMeConfig {
#[serde(skip)]
pub anime_type: AnimeType,
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>,
// pub brightness: f32,
pub display_enabled: bool,
pub display_brightness: Brightness,
pub builtin_anims_enabled: bool,
pub off_when_unplugged: bool,
pub off_when_suspended: bool,
pub off_when_lid_closed: bool,
pub brightness_on_battery: Brightness,
pub builtin_anims: Animations
}
impl Default for AniMeConfig {
fn default() -> Self {
AniMeConfig {
anime_type: AnimeType::GA402,
system: Vec::new(),
boot: Vec::new(),
wake: Vec::new(),
shutdown: Vec::new(),
// brightness: 1.0,
display_enabled: true,
display_brightness: Brightness::Med,
builtin_anims_enabled: true,
off_when_unplugged: true,
off_when_suspended: true,
off_when_lid_closed: true,
brightness_on_battery: Brightness::Low,
builtin_anims: Animations::default()
}
}
}
impl StdConfig for AniMeConfig {
fn new() -> Self {
Self::create_default()
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
fn config_dir() -> std::path::PathBuf {
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
}
}
impl StdConfigLoad for AniMeConfig {}
impl From<&AniMeConfig> for DeviceState {
fn from(config: &AniMeConfig) -> Self {
DeviceState {
display_enabled: config.display_enabled,
display_brightness: config.display_brightness,
builtin_anims_enabled: config.builtin_anims_enabled,
builtin_anims: config.builtin_anims,
off_when_unplugged: config.off_when_unplugged,
off_when_suspended: config.off_when_suspended,
off_when_lid_closed: config.off_when_lid_closed,
brightness_on_battery: config.brightness_on_battery
}
}
}
impl AniMeConfig {
// fn clamp_config_brightness(mut config: &mut AnimeConfig) {
// if config.brightness < 0.0 || config.brightness > 1.0 {
// warn!(
// "Clamped brightness to [0.0 ; 1.0], was {}",
// config.brightness
// );
// config.brightness = f32::max(0.0, f32::min(1.0, config.brightness));
// }
// }
fn create_default() -> Self {
// create a default config here
AniMeConfig {
system: vec![],
boot: vec![
ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
brightness: 1.0,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(2)),
Duration::from_secs(2)
))
},
],
wake: vec![
ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
brightness: 1.0,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(2)),
Duration::from_secs(2)
))
},
],
shutdown: vec![
ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
scale: 0.9,
angle: 0.0,
translation: Vec2::new(3.0, 2.0),
brightness: 1.0,
time: AnimTime::Infinite
},
],
..Default::default()
}
}
}

248
asusd/src/aura_anime/mod.rs Normal file
View File

@@ -0,0 +1,248 @@
pub mod config;
/// Implements `CtrlTask`, Reloadable, `ZbusRun`
pub mod trait_impls;
use std::convert::TryFrom;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::sleep;
use config_traits::StdConfig;
use futures_util::lock::Mutex;
use log::{debug, error, info, warn};
use rog_anime::usb::{
pkt_flush, pkt_set_brightness, pkt_set_enable_display, pkt_set_enable_powersave_anim,
pkts_for_init, Brightness
};
use rog_anime::{ActionData, AnimeDataBuffer, AnimePacketType};
use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw;
use self::config::{AniMeConfig, AniMeConfigCached};
use crate::error::RogError;
#[derive(Debug, Clone)]
pub struct AniMe {
hid: Option<Arc<Mutex<HidRaw>>>,
usb: Option<Arc<Mutex<USBRaw>>>,
config: Arc<Mutex<AniMeConfig>>,
cache: AniMeConfigCached,
// set to force thread to exit
thread_exit: Arc<AtomicBool>,
// Set to false when the thread exits
thread_running: Arc<AtomicBool>
}
impl AniMe {
pub fn new(
hid: Option<Arc<Mutex<HidRaw>>>,
usb: Option<Arc<Mutex<USBRaw>>>,
config: Arc<Mutex<AniMeConfig>>
) -> Self {
Self {
hid,
usb,
config,
cache: AniMeConfigCached::default(),
thread_exit: Arc::new(AtomicBool::new(false)),
thread_running: Arc::new(AtomicBool::new(false))
}
}
/// Will fail if something is already holding the config lock
async fn do_init_cache(&mut self) {
if let Some(mut config) = self.config.try_lock() {
if let Err(e) = self.cache.init_from_config(&config, config.anime_type) {
error!(
"Trying to cache the Anime Config failed, will reset to default config: {e:?}"
);
config.rename_file_old();
*config = AniMeConfig::new();
config.write();
} else {
debug!("Initialised AniMe cache");
}
} else {
error!("AniMe Matrix could not init cache")
}
}
/// Initialise the device if required.
pub async fn do_initialization(&mut self) -> Result<(), RogError> {
self.do_init_cache().await;
let pkts = pkts_for_init();
self.write_bytes(&pkts[0]).await?;
self.write_bytes(&pkts[1]).await?;
debug!("Succesfully initialised AniMe matrix display");
Ok(())
}
pub async fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
if let Some(hid) = &self.hid {
hid.lock().await.write_bytes(message)?;
} else if let Some(usb) = &self.usb {
usb.lock().await.write_bytes(message)?;
}
Ok(())
}
/// Write only a data packet. This will modify the leds brightness using the
/// global brightness set in config.
async fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) -> Result<(), RogError> {
for led in buffer.data_mut().iter_mut() {
let mut bright = *led as f32;
if bright > 254.0 {
bright = 254.0;
}
*led = bright as u8;
}
let data = AnimePacketType::try_from(buffer)?;
for row in &data {
self.write_bytes(row).await?;
}
self.write_bytes(&pkt_flush()).await
}
pub async fn set_builtins_enabled(
&self,
enabled: bool,
bright: Brightness
) -> Result<(), RogError> {
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
.await?;
self.write_bytes(&pkt_set_enable_display(enabled)).await?;
self.write_bytes(&pkt_set_brightness(bright)).await?;
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
.await
}
/// Start an action thread. This is classed as a singleton and there should
/// be only one running - so the thread uses atomics to signal run/exit.
///
/// Because this also writes to the usb device, other write tries (display
/// only) *must* get the mutex lock and set the `thread_exit` atomic.
async fn run_thread(&self, actions: Vec<ActionData>, mut once: bool) {
if actions.is_empty() {
warn!("AniMe system actions was empty");
return;
}
self.write_bytes(&pkt_set_enable_powersave_anim(false))
.await
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
let thread_exit = self.thread_exit.clone();
let thread_running = self.thread_running.clone();
let anime_type = self.config.lock().await.anime_type;
let inner = self.clone();
// Loop rules:
// - Lock the mutex **only when required**. That is, the lock must be held for
// the shortest duration possible.
// - An AtomicBool used for thread exit should be checked in every loop,
// including nested
// The only reason for this outer thread is to prevent blocking while waiting
// for the next spawned thread to exit
// TODO: turn this in to async task (maybe? COuld still risk blocking main
// thread)
tokio::spawn(async move {
info!("AniMe new system thread started");
// First two loops are to ensure we *do* aquire a lock on the mutex
// The reason the loop is required is because the USB writes can block
// for up to 10ms. We can't fail to get the atomics.
while thread_running.load(Ordering::SeqCst) {
// Make any running loop exit first
thread_exit.store(true, Ordering::SeqCst);
}
info!("AniMe no previous system thread running (now)");
thread_exit.store(false, Ordering::SeqCst);
thread_running.store(true, Ordering::SeqCst);
'main: loop {
for action in &actions {
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
match action {
ActionData::Animation(frames) => {
// TODO: sort all this out
rog_anime::run_animation(frames, &|frame| {
if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: animation sub-loop was asked to exit");
return Ok(true); // Do safe exit
}
let inner = inner.clone();
tokio::task::spawn(async move {
inner
.write_data_buffer(frame)
.await
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
});
Ok(false) // Don't exit yet
});
if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: sub-loop exited and main loop exiting now");
break 'main;
}
}
ActionData::Image(image) => {
once = false;
inner
.write_data_buffer(image.as_ref().clone())
.await
.map_err(|e| error!("{}", e))
.ok();
}
ActionData::Pause(duration) => sleep(*duration),
ActionData::AudioEq
| ActionData::SystemInfo
| ActionData::TimeDate
| ActionData::Matrix => {}
}
}
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
if once || actions.is_empty() {
break 'main;
}
}
// Clear the display on exit
if let Ok(data) =
AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()])
.map_err(|e| error!("{}", e))
{
inner
.write_data_buffer(data)
.await
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
}
// A write can block for many milliseconds so lets not hold the config lock for
// the same period
let enabled = inner.config.lock().await.builtin_anims_enabled;
inner
.write_bytes(&pkt_set_enable_powersave_anim(enabled))
.await
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
// Loop ended, set the atmonics
thread_running.store(false, Ordering::SeqCst);
info!("AniMe system thread exited");
})
.await
.map(|err| info!("AniMe system thread: {:?}", err))
.ok();
}
}

View File

@@ -1,23 +1,22 @@
use std::sync::atomic::Ordering;
use std::sync::Arc;
use config_traits::StdConfig;
use log::warn;
use log::{debug, error, warn};
use logind_zbus::manager::ManagerProxy;
use rog_anime::usb::{
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 zbus::export::futures_util::lock::Mutex;
use zbus::{interface, CacheProperties, Connection, SignalContext};
use zbus::object_server::SignalEmitter;
use zbus::proxy::CacheProperties;
use zbus::zvariant::OwnedObjectPath;
use zbus::{interface, Connection};
use super::config::AnimeConfig;
use super::CtrlAnime;
use super::config::AniMeConfig;
use super::AniMe;
use crate::error::RogError;
pub const ANIME_ZBUS_NAME: &str = "Anime";
pub const ANIME_ZBUS_PATH: &str = "/org/asuslinux";
use crate::Reloadable;
async fn get_logind_manager<'a>() -> ManagerProxy<'a> {
let connection = Connection::system()
@@ -32,104 +31,117 @@ async fn get_logind_manager<'a>() -> ManagerProxy<'a> {
}
#[derive(Clone)]
pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>);
pub struct AniMeZbus(AniMe);
/// The struct with the main dbus methods requires this trait
impl crate::ZbusRun for CtrlAnimeZbus {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, ANIME_ZBUS_PATH, server).await;
impl AniMeZbus {
pub fn new(anime: AniMe) -> Self {
Self(anime)
}
pub async fn start_tasks(
mut self,
connection: &Connection,
path: OwnedObjectPath
) -> Result<(), RogError> {
// let task = zbus.clone();
self.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
connection
.object_server()
.at(path.clone(), self)
.await
.map_err(|e| {
error!("Couldn't add server at path: {path}, {e:?}");
e
})?;
debug!("start_tasks was successful");
Ok(())
}
}
// None of these calls can be guarnateed to succeed unless we loop until okay
// If the try_lock *does* succeed then any other thread trying to lock will not
// grab it until we finish.
#[interface(name = "org.asuslinux.Anime")]
impl CtrlAnimeZbus {
#[interface(name = "xyz.ljones.Anime")]
impl AniMeZbus {
/// Writes a data stream of length. Will force system thread to exit until
/// it is restarted
async fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> {
self.0
.lock()
.await
.thread_exit
.store(true, Ordering::SeqCst);
self.0
.lock()
.await
.write_data_buffer(input)
.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
err
})?;
let bright = self.0.config.lock().await.display_brightness;
self.0.set_builtins_enabled(false, bright).await?;
self.0.thread_exit.store(true, Ordering::SeqCst);
self.0.write_data_buffer(input).await.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
err
})?;
Ok(())
}
/// Set base brightness level
#[zbus(property)]
async fn brightness(&self) -> Brightness {
self.0.lock().await.config.display_brightness
if let Some(config) = self.0.config.try_lock() {
return config.display_brightness;
}
Brightness::Off
}
/// Set base brightness level
#[zbus(property)]
async fn set_brightness(&self, brightness: Brightness) {
self.0
.lock()
.await
.node
.write_bytes(&pkt_set_brightness(brightness))
.await
.map_err(|err| {
warn!("ctrl_anime::set_brightness {}", err);
})
.ok();
self.0
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(brightness != Brightness::Off))
.await
.map_err(|err| {
warn!("ctrl_anime::set_brightness {}", err);
})
.ok();
self.0.lock().await.config.display_enabled = brightness != Brightness::Off;
self.0.lock().await.config.display_brightness = brightness;
self.0.lock().await.config.write();
let mut config = self.0.config.lock().await;
config.display_enabled = brightness != Brightness::Off;
config.display_brightness = brightness;
config.write();
}
#[zbus(property)]
async fn builtins_enabled(&self) -> bool {
let lock = self.0.lock().await;
lock.config.builtin_anims_enabled
if let Some(config) = self.0.config.try_lock() {
return config.builtin_anims_enabled;
}
false
}
/// Enable the builtin animations or not. This is quivalent to "Powersave
/// animations" in Armory crate
#[zbus(property)]
async fn set_builtins_enabled(&self, enabled: bool) {
let brightness = self.0.lock().await.config.display_brightness;
let mut config = self.0.config.lock().await;
let brightness = config.display_brightness;
self.0
.lock()
.await
.node
.set_builtins_enabled(enabled, brightness)
.await
.map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err);
})
.ok();
if !enabled {
let anime_type = self.0.lock().await.anime_type;
let anime_type = config.anime_type;
let data = vec![255u8; anime_type.data_length()];
if let Ok(tmp) = AnimeDataBuffer::from_vec(anime_type, data).map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err);
}) {
self.0
.lock()
.await
.node
.write_bytes(tmp.data())
.await
.map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err);
})
@@ -137,77 +149,75 @@ impl CtrlAnimeZbus {
}
}
self.0.lock().await.config.builtin_anims_enabled = enabled;
self.0.lock().await.config.write();
config.builtin_anims_enabled = enabled;
config.write();
if enabled {
self.0
.lock()
.await
.thread_exit
.store(true, Ordering::Release);
self.0.thread_exit.store(true, Ordering::Release);
}
}
#[zbus(property)]
async fn builtin_animations(&self) -> Animations {
self.0.lock().await.config.builtin_anims
if let Some(config) = self.0.config.try_lock() {
return config.builtin_anims;
}
Animations::default()
}
/// Set which builtin animation is used for each stage
#[zbus(property)]
async fn set_builtin_animations(&self, settings: Animations) {
self.0
.lock()
.await
.node
.write_bytes(&pkt_set_builtin_animations(
settings.boot,
settings.awake,
settings.sleep,
settings.shutdown,
settings.boot, settings.awake, settings.sleep, settings.shutdown
))
.await
.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
})
.ok();
self.0
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(true))
.await
.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
})
.ok();
self.0.lock().await.config.display_enabled = true;
self.0.lock().await.config.builtin_anims = settings;
self.0.lock().await.config.write();
let mut config = self.0.config.lock().await;
config.display_enabled = true;
config.builtin_anims = settings;
config.write();
}
#[zbus(property)]
async fn enable_display(&self) -> bool {
self.0.lock().await.config.display_enabled
if let Some(config) = self.0.config.try_lock() {
return config.display_enabled;
}
false
}
/// Set whether the AniMe is enabled at all
#[zbus(property)]
async fn set_enable_display(&self, enabled: bool) {
self.0
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(enabled))
.await
.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
})
.ok();
self.0.lock().await.config.display_enabled = enabled;
self.0.lock().await.config.write();
let mut config = self.0.config.lock().await;
config.display_enabled = enabled;
config.write();
}
#[zbus(property)]
async fn off_when_unplugged(&self) -> bool {
self.0.lock().await.config.off_when_unplugged
if let Some(config) = self.0.config.try_lock() {
return config.off_when_unplugged;
}
false
}
/// Set if to turn the AniMe Matrix off when external power is unplugged
@@ -217,34 +227,40 @@ impl CtrlAnimeZbus {
let pow = manager.on_external_power().await.unwrap_or_default();
self.0
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(!pow && !enabled))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
})
.ok();
self.0.lock().await.config.off_when_unplugged = enabled;
self.0.lock().await.config.write();
let mut config = self.0.config.lock().await;
config.off_when_unplugged = enabled;
config.write();
}
#[zbus(property)]
async fn off_when_suspended(&self) -> bool {
self.0.lock().await.config.off_when_suspended
if let Some(config) = self.0.config.try_lock() {
return config.off_when_suspended;
}
false
}
/// Set if to turn the AniMe Matrix off when the laptop is suspended
#[zbus(property)]
async fn set_off_when_suspended(&self, enabled: bool) {
self.0.lock().await.config.off_when_suspended = enabled;
self.0.lock().await.config.write();
let mut config = self.0.config.lock().await;
config.off_when_suspended = enabled;
config.write();
}
#[zbus(property)]
async fn off_when_lid_closed(&self) -> bool {
self.0.lock().await.config.off_when_lid_closed
if let Some(config) = self.0.config.try_lock() {
return config.off_when_lid_closed;
}
false
}
/// Set if to turn the AniMe Matrix off when the lid is closed
@@ -254,50 +270,40 @@ impl CtrlAnimeZbus {
let lid = manager.lid_closed().await.unwrap_or_default();
self.0
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(lid && !enabled))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
})
.ok();
self.0.lock().await.config.off_when_lid_closed = enabled;
self.0.lock().await.config.write();
let mut config = self.0.config.lock().await;
config.off_when_lid_closed = enabled;
config.write();
}
/// The main loop is the base system set action if the user isn't running
/// the user daemon
async fn run_main_loop(&self, start: bool) {
if start {
self.0
.lock()
.await
.thread_exit
.store(true, Ordering::SeqCst);
CtrlAnime::run_thread(
self.0.clone(),
self.0.lock().await.cache.system.clone(),
false,
)
.await;
self.0.thread_exit.store(true, Ordering::SeqCst);
self.0.run_thread(self.0.cache.system.clone(), false).await;
}
}
/// Get the device state as stored by asusd
// #[zbus(property)]
async fn device_state(&self) -> DeviceState {
DeviceState::from(&self.0.lock().await.config)
DeviceState::from(&*self.0.config.lock().await)
}
}
impl crate::CtrlTask for CtrlAnimeZbus {
impl crate::CtrlTask for AniMeZbus {
fn zbus_path() -> &'static str {
ANIME_ZBUS_PATH
"ANIME_ZBUS_PATH"
}
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
async fn create_tasks(&self, _: SignalEmitter<'static>) -> Result<(), RogError> {
let inner1 = self.0.clone();
let inner2 = self.0.clone();
let inner3 = self.0.clone();
@@ -307,21 +313,15 @@ impl crate::CtrlTask for CtrlAnimeZbus {
// on_sleep
let inner = inner1.clone();
async move {
let config = inner.lock().await.config.clone();
let config = inner.config.lock().await.clone();
if config.display_enabled {
inner
.lock()
.await
.thread_exit
.store(true, Ordering::Release); // ensure clean slate
inner.thread_exit.store(true, Ordering::Release); // ensure clean slate
inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(
!(sleeping && config.off_when_suspended),
!(sleeping && config.off_when_suspended)
))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err);
})
@@ -329,12 +329,10 @@ impl crate::CtrlTask for CtrlAnimeZbus {
if config.builtin_anims_enabled {
inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(
!(sleeping && config.off_when_suspended),
!(sleeping && config.off_when_suspended)
))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err);
})
@@ -342,18 +340,11 @@ impl crate::CtrlTask for CtrlAnimeZbus {
} else if !sleeping && !config.builtin_anims_enabled {
// Run custom wake animation
inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(false))
.await
.ok(); // ensure builtins are disabled
CtrlAnime::run_thread(
inner.clone(),
inner.lock().await.cache.wake.clone(),
true,
)
.await;
inner.run_thread(inner.cache.wake.clone(), true).await;
}
}
}
@@ -362,26 +353,16 @@ impl crate::CtrlTask for CtrlAnimeZbus {
// on_shutdown
let inner = inner2.clone();
async move {
let AnimeConfig {
let AniMeConfig {
display_enabled,
builtin_anims_enabled,
..
} = inner.lock().await.config;
} = *inner.config.lock().await;
if display_enabled && !builtin_anims_enabled {
if shutting_down {
CtrlAnime::run_thread(
inner.clone(),
inner.lock().await.cache.shutdown.clone(),
true,
)
.await;
inner.run_thread(inner.cache.shutdown.clone(), true).await;
} else {
CtrlAnime::run_thread(
inner.clone(),
inner.lock().await.cache.boot.clone(),
true,
)
.await;
inner.run_thread(inner.cache.boot.clone(), true).await;
}
}
}
@@ -390,28 +371,24 @@ impl crate::CtrlTask for CtrlAnimeZbus {
let inner = inner3.clone();
// on lid change
async move {
let AnimeConfig {
let AniMeConfig {
off_when_lid_closed,
builtin_anims_enabled,
..
} = inner.lock().await.config;
} = *inner.config.lock().await;
if off_when_lid_closed {
if builtin_anims_enabled {
inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(!lid_closed))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err);
})
.ok();
}
inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(!lid_closed))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
})
@@ -423,46 +400,40 @@ impl crate::CtrlTask for CtrlAnimeZbus {
let inner = inner4.clone();
// on power change
async move {
let AnimeConfig {
let AniMeConfig {
off_when_unplugged,
builtin_anims_enabled,
brightness_on_battery,
..
} = inner.lock().await.config;
} = *inner.config.lock().await;
if off_when_unplugged {
if builtin_anims_enabled {
inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(power_plugged))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err);
})
.ok();
}
inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(power_plugged))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_unplugged {}", err);
})
.ok();
} else {
inner
.lock()
.await
.node
.write_bytes(&pkt_set_brightness(brightness_on_battery))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_unplugged {}", err);
})
.ok();
}
}
},
}
)
.await;
@@ -470,52 +441,62 @@ impl crate::CtrlTask for CtrlAnimeZbus {
}
}
impl crate::Reloadable for CtrlAnimeZbus {
impl crate::Reloadable for AniMeZbus {
async fn reload(&mut self) -> Result<(), RogError> {
if let Some(lock) = self.0.try_lock() {
let anim = &lock.config.builtin_anims;
// Set builtins
if lock.config.builtin_anims_enabled {
lock.node.write_bytes(&pkt_set_builtin_animations(
anim.boot,
anim.awake,
anim.sleep,
anim.shutdown,
))?;
}
// Builtins enabled or na?
lock.node.set_builtins_enabled(
lock.config.builtin_anims_enabled,
lock.config.display_brightness,
)?;
let AniMeConfig {
builtin_anims_enabled,
builtin_anims,
display_enabled,
display_brightness,
off_when_lid_closed,
off_when_unplugged,
..
} = *self.0.config.lock().await;
let manager = get_logind_manager().await;
let lid_closed = manager.lid_closed().await.unwrap_or_default();
let power_plugged = manager.on_external_power().await.unwrap_or_default();
// Set builtins
if builtin_anims_enabled {
self.0
.write_bytes(&pkt_set_builtin_animations(
builtin_anims.boot,
builtin_anims.awake,
builtin_anims.sleep,
builtin_anims.shutdown
))
.await?;
}
// Builtins enabled or na?
self.0
.set_builtins_enabled(builtin_anims_enabled, display_brightness)
.await?;
let turn_off = (lid_closed && lock.config.off_when_lid_closed)
|| (!power_plugged && lock.config.off_when_unplugged);
lock.node
.write_bytes(&pkt_set_enable_display(!turn_off))
.map_err(|err| {
warn!("create_sys_event_tasks::reload {}", err);
})
let manager = get_logind_manager().await;
let lid_closed = manager.lid_closed().await.unwrap_or_default();
let power_plugged = manager.on_external_power().await.unwrap_or_default();
let turn_off =
(lid_closed && off_when_lid_closed) || (!power_plugged && off_when_unplugged);
self.0
.write_bytes(&pkt_set_enable_display(!turn_off))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::reload {}", err);
})
.ok();
if turn_off || !display_enabled {
self.0.write_bytes(&pkt_set_enable_display(false)).await?;
// early return so we don't run animation thread
return Ok(());
}
if !builtin_anims_enabled && !self.0.cache.boot.is_empty() {
self.0
.write_bytes(&pkt_set_enable_powersave_anim(false))
.await
.ok();
if turn_off || !lock.config.display_enabled {
lock.node.write_bytes(&pkt_set_enable_display(false))?;
// early return so we don't run animation thread
return Ok(());
}
if !lock.config.builtin_anims_enabled && !lock.cache.boot.is_empty() {
lock.node
.write_bytes(&pkt_set_enable_powersave_anim(false))
.ok();
let action = lock.cache.boot.clone();
CtrlAnime::run_thread(self.0.clone(), action, true).await;
}
let action = self.0.cache.boot.clone();
self.0.run_thread(action, true).await;
}
Ok(())
}

View File

@@ -5,14 +5,22 @@ use log::{debug, info, warn};
use rog_aura::aura_detection::LedSupportData;
use rog_aura::keyboard::LaptopAuraPower;
use rog_aura::{
AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, Direction, LedBrightness, Speed, GRADIENT,
AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, Direction, LedBrightness, Speed, GRADIENT
};
use serde_derive::{Deserialize, Serialize};
use serde::{Deserialize, Serialize};
use crate::error::RogError;
#[derive(Deserialize, Serialize, Default, Debug, Clone)]
// #[serde(default)]
pub struct AuraConfig {
#[serde(skip)]
pub led_type: AuraDeviceType,
#[serde(skip)]
pub support_data: LedSupportData,
pub config_name: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ally_fix: Option<bool>,
pub brightness: LedBrightness,
pub current_mode: AuraModeNum,
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
@@ -20,6 +28,8 @@ pub struct AuraConfig {
pub multizone: Option<BTreeMap<AuraModeNum, Vec<AuraEffect>>>,
pub multizone_on: bool,
pub enabled: LaptopAuraPower,
#[serde(skip)]
pub per_key_mode_active: bool
}
impl StdConfig for AuraConfig {
@@ -54,31 +64,35 @@ impl AuraConfig {
let support_data = LedSupportData::get_data(prod_id);
let enabled = LaptopAuraPower::new(device_type, &support_data);
let mut config = AuraConfig {
led_type: device_type,
support_data,
config_name: format!("aura_{prod_id}.ron"),
ally_fix: None,
brightness: LedBrightness::Med,
current_mode: AuraModeNum::Static,
builtins: BTreeMap::new(),
multizone: None,
multizone_on: false,
enabled,
per_key_mode_active: false
};
for n in &support_data.basic_modes {
for n in &config.support_data.basic_modes {
debug!("creating default for {n}");
config
.builtins
.insert(*n, AuraEffect::default_with_mode(*n));
if !support_data.basic_zones.is_empty() {
if !config.support_data.basic_zones.is_empty() {
let mut default = vec![];
for (i, tmp) in support_data.basic_zones.iter().enumerate() {
for (i, tmp) in config.support_data.basic_zones.iter().enumerate() {
default.push(AuraEffect {
mode: *n,
zone: *tmp,
colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]),
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
speed: Speed::Med,
direction: Direction::Left,
direction: Direction::Left
});
}
if let Some(m) = config.multizone.as_mut() {
@@ -130,23 +144,109 @@ impl AuraConfig {
}
None
}
/// Create a default for the `current_mode` if multizone and no config
/// exists.
pub fn create_multizone_default(&mut self) -> Result<(), RogError> {
let mut default = vec![];
for (i, tmp) in self.support_data.basic_zones.iter().enumerate() {
default.push(AuraEffect {
mode: self.current_mode,
zone: *tmp,
colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]),
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
speed: Speed::Med,
direction: Direction::Left
});
}
if default.is_empty() {
return Err(RogError::AuraEffectNotSupported);
}
if let Some(multizones) = self.multizone.as_mut() {
multizones.insert(self.current_mode, default);
} else {
let mut tmp = BTreeMap::new();
tmp.insert(self.current_mode, default);
self.multizone = Some(tmp);
}
Ok(())
}
/// Reload the config from disk then verify and update it if required.
/// Always rewrites the file to disk.
pub fn load_and_update_config(prod_id: &str) -> AuraConfig {
// New loads data from the DB also
let mut config_init = AuraConfig::new(prod_id);
// config_init.set_filename(prod_id);
let mut config_loaded = config_init.clone().load();
// update the initialised data with what we loaded from disk
for mode_init in &mut config_init.builtins {
// update init values from loaded values if they exist
if let Some(loaded) = config_loaded.builtins.get(mode_init.0) {
*mode_init.1 = loaded.clone();
}
}
// Then replace just incase the initialised data contains new modes added
config_loaded.builtins = config_init.builtins;
config_loaded.support_data = config_init.support_data;
config_loaded.led_type = config_init.led_type;
config_loaded.ally_fix = config_init.ally_fix;
for enabled_init in &mut config_init.enabled.states {
for enabled in &mut config_loaded.enabled.states {
if enabled.zone == enabled_init.zone {
*enabled_init = *enabled;
break;
}
}
}
config_loaded.enabled = config_init.enabled;
if let (Some(mut multizone_init), Some(multizone_loaded)) =
(config_init.multizone, config_loaded.multizone.as_mut())
{
for mode in multizone_init.iter_mut() {
// update init values from loaded values if they exist
if let Some(loaded) = multizone_loaded.get(mode.0) {
let mut new_set = Vec::new();
let data = LedSupportData::get_data(prod_id);
// only reuse a zone mode if the mode is supported
for mode in loaded {
if data.basic_modes.contains(&mode.mode) {
new_set.push(mode.clone());
}
}
*mode.1 = new_set;
}
}
*multizone_loaded = multizone_init;
}
config_loaded.write();
config_loaded
}
}
#[cfg(test)]
mod tests {
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour};
use rog_aura::keyboard::AuraPowerState;
use rog_aura::{
AuraEffect, AuraModeNum, AuraZone, Colour, Direction, LedBrightness, PowerZones, Speed
};
use super::AuraConfig;
#[test]
fn set_multizone_4key_config() {
std::env::set_var("BOARD_NAME", "");
let mut config = AuraConfig::new("19b6");
let effect = AuraEffect {
colour1: Colour {
r: 0xff,
g: 0x00,
b: 0xff,
b: 0xff
},
zone: AuraZone::Key1,
..Default::default()
@@ -159,7 +259,7 @@ mod tests {
colour1: Colour {
r: 0x00,
g: 0xff,
b: 0xff,
b: 0xff
},
zone: AuraZone::Key2,
..Default::default()
@@ -170,7 +270,7 @@ mod tests {
colour1: Colour {
r: 0xff,
g: 0xff,
b: 0x00,
b: 0x00
},
zone: AuraZone::Key3,
..Default::default()
@@ -181,7 +281,7 @@ mod tests {
colour1: Colour {
r: 0x00,
g: 0xff,
b: 0x00,
b: 0x00
},
zone: AuraZone::Key4,
..Default::default()
@@ -194,42 +294,31 @@ mod tests {
let res = config.multizone.unwrap();
let sta = res.get(&AuraModeNum::Static).unwrap();
assert_eq!(sta.len(), 4);
assert_eq!(
sta[0].colour1,
Colour {
r: 0xff,
g: 0x00,
b: 0xff
}
);
assert_eq!(
sta[1].colour1,
Colour {
r: 0x00,
g: 0xff,
b: 0xff
}
);
assert_eq!(
sta[2].colour1,
Colour {
r: 0xff,
g: 0xff,
b: 0x00
}
);
assert_eq!(
sta[3].colour1,
Colour {
r: 0x00,
g: 0xff,
b: 0x00
}
);
assert_eq!(sta[0].colour1, Colour {
r: 0xff,
g: 0x00,
b: 0xff
});
assert_eq!(sta[1].colour1, Colour {
r: 0x00,
g: 0xff,
b: 0xff
});
assert_eq!(sta[2].colour1, Colour {
r: 0xff,
g: 0xff,
b: 0x00
});
assert_eq!(sta[3].colour1, Colour {
r: 0x00,
g: 0xff,
b: 0x00
});
}
#[test]
fn set_multizone_multimode_config() {
std::env::set_var("BOARD_NAME", "");
let mut config = AuraConfig::new("19b6");
let effect = AuraEffect {
@@ -274,4 +363,54 @@ mod tests {
let sta = res.get(&AuraModeNum::Pulse).unwrap();
assert_eq!(sta.len(), 1);
}
#[test]
fn verify_0x1866_g531i() {
std::env::set_var("BOARD_NAME", "G513I");
let mut config = AuraConfig::new("1866");
assert_eq!(config.brightness, LedBrightness::Med);
assert_eq!(config.builtins.len(), 5);
assert_eq!(config.builtins.first_entry().unwrap().get(), &AuraEffect {
mode: AuraModeNum::Static,
zone: AuraZone::None,
colour1: Colour { r: 166, g: 0, b: 0 },
colour2: Colour { r: 0, g: 0, b: 0 },
speed: Speed::Med,
direction: Direction::Right
});
assert_eq!(config.enabled.states.len(), 1);
assert_eq!(config.enabled.states[0], AuraPowerState {
zone: PowerZones::KeyboardAndLightbar,
boot: true,
awake: true,
sleep: true,
shutdown: true
});
}
#[test]
fn verify_0x19b6_g634j() {
std::env::set_var("BOARD_NAME", "G634J");
let mut config = AuraConfig::new("19b6");
assert_eq!(config.brightness, LedBrightness::Med);
assert_eq!(config.builtins.len(), 12);
assert_eq!(config.builtins.first_entry().unwrap().get(), &AuraEffect {
mode: AuraModeNum::Static,
zone: AuraZone::None,
colour1: Colour { r: 166, g: 0, b: 0 },
colour2: Colour { r: 0, g: 0, b: 0 },
speed: Speed::Med,
direction: Direction::Right
});
assert_eq!(config.enabled.states.len(), 4);
assert_eq!(config.enabled.states[0], AuraPowerState {
zone: PowerZones::Keyboard,
boot: true,
awake: true,
sleep: true,
shutdown: true
});
}
}

View File

@@ -0,0 +1,229 @@
use std::sync::Arc;
use config::AuraConfig;
use config_traits::StdConfig;
use futures_util::lock::{Mutex, MutexGuard};
use log::info;
use rog_aura::keyboard::{AuraLaptopUsbPackets, LedUsbPackets};
use rog_aura::usb::{AURA_LAPTOP_LED_APPLY, AURA_LAPTOP_LED_SET};
use rog_aura::{AuraDeviceType, AuraEffect, LedBrightness, PowerZones, AURA_LAPTOP_LED_MSG_LEN};
use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardBacklight;
use crate::error::RogError;
pub mod config;
pub mod trait_impls;
#[derive(Debug, Clone)]
pub struct Aura {
pub hid: Option<Arc<Mutex<HidRaw>>>,
pub backlight: Option<Arc<Mutex<KeyboardBacklight>>>,
pub config: Arc<Mutex<AuraConfig>>
}
impl Aura {
/// Initialise the device if required.
pub async fn do_initialization(&self) -> Result<(), RogError> {
Ok(())
}
pub async fn lock_config(&self) -> MutexGuard<AuraConfig> {
self.config.lock().await
}
/// Will lock the internal config and update. If anything else has locked
/// this in scope then a deadlock can occur.
pub async fn update_config(&self) -> Result<(), RogError> {
let mut config = self.config.lock().await;
let bright = if let Some(bl) = self.backlight.as_ref() {
bl.lock().await.get_brightness().unwrap_or_default()
} else {
config.brightness.into()
};
config.read();
config.brightness = bright.into();
config.write();
Ok(())
}
pub async fn write_current_config_mode(&self, config: &mut AuraConfig) -> Result<(), RogError> {
if config.multizone_on {
let mode = config.current_mode;
let mut create = false;
// There is no multizone config for this mode so create one here
// using the colours of rainbow if it exists, or first available
// mode, or random
if config.multizone.is_none() {
create = true;
} else if let Some(multizones) = config.multizone.as_ref() {
if !multizones.contains_key(&mode) {
create = true;
}
}
if create {
info!("No user-set config for zone founding, attempting a default");
config.create_multizone_default()?;
}
if let Some(multizones) = config.multizone.as_mut() {
if let Some(set) = multizones.get(&mode) {
for mode in set.clone() {
self.write_effect_and_apply(config.led_type, &mode).await?;
}
}
}
} else {
let mode = config.current_mode;
if let Some(effect) = config.builtins.get(&mode).cloned() {
self.write_effect_and_apply(config.led_type, &effect)
.await?;
}
}
Ok(())
}
/// Write the AuraEffect to the device. Will lock `backlight` or `hid`.
///
/// If per-key or software-mode is active it must be marked as disabled in
/// config.
pub async fn write_effect_and_apply(
&self,
dev_type: AuraDeviceType,
mode: &AuraEffect
) -> Result<(), RogError> {
if matches!(dev_type, AuraDeviceType::LaptopKeyboardTuf) {
if let Some(platform) = &self.backlight {
let buf = [
1, mode.mode as u8, mode.colour1.r, mode.colour1.g, mode.colour1.b,
mode.speed as u8
];
platform.lock().await.set_kbd_rgb_mode(&buf)?;
}
} else if let Some(hid_raw) = &self.hid {
let bytes: [u8; AURA_LAPTOP_LED_MSG_LEN] = mode.into();
let hid_raw = hid_raw.lock().await;
hid_raw.write_bytes(&bytes)?;
hid_raw.write_bytes(&AURA_LAPTOP_LED_SET)?;
// Changes won't persist unless apply is set
hid_raw.write_bytes(&AURA_LAPTOP_LED_APPLY)?;
} else {
return Err(RogError::NoAuraKeyboard);
}
Ok(())
}
pub async fn set_brightness(&self, value: u8) -> Result<(), RogError> {
if let Some(backlight) = &self.backlight {
backlight.lock().await.set_brightness(value)?;
return Ok(());
}
Err(RogError::MissingFunction(
"No LED backlight control available".to_string()
))
}
/// Set combination state for boot animation/sleep animation/all leds/keys
/// leds/side leds LED active
pub async fn set_power_states(&self, config: &AuraConfig) -> Result<(), RogError> {
if matches!(config.led_type, rog_aura::AuraDeviceType::LaptopKeyboardTuf) {
if let Some(backlight) = &self.backlight {
// TODO: tuf bool array
let buf = config.enabled.to_bytes(config.led_type);
backlight.lock().await.set_kbd_rgb_state(&buf)?;
}
} else if let Some(hid_raw) = &self.hid {
let hid_raw = hid_raw.lock().await;
if let Some(p) = config.enabled.states.first() {
if p.zone == PowerZones::Ally {
let msg = [
0x5d,
0xd1,
0x09,
0x01,
p.new_to_byte() as u8,
0x0,
0x0
];
hid_raw.write_bytes(&msg)?;
return Ok(());
}
}
let bytes = config.enabled.to_bytes(config.led_type);
let msg = [
0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2], bytes[3]
];
hid_raw.write_bytes(&msg)?;
}
Ok(())
}
/// Write an effect block. This is for per-key, but can be repurposed to
/// write the raw factory mode packets - when doing this it is expected that
/// only the first `Vec` (`effect[0]`) is valid.
pub async fn write_effect_block(
&self,
config: &mut AuraConfig,
effect: &AuraLaptopUsbPackets
) -> Result<(), RogError> {
if config.brightness == LedBrightness::Off {
config.brightness = LedBrightness::Med;
config.write();
}
let pkt_type = effect[0][1];
const PER_KEY_TYPE: u8 = 0xbc;
if let Some(hid_raw) = &self.hid {
let hid_raw = hid_raw.lock().await;
if pkt_type != PER_KEY_TYPE {
config.per_key_mode_active = false;
hid_raw.write_bytes(&effect[0])?;
hid_raw.write_bytes(&AURA_LAPTOP_LED_SET)?;
// hid_raw.write_bytes(&LED_APPLY)?;
} else {
if !config.per_key_mode_active {
let init = LedUsbPackets::get_init_msg();
hid_raw.write_bytes(&init)?;
config.per_key_mode_active = true;
}
for row in effect.iter() {
hid_raw.write_bytes(row)?;
}
}
} else if matches!(config.led_type, rog_aura::AuraDeviceType::LaptopKeyboardTuf) {
if let Some(tuf) = &self.backlight {
for row in effect.iter() {
let r = row[9];
let g = row[10];
let b = row[11];
tuf.lock().await.set_kbd_rgb_mode(&[
0, 0, r, g, b, 0
])?;
}
}
}
Ok(())
}
pub async fn fix_ally_power(&mut self) -> Result<(), RogError> {
if self.config.lock().await.led_type == AuraDeviceType::Ally {
if let Some(hid_raw) = &self.hid {
let mut config = self.config.lock().await;
if config.ally_fix.is_none() {
let msg = [
0x5d, 0xbd, 0x01, 0xff, 0xff, 0xff, 0xff
];
hid_raw.lock().await.write_bytes(&msg)?;
info!("Reset Ally power settings to base");
config.ally_fix = Some(true);
}
config.write();
}
}
Ok(())
}
}

View File

@@ -0,0 +1,343 @@
use std::collections::BTreeMap;
use config_traits::StdConfig;
use log::{debug, error, info, warn};
use rog_aura::keyboard::{AuraLaptopUsbPackets, LaptopAuraPower};
use rog_aura::{AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, LedBrightness, PowerZones};
use zbus::fdo::Error as ZbErr;
use zbus::object_server::SignalEmitter;
use zbus::zvariant::OwnedObjectPath;
use zbus::{interface, Connection};
use super::Aura;
use crate::error::RogError;
use crate::{CtrlTask, Reloadable};
pub const AURA_ZBUS_NAME: &str = "Aura";
pub const AURA_ZBUS_PATH: &str = "/xyz/ljones";
#[derive(Clone)]
pub struct AuraZbus(Aura);
impl AuraZbus {
pub fn new(aura: Aura) -> Self {
Self(aura)
}
pub async fn start_tasks(
mut self,
connection: &Connection,
// _signal_ctx: SignalEmitter<'static>,
path: OwnedObjectPath
) -> Result<(), RogError> {
// let task = zbus.clone();
// let signal_ctx = signal_ctx.clone();
self.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
connection
.object_server()
.at(path.clone(), self)
.await
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
.ok();
// TODO: skip this until we keep handles to tasks so they can be killed
// task.create_tasks(signal_ctx).await
Ok(())
}
}
/// The main interface for changing, reading, or notfying
///
/// LED commands are split between Brightness, Modes, Per-Key
#[interface(name = "xyz.ljones.Aura")]
impl AuraZbus {
/// Return the device type for this Aura keyboard
#[zbus(property)]
async fn device_type(&self) -> AuraDeviceType {
self.0.config.lock().await.led_type
}
/// Return the current LED brightness
#[zbus(property)]
async fn brightness(&self) -> Result<LedBrightness, ZbErr> {
if let Some(bl) = self.0.backlight.as_ref() {
return Ok(bl.lock().await.get_brightness().map(|n| n.into())?);
}
Err(ZbErr::Failed("No sysfs brightness control".to_string()))
}
/// Set the keyboard brightness level (0-3)
#[zbus(property)]
async fn set_brightness(&mut self, brightness: LedBrightness) -> Result<(), ZbErr> {
if let Some(bl) = self.0.backlight.as_ref() {
return Ok(bl.lock().await.set_brightness(brightness.into())?);
}
Err(ZbErr::Failed("No sysfs brightness control".to_string()))
}
/// Total levels of brightness available
#[zbus(property)]
async fn supported_brightness(&self) -> Vec<LedBrightness> {
vec![
LedBrightness::Off,
LedBrightness::Low,
LedBrightness::Med,
LedBrightness::High,
]
}
/// The total available modes
#[zbus(property)]
async fn supported_basic_modes(&self) -> Result<Vec<AuraModeNum>, ZbErr> {
let config = self.0.config.lock().await;
Ok(config.builtins.keys().cloned().collect())
}
#[zbus(property)]
async fn supported_basic_zones(&self) -> Result<Vec<AuraZone>, ZbErr> {
let config = self.0.config.lock().await;
Ok(config.support_data.basic_zones.clone())
}
#[zbus(property)]
async fn supported_power_zones(&self) -> Result<Vec<PowerZones>, ZbErr> {
let config = self.0.config.lock().await;
Ok(config.support_data.power_zones.clone())
}
/// The current mode data
#[zbus(property)]
async fn led_mode(&self) -> Result<AuraModeNum, ZbErr> {
// entirely possible to deadlock here, so use try instead of lock()
// let ctrl = self.0.lock().await;
// Ok(config.current_mode)
if let Some(config) = self.0.config.try_lock() {
Ok(config.current_mode)
} else {
Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
}
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode(&mut self, num: AuraModeNum) -> Result<(), ZbErr> {
let mut config = self.0.config.lock().await;
config.current_mode = num;
self.0.write_current_config_mode(&mut config).await?;
if config.brightness == LedBrightness::Off {
config.brightness = LedBrightness::Med;
}
self.0.set_brightness(config.brightness.into()).await?;
config.write();
Ok(())
}
/// The current mode data
#[zbus(property)]
async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> {
// entirely possible to deadlock here, so use try instead of lock()
if let Some(config) = self.0.config.try_lock() {
let mode = config.current_mode;
match config.builtins.get(&mode) {
Some(effect) => Ok(effect.clone()),
None => Err(ZbErr::Failed("Could not get the current effect".into()))
}
} else {
Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
}
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode_data(&mut self, effect: AuraEffect) -> Result<(), ZbErr> {
let mut config = self.0.config.lock().await;
if !config.support_data.basic_modes.contains(&effect.mode)
|| effect.zone != AuraZone::None
&& !config.support_data.basic_zones.contains(&effect.zone)
{
return Err(ZbErr::NotSupported(format!(
"The Aura effect is not supported: {effect:?}"
)));
}
self.0
.write_effect_and_apply(config.led_type, &effect)
.await?;
if config.brightness == LedBrightness::Off {
config.brightness = LedBrightness::Med;
}
self.0.set_brightness(config.brightness.into()).await?;
config.set_builtin(effect);
config.write();
Ok(())
}
/// Get the data set for every mode available
async fn all_mode_data(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
let config = self.0.config.lock().await;
config.builtins.clone()
}
// As property doesn't work for AuraPowerDev (complexity of serialization?)
#[zbus(property)]
async fn led_power(&self) -> LaptopAuraPower {
let config = self.0.config.lock().await;
config.enabled.clone()
}
/// Set a variety of states, input is array of enum.
/// `enabled` sets if the sent array should be disabled or enabled
///
/// For Modern ROG devices the "enabled" flag is ignored.
#[zbus(property)]
async fn set_led_power(&mut self, options: LaptopAuraPower) -> Result<(), ZbErr> {
let mut config = self.0.config.lock().await;
for opt in options.states {
let zone = opt.zone;
for config in config.enabled.states.iter_mut() {
if config.zone == zone {
*config = opt;
}
}
}
config.write();
Ok(self.0.set_power_states(&config).await.map_err(|e| {
warn!("{}", e);
e
})?)
}
/// On machine that have some form of either per-key keyboard or per-zone
/// this can be used to write custom effects over dbus. The input is a
/// nested `Vec<Vec<8>>` where `Vec<u8>` is a raw USB packet
async fn direct_addressing_raw(&self, data: AuraLaptopUsbPackets) -> Result<(), ZbErr> {
let mut config = self.0.config.lock().await;
self.0.write_effect_block(&mut config, &data).await?;
Ok(())
}
}
impl CtrlTask for AuraZbus {
fn zbus_path() -> &'static str {
"/xyz/ljones"
}
async fn create_tasks(&self, _: SignalEmitter<'static>) -> Result<(), RogError> {
let inner1 = self.0.clone();
let inner3 = self.0.clone();
self.create_sys_event_tasks(
move |sleeping| {
let inner1 = inner1.clone();
// unwrap as we want to bomb out of the task
async move {
if !sleeping {
info!("CtrlKbdLedTask reloading brightness and modes");
if let Some(backlight) = &inner1.backlight {
backlight
.lock()
.await
.set_brightness(inner1.config.lock().await.brightness.into())
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})
.unwrap();
}
let mut config = inner1.config.lock().await;
inner1
.write_current_config_mode(&mut config)
.await
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})
.unwrap();
} else if sleeping {
inner1
.update_config()
.await
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})
.unwrap();
}
}
},
move |_shutting_down| {
let inner3 = inner3.clone();
async move {
info!("CtrlKbdLedTask reloading brightness and modes");
if let Some(backlight) = &inner3.backlight {
// unwrap as we want to bomb out of the task
backlight
.lock()
.await
.set_brightness(inner3.config.lock().await.brightness.into())
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})
.unwrap();
}
}
},
move |_lid_closed| {
// on lid change
async move {}
},
move |_power_plugged| {
// power change
async move {}
}
)
.await;
// let ctrl2 = self.0.clone();
// let ctrl = self.0.lock().await;
// if ctrl.led_node.has_brightness_control() {
// let watch = ctrl.led_node.monitor_brightness()?;
// tokio::spawn(async move {
// let mut buffer = [0; 32];
// watch
// .into_event_stream(&mut buffer)
// .unwrap()
// .for_each(|_| async {
// if let Some(lock) = ctrl2.try_lock() {
// load_save(true, lock).unwrap(); // unwrap as we want
// // to
// // bomb out of the
// // task
// }
// })
// .await;
// });
// }
Ok(())
}
}
impl Reloadable for AuraZbus {
async fn reload(&mut self) -> Result<(), RogError> {
self.0.fix_ally_power().await?;
debug!("reloading keyboard mode");
let mut config = self.0.lock_config().await;
self.0.write_current_config_mode(&mut config).await?;
debug!("reloading power states");
self.0
.set_power_states(&config)
.await
.map_err(|err| warn!("{err}"))
.ok();
Ok(())
}
}

541
asusd/src/aura_manager.rs Normal file
View File

@@ -0,0 +1,541 @@
// Plan:
// - Manager has udev monitor on USB looking for ROG devices
// - If a device is found, add it to watch
// - Add it to Zbus server
// - If udev sees device removed then remove the zbus path
use std::sync::Arc;
use dmi_id::DMIID;
use futures_lite::future::block_on;
use futures_util::lock::Mutex;
use log::{debug, error, info, warn};
use mio::{Events, Interest, Poll, Token};
use rog_platform::error::PlatformError;
use rog_platform::hid_raw::HidRaw;
use udev::{Device, MonitorBuilder};
use zbus::zvariant::{ObjectPath, OwnedObjectPath};
use zbus::Connection;
use crate::aura_anime::trait_impls::AniMeZbus;
use crate::aura_laptop::trait_impls::AuraZbus;
use crate::aura_scsi::trait_impls::ScsiZbus;
use crate::aura_slash::trait_impls::SlashZbus;
use crate::aura_types::DeviceHandle;
use crate::error::RogError;
use crate::ASUS_ZBUS_PATH;
const MOD_NAME: &str = "aura";
/// Returns only the Device details concatenated in a form usable for
/// adding/appending to a filename
pub fn filename_partial(parent: &Device) -> Option<OwnedObjectPath> {
if let Some(id_product) = parent.attribute_value("idProduct") {
let id_product = id_product.to_string_lossy();
let mut path = if let Some(devnum) = parent.attribute_value("devnum") {
let devnum = devnum.to_string_lossy();
if let Some(devpath) = parent.attribute_value("devpath") {
let devpath = devpath.to_string_lossy();
format!("{id_product}_{devnum}_{devpath}")
} else {
format!("{id_product}_{devnum}")
}
} else {
format!("{id_product}")
};
if path.contains('.') {
warn!("dbus path for {id_product} contains `.`, removing");
path.replace('.', "").clone_into(&mut path);
}
return Some(ObjectPath::from_str_unchecked(&path).into());
}
None
}
fn dbus_path_for_dev(parent: &Device) -> Option<OwnedObjectPath> {
if let Some(filename) = filename_partial(parent) {
return Some(
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{filename}"))
.into()
);
}
None
}
fn dbus_path_for_tuf() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/tuf")).into()
}
fn dbus_path_for_slash() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/slash")).into()
}
fn dbus_path_for_anime() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/anime")).into()
}
fn dbus_path_for_scsi(prod_id: &str) -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{prod_id}_scsi")).into()
}
fn dev_prop_matches(dev: &Device, prop: &str, value: &str) -> bool {
if let Some(p) = dev.property_value(prop) {
return p == value;
}
false
}
/// A device.
///
/// Each controller within should track its dbus path so it can be removed if
/// required.
pub struct AsusDevice {
device: DeviceHandle,
dbus_path: OwnedObjectPath
}
pub struct DeviceManager {
_dbus_connection: Connection
}
impl DeviceManager {
async fn init_hid_devices(
connection: &Connection,
device: Device
) -> Result<Vec<AsusDevice>, RogError> {
let mut devices = Vec::new();
if let Some(usb_device) = device.parent_with_subsystem_devtype("usb", "usb_device")? {
if let Some(usb_id) = usb_device.attribute_value("idProduct") {
if let Some(vendor_id) = usb_device.attribute_value("idVendor") {
if vendor_id != "0b05" {
debug!("Not ASUS vendor ID");
return Ok(devices);
}
// Almost all devices are identified by the productId.
// So let's see what we have and:
// 1. Generate an interface path
// 2. Create the device
// Use the top-level endpoint, not the parent
if let Ok(hidraw) = HidRaw::from_device(device) {
debug!("Testing device {usb_id:?}");
let dev = Arc::new(Mutex::new(hidraw));
// SLASH DEVICE
if let Ok(dev_type) = DeviceHandle::new_slash_hid(
dev.clone(),
usb_id.to_str().unwrap_or_default()
)
.await
{
if let DeviceHandle::Slash(slash) = dev_type.clone() {
let path =
dbus_path_for_dev(&usb_device).unwrap_or(dbus_path_for_slash());
let ctrl = SlashZbus::new(slash);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path
});
}
}
// ANIME MATRIX DEVICE
if let Ok(dev_type) = DeviceHandle::maybe_anime_hid(
dev.clone(),
usb_id.to_str().unwrap_or_default()
)
.await
{
if let DeviceHandle::AniMe(anime) = dev_type.clone() {
let path =
dbus_path_for_dev(&usb_device).unwrap_or(dbus_path_for_anime());
let ctrl = AniMeZbus::new(anime);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path
});
}
}
// AURA LAPTOP DEVICE
if let Ok(dev_type) = DeviceHandle::maybe_laptop_aura(
Some(dev),
usb_id.to_str().unwrap_or_default()
)
.await
{
if let DeviceHandle::Aura(aura) = dev_type.clone() {
let path =
dbus_path_for_dev(&usb_device).unwrap_or(dbus_path_for_tuf());
let ctrl = AuraZbus::new(aura);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path
});
}
}
}
}
}
}
Ok(devices)
}
/// To be called on daemon startup
async fn init_all_hid(connection: &Connection) -> Result<Vec<AsusDevice>, RogError> {
// track and ensure we use only one hidraw per prod_id
// let mut interfaces = HashSet::new();
let mut devices: Vec<AsusDevice> = Vec::new();
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
PlatformError::Udev("enumerator failed".into(), err)
})?;
enumerator.match_subsystem("hidraw").map_err(|err| {
warn!("{}", err);
PlatformError::Udev("match_subsystem failed".into(), err)
})?;
for device in enumerator
.scan_devices()
.map_err(|e| PlatformError::IoPath("enumerator".to_owned(), e))?
{
devices.append(&mut Self::init_hid_devices(connection, device).await?);
}
Ok(devices)
}
async fn init_scsi(
connection: &Connection,
device: &Device,
path: OwnedObjectPath
) -> Option<AsusDevice> {
// "ID_MODEL_ID" "1932"
// "ID_VENDOR_ID" "0b05"
if dev_prop_matches(device, "ID_VENDOR_ID", "0b05") {
if let Some(dev_node) = device.devnode() {
let prod_id = device
.property_value("ID_MODEL_ID")
.unwrap_or_default()
.to_string_lossy();
if let Ok(dev_type) =
DeviceHandle::maybe_scsi(dev_node.as_os_str().to_str().unwrap(), &prod_id).await
{
if let DeviceHandle::Scsi(scsi) = dev_type.clone() {
let ctrl = ScsiZbus::new(scsi);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
return Some(AsusDevice {
device: dev_type,
dbus_path: path
});
}
}
}
}
None
}
async fn init_all_scsi(connection: &Connection) -> Result<Vec<AsusDevice>, RogError> {
// track and ensure we use only one hidraw per prod_id
// let mut interfaces = HashSet::new();
let mut devices: Vec<AsusDevice> = Vec::new();
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
PlatformError::Udev("enumerator failed".into(), err)
})?;
enumerator.match_subsystem("block").map_err(|err| {
warn!("{}", err);
PlatformError::Udev("match_subsystem failed".into(), err)
})?;
let mut found = Vec::new();
for device in enumerator
.scan_devices()
.map_err(|e| PlatformError::IoPath("enumerator".to_owned(), e))?
{
if let Some(serial) = device.property_value("ID_SERIAL_SHORT") {
let serial = serial.to_string_lossy().to_string();
let path = dbus_path_for_scsi(&serial);
if found.contains(&path) {
continue;
}
if let Some(dev) = Self::init_scsi(connection, &device, path.clone()).await {
devices.push(dev);
found.push(path);
}
} else {
debug!("No serial for SCSI device: {:?}", device.devpath());
}
}
Ok(devices)
}
pub async fn find_all_devices(connection: &Connection) -> Vec<AsusDevice> {
let mut devices: Vec<AsusDevice> = Vec::new();
// HID first, always
if let Ok(devs) = &mut Self::init_all_hid(connection).await {
devices.append(devs);
}
// USB after, need to check if HID picked something up and if so, skip it
let mut do_anime = true;
let mut do_slash = true;
let mut do_kb_backlight = true;
for dev in devices.iter() {
if matches!(dev.device, DeviceHandle::Slash(_)) {
do_slash = false;
}
if matches!(dev.device, DeviceHandle::AniMe(_)) {
do_anime = false;
}
if matches!(dev.device, DeviceHandle::Aura(_) | DeviceHandle::OldAura(_)) {
do_kb_backlight = false;
}
}
if do_slash {
if let Ok(dev_type) = DeviceHandle::new_slash_usb().await {
if let DeviceHandle::Slash(slash) = dev_type.clone() {
let path = dbus_path_for_slash();
let ctrl = SlashZbus::new(slash);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path
});
}
} else {
info!("Tested device was not Slash");
}
}
if do_anime {
if let Ok(dev_type) = DeviceHandle::maybe_anime_usb().await {
// TODO: this is copy/pasted
if let DeviceHandle::AniMe(anime) = dev_type.clone() {
let path = dbus_path_for_anime();
let ctrl = AniMeZbus::new(anime);
if ctrl
.start_tasks(connection, path.clone())
.await
.map_err(|e| error!("Failed to start tasks: {e:?}, not adding this device"))
.is_ok()
{
devices.push(AsusDevice {
device: dev_type,
dbus_path: path
});
}
}
} else {
info!("Tested device was not AniMe Matrix");
}
}
if do_kb_backlight {
// TUF AURA LAPTOP DEVICE
// product_name = ASUS TUF Gaming F15 FX507ZE_FX507ZE
// product_family = ASUS TUF Gaming F15
let product_name = DMIID::new().unwrap_or_default().product_name;
let product_family = DMIID::new().unwrap_or_default().product_family;
info!(
"No USB keyboard aura, system is {product_name}, try using sysfs backlight control"
);
if product_name.contains("TUF") || product_family.contains("TUF") {
info!("TUF laptop, try using sysfs backlight control");
if let Ok(dev_type) = DeviceHandle::maybe_laptop_aura(None, "tuf").await {
if let DeviceHandle::Aura(aura) = dev_type.clone() {
let path = dbus_path_for_tuf();
let ctrl = AuraZbus::new(aura);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path
});
}
}
}
}
if let Ok(devs) = &mut Self::init_all_scsi(connection).await {
devices.append(devs);
}
devices
}
pub async fn new(connection: Connection) -> Result<Self, RogError> {
let conn_copy = connection.clone();
let devices = Self::find_all_devices(&conn_copy).await;
info!("Found {} valid devices on startup", devices.len());
let devices = Arc::new(Mutex::new(devices));
let manager = Self {
_dbus_connection: connection
};
// TODO: The /sysfs/ LEDs don't cause events, so they need to be manually
// checked for and added
std::thread::spawn(move || {
let mut monitor = MonitorBuilder::new()?.listen()?;
let mut poll = Poll::new()?;
let mut events = Events::with_capacity(1024);
poll.registry()
.register(&mut monitor, Token(0), Interest::READABLE)?;
let rt = tokio::runtime::Runtime::new().expect("Unable to create Runtime");
let _enter = rt.enter();
loop {
if poll.poll(&mut events, None).is_err() {
continue;
}
for event in monitor.iter() {
let action = event
.action()
.unwrap_or_default()
.to_string_lossy()
.to_string();
let subsys = if let Some(subsys) = event.subsystem() {
subsys.to_string_lossy().to_string()
} else {
continue;
};
let devices = devices.clone();
let conn_copy = conn_copy.clone();
block_on(async move {
// SCSCI devs
if subsys == "block" {
if action == "remove" {
if let Some(serial) =
event.device().property_value("ID_SERIAL_SHORT")
{
let serial = serial.to_string_lossy().to_string();
let path = dbus_path_for_scsi(&serial);
let index = if let Some(index) = devices
.lock()
.await
.iter()
.position(|dev| dev.dbus_path == path)
{
index
} else {
if dev_prop_matches(&event.device(), "ID_VENDOR_ID", "0b05")
{
warn!("No device for dbus path: {path:?}");
}
return Ok(());
};
info!("removing: {path:?}");
let dev = devices.lock().await.remove(index);
let path = path.clone();
if let DeviceHandle::Scsi(_) = dev.device {
conn_copy
.object_server()
.remove::<ScsiZbus, _>(&path)
.await?;
}
}
} else if action == "add" {
let evdev = event.device();
if let Some(serial) = evdev.property_value("ID_SERIAL_SHORT") {
let serial = serial.to_string_lossy().to_string();
let path = dbus_path_for_scsi(&serial);
if let Some(new_devs) =
Self::init_scsi(&conn_copy, &evdev, path).await
{
devices.lock().await.append(&mut vec![new_devs]);
}
}
};
}
if subsys == "hidraw" {
if let Some(parent) =
event.parent_with_subsystem_devtype("usb", "usb_device")?
{
if action == "remove" {
if let Some(path) = dbus_path_for_dev(&parent) {
// Find the indexs of devices matching the path
let removals: Vec<usize> = devices
.lock()
.await
.iter()
.enumerate()
.filter_map(|(i, dev)| {
if dev.dbus_path == path {
Some(i)
} else {
None
}
})
.collect();
if removals.is_empty() {
return Ok(());
}
info!("removing: {path:?}");
// Iter in reverse so as to not screw up indexing
for index in removals.iter().rev() {
let dev = devices.lock().await.remove(*index);
let path = path.clone();
let res = match dev.device {
DeviceHandle::Aura(_) => {
conn_copy
.object_server()
.remove::<AuraZbus, _>(&path)
.await?
}
DeviceHandle::Slash(_) => {
conn_copy
.object_server()
.remove::<SlashZbus, _>(&path)
.await?
}
DeviceHandle::AniMe(_) => {
conn_copy
.object_server()
.remove::<AniMeZbus, _>(&path)
.await?
}
DeviceHandle::Scsi(_) => {
conn_copy
.object_server()
.remove::<ScsiZbus, _>(&path)
.await?
}
_ => todo!()
};
info!("AuraManager removed: {path:?}, {res}");
}
}
} else if action == "add" {
let evdev = event.device();
if let Ok(mut new_devs) =
Self::init_hid_devices(&conn_copy, evdev)
.await
.map_err(|e| error!("Couldn't add new device: {e:?}"))
{
devices.lock().await.append(&mut new_devs);
}
};
}
}
Ok::<(), RogError>(())
})
.map_err(|e| error!("{e:?}"))
.ok();
}
}
// Required for return type on spawn
#[allow(unreachable_code)]
Ok::<(), RogError>(())
});
Ok(manager)
}
}

View File

@@ -0,0 +1,114 @@
use std::collections::BTreeMap;
use config_traits::{StdConfig, StdConfigLoad};
use rog_aura::AuraDeviceType;
use rog_scsi::{AuraEffect, AuraMode};
use serde::{Deserialize, Serialize};
const CONFIG_FILE: &str = "scsi.ron";
/// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug)]
pub struct ScsiConfig {
#[serde(skip)]
pub dev_type: AuraDeviceType,
pub enabled: bool,
pub current_mode: AuraMode,
pub modes: BTreeMap<AuraMode, AuraEffect>
}
impl ScsiConfig {
pub fn get_effect(&mut self, mode: AuraMode) -> Option<&AuraEffect> {
self.modes.get(&mode)
}
pub fn save_effect(&mut self, effect: AuraEffect) {
self.current_mode = effect.mode;
self.modes.insert(*effect.mode(), effect);
}
}
impl Default for ScsiConfig {
fn default() -> Self {
ScsiConfig {
enabled: true,
current_mode: AuraMode::Static,
dev_type: AuraDeviceType::ScsiExtDisk,
modes: BTreeMap::from([
(AuraMode::Off, AuraEffect::default_with_mode(AuraMode::Off)),
(
AuraMode::Static,
AuraEffect::default_with_mode(AuraMode::Static)
),
(
AuraMode::Breathe,
AuraEffect::default_with_mode(AuraMode::Breathe)
),
(
AuraMode::Flashing,
AuraEffect::default_with_mode(AuraMode::Flashing)
),
(
AuraMode::RainbowCycle,
AuraEffect::default_with_mode(AuraMode::RainbowCycle)
),
(
AuraMode::RainbowWave,
AuraEffect::default_with_mode(AuraMode::RainbowWave)
),
(
AuraMode::RainbowCycleBreathe,
AuraEffect::default_with_mode(AuraMode::RainbowCycleBreathe)
),
(
AuraMode::ChaseFade,
AuraEffect::default_with_mode(AuraMode::ChaseFade)
),
(
AuraMode::RainbowCycleChaseFade,
AuraEffect::default_with_mode(AuraMode::RainbowCycleChaseFade)
),
(
AuraMode::Chase,
AuraEffect::default_with_mode(AuraMode::Chase)
),
(
AuraMode::RainbowCycleChase,
AuraEffect::default_with_mode(AuraMode::RainbowCycleChase)
),
(
AuraMode::RainbowCycleWave,
AuraEffect::default_with_mode(AuraMode::RainbowCycleWave)
),
(
AuraMode::RainbowPulseChase,
AuraEffect::default_with_mode(AuraMode::RainbowPulseChase)
),
(
AuraMode::RandomFlicker,
AuraEffect::default_with_mode(AuraMode::RandomFlicker)
),
(
AuraMode::DoubleFade,
AuraEffect::default_with_mode(AuraMode::DoubleFade)
)
])
}
}
}
impl StdConfig for ScsiConfig {
fn new() -> Self {
Self::default()
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
fn config_dir() -> std::path::PathBuf {
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
}
}
impl StdConfigLoad for ScsiConfig {}

View File

@@ -0,0 +1,45 @@
use std::sync::Arc;
use config::ScsiConfig;
use futures_util::lock::{Mutex, MutexGuard};
use rog_scsi::{AuraEffect, Device, Task};
use crate::error::RogError;
pub mod config;
pub mod trait_impls;
#[derive(Clone)]
pub struct ScsiAura {
device: Arc<Mutex<Device>>,
config: Arc<Mutex<ScsiConfig>>
}
impl ScsiAura {
pub fn new(device: Arc<Mutex<Device>>, config: Arc<Mutex<ScsiConfig>>) -> Self {
Self { device, config }
}
pub async fn lock_config(&self) -> MutexGuard<ScsiConfig> {
self.config.lock().await
}
pub async fn write_effect(&self, effect: &AuraEffect) -> Result<(), RogError> {
let tasks: Vec<Task> = effect.into();
for task in &tasks {
self.device.lock().await.perform(task).ok();
}
Ok(())
}
/// Initialise the device if required. Locks the internal config so be wary
/// of deadlocks.
pub async fn do_initialization(&self) -> Result<(), RogError> {
let config = self.config.lock().await;
let mode = config.current_mode;
if let Some(effect) = config.modes.get(&mode) {
self.write_effect(effect).await?;
}
Ok(())
}
}

View File

@@ -0,0 +1,116 @@
use std::collections::BTreeMap;
use config_traits::StdConfig;
use log::error;
use rog_aura::AuraDeviceType;
use rog_scsi::{AuraEffect, AuraMode};
use zbus::fdo::Error as ZbErr;
use zbus::zvariant::OwnedObjectPath;
use zbus::{interface, Connection};
use super::ScsiAura;
use crate::error::RogError;
#[derive(Clone)]
pub struct ScsiZbus(ScsiAura);
impl ScsiZbus {
pub fn new(scsi: ScsiAura) -> Self {
Self(scsi)
}
pub async fn start_tasks(
self,
connection: &Connection,
path: OwnedObjectPath
) -> Result<(), RogError> {
connection
.object_server()
.at(path.clone(), self)
.await
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
.ok();
Ok(())
}
}
#[interface(name = "xyz.ljones.ScsiAura")]
impl ScsiZbus {
/// Return the device type for this Aura keyboard
#[zbus(property)]
async fn device_type(&self) -> AuraDeviceType {
self.0.config.lock().await.dev_type
}
/// Get enabled or not
#[zbus(property)]
async fn enabled(&self) -> bool {
let lock = self.0.lock_config().await;
lock.enabled
}
/// Set enabled true or false
#[zbus(property)]
async fn set_enabled(&self, enabled: bool) {
let mut config = self.0.lock_config().await;
config.enabled = enabled;
config.write();
}
#[zbus(property)]
async fn led_mode(&self) -> u8 {
let config = self.0.lock_config().await;
config.current_mode as u8
}
#[zbus(property)]
async fn set_led_mode(&self, mode: AuraMode) -> Result<(), zbus::Error> {
let mut config = self.0.lock_config().await;
if let Some(effect) = config.get_effect(mode) {
self.0
.write_effect(effect)
.await
.map_err(|e| zbus::Error::Failure(format!("{e:?}")))?;
} else {
return Err(zbus::Error::Failure("Mode data does not exist".to_string()));
}
config.current_mode = mode;
config.write();
Ok(())
}
/// The current mode data
#[zbus(property)]
async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> {
// entirely possible to deadlock here, so use try instead of lock()
if let Some(config) = self.0.config.try_lock() {
let mode = config.current_mode;
match config.modes.get(&mode) {
Some(effect) => Ok(effect.clone()),
None => Err(ZbErr::Failed("Could not get the current effect".into()))
}
} else {
Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
}
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode_data(&mut self, effect: AuraEffect) -> Result<(), ZbErr> {
self.0.write_effect(&effect).await?;
let mut config = self.0.config.lock().await;
config.save_effect(effect);
config.write();
Ok(())
}
/// Get the data set for every mode available
async fn all_mode_data(&self) -> BTreeMap<AuraMode, AuraEffect> {
let config = self.0.config.lock().await;
config.modes.clone()
}
}

View File

@@ -0,0 +1,64 @@
use config_traits::{StdConfig, StdConfigLoad};
use rog_slash::{DeviceState, SlashMode, SlashType};
use serde::{Deserialize, Serialize};
const CONFIG_FILE: &str = "slash.ron";
/// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug)]
pub struct SlashConfig {
#[serde(skip)]
pub slash_type: SlashType,
pub enabled: bool,
pub brightness: u8,
pub display_interval: u8,
pub display_mode: SlashMode,
pub show_on_boot: bool,
pub show_on_shutdown: bool,
pub show_on_sleep: bool,
pub show_on_battery: bool,
pub show_battery_warning: bool
}
impl Default for SlashConfig {
fn default() -> Self {
SlashConfig {
enabled: true,
brightness: 255,
display_interval: 0,
display_mode: SlashMode::Bounce,
slash_type: SlashType::Unsupported,
show_on_boot: true,
show_on_shutdown: true,
show_on_sleep: true,
show_on_battery: true,
show_battery_warning: true
}
}
}
impl StdConfig for SlashConfig {
fn new() -> Self {
Self::default()
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
fn config_dir() -> std::path::PathBuf {
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
}
}
impl StdConfigLoad for SlashConfig {}
impl From<&SlashConfig> for DeviceState {
fn from(config: &SlashConfig) -> Self {
DeviceState {
slash_enabled: config.enabled,
slash_brightness: config.brightness,
slash_interval: config.display_interval,
slash_mode: config.display_mode
}
}
}

View File

@@ -0,0 +1,70 @@
use std::sync::Arc;
use config::SlashConfig;
use futures_util::lock::{Mutex, MutexGuard};
use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw;
use rog_slash::usb::{get_options_packet, pkt_set_mode, pkts_for_init};
use rog_slash::SlashType;
use crate::error::RogError;
pub mod config;
pub mod trait_impls;
#[derive(Debug, Clone)]
pub struct Slash {
hid: Option<Arc<Mutex<HidRaw>>>,
usb: Option<Arc<Mutex<USBRaw>>>,
config: Arc<Mutex<SlashConfig>>
}
impl Slash {
pub fn new(
hid: Option<Arc<Mutex<HidRaw>>>,
usb: Option<Arc<Mutex<USBRaw>>>,
config: Arc<Mutex<SlashConfig>>
) -> Self {
Self { hid, usb, config }
}
pub async fn lock_config(&self) -> MutexGuard<SlashConfig> {
self.config.lock().await
}
pub async fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
if let Some(hid) = &self.hid {
hid.lock().await.write_bytes(message)?;
} else if let Some(usb) = &self.usb {
usb.lock().await.write_bytes(message)?;
}
Ok(())
}
/// Initialise the device if required. Locks the internal config so be wary
/// of deadlocks.
pub async fn do_initialization(&self) -> Result<(), RogError> {
// Don't try to initialise these models as the asus drivers already did
let config = self.config.lock().await;
if !matches!(config.slash_type, SlashType::GA605 | SlashType::GU605) {
for pkt in &pkts_for_init(config.slash_type) {
self.write_bytes(pkt).await?;
}
}
// Apply config upon initialization
let option_packets = get_options_packet(
config.slash_type,
config.enabled,
config.brightness,
config.display_interval
);
self.write_bytes(&option_packets).await?;
let mode_packets = pkt_set_mode(config.slash_type, config.display_mode);
// self.node.write_bytes(&mode_packets[0])?;
self.write_bytes(&mode_packets[1]).await?;
Ok(())
}
}

View File

@@ -0,0 +1,287 @@
use config_traits::StdConfig;
use log::{debug, error, warn};
use rog_slash::usb::{
get_battery_saver_packet, get_boot_packet, get_low_battery_packet, get_options_packet,
get_shutdown_packet, get_sleep_packet, pkt_save, pkt_set_mode
};
use rog_slash::{DeviceState, SlashMode};
use zbus::zvariant::OwnedObjectPath;
use zbus::{interface, Connection};
use super::Slash;
use crate::error::RogError;
use crate::Reloadable;
#[derive(Clone)]
pub struct SlashZbus(Slash);
impl SlashZbus {
pub fn new(slash: Slash) -> Self {
Self(slash)
}
pub async fn start_tasks(
mut self,
connection: &Connection,
path: OwnedObjectPath
) -> Result<(), RogError> {
// let task = zbus.clone();
self.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
connection
.object_server()
.at(path.clone(), self)
.await
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
.ok();
Ok(())
}
}
#[interface(name = "xyz.ljones.Slash")]
impl SlashZbus {
/// Get enabled or not
#[zbus(property)]
async fn enabled(&self) -> bool {
let lock = self.0.lock_config().await;
lock.enabled
}
/// Set enabled true or false
#[zbus(property)]
async fn set_enabled(&self, enabled: bool) {
let mut config = self.0.lock_config().await;
let brightness = if enabled && config.brightness == 0 {
0x88
} else {
config.brightness
};
self.0
.write_bytes(&get_options_packet(
config.slash_type,
enabled,
brightness,
config.display_interval
))
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
config.enabled = enabled;
config.brightness = brightness;
config.write();
}
/// Get brightness level
#[zbus(property)]
async fn brightness(&self) -> u8 {
let config = self.0.lock_config().await;
config.brightness
}
/// Set brightness level
#[zbus(property)]
async fn set_brightness(&self, brightness: u8) {
let mut config = self.0.lock_config().await;
let enabled = brightness > 0;
self.0
.write_bytes(&get_options_packet(
config.slash_type,
enabled,
brightness,
config.display_interval
))
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
config.enabled = enabled;
config.brightness = brightness;
config.write();
}
#[zbus(property)]
async fn interval(&self) -> u8 {
let config = self.0.lock_config().await;
config.display_interval
}
/// Set interval between slash animations (0-255)
#[zbus(property)]
async fn set_interval(&self, interval: u8) {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&get_options_packet(
config.slash_type, config.enabled, config.brightness, interval
))
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
config.display_interval = interval;
config.write();
}
#[zbus(property)]
async fn mode(&self) -> zbus::fdo::Result<u8> {
let config = self.0.lock_config().await;
Ok(config.display_interval)
}
/// Set interval between slash animations (0-255)
#[zbus(property)]
async fn set_mode(&self, mode: SlashMode) -> zbus::Result<()> {
let mut config = self.0.lock_config().await;
let command_packets = pkt_set_mode(config.slash_type, mode);
// self.node.write_bytes(&command_packets[0])?;
self.0.write_bytes(&command_packets[1]).await?;
self.0.write_bytes(&pkt_save(config.slash_type)).await?;
config.display_mode = mode;
config.write();
Ok(())
}
/// Get the device state as stored by asusd
// #[zbus(property)]
async fn device_state(&self) -> DeviceState {
let config = self.0.lock_config().await;
DeviceState::from(&*config)
}
#[zbus(property)]
async fn show_on_boot(&self) -> zbus::fdo::Result<bool> {
let config = self.0.lock_config().await;
Ok(config.show_on_boot)
}
#[zbus(property)]
async fn set_show_on_boot(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&get_boot_packet(config.slash_type, enable))
.await?;
config.show_on_boot = enable;
config.write();
Ok(())
}
#[zbus(property)]
async fn show_on_sleep(&self) -> zbus::fdo::Result<bool> {
let config = self.0.lock_config().await;
Ok(config.show_on_sleep)
}
#[zbus(property)]
async fn set_show_on_sleep(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&get_sleep_packet(config.slash_type, enable))
.await?;
config.show_on_sleep = enable;
config.write();
Ok(())
}
#[zbus(property)]
async fn show_on_shutdown(&self) -> zbus::fdo::Result<bool> {
let config = self.0.lock_config().await;
Ok(config.show_on_shutdown)
}
#[zbus(property)]
async fn set_show_on_shutdown(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&get_shutdown_packet(config.slash_type, enable))
.await?;
config.show_on_shutdown = enable;
config.write();
Ok(())
}
#[zbus(property)]
async fn show_on_battery(&self) -> zbus::fdo::Result<bool> {
let config = self.0.lock_config().await;
Ok(config.show_on_battery)
}
#[zbus(property)]
async fn set_show_on_battery(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&get_battery_saver_packet(config.slash_type, enable))
.await?;
config.show_on_battery = enable;
config.write();
Ok(())
}
#[zbus(property)]
async fn show_battery_warning(&self) -> zbus::fdo::Result<bool> {
let config = self.0.lock_config().await;
Ok(config.show_battery_warning)
}
#[zbus(property)]
async fn set_show_battery_warning(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&get_low_battery_packet(config.slash_type, enable))
.await?;
config.show_battery_warning = enable;
config.write();
Ok(())
}
}
impl Reloadable for SlashZbus {
async fn reload(&mut self) -> Result<(), RogError> {
debug!("reloading slash settings");
let config = self.0.lock_config().await;
self.0
.write_bytes(&get_options_packet(
config.slash_type,
config.enabled,
config.brightness,
config.display_interval
))
.await
.map_err(|err| {
warn!("set_options {}", err);
})
.ok();
macro_rules! write_bytes_with_warning {
($packet_fn:expr, $cfg:ident, $warn_msg:expr) => {
self.0
.write_bytes(&$packet_fn(config.slash_type, config.$cfg))
.await
.map_err(|err| {
warn!("{} {}", $warn_msg, err);
})
.ok();
};
}
write_bytes_with_warning!(get_boot_packet, show_on_boot, "show_on_boot");
write_bytes_with_warning!(get_sleep_packet, show_on_sleep, "show_on_sleep");
write_bytes_with_warning!(get_shutdown_packet, show_on_shutdown, "show_on_shutdown");
write_bytes_with_warning!(get_battery_saver_packet, show_on_battery, "show_on_battery");
write_bytes_with_warning!(
get_low_battery_packet,
show_battery_warning,
"show_battery_warning"
);
Ok(())
}
}

209
asusd/src/aura_types.rs Normal file
View File

@@ -0,0 +1,209 @@
use std::sync::Arc;
use config_traits::{StdConfig, StdConfigLoad};
use futures_util::lock::Mutex;
use log::{debug, error, info};
use rog_anime::error::AnimeError;
use rog_anime::usb::get_anime_type;
use rog_anime::AnimeType;
use rog_aura::AuraDeviceType;
use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardBacklight;
use rog_platform::usb_raw::USBRaw;
use rog_scsi::{open_device, ScsiType};
use rog_slash::error::SlashError;
use rog_slash::SlashType;
use crate::aura_anime::config::AniMeConfig;
use crate::aura_anime::AniMe;
use crate::aura_laptop::config::AuraConfig;
use crate::aura_laptop::Aura;
use crate::aura_scsi::config::ScsiConfig;
use crate::aura_scsi::ScsiAura;
use crate::aura_slash::config::SlashConfig;
use crate::aura_slash::Slash;
use crate::error::RogError;
pub enum _DeviceHandle {
/// The AniMe devices require USBRaw as they are not HID devices
Usb(USBRaw),
HidRaw(HidRaw),
LedClass(KeyboardBacklight),
/// TODO
MulticolourLed,
None
}
#[derive(Clone)]
pub enum DeviceHandle {
Aura(Aura),
Slash(Slash),
/// The AniMe devices require USBRaw as they are not HID devices
AniMe(AniMe),
Scsi(ScsiAura),
Ally(Arc<Mutex<HidRaw>>),
OldAura(Arc<Mutex<HidRaw>>),
/// TUF laptops have an aditional set of attributes added to the LED /sysfs/
TufLedClass(Arc<Mutex<HidRaw>>),
/// TODO
MulticolourLed,
None
}
impl DeviceHandle {
/// Try Slash HID. If one exists it is initialsed and returned.
pub async fn new_slash_hid(
device: Arc<Mutex<HidRaw>>,
prod_id: &str
) -> Result<Self, RogError> {
debug!("Testing for HIDRAW Slash");
let slash_type = SlashType::from_dmi();
if matches!(slash_type, SlashType::Unsupported)
|| slash_type
.prod_id_str()
.to_lowercase()
.trim_start_matches("0x")
!= prod_id
{
log::info!("Unknown or invalid slash: {prod_id:?}, skipping");
return Err(RogError::NotFound("No slash device".to_string()));
}
info!("Found slash type {slash_type:?}: {prod_id}");
let mut config = SlashConfig::new().load();
config.slash_type = slash_type;
let slash = Slash::new(Some(device), None, Arc::new(Mutex::new(config)));
slash.do_initialization().await?;
Ok(Self::Slash(slash))
}
/// Try Slash USB. If one exists it is initialsed and returned.
pub async fn new_slash_usb() -> Result<Self, RogError> {
debug!("Testing for USB Slash");
let slash_type = SlashType::from_dmi();
if matches!(slash_type, SlashType::Unsupported) {
return Err(RogError::Slash(SlashError::NoDevice));
}
if let Ok(usb) = USBRaw::new(slash_type.prod_id()) {
info!("Found Slash USB {slash_type:?}");
let mut config = SlashConfig::new().load();
config.slash_type = slash_type;
let slash = Slash::new(
None,
Some(Arc::new(Mutex::new(usb))),
Arc::new(Mutex::new(config))
);
slash.do_initialization().await?;
Ok(Self::Slash(slash))
} else {
Err(RogError::NotFound("No slash device found".to_string()))
}
}
/// Try AniMe Matrix HID. If one exists it is initialsed and returned.
pub async fn maybe_anime_hid(
_device: Arc<Mutex<HidRaw>>,
_prod_id: &str
) -> Result<Self, RogError> {
// TODO: can't use HIDRAW for anime at the moment
Err(RogError::NotFound(
"Can't use anime over hidraw yet. Skip.".to_string()
))
// debug!("Testing for HIDRAW AniMe");
// let anime_type = AnimeType::from_dmi();
// dbg!(prod_id);
// if matches!(anime_type, AnimeType::Unsupported) || prod_id != "193b"
// { log::info!("Unknown or invalid AniMe: {prod_id:?},
// skipping"); return Err(RogError::NotFound("No
// anime-matrix device".to_string())); }
// info!("Found AniMe Matrix HIDRAW {anime_type:?}: {prod_id}");
// let mut config = AniMeConfig::new().load();
// config.anime_type = anime_type;
// let mut anime = AniMe::new(Some(device), None,
// Arc::new(Mutex::new(config))); anime.do_initialization().
// await?; Ok(Self::AniMe(anime))
}
pub async fn maybe_anime_usb() -> Result<Self, RogError> {
debug!("Testing for USB AniMe");
let anime_type = get_anime_type();
if matches!(anime_type, AnimeType::Unsupported) {
info!("No Anime Matrix capable laptop found");
return Err(RogError::Anime(AnimeError::NoDevice));
}
if let Ok(usb) = USBRaw::new(0x193b) {
info!("Found AniMe Matrix USB {anime_type:?}");
let mut config = AniMeConfig::new().load();
config.anime_type = anime_type;
let mut anime = AniMe::new(
None,
Some(Arc::new(Mutex::new(usb))),
Arc::new(Mutex::new(config))
);
anime.do_initialization().await?;
Ok(Self::AniMe(anime))
} else {
Err(RogError::NotFound(
"No AnimeMatrix device found".to_string()
))
}
}
pub async fn maybe_scsi(dev_node: &str, prod_id: &str) -> Result<Self, RogError> {
debug!("Testing for SCSI");
let prod_id = ScsiType::from(prod_id);
if prod_id == ScsiType::Unsupported {
log::info!("Unknown or invalid SCSI: {prod_id:?}, skipping");
return Err(RogError::NotFound("No SCSI device".to_string()));
}
info!("Found SCSI device {prod_id:?} on {dev_node}");
let mut config = ScsiConfig::new().load();
config.dev_type = AuraDeviceType::ScsiExtDisk;
let dev = Arc::new(Mutex::new(open_device(dev_node)?));
let scsi = ScsiAura::new(dev, Arc::new(Mutex::new(config)));
scsi.do_initialization().await?;
Ok(Self::Scsi(scsi))
}
pub async fn maybe_laptop_aura(
device: Option<Arc<Mutex<HidRaw>>>,
prod_id: &str
) -> Result<Self, RogError> {
debug!("Testing for laptop aura");
let aura_type = AuraDeviceType::from(prod_id);
if !matches!(
aura_type,
AuraDeviceType::LaptopKeyboard2021
| AuraDeviceType::LaptopKeyboardPre2021
| AuraDeviceType::LaptopKeyboardTuf
) {
log::info!("Unknown or invalid laptop aura: {prod_id:?}, skipping");
return Err(RogError::NotFound("No laptop aura device".to_string()));
}
info!("Found laptop aura type {prod_id:?}");
let backlight = KeyboardBacklight::new()
.map_err(|e| error!("Keyboard backlight error: {e:?}"))
.map_or(None, |k| {
info!("Found sysfs backlight control");
Some(Arc::new(Mutex::new(k)))
});
let mut config = AuraConfig::load_and_update_config(prod_id);
config.led_type = aura_type;
let aura = Aura {
hid: device,
backlight,
config: Arc::new(Mutex::new(config))
};
aura.do_initialization().await?;
Ok(Self::Aura(aura))
}
}

View File

@@ -1,85 +1,88 @@
use config_traits::{StdConfig, StdConfigLoad3};
use std::collections::HashMap;
use config_traits::{StdConfig, StdConfigLoad1};
use rog_platform::asus_armoury::FirmwareAttribute;
use rog_platform::cpu::CPUEPP;
use rog_platform::platform::ThrottlePolicy;
use serde_derive::{Deserialize, Serialize};
use rog_platform::platform::PlatformProfile;
use serde::{Deserialize, Serialize};
const CONFIG_FILE: &str = "asusd.ron";
#[derive(Deserialize, Serialize, Debug, PartialEq, PartialOrd)]
#[derive(Default, Clone, Deserialize, Serialize, PartialEq)]
pub struct Tuning {
pub enabled: bool,
pub group: HashMap<FirmwareAttribute, i32>
}
type Tunings = HashMap<PlatformProfile, Tuning>;
#[derive(Deserialize, Serialize, PartialEq)]
pub struct Config {
/// Save charge limit for restoring on boot/resume
// The current charge limit applied
pub charge_control_end_threshold: u8,
pub panel_od: bool,
pub boot_sound: bool,
pub mini_led_mode: bool,
/// Save charge limit for restoring
#[serde(skip)]
pub base_charge_control_end_threshold: u8,
pub disable_nvidia_powerd_on_battery: bool,
/// An optional command/script to run when power is changed to AC
pub ac_command: String,
/// An optional command/script to run when power is changed to battery
pub bat_command: String,
/// Set true if energy_performance_preference should be set if the
/// throttle/platform profile is changed
pub throttle_policy_linked_epp: bool,
/// Which throttle/profile to use on battery power
pub throttle_policy_on_battery: ThrottlePolicy,
/// Which throttle/profile to use on AC power
pub throttle_policy_on_ac: ThrottlePolicy,
/// The energy_performance_preference for this throttle/platform profile
pub throttle_quiet_epp: CPUEPP,
/// The energy_performance_preference for this throttle/platform profile
pub throttle_balanced_epp: CPUEPP,
/// The energy_performance_preference for this throttle/platform profile
pub throttle_performance_epp: CPUEPP,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_pl1_spl: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_pl2_sppt: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_fppt: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_apu_sppt: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_platform_sppt: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_dynamic_boost: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_temp_target: Option<u8>,
/// platform profile is changed
pub platform_profile_linked_epp: bool,
/// Which platform profile to use on battery power
pub platform_profile_on_battery: PlatformProfile,
/// Should the throttle policy be set on bat/ac change?
pub change_platform_profile_on_battery: bool,
/// Which platform profile to use on AC power
pub platform_profile_on_ac: PlatformProfile,
/// Should the platform profile be set on bat/ac change?
pub change_platform_profile_on_ac: bool,
/// The energy_performance_preference for this platform profile
pub profile_quiet_epp: CPUEPP,
/// The energy_performance_preference for this platform profile
pub profile_balanced_epp: CPUEPP,
/// The energy_performance_preference for this platform profile
pub profile_performance_epp: CPUEPP,
pub ac_profile_tunings: Tunings,
pub dc_profile_tunings: Tunings,
pub armoury_settings: HashMap<FirmwareAttribute, i32>,
/// Temporary state for AC/Batt
#[serde(skip)]
pub last_power_plugged: u8,
pub last_power_plugged: u8
}
impl Config {
pub fn select_tunings(&mut self, power_plugged: bool, profile: PlatformProfile) -> &mut Tuning {
let config = if power_plugged {
&mut self.ac_profile_tunings
} else {
&mut self.dc_profile_tunings
};
config.entry(profile).or_insert_with(Tuning::default)
}
}
impl Default for Config {
fn default() -> Self {
Self {
charge_control_end_threshold: 100,
panel_od: false,
boot_sound: false,
mini_led_mode: false,
base_charge_control_end_threshold: 100,
disable_nvidia_powerd_on_battery: true,
ac_command: Default::default(),
bat_command: Default::default(),
throttle_policy_linked_epp: true,
throttle_policy_on_battery: ThrottlePolicy::Quiet,
throttle_policy_on_ac: ThrottlePolicy::Performance,
throttle_quiet_epp: CPUEPP::Power,
throttle_balanced_epp: CPUEPP::BalancePower,
throttle_performance_epp: CPUEPP::Performance,
ppt_pl1_spl: Default::default(),
ppt_pl2_sppt: Default::default(),
ppt_fppt: Default::default(),
ppt_apu_sppt: Default::default(),
ppt_platform_sppt: Default::default(),
nv_dynamic_boost: Default::default(),
nv_temp_target: Default::default(),
last_power_plugged: Default::default(),
platform_profile_linked_epp: true,
platform_profile_on_battery: PlatformProfile::Quiet,
change_platform_profile_on_battery: true,
platform_profile_on_ac: PlatformProfile::Performance,
change_platform_profile_on_ac: true,
profile_quiet_epp: CPUEPP::Power,
profile_balanced_epp: CPUEPP::BalancePower,
profile_performance_epp: CPUEPP::Performance,
ac_profile_tunings: HashMap::default(),
dc_profile_tunings: HashMap::default(),
armoury_settings: HashMap::default(),
last_power_plugged: Default::default()
}
}
}
@@ -89,8 +92,8 @@ impl StdConfig for Config {
Config {
charge_control_end_threshold: 100,
disable_nvidia_powerd_on_battery: true,
throttle_policy_on_battery: ThrottlePolicy::Quiet,
throttle_policy_on_ac: ThrottlePolicy::Performance,
platform_profile_on_battery: PlatformProfile::Quiet,
platform_profile_on_ac: PlatformProfile::Performance,
ac_command: String::new(),
bat_command: String::new(),
..Default::default()
@@ -106,130 +109,68 @@ impl StdConfig for Config {
}
}
impl StdConfigLoad3<Config472, Config506, Config507> for Config {}
impl StdConfigLoad1<Config601> for Config {}
#[derive(Deserialize, Serialize)]
pub struct Config507 {
/// Save charge limit for restoring on boot
pub struct Config601 {
pub charge_control_end_threshold: u8,
pub panel_od: bool,
pub mini_led_mode: bool,
pub disable_nvidia_powerd_on_battery: bool,
pub ac_command: String,
pub bat_command: String,
pub platform_policy_linked_epp: bool,
pub platform_policy_on_battery: ThrottlePolicy,
pub platform_policy_on_ac: ThrottlePolicy,
//
pub ppt_pl1_spl: Option<u8>,
pub ppt_pl2_sppt: Option<u8>,
pub ppt_fppt: Option<u8>,
pub ppt_apu_sppt: Option<u8>,
pub ppt_platform_sppt: Option<u8>,
pub nv_dynamic_boost: Option<u8>,
pub nv_temp_target: Option<u8>,
}
impl From<Config507> for Config {
fn from(c: Config507) -> Self {
Self {
charge_control_end_threshold: c.charge_control_end_threshold,
panel_od: c.panel_od,
boot_sound: false,
disable_nvidia_powerd_on_battery: c.disable_nvidia_powerd_on_battery,
ac_command: c.ac_command,
bat_command: c.bat_command,
mini_led_mode: c.mini_led_mode,
throttle_policy_linked_epp: true,
throttle_policy_on_battery: c.platform_policy_on_battery,
throttle_policy_on_ac: c.platform_policy_on_ac,
throttle_quiet_epp: CPUEPP::Power,
throttle_balanced_epp: CPUEPP::BalancePower,
throttle_performance_epp: CPUEPP::Performance,
ppt_pl1_spl: c.ppt_pl1_spl,
ppt_pl2_sppt: c.ppt_pl2_sppt,
ppt_fppt: c.ppt_fppt,
ppt_apu_sppt: c.ppt_apu_sppt,
ppt_platform_sppt: c.ppt_platform_sppt,
nv_dynamic_boost: c.nv_dynamic_boost,
nv_temp_target: c.nv_temp_target,
last_power_plugged: 0,
}
}
}
#[derive(Deserialize, Serialize)]
pub struct Config506 {
/// Save charge limit for restoring on boot
pub charge_control_end_threshold: u8,
pub panel_od: bool,
pub mini_led_mode: bool,
pub disable_nvidia_powerd_on_battery: bool,
pub ac_command: String,
pub bat_command: String,
/// Restored on boot as well as when power is plugged
#[serde(skip)]
pub platform_policy_to_restore: ThrottlePolicy,
pub platform_policy_on_battery: ThrottlePolicy,
pub platform_policy_on_ac: ThrottlePolicy,
//
pub ppt_pl1_spl: Option<u8>,
pub ppt_pl2_sppt: Option<u8>,
pub ppt_fppt: Option<u8>,
pub ppt_apu_sppt: Option<u8>,
pub ppt_platform_sppt: Option<u8>,
pub nv_dynamic_boost: Option<u8>,
pub nv_temp_target: Option<u8>,
}
impl From<Config506> for Config {
fn from(c: Config506) -> Self {
Self {
charge_control_end_threshold: c.charge_control_end_threshold,
panel_od: c.panel_od,
boot_sound: false,
disable_nvidia_powerd_on_battery: c.disable_nvidia_powerd_on_battery,
ac_command: c.ac_command,
bat_command: c.bat_command,
mini_led_mode: c.mini_led_mode,
throttle_policy_linked_epp: true,
throttle_policy_on_battery: c.platform_policy_on_battery,
throttle_policy_on_ac: c.platform_policy_on_ac,
throttle_quiet_epp: CPUEPP::Power,
throttle_balanced_epp: CPUEPP::BalancePower,
throttle_performance_epp: CPUEPP::Performance,
ppt_pl1_spl: c.ppt_pl1_spl,
ppt_pl2_sppt: c.ppt_pl2_sppt,
ppt_fppt: c.ppt_fppt,
ppt_apu_sppt: c.ppt_apu_sppt,
ppt_platform_sppt: c.ppt_platform_sppt,
nv_dynamic_boost: c.nv_dynamic_boost,
nv_temp_target: c.nv_temp_target,
last_power_plugged: 0,
}
}
}
#[derive(Deserialize, Serialize)]
pub struct Config472 {
/// Save charge limit for restoring on boot
pub bat_charge_limit: u8,
pub base_charge_control_end_threshold: u8,
pub panel_od: bool,
pub boot_sound: bool,
pub mini_led_mode: bool,
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_performance_epp: CPUEPP,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_pl1_spl: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_pl2_sppt: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_pl3_fppt: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_fppt: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_apu_sppt: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_platform_sppt: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_dynamic_boost: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_temp_target: Option<u8>,
#[serde(skip)]
pub last_power_plugged: u8
}
impl From<Config472> for Config {
fn from(c: Config472) -> Self {
impl From<Config601> for Config {
fn from(c: Config601) -> Self {
Self {
charge_control_end_threshold: c.bat_charge_limit,
panel_od: c.panel_od,
disable_nvidia_powerd_on_battery: true,
// 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,
..Default::default()
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,
last_power_plugged: c.last_power_plugged,
ac_profile_tunings: HashMap::default(),
dc_profile_tunings: HashMap::default(),
armoury_settings: HashMap::default()
}
}
}

View File

@@ -1,229 +0,0 @@
use std::time::Duration;
use config_traits::{StdConfig, StdConfigLoad2};
use rog_anime::error::AnimeError;
use rog_anime::usb::Brightness;
use rog_anime::{
ActionData, ActionLoader, AnimTime, Animations, AnimeType, DeviceState, Fade, Vec2,
};
use serde_derive::{Deserialize, Serialize};
const CONFIG_FILE: &str = "anime.ron";
#[derive(Deserialize, Serialize)]
pub struct AnimeConfigV460 {
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
pub sleep: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>,
pub brightness: f32,
}
impl From<AnimeConfigV460> for AnimeConfig {
fn from(c: AnimeConfigV460) -> AnimeConfig {
AnimeConfig {
system: c.system,
boot: c.boot,
wake: c.wake,
shutdown: c.shutdown,
..Default::default()
}
}
}
#[derive(Deserialize, Serialize, Debug)]
pub struct AnimeConfigV472 {
pub model_override: Option<AnimeType>,
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
pub sleep: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>,
pub brightness: f32,
pub display_enabled: bool,
pub display_brightness: Brightness,
pub builtin_anims_enabled: bool,
pub builtin_anims: Animations,
}
impl From<AnimeConfigV472> for AnimeConfig {
fn from(c: AnimeConfigV472) -> AnimeConfig {
AnimeConfig {
system: c.system,
boot: c.boot,
wake: c.wake,
shutdown: c.shutdown,
model_override: c.model_override,
display_enabled: c.display_enabled,
display_brightness: c.display_brightness,
builtin_anims_enabled: c.builtin_anims_enabled,
builtin_anims: c.builtin_anims,
..Default::default()
}
}
}
#[derive(Deserialize, Serialize, Default)]
pub struct AnimeConfigCached {
pub system: Vec<ActionData>,
pub boot: Vec<ActionData>,
pub wake: Vec<ActionData>,
pub shutdown: Vec<ActionData>,
}
impl AnimeConfigCached {
pub fn init_from_config(
&mut self,
config: &AnimeConfig,
anime_type: AnimeType,
) -> Result<(), AnimeError> {
let mut sys = Vec::with_capacity(config.system.len());
for ani in &config.system {
sys.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.system = sys;
let mut boot = Vec::with_capacity(config.boot.len());
for ani in &config.boot {
boot.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.boot = boot;
let mut wake = Vec::with_capacity(config.wake.len());
for ani in &config.wake {
wake.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.wake = wake;
let mut shutdown = Vec::with_capacity(config.shutdown.len());
for ani in &config.shutdown {
shutdown.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.shutdown = shutdown;
Ok(())
}
}
/// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct AnimeConfig {
pub model_override: Option<AnimeType>,
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>,
// pub brightness: f32,
pub display_enabled: bool,
pub display_brightness: Brightness,
pub builtin_anims_enabled: bool,
pub off_when_unplugged: bool,
pub off_when_suspended: bool,
pub off_when_lid_closed: bool,
pub brightness_on_battery: Brightness,
pub builtin_anims: Animations,
}
impl Default for AnimeConfig {
fn default() -> Self {
AnimeConfig {
model_override: None,
system: Vec::new(),
boot: Vec::new(),
wake: Vec::new(),
shutdown: Vec::new(),
// brightness: 1.0,
display_enabled: true,
display_brightness: Brightness::Med,
builtin_anims_enabled: true,
off_when_unplugged: true,
off_when_suspended: true,
off_when_lid_closed: true,
brightness_on_battery: Brightness::Low,
builtin_anims: Animations::default(),
}
}
}
impl StdConfig for AnimeConfig {
fn new() -> Self {
Self::create_default()
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
fn config_dir() -> std::path::PathBuf {
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
}
}
impl StdConfigLoad2<AnimeConfigV460, AnimeConfigV472> for AnimeConfig {}
impl From<&AnimeConfig> for DeviceState {
fn from(config: &AnimeConfig) -> Self {
DeviceState {
display_enabled: config.display_enabled,
display_brightness: config.display_brightness,
builtin_anims_enabled: config.builtin_anims_enabled,
builtin_anims: config.builtin_anims,
off_when_unplugged: config.off_when_unplugged,
off_when_suspended: config.off_when_suspended,
off_when_lid_closed: config.off_when_lid_closed,
brightness_on_battery: config.brightness_on_battery,
}
}
}
impl AnimeConfig {
// fn clamp_config_brightness(mut config: &mut AnimeConfig) {
// if config.brightness < 0.0 || config.brightness > 1.0 {
// warn!(
// "Clamped brightness to [0.0 ; 1.0], was {}",
// config.brightness
// );
// config.brightness = f32::max(0.0, f32::min(1.0, config.brightness));
// }
// }
fn create_default() -> Self {
// create a default config here
AnimeConfig {
system: vec![],
boot: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
brightness: 1.0,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(2)),
Duration::from_secs(2),
)),
}],
wake: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
brightness: 1.0,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(2)),
Duration::from_secs(2),
)),
}],
shutdown: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
scale: 0.9,
angle: 0.0,
translation: Vec2::new(3.0, 2.0),
brightness: 1.0,
time: AnimTime::Infinite,
}],
..Default::default()
}
}
}

View File

@@ -1,292 +0,0 @@
pub mod config;
/// Implements `CtrlTask`, Reloadable, `ZbusRun`
pub mod trait_impls;
use std::convert::TryFrom;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::sleep;
use ::zbus::export::futures_util::lock::Mutex;
use config_traits::{StdConfig, StdConfigLoad2};
use log::{error, info, warn};
use rog_anime::error::AnimeError;
use rog_anime::usb::{
get_anime_type, pkt_flush, pkt_set_brightness, pkt_set_enable_display,
pkt_set_enable_powersave_anim, pkts_for_init, Brightness,
};
use rog_anime::{ActionData, AnimeDataBuffer, AnimePacketType, AnimeType};
use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw;
use self::config::{AnimeConfig, AnimeConfigCached};
use crate::error::RogError;
enum Node {
Usb(USBRaw),
Hid(HidRaw),
}
impl Node {
pub fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
// TODO: map and pass on errors
match self {
Node::Usb(u) => {
u.write_bytes(message).ok();
}
Node::Hid(h) => {
h.write_bytes(message).ok();
}
}
Ok(())
}
pub fn set_builtins_enabled(&self, enabled: bool, bright: Brightness) -> Result<(), RogError> {
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))?;
self.write_bytes(&pkt_set_enable_display(enabled))?;
self.write_bytes(&pkt_set_brightness(bright))?;
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
}
}
pub struct CtrlAnime {
// node: HidRaw,
node: Node,
anime_type: AnimeType,
cache: AnimeConfigCached,
config: AnimeConfig,
// set to force thread to exit
thread_exit: Arc<AtomicBool>,
// Set to false when the thread exits
thread_running: Arc<AtomicBool>,
}
impl CtrlAnime {
#[inline]
pub fn new() -> Result<CtrlAnime, RogError> {
let usb = USBRaw::new(0x193b).ok();
let hid = HidRaw::new("193b").ok();
let node = if usb.is_some() {
info!("Anime using the USB interface");
unsafe { Node::Usb(usb.unwrap_unchecked()) }
} else if hid.is_some() {
info!("Anime using the HID interface");
unsafe { Node::Hid(hid.unwrap_unchecked()) }
} else {
return Err(RogError::Anime(AnimeError::NoDevice));
};
// TODO: something better to set wakeups disabled
// if matches!(node, Node::Usb(_)) {
// if let Ok(mut enumerator) = udev::Enumerator::new() {
// enumerator.match_subsystem("usb").ok();
// enumerator.match_attribute("idProduct", "193b").ok();
// if let Ok(mut enumer) = enumerator.scan_devices() {
// if let Some(mut dev) = enumer.next() {
// dev.set_attribute_value("power/wakeup", "disabled").ok();
// }
// }
// }
// }
let config = AnimeConfig::new().load();
let mut anime_type = get_anime_type()?;
if let AnimeType::Unknown = anime_type {
if let Some(model) = config.model_override {
warn!("Overriding the Animatrix type as {model:?}");
anime_type = model;
}
}
info!("Device has an AniMe Matrix display: {anime_type:?}");
let mut cache = AnimeConfigCached::default();
cache.init_from_config(&config, anime_type)?;
let ctrl = CtrlAnime {
node,
anime_type,
cache,
config,
thread_exit: Arc::new(AtomicBool::new(false)),
thread_running: Arc::new(AtomicBool::new(false)),
};
ctrl.do_initialization()?;
Ok(ctrl)
}
// let device = CtrlAnime::get_device(0x0b05, 0x193b)?;
/// Start an action thread. This is classed as a singleton and there should
/// be only one running - so the thread uses atomics to signal run/exit.
///
/// Because this also writes to the usb device, other write tries (display
/// only) *must* get the mutex lock and set the `thread_exit` atomic.
async fn run_thread(inner: Arc<Mutex<CtrlAnime>>, actions: Vec<ActionData>, mut once: bool) {
if actions.is_empty() {
warn!("AniMe system actions was empty");
return;
}
if let Some(lock) = inner.try_lock() {
lock.node
.write_bytes(&pkt_set_enable_powersave_anim(false))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
}
// Loop rules:
// - Lock the mutex **only when required**. That is, the lock must be held for
// the shortest duration possible.
// - An AtomicBool used for thread exit should be checked in every loop,
// including nested
// The only reason for this outer thread is to prevent blocking while waiting
// for the next spawned thread to exit
// TODO: turn this in to async task (maybe? COuld still risk blocking main
// thread)
std::thread::Builder::new()
.name("AniMe system thread start".into())
.spawn(move || {
info!("AniMe new system thread started");
// Getting copies of these Atomics is done *in* the thread to ensure
// we don't block other threads/main
let thread_exit;
let thread_running;
let anime_type;
loop {
if let Some(lock) = inner.try_lock() {
thread_exit = lock.thread_exit.clone();
thread_running = lock.thread_running.clone();
anime_type = lock.anime_type;
break;
}
}
// First two loops are to ensure we *do* aquire a lock on the mutex
// The reason the loop is required is because the USB writes can block
// for up to 10ms. We can't fail to get the atomics.
while thread_running.load(Ordering::SeqCst) {
// Make any running loop exit first
thread_exit.store(true, Ordering::SeqCst);
}
info!("AniMe no previous system thread running (now)");
thread_exit.store(false, Ordering::SeqCst);
thread_running.store(true, Ordering::SeqCst);
'main: loop {
for action in &actions {
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
match action {
ActionData::Animation(frames) => {
rog_anime::run_animation(frames, &|frame| {
if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: animation sub-loop was asked to exit");
return Ok(true); // Do safe exit
}
inner
.try_lock()
.map(|lock| {
lock.write_data_buffer(frame)
.map_err(|err| {
warn!(
"rog_anime::run_animation:callback {}",
err
);
})
.ok();
false // Don't exit yet
})
.map_or_else(
|| {
warn!("rog_anime::run_animation:callback failed");
Err(AnimeError::NoFrames)
},
Ok,
)
});
if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: sub-loop exited and main loop exiting now");
break 'main;
}
}
ActionData::Image(image) => {
once = false;
if let Some(lock) = inner.try_lock() {
lock.write_data_buffer(image.as_ref().clone())
.map_err(|e| error!("{}", e))
.ok();
}
}
ActionData::Pause(duration) => sleep(*duration),
ActionData::AudioEq
| ActionData::SystemInfo
| ActionData::TimeDate
| ActionData::Matrix => {}
}
}
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
if once || actions.is_empty() {
break 'main;
}
}
// Clear the display on exit
if let Some(lock) = inner.try_lock() {
if let Ok(data) =
AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()])
.map_err(|e| error!("{}", e))
{
lock.write_data_buffer(data)
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
}
lock.node
.write_bytes(&pkt_set_enable_powersave_anim(
lock.config.builtin_anims_enabled,
))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
}
// Loop ended, set the atmonics
thread_running.store(false, Ordering::SeqCst);
info!("AniMe system thread exited");
})
.map(|err| info!("AniMe system thread: {:?}", err))
.ok();
}
/// Write only a data packet. This will modify the leds brightness using the
/// global brightness set in config.
fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) -> Result<(), RogError> {
for led in buffer.data_mut().iter_mut() {
let mut bright = *led as f32;
if bright > 254.0 {
bright = 254.0;
}
*led = bright as u8;
}
let data = AnimePacketType::try_from(buffer)?;
for row in &data {
self.node.write_bytes(row)?;
}
self.node.write_bytes(&pkt_flush())?;
Ok(())
}
fn do_initialization(&self) -> Result<(), RogError> {
let pkts = pkts_for_init();
self.node.write_bytes(&pkts[0])?;
self.node.write_bytes(&pkts[1])?;
Ok(())
}
}

View File

@@ -1,470 +0,0 @@
use std::collections::{BTreeMap, HashSet};
use config_traits::{StdConfig, StdConfigLoad};
use dmi_id::DMIID;
use inotify::Inotify;
use log::{debug, info, warn};
use rog_aura::aura_detection::LedSupportData;
use rog_aura::keyboard::{LedUsbPackets, UsbPackets};
use rog_aura::usb::{LED_APPLY, LED_SET};
use rog_aura::{
AuraDeviceType, AuraEffect, Direction, LedBrightness, Speed, GRADIENT, LED_MSG_LEN,
};
use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardLed;
use zbus::zvariant::OwnedObjectPath;
use super::config::AuraConfig;
use crate::ctrl_aura::manager::{dbus_path_for_dev, dbus_path_for_tuf};
use crate::error::RogError;
#[derive(Debug)]
pub enum LEDNode {
/// Brightness and/or TUF RGB controls
KbdLed(KeyboardLed),
/// Raw HID handle
Rog(KeyboardLed, HidRaw),
}
impl LEDNode {
// TODO: move various methods upwards to this
pub fn set_brightness(&self, value: u8) -> Result<(), RogError> {
match self {
LEDNode::KbdLed(k) => k.set_brightness(value)?,
LEDNode::Rog(k, _) => k.set_brightness(value)?,
}
Ok(())
}
pub fn get_brightness(&self) -> Result<u8, RogError> {
Ok(match self {
LEDNode::KbdLed(k) => k.get_brightness()?,
LEDNode::Rog(k, _) => k.get_brightness()?,
})
}
pub fn monitor_brightness(&self) -> Result<Inotify, RogError> {
Ok(match self {
LEDNode::KbdLed(k) => k.monitor_brightness()?,
LEDNode::Rog(k, _) => k.monitor_brightness()?,
})
}
}
/// Individual controller for one Aura device
pub struct CtrlKbdLed {
pub led_type: AuraDeviceType,
pub led_node: LEDNode,
pub supported_data: LedSupportData, // TODO: is storing this really required?
pub per_key_mode_active: bool,
pub config: AuraConfig,
pub dbus_path: OwnedObjectPath,
}
impl CtrlKbdLed {
pub fn find_all() -> Result<Vec<Self>, RogError> {
info!("Searching for all Aura devices");
let mut devices = Vec::new();
let mut found = HashSet::new(); // track and ensure we use only one hidraw per prod_id
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
err
})?;
enumerator.match_subsystem("hidraw").map_err(|err| {
warn!("{}", err);
err
})?;
for end_point in enumerator.scan_devices()? {
// usb_device gives us a product and vendor ID
if let Some(usb_device) =
end_point.parent_with_subsystem_devtype("usb", "usb_device")?
{
// The asus_wmi driver latches MCU that controls the USB endpoints
if let Some(parent) = end_point.parent() {
if let Some(driver) = parent.driver() {
// There is a tree of devices added so filter by driver
if driver != "asus" {
continue;
}
} else {
continue;
}
}
// Device is something like 002, while its parent is the MCU
// Think of it like the device is an endpoint of the USB device attached
let mut prod_id = String::new();
if let Some(usb_id) = usb_device.attribute_value("idProduct") {
prod_id = usb_id.to_string_lossy().to_string();
let aura_dev = AuraDeviceType::from(prod_id.as_str());
if aura_dev == AuraDeviceType::Unknown || found.contains(&aura_dev) {
log::debug!("Unknown or invalid device: {usb_id:?}, skipping");
continue;
}
found.insert(aura_dev);
}
let dev_node = if let Some(dev_node) = usb_device.devnode() {
dev_node
} else {
debug!("Device has no devnode, skipping");
continue;
};
info!("AuraControl found device at: {:?}", dev_node);
let dbus_path = dbus_path_for_dev(&usb_device).unwrap_or_default();
let dev = HidRaw::from_device(end_point)?;
let mut dev = Self::from_hidraw(dev, dbus_path)?;
dev.config = Self::init_config(&prod_id);
devices.push(dev);
}
}
// Check for a TUF laptop LED. Assume there is only ever one.
if let Ok(kbd_backlight) = KeyboardLed::new() {
if kbd_backlight.has_kbd_rgb_mode() {
// Extra sure double-check that this isn't a laptop with crap
// ACPI with borked return on the TUF rgb methods
let dmi = DMIID::new().unwrap_or_default();
info!("Found a TUF with product family: {}", dmi.product_family);
info!("and board name: {}", dmi.board_name);
if dmi.product_family.contains("TUF") {
info!("AuraControl found a TUF laptop keyboard");
let ctrl = CtrlKbdLed {
led_type: AuraDeviceType::LaptopTuf,
led_node: LEDNode::KbdLed(kbd_backlight),
supported_data: LedSupportData::get_data("tuf"),
per_key_mode_active: false,
config: Self::init_config("tuf"),
dbus_path: dbus_path_for_tuf(),
};
devices.push(ctrl);
}
}
} else {
let dmi = DMIID::new().unwrap_or_default();
warn!("No asus::kbd_backlight found for {} ??", dmi.product_family);
}
info!("Found {} Aura devices", devices.len());
Ok(devices)
}
/// The generated data from this function has a default config. This config
/// should be overwritten. The reason for the default config is because
/// of async issues between this and udev/hidraw
pub fn from_hidraw(device: HidRaw, dbus_path: OwnedObjectPath) -> Result<Self, RogError> {
let rgb_led = KeyboardLed::new()?;
let prod_id = AuraDeviceType::from(device.prod_id());
if prod_id == AuraDeviceType::Unknown {
log::error!("{} is AuraDevice::Unknown", device.prod_id());
return Err(RogError::NoAuraNode);
}
// New loads data from the DB also
// let config = Self::init_config(prod_id, data);
let data = LedSupportData::get_data(device.prod_id());
let ctrl = CtrlKbdLed {
led_type: prod_id,
led_node: LEDNode::Rog(rgb_led, device),
supported_data: data.clone(),
per_key_mode_active: false,
config: AuraConfig::default(),
dbus_path,
};
Ok(ctrl)
}
pub fn init_config(prod_id: &str) -> AuraConfig {
// New loads data from the DB also
let mut config_init = AuraConfig::new(prod_id);
// config_init.set_filename(prod_id);
let mut config_loaded = config_init.clone().load();
// update the initialised data with what we loaded from disk
for mode in &mut config_init.builtins {
// update init values from loaded values if they exist
if let Some(loaded) = config_loaded.builtins.get(mode.0) {
*mode.1 = loaded.clone();
}
}
// Then replace just incase the initialised data contains new modes added
config_loaded.builtins = config_init.builtins;
if let (Some(mut multizone_init), Some(multizone_loaded)) =
(config_init.multizone, config_loaded.multizone.as_mut())
{
for mode in multizone_init.iter_mut() {
// update init values from loaded values if they exist
if let Some(loaded) = multizone_loaded.get(mode.0) {
let mut new_set = Vec::new();
let data = LedSupportData::get_data(prod_id);
// only reuse a zone mode if the mode is supported
for mode in loaded {
if data.basic_modes.contains(&mode.mode) {
new_set.push(mode.clone());
}
}
*mode.1 = new_set;
}
}
*multizone_loaded = multizone_init;
}
config_loaded
}
/// Set combination state for boot animation/sleep animation/all leds/keys
/// leds/side leds LED active
pub(super) fn set_power_states(&mut self) -> Result<(), RogError> {
if let LEDNode::KbdLed(_platform) = &mut self.led_node {
// TODO: tuf bool array
// if let Some(pwr) =
// AuraPowerConfig::to_tuf_bool_array(&self.config.enabled) {
// let buf = [1, pwr[1] as u8, pwr[2] as u8, pwr[3] as u8,
// pwr[4] as u8]; platform.set_kbd_rgb_state(&buf)?;
// }
} else if let LEDNode::Rog(_, hid_raw) = &self.led_node {
let bytes = self.config.enabled.to_bytes(self.led_type);
let message = [0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2], bytes[3]];
hid_raw.write_bytes(&message)?;
hid_raw.write_bytes(&LED_SET)?;
// Changes won't persist unless apply is set
hid_raw.write_bytes(&LED_APPLY)?;
}
Ok(())
}
/// Write an effect block. This is for per-key, but can be repurposed to
/// write the raw factory mode packets - when doing this it is expected that
/// only the first `Vec` (`effect[0]`) is valid.
pub fn write_effect_block(&mut self, effect: &UsbPackets) -> Result<(), RogError> {
if self.config.brightness == LedBrightness::Off {
self.config.brightness = LedBrightness::Med;
self.config.write();
}
let pkt_type = effect[0][1];
const PER_KEY_TYPE: u8 = 0xbc;
if pkt_type != PER_KEY_TYPE {
self.per_key_mode_active = false;
if let LEDNode::Rog(_, hid_raw) = &self.led_node {
hid_raw.write_bytes(&effect[0])?;
hid_raw.write_bytes(&LED_SET)?;
// hid_raw.write_bytes(&LED_APPLY)?;
}
} else {
if !self.per_key_mode_active {
if let LEDNode::Rog(_, hid_raw) = &self.led_node {
let init = LedUsbPackets::get_init_msg();
hid_raw.write_bytes(&init)?;
}
self.per_key_mode_active = true;
}
if let LEDNode::Rog(_, hid_raw) = &self.led_node {
for row in effect.iter() {
hid_raw.write_bytes(row)?;
}
} else if let LEDNode::KbdLed(tuf) = &self.led_node {
for row in effect.iter() {
let r = row[9];
let g = row[10];
let b = row[11];
tuf.set_kbd_rgb_mode(&[0, 0, r, g, b, 0])?;
}
}
}
Ok(())
}
pub fn write_mode(&mut self, mode: &AuraEffect) -> Result<(), RogError> {
if let LEDNode::KbdLed(platform) = &self.led_node {
let buf = [
1,
mode.mode as u8,
mode.colour1.r,
mode.colour1.g,
mode.colour1.b,
mode.speed as u8,
];
platform.set_kbd_rgb_mode(&buf)?;
} else if let LEDNode::Rog(_, hid_raw) = &self.led_node {
let bytes: [u8; LED_MSG_LEN] = mode.into();
hid_raw.write_bytes(&bytes)?;
hid_raw.write_bytes(&LED_SET)?;
// Changes won't persist unless apply is set
hid_raw.write_bytes(&LED_APPLY)?;
} else {
return Err(RogError::NoAuraKeyboard);
}
self.per_key_mode_active = false;
Ok(())
}
pub(super) fn write_current_config_mode(&mut self) -> Result<(), RogError> {
if self.config.multizone_on {
let mode = self.config.current_mode;
let mut create = false;
// There is no multizone config for this mode so create one here
// using the colours of rainbow if it exists, or first available
// mode, or random
if self.config.multizone.is_none() {
create = true;
} else if let Some(multizones) = self.config.multizone.as_ref() {
if !multizones.contains_key(&mode) {
create = true;
}
}
if create {
info!("No user-set config for zone founding, attempting a default");
self.create_multizone_default()?;
}
if let Some(multizones) = self.config.multizone.as_mut() {
if let Some(set) = multizones.get(&mode) {
for mode in set.clone() {
self.write_mode(&mode)?;
}
}
}
} else {
let mode = self.config.current_mode;
if let Some(effect) = self.config.builtins.get(&mode).cloned() {
self.write_mode(&effect)?;
}
}
Ok(())
}
/// Create a default for the `current_mode` if multizone and no config
/// exists.
fn create_multizone_default(&mut self) -> Result<(), RogError> {
let mut default = vec![];
for (i, tmp) in self.supported_data.basic_zones.iter().enumerate() {
default.push(AuraEffect {
mode: self.config.current_mode,
zone: *tmp,
colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]),
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
speed: Speed::Med,
direction: Direction::Left,
});
}
if default.is_empty() {
return Err(RogError::AuraEffectNotSupported);
}
if let Some(multizones) = self.config.multizone.as_mut() {
multizones.insert(self.config.current_mode, default);
} else {
let mut tmp = BTreeMap::new();
tmp.insert(self.config.current_mode, default);
self.config.multizone = Some(tmp);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use rog_aura::aura_detection::LedSupportData;
use rog_aura::{AuraDeviceType, AuraModeNum, AuraZone, PowerZones};
use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardLed;
use zbus::zvariant::OwnedObjectPath;
use super::CtrlKbdLed;
use crate::ctrl_aura::config::AuraConfig;
use crate::ctrl_aura::controller::LEDNode;
#[test]
#[ignore = "Unable to run in CI as the HIDRAW device is required"]
fn create_multizone_if_no_config() {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::new("19b6");
let supported_basic_modes = LedSupportData {
device_name: String::new(),
product_id: String::new(),
layout_name: "ga401".to_owned(),
basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![],
advanced_type: rog_aura::keyboard::AdvancedAuraType::None,
power_zones: vec![PowerZones::Keyboard, PowerZones::RearGlow],
};
let mut controller = CtrlKbdLed {
led_type: AuraDeviceType::LaptopPost2021,
led_node: LEDNode::Rog(KeyboardLed::default(), HidRaw::new("19b6").unwrap()),
supported_data: supported_basic_modes,
per_key_mode_active: false,
config,
dbus_path: OwnedObjectPath::default(),
};
assert!(controller.config.multizone.is_none());
assert!(controller.create_multizone_default().is_err());
assert!(controller.config.multizone.is_none());
controller.supported_data.basic_zones.push(AuraZone::Key1);
controller.supported_data.basic_zones.push(AuraZone::Key2);
assert!(controller.create_multizone_default().is_ok());
assert!(controller.config.multizone.is_some());
let m = controller.config.multizone.unwrap();
assert!(m.contains_key(&AuraModeNum::Static));
let e = m.get(&AuraModeNum::Static).unwrap();
assert_eq!(e.len(), 2);
assert_eq!(e[0].zone, AuraZone::Key1);
assert_eq!(e[1].zone, AuraZone::Key2);
}
#[test]
#[ignore = "Unable to run in CI as the HIDRAW device is required"]
// TODO: use sim device
fn next_mode_create_multizone_if_no_config() {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::new("19b6");
let supported_basic_modes = LedSupportData {
device_name: String::new(),
product_id: String::new(),
layout_name: "ga401".to_owned(),
basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![AuraZone::Key1, AuraZone::Key2],
advanced_type: rog_aura::keyboard::AdvancedAuraType::None,
power_zones: vec![PowerZones::Keyboard, PowerZones::RearGlow],
};
let mut controller = CtrlKbdLed {
led_type: AuraDeviceType::LaptopPost2021,
led_node: LEDNode::Rog(KeyboardLed::default(), HidRaw::new("19b6").unwrap()),
supported_data: supported_basic_modes,
per_key_mode_active: false,
config,
dbus_path: OwnedObjectPath::default(),
};
assert!(controller.config.multizone.is_none());
controller.config.multizone_on = true;
// This is called in toggle_mode. It will error here because we have no
// keyboard node in tests.
assert_eq!(
controller
.write_current_config_mode()
.unwrap_err()
.to_string(),
"No supported Aura keyboard"
);
assert!(controller.config.multizone.is_some());
let m = controller.config.multizone.unwrap();
assert!(m.contains_key(&AuraModeNum::Static));
let e = m.get(&AuraModeNum::Static).unwrap();
assert_eq!(e.len(), 2);
assert_eq!(e[0].zone, AuraZone::Key1);
assert_eq!(e[1].zone, AuraZone::Key2);
}
}

View File

@@ -1,187 +0,0 @@
// Plan:
// - Manager has udev monitor on USB looking for ROG devices
// - If a device is found, add it to watch
// - Add it to Zbus server
// - If udev sees device removed then remove the zbus path
use std::collections::HashSet;
use log::{debug, error, info, warn};
use mio::{Events, Interest, Poll, Token};
use rog_aura::AuraDeviceType;
use rog_platform::hid_raw::HidRaw;
use tokio::task::spawn_blocking;
use udev::{Device, MonitorBuilder};
use zbus::object_server::SignalContext;
use zbus::zvariant::{ObjectPath, OwnedObjectPath};
use zbus::Connection;
use crate::ctrl_aura::controller::CtrlKbdLed;
use crate::ctrl_aura::trait_impls::{CtrlAuraZbus, AURA_ZBUS_PATH};
use crate::error::RogError;
use crate::{CtrlTask, Reloadable};
pub struct AuraManager {
_connection: Connection,
}
impl AuraManager {
pub async fn new(connection: Connection) -> Result<Self, RogError> {
let conn_copy = connection.clone();
let mut interfaces = HashSet::new();
// Do the initial keyboard detection:
let all = CtrlKbdLed::find_all()?;
for ctrl in all {
let path = ctrl.dbus_path.clone();
interfaces.insert(path.clone()); // ensure we record the initial stuff
let sig_ctx = CtrlAuraZbus::signal_context(&connection)?;
let sig_ctx2 = sig_ctx.clone();
let zbus = CtrlAuraZbus::new(ctrl, sig_ctx);
start_tasks(zbus, connection.clone(), sig_ctx2, path).await?;
}
let manager = Self {
_connection: connection,
};
// detect all plugged in aura devices (eventually)
// only USB devices are detected for here
spawn_blocking(move || {
let mut monitor = MonitorBuilder::new()?.match_subsystem("hidraw")?.listen()?;
let mut poll = Poll::new()?;
let mut events = Events::with_capacity(1024);
poll.registry()
.register(&mut monitor, Token(0), Interest::READABLE)?;
loop {
if poll.poll(&mut events, None).is_err() {
continue;
}
for event in monitor.iter() {
let parent = if let Some(parent) =
event.parent_with_subsystem_devtype("usb", "usb_device")?
{
parent
} else {
continue;
};
let action = if let Some(action) = event.action() {
action
} else {
continue;
};
let id_product = if let Some(id_product) = parent.attribute_value("idProduct") {
id_product.to_string_lossy()
} else {
continue;
};
let path = if let Some(path) = dbus_path_for_dev(&parent) {
path
} else {
continue;
};
let aura_device = AuraDeviceType::from(&*id_product);
if aura_device == AuraDeviceType::Unknown {
warn!("idProduct:{id_product:?} is unknown, not using");
continue;
}
if action == "remove" {
if interfaces.remove(&path) {
info!("AuraManager removing: {path:?}");
let conn_copy = conn_copy.clone();
tokio::spawn(async move {
let res = conn_copy
.object_server()
.remove::<CtrlAuraZbus, _>(&path)
.await
.map_err(|e| {
error!("Failed to remove {path:?}, {e:?}");
e
})?;
info!("AuraManager removed: {path:?}, {res}");
Ok::<(), RogError>(())
});
}
} else if action == "add" {
if interfaces.contains(&path) {
debug!("Already a ctrl at {path:?}");
continue;
}
// Need to check the driver is asus to prevent using hid_generic
if let Some(p2) = event.parent() {
if let Some(driver) = p2.driver() {
// There is a tree of devices added so filter by driver
if driver != "asus" {
debug!("{id_product:?} driver was not asus, skipping");
continue;
}
} else {
continue;
}
}
if let Some(dev_node) = event.devnode() {
if let Ok(raw) = HidRaw::from_device(event.device())
.map_err(|e| error!("device path error: {e:?}"))
{
if let Ok(mut ctrl) = CtrlKbdLed::from_hidraw(raw, path.clone()) {
ctrl.config = CtrlKbdLed::init_config(&id_product);
interfaces.insert(path.clone());
info!("AuraManager starting device at: {dev_node:?}, {path:?}");
let sig_ctx = CtrlAuraZbus::signal_context(&conn_copy)?;
let zbus = CtrlAuraZbus::new(ctrl, sig_ctx);
let sig_ctx = CtrlAuraZbus::signal_context(&conn_copy)?;
let conn_copy = conn_copy.clone();
tokio::spawn(async move {
start_tasks(zbus, conn_copy.clone(), sig_ctx, path).await
});
}
}
}
};
}
}
// Required for return type on spawn
#[allow(unreachable_code)]
Ok::<(), RogError>(())
});
Ok(manager)
}
}
pub(crate) fn dbus_path_for_dev(parent: &Device) -> Option<OwnedObjectPath> {
if let Some(filename) = super::filename_partial(parent) {
return Some(
ObjectPath::from_str_unchecked(&format!("{AURA_ZBUS_PATH}/{filename}")).into(),
);
}
None
}
pub(crate) fn dbus_path_for_tuf() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{AURA_ZBUS_PATH}/tuf")).into()
}
async fn start_tasks(
mut zbus: CtrlAuraZbus,
connection: Connection,
_signal_ctx: SignalContext<'static>,
path: OwnedObjectPath,
) -> Result<(), RogError> {
// let task = zbus.clone();
// let signal_ctx = signal_ctx.clone();
zbus.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
connection.object_server().at(path, zbus).await.unwrap();
// TODO: skip this until we keep handles to tasks so they can be killed
// task.create_tasks(signal_ctx).await
Ok(())
}

View File

@@ -1,29 +0,0 @@
use udev::Device;
use zbus::zvariant::{ObjectPath, OwnedObjectPath};
pub mod config;
pub mod controller;
pub mod manager;
/// Implements `CtrlTask`, `Reloadable`, `ZbusRun`
pub mod trait_impls;
/// Returns only the Device details concatenated in a form usable for
/// adding/appending to a filename
pub(super) fn filename_partial(parent: &Device) -> Option<OwnedObjectPath> {
if let Some(id_product) = parent.attribute_value("idProduct") {
let id_product = id_product.to_string_lossy();
let path = if let Some(devnum) = parent.attribute_value("devnum") {
let devnum = devnum.to_string_lossy();
if let Some(devpath) = parent.attribute_value("devpath") {
let devpath = devpath.to_string_lossy();
format!("{id_product}_{devnum}_{devpath}")
} else {
format!("{id_product}_{devnum}")
}
} else {
format!("{id_product}")
};
return Some(ObjectPath::from_str_unchecked(&path).into());
}
None
}

View File

@@ -1,295 +0,0 @@
use std::collections::BTreeMap;
use std::sync::Arc;
use config_traits::StdConfig;
use log::{debug, error, info, warn};
use rog_aura::keyboard::{LaptopAuraPower, UsbPackets};
use rog_aura::{AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, LedBrightness, PowerZones};
use zbus::export::futures_util::lock::{Mutex, MutexGuard};
use zbus::export::futures_util::StreamExt;
use zbus::fdo::Error as ZbErr;
use zbus::{interface, SignalContext};
use super::controller::CtrlKbdLed;
use crate::error::RogError;
use crate::CtrlTask;
pub const AURA_ZBUS_NAME: &str = "Aura";
pub const AURA_ZBUS_PATH: &str = "/org/asuslinux";
#[derive(Clone)]
pub struct CtrlAuraZbus(Arc<Mutex<CtrlKbdLed>>, SignalContext<'static>);
impl CtrlAuraZbus {
pub fn new(controller: CtrlKbdLed, signal: SignalContext<'static>) -> Self {
Self(Arc::new(Mutex::new(controller)), signal)
}
fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> {
let bright = lock.led_node.get_brightness()?;
lock.config.read();
lock.config.brightness = bright.into();
lock.config.write();
Ok(())
}
}
/// The main interface for changing, reading, or notfying
///
/// LED commands are split between Brightness, Modes, Per-Key
#[interface(name = "org.asuslinux.Aura")]
impl CtrlAuraZbus {
/// Return the device type for this Aura keyboard
#[zbus(property)]
async fn device_type(&self) -> AuraDeviceType {
let ctrl = self.0.lock().await;
ctrl.led_type
}
/// Return the current LED brightness
#[zbus(property)]
async fn brightness(&self) -> Result<LedBrightness, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.led_node.get_brightness().map(|n| n.into())?)
}
/// Set the keyboard brightness level (0-3)
#[zbus(property)]
async fn set_brightness(&mut self, brightness: LedBrightness) -> Result<(), ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.led_node.set_brightness(brightness.into())?)
}
/// Total levels of brightness available
#[zbus(property)]
async fn supported_brightness(&self) -> Vec<LedBrightness> {
vec![
LedBrightness::Off,
LedBrightness::Low,
LedBrightness::Med,
LedBrightness::High,
]
}
/// The total available modes
#[zbus(property)]
async fn supported_basic_modes(&self) -> Result<Vec<AuraModeNum>, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.config.builtins.keys().cloned().collect())
}
#[zbus(property)]
async fn supported_basic_zones(&self) -> Result<Vec<AuraZone>, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.supported_data.basic_zones.clone())
}
#[zbus(property)]
async fn supported_power_zones(&self) -> Result<Vec<PowerZones>, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.supported_data.power_zones.clone())
}
/// The current mode data
#[zbus(property)]
async fn led_mode(&self) -> Result<AuraModeNum, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.config.current_mode)
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode(&mut self, num: AuraModeNum) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
ctrl.config.current_mode = num;
ctrl.write_current_config_mode()?;
if ctrl.config.brightness == LedBrightness::Off {
ctrl.config.brightness = LedBrightness::Med;
}
ctrl.led_node
.set_brightness(ctrl.config.brightness.into())?;
ctrl.config.write();
self.led_mode_data_invalidate(&self.1).await.ok();
Ok(())
}
/// The current mode data
#[zbus(property)]
async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> {
let ctrl = self.0.lock().await;
let mode = ctrl.config.current_mode;
match ctrl.config.builtins.get(&mode) {
Some(effect) => Ok(effect.clone()),
None => Err(ZbErr::Failed("Could not get the current effect".into())),
}
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode_data(&mut self, effect: AuraEffect) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
if !ctrl.supported_data.basic_modes.contains(&effect.mode)
|| effect.zone != AuraZone::None
&& !ctrl.supported_data.basic_zones.contains(&effect.zone)
{
return Err(ZbErr::NotSupported(format!(
"The Aura effect is not supported: {effect:?}"
)));
}
ctrl.write_mode(&effect)?;
if ctrl.config.brightness == LedBrightness::Off {
ctrl.config.brightness = LedBrightness::Med;
}
ctrl.led_node
.set_brightness(ctrl.config.brightness.into())?;
ctrl.config.set_builtin(effect);
ctrl.config.write();
self.led_mode_invalidate(&self.1).await.ok();
Ok(())
}
/// Get the data set for every mode available
async fn all_mode_data(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
let ctrl = self.0.lock().await;
ctrl.config.builtins.clone()
}
// As property doesn't work for AuraPowerDev (complexity of serialization?)
#[zbus(property)]
async fn led_power(&self) -> LaptopAuraPower {
let ctrl = self.0.lock().await;
ctrl.config.enabled.clone()
}
/// Set a variety of states, input is array of enum.
/// `enabled` sets if the sent array should be disabled or enabled
///
/// For Modern ROG devices the "enabled" flag is ignored.
#[zbus(property)]
async fn set_led_power(&mut self, options: LaptopAuraPower) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
for opt in options.states {
let zone = opt.zone;
for config in ctrl.config.enabled.states.iter_mut() {
if config.zone == zone {
*config = opt;
}
}
}
ctrl.config.write();
Ok(ctrl.set_power_states().map_err(|e| {
warn!("{}", e);
e
})?)
}
/// On machine that have some form of either per-key keyboard or per-zone
/// this can be used to write custom effects over dbus. The input is a
/// nested `Vec<Vec<8>>` where `Vec<u8>` is a raw USB packet
async fn direct_addressing_raw(&self, data: UsbPackets) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
ctrl.write_effect_block(&data)?;
Ok(())
}
}
impl CtrlTask for CtrlAuraZbus {
fn zbus_path() -> &'static str {
"/org/asuslinux"
}
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
let load_save =
|start: bool, mut lock: MutexGuard<'_, CtrlKbdLed>| -> Result<(), RogError> {
// If waking up
if !start {
info!("CtrlKbdLedTask reloading brightness and modes");
lock.led_node
.set_brightness(lock.config.brightness.into())
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})?;
lock.write_current_config_mode().map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})?;
} else if start {
Self::update_config(&mut lock).map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})?;
}
Ok(())
};
let inner1 = self.0.clone();
let inner3 = self.0.clone();
self.create_sys_event_tasks(
move |sleeping| {
let inner1 = inner1.clone();
async move {
let lock = inner1.lock().await;
load_save(sleeping, lock).unwrap(); // unwrap as we want to
// bomb out of the task
}
},
move |_shutting_down| {
let inner3 = inner3.clone();
async move {
let lock = inner3.lock().await;
load_save(false, lock).unwrap(); // unwrap as we want to
// bomb out of the task
}
},
move |_lid_closed| {
// on lid change
async move {}
},
move |_power_plugged| {
// power change
async move {}
},
)
.await;
let ctrl2 = self.0.clone();
let ctrl = self.0.lock().await;
let watch = ctrl.led_node.monitor_brightness()?;
tokio::spawn(async move {
let mut buffer = [0; 32];
watch
.into_event_stream(&mut buffer)
.unwrap()
.for_each(|_| async {
if let Some(lock) = ctrl2.try_lock() {
load_save(true, lock).unwrap(); // unwrap as we want to
// bomb out of the task
}
})
.await;
});
Ok(())
}
}
impl crate::Reloadable for CtrlAuraZbus {
async fn reload(&mut self) -> Result<(), RogError> {
let mut ctrl = self.0.lock().await;
debug!("reloading keyboard mode");
ctrl.write_current_config_mode()?;
debug!("reloading power states");
ctrl.set_power_states().map_err(|err| warn!("{err}")).ok();
Ok(())
}
}

View File

@@ -3,26 +3,27 @@ use std::sync::Arc;
use config_traits::{StdConfig, StdConfigLoad};
use futures_lite::StreamExt;
use futures_util::lock::Mutex;
use log::{debug, error, info, warn};
use rog_platform::platform::{RogPlatform, ThrottlePolicy};
use rog_platform::platform::{PlatformProfile, RogPlatform};
use rog_profiles::error::ProfileError;
use rog_profiles::fan_curve_set::CurveData;
use rog_profiles::{find_fan_curve_node, FanCurvePU, FanCurveProfiles};
use serde_derive::{Deserialize, Serialize};
use tokio::sync::Mutex;
use zbus::{interface, Connection, SignalContext};
use serde::{Deserialize, Serialize};
use zbus::object_server::SignalEmitter;
use zbus::{interface, Connection};
use crate::error::RogError;
use crate::{CtrlTask, CONFIG_PATH_BASE};
pub const FAN_CURVE_ZBUS_NAME: &str = "FanCurves";
pub const FAN_CURVE_ZBUS_PATH: &str = "/org/asuslinux";
pub const FAN_CURVE_ZBUS_PATH: &str = "/xyz/ljones";
#[derive(Deserialize, Serialize, Debug, Default)]
pub struct FanCurveConfig {
pub profiles: FanCurveProfiles,
#[serde(skip)]
pub current: u8,
pub current: PlatformProfile
}
impl StdConfig for FanCurveConfig {
@@ -46,14 +47,14 @@ impl StdConfigLoad for FanCurveConfig {}
#[derive(Debug, Clone)]
pub struct CtrlFanCurveZbus {
config: Arc<Mutex<FanCurveConfig>>,
platform: RogPlatform,
platform: RogPlatform
}
// Non-zbus-derive impl
impl CtrlFanCurveZbus {
pub fn new() -> Result<Self, RogError> {
let platform = RogPlatform::new()?;
if platform.has_throttle_thermal_policy() {
if platform.has_platform_profile() {
info!("Device has profile control available");
find_fan_curve_node()?;
info!("Device has fan curves available");
@@ -64,16 +65,16 @@ impl CtrlFanCurveZbus {
if config.profiles.balanced.is_empty() || !config.file_path().exists() {
info!("Fetching default fan curves");
let current = platform.get_throttle_thermal_policy()?;
let current = platform.get_platform_profile()?;
for this in [
ThrottlePolicy::Balanced,
ThrottlePolicy::Performance,
ThrottlePolicy::Quiet,
PlatformProfile::Balanced,
PlatformProfile::Performance,
PlatformProfile::Quiet
] {
// For each profile we need to switch to it before we
// can read the existing values from hardware. The ACPI method used
// for this is what limits us.
platform.set_throttle_thermal_policy(this.into())?;
platform.set_platform_profile(this.into())?;
let mut dev = find_fan_curve_node()?;
fan_curves.set_active_curve_to_defaults(this, &mut dev)?;
@@ -82,7 +83,7 @@ impl CtrlFanCurveZbus {
info!("{}", String::from(curve));
}
}
platform.set_throttle_thermal_policy(current)?;
platform.set_platform_profile(current.as_str())?;
config.profiles = fan_curves;
config.write();
} else {
@@ -92,7 +93,7 @@ impl CtrlFanCurveZbus {
return Ok(Self {
config: Arc::new(Mutex::new(config)),
platform,
platform
});
}
@@ -100,14 +101,14 @@ impl CtrlFanCurveZbus {
}
}
#[interface(name = "org.asuslinux.FanCurves")]
#[interface(name = "xyz.ljones.FanCurves")]
impl CtrlFanCurveZbus {
/// Set all fan curves for a profile to enabled status. Will also activate a
/// fan curve if in the same profile mode
async fn set_fan_curves_enabled(
&mut self,
profile: ThrottlePolicy,
enabled: bool,
profile: PlatformProfile,
enabled: bool
) -> zbus::fdo::Result<()> {
self.config
.lock()
@@ -127,9 +128,9 @@ impl CtrlFanCurveZbus {
/// activate a fan curve if in the same profile mode
async fn set_profile_fan_curve_enabled(
&mut self,
profile: ThrottlePolicy,
profile: PlatformProfile,
fan: FanCurvePU,
enabled: bool,
enabled: bool
) -> zbus::fdo::Result<()> {
self.config
.lock()
@@ -148,7 +149,7 @@ impl CtrlFanCurveZbus {
/// Get the fan-curve data for the currently active ThrottlePolicy
async fn fan_curve_data(
&mut self,
profile: ThrottlePolicy,
profile: PlatformProfile
) -> zbus::fdo::Result<Vec<CurveData>> {
let curve = self
.config
@@ -164,15 +165,15 @@ impl CtrlFanCurveZbus {
/// Will also activate the fan curve if the user is in the same mode.
async fn set_fan_curve(
&mut self,
profile: ThrottlePolicy,
curve: CurveData,
profile: PlatformProfile,
curve: CurveData
) -> zbus::fdo::Result<()> {
self.config
.lock()
.await
.profiles
.save_fan_curve(curve, profile)?;
let active: ThrottlePolicy = self.platform.get_throttle_thermal_policy()?.into();
let active: PlatformProfile = self.platform.get_platform_profile()?.into();
if active == profile {
self.config
.lock()
@@ -189,15 +190,15 @@ impl CtrlFanCurveZbus {
///
/// Each platform_profile has a different default and the default can be
/// read only for the currently active profile.
async fn set_curves_to_defaults(&mut self, profile: ThrottlePolicy) -> zbus::fdo::Result<()> {
let active = self.platform.get_throttle_thermal_policy()?;
self.platform.set_throttle_thermal_policy(profile.into())?;
async fn set_curves_to_defaults(&mut self, profile: PlatformProfile) -> zbus::fdo::Result<()> {
let active = self.platform.get_platform_profile()?;
self.platform.set_platform_profile(profile.into())?;
self.config
.lock()
.await
.profiles
.set_active_curve_to_defaults(profile, &mut find_fan_curve_node()?)?;
self.platform.set_throttle_thermal_policy(active)?;
self.platform.set_platform_profile(active.as_str())?;
self.config.lock().await.write();
Ok(())
}
@@ -207,16 +208,16 @@ impl CtrlFanCurveZbus {
///
/// Each platform_profile has a different default and the defualt can be
/// read only for the currently active profile.
async fn reset_profile_curves(&self, profile: ThrottlePolicy) -> zbus::fdo::Result<()> {
let active = self.platform.get_throttle_thermal_policy()?;
async fn reset_profile_curves(&self, profile: PlatformProfile) -> zbus::fdo::Result<()> {
let active = self.platform.get_platform_profile()?;
self.platform.set_throttle_thermal_policy(profile.into())?;
self.platform.set_platform_profile(profile.into())?;
self.config
.lock()
.await
.profiles
.set_active_curve_to_defaults(active.into(), &mut find_fan_curve_node()?)?;
self.platform.set_throttle_thermal_policy(active)?;
.set_active_curve_to_defaults((&active).into(), &mut find_fan_curve_node()?)?;
self.platform.set_platform_profile(active.as_str())?;
self.config.lock().await.write();
Ok(())
@@ -234,28 +235,33 @@ impl CtrlTask for CtrlFanCurveZbus {
FAN_CURVE_ZBUS_PATH
}
async fn create_tasks(&self, _signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
let watch_throttle_thermal_policy = self.platform.monitor_throttle_thermal_policy()?;
async fn create_tasks(&self, _signal_ctxt: SignalEmitter<'static>) -> Result<(), RogError> {
let watch_platform_profile = self.platform.monitor_platform_profile()?;
let platform = self.platform.clone();
let config = self.config.clone();
let fan_curves = self.config.clone();
tokio::spawn(async move {
let mut buffer = [0; 32];
if let Ok(mut stream) = watch_throttle_thermal_policy.into_event_stream(&mut buffer) {
if let Ok(mut stream) = watch_platform_profile.into_event_stream(&mut buffer) {
while (stream.next().await).is_some() {
debug!("watch_throttle_thermal_policy changed");
if let Ok(profile) = platform.get_throttle_thermal_policy().map_err(|e| {
error!("get_throttle_thermal_policy error: {e}");
}) {
debug!("watch_platform_profile changed");
if let Ok(profile) =
platform
.get_platform_profile()
.map(|p| p.into())
.map_err(|e| {
error!("get_platform_profile error: {e}");
})
{
if profile != config.lock().await.current {
fan_curves
.lock()
.await
.profiles
.write_profile_curve_to_platform(
profile.into(),
&mut find_fan_curve_node().unwrap(),
profile,
&mut find_fan_curve_node().unwrap()
)
.map_err(|e| warn!("write_profile_curve_to_platform, {}", e))
.ok();
@@ -273,7 +279,7 @@ impl CtrlTask for CtrlFanCurveZbus {
impl crate::Reloadable for CtrlFanCurveZbus {
/// Fetch the active profile and use that to set all related components up
async fn reload(&mut self) -> Result<(), RogError> {
let active = self.platform.get_throttle_thermal_policy()?.into();
let active = self.platform.get_platform_profile()?.into();
let mut config = self.config.lock().await;
if let Ok(mut device) = find_fan_curve_node() {
config

File diff suppressed because it is too large Load Diff

View File

@@ -1,51 +0,0 @@
use config_traits::{StdConfig, StdConfigLoad};
use rog_slash::{DeviceState, SlashMode};
use serde_derive::{Deserialize, Serialize};
const CONFIG_FILE: &str = "slash.ron";
/// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug)]
pub struct SlashConfig {
pub slash_enabled: bool,
pub slash_brightness: u8,
pub slash_interval: u8,
pub slash_mode: SlashMode,
}
impl Default for SlashConfig {
fn default() -> Self {
SlashConfig {
slash_enabled: true,
slash_brightness: 255,
slash_interval: 0,
slash_mode: SlashMode::Bounce,
}
}
}
impl StdConfig for SlashConfig {
fn new() -> Self {
Self::default()
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
fn config_dir() -> std::path::PathBuf {
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
}
}
impl StdConfigLoad for SlashConfig {}
impl From<&SlashConfig> for DeviceState {
fn from(config: &SlashConfig) -> Self {
DeviceState {
slash_enabled: config.slash_enabled,
slash_brightness: config.slash_brightness,
slash_interval: config.slash_interval,
slash_mode: config.slash_mode,
}
}
}

View File

@@ -1,98 +0,0 @@
pub mod config;
pub mod trait_impls;
use config_traits::{StdConfig, StdConfigLoad};
use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw;
use rog_slash::error::SlashError;
use rog_slash::usb::{get_slash_type, pkt_set_mode, pkt_set_options, pkts_for_init};
use rog_slash::{SlashMode, SlashType};
use crate::ctrl_slash::config::SlashConfig;
use crate::error::RogError;
enum Node {
Usb(USBRaw),
Hid(HidRaw),
}
impl Node {
pub fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
// TODO: map and pass on errors
match self {
Node::Usb(u) => {
u.write_bytes(message).ok();
}
Node::Hid(h) => {
h.write_bytes(message).ok();
}
}
Ok(())
}
}
pub struct CtrlSlash {
node: Node,
config: SlashConfig,
}
impl CtrlSlash {
#[inline]
pub fn new() -> Result<CtrlSlash, RogError> {
let slash_type = get_slash_type()?;
if matches!(slash_type, SlashType::Unknown | SlashType::Unsupported) {
return Err(RogError::Slash(SlashError::NoDevice));
}
let usb = USBRaw::new(rog_slash::usb::PROD_ID).ok();
let hid = HidRaw::new(rog_slash::usb::PROD_ID_STR).ok();
let node = if usb.is_some() {
unsafe { Node::Usb(usb.unwrap_unchecked()) }
} else if hid.is_some() {
unsafe { Node::Hid(hid.unwrap_unchecked()) }
} else {
return Err(RogError::NotSupported);
};
let ctrl = CtrlSlash {
node,
config: SlashConfig::new().load(),
};
ctrl.do_initialization()?;
Ok(ctrl)
}
fn do_initialization(&self) -> Result<(), RogError> {
let init_packets = pkts_for_init();
self.node.write_bytes(&init_packets[0])?;
self.node.write_bytes(&init_packets[1])?;
// Apply config upon initialization
let option_packets = pkt_set_options(
self.config.slash_enabled,
self.config.slash_brightness,
self.config.slash_interval,
);
self.node.write_bytes(&option_packets)?;
let mode_packets = pkt_set_mode(self.config.slash_mode);
self.node.write_bytes(&mode_packets[0])?;
self.node.write_bytes(&mode_packets[1])?;
Ok(())
}
pub fn set_options(&self, enabled: bool, brightness: u8, interval: u8) -> Result<(), RogError> {
let command_packets = pkt_set_options(enabled, brightness, interval);
self.node.write_bytes(&command_packets)?;
Ok(())
}
pub fn set_slash_mode(&self, slash_mode: SlashMode) -> Result<(), RogError> {
let command_packets = pkt_set_mode(slash_mode);
self.node.write_bytes(&command_packets[0])?;
self.node.write_bytes(&command_packets[1])?;
Ok(())
}
}

View File

@@ -1,165 +0,0 @@
use std::sync::Arc;
use config_traits::StdConfig;
use log::warn;
use rog_slash::usb::{pkt_set_mode, pkt_set_options};
use rog_slash::{DeviceState, SlashMode};
use zbus::export::futures_util::lock::Mutex;
use zbus::{interface, Connection, SignalContext};
use crate::ctrl_slash::CtrlSlash;
use crate::error::RogError;
pub const SLASH_ZBUS_NAME: &str = "Slash";
pub const SLASH_ZBUS_PATH: &str = "/org/asuslinux";
#[derive(Clone)]
pub struct CtrlSlashZbus(pub Arc<Mutex<CtrlSlash>>);
/// The struct with the main dbus methods requires this trait
impl crate::ZbusRun for CtrlSlashZbus {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, SLASH_ZBUS_PATH, server).await;
}
}
#[interface(name = "org.asuslinux.Slash")]
impl CtrlSlashZbus {
/// Get enabled or not
#[zbus(property)]
async fn enabled(&self) -> bool {
let lock = self.0.lock().await;
lock.config.slash_enabled
}
/// Set enabled true or false
#[zbus(property)]
async fn set_enabled(&self, enabled: bool) {
let mut lock = self.0.lock().await;
let brightness = if enabled && lock.config.slash_brightness == 0 {
0x88
} else {
lock.config.slash_brightness
};
lock.node
.write_bytes(&pkt_set_options(
enabled,
brightness,
lock.config.slash_interval,
))
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_enabled = enabled;
lock.config.slash_brightness = brightness;
lock.config.write();
}
/// Get brightness level
#[zbus(property)]
async fn brightness(&self) -> u8 {
let lock = self.0.lock().await;
lock.config.slash_brightness
}
/// Set brightness level
#[zbus(property)]
async fn set_brightness(&self, brightness: u8) {
let mut lock = self.0.lock().await;
let enabled = brightness > 0;
lock.node
.write_bytes(&pkt_set_options(
enabled,
brightness,
lock.config.slash_interval,
))
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_enabled = enabled;
lock.config.slash_brightness = brightness;
lock.config.write();
}
#[zbus(property)]
async fn interval(&self) -> u8 {
let lock = self.0.lock().await;
lock.config.slash_interval
}
/// Set interval between slash animations (0-255)
#[zbus(property)]
async fn set_interval(&self, interval: u8) {
let mut lock = self.0.lock().await;
lock.node
.write_bytes(&pkt_set_options(
lock.config.slash_enabled,
lock.config.slash_brightness,
interval,
))
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_interval = interval;
lock.config.write();
}
#[zbus(property)]
async fn slash_mode(&self) -> u8 {
let lock = self.0.lock().await;
lock.config.slash_interval
}
/// Set interval between slash animations (0-255)
#[zbus(property)]
async fn set_slash_mode(&self, slash_mode: SlashMode) {
let mut lock = self.0.lock().await;
let command_packets = pkt_set_mode(slash_mode);
lock.node
.write_bytes(&command_packets[0])
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.node
.write_bytes(&command_packets[1])
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_mode = slash_mode;
lock.config.write();
}
/// Get the device state as stored by asusd
// #[zbus(property)]
async fn device_state(&self) -> DeviceState {
let lock = self.0.lock().await;
DeviceState::from(&lock.config)
}
}
impl crate::CtrlTask for CtrlSlashZbus {
fn zbus_path() -> &'static str {
SLASH_ZBUS_PATH
}
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
Ok(())
}
}
impl crate::Reloadable for CtrlSlashZbus {
async fn reload(&mut self) -> Result<(), RogError> {
Ok(())
}
}

View File

@@ -2,19 +2,19 @@ use std::env;
use std::error::Error;
use std::sync::Arc;
use ::zbus::export::futures_util::lock::Mutex;
use ::zbus::Connection;
use asusd::asus_armoury::start_attributes_zbus;
use asusd::aura_manager::DeviceManager;
use asusd::config::Config;
use asusd::ctrl_anime::trait_impls::CtrlAnimeZbus;
use asusd::ctrl_anime::CtrlAnime;
use asusd::ctrl_aura::manager::AuraManager;
use asusd::ctrl_fancurves::CtrlFanCurveZbus;
use asusd::ctrl_platform::CtrlPlatform;
use asusd::ctrl_slash::trait_impls::CtrlSlashZbus;
use asusd::ctrl_slash::CtrlSlash;
use asusd::{print_board_info, start_tasks, CtrlTask, DBUS_NAME};
use config_traits::{StdConfig, StdConfigLoad3};
use config_traits::{StdConfig, StdConfigLoad1};
use futures_util::lock::Mutex;
use log::{error, info};
use rog_platform::asus_armoury::FirmwareAttributes;
use rog_platform::platform::RogPlatform;
use rog_platform::power::AsusPower;
use zbus::fdo::ObjectManager;
#[tokio::main]
@@ -25,11 +25,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.parse_default_env()
.target(env_logger::Target::Stdout)
.format_timestamp(None)
.filter_level(log::LevelFilter::Debug)
.init();
let is_service = match env::var_os("IS_SERVICE") {
Some(val) => val == "1",
None => false,
None => true
};
if !is_service {
@@ -59,23 +60,30 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
// println!("{:?}", supported.supported_functions());
// Start zbus server
let mut connection = Connection::system().await?;
connection
.object_server()
.at("/org/asuslinux", ObjectManager)
.await
.unwrap();
let mut server = Connection::system().await?;
server.object_server().at("/", ObjectManager).await.unwrap();
let config = Config::new().load();
let cfg_path = config.file_path();
let config = Arc::new(Mutex::new(config));
// supported.add_to_server(&mut connection).await;
let platform = RogPlatform::new()?; // TODO: maybe needs async mutex?
let power = AsusPower::new()?; // TODO: maybe needs async mutex?
let attributes = FirmwareAttributes::new();
start_attributes_zbus(
&server,
platform.clone(),
power.clone(),
attributes.clone(),
config.clone()
)
.await?;
match CtrlFanCurveZbus::new() {
Ok(ctrl) => {
let sig_ctx = CtrlFanCurveZbus::signal_context(&connection)?;
start_tasks(ctrl, &mut connection, sig_ctx).await?;
let sig_ctx = CtrlFanCurveZbus::signal_context(&server)?;
start_tasks(ctrl, &mut server, sig_ctx).await?;
}
Err(err) => {
error!("FanCurves: {}", err);
@@ -83,52 +91,30 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
}
match CtrlPlatform::new(
platform,
power,
attributes,
config.clone(),
&cfg_path,
CtrlPlatform::signal_context(&connection)?,
CtrlPlatform::signal_context(&server)?
) {
Ok(ctrl) => {
let sig_ctx = CtrlPlatform::signal_context(&connection)?;
start_tasks(ctrl, &mut connection, sig_ctx).await?;
let sig_ctx = CtrlPlatform::signal_context(&server)?;
start_tasks(ctrl, &mut server, sig_ctx).await?;
}
Err(err) => {
error!("CtrlPlatform: {}", err);
}
}
match CtrlAnime::new() {
Ok(ctrl) => {
let zbus = CtrlAnimeZbus(Arc::new(Mutex::new(ctrl)));
let sig_ctx = CtrlAnimeZbus::signal_context(&connection)?;
start_tasks(zbus, &mut connection, sig_ctx).await?;
}
Err(err) => {
info!("AniMe control: {}", err);
}
}
match CtrlSlash::new() {
Ok(ctrl) => {
let zbus = CtrlSlashZbus(Arc::new(Mutex::new(ctrl)));
// Currently, the Slash has no need for a loop watching power events, however,
// it could be cool to have the slash do some power-on/off animation
// (It has a built-in power on animation which plays when u plug in the power
// supply)
let sig_ctx = CtrlSlashZbus::signal_context(&connection)?;
start_tasks(zbus, &mut connection, sig_ctx).await?;
}
Err(err) => {
info!("AniMe control: {}", err);
}
}
let _ = AuraManager::new(connection.clone()).await?;
let _ = DeviceManager::new(server.clone()).await?;
// Request dbus name after finishing initalizing all functions
connection.request_name(DBUS_NAME).await?;
server.request_name(DBUS_NAME).await?;
info!("Startup success, begining dbus server loop");
loop {
// This is just a blocker to idle and ensure the reator reacts
connection.executor().tick().await;
server.executor().tick().await;
}
}

View File

@@ -37,7 +37,7 @@ pub enum RogError {
SystemdUnitAction(String),
SystemdUnitWaitTimeout(String),
Command(String, std::io::Error),
ParseRon(ron::Error),
ParseRon(ron::Error)
}
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::ParseRon(error) => write!(f, "Parse config error: {}", error),
RogError::ParseRon(error) => write!(f, "Parse config error: {}", error)
}
}
}
@@ -142,3 +142,10 @@ impl From<RogError> for zbus::fdo::Error {
zbus::fdo::Error::Failed(format!("{}", err))
}
}
impl From<RogError> for zbus::Error {
#[inline]
fn from(err: RogError) -> Self {
zbus::Error::Failure(format!("{}", err))
}
}

View File

@@ -1,17 +1,18 @@
#![deny(unused_must_use)]
/// Configuration loading, saving
pub mod config;
/// Control of anime matrix display
pub mod ctrl_anime;
/// Keyboard LED brightness control, RGB, and LED display modes
pub mod ctrl_aura;
/// Control platform profiles + fan-curves if available
pub mod ctrl_fancurves;
/// Control ASUS bios function such as boot sound, Optimus/Dedicated gfx mode
pub mod ctrl_platform;
/// Control of Slash led bar
pub mod ctrl_slash;
pub mod asus_armoury;
pub mod aura_anime;
pub mod aura_laptop;
pub mod aura_manager;
pub mod aura_scsi;
pub mod aura_slash;
pub mod aura_types;
pub mod error;
use std::future::Future;
@@ -22,15 +23,19 @@ use futures_lite::stream::StreamExt;
use log::{debug, info, warn};
use logind_zbus::manager::ManagerProxy;
use tokio::time::sleep;
use zbus::object_server::{Interface, SignalEmitter};
use zbus::proxy::CacheProperties;
use zbus::zvariant::ObjectPath;
use zbus::{CacheProperties, Connection, SignalContext};
use zbus::Connection;
use crate::error::RogError;
const CONFIG_PATH_BASE: &str = "/etc/asusd/";
pub static DBUS_NAME: &str = "org.asuslinux.Daemon";
pub static DBUS_PATH: &str = "/org/asuslinux/Daemon";
pub static DBUS_IFACE: &str = "org.asuslinux.Daemon";
pub const ASUS_ZBUS_PATH: &str = "/xyz/ljones";
pub static DBUS_NAME: &str = "xyz.ljones.Asusd";
pub static DBUS_PATH: &str = "/xyz/ljones/Daemon";
pub static DBUS_IFACE: &str = "xyz.ljones.Asusd";
/// This macro adds a function which spawns an `inotify` task on the passed in
/// `Executor`.
@@ -39,7 +44,7 @@ pub static DBUS_IFACE: &str = "org.asuslinux.Daemon";
/// methods to be available:
/// - `<name>() -> SomeValue`, functionally is a getter, but is allowed to have
/// side effects.
/// - `notify_<name>(SignalContext, SomeValue)`
/// - `notify_<name>(SignalEmitter, SomeValue)`
///
/// In most cases if `SomeValue` is stored in a config then `<name>()` getter is
/// expected to update it. The getter should *never* write back to the path or
@@ -56,13 +61,13 @@ pub static DBUS_IFACE: &str = "org.asuslinux.Daemon";
/// // TODO: this is kind of useless if it can't trigger some action
#[macro_export]
macro_rules! task_watch_item {
($name:ident $self_inner:ident) => {
($name:ident $name_str:literal $self_inner:ident) => {
concat_idents::concat_idents!(fn_name = watch_, $name {
async fn fn_name(
&self,
signal_ctxt: SignalContext<'static>,
signal_ctxt: SignalEmitter<'static>,
) -> Result<(), RogError> {
use zbus::export::futures_util::StreamExt;
use futures_util::StreamExt;
let ctrl = self.clone();
concat_idents::concat_idents!(watch_fn = monitor_, $name {
@@ -72,12 +77,15 @@ macro_rules! task_watch_item {
let mut buffer = [0; 32];
watch.into_event_stream(&mut buffer).unwrap().for_each(|_| async {
if let Ok(value) = ctrl.$name() { // get new value from zbus method
concat_idents::concat_idents!(notif_fn = $name, _changed {
ctrl.notif_fn(&signal_ctxt).await.ok();
});
let mut lock = ctrl.config.lock().await;
lock.$name = value;
lock.write();
if ctrl.config.lock().await.$name != value {
log::debug!("{} was changed to {} externally", $name_str, value);
concat_idents::concat_idents!(notif_fn = $name, _changed {
ctrl.notif_fn(&signal_ctxt).await.ok();
});
let mut lock = ctrl.config.lock().await;
lock.$name = value;
lock.write();
}
}
}).await;
});
@@ -97,7 +105,7 @@ macro_rules! task_watch_item_notify {
concat_idents::concat_idents!(fn_name = watch_, $name {
async fn fn_name(
&self,
signal_ctxt: SignalContext<'static>,
signal_ctxt: SignalEmitter<'static>,
) -> Result<(), RogError> {
use zbus::export::futures_util::StreamExt;
@@ -140,8 +148,8 @@ pub trait ReloadAndNotify {
fn reload_and_notify(
&mut self,
signal_context: &SignalContext<'static>,
data: Self::Data,
signal_context: &SignalEmitter<'static>,
data: Self::Data
) -> impl Future<Output = Result<(), RogError>> + Send;
}
@@ -149,9 +157,9 @@ pub trait ZbusRun {
fn add_to_server(self, server: &mut Connection) -> impl Future<Output = ()> + Send;
fn add_to_server_helper(
iface: impl zbus::Interface,
iface: impl Interface,
path: &str,
server: &mut Connection,
server: &mut Connection
) -> impl Future<Output = ()> + Send {
async move {
server
@@ -171,8 +179,8 @@ pub trait ZbusRun {
pub trait CtrlTask {
fn zbus_path() -> &'static str;
fn signal_context(connection: &Connection) -> Result<SignalContext<'static>, zbus::Error> {
SignalContext::new(connection, Self::zbus_path())
fn signal_context(connection: &Connection) -> Result<SignalEmitter<'static>, zbus::Error> {
SignalEmitter::new(connection, Self::zbus_path())
}
/// Implement to set up various tasks that may be required, using the
@@ -180,7 +188,7 @@ pub trait CtrlTask {
/// separate thread.
fn create_tasks(
&self,
signal: SignalContext<'static>,
signal: SignalEmitter<'static>
) -> impl Future<Output = Result<(), RogError>> + Send;
// /// Create a timed repeating task
@@ -204,7 +212,7 @@ pub trait CtrlTask {
mut on_prepare_for_sleep: F1,
mut on_prepare_for_shutdown: F2,
mut on_lid_change: F3,
mut on_external_power_change: F4,
mut on_external_power_change: F4
) -> impl Future<Output = ()> + Send
where
F1: FnMut(bool) -> Fut1 + Send + 'static,
@@ -214,7 +222,7 @@ pub trait CtrlTask {
Fut1: Future<Output = ()> + Send,
Fut2: Future<Output = ()> + Send,
Fut3: Future<Output = ()> + Send,
Fut4: Future<Output = ()> + Send,
Fut4: Future<Output = ()> + Send
{
async {
let connection = Connection::system()
@@ -294,10 +302,10 @@ pub trait GetSupported {
pub async fn start_tasks<T>(
mut zbus: T,
connection: &mut Connection,
signal_ctx: SignalContext<'static>,
signal_ctx: SignalEmitter<'static>
) -> Result<(), RogError>
where
T: ZbusRun + Reloadable + CtrlTask + Clone,
T: ZbusRun + Reloadable + CtrlTask + Clone
{
let zbus_clone = zbus.clone();

View File

@@ -13,6 +13,3 @@ serde.workspace = true
ron.workspace = true
log.workspace = true
[dev-dependencies]
cargo-husky.workspace = true

View File

@@ -19,7 +19,7 @@ use serde::Serialize;
/// implemented, the rest are intended to be free methods.
pub trait StdConfig
where
Self: Serialize + DeserializeOwned,
Self: Serialize + DeserializeOwned
{
/// Taking over the standard `new()` to ensure things can be generic
fn new() -> Self;
@@ -146,11 +146,7 @@ where
/// Renames the existing file to `<file>-old`
fn rename_file_old(&self) {
warn!(
"Renaming {} to {}-old and recreating config",
self.file_name(),
self.file_name()
);
warn!("Renaming {} to {}-old", self.file_name(), self.file_name());
let mut cfg_old = self.file_path().to_string_lossy().to_string();
cfg_old.push_str("-old");
std::fs::rename(self.file_path(), cfg_old).unwrap_or_else(|err| {
@@ -207,7 +203,7 @@ macro_rules! std_config_load {
/// new one created
pub trait $trait_name<$($generic),*>
where
Self: $crate::StdConfig +std::fmt::Debug + DeserializeOwned + Serialize,
Self: $crate::StdConfig + DeserializeOwned + Serialize,
$($generic: DeserializeOwned + Into<Self>),*
{
fn load(mut self) -> Self {
@@ -274,6 +270,8 @@ mod tests {
}
}
let _ = Test {};
impl crate::StdConfigLoad1<Old1> for Test {}
}
@@ -323,6 +321,8 @@ mod tests {
}
}
let _ = Test {};
impl crate::StdConfigLoad3<Old1, Old2, Old3> for Test {}
}
}

View File

@@ -1,11 +0,0 @@
[package]
name = "cpuctl"
license.workspace = true
version.workspace = true
readme.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
edition.workspace = true
[dependencies]

View File

@@ -1,14 +0,0 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}

View File

@@ -3,24 +3,24 @@
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy group="adm">
<allow send_destination="org.asuslinux.Daemon"/>
<allow receive_sender="org.asuslinux.Daemon"/>
<allow send_destination="xyz.ljones.Asusd"/>
<allow receive_sender="xyz.ljones.Asusd"/>
</policy>
<policy group="sudo">
<allow send_destination="org.asuslinux.Daemon"/>
<allow receive_sender="org.asuslinux.Daemon"/>
<allow send_destination="xyz.ljones.Asusd"/>
<allow receive_sender="xyz.ljones.Asusd"/>
</policy>
<policy group="users">
<allow send_destination="org.asuslinux.Daemon"/>
<allow receive_sender="org.asuslinux.Daemon"/>
<allow send_destination="xyz.ljones.Asusd"/>
<allow receive_sender="xyz.ljones.Asusd"/>
</policy>
<policy group="wheel">
<allow send_destination="org.asuslinux.Daemon"/>
<allow receive_sender="org.asuslinux.Daemon"/>
<allow send_destination="xyz.ljones.Asusd"/>
<allow receive_sender="xyz.ljones.Asusd"/>
</policy>
<policy user="root">
<allow own="org.asuslinux.Daemon"/>
<allow send_destination="org.asuslinux.Daemon"/>
<allow receive_sender="org.asuslinux.Daemon"/>
<allow own="xyz.ljones.Asusd"/>
<allow send_destination="xyz.ljones.Asusd"/>
<allow receive_sender="xyz.ljones.Asusd"/>
</policy>
</busconfig>

View File

@@ -7,6 +7,8 @@ ENV{DMI_FAMILY}=="*ROG*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Zephyrus*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Strix*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Vivo*ook*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Zenbook*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*ProArt*", GOTO="asusd_start"
# No match so
GOTO="asusd_end"

View File

@@ -6,13 +6,14 @@ After=nvidia-powerd.service systemd-udevd.service
[Service]
Environment=IS_SERVICE=1
Environment=RUST_LOG="info"
Environment=RUST_LOG="debug"
# required to prevent init issues with hid_asus and MCU
ExecStartPre=/bin/sleep 1
ExecStart=/usr/bin/asusd
Restart=on-failure
RestartSec=1
Type=dbus
BusName=org.asuslinux.Daemon
BusName=xyz.ljones.Asusd
SELinuxContext=system_u:system_r:unconfined_t:s0
#SELinuxContext=system_u:object_r:modules_object_t:s0
TimeoutSec=10

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 75 KiB

View File

@@ -135,7 +135,7 @@ impl crate::ZbusAdd for CtrlAnimeZbus {
}
}
#[dbus_interface(name = "org.asuslinux.Daemon")]
#[dbus_interface(name = "xyz.ljones.Asusd")]
impl CtrlAnimeZbus {
async fn <zbus method>() {
let lock = self.inner.lock().await;

View File

@@ -20,13 +20,14 @@
%global debug_package %{nil}
%endif
%global rpm_dkms_opt 1
%define specrelease %{?dist}
%define pkg_release 3%{specrelease}
# Use hardening ldflags.
%global rustflags -Clink-arg=-Wl,-z,relro,-z,now
Name: asusctl
Version: 4.7.0
Release: 2
Version: 6.0.7
Release: %{pkg_release}
Summary: Control fan speeds, LEDs, graphics modes, and charge levels for ASUS notebooks
License: MPLv2
@@ -34,8 +35,8 @@ Group: System Environment/Kernel
URL: https://gitlab.com/asus-linux/asusctl
Source: %{name}-%{version}.tar.gz
Source1: vendor-%{name}-%{version}.tar.gz
Source2: cargo_config
Source1: vendor_%{name}_%{version}.tar.xz
Source2: cargo-config
BuildRequires: cargo
BuildRequires: rust-packaging
@@ -44,12 +45,16 @@ BuildRequires: clang-devel
BuildRequires: cmake
BuildRequires: rust
BuildRequires: rust-std-static
BuildRequires: pkgconfig(expat)
BuildRequires: pkgconfig(dbus-1)
BuildRequires: pkgconfig(libudev)
BuildRequires: pkgconfig(xkbcommon)
BuildRequires: pkgconfig(libzstd)
BuildRequires: pkgconfig(gtk+-3.0)
BuildRequires: pkgconfig(gdk-3.0)
BuildRequires: desktop-file-utils
Requires: libappindicator-gtk3
# expat-devel pcre2-devel
%description
asus-nb-ctrl is a utility for Linux to control many aspects of various
@@ -67,9 +72,10 @@ 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.
%prep
# %setup -D -T -a 1 -c -n %{name}-%{version}/vendor
# %setup -D -T -a 0 -c
%autosetup
%setup -D -T -a 1 -c -n %{name}-%{version}/vendor
cd ..
%setup -D -T -a 1
mv Cargo.lock{,.bak}
%cargo_prep
@@ -86,7 +92,7 @@ export RUSTFLAGS="%{rustflags}"
export RUSTFLAGS="%{rustflags}"
mkdir -p "%{buildroot}/%{_bindir}" "%{buildroot}%{_docdir}"
%make_install
install -D -m 0644 README.md %{buildroot}/%{_docdir}/%{name}/README.md
install -D -m 0644 rog-anime/README.md %{buildroot}/%{_docdir}/%{name}/README-anime.md
install -D -m 0644 rog-anime/data/diagonal-template.png %{buildroot}/%{_docdir}/%{name}/diagonal-template.png

View File

@@ -1,4 +1,4 @@
use log::{info, warn};
use log::warn;
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Clone)]
pub struct DMIID {
@@ -12,7 +12,7 @@ pub struct DMIID {
pub bios_vendor: String,
pub bios_version: String,
pub product_family: String,
pub product_name: String,
pub product_name: String
}
impl DMIID {
@@ -33,8 +33,6 @@ impl DMIID {
})?;
if let Some(device) = (result).next() {
info!("Found dmi ID info at {:?}", device.sysname());
return Ok(Self {
id_model: device
.property_value("ID_MODEL")
@@ -79,7 +77,7 @@ impl DMIID {
product_name: device
.attribute_value("product_name")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
.unwrap_or("Unknown".to_string())
});
}
Err("dmi not found".into())

View File

@@ -28,17 +28,9 @@ gif.workspace = true
log.workspace = true
serde.workspace = true
serde_derive.workspace = true
glam.workspace = true
typeshare.workspace = true
zbus = { workspace = true, optional = true }
dmi_id = { path = "../dmi-id", optional = true }
[dev-dependencies]
cargo-husky.workspace = true
[package.metadata.cargo-machete]
ignored = ["serde"]

View File

@@ -3,9 +3,9 @@ use std::str::FromStr;
use std::thread::sleep;
use std::time::{Duration, Instant};
use dmi_id::DMIID;
use log::info;
use serde_derive::{Deserialize, Serialize};
use typeshare::typeshare;
use serde::{Deserialize, Serialize};
#[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value};
@@ -22,27 +22,29 @@ const BLOCK_END: usize = 634;
const PANE_LEN: usize = BLOCK_END - BLOCK_START;
/// First packet is for GA401 + GA402
pub const USB_PREFIX1: [u8; 7] = [0x5e, 0xc0, 0x02, 0x01, 0x00, 0x73, 0x02];
pub const USB_PREFIX1: [u8; 7] = [
0x5e, 0xc0, 0x02, 0x01, 0x00, 0x73, 0x02
];
/// Second packet is for GA401 + GA402
pub const USB_PREFIX2: [u8; 7] = [0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02];
pub const USB_PREFIX2: [u8; 7] = [
0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02
];
/// Third packet is for GA402 matrix
pub const USB_PREFIX3: [u8; 7] = [0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02];
pub const USB_PREFIX3: [u8; 7] = [
0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02
];
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[typeshare]
#[derive(Default, Deserialize, PartialEq, Eq, Clone, Copy, Serialize, Debug)]
pub struct Animations {
pub boot: AnimBooting,
pub awake: AnimAwake,
pub sleep: AnimSleeping,
pub shutdown: AnimShutdown,
pub shutdown: AnimShutdown
}
// TODO: move this out
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type))]
#[typeshare]
#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub struct DeviceState {
pub display_enabled: bool,
@@ -52,17 +54,17 @@ pub struct DeviceState {
pub off_when_unplugged: bool,
pub off_when_suspended: bool,
pub off_when_lid_closed: bool,
pub brightness_on_battery: Brightness,
pub brightness_on_battery: Brightness
}
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type), zvariant(signature = "s"))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize, Default)]
pub enum AnimeType {
GA401,
GA402,
GU604,
Unknown,
#[default]
Unsupported
}
impl FromStr for AnimeType {
@@ -73,17 +75,30 @@ impl FromStr for AnimeType {
"ga401" | "GA401" => Self::GA401,
"ga402" | "GA402" => Self::GA402,
"gu604" | "GU604" => Self::GU604,
_ => Self::Unknown,
_ => Self::Unsupported
})
}
}
impl AnimeType {
pub fn from_dmi() -> Self {
let board_name = DMIID::new().unwrap_or_default().board_name.to_uppercase();
if board_name.contains("GA401I") || board_name.contains("GA401Q") {
AnimeType::GA401
} else if board_name.contains("GA402R") || board_name.contains("GA402X") {
AnimeType::GA402
} else if board_name.contains("GU604V") {
AnimeType::GU604
} else {
AnimeType::Unsupported
}
}
/// The width of diagonal images
pub fn width(&self) -> usize {
match self {
AnimeType::GU604 => 70,
_ => 74,
_ => 74
}
}
@@ -92,7 +107,7 @@ impl AnimeType {
match self {
AnimeType::GA401 => 36,
AnimeType::GU604 => 43,
_ => 39,
_ => 39
}
}
@@ -101,7 +116,7 @@ impl AnimeType {
match self {
AnimeType::GA401 => PANE_LEN * 2,
AnimeType::GU604 => PANE_LEN * 3,
_ => PANE_LEN * 3,
_ => PANE_LEN * 3
}
}
}
@@ -112,7 +127,7 @@ impl AnimeType {
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AnimeDataBuffer {
data: Vec<u8>,
anime: AnimeType,
anime: AnimeType
}
impl AnimeDataBuffer {
@@ -122,7 +137,7 @@ impl AnimeDataBuffer {
AnimeDataBuffer {
data: vec![0u8; len],
anime,
anime
}
}
@@ -165,7 +180,7 @@ impl TryFrom<AnimeDataBuffer> for AnimePacketType {
let mut buffers = match anime.anime {
AnimeType::GA401 => vec![[0; 640]; 2],
AnimeType::GA402 | AnimeType::GU604 | AnimeType::Unknown => vec![[0; 640]; 3],
AnimeType::GA402 | AnimeType::GU604 | AnimeType::Unsupported => vec![[0; 640]; 3]
};
for (idx, chunk) in anime.data.as_slice().chunks(PANE_LEN).enumerate() {
@@ -176,7 +191,7 @@ impl TryFrom<AnimeDataBuffer> for AnimePacketType {
if matches!(
anime.anime,
AnimeType::GA402 | AnimeType::GU604 | AnimeType::Unknown
AnimeType::GA402 | AnimeType::GU604 | AnimeType::Unsupported
) {
buffers[2][..7].copy_from_slice(&USB_PREFIX3);
}

View File

@@ -3,6 +3,8 @@
use std::path::Path;
use std::time::Duration;
use log::error;
use crate::data::AnimeDataBuffer;
use crate::error::{AnimeError, Result};
use crate::AnimeType;
@@ -18,7 +20,7 @@ impl AnimeDiagonal {
Self(
anime_type,
vec![vec![0; anime_type.width()]; anime_type.height()],
duration,
duration
)
}
@@ -47,9 +49,12 @@ impl AnimeDiagonal {
path: &Path,
duration: Option<Duration>,
bright: f32,
anime_type: AnimeType,
anime_type: AnimeType
) -> Result<Self> {
let data = std::fs::read(path)?;
let data = std::fs::read(path).map_err(|e| {
error!("Could not open {path:?}: {e:?}");
e
})?;
let data = std::io::Cursor::new(data);
let decoder = png_pong::Decoder::new(data)?.into_steps();
let png_pong::Step { raster, delay: _ } = decoder.last().ok_or(AnimeError::NoFrames)??;
@@ -81,7 +86,7 @@ impl AnimeDiagonal {
png_pong::PngRaster::Rgba16(ras) => {
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)
@@ -91,9 +96,9 @@ impl AnimeDiagonal {
ras: &pix::Raster<P>,
matrix: &mut AnimeDiagonal,
bright: f32,
grey: bool,
grey: bool
) where
P: pix::el::Pixel<Chan = pix::chan::Ch8>,
P: pix::el::Pixel<Chan = pix::chan::Ch8>
{
let width = ras.width();
for (y, row) in ras.pixels().chunks(width as usize).enumerate() {
@@ -116,9 +121,9 @@ impl AnimeDiagonal {
ras: &pix::Raster<P>,
matrix: &mut AnimeDiagonal,
bright: f32,
grey: bool,
grey: bool
) where
P: pix::el::Pixel<Chan = pix::chan::Ch16>,
P: pix::el::Pixel<Chan = pix::chan::Ch16>
{
let width = ras.width();
for (y, row) in ras.pixels().chunks(width as usize).enumerate() {
@@ -141,7 +146,7 @@ impl AnimeDiagonal {
match anime_type {
AnimeType::GA401 => self.to_ga401_packets(),
AnimeType::GU604 => self.to_gu604_packets(),
_ => self.to_ga402_packets(),
_ => self.to_ga402_packets()
}
}
@@ -219,7 +224,7 @@ impl AnimeDiagonal {
x: usize,
y: usize,
start_index: &mut usize,
len: usize,
len: usize
) {
buf[*start_index..*start_index + len].copy_from_slice(&anime.get_row(x, y, len));
*start_index += len;
@@ -302,7 +307,7 @@ impl AnimeDiagonal {
x: usize,
y: usize,
start_index: &mut usize,
len: usize,
len: usize
) {
buf[*start_index..*start_index + len].copy_from_slice(&anime.get_row(x, y, len));
*start_index += len;

View File

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

View File

@@ -4,7 +4,8 @@ use std::path::Path;
use std::time::Duration;
use glam::Vec2;
use serde_derive::{Deserialize, Serialize};
use log::error;
use serde::{Deserialize, Serialize};
use crate::error::{AnimeError, Result};
use crate::{AnimeDataBuffer, AnimeDiagonal, AnimeImage, AnimeType, Pixel};
@@ -15,7 +16,7 @@ pub struct AnimeFrame {
/// the `asusd` daemon over dbus or converted to USB packet with
/// `AnimePacketType::from(buffer)`
data: AnimeDataBuffer,
delay: Duration,
delay: Duration
}
impl AnimeFrame {
@@ -43,7 +44,7 @@ pub enum AnimTime {
/// Run for infinite time
Infinite,
/// Fade in, play for, fade out
Fade(Fade),
Fade(Fade)
}
impl Default for AnimTime {
@@ -58,7 +59,7 @@ impl Default for AnimTime {
pub struct Fade {
fade_in: Duration,
show_for: Option<Duration>,
fade_out: Duration,
fade_out: Duration
}
impl Fade {
@@ -66,7 +67,7 @@ impl Fade {
Self {
fade_in,
show_for,
fade_out,
fade_out
}
}
@@ -99,7 +100,7 @@ impl AnimeGif {
file_name: &Path,
duration: AnimTime,
brightness: f32,
anime_type: AnimeType,
anime_type: AnimeType
) -> Result<Self> {
let mut matrix = AnimeDiagonal::new(anime_type, None);
@@ -107,7 +108,10 @@ impl AnimeGif {
// Configure the decoder such that it will expand the image to RGBA.
decoder.set_color_output(gif::ColorOutput::RGBA);
// Read the file header
let file = File::open(file_name)?;
let file = File::open(file_name).map_err(|e| {
error!("Could not open {file_name:?}: {e:?}");
e
})?;
let mut decoder = decoder.read_info(file)?;
let mut frames = Vec::default();
@@ -138,7 +142,7 @@ impl AnimeGif {
frames.push(AnimeFrame {
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))
@@ -150,7 +154,7 @@ impl AnimeGif {
file_name: &Path,
anime_type: AnimeType,
duration: AnimTime,
brightness: f32,
brightness: f32
) -> Result<Self> {
let image = AnimeDiagonal::from_png(file_name, None, brightness, anime_type)?;
@@ -166,7 +170,7 @@ impl AnimeGif {
let single = AnimeFrame {
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];
@@ -183,15 +187,17 @@ impl AnimeGif {
translation: Vec2,
duration: AnimTime,
brightness: f32,
anime_type: AnimeType,
anime_type: AnimeType
) -> Result<Self> {
let mut frames = Vec::new();
let mut decoder = gif::DecodeOptions::new();
// Configure the decoder such that it will expand the image to RGBA.
decoder.set_color_output(gif::ColorOutput::RGBA);
// Read the file header
let file = File::open(file_name)?;
let file = File::open(file_name).map_err(|e| {
error!("Could not open {file_name:?}: {e:?}");
e
})?;
let mut decoder = decoder.read_info(file)?;
let height = decoder.height();
@@ -205,7 +211,7 @@ impl AnimeGif {
brightness,
pixels,
decoder.width() as u32,
anime_type,
anime_type
)?;
while let Some(frame) = decoder.read_next_frame()? {
@@ -220,7 +226,7 @@ impl AnimeGif {
brightness,
pixels,
width as u32,
anime_type,
anime_type
)?;
}
for (y, row) in frame.buffer.chunks(frame.width as usize * 4).enumerate() {
@@ -233,7 +239,7 @@ impl AnimeGif {
(x + frame.left as usize) + ((y + frame.top as usize) * width as usize);
image.get_mut()[pos] = Pixel {
color: ((px[0] as u32 + px[1] as u32 + px[2] as u32) / 3),
alpha: 1.0,
alpha: 1.0
};
}
}
@@ -241,7 +247,7 @@ impl AnimeGif {
frames.push(AnimeFrame {
data: <AnimeDataBuffer>::try_from(&image)?,
delay: Duration::from_millis(wait as u64),
delay: Duration::from_millis(wait as u64)
});
}
Ok(Self(frames, duration))
@@ -259,7 +265,7 @@ impl AnimeGif {
translation: Vec2,
duration: AnimTime,
brightness: f32,
anime_type: AnimeType,
anime_type: AnimeType
) -> Result<Self> {
let image =
AnimeImage::from_png(file_name, scale, angle, translation, brightness, anime_type)?;
@@ -276,7 +282,7 @@ impl AnimeGif {
let single = AnimeFrame {
data: <AnimeDataBuffer>::try_from(&image)?,
delay: Duration::from_millis(30),
delay: Duration::from_millis(30)
};
let frames = vec![single; frame_count as usize];

View File

@@ -18,7 +18,7 @@ const HEIGHT: usize = 55;
#[derive(Debug, Clone)]
pub struct AnimeGrid {
anime_type: AnimeType,
data: [[u8; WIDTH]; HEIGHT],
data: [[u8; WIDTH]; HEIGHT]
}
impl AnimeGrid {
@@ -26,7 +26,7 @@ impl AnimeGrid {
pub fn new(anime_type: AnimeType) -> Self {
Self {
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, 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, 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);
}

View File

@@ -3,6 +3,7 @@ use std::path::Path;
pub use glam::Vec2;
use glam::{Mat3, Vec3};
use log::error;
use crate::data::AnimeDataBuffer;
use crate::error::{AnimeError, Result};
@@ -12,7 +13,7 @@ use crate::AnimeType;
#[derive(Copy, Clone, Debug)]
pub struct Pixel {
pub color: u32,
pub alpha: f32,
pub alpha: f32
}
impl Default for Pixel {
@@ -20,7 +21,7 @@ impl Default for Pixel {
fn default() -> Self {
Pixel {
color: 0,
alpha: 0.0,
alpha: 0.0
}
}
}
@@ -75,7 +76,7 @@ pub struct AnimeImage {
/// 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
/// `AnimeType`.
anime_type: AnimeType,
anime_type: AnimeType
}
impl AnimeImage {
@@ -87,7 +88,7 @@ impl AnimeImage {
bright: f32,
pixels: Vec<Pixel>,
width: u32,
anime_type: AnimeType,
anime_type: AnimeType
) -> Result<Self> {
if !(0.0..=1.0).contains(&bright) {
return Err(AnimeError::InvalidBrightness(bright));
@@ -101,7 +102,7 @@ impl AnimeImage {
led_pos: Self::generate_image_positioning(anime_type),
img_pixels: pixels,
width,
anime_type,
anime_type
})
}
@@ -120,7 +121,7 @@ impl AnimeImage {
match anime_type {
AnimeType::GA401 => 0.8,
AnimeType::GU604 => 0.78,
_ => 0.77,
_ => 0.77
}
}
@@ -137,7 +138,7 @@ impl AnimeImage {
match anime_type {
AnimeType::GA401 => 0.3,
AnimeType::GU604 => 0.28,
_ => 0.283,
_ => 0.283
}
}
@@ -236,7 +237,7 @@ impl AnimeImage {
AnimeType::GA401 => (33.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)
}
}
@@ -245,7 +246,7 @@ impl AnimeImage {
match anime_type {
AnimeType::GA401 => 55,
AnimeType::GU604 => 62,
_ => 61,
_ => 61
}
}
@@ -256,7 +257,7 @@ impl AnimeImage {
AnimeType::GA401 => (54.0 + 1.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
_ => 61.0 * Self::scale_y(anime_type),
_ => 61.0 * Self::scale_y(anime_type)
}
}
@@ -266,11 +267,11 @@ impl AnimeImage {
AnimeType::GA401 => match y {
0 | 2 | 4 => 33,
1 | 3 => 35, // Some rows are padded
_ => 36 - y / 2,
_ => 36 - y / 2
},
AnimeType::GU604 => AnimeImage::width(anime_type, y),
// GA402 does not have padding, equivalent to width
_ => AnimeImage::width(anime_type, y),
_ => AnimeImage::width(anime_type, y)
}
}
@@ -320,7 +321,9 @@ impl AnimeImage {
let pos = Vec3::new(led.x(), led.y(), 1.0);
let x0 = led_from_px.mul_vec3(pos + Vec3::new(0.0, -0.5, 0.0));
const GROUP: [f32; 4] = [0.0, 0.5, 1.0, 1.5];
const GROUP: [f32; 4] = [
0.0, 0.5, 1.0, 1.5
];
for u in &GROUP {
for v in &GROUP {
let sample = x0 + *u * du + *v * dv;
@@ -396,7 +399,7 @@ impl AnimeImage {
let led_from_cm = Mat3::from_scale(Vec2::new(
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 =
@@ -419,9 +422,12 @@ impl AnimeImage {
angle: f32,
translation: Vec2,
bright: f32,
anime_type: AnimeType,
anime_type: AnimeType
) -> Result<Self> {
let data = std::fs::read(path)?;
let data = std::fs::read(path).map_err(|e| {
error!("Could not open {path:?}: {e:?}");
e
})?;
let data = std::io::Cursor::new(data);
let decoder = png_pong::Decoder::new(data)?.into_steps();
let png_pong::Step { raster, delay: _ } = decoder.last().ok_or(AnimeError::NoFrames)??;
@@ -460,7 +466,7 @@ impl AnimeImage {
width = ras.width();
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(
@@ -470,7 +476,7 @@ impl AnimeImage {
bright,
pixels,
width,
anime_type,
anime_type
)?;
matrix.update();
@@ -479,7 +485,7 @@ impl AnimeImage {
fn pixels_from_8bit<P>(ras: &pix::Raster<P>, grey: bool) -> Vec<Pixel>
where
P: pix::el::Pixel<Chan = pix::chan::Ch8>,
P: pix::el::Pixel<Chan = pix::chan::Ch8>
{
ras.pixels()
.iter()
@@ -491,14 +497,14 @@ impl AnimeImage {
+ (<u8>::from(px.two()) / 3) as u32
+ (<u8>::from(px.three()) / 3) as u32
},
alpha: <f32>::from(px.alpha()),
alpha: <f32>::from(px.alpha())
})
.collect()
}
fn pixels_from_16bit<P>(ras: &pix::Raster<P>, grey: bool) -> Vec<Pixel>
where
P: pix::el::Pixel<Chan = pix::chan::Ch16>,
P: pix::el::Pixel<Chan = pix::chan::Ch16>
{
ras.pixels()
.iter()
@@ -510,7 +516,7 @@ impl AnimeImage {
+ ((<u16>::from(px.two()) / 3) >> 8) as u32
+ ((<u16>::from(px.three()) / 3) >> 8) as u32
},
alpha: <f32>::from(px.alpha()),
alpha: <f32>::from(px.alpha())
})
.collect()
}
@@ -647,7 +653,7 @@ mod tests {
Vec2::default(),
AnimTime::Infinite,
1.0,
AnimeType::GA402,
AnimeType::GA402
)
.unwrap();
matrix.frames()[0].frame();

View File

@@ -3,7 +3,7 @@ use std::path::PathBuf;
use std::time::Duration;
use glam::Vec2;
use serde_derive::{Deserialize, Serialize};
use serde::{Deserialize, Serialize};
use crate::error::Result;
use crate::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType};
@@ -16,13 +16,13 @@ pub enum ActionLoader {
AsusAnimation {
file: PathBuf,
time: AnimTime,
brightness: f32,
brightness: f32
},
/// Image designed to be pixel perfect using the slanted template
AsusImage {
file: PathBuf,
time: AnimTime,
brightness: f32,
brightness: f32
},
/// Animated gif. If the file is a png a static gif is created using the
/// `time` properties
@@ -32,7 +32,7 @@ pub enum ActionLoader {
angle: f32,
translation: Vec2,
time: AnimTime,
brightness: f32,
brightness: f32
},
Image {
file: PathBuf,
@@ -40,10 +40,10 @@ pub enum ActionLoader {
angle: f32,
translation: Vec2,
time: AnimTime,
brightness: f32,
brightness: f32
},
/// 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
@@ -64,7 +64,7 @@ pub enum ActionData {
/// Placeholder
TimeDate,
/// Placeholder
Matrix,
Matrix
}
impl ActionData {
@@ -73,17 +73,14 @@ impl ActionData {
ActionLoader::AsusAnimation {
file,
time,
brightness,
brightness
} => ActionData::Animation(AnimeGif::from_diagonal_gif(
file,
*time,
*brightness,
anime_type,
file, *time, *brightness, anime_type
)?),
ActionLoader::AsusImage {
file,
time,
brightness,
brightness
} => match time {
AnimTime::Infinite => {
let image = AnimeDiagonal::from_png(file, None, *brightness, anime_type)?;
@@ -91,11 +88,8 @@ impl ActionData {
ActionData::Image(Box::new(data))
}
_ => ActionData::Animation(AnimeGif::from_diagonal_png(
file,
anime_type,
*time,
*brightness,
)?),
file, anime_type, *time, *brightness
)?)
},
ActionLoader::ImageAnimation {
file,
@@ -103,29 +97,17 @@ impl ActionData {
angle,
translation,
time,
brightness,
brightness
} => {
if let Some(ext) = file.extension() {
if ext.to_string_lossy().to_lowercase() == "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(
file,
*scale,
*angle,
*translation,
*time,
*brightness,
anime_type,
file, *scale, *angle, *translation, *time, *brightness, anime_type
)?)
}
ActionLoader::Image {
@@ -134,34 +116,23 @@ impl ActionData {
angle,
translation,
brightness,
time,
time
} => {
match time {
AnimTime::Infinite => {
// If no time then create a plain static image
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)?;
ActionData::Image(Box::new(data))
}
_ => 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)
}
@@ -200,7 +171,7 @@ impl Sequences {
pub fn iter(&self) -> ActionIterator<'_> {
ActionIterator {
actions: self,
next_idx: 0,
next_idx: 0
}
}
}
@@ -208,7 +179,7 @@ impl Sequences {
/// Iteractor helper for iterating over all the actions in `Sequences`
pub struct ActionIterator<'a> {
actions: &'a Sequences,
next_idx: usize,
next_idx: usize
}
impl<'a> Iterator for ActionIterator<'a> {

View File

@@ -11,8 +11,7 @@
use std::str::FromStr;
use dmi_id::DMIID;
use serde_derive::{Deserialize, Serialize};
use typeshare::typeshare;
use serde::{Deserialize, Serialize};
#[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value};
@@ -29,7 +28,6 @@ pub const PROD_ID: u16 = 0x193b;
derive(Type, Value, OwnedValue),
zvariant(signature = "u")
)]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
/// Base LED brightness of the display
pub enum Brightness {
@@ -37,7 +35,7 @@ pub enum Brightness {
Low = 1,
#[default]
Med = 2,
High = 3,
High = 3
}
impl FromStr for Brightness {
@@ -49,7 +47,7 @@ impl FromStr for Brightness {
"Low" | "low" => Brightness::Low,
"Med" | "med" => Brightness::Med,
"High" | "high" => Brightness::High,
_ => Brightness::Med,
_ => Brightness::Med
})
}
}
@@ -60,7 +58,7 @@ impl From<u8> for Brightness {
0 => Brightness::Off,
1 => Brightness::Low,
3 => Brightness::High,
_ => Brightness::Med,
_ => Brightness::Med
}
}
}
@@ -82,12 +80,11 @@ impl From<Brightness> for i32 {
derive(Type, Value, OwnedValue),
zvariant(signature = "s")
)]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub enum AnimBooting {
#[default]
GlitchConstruction = 0,
StaticEmergence = 1,
StaticEmergence = 1
}
impl FromStr for AnimBooting {
@@ -97,7 +94,7 @@ impl FromStr for AnimBooting {
match s {
"GlitchConstruction" => Ok(Self::GlitchConstruction),
"StaticEmergence" => Ok(Self::StaticEmergence),
_ => Err(AnimeError::ParseError(s.to_owned())),
_ => Err(AnimeError::ParseError(s.to_owned()))
}
}
}
@@ -107,7 +104,7 @@ impl From<i32> for AnimBooting {
match value {
0 => Self::GlitchConstruction,
1 => Self::StaticEmergence,
_ => Self::default(),
_ => Self::default()
}
}
}
@@ -123,12 +120,11 @@ impl From<AnimBooting> for i32 {
derive(Type, Value, OwnedValue),
zvariant(signature = "s")
)]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub enum AnimAwake {
#[default]
BinaryBannerScroll = 0,
RogLogoGlitch = 1,
RogLogoGlitch = 1
}
impl FromStr for AnimAwake {
@@ -138,7 +134,7 @@ impl FromStr for AnimAwake {
match s {
"BinaryBannerScroll" => Ok(Self::BinaryBannerScroll),
"RogLogoGlitch" => Ok(Self::RogLogoGlitch),
_ => Err(AnimeError::ParseError(s.to_owned())),
_ => Err(AnimeError::ParseError(s.to_owned()))
}
}
}
@@ -148,7 +144,7 @@ impl From<i32> for AnimAwake {
match value {
0 => Self::BinaryBannerScroll,
1 => Self::RogLogoGlitch,
_ => Self::default(),
_ => Self::default()
}
}
}
@@ -164,12 +160,11 @@ impl From<AnimAwake> for i32 {
derive(Type, Value, OwnedValue),
zvariant(signature = "s")
)]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub enum AnimSleeping {
#[default]
BannerSwipe = 0,
Starfield = 1,
Starfield = 1
}
impl FromStr for AnimSleeping {
@@ -179,7 +174,7 @@ impl FromStr for AnimSleeping {
match s {
"BannerSwipe" => Ok(Self::BannerSwipe),
"Starfield" => Ok(Self::Starfield),
_ => Err(AnimeError::ParseError(s.to_owned())),
_ => Err(AnimeError::ParseError(s.to_owned()))
}
}
}
@@ -189,7 +184,7 @@ impl From<i32> for AnimSleeping {
match value {
0 => Self::BannerSwipe,
1 => Self::Starfield,
_ => Self::default(),
_ => Self::default()
}
}
}
@@ -205,12 +200,11 @@ impl From<AnimSleeping> for i32 {
derive(Type, Value, OwnedValue),
zvariant(signature = "s")
)]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub enum AnimShutdown {
#[default]
GlitchOut = 0,
SeeYa = 1,
SeeYa = 1
}
impl FromStr for AnimShutdown {
@@ -220,7 +214,7 @@ impl FromStr for AnimShutdown {
match s {
"GlitchOut" => Ok(Self::GlitchOut),
"SeeYa" => Ok(Self::SeeYa),
_ => Err(AnimeError::ParseError(s.to_owned())),
_ => Err(AnimeError::ParseError(s.to_owned()))
}
}
}
@@ -230,7 +224,7 @@ impl From<i32> for AnimShutdown {
match value {
0 => Self::GlitchOut,
1 => Self::SeeYa,
_ => Self::default(),
_ => Self::default()
}
}
}
@@ -241,25 +235,25 @@ impl From<AnimShutdown> for i32 {
}
}
/// `get_anime_type` is very broad, matching on part of the laptop board name
/// only. For this reason `find_node()` must be used also to verify if the USB
/// device is available.
/// `get_maybe_anime_type` is very broad, matching on part of the laptop board
/// name only. For this reason `find_node()` must be used also to verify if the
/// USB device is available.
///
/// The currently known USB device is `19b6`.
#[inline]
pub fn get_anime_type() -> Result<AnimeType, AnimeError> {
let dmi = DMIID::new().map_err(|_| AnimeError::NoDevice)?; // TODO: better error
pub fn get_anime_type() -> AnimeType {
let dmi = DMIID::new().unwrap_or_default();
let board_name = dmi.board_name;
if board_name.contains("GA401I") || board_name.contains("GA401Q") {
return Ok(AnimeType::GA401);
} else if board_name.contains("GA402R") {
return Ok(AnimeType::GA402);
AnimeType::GA401
} else if board_name.contains("GA402R") || board_name.contains("GA402X") {
AnimeType::GA402
} else if board_name.contains("GU604V") {
return Ok(AnimeType::GU604);
AnimeType::GU604
} else {
AnimeType::Unsupported
}
log::warn!("AniMe Matrix device found but not yet supported, will default to a GA402 layout");
Ok(AnimeType::Unknown)
}
/// Get the two device initialization packets. These are required for device
@@ -332,7 +326,7 @@ pub const fn pkt_set_builtin_animations(
boot: AnimBooting,
awake: AnimAwake,
sleep: AnimSleeping,
shutdown: AnimShutdown,
shutdown: AnimShutdown
) -> [u8; PACKET_SIZE] {
let mut pkt = [0; PACKET_SIZE];
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, 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, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
];
let pkt1_check = [
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, 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,
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(
@@ -110,7 +110,7 @@ mod tests {
0.0,
vec![Pixel::default(); 1000],
100,
AnimeType::GA401,
AnimeType::GA401
)
.unwrap();
matrix.edge_outline();
@@ -175,7 +175,7 @@ mod tests {
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, 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 = [
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, 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,
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"));

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, 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
];
let pkt1_check = [
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,
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
];
let pkt2_check = [
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
];
let mut matrix = AnimeImage::new(
@@ -158,7 +158,7 @@ mod tests {
0.0,
vec![Pixel::default(); 1000],
100,
AnimeType::GA402,
AnimeType::GA402
)
.unwrap();
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, 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
];
let pkt1_check = [
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, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
];
let pkt2_check = [
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
];
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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 = [
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, 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 = [
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
];
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, 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
];
let pkt1_check = [
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, 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 = [
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
];
let mut matrix = AnimeImage::new(
@@ -158,7 +158,7 @@ mod tests {
0.0,
vec![Pixel::default(); 1000],
100,
AnimeType::GU604,
AnimeType::GU604
)
.unwrap();
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, 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
];
let pkt1_check = [
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, 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, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
];
let pkt2_check = [
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
];
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));

View File

@@ -18,15 +18,10 @@ dbus = ["zbus"]
[dependencies]
serde.workspace = true
serde_derive.workspace = true
zbus = { workspace = true, optional = true }
dmi_id = { path = "../dmi-id" }
# cli and logging
log.workspace = true
typeshare.workspace = true
ron = { version = "*", optional = true }
[dev-dependencies]
cargo-husky.workspace = true

View File

@@ -3,7 +3,16 @@
device_name: "FA506I",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "FA506N",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -12,7 +21,7 @@
device_name: "FA506Q",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -21,7 +30,16 @@
device_name: "FA507",
product_id: "",
layout_name: "fa507",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "FA617NS",
product_id: "",
layout_name: "fa617ns",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -30,7 +48,7 @@
device_name: "FX505",
product_id: "",
layout_name: "fx505d",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -39,7 +57,7 @@
device_name: "FX506",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -48,7 +66,7 @@
device_name: "FX507Z",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -57,7 +75,25 @@
device_name: "FX516P",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe],
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "FX517Z",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "FX617X",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -66,25 +102,25 @@
device_name: "FX705D",
product_id: "",
layout_name: "fx505d",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "G512",
device_name: "G512L",
product_id: "",
layout_name: "g512",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G513I",
product_id: "",
layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]),
power_zones: [Keyboard, Lightbar],
@@ -92,89 +128,53 @@
(
device_name: "G513Q",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "G513QE",
product_id: "",
layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G513QY",
device_name: "G513QR",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G513R",
product_id: "",
layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]),
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G513RW",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G531",
device_name: "G513RC",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "G531GD",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "G531GT",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "G531GU",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow],
layout_name: "g513i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]),
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G531GV",
device_name: "G531G",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -183,7 +183,7 @@
device_name: "G531GW",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -192,7 +192,7 @@
device_name: "G532",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
@@ -201,16 +201,25 @@
device_name: "G533Q",
product_id: "1866",
layout_name: "g533q-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G533QS",
product_id: "18c6",
layout_name: "g533q-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G533Z",
product_id: "",
layout_name: "g533q-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
@@ -219,16 +228,34 @@
device_name: "G614J",
product_id: "",
layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, Pulse, Strobe, Rainbow],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G614JJ",
product_id: "",
layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G614JZ",
product_id: "",
layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G634J",
product_id: "",
layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar, Logo, RearGlow],
@@ -237,7 +264,7 @@
device_name: "G712LI",
product_id: "",
layout_name: "gl503",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -246,7 +273,7 @@
device_name: "G712LV",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -255,7 +282,7 @@
device_name: "G712LW",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -264,43 +291,61 @@
device_name: "G713IC",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G713P",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G713QC",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G713QE",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G713QM",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
layout_name: "gx502",
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: "G713QR",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G713RC",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]),
power_zones: [Keyboard, Lightbar],
@@ -309,7 +354,7 @@
device_name: "G713RM",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -318,7 +363,7 @@
device_name: "G713RS",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
@@ -327,34 +372,25 @@
device_name: "G713RW",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4, BarLeft, BarRight],
advanced_type: None,
power_zones: [Keyboard],
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G731",
product_id: "",
layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "G731GT",
device_name: "G731G",
product_id: "",
layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "G731GU",
product_id: "",
layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -363,7 +399,7 @@
device_name: "G731GV",
product_id: "",
layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -372,7 +408,7 @@
device_name: "G731GW",
product_id: "",
layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -381,16 +417,16 @@
device_name: "G733C",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar, Logo, Lid],
),
(
device_name: "G733PZ",
device_name: "G733P",
product_id: "",
layout_name: "g733pz-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
@@ -399,7 +435,7 @@
device_name: "G733Q",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
@@ -408,16 +444,16 @@
device_name: "G733Z",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
power_zones: [Keyboard, Lightbar, Logo, Lid],
),
(
device_name: "G814J",
product_id: "",
layout_name: "g814ji-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
@@ -426,11 +462,20 @@
device_name: "G834J",
product_id: "",
layout_name: "g814ji-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar, Logo, RearGlow],
),
(
device_name: "GA401I",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GA401Q",
product_id: "",
@@ -444,7 +489,16 @@
device_name: "GA402N",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse, Rainbow, Strobe],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GA402NU-0002",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -453,7 +507,7 @@
device_name: "GA402R",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse, Rainbow],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -462,13 +516,13 @@
device_name: "GA402X",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse, Rainbow],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GA402XV",
device_name: "GA402XV-NC012",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
@@ -489,7 +543,7 @@
device_name: "GA503Q",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse, Rainbow, Strobe],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -507,16 +561,25 @@
device_name: "GA503R",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse, Rainbow, Strobe],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GA605W",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
),
(
device_name: "GL503",
product_id: "",
layout_name: "gl503",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -525,7 +588,7 @@
device_name: "GL503V",
product_id: "",
layout_name: "gl503",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -534,7 +597,7 @@
device_name: "GL504G",
product_id: "",
layout_name: "gl503",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4, Logo, BarLeft, BarRight],
advanced_type: None,
power_zones: [Keyboard],
@@ -543,7 +606,7 @@
device_name: "GL531",
product_id: "",
layout_name: "g512",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
@@ -552,7 +615,7 @@
device_name: "GL553V",
product_id: "",
layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -561,7 +624,7 @@
device_name: "GL703G",
product_id: "",
layout_name: "gl503",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -570,43 +633,25 @@
device_name: "GM501G",
product_id: "",
layout_name: "fa507",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GU502",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "GU502L",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "GU502LU",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GU603H",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
@@ -615,7 +660,7 @@
device_name: "GU603V",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
@@ -624,7 +669,7 @@
device_name: "GU603Z",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
@@ -633,7 +678,7 @@
device_name: "GU604V",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
@@ -642,7 +687,7 @@
device_name: "GU605M",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
@@ -678,7 +723,7 @@
device_name: "GV601R",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -696,7 +741,7 @@
device_name: "GV604V",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -705,7 +750,7 @@
device_name: "GX502",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
@@ -714,7 +759,7 @@
device_name: "GX531",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -723,7 +768,7 @@
device_name: "GX550L",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
@@ -732,7 +777,7 @@
device_name: "GX551Q",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
@@ -741,7 +786,16 @@
device_name: "GX650P",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "GX650R",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
@@ -750,7 +804,7 @@
device_name: "GX701",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
@@ -759,7 +813,7 @@
device_name: "GX703H",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -782,15 +836,6 @@
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GZ301Z",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GZ301Z",
product_id: "18c6",
@@ -813,9 +858,18 @@
device_name: "RC71L",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
power_zones: [Ally],
),
(
device_name: "RC72L",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Ally],
),
])

View File

@@ -1,6 +1,8 @@
use std::env;
use dmi_id::DMIID;
use log::{error, info, warn};
use serde_derive::{Deserialize, Serialize};
use serde::{Deserialize, Serialize};
use crate::keyboard::AdvancedAuraType;
use crate::{AuraModeNum, AuraZone, PowerZones};
@@ -52,7 +54,7 @@ pub struct LedSupportData {
#[serde(default)]
pub advanced_type: AdvancedAuraType,
/// If empty will default to `Keyboard` power zone
pub power_zones: Vec<PowerZones>,
pub power_zones: Vec<PowerZones>
}
impl LedSupportData {
@@ -60,17 +62,20 @@ impl LedSupportData {
/// matches against laptops first, then will proceed with matching the
/// `device_name` if there are no DMI matches.
pub fn get_data(product_id: &str) -> Self {
let dmi = DMIID::new().unwrap_or_default();
let mut dmi = DMIID::new().unwrap_or_default();
if let Ok(board_name) = env::var("BOARD_NAME") {
dmi.board_name = board_name;
}
// let prod_family = dmi.product_family().expect("Could not get
// product_family");
if let Some(data) = LedSupportFile::load_from_supoprt_db() {
if let Some(data) = data.match_device(&dmi.board_name, product_id) {
return data;
}
return data.match_device(&dmi.board_name, product_id);
}
info!("Using generic LED control for keyboard brightness only");
LedSupportData::default()
info!("Using generic LED control for keyboard brightness only. No aura_support file found");
let mut data = LedSupportData::default();
data.power_zones.push(PowerZones::Keyboard);
data
}
}
@@ -84,7 +89,7 @@ impl LedSupportFile {
/// The list is stored in ordered format, so the iterator must be reversed
/// to ensure we match to *whole names* first before doing a glob match
fn match_device(&self, device_name: &str, product_id: &str) -> Option<LedSupportData> {
fn match_device(&self, device_name: &str, product_id: &str) -> LedSupportData {
for config in self.0.iter().rev() {
if device_name.contains(&config.device_name) {
info!("Matched to {}", config.device_name);
@@ -92,15 +97,27 @@ impl LedSupportFile {
info!("Checking product ID");
if config.product_id == product_id {
info!("Matched to {}", config.product_id);
return Some(config.clone());
return config.clone();
} else {
continue;
}
}
return Some(config.clone());
return config.clone();
}
}
None
warn!(
"the aura_support.ron file has no entry for this model: {device_name}, {product_id}. \
Using a default"
);
LedSupportData {
device_name: device_name.to_owned(),
product_id: product_id.to_owned(),
layout_name: "Default".to_owned(),
basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![],
advanced_type: AdvancedAuraType::None,
power_zones: vec![PowerZones::Keyboard]
}
}
/// Load `LedSupportFile` from the `aura_support.ron` file at
@@ -154,6 +171,7 @@ impl LedSupportFile {
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::PathBuf;
@@ -173,9 +191,16 @@ mod tests {
product_id: String::new(),
layout_name: "ga401".to_owned(),
basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![AuraZone::Key1, AuraZone::Logo, AuraZone::BarLeft],
basic_zones: vec![
AuraZone::Key1,
AuraZone::Logo,
AuraZone::BarLeft,
],
advanced_type: AdvancedAuraType::Zoned(vec![LedCode::LightbarRight]),
power_zones: vec![PowerZones::Keyboard, PowerZones::RearGlow],
power_zones: vec![
PowerZones::Keyboard,
PowerZones::RearGlow,
]
};
assert!(ron::to_string(&led).is_ok());
@@ -195,6 +220,9 @@ mod tests {
let mut tmp_sort = tmp.clone();
tmp_sort.0.sort_by(|a, b| a.product_id.cmp(&b.product_id));
tmp_sort.0.sort_by(|a, b| a.device_name.cmp(&b.device_name));
for model in tmp_sort.0.iter_mut() {
model.basic_modes.sort_by_key(|a| *a as u8);
}
if tmp != tmp_sort {
let sorted =
ron::ser::to_string_pretty(&tmp_sort, PrettyConfig::new().depth_limit(2)).unwrap();
@@ -217,4 +245,31 @@ mod tests {
ron::ser::to_string_pretty(&tmp, my_config).unwrap()
);
}
#[test]
fn find_data_file_groups() {
let mut data = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
data.push("data/aura_support.ron");
let buf = std::fs::read_to_string(&data).unwrap();
let tmp = ron::from_str::<LedSupportFile>(&buf).unwrap();
let mut modes: HashMap<Vec<AuraModeNum>, Vec<String>> = HashMap::new();
for entry in tmp.0 {
if let Some(modes) = modes.get_mut(&entry.basic_modes) {
modes.push(entry.device_name);
} else {
modes.insert(entry.basic_modes, vec![entry.device_name]);
}
}
dbg!(modes);
// let my_config = PrettyConfig::new().depth_limit(2);
// println!(
// "RON: {}",
// ron::ser::to_string_pretty(&tmp, my_config).unwrap()
// );
}
}

View File

@@ -1,15 +1,13 @@
use std::fmt::Display;
use std::str::FromStr;
use serde_derive::{Deserialize, Serialize};
use typeshare::typeshare;
use serde::{Deserialize, Serialize};
#[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value};
use crate::error::Error;
use crate::LED_MSG_LEN;
use crate::AURA_LAPTOP_LED_MSG_LEN;
#[typeshare]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[cfg_attr(
feature = "dbus",
@@ -21,7 +19,7 @@ pub enum LedBrightness {
Low = 1,
#[default]
Med = 2,
High = 3,
High = 3
}
impl LedBrightness {
@@ -30,7 +28,7 @@ impl LedBrightness {
Self::Off => Self::Low,
Self::Low => Self::Med,
Self::Med => Self::High,
Self::High => Self::Off,
Self::High => Self::Off
}
}
@@ -39,7 +37,7 @@ impl LedBrightness {
Self::Off => Self::High,
Self::Low => Self::Off,
Self::Med => Self::Low,
Self::High => Self::Med,
Self::High => Self::Med
}
}
}
@@ -50,7 +48,7 @@ impl From<u8> for LedBrightness {
0 => LedBrightness::Off,
1 => LedBrightness::Low,
3 => LedBrightness::High,
_ => LedBrightness::Med,
_ => LedBrightness::Med
}
}
}
@@ -74,18 +72,17 @@ impl From<i32> for LedBrightness {
1 => LedBrightness::Low,
2 => LedBrightness::Med,
3 => LedBrightness::High,
_ => LedBrightness::Med,
_ => LedBrightness::Med
}
}
}
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Debug, Clone, PartialEq, Eq, Copy, Deserialize, Serialize)]
pub struct Colour {
pub r: u8,
pub g: u8,
pub b: u8,
pub b: u8
}
impl Default for Colour {
@@ -113,14 +110,18 @@ impl From<&[f32; 3]> for Colour {
Self {
r: (255.0 * c[0]) as u8,
g: (255.0 * c[1]) as u8,
b: (255.0 * c[2]) as u8,
b: (255.0 * c[2]) as u8
}
}
}
impl From<Colour> for [f32; 3] {
fn from(c: Colour) -> Self {
[c.r as f32 / 255.0, c.g as f32 / 255.0, c.b as f32 / 255.0]
[
c.r as f32 / 255.0,
c.g as f32 / 255.0,
c.b as f32 / 255.0
]
}
}
@@ -129,18 +130,19 @@ impl From<&[u8; 3]> for Colour {
Self {
r: c[0],
g: c[1],
b: c[2],
b: c[2]
}
}
}
impl From<Colour> for [u8; 3] {
fn from(c: Colour) -> Self {
[c.r, c.g, c.b]
[
c.r, c.g, c.b
]
}
}
#[typeshare]
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
@@ -151,7 +153,7 @@ pub enum Speed {
Low = 0xe1,
#[default]
Med = 0xeb,
High = 0xf5,
High = 0xf5
}
impl FromStr for Speed {
@@ -163,7 +165,7 @@ impl FromStr for Speed {
"low" => Ok(Speed::Low),
"med" => Ok(Speed::Med),
"high" => Ok(Speed::High),
_ => Err(Error::ParseSpeed),
_ => Err(Error::ParseSpeed)
}
}
}
@@ -173,7 +175,7 @@ impl From<i32> for Speed {
match value {
0 => Self::Low,
2 => Self::High,
_ => Self::Med,
_ => Self::Med
}
}
}
@@ -183,7 +185,7 @@ impl From<Speed> for i32 {
match value {
Speed::Low => 0,
Speed::Med => 1,
Speed::High => 2,
Speed::High => 2
}
}
}
@@ -193,14 +195,13 @@ impl From<Speed> for u8 {
match s {
Speed::Low => 0,
Speed::Med => 1,
Speed::High => 2,
Speed::High => 2
}
}
}
/// Used for Rainbow mode.
///
/// Enum corresponds to the required integer value
#[typeshare]
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
@@ -212,7 +213,7 @@ pub enum Direction {
Right = 0,
Left = 1,
Up = 2,
Down = 3,
Down = 3
}
impl FromStr for Direction {
@@ -225,7 +226,7 @@ impl FromStr for Direction {
"up" => Ok(Direction::Up),
"down" => Ok(Direction::Down),
"left" => Ok(Direction::Left),
_ => Err(Error::ParseDirection),
_ => Err(Error::ParseDirection)
}
}
}
@@ -236,7 +237,7 @@ impl From<i32> for Direction {
1 => Self::Left,
2 => Self::Up,
3 => Self::Down,
_ => Self::Right,
_ => Self::Right
}
}
}
@@ -248,7 +249,6 @@ impl From<Direction> for i32 {
}
/// Enum of modes that convert to the actual number required by a USB HID packet
#[typeshare]
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
@@ -261,8 +261,8 @@ pub enum AuraModeNum {
#[default]
Static = 0,
Breathe = 1,
Strobe = 2,
Rainbow = 3,
RainbowCycle = 2,
RainbowWave = 3,
Star = 4,
Rain = 5,
Highlight = 6,
@@ -270,7 +270,7 @@ pub enum AuraModeNum {
Ripple = 8,
Pulse = 10,
Comet = 11,
Flash = 12,
Flash = 12
}
impl Display for AuraModeNum {
@@ -290,8 +290,8 @@ impl From<&AuraModeNum> for &str {
match mode {
AuraModeNum::Static => "Static",
AuraModeNum::Breathe => "Breathe",
AuraModeNum::Strobe => "Strobe",
AuraModeNum::Rainbow => "Rainbow",
AuraModeNum::RainbowCycle => "RainbowCycle",
AuraModeNum::RainbowWave => "RainbowWave",
AuraModeNum::Star => "Stars",
AuraModeNum::Rain => "Rain",
AuraModeNum::Highlight => "Highlight",
@@ -299,7 +299,7 @@ impl From<&AuraModeNum> for &str {
AuraModeNum::Ripple => "Ripple",
AuraModeNum::Pulse => "Pulse",
AuraModeNum::Comet => "Comet",
AuraModeNum::Flash => "Flash",
AuraModeNum::Flash => "Flash"
}
}
}
@@ -307,8 +307,8 @@ impl From<&str> for AuraModeNum {
fn from(mode: &str) -> Self {
match mode {
"Breathe" => AuraModeNum::Breathe,
"Strobe" => AuraModeNum::Strobe,
"Rainbow" => AuraModeNum::Rainbow,
"RainbowCycle" => AuraModeNum::RainbowCycle,
"RainbowWave" => AuraModeNum::RainbowWave,
"Stars" => AuraModeNum::Star,
"Rain" => AuraModeNum::Rain,
"Highlight" => AuraModeNum::Highlight,
@@ -317,7 +317,7 @@ impl From<&str> for AuraModeNum {
"Pulse" => AuraModeNum::Pulse,
"Comet" => AuraModeNum::Comet,
"Flash" => AuraModeNum::Flash,
_ => AuraModeNum::Static,
_ => AuraModeNum::Static
}
}
}
@@ -326,8 +326,8 @@ impl From<u8> for AuraModeNum {
fn from(mode: u8) -> Self {
match mode {
1 => AuraModeNum::Breathe,
2 => AuraModeNum::Strobe,
3 => AuraModeNum::Rainbow,
2 => AuraModeNum::RainbowCycle,
3 => AuraModeNum::RainbowWave,
4 => AuraModeNum::Star,
5 => AuraModeNum::Rain,
6 => AuraModeNum::Highlight,
@@ -336,7 +336,7 @@ impl From<u8> for AuraModeNum {
10 => AuraModeNum::Pulse,
11 => AuraModeNum::Comet,
12 => AuraModeNum::Flash,
_ => AuraModeNum::Static,
_ => AuraModeNum::Static
}
}
}
@@ -360,7 +360,6 @@ impl From<AuraEffect> for AuraModeNum {
}
/// Base effects have no zoning, while multizone is 1-4
#[typeshare]
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
@@ -384,7 +383,7 @@ pub enum AuraZone {
/// The left part of a lightbar (typically on the front of laptop)
BarLeft = 6,
/// The right part of a lightbar
BarRight = 7,
BarRight = 7
}
impl FromStr for AuraZone {
@@ -401,7 +400,7 @@ impl FromStr for AuraZone {
"5" | "logo" => Ok(AuraZone::Logo),
"6" | "lightbar-left" => Ok(AuraZone::BarLeft),
"7" | "lightbar-right" => Ok(AuraZone::BarRight),
_ => Err(Error::ParseSpeed),
_ => Err(Error::ParseSpeed)
}
}
}
@@ -416,7 +415,7 @@ impl From<i32> for AuraZone {
5 => Self::Logo,
6 => Self::BarLeft,
7 => Self::BarRight,
_ => Self::default(),
_ => Self::default()
}
}
}
@@ -432,9 +431,8 @@ impl From<AuraZone> for i32 {
/// ```rust
/// // let bytes: [u8; LED_MSG_LEN] = mode.into();
/// ```
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Debug, Clone, Deserialize, Serialize)]
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct AuraEffect {
/// The effect type
pub mode: AuraModeNum,
@@ -447,7 +445,7 @@ pub struct AuraEffect {
/// One of three speeds for modes that support speed (most that animate)
pub speed: Speed,
/// Up, down, left, right. Only Rainbow mode seems to use this
pub direction: Direction,
pub direction: Direction
}
impl AuraEffect {
@@ -483,7 +481,7 @@ impl Default for AuraEffect {
colour1: Colour { r: 166, g: 0, b: 0 },
colour2: Colour { r: 0, g: 0, b: 0 },
speed: Speed::Med,
direction: Direction::Right,
direction: Direction::Right
}
}
}
@@ -494,56 +492,6 @@ impl Display for AuraEffect {
}
}
pub struct AuraParameters {
pub zone: bool,
pub colour1: bool,
pub colour2: bool,
pub speed: bool,
pub direction: bool,
}
#[allow(clippy::fn_params_excessive_bools)]
impl AuraParameters {
pub const fn new(
zone: bool,
colour1: bool,
colour2: bool,
speed: bool,
direction: bool,
) -> Self {
Self {
zone,
colour1,
colour2,
speed,
direction,
}
}
}
impl AuraEffect {
/// A helper to provide detail on what effects have which parameters, e.g
/// the static factory mode accepts only one colour.
pub const fn allowed_parameters(mode: AuraModeNum) -> AuraParameters {
match mode {
AuraModeNum::Static
| AuraModeNum::Highlight
| AuraModeNum::Pulse
| AuraModeNum::Comet
| AuraModeNum::Flash => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Breathe => AuraParameters::new(true, true, true, true, false),
AuraModeNum::Strobe | AuraModeNum::Rain => {
AuraParameters::new(true, false, false, true, false)
}
AuraModeNum::Rainbow => AuraParameters::new(true, false, false, true, true),
AuraModeNum::Star => AuraParameters::new(true, true, true, true, true),
AuraModeNum::Laser | AuraModeNum::Ripple => {
AuraParameters::new(true, true, false, true, false)
}
}
}
}
/// Parses `AuraEffect` in to packet data for writing to the USB interface
///
/// Byte structure where colour is RGB, one byte per R, G, B:
@@ -552,9 +500,9 @@ impl AuraEffect {
/// |---|---|-----|-----|---------|------|----------|---|-----------|
/// |5d |b3 |Zone |Mode |Colour 1 |Speed |Direction |00 |Colour 2 |
/// ```
impl From<&AuraEffect> for [u8; LED_MSG_LEN] {
impl From<&AuraEffect> for [u8; AURA_LAPTOP_LED_MSG_LEN] {
fn from(aura: &AuraEffect) -> Self {
let mut msg = [0u8; LED_MSG_LEN];
let mut msg = [0u8; AURA_LAPTOP_LED_MSG_LEN];
msg[0] = 0x5d;
msg[1] = 0xb3;
msg[2] = aura.zone as u8;
@@ -573,7 +521,7 @@ impl From<&AuraEffect> for [u8; LED_MSG_LEN] {
impl From<&AuraEffect> for Vec<u8> {
fn from(aura: &AuraEffect) -> Self {
let mut msg = vec![0u8; LED_MSG_LEN];
let mut msg = vec![0u8; AURA_LAPTOP_LED_MSG_LEN];
msg[0] = 0x5d;
msg[1] = 0xb3;
msg[2] = aura.zone as u8;
@@ -592,7 +540,9 @@ impl From<&AuraEffect> for Vec<u8> {
#[cfg(test)]
mod tests {
use crate::{AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed, LED_MSG_LEN};
use crate::{
AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed, AURA_LAPTOP_LED_MSG_LEN
};
#[test]
fn check_led_static_packet() {
@@ -602,18 +552,18 @@ mod tests {
colour1: Colour {
r: 0xff,
g: 0x11,
b: 0xdd,
b: 0xdd
},
colour2: Colour::default(),
speed: Speed::Med,
direction: Direction::Right,
direction: Direction::Right
};
let ar = <[u8; LED_MSG_LEN]>::from(&st);
let ar = <[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st);
println!("{:02x?}", ar);
let check = [
0x5d, 0xb3, 0x0, 0x0, 0xff, 0x11, 0xdd, 0xeb, 0x0, 0x0, 0xa6, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0,
0x0
];
assert_eq!(ar, check);
}
@@ -626,95 +576,119 @@ mod tests {
colour1: Colour {
r: 0xff,
g: 0,
b: 0,
b: 0
},
colour2: Colour { r: 0, g: 0, b: 0 },
speed: Speed::Low,
direction: Direction::Left,
direction: Direction::Left
};
let capture = [
0x5d, 0xb3, 0x01, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
0x0, 0x0
];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.zone = AuraZone::Key2;
st.colour1 = Colour {
r: 0xff,
g: 0xff,
b: 0,
b: 0
};
let capture = [
0x5d, 0xb3, 0x02, 0x00, 0xff, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
0x0, 0x0
];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.zone = AuraZone::Key3;
st.colour1 = Colour {
r: 0,
g: 0xff,
b: 0xff,
b: 0xff
};
let capture = [
0x5d, 0xb3, 0x03, 0x00, 0x00, 0xff, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
0x0, 0x0
];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.zone = AuraZone::Key4;
st.colour1 = Colour {
r: 0xff,
g: 0x00,
b: 0xff,
b: 0xff
};
let capture = [
0x5d, 0xb3, 0x04, 0x00, 0xff, 0x00, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
0x0, 0x0
];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.zone = AuraZone::Logo;
st.colour1 = Colour {
r: 0x2c,
g: 0xff,
b: 0x00,
b: 0x00
};
let capture = [
0x5d, 0xb3, 0x05, 0x00, 0x2c, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
0x0, 0x0
];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.zone = AuraZone::BarLeft;
st.colour1 = Colour {
r: 0xff,
g: 0x00,
b: 0x00,
b: 0x00
};
let capture = [
0x5d, 0xb3, 0x06, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
0x0, 0x0
];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.zone = AuraZone::BarRight;
st.colour1 = Colour {
r: 0xff,
g: 0x00,
b: 0xcd,
b: 0xcd
};
let capture = [
0x5d, 0xb3, 0x07, 0x00, 0xff, 0x00, 0xcd, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
0x0, 0x0
];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.mode = AuraModeNum::Rainbow;
st.mode = AuraModeNum::RainbowWave;
let capture = [
0x5d, 0xb3, 0x07, 0x03, 0xff, 0x00, 0xcd, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
0x0, 0x0
];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
}
}

View File

@@ -2,6 +2,7 @@ use super::{EffectState, InputForEffect};
use crate::keyboard::{KeyLayout, LedCode};
use crate::Colour;
#[allow(dead_code)]
pub struct InputBased {
led: LedCode,
colour: Colour,
@@ -10,7 +11,7 @@ pub struct InputBased {
/// - temperature
/// - fan speed
/// - time
input: Box<dyn InputForEffect>,
input: Box<dyn InputForEffect>
}
impl EffectState for InputBased {

View File

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

View File

@@ -13,7 +13,7 @@ pub struct DoomFlicker {
#[serde(skip)]
count: u8,
#[serde(skip)]
colour: Colour,
colour: Colour
}
impl DoomFlicker {
@@ -24,7 +24,7 @@ impl DoomFlicker {
count: 4,
max_percentage,
min_percentage,
start_colour: colour,
start_colour: colour
}
}
}
@@ -53,13 +53,13 @@ impl EffectState for DoomFlicker {
let max_light = Colour {
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,
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
let min_light = Colour {
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,
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
@@ -96,7 +96,7 @@ pub struct DoomLightFlash {
#[serde(skip)]
count: u8,
#[serde(skip)]
colour: Colour,
colour: Colour
}
impl DoomLightFlash {
@@ -109,7 +109,7 @@ impl DoomLightFlash {
min_percentage,
start_colour: colour,
max_time: 32,
min_time: 7,
min_time: 7
}
}
}
@@ -135,13 +135,13 @@ impl EffectState for DoomLightFlash {
let max_light = Colour {
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,
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
let min_light = Colour {
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,
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 {

View File

@@ -1,4 +1,4 @@
use serde_derive::{Deserialize, Serialize};
use serde::{Deserialize, Serialize};
mod doom;
pub use doom::*;
@@ -12,7 +12,7 @@ pub use breathe::*;
mod static_;
pub use static_::*;
use crate::keyboard::{KeyLayout, LedCode, LedUsbPackets, UsbPackets};
use crate::keyboard::{AuraLaptopUsbPackets, KeyLayout, LedCode, LedUsbPackets};
use crate::Colour;
// static mut RNDINDEX: usize = 0;
@@ -32,7 +32,7 @@ pub const RNDTABLE: [i32; 256] = [
206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95, 28, 139, 123, 98, 125, 196, 15, 70, 194, 253,
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,
84, 118, 222, 187, 136, 120, 163, 236, 249,
84, 118, 222, 187, 136, 120, 163, 236, 249
];
pub fn p_random() -> i32 {
@@ -67,7 +67,7 @@ pub(crate) trait EffectState {
#[derive(Debug, Deserialize, Serialize, Default)]
pub struct AdvancedEffects {
effects: Vec<Effect>,
zoned: bool,
zoned: bool
}
impl AdvancedEffects {
@@ -75,7 +75,7 @@ impl AdvancedEffects {
pub fn new(zoned: bool) -> Self {
Self {
effects: Default::default(),
zoned,
zoned
}
}
@@ -106,7 +106,7 @@ impl AdvancedEffects {
}
}
pub fn create_packets(&self) -> UsbPackets {
pub fn create_packets(&self) -> AuraLaptopUsbPackets {
let mut usb_packets = if self.zoned {
// TODO: figure out if that single byte difference for multizone actually
// matters
@@ -186,7 +186,7 @@ pub enum Effect {
Static(Static),
Breathe(Breathe),
DoomFlicker(DoomFlicker),
DoomLightFlash(DoomLightFlash),
DoomLightFlash(DoomLightFlash)
}
impl Default for Effect {
@@ -207,14 +207,12 @@ mod tests {
fn single_key_next_state_then_create() {
let layout = KeyLayout::default_layout();
let mut seq = AdvancedEffects::new(false);
seq.effects.push(Effect::Static(Static::new(
LedCode::F,
Colour {
seq.effects
.push(Effect::Static(Static::new(LedCode::F, Colour {
r: 255,
g: 127,
b: 0,
},
)));
b: 0
})));
seq.next_state(&layout);
let packets = seq.create_packets();
@@ -234,14 +232,14 @@ mod tests {
Colour {
r: 255,
g: 127,
b: 0,
b: 0
},
Colour {
r: 127,
g: 0,
b: 255,
b: 255
},
Speed::Med,
Speed::Med
)));
let s =
@@ -276,10 +274,10 @@ mod tests {
Colour {
r: 255,
g: 127,
b: 80,
b: 80
},
100,
10,
10
)));
seq.next_state(&layout);

View File

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

View File

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

@@ -1,6 +1,5 @@
use log::warn;
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")]
use zbus::zvariant::Type;
@@ -165,7 +164,7 @@ pub enum LedCode {
/// To be ignored by effects
Spacing,
/// To be ignored by effects
Blocking,
Blocking
}
impl LedCode {
@@ -194,8 +193,7 @@ impl LedCode {
}
/// Represents the per-key raw USB packets
#[typeshare]
pub type UsbPackets = Vec<Vec<u8>>;
pub type AuraLaptopUsbPackets = Vec<Vec<u8>>;
/// A `UsbPackets` contains all data to change the full set of keyboard
/// key colours individually.
@@ -204,16 +202,15 @@ pub type UsbPackets = Vec<Vec<u8>>;
/// to the keyboard EC. One row controls one group of keys, these keys are not
/// necessarily all on the same row of the keyboard, with some splitting between
/// two rows.
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct LedUsbPackets {
/// The packet data used to send data to the USB keyboard
usb_packets: UsbPackets,
usb_packets: AuraLaptopUsbPackets,
/// Wether or not this packet collection is zoned. The determines which
/// starting bytes are used and what the indexing is for lightbar RGB
/// colours
zoned: bool,
zoned: bool
}
impl Default for LedUsbPackets {
@@ -247,7 +244,7 @@ impl LedUsbPackets {
}
Self {
usb_packets: set,
zoned: false,
zoned: false
}
}
@@ -277,7 +274,7 @@ impl LedUsbPackets {
}
Self {
usb_packets: vec![pkt],
zoned: true,
zoned: true
}
}
@@ -472,22 +469,22 @@ impl LedUsbPackets {
}
#[inline]
pub fn get(&self) -> UsbPackets {
pub fn get(&self) -> AuraLaptopUsbPackets {
self.usb_packets.clone()
}
#[inline]
pub fn get_ref(&self) -> &UsbPackets {
pub fn get_ref(&self) -> &AuraLaptopUsbPackets {
&self.usb_packets
}
#[inline]
pub fn get_mut(&mut self) -> &mut UsbPackets {
pub fn get_mut(&mut self) -> &mut AuraLaptopUsbPackets {
&mut self.usb_packets
}
}
impl From<LedUsbPackets> for UsbPackets {
impl From<LedUsbPackets> for AuraLaptopUsbPackets {
fn from(k: LedUsbPackets) -> Self {
k.usb_packets
}
@@ -636,14 +633,14 @@ impl From<&LedCode> for &str {
LedCode::ZonedKbLeft => "Left Zone (zone 1)",
LedCode::ZonedKbLeftMid => "Center-left Zone (zone 2)",
LedCode::ZonedKbRightMid => "Center-right Zone (zone 3)",
LedCode::ZonedKbRight => "Right Zone (zone 4)",
LedCode::ZonedKbRight => "Right Zone (zone 4)"
}
}
}
#[cfg(test)]
mod tests {
use crate::keyboard::{LedCode, LedUsbPackets, UsbPackets};
use crate::keyboard::{AuraLaptopUsbPackets, LedCode, LedUsbPackets};
macro_rules! colour_check_zoned {
($zone:expr, $pkt_idx_start:expr) => {
@@ -653,7 +650,7 @@ mod tests {
c[1] = 255;
c[2] = 255;
let pkt: UsbPackets = zone.into();
let pkt: AuraLaptopUsbPackets = zone.into();
assert_eq!(pkt[0][$pkt_idx_start], 0xff);
assert_eq!(pkt[0][$pkt_idx_start + 1], 0xff);
assert_eq!(pkt[0][$pkt_idx_start + 2], 0xff);
@@ -663,7 +660,7 @@ mod tests {
#[test]
fn zone_to_packet_check() {
let zone = LedUsbPackets::new_zoned(true);
let pkt: UsbPackets = zone.into();
let pkt: AuraLaptopUsbPackets = zone.into();
assert_eq!(pkt[0][0], 0x5d);
assert_eq!(pkt[0][1], 0xbc);
assert_eq!(pkt[0][2], 0x01);
@@ -686,7 +683,7 @@ mod tests {
#[test]
fn perkey_to_packet_check() {
let per_key = LedUsbPackets::new_per_key();
let pkt: UsbPackets = per_key.into();
let pkt: AuraLaptopUsbPackets = per_key.into();
assert_eq!(pkt[0][0], 0x5d);
assert_eq!(pkt[0][1], 0xbc);
assert_eq!(pkt[0][2], 0x00);
@@ -712,7 +709,7 @@ mod tests {
c[1] = 255;
c[2] = 255;
let pkt: UsbPackets = per_key.into();
let pkt: AuraLaptopUsbPackets = per_key.into();
assert_eq!(pkt[5][30], 0xff); // D, red
assert_eq!(pkt[5][31], 0xff); // D
assert_eq!(pkt[5][32], 0xff); // D

View File

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

View File

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

View File

@@ -5,7 +5,6 @@ use std::ops::{BitAnd, BitOr};
use log::warn;
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value};
@@ -16,7 +15,6 @@ use crate::{AuraDeviceType, PowerZones};
/// - 2021+, the struct is a single zone with 4 states
/// - pre-2021, the struct is 1 or 2 zones and 3 states
/// - Tuf, the struct is 1 zone and 3 states
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuraPowerState {
@@ -25,7 +23,7 @@ pub struct AuraPowerState {
pub awake: bool,
pub sleep: bool,
/// Ignored for pre-2021 and Tuf
pub shutdown: bool,
pub shutdown: bool
}
impl Default for AuraPowerState {
@@ -36,7 +34,7 @@ impl Default for AuraPowerState {
boot: true,
awake: true,
sleep: true,
shutdown: true,
shutdown: true
}
}
}
@@ -48,12 +46,15 @@ impl AuraPowerState {
boot: true,
awake: true,
sleep: true,
shutdown: true,
shutdown: true
}
}
fn tuf_to_bytes(&self) -> Vec<u8> {
todo!("0s and 1s for bool array")
// &cmd, &boot, &awake, &sleep, &keyboard
vec![
1, self.boot as u8, self.awake as u8, self.sleep as u8, 1,
]
}
/// # Bits for older 0x1866 keyboard model
@@ -101,48 +102,53 @@ impl AuraPowerState {
]
}
fn new_to_byte(&self) -> u32 {
pub fn new_to_byte(&self) -> u32 {
match self.zone {
PowerZones::Logo => {
self.boot as u32
| (self.awake as u32) << 2
| (self.sleep as u32) << 4
| (self.shutdown as u32) << 6
| ((self.awake as u32) << 2)
| ((self.sleep as u32) << 4)
| ((self.shutdown as u32) << 6)
}
PowerZones::Ally => {
(self.boot as u32)
| ((self.awake as u32) << 1)
| ((self.sleep as u32) << 2)
| ((self.shutdown as u32) << 3)
}
PowerZones::Keyboard => {
(self.boot as u32) << 1
| (self.awake as u32) << 3
| (self.sleep as u32) << 5
| (self.shutdown as u32) << 7
((self.boot as u32) << 1)
| ((self.awake as u32) << 3)
| ((self.sleep as u32) << 5)
| ((self.shutdown as u32) << 7)
}
PowerZones::Lightbar => {
(self.boot as u32) << (7 + 2)
| (self.awake as u32) << (7 + 3)
| (self.sleep as u32) << (7 + 4)
| (self.shutdown as u32) << (7 + 5)
((self.boot as u32) << (7 + 2))
| ((self.awake as u32) << (7 + 3))
| ((self.sleep as u32) << (7 + 4))
| ((self.shutdown as u32) << (7 + 5))
}
PowerZones::Lid => {
(self.boot as u32) << (15 + 1)
| (self.awake as u32) << (15 + 2)
| (self.sleep as u32) << (15 + 3)
| (self.shutdown as u32) << (15 + 4)
((self.boot as u32) << (15 + 1))
| ((self.awake as u32) << (15 + 2))
| ((self.sleep as u32) << (15 + 3))
| ((self.shutdown as u32) << (15 + 4))
}
PowerZones::RearGlow => {
(self.boot as u32) << (23 + 1)
| (self.awake as u32) << (23 + 2)
| (self.sleep as u32) << (23 + 3)
| (self.shutdown as u32) << (23 + 4)
((self.boot as u32) << (23 + 1))
| ((self.awake as u32) << (23 + 2))
| ((self.sleep as u32) << (23 + 3))
| ((self.shutdown as u32) << (23 + 4))
}
PowerZones::KeyboardAndLightbar | PowerZones::None => 0,
PowerZones::None | PowerZones::KeyboardAndLightbar => 0
}
}
}
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct LaptopAuraPower {
pub states: Vec<AuraPowerState>,
pub states: Vec<AuraPowerState>
}
impl LaptopAuraPower {
@@ -186,41 +192,70 @@ impl LaptopAuraPower {
// TODO: use support data to setup correct zones
pub fn new(aura_type: AuraDeviceType, support_data: &LedSupportData) -> Self {
match aura_type {
AuraDeviceType::Unknown | AuraDeviceType::LaptopPost2021 => {
AuraDeviceType::Unknown | AuraDeviceType::Ally | AuraDeviceType::LaptopKeyboard2021 => {
let mut states = Vec::new();
for zone in support_data.power_zones.iter() {
states.push(AuraPowerState::default_for(*zone))
}
Self { states }
}
AuraDeviceType::LaptopPre2021 => {
AuraDeviceType::LaptopKeyboardPre2021 => {
// The older devices are tri-state if have lightbar:
// 1. Keyboard
// 2. Lightbar
// 3. KeyboardAndLightbar
if support_data.power_zones.contains(&PowerZones::Lightbar) {
Self {
states: vec![AuraPowerState::default_for(PowerZones::KeyboardAndLightbar)],
states: vec![AuraPowerState::default_for(PowerZones::KeyboardAndLightbar)]
}
} else {
Self {
states: vec![AuraPowerState::default_for(PowerZones::Keyboard)],
states: vec![AuraPowerState::default_for(PowerZones::Keyboard)]
}
}
}
AuraDeviceType::LaptopTuf => Self {
states: vec![AuraPowerState::default_for(PowerZones::Keyboard)],
AuraDeviceType::LaptopKeyboardTuf => Self {
states: vec![AuraPowerState::default_for(PowerZones::Keyboard)]
},
AuraDeviceType::ScsiExtDisk => todo!(),
AuraDeviceType::AnimeOrSlash => todo!()
}
}
pub fn to_bytes(&self, aura_type: AuraDeviceType) -> Vec<u8> {
if let Some(stuff) = self.states.first() {
if stuff.zone == PowerZones::Ally {
return vec![
0x5d,
0xd1,
0x09,
0x01,
stuff.new_to_byte() as u8,
];
}
}
match aura_type {
AuraDeviceType::LaptopPost2021 => self.new_to_bytes(),
AuraDeviceType::LaptopPre2021 => self
.states
.first()
.cloned()
.unwrap_or_default()
.old_to_bytes(),
AuraDeviceType::LaptopTuf => self
AuraDeviceType::LaptopKeyboard2021 | AuraDeviceType::Ally => self.new_to_bytes(),
AuraDeviceType::LaptopKeyboardPre2021 => {
if self.states.len() == 1 {
self.states
.first()
.cloned()
.unwrap_or_default()
.old_to_bytes()
} else {
let mut bytes: Vec<Vec<u8>> =
self.states.iter().map(|s| s.old_to_bytes()).collect();
let mut b = bytes.pop().unwrap();
for i in bytes {
for (i, n) in i.iter().enumerate() {
b[i] |= n;
}
}
b
}
}
AuraDeviceType::LaptopKeyboardTuf => self
.states
.first()
.cloned()
@@ -230,7 +265,8 @@ impl LaptopAuraPower {
warn!("Trying to create bytes for an unknown device");
self.new_to_bytes()
}
AuraDeviceType::ScsiExtDisk => todo!(),
AuraDeviceType::ScsiExtDisk => todo!("scsi disk not implemented yet"),
AuraDeviceType::AnimeOrSlash => todo!("anime/slash not implemented yet")
}
}
}
@@ -249,7 +285,7 @@ enum OldAuraPower {
Boot = 0xc31209,
Sleep = 0x300804,
Keyboard = 0x080000,
Lightbar = 0x040500,
Lightbar = 0x040500
}
impl BitOr<OldAuraPower> for OldAuraPower {
@@ -279,254 +315,297 @@ mod test {
use crate::keyboard::{AuraPowerState, LaptopAuraPower};
use crate::{AuraDeviceType, PowerZones};
fn to_binary_string_post2021(power: &LaptopAuraPower) -> String {
let bytes = power.to_bytes(AuraDeviceType::LaptopKeyboard2021);
format!(
"{:08b}, {:08b}, {:08b}, {:08b}",
bytes[0], bytes[1], bytes[2], bytes[3]
)
}
#[test]
fn check_0x1866_control_bytes() {
let state = AuraPowerState {
zone: PowerZones::Keyboard,
awake: true,
boot: false,
sleep: false,
shutdown: false,
let power = LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Keyboard,
boot: false,
awake: true,
sleep: false,
shutdown: false
},
]
};
let bytes = state.old_to_bytes();
let bytes = power.to_bytes(AuraDeviceType::LaptopKeyboardPre2021);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x08, 0x00, 0x02, 0x00]);
let state = AuraPowerState {
zone: PowerZones::Lightbar,
awake: true,
boot: false,
sleep: false,
shutdown: false,
let power = LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Lightbar,
boot: false,
awake: true,
sleep: false,
shutdown: false
},
]
};
let bytes = state.old_to_bytes();
let bytes = power.to_bytes(AuraDeviceType::LaptopKeyboardPre2021);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x04, 0x05, 0x02, 0x00]);
let bytes = AuraPowerState {
zone: PowerZones::None,
awake: false,
boot: false,
sleep: true,
shutdown: false,
// let bytes = [
// OldAuraPower::Keyboard,
// OldAuraPower::Lightbar,
// OldAuraPower::Awake,
// OldAuraPower::Sleep,
// OldAuraPower::Boot,
// ];
let power = LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Keyboard,
boot: true,
awake: true,
sleep: true,
shutdown: false
},
AuraPowerState {
zone: PowerZones::Lightbar,
boot: true,
awake: true,
sleep: true,
shutdown: false
},
]
};
let bytes = bytes.old_to_bytes();
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x30, 0x08, 0x04, 0x00]);
let bytes = AuraPowerState {
zone: PowerZones::None,
awake: false,
boot: true,
sleep: false,
shutdown: false,
};
let bytes = bytes.old_to_bytes();
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0xc3, 0x12, 0x09, 0x00]);
let power = AuraPowerState {
zone: PowerZones::KeyboardAndLightbar,
awake: true,
boot: true,
sleep: true,
shutdown: false,
};
let bytes = power.old_to_bytes();
let bytes = power.to_bytes(AuraDeviceType::LaptopKeyboardPre2021);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0xff, 0x1f, 0x000f, 0x00]);
}
#[test]
fn check_0x19b6_control_bytes_binary_rep() {
fn to_binary_string(power: &LaptopAuraPower) -> String {
let bytes = power.to_bytes(AuraDeviceType::LaptopPost2021);
format!(
"{:08b}, {:08b}, {:08b}, {:08b}",
bytes[0], bytes[1], bytes[2], bytes[3]
)
}
let boot_logo_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Logo,
boot: true,
awake: false,
sleep: false,
shutdown: false,
}],
let boot_logo_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Logo,
boot: true,
awake: false,
sleep: false,
shutdown: false
},
]
});
let boot_keyb_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Keyboard,
boot: true,
awake: false,
sleep: false,
shutdown: false,
}],
let boot_keyb_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Keyboard,
boot: true,
awake: false,
sleep: false,
shutdown: false
},
]
});
let sleep_logo = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Logo,
boot: false,
awake: false,
sleep: true,
shutdown: false,
}],
let sleep_logo = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Logo,
boot: false,
awake: false,
sleep: true,
shutdown: false
},
]
});
let sleep_keyb = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Keyboard,
boot: false,
awake: false,
sleep: true,
shutdown: false,
}],
let sleep_keyb = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Keyboard,
boot: false,
awake: false,
sleep: true,
shutdown: false
},
]
});
let awake_logo = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Logo,
boot: false,
awake: true,
sleep: false,
shutdown: false,
}],
let awake_logo = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Logo,
boot: false,
awake: true,
sleep: false,
shutdown: false
},
]
});
let awake_keyb = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Keyboard,
boot: false,
awake: true,
sleep: false,
shutdown: false,
}],
let awake_keyb = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Keyboard,
boot: false,
awake: true,
sleep: false,
shutdown: false
},
]
});
let shut_logo_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Logo,
boot: false,
awake: false,
sleep: false,
shutdown: true,
}],
let shut_logo_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Logo,
boot: false,
awake: false,
sleep: false,
shutdown: true
},
]
});
let shut_keyb_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Keyboard,
boot: false,
awake: false,
sleep: false,
shutdown: true,
}],
let shut_keyb_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Keyboard,
boot: false,
awake: false,
sleep: false,
shutdown: true
},
]
});
let boot_bar__ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lightbar,
boot: true,
awake: false,
sleep: false,
shutdown: false,
}],
let boot_bar__ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Lightbar,
boot: true,
awake: false,
sleep: false,
shutdown: false
},
]
});
let awake_bar_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lightbar,
boot: false,
awake: true,
sleep: false,
shutdown: false,
}],
let awake_bar_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Lightbar,
boot: false,
awake: true,
sleep: false,
shutdown: false
},
]
});
let sleep_bar_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lightbar,
boot: false,
awake: false,
sleep: true,
shutdown: false,
}],
let sleep_bar_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Lightbar,
boot: false,
awake: false,
sleep: true,
shutdown: false
},
]
});
let shut_bar__ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lightbar,
boot: false,
awake: false,
sleep: false,
shutdown: true,
}],
let shut_bar__ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Lightbar,
boot: false,
awake: false,
sleep: false,
shutdown: true
},
]
});
let boot_lid__ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lid,
boot: true,
awake: false,
sleep: false,
shutdown: false,
}],
let boot_lid__ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Lid,
boot: true,
awake: false,
sleep: false,
shutdown: false
},
]
});
let awake_lid_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lid,
boot: false,
awake: true,
sleep: false,
shutdown: false,
}],
let awake_lid_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Lid,
boot: false,
awake: true,
sleep: false,
shutdown: false
},
]
});
let sleep_lid_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lid,
boot: false,
awake: false,
sleep: true,
shutdown: false,
}],
let sleep_lid_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Lid,
boot: false,
awake: false,
sleep: true,
shutdown: false
},
]
});
let shut_lid__ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lid,
boot: false,
awake: false,
sleep: false,
shutdown: true,
}],
let shut_lid__ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Lid,
boot: false,
awake: false,
sleep: false,
shutdown: true
},
]
});
let boot_rear_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::RearGlow,
boot: true,
awake: false,
sleep: false,
shutdown: false,
}],
let boot_rear_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::RearGlow,
boot: true,
awake: false,
sleep: false,
shutdown: false
},
]
});
let awake_rear = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::RearGlow,
boot: false,
awake: true,
sleep: false,
shutdown: false,
}],
let awake_rear = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::RearGlow,
boot: false,
awake: true,
sleep: false,
shutdown: false
},
]
});
let sleep_rear = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::RearGlow,
boot: false,
awake: false,
sleep: true,
shutdown: false,
}],
let sleep_rear = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::RearGlow,
boot: false,
awake: false,
sleep: true,
shutdown: false
},
]
});
let shut_rear_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::RearGlow,
boot: false,
awake: false,
sleep: false,
shutdown: true,
}],
let shut_rear_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::RearGlow,
boot: false,
awake: false,
sleep: false,
shutdown: true
},
]
});
assert_eq!(boot_logo_, "00000001, 00000000, 00000000, 00000000");
@@ -554,7 +633,7 @@ mod test {
assert_eq!(shut_rear_, "00000000, 00000000, 00000000, 00001000");
// All on
let byte1 = to_binary_string(&LaptopAuraPower {
let byte1 = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Keyboard,
@@ -576,7 +655,7 @@ mod test {
zone: PowerZones::RearGlow,
..Default::default()
},
],
]
});
assert_eq!(byte1, "11111111, 00011110, 00001111, 00001111");
}

View File

@@ -6,7 +6,6 @@
use std::fmt::Debug;
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value};
@@ -24,70 +23,77 @@ pub mod usb;
pub mod keyboard;
pub const LED_MSG_LEN: usize = 17;
pub const AURA_LAPTOP_LED_MSG_LEN: usize = 17;
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub const RED: Colour = Colour {
r: 0xff,
g: 0x00,
b: 0x00,
b: 0x00
};
pub const GREEN: Colour = Colour {
r: 0x00,
g: 0xff,
b: 0x00,
b: 0x00
};
pub const BLUE: Colour = Colour {
r: 0x00,
g: 0x00,
b: 0xff,
b: 0xff
};
pub const VIOLET: Colour = Colour {
r: 0x9b,
g: 0x26,
b: 0xb6,
b: 0xb6
};
pub const TEAL: Colour = Colour {
r: 0x00,
g: 0x7c,
b: 0x80,
b: 0x80
};
pub const YELLOW: Colour = Colour {
r: 0xff,
g: 0xef,
b: 0x00,
b: 0x00
};
pub const ORANGE: Colour = Colour {
r: 0xff,
g: 0xa4,
b: 0x00,
b: 0x00
};
pub const GRADIENT: [Colour; 7] = [RED, VIOLET, BLUE, TEAL, GREEN, YELLOW, ORANGE];
pub const GRADIENT: [Colour; 7] = [
RED, VIOLET, BLUE, TEAL, GREEN, YELLOW, ORANGE
];
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum AuraDeviceType {
/// Most new laptops
#[default]
LaptopPost2021 = 0,
LaptopPre2021 = 1,
LaptopTuf = 2,
LaptopKeyboard2021 = 0,
LaptopKeyboardPre2021 = 1,
LaptopKeyboardTuf = 2,
ScsiExtDisk = 3,
Unknown = 255,
Ally = 4,
AnimeOrSlash = 5,
Unknown = 255
}
impl AuraDeviceType {
pub fn is_old_laptop(&self) -> bool {
*self == Self::LaptopPre2021
*self == Self::LaptopKeyboardPre2021
}
pub fn is_tuf_laptop(&self) -> bool {
*self == Self::LaptopTuf
*self == Self::LaptopKeyboardTuf
}
pub fn is_new_laptop(&self) -> bool {
*self == Self::LaptopPost2021
*self == Self::LaptopKeyboard2021
}
pub fn is_ally(&self) -> bool {
*self == Self::Ally
}
pub fn is_scsi(&self) -> bool {
@@ -98,16 +104,18 @@ impl AuraDeviceType {
impl From<&str> for AuraDeviceType {
fn from(s: &str) -> Self {
match s.to_lowercase().trim_start_matches("0x") {
"tuf" => AuraDeviceType::LaptopTuf,
"tuf" => AuraDeviceType::LaptopKeyboardTuf,
"1932" => AuraDeviceType::ScsiExtDisk,
"1866" | "18c6" | "1869" | "1854" => Self::LaptopPre2021,
_ => Self::LaptopPost2021,
"1866" | "18c6" | "1869" | "1854" => Self::LaptopKeyboardPre2021,
"1abe" | "1b4c" => Self::Ally,
"19b3" | "193b" => Self::AnimeOrSlash,
"19b6" => Self::LaptopKeyboard2021,
_ => Self::Unknown
}
}
}
/// The powerr zones this laptop supports
#[typeshare]
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
@@ -126,7 +134,9 @@ pub enum PowerZones {
Lid = 3,
/// The led strip on the rear of some laptops
RearGlow = 4,
/// On pre-2021 laptops there is either 1 or 2 zones used
/// Exists for the older 0x1866 models
KeyboardAndLightbar = 5,
None = 255,
/// Ally specific for creating correct packet
Ally = 6,
None = 255
}

View File

@@ -1,10 +1,7 @@
// Only these two packets must be 17 bytes
pub const LED_APPLY: [u8; 17] = [0x5d, 0xb4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
pub const LED_SET: [u8; 17] = [0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
/// Writes out the correct byte string for brightness
pub const fn aura_brightness_bytes(brightness: u8) -> [u8; 17] {
[
0x5a, 0xba, 0xc5, 0xc4, brightness, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]
}
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
];
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
];

View File

@@ -9,13 +9,17 @@ homepage.workspace = true
edition.workspace = true
[features]
#default = ["mocking"]
#mocking = []
default = []
mocking = []
x11 = ["slint/backend-winit-x11"]
# Requires RUSTFLAGS="--cfg tokio_unstable"
tokio-debug = ["console-subscriber"]
[dependencies]
nix = { version = "^0.28.0", features = ["fs"] }
tempfile = "3.3.0"
betrayer = { version = "0.2.0" }
console-subscriber = { version = "^0.4", optional = true }
ksni = { version = "0.3", default-features = false, features = ["async-io"] }
image = "0.25.5"
asusd = { path = "../asusd" }
config-traits = { path = "../config-traits" }
@@ -24,7 +28,7 @@ rog_dbus = { path = "../rog-dbus" }
rog_aura = { path = "../rog-aura" }
rog_profiles = { path = "../rog-profiles" }
rog_platform = { path = "../rog-platform" }
supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", rev = "4eb6e97c22b68ae8d1e80500709b0c0580776ad3", default-features = false }
supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", default-features = false }
dmi_id = { path = "../dmi-id" }
gumdrop.workspace = true
@@ -33,11 +37,11 @@ env_logger.workspace = true
tokio.workspace = true
serde.workspace = true
serde_derive.workspace = true
zbus.workspace = true
dirs.workspace = true
notify-rust.workspace = true
concat-idents.workspace = true
futures-util.workspace = true
versions.workspace = true
@@ -47,7 +51,6 @@ default-features = false
features = [
"gettext",
"compat-1-2",
"backend-linuxkms",
"backend-winit-wayland",
"renderer-winit-femtovg",
# "renderer-skia-opengl",
@@ -55,6 +58,3 @@ features = [
[build-dependencies.slint-build]
git = "https://github.com/slint-ui/slint.git"
[dev-dependencies]
cargo-husky.workspace = true

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