Compare commits

...

154 Commits

Author SHA1 Message Date
Luke D. Jones 773c9902a5 Release 4.3.3 2022-08-02 15:11:02 +12:00
Luke D. Jones e05d5bd143 Version bump. Add early-error display 2022-08-02 15:09:25 +12:00
Luke D. Jones 3e244d7d3d ROGCC: effect visuals. daemon: support TUF RGB 2022-08-02 14:25:27 +12:00
Luke D. Jones ba4589f986 ROGCC: effect visual test 2022-08-02 09:27:10 +12:00
Luke Jones 083134fc73 Merge branch 'support-g713rw' into 'main'
Add G713RW to asusd-ledmodes.toml

See merge request asus-linux/asusctl!125
2022-07-31 21:27:43 +00:00
Luke Jones 3cc04fba60 Merge branch 'danielphan2003-main-patch-53505' into 'main'
Use INSTALL_DATA for toml and gif files

See merge request asus-linux/asusctl!127
2022-07-31 21:27:17 +00:00
Daniel Phan 3a00e4f1a3 Use INSTALL_DATA for toml and gif files 2022-07-31 15:34:25 +00:00
Luke D. Jones eb78fb613c New udev rules to work with both TUF and ROG 2022-07-30 23:08:47 +12:00
Luke D. Jones d0b9aee85a daemon: Re-enable aura control for TUF
Closes #228
2022-07-30 20:52:43 +12:00
Luke Jones 3e94ef05fb Merge branch 'fix/anime-brightness-ignores-first-6-leds' into 'main'
Fix brightness setting ignoring the first 6 leds

Closes #230

See merge request asus-linux/asusctl!126
2022-07-29 21:15:06 +00:00
Ivan Voskoboinyk fbb025875b Fix brightness setting ignoring the first 6 leds
Fixes #230
2022-07-29 15:29:16 +00:00
Yevhen Kolomeiko ae816bd13c Add G713RW to asusd-ledmodes.toml 2022-07-29 11:41:19 +00:00
Luke D. Jones 14f0693511 rog-aura: add gap between numpad on gl504 2022-07-29 19:22:47 +12:00
Luke D. Jones de7fb4a942 ROGCC: use the correct colourspace for colour picker 2022-07-29 18:59:52 +12:00
Luke D. Jones 4164b4645d ROGCC: split keyboard layout into widget 2022-07-29 16:08:55 +12:00
Luke D. Jones 649b14fd0d rog-aura: stand-off the rog row 2022-07-29 16:03:39 +12:00
Luke D. Jones 6d97ef13a1 rog-aura: adjustment of layouts and key sizes 2022-07-29 15:56:03 +12:00
Luke D. Jones 7abad979c8 rog-aura: adjustment of layouts and key sizes 2022-07-29 15:35:03 +12:00
Luke D. Jones 0ec1574219 rog-aura: Cleanup layouts, add gl504_US.toml 2022-07-29 12:07:16 +12:00
Luke D. Jones 03042dd5c3 Remove accidental board name test 2022-07-29 11:26:38 +12:00
Luke D. Jones 3330e4973f rog-aura: add proper labels for keys via &str into 2022-07-29 11:15:37 +12:00
Luke D. Jones 5e06aeabe9 rog-aura: fix up G533 layout 2022-07-29 10:54:59 +12:00
Luke D. Jones e99d8766fc ROGCC: rog-aura: Keyboard layout templates and definitions
This also removes shell completitions as these are not maintained.
2022-07-29 09:19:49 +12:00
Luke D. Jones 8f65b7e334 ROGCC: add enable/disable aura options depending on mode 2022-07-27 11:39:55 +12:00
Luke D. Jones 5a54b830bf ROGCC: style change 2022-07-26 22:35:51 +12:00
Luke D. Jones 85e08510f7 ROGCC: style change 2022-07-26 22:20:00 +12:00
Luke D. Jones d56eeb7fb2 ROGCC: split widgets from pages 2022-07-26 21:39:30 +12:00
Luke D. Jones bbc520a7f2 Update deps 2022-07-26 19:30:50 +12:00
Luke D. Jones 10e43c64ca ROGCC: rename config file 2022-07-26 19:16:46 +12:00
Luke D. Jones 38be25174a Add verbose output for fan-curve detection. Add mocking to GUI.
asusd: Verbose output of fan-curves on startup
ROGCC: Try to mock more of GUI state
2022-07-26 18:59:21 +12:00
Luke D. Jones 2584d69930 Update deps 2022-07-25 21:38:00 +12:00
Luke D. Jones 523f39cf9c Version bump for RC 2022-07-25 21:34:33 +12:00
Luke D. Jones 669760223e Clean up erroneously included files 2022-07-25 21:32:58 +12:00
Luke D. Jones 71ec13fa9f ROGCC: Attempt to add LED brightness 2022-07-25 21:21:32 +12:00
Luke D. Jones 409528b286 ROGCC: Better control of notifs, add panel_od 2022-07-25 20:55:30 +12:00
Luke D. Jones 17df3cf01d Add rog-control-center to the workspace 2022-07-25 16:43:48 +12:00
Luke D. Jones 808a1d2470 Fix misnamed led dbus method 2022-07-25 14:22:29 +12:00
Luke D. Jones 840c500b5e Switch a keyboard prod_id to enum 2022-07-25 14:07:29 +12:00
Luke D. Jones 42dc360d16 Bump daemon version 2022-07-25 12:43:17 +12:00
Luke D. Jones f6183597c9 Trial BTreeMap<AuraModeNum, AuraEffect> return for led dbus 2022-07-25 10:26:47 +12:00
Luke D. Jones 79a45c4f10 Add to/from [f32;3] for Colour] 2022-07-25 09:51:25 +12:00
Luke D. Jones 19370215c0 Cleanup 2022-07-24 20:55:09 +12:00
Luke D. Jones 030dd661b8 Switch zbus led_mode to return AuraModeNum 2022-07-24 20:45:20 +12:00
Luke D. Jones 1fc12d9855 Make CurveData members public 2022-07-24 10:50:28 +12:00
Luke D. Jones 6c1b2b70ea Make FanCurveSet members public 2022-07-24 10:47:40 +12:00
Luke D. Jones 23f9af35bf Add Hash derive to Profile 2022-07-24 09:43:22 +12:00
Luke D. Jones 526626b80c Minor tweaks on derives 2022-07-24 09:28:00 +12:00
Luke Jones 10eaaac54b Merge branch 'sova/G713IC-led-support' into 'main'
Add LED support for G713IC

See merge request asus-linux/asusctl!124
2022-07-21 12:39:43 +00:00
SoVa 901a3ddcc9 Add LED support for G713IC 2022-07-21 14:18:33 +02:00
Luke Jones e6ebf72a11 Merge branch 'main' into 'main'
Fix some typos.

See merge request asus-linux/asusctl!123
2022-07-21 07:59:16 +00:00
Luke D. Jones cd7e748c88 Prep new release 2022-07-21 19:36:17 +12:00
成超(Cheng Chao) a313359ef6 Fix some typos. 2022-07-21 12:46:44 +08:00
Luke Jones f222eef6b7 Update CHANGELOG.md 2022-07-21 03:40:04 +00:00
Luke Jones e6f3aeb851 Merge branch 'fluke/multi-led-power' into 'main'
Make LED power more universal

Closes #219

See merge request asus-linux/asusctl!122
2022-07-21 03:39:11 +00:00
Luke D. Jones 22605e57cc Properly set full defaults for LED power 2022-07-21 14:56:30 +12:00
Luke D. Jones 02fb7addf4 Make LED power more universal
Closes #219
2022-07-21 14:48:16 +12:00
Luke Jones bdbb403a0e Merge branch 'fluke/errors' into 'main'
Improve error handling in some cases

See merge request asus-linux/asusctl!121
2022-07-20 09:01:55 +00:00
Luke D. Jones 7a8bede92f Return error if a pixel-gif is larger than the anime-display dimensions 2022-07-20 20:52:03 +12:00
Luke D. Jones a71a40b509 Make rog-anime more tolerent of faults 2022-07-20 20:17:43 +12:00
Luke D. Jones 42fc5a5392 Minor doc comment change 2022-07-18 15:43:09 +12:00
Luke D. Jones 5017a0ea9b Add extra multizone test 2022-07-18 14:11:54 +12:00
Luke D. Jones 05f7b0060f Prep new release 2022-07-18 14:01:32 +12:00
Luke Jones 1043da5328 Merge branch 'fluke/multizone-defaults' into 'main'
Create defaults on missing zones

See merge request asus-linux/asusctl!120
2022-07-18 01:50:35 +00:00
Luke D. Jones 959ad35afa Create defaults on missing zones 2022-07-18 13:40:42 +12:00
Luke Jones 1e10255d01 Merge branch 'fluke/multizone-fixes' into 'main'
Fluke/multizone fixes

See merge request asus-linux/asusctl!119
2022-07-17 10:02:32 +00:00
Luke D. Jones c72693bc53 Fix test for CI 2022-07-17 22:02:01 +12:00
Luke D. Jones 2297aad5e5 Fix test 2022-07-17 21:49:15 +12:00
Luke D. Jones f39c0db680 Add more support detection for bios/system level components 2022-07-17 21:02:39 +12:00
Luke D. Jones 23353c77f3 Add panel_od support 2022-07-17 20:31:47 +12:00
Luke D. Jones fee1486db6 Correctly save multizone config 2022-07-17 19:23:08 +12:00
Luke D. Jones 39c4253b24 Add GA401I Anime support 2022-07-17 15:08:18 +12:00
Luke D. Jones 84e924f46a Add doc comments for bytes of 0x1866 power control 2022-07-16 22:52:36 +12:00
Luke D. Jones 43364398b4 Add LED support for GX703HS
Closes #207
2022-07-16 15:11:37 +12:00
Luke D. Jones 3f09f221f4 Bump versions 2022-07-16 15:03:42 +12:00
Luke Jones 4ed922154b Merge branch 'validate-anime-brightness' into 'main'
Enforce valid image brightness in daemon and asusctl

See merge request asus-linux/asusctl!118
2022-07-16 03:00:22 +00:00
I Al Istannen c0e36295b7 Enforce valid image brightness in daemon and asusctl 2022-07-16 03:00:22 +00:00
Luke D. Jones ef04549c8e Bump versions 2022-07-16 13:45:23 +12:00
Luke D. Jones a117c300d5 Add support for /etc/asusd/asusd-user-ledmodes.toml 2022-07-16 13:40:57 +12:00
Luke D. Jones 6956f7dffc Re-enable cargo test in pipeline 2022-07-15 23:54:30 +12:00
Luke D. Jones fe49913550 Updated changelog 2022-07-15 23:54:20 +12:00
Luke D. Jones 89ddade2a3 Updated changelog 2022-07-15 23:54:07 +12:00
Luke Jones bcb3d53ade Merge branch 'readme_cargo' into 'main'
cargo added to installation

See merge request asus-linux/asusctl!115
2022-07-15 11:31:05 +00:00
Luke Jones 9ab9028d79 Merge branch 'fluke/ga402-anime' into 'main'
First pass of Anime update for new matrix display

See merge request asus-linux/asusctl!113
2022-07-15 11:30:28 +00:00
Luke Jones 8de6447562 Merge branch 'fluke/ga402-anime' into 'fluke/ga402-anime'
AnimeImage patch for GA402

See merge request asus-linux/asusctl!116
2022-07-15 11:23:43 +00:00
I-Al-Istannen 2512cea19d Slightly adjust G402 scaling, add some more documentation 2022-07-15 13:12:34 +02:00
Luke D. Jones eabe78af71 tmp 2022-07-15 23:00:01 +12:00
Luke D. Jones 16c4ee609e Add GA402 anime-matrix packet unit test 2022-07-15 20:40:16 +12:00
Luke D. Jones 37d586c5de Additional comments in animeimage 2022-07-15 20:38:07 +12:00
Luke D. Jones e66847c263 Add commenting to AnimeImage to help with GA402 2022-07-15 20:37:30 +12:00
I-Al-Istannen a2c8a226a4 Complete anime diagonal gif support for GA402 2022-07-15 20:37:30 +12:00
Luke D. Jones f3f6fadfe2 Fix anime exampels 2022-07-15 20:37:30 +12:00
Luke D. Jones ff76c356c5 First pass of Anime update for new matrix display
Co-authored with @I-Al-Istannen

Part of #189
2022-07-15 20:37:30 +12:00
Luke D. Jones a22808aff3 Add note in cargo.toml re: lto 2022-07-15 10:21:11 +12:00
Luke D. Jones f39fd6dfbb Use hashset in aura power config 2022-07-15 09:30:10 +12:00
Luke Jones 95598f2a76 Merge branch 'fluke/multizone' into 'main'
Multizone support plus rebuild of LED power control

Closes #213

See merge request asus-linux/asusctl!117
2022-07-14 11:20:22 +00:00
Luke D. Jones 535e104ccc Rebuild of LED power control 2022-07-14 23:05:33 +12:00
saswatasarkar13 522cee42e5 cargo added to installation 2022-07-14 14:57:04 +05:30
Luke D. Jones 51656dc13f Initial multizone fixup work 2022-07-14 15:09:28 +12:00
Luke D. Jones abd412b5d5 Save and restore kb bright only on shutdown/sleep/boot/wak
Closes #213
2022-07-14 13:05:51 +12:00
Luke D. Jones 4d8c05cd92 Add more multizone support 2022-07-14 13:05:41 +12:00
Luke Jones f2ca8081af Merge branch 'user.sv4.apps-main-patch-52927' into 'main'
Fan curve kernel requirement message update.

See merge request asus-linux/asusctl!112
2022-07-12 01:55:42 +00:00
User 7539011be5 Update main.rs 2022-07-09 10:41:29 +00:00
Luke D. Jones 5117427143 Add GA503R LED modes
Closes #205
2022-06-26 11:57:15 +12:00
Luke D. Jones e95986b830 Update changelog 2022-06-24 23:12:04 +12:00
Luke D. Jones 5311972345 Set keyboard brightness on resume. Refactor some tasks 2022-06-24 23:09:45 +12:00
Luke D. Jones 967295fba7 Fixes to anime-matrix system thread handling 2022-06-21 23:29:44 +12:00
Luke D. Jones 5403c5fb4f Update changelog 2022-06-20 23:16:25 +12:00
Luke D. Jones 65986c3114 prep new release 2022-06-20 23:13:17 +12:00
Luke D. Jones 13a90b00f3 Adjust how thread exit is handled for anime controller 2022-06-20 22:43:12 +12:00
Luke Jones 2ee7fc9910 Merge branch '191-full-power-states-combinations' into 'main'
Combination for power state leds boot/sleep/all/keys/side LEDS

Closes #191

See merge request asus-linux/asusctl!111
2022-06-19 22:03:23 +00:00
mpiffault a0a0efabbb Combination for power state leds boot/sleep/all/keys/side LEDS 2022-06-19 22:03:23 +00:00
Luke Jones 9a50278b98 Merge branch 'fluke/async-tasks' into 'main'
Fixes to tasks

See merge request asus-linux/asusctl!110
2022-06-12 03:42:09 +00:00
Luke D. Jones 9519a35e32 Fixes to tasks 2022-06-12 15:34:38 +12:00
Luke Jones 578d5fd541 Merge branch 'fix/199-multizone-commands-not-displayed' into 'main'
Fixes #199 multizone commands not displayed

Closes #199

See merge request asus-linux/asusctl!109
2022-06-12 03:32:11 +00:00
Martin Piffault 642bc5dda1 output multizone commands required 2022-06-07 22:31:53 +02:00
Martin Piffault 88274abdb5 init multizone_led_mode and per_key_led_mode from LaptopLedData 2022-06-07 22:30:37 +02:00
Luke Jones aea65f5c5f Merge branch 'fluke/async-tasks' into 'main'
Re-enable notif for profile change

See merge request asus-linux/asusctl!108
2022-06-07 00:13:24 +00:00
Luke D. Jones edfbfde13b Re-enable notif for profile change 2022-06-07 12:08:36 +12:00
Luke Jones dcc676d60a Merge branch 'fluke/async-tasks' into 'main'
Added tasks for reload keyboard bright, and for charge control

See merge request asus-linux/asusctl!107
2022-06-06 23:53:41 +00:00
Luke D. Jones 561f61116c Added tasks for reload keyboard bright, and for charge control 2022-06-07 11:49:12 +12:00
Luke Jones 6a4594466b Merge branch 'fluke/async-tasks' into 'main'
Async tasks

See merge request asus-linux/asusctl!106
2022-06-06 13:00:10 +00:00
Luke D. Jones af216ee08c Async tasks 2022-06-07 00:54:33 +12:00
Luke D. Jones e493113450 update changelog 2022-06-06 18:25:27 +12:00
Luke D. Jones 74e1d5bdc4 Add brightness to anime zbus notif 2022-06-06 18:24:08 +12:00
Luke D. Jones 5c0ad3e590 Re-enable notification on anime power-state change 2022-06-06 18:18:35 +12:00
Luke D. Jones 6e872ecab9 Add diagonal-template.png to rog-anime/data/anime/custom/
Closes #163
2022-06-06 17:20:38 +12:00
Luke D. Jones 46a4cde77f Add G512 to LED support list
Closes #174
2022-06-06 17:02:41 +12:00
Luke D. Jones fc7c444107 Add GU502LV LED support
Closes #187
2022-06-06 16:57:50 +12:00
Luke D. Jones 822438e0d2 Version bumps 2022-06-06 14:53:30 +12:00
Luke D. Jones de43a37e9e Use smol async for daemon and daemon-user 2022-06-06 14:34:59 +12:00
Luke D. Jones d4b2d2f403 Use smol async for asus-notify 2022-06-06 13:57:03 +12:00
Luke D. Jones 6e33eab136 Update anime examples 2022-06-06 13:11:54 +12:00
Luke D. Jones 1e4bc85fee Partial asusd-user update 2022-06-06 01:33:14 +12:00
Luke D. Jones 31fff75f08 Update more deps 2022-06-06 00:41:07 +12:00
Luke D. Jones f0620154c8 Update changelog 2022-06-06 00:21:05 +12:00
Luke D. Jones 711aa1e4be Add support for GA402R
Closes #196
2022-06-06 00:14:28 +12:00
Luke D. Jones 854f2d75b3 Format 2022-06-06 00:09:23 +12:00
Luke D. Jones a85e2f6130 Finalise zbus3 conversion 2022-06-06 00:08:59 +12:00
Luke Jones bac2ba6f09 Merge branch 'mpiffault-main-patch-00521' into 'main'
Update asusd-ledmodes.toml to support Asus Rog Strix G15 G513QM

See merge request asus-linux/asusctl!103
2022-06-05 02:58:49 +00:00
Luke Jones 47e5270f9c Merge branch 'feat/side-leds-toggle-support' into 'main'
Adds support to enable/disable side leds #191

See merge request asus-linux/asusctl!104
2022-06-05 02:58:24 +00:00
Luke Jones 68cbf09e9f Merge branch 'fix/aura-help-options-display' into 'main'
Fixes all available led-mode commands not displaying with `asusctl led-mode --help`

See merge request asus-linux/asusctl!105
2022-06-05 02:54:31 +00:00
Martin Piffault 9f18c88153 fix all available options not being displayed in led-mode help 2022-06-04 14:47:40 +02:00
Martin Piffault c6caafdcb7 adds support to enable/disable side leds 2022-05-31 17:00:21 +02:00
mpiffault b22a3e1a59 Update asusd-ledmodes.toml to support Asus Rog Strix G15 G513QM 2022-05-30 08:06:26 +00:00
Luke Jones b6934bbf63 Merge branch 'as/issue_176_1' into 'main'
Fix for fan_curve related integer divison bug

Closes #176

See merge request asus-linux/asusctl!102
2022-04-08 21:18:47 +00:00
Armas Spann 3cd6eb13a9 fixed interger division bug and related tests, as well as a comment bug 2022-04-07 01:16:06 +02:00
Luke Jones 272be2aaad Merge branch 'as/issue_176' into 'main'
fix for #176 - fancurve percentage check not handled correctly

Closes #176

See merge request asus-linux/asusctl!101
2022-04-05 21:37:23 +00:00
Armas Spann 99dd6ce77f fix for #176 - fancurve percentage check not handled correctly 2022-04-05 23:33:43 +02:00
Luke Jones fc14455da4 Merge branch 'ankushmishra9-main-patch-66732' into 'main'
Fixed formatting of install instructions

See merge request asus-linux/asusctl!96
2022-02-07 22:59:34 +00:00
Luke Jones 26a52dae23 Merge branch 'main' into 'main'
add g513ic led

See merge request asus-linux/asusctl!94
2022-01-19 20:13:34 +00:00
Ankush Mishra 75864d33a6 Fixed formatting of install instructions 2022-01-19 17:57:06 +00:00
Luke Jones 63a97b6665 Merge branch 'main' into 'main'
fixed a small typo

See merge request asus-linux/asusctl!95
2022-01-15 20:51:52 +00:00
AlenPaulVarghese 21a37a3bb0 fixed a small typo 2022-01-14 18:49:15 +00:00
dada513 de586b5368 add g513ic led 2022-01-11 10:08:45 +00:00
126 changed files with 11621 additions and 3945 deletions
+1
View File
@@ -10,6 +10,7 @@ stages:
test: test:
script: script:
- cargo check #+nightly check --features "clippy" - cargo check #+nightly check --features "clippy"
- cargo test
build: build:
only: only:
+74 -1
View File
@@ -4,7 +4,80 @@ 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/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased] ## [Unreleased ]
## [4.3.3] - 2022-08-02
### Added
- `rog-control-center` has now been moved in to the main workspace due to
the heavy dependencies on most of the rog crates
- Preliminary support of TUF RGB keyboards + power states
- Support for G713RW LED modes (Author: jarvis2709)
- Support for G713IC LED modes
### Changed
- The udev rules have been changed to make asusd load with all gamer variants when asus-nb-wmi is loaded
- TUF, ROG, Zephyrus, Strix
## [4.3.0] - 2022-07-21
### Added
- Clear command for anime `asusctl anime --clear` will clear the display
- Re-added support for LED power states on `0x1866` type keyboards
### Changed
- Make rog-anime more error tolerent. Remove various asserts and return errors instead
- Return error if a pixel-gif is larger than the anime-display dimensions
- Both Anime and Aura dbus interfaces are changed a little
- Aura power has changed, all power related settings are now in one method
- Anime methods will now return an error (if errored)
- /org/asuslinux/Led renamed to /org/asuslinux/Aura
## [4.2.1] - 2022-07-18
### Added
- Add panel overdrive support (autodetects if supported)
- Add detection of dgpu_disable and egpu_enable for diagnostic
### Changed
- Fixed save and restore of multizone LED settings
- Create defaults for multizone
## [4.2.0] - 2022-07-16
### Added
- Support for GA402 Anime Matrix display (Author: I-Al-Istannen & Luke Jones)
- Support for power-config of all LED zones. See `asusctrl led-power --help` (Author: Luke Jones, With much help from: @MNS26)
- Full support for multizone LED <logo, keyboard, lightbar> (Author: Luke Jones, With much help from: @MNS26)
- Add ability to load extra data from `/etc/asusd/asusd-user-ledmodes.toml` for LED support if file exits
- Support for G513IM LED modes
- Support for GX703HS LED modes
### Changed
- Dbus interface for Aura config has been changed, all power control is done with `SetLedsEnabled` and `SetLedsDisabled`
- Data for anime-matrix now requires passing the laptop model as enum
- Extra unit tests for anime stuff to help verify things
### Added
- Support for GA503R LED modes
### Changed
- Refactor LED and AniMe tasks
- Reload keyboard brightness on resume from sleep/hiber
## [4.1.1] - 2022-06-21
### Changed
- Fixes to anime matrix system thread cancelation
## [4.1.0] - 2022-06-20
### Changed
- Huge refactor to use zbus 2.2 + zvariant 3.0 in system-daemon.
- Daemons with tasks now use `smol` for async ops.
- Fixes to fan-curve settings from CLI (Author: Armas Span)
- Add brightness to anime zbus notification
- Adjust how threads in AniMe matrix controller work
- Use proper power-state packet for keyboard LED's (Author: Martin Piffault)
### Added
- Support for GA402R LED modes
- Support for GU502LV LED modes
- Support for G512 LED modes
- Support for G513IC LED modes (Author: dada513)
- Support for G513QM LED modes (Author: Martin Piffault)
- Add side-LED toggle support (Author: Martin Piffault)
- Support reloading keyboard mode on wake (from sleep/hiber)
- Support reloading charge-level on wake (from sleep/hiber)
- Support running AniMe animation blocks on wake/sleep and boot/shutdown events
# [4.0.7] - 2021-12-19 # [4.0.7] - 2021-12-19
### Changed ### Changed
Generated
+2020 -324
View File
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -1,8 +1,10 @@
[workspace] [workspace]
members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-supported", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles"] members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-supported", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "rog-control-center"]
[profile.release] [profile.release]
lto = true # thin = 57s, asusd = 9.0M
# fat = 72s, asusd = 6.4M
lto = "fat"
debug = false debug = false
opt-level = 3 opt-level = 3
panic = "abort" panic = "abort"
+7 -7
View File
@@ -60,17 +60,17 @@ The defaults are located at `/etc/asusd/asusd-ledmodes.toml`, and on `asusd` sta
Example: Example:
```toml ```toml
[[led_data]] [[led_data]]
prod_family = "ROG Zephyrus M15" prod_family = "Strix"
board_names = ["GU502LU"] board_names = ["GL504G"]
standard = ["Static", "Breathe", "Strobe", "Pulse"] standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = false multizone = ["Key1", "Key2", "Key3", "Key4", "Logo", "BarLeft", "BarRight"]
per_key = false per_key = false
``` ```
1. `prod_family`: you can find this in `journalctl -b -u asusd`, or `cat /sys/class/dmi/id/product_name`. It should be copied as written. There can be multiple `led-data` groups of the same `prod_family` with differing `board_names`. 1. `prod_family`: you can find this in `journalctl -b -u asusd`, or `cat /sys/class/dmi/id/product_name`. It should be copied as written. There can be multiple `led-data` groups of the same `prod_family` with differing `board_names`.
2. `board_names`: is an array of board names in this product family. Find this in the journal as above or by `cat /sys/class/dmi/id/board_name`. 2. `board_names`: is an array of board names in this product family. Find this in the journal as above or by `cat /sys/class/dmi/id/board_name`.
3. `standard` are the factory preset modes, the names should corrospond to Armory Crate names 3. `standard` are the factory preset modes, the names should corrospond to Armory Crate names
4. `multizone`: some keyboards have 4 zones of LED control, this enables setting a colour in each zone. The keyboard must support this or it has no effect. 4. `multizone`: some models have 4 to 7 zones of LED control as shown in the example. If the laptop has no zones then an empty array will suffice.
5. `per_key`: enable per-key RGB effects. The keyboard must support this or it has no effect. 5. `per_key`: enable per-key RGB effects. The keyboard must support this or it has no effect.
##### /etc/asusd/aura.conf ##### /etc/asusd/aura.conf
@@ -90,7 +90,7 @@ where the number is a percentage.
Some options that you find in Armory Crate are available under this controller, so far there is: Some options that you find in Armory Crate are available under this controller, so far there is:
- POST sound: this is the sound you here on bios boot post - POST sound: this is the sound you hear on bios boot post
- G-Sync: this controls if the dGPU (Nvidia) is the *only* GPU, making it the main GPU and disabling the iGPU - G-Sync: this controls if the dGPU (Nvidia) is the *only* GPU, making it the main GPU and disabling the iGPU
These options are not written to the config file as they are stored in efivars. The only way to change these is to use the exposed safe dbus methods, or use the `asusctl` CLI tool. These options are not written to the config file as they are stored in efivars. The only way to change these is to use the exposed safe dbus methods, or use the `asusctl` CLI tool.
@@ -293,7 +293,7 @@ A plain non-float integer.
## asusctl ## asusctl
`asusctl` is a commandline interface which intends to be the main method of interacting with `asusd`. I can be used in any place a terminal app can be used. `asusctl` is a commandline interface which intends to be the main method of interacting with `asusd`. It can be used in any place a terminal app can be used.
This program will query `asusd` for the `Support` level of the laptop and show or hide options according to this support level. This program will query `asusd` for the `Support` level of the laptop and show or hide options according to this support level.
+12 -5
View File
@@ -11,6 +11,7 @@ datarootdir = $(prefix)/share
libdir = $(exec_prefix)/lib libdir = $(exec_prefix)/lib
zshcpl = $(datarootdir)/zsh/site-functions zshcpl = $(datarootdir)/zsh/site-functions
BIN_ROG := rog-control-center
BIN_C := asusctl BIN_C := asusctl
BIN_D := asusd BIN_D := asusd
BIN_U := asusd-user BIN_U := asusd-user
@@ -39,6 +40,11 @@ distclean:
rm -rf .cargo vendor vendor.tar.xz rm -rf .cargo vendor vendor.tar.xz
install: install:
$(INSTALL_PROGRAM) "./target/release/$(BIN_ROG)" "$(DESTDIR)$(bindir)/$(BIN_ROG)"
$(INSTALL_DATA) "./rog-control-center/data/$(BIN_ROG).desktop" "$(DESTDIR)$(datarootdir)/applications/$(BIN_ROG).desktop"
$(INSTALL_DATA) "./rog-control-center/data/$(BIN_ROG).png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/$(BIN_ROG).png"
cd rog-aura/data/layouts && find . -type f -name "*.toml" -exec $(INSTALL_DATA) "{}" "$(DESTDIR)$(datarootdir)/rog-gui/layouts/{}" \;
$(INSTALL_PROGRAM) "./target/release/$(BIN_C)" "$(DESTDIR)$(bindir)/$(BIN_C)" $(INSTALL_PROGRAM) "./target/release/$(BIN_C)" "$(DESTDIR)$(bindir)/$(BIN_C)"
$(INSTALL_PROGRAM) "./target/release/$(BIN_D)" "$(DESTDIR)$(bindir)/$(BIN_D)" $(INSTALL_PROGRAM) "./target/release/$(BIN_D)" "$(DESTDIR)$(bindir)/$(BIN_D)"
$(INSTALL_PROGRAM) "./target/release/$(BIN_U)" "$(DESTDIR)$(bindir)/$(BIN_U)" $(INSTALL_PROGRAM) "./target/release/$(BIN_U)" "$(DESTDIR)$(bindir)/$(BIN_U)"
@@ -63,11 +69,13 @@ install:
$(INSTALL_DATA) "./data/icons/scalable/gpu-vfio.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-vfio.svg" $(INSTALL_DATA) "./data/icons/scalable/gpu-vfio.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-vfio.svg"
$(INSTALL_DATA) "./data/icons/scalable/notification-reboot.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/notification-reboot.svg" $(INSTALL_DATA) "./data/icons/scalable/notification-reboot.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/notification-reboot.svg"
$(INSTALL_DATA) "./data/_asusctl" "$(DESTDIR)$(zshcpl)/_asusctl" cd rog-anime/data && find "./anime" -type f -exec $(INSTALL_DATA) "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \;
$(INSTALL_DATA) "./data/completions/asusctl.fish" "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish"
cd rog-anime/data && find "./anime" -type f -exec install -Dm 755 "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \;
uninstall: uninstall:
rm -f "$(DESTDIR)$(bindir)/$(BIN_ROG)"
rm -r "$(DESTDIR)$(datarootdir)/applications/$(BIN_ROG).desktop"
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/$(BIN_ROG).png"
rm -f "$(DESTDIR)$(bindir)/$(BIN_C)" rm -f "$(DESTDIR)$(bindir)/$(BIN_C)"
rm -f "$(DESTDIR)$(bindir)/$(BIN_D)" rm -f "$(DESTDIR)$(bindir)/$(BIN_D)"
rm -f "$(DESTDIR)$(bindir)/$(BIN_N)" rm -f "$(DESTDIR)$(bindir)/$(BIN_N)"
@@ -85,9 +93,8 @@ uninstall:
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-nvidia.svg" rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-nvidia.svg"
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-vfio.svg" rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-vfio.svg"
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/notification-reboot.svg" rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/notification-reboot.svg"
rm -f "$(DESTDIR)$(zshcpl)/_asusctl"
rm -f "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish"
rm -rf "$(DESTDIR)$(datarootdir)/asusd" rm -rf "$(DESTDIR)$(datarootdir)/asusd"
rm -rf "$(DESTDIR)$(datarootdir)/rog-gui"
update: update:
cargo update cargo update
+24 -12
View File
@@ -5,11 +5,11 @@
`asusd` is a utility for Linux to control many aspects of various ASUS laptops `asusd` is a utility for Linux to control many aspects of various ASUS laptops
but can also be used with non-asus laptops with reduced features. but can also be used with non-asus laptops with reduced features.
Now includes a GUI, `rog-control-center`.
## Kernel support ## Kernel support
**The minimum supported kernel version is 5.15** **The minimum supported kernel version is 5.17**
Fan curve control on laptops with this feature require [this patch](https://lkml.org/lkml/2021/10/23/250) whcih has been merged for 5.17 upstream.
## Goals ## Goals
@@ -42,6 +42,16 @@ then it may work without tweaks. Technically all other functions except the LED
and AniMe parts should work regardless of your latop make. Eventually this project and AniMe parts should work regardless of your latop make. Eventually this project
will probably suffer another rename once it becomes generic enough to do so. will probably suffer another rename once it becomes generic enough to do so.
### Unsupported (e.g, TUF laptops)
- `sudo systemctl mask asusd && sudo systemctl stop asusd`
- `copy asusd-alt.service to /etc/systemd/system/`
- `sudo systemctl enable asusd-alt`
- `sudo systemctl start asusd-alt`
- `asusctl -s`
`asusd-alt.service` is in the `data` dir of this repo.
## Implemented ## Implemented
- [X] System daemon - [X] System daemon
@@ -60,16 +70,18 @@ will probably suffer another rename once it becomes generic enough to do so.
Requirements are rust >= 1.57 installed from rustup.io if the distro provided version is too old, and `make`. Requirements are rust >= 1.57 installed from rustup.io if the distro provided version is too old, and `make`.
**Ubuntu (unsuported):** **Ubuntu (unsuported):**
`apt install libclang-dev libudev-dev`
`curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh`
`make`
`sudo make install`
**fedora:** apt install libclang-dev libudev-dev
`dnf install clang-devel systemd-devel` curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
`make` make
`sudo make install` sudo make install
**fedora:**
dnf install clang-devel systemd-devel cargo
make
sudo make install
## Installing ## Installing
- Fedora copr = https://copr.fedorainfracloud.org/coprs/lukenukem/asus-linux/ - Fedora copr = https://copr.fedorainfracloud.org/coprs/lukenukem/asus-linux/
+3 -2
View File
@@ -1,19 +1,20 @@
[package] [package]
name = "asus-notify" name = "asus-notify"
version = "3.0.2" version = "3.1.0"
authors = ["Luke D Jones <luke@ljones.dev>"] authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2018" edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
zbus = "^1.9" zbus = "^2.2"
# serialisation # serialisation
serde_json = "^1.0" serde_json = "^1.0"
rog_dbus = { path = "../rog-dbus" } rog_dbus = { path = "../rog-dbus" }
rog_aura = { path = "../rog-aura" } rog_aura = { path = "../rog-aura" }
rog_supported = { path = "../rog-supported" } rog_supported = { path = "../rog-supported" }
rog_profiles = { path = "../rog-profiles" } rog_profiles = { path = "../rog-profiles" }
smol = "^1.2"
[dependencies.notify-rust] [dependencies.notify-rust]
version = "^4.3" version = "^4.3"
+97 -34
View File
@@ -1,10 +1,16 @@
use notify_rust::{Hint, Notification, NotificationHandle}; use notify_rust::{Hint, Notification, NotificationHandle};
use rog_aura::AuraEffect; use rog_aura::AuraEffect;
use rog_dbus::{DbusProxies, Signals}; use rog_dbus::{
zbus_charge::ChargeProxy, zbus_led::LedProxy, zbus_profile::ProfileProxy,
zbus_rogbios::RogBiosProxy,
};
use rog_profiles::Profile; use rog_profiles::Profile;
use std::error::Error; use smol::{future, Executor};
use std::thread::sleep; use std::{
use std::time::Duration; error::Error,
sync::{Arc, Mutex},
};
use zbus::export::futures_util::StreamExt;
const NOTIF_HEADER: &str = "ROG Control"; const NOTIF_HEADER: &str = "ROG Control";
@@ -14,7 +20,7 @@ macro_rules! notify {
notif.close(); notif.close();
} }
if let Ok(x) = $notifier($data) { if let Ok(x) = $notifier($data) {
$last_notif = Some(x); $last_notif.replace(x);
} }
}; };
} }
@@ -29,43 +35,96 @@ macro_rules! base_notification {
}; };
} }
type SharedHandle = Arc<Mutex<Option<NotificationHandle>>>;
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("asus-notify version {}", env!("CARGO_PKG_VERSION")); println!("asus-notify version {}", env!("CARGO_PKG_VERSION"));
println!(" rog-dbus version {}", rog_dbus::VERSION); println!(" rog-dbus version {}", rog_dbus::VERSION);
let (proxies, conn) = DbusProxies::new()?; let last_notification: SharedHandle = Arc::new(Mutex::new(None));
let signals = Signals::new(&proxies)?;
let mut last_notification: Option<NotificationHandle> = None; let executor = Executor::new();
// BIOS notif
let x = last_notification.clone();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = RogBiosProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_post_boot_sound().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if let Ok(ref mut lock) = x.try_lock() {
notify!(do_post_sound_notif, lock, &out.sound());
}
}
future::ready(())
})
.await;
};
})
.detach();
let recv = proxies.setup_recv(conn); // Charge notif
let mut err_count = 0; let x = last_notification.clone();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = ChargeProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_charge().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if let Ok(ref mut lock) = x.try_lock() {
notify!(do_charge_notif, lock, &out.limit);
}
}
future::ready(())
})
.await;
};
})
.detach();
// Profile notif
let x = last_notification.clone();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = ProfileProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_profile().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if let Ok(ref mut lock) = x.try_lock() {
notify!(do_thermal_notif, lock, &out.profile);
}
}
future::ready(())
})
.await;
};
})
.detach();
// LED notif
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = LedProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_led().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if let Ok(ref mut lock) = last_notification.try_lock() {
notify!(do_led_notif, lock, &out.data);
}
}
future::ready(())
})
.await;
};
})
.detach();
loop { loop {
sleep(Duration::from_millis(100)); smol::block_on(executor.tick());
if let Err(err) = recv.next_signal() {
if err_count < 3 {
err_count += 1;
println!("{}", err);
}
if err_count == 3 {
err_count += 1;
println!("Max error count reached. Spooling silently.");
}
sleep(Duration::from_millis(2000));
continue;
}
err_count = 0;
if let Ok(data) = signals.led_mode.try_recv() {
notify!(do_led_notif, last_notification, &data);
}
if let Ok(data) = signals.profile.try_recv() {
notify!(do_thermal_notif, last_notification, &data);
}
if let Ok(data) = signals.charge.try_recv() {
notify!(do_charge_notif, last_notification, &data);
}
} }
} }
@@ -101,3 +160,7 @@ fn do_led_notif(ledmode: &AuraEffect) -> Result<NotificationHandle, notify_rust:
fn do_charge_notif(limit: &u8) -> Result<NotificationHandle, notify_rust::error::Error> { fn do_charge_notif(limit: &u8) -> Result<NotificationHandle, notify_rust::error::Error> {
base_notification!(&format!("Battery charge limit changed to {}", limit)) base_notification!(&format!("Battery charge limit changed to {}", limit))
} }
fn do_post_sound_notif(on: &bool) -> Result<NotificationHandle, notify_rust::error::Error> {
base_notification!(&format!("BIOS Post sound {}", on))
}
+4 -4
View File
@@ -1,13 +1,13 @@
[package] [package]
name = "asusctl" name = "asusctl"
version = "4.0.7" version = "4.3.3"
authors = ["Luke D Jones <luke@ljones.dev>"] authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2018" edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
zbus = "^1.9.1" zbus = "^2.2"
rog_anime = { path = "../rog-anime" } rog_anime = { path = "../rog-anime" }
rog_aura = { path = "../rog-aura" } rog_aura = { path = "../rog-aura" }
rog_dbus = { path = "../rog-dbus" } rog_dbus = { path = "../rog-dbus" }
@@ -20,7 +20,7 @@ toml = "^0.5.8"
sysfs-class = "^0.1.2" sysfs-class = "^0.1.2"
[dev-dependencies] [dev-dependencies]
tinybmp = "^0.2.3" tinybmp = "^0.3.3"
glam = "0.14.0" glam = "^0.21.2"
rog_dbus = { path = "../rog-dbus" } rog_dbus = { path = "../rog-dbus" }
gif = "^0.11.2" gif = "^0.11.2"
+12 -6
View File
@@ -1,10 +1,10 @@
use std::{env, error::Error, path::Path, process::exit}; use std::{env, error::Error, path::Path, process::exit};
use rog_anime::{AnimeDataBuffer, AnimeDiagonal}; use rog_anime::{usb::get_anime_type, AnimeDiagonal, AnimeType};
use rog_dbus::RogDbusClient; use rog_dbus::RogDbusClientBlocking;
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
let (client, _) = RogDbusClient::new().unwrap(); let (client, _) = RogDbusClientBlocking::new().unwrap();
let args: Vec<String> = env::args().into_iter().collect(); let args: Vec<String> = env::args().into_iter().collect();
if args.len() != 3 { if args.len() != 3 {
@@ -13,13 +13,19 @@ fn main() -> Result<(), Box<dyn Error>> {
exit(-1); exit(-1);
} }
let matrix = let matrix = AnimeDiagonal::from_png(
AnimeDiagonal::from_png(Path::new(&args[1]), None, args[2].parse::<f32>().unwrap())?; Path::new(&args[1]),
None,
args[2].parse::<f32>().unwrap(),
AnimeType::GA401,
)?;
let anime_type = get_anime_type()?;
client client
.proxies() .proxies()
.anime() .anime()
.write(<AnimeDataBuffer>::from(&matrix)) .write(matrix.into_data_buffer(anime_type)?)
.unwrap(); .unwrap();
Ok(()) Ok(())
+10 -6
View File
@@ -1,7 +1,7 @@
use std::{thread::sleep, time::Duration}; use std::{thread::sleep, time::Duration};
use rog_anime::{AnimeDataBuffer, AnimeDiagonal}; use rog_anime::{usb::get_anime_type, AnimeDiagonal, AnimeType};
use rog_dbus::RogDbusClient; use rog_dbus::RogDbusClientBlocking;
// In usable data: // In usable data:
// Top row start at 1, ends at 32 // Top row start at 1, ends at 32
@@ -9,10 +9,10 @@ use rog_dbus::RogDbusClient;
// 74w x 36h diagonal used by the windows app // 74w x 36h diagonal used by the windows app
fn main() { fn main() {
let (client, _) = RogDbusClient::new().unwrap(); let (client, _) = RogDbusClientBlocking::new().unwrap();
for step in (2..50).rev() { for step in (2..50).rev() {
let mut matrix = AnimeDiagonal::new(None); let mut matrix = AnimeDiagonal::new(AnimeType::GA401, None);
for c in (0..60).into_iter().step_by(step) { for c in (0..60).into_iter().step_by(step) {
for i in matrix.get_mut().iter_mut() { for i in matrix.get_mut().iter_mut() {
i[c] = 50; i[c] = 50;
@@ -25,8 +25,12 @@ fn main() {
} }
} }
let m = <AnimeDataBuffer>::from(&matrix); let anime_type = get_anime_type().unwrap();
client.proxies().anime().write(m).unwrap(); client
.proxies()
.anime()
.write(matrix.into_data_buffer(anime_type).unwrap())
.unwrap();
sleep(Duration::from_millis(300)); sleep(Duration::from_millis(300));
} }
} }
+5 -4
View File
@@ -1,10 +1,10 @@
use std::{env, path::Path, thread::sleep}; use std::{env, path::Path, thread::sleep};
use rog_anime::{ActionData, ActionLoader, Sequences}; use rog_anime::{usb::get_anime_type, ActionData, ActionLoader, Sequences};
use rog_dbus::RogDbusClient; use rog_dbus::RogDbusClientBlocking;
fn main() { fn main() {
let (client, _) = RogDbusClient::new().unwrap(); let (client, _) = RogDbusClientBlocking::new().unwrap();
let args: Vec<String> = env::args().into_iter().collect(); let args: Vec<String> = env::args().into_iter().collect();
if args.len() != 3 { if args.len() != 3 {
@@ -14,7 +14,8 @@ fn main() {
let path = Path::new(&args[1]); let path = Path::new(&args[1]);
let brightness = args[2].parse::<f32>().unwrap(); let brightness = args[2].parse::<f32>().unwrap();
let mut seq = Sequences::new(); let anime_type = get_anime_type().unwrap();
let mut seq = Sequences::new(anime_type);
seq.insert( seq.insert(
0, 0,
&ActionLoader::AsusAnimation { &ActionLoader::AsusAnimation {
+7 -5
View File
@@ -1,5 +1,6 @@
use rog_anime::{AnimeDataBuffer, AnimeGrid}; use rog_anime::{usb::get_anime_type, AnimeDataBuffer, AnimeGrid};
use rog_dbus::RogDbusClient; use rog_dbus::RogDbusClientBlocking;
use std::convert::TryFrom;
// In usable data: // In usable data:
// Top row start at 1, ends at 32 // Top row start at 1, ends at 32
@@ -7,8 +8,9 @@ use rog_dbus::RogDbusClient;
// 74w x 36h diagonal used by the windows app // 74w x 36h diagonal used by the windows app
fn main() { fn main() {
let (client, _) = RogDbusClient::new().unwrap(); let (client, _) = RogDbusClientBlocking::new().unwrap();
let mut matrix = AnimeGrid::new(None); let anime_type = get_anime_type().unwrap();
let mut matrix = AnimeGrid::new(anime_type);
let tmp = matrix.get_mut(); let tmp = matrix.get_mut();
let mut i = 0; let mut i = 0;
@@ -38,7 +40,7 @@ fn main() {
} }
} }
let matrix = <AnimeDataBuffer>::from(matrix); let matrix = <AnimeDataBuffer>::try_from(matrix).unwrap();
client.proxies().anime().write(matrix).unwrap(); client.proxies().anime().write(matrix).unwrap();
} }
+117 -116
View File
@@ -1,128 +1,129 @@
use rog_anime::AnimeDataBuffer; use rog_anime::{usb::get_anime_type, AnimeDataBuffer};
use rog_dbus::RogDbusClient; use rog_dbus::RogDbusClientBlocking;
// In usable data: // In usable data:
// Top row start at 1, ends at 32 // Top row start at 1, ends at 32
fn main() { fn main() {
let (client, _) = RogDbusClient::new().unwrap(); let (client, _) = RogDbusClientBlocking::new().unwrap();
let mut matrix = AnimeDataBuffer::new(); let anime_type = get_anime_type().unwrap();
matrix.get_mut()[1] = 100; // start = 1 let mut matrix = AnimeDataBuffer::new(anime_type);
for n in matrix.get_mut()[2..32].iter_mut() { matrix.data_mut()[1] = 100; // start = 1
for n in matrix.data_mut()[2..32].iter_mut() {
*n = 250; *n = 250;
} }
matrix.get_mut()[32] = 100; // end matrix.data_mut()[32] = 100; // end
matrix.get_mut()[34] = 100; // start x = 0 matrix.data_mut()[34] = 100; // start x = 0
matrix.get_mut()[66] = 100; // end matrix.data_mut()[66] = 100; // end
matrix.get_mut()[69] = 100; // start x = 1 matrix.data_mut()[69] = 100; // start x = 1
matrix.get_mut()[101] = 100; // end matrix.data_mut()[101] = 100; // end
matrix.get_mut()[102] = 100; // start matrix.data_mut()[102] = 100; // start
matrix.get_mut()[134] = 100; // end matrix.data_mut()[134] = 100; // end
matrix.get_mut()[137] = 100; // start matrix.data_mut()[137] = 100; // start
matrix.get_mut()[169] = 100; // end matrix.data_mut()[169] = 100; // end
matrix.get_mut()[170] = 100; // start matrix.data_mut()[170] = 100; // start
matrix.get_mut()[202] = 100; // end matrix.data_mut()[202] = 100; // end
matrix.get_mut()[204] = 100; // start matrix.data_mut()[204] = 100; // start
matrix.get_mut()[236] = 100; // end matrix.data_mut()[236] = 100; // end
matrix.get_mut()[237] = 100; // start matrix.data_mut()[237] = 100; // start
matrix.get_mut()[268] = 100; // end matrix.data_mut()[268] = 100; // end
matrix.get_mut()[270] = 100; // start matrix.data_mut()[270] = 100; // start
matrix.get_mut()[301] = 100; // end matrix.data_mut()[301] = 100; // end
matrix.get_mut()[302] = 100; // start matrix.data_mut()[302] = 100; // start
matrix.get_mut()[332] = 100; // end matrix.data_mut()[332] = 100; // end
matrix.get_mut()[334] = 100; // start matrix.data_mut()[334] = 100; // start
matrix.get_mut()[364] = 100; // end matrix.data_mut()[364] = 100; // end
matrix.get_mut()[365] = 100; // start matrix.data_mut()[365] = 100; // start
matrix.get_mut()[394] = 100; // end matrix.data_mut()[394] = 100; // end
matrix.get_mut()[396] = 100; // start matrix.data_mut()[396] = 100; // start
matrix.get_mut()[425] = 100; // end matrix.data_mut()[425] = 100; // end
matrix.get_mut()[426] = 100; // start matrix.data_mut()[426] = 100; // start
matrix.get_mut()[454] = 100; // end matrix.data_mut()[454] = 100; // end
matrix.get_mut()[456] = 100; // start matrix.data_mut()[456] = 100; // start
matrix.get_mut()[484] = 100; // end matrix.data_mut()[484] = 100; // end
matrix.get_mut()[485] = 100; // start matrix.data_mut()[485] = 100; // start
matrix.get_mut()[512] = 100; // end matrix.data_mut()[512] = 100; // end
matrix.get_mut()[514] = 100; // start matrix.data_mut()[514] = 100; // start
matrix.get_mut()[541] = 100; // end matrix.data_mut()[541] = 100; // end
matrix.get_mut()[542] = 100; // start matrix.data_mut()[542] = 100; // start
matrix.get_mut()[568] = 100; // end matrix.data_mut()[568] = 100; // end
matrix.get_mut()[570] = 100; // start matrix.data_mut()[570] = 100; // start
matrix.get_mut()[596] = 100; // end matrix.data_mut()[596] = 100; // end
matrix.get_mut()[597] = 100; // start matrix.data_mut()[597] = 100; // start
matrix.get_mut()[622] = 100; // end matrix.data_mut()[622] = 100; // end
matrix.get_mut()[624] = 100; // start matrix.data_mut()[624] = 100; // start
matrix.get_mut()[649] = 100; // end matrix.data_mut()[649] = 100; // end
matrix.get_mut()[650] = 100; // start matrix.data_mut()[650] = 100; // start
matrix.get_mut()[674] = 100; // end matrix.data_mut()[674] = 100; // end
matrix.get_mut()[676] = 100; // start matrix.data_mut()[676] = 100; // start
matrix.get_mut()[700] = 100; // end matrix.data_mut()[700] = 100; // end
matrix.get_mut()[701] = 100; // start matrix.data_mut()[701] = 100; // start
matrix.get_mut()[724] = 100; // end matrix.data_mut()[724] = 100; // end
matrix.get_mut()[726] = 100; // start matrix.data_mut()[726] = 100; // start
matrix.get_mut()[749] = 100; // end matrix.data_mut()[749] = 100; // end
matrix.get_mut()[750] = 100; // start matrix.data_mut()[750] = 100; // start
matrix.get_mut()[772] = 100; // end matrix.data_mut()[772] = 100; // end
matrix.get_mut()[774] = 100; // start matrix.data_mut()[774] = 100; // start
matrix.get_mut()[796] = 100; // end matrix.data_mut()[796] = 100; // end
matrix.get_mut()[797] = 100; // start matrix.data_mut()[797] = 100; // start
matrix.get_mut()[818] = 100; // end matrix.data_mut()[818] = 100; // end
matrix.get_mut()[820] = 100; // start matrix.data_mut()[820] = 100; // start
matrix.get_mut()[841] = 100; // end matrix.data_mut()[841] = 100; // end
matrix.get_mut()[842] = 100; // start matrix.data_mut()[842] = 100; // start
matrix.get_mut()[862] = 100; // end matrix.data_mut()[862] = 100; // end
matrix.get_mut()[864] = 100; // start matrix.data_mut()[864] = 100; // start
matrix.get_mut()[884] = 100; // end matrix.data_mut()[884] = 100; // end
matrix.get_mut()[885] = 100; // start matrix.data_mut()[885] = 100; // start
matrix.get_mut()[904] = 100; // end matrix.data_mut()[904] = 100; // end
matrix.get_mut()[906] = 100; // start matrix.data_mut()[906] = 100; // start
matrix.get_mut()[925] = 100; // end matrix.data_mut()[925] = 100; // end
matrix.get_mut()[926] = 100; // start matrix.data_mut()[926] = 100; // start
matrix.get_mut()[944] = 100; // end matrix.data_mut()[944] = 100; // end
matrix.get_mut()[946] = 100; // start matrix.data_mut()[946] = 100; // start
matrix.get_mut()[964] = 100; // end matrix.data_mut()[964] = 100; // end
matrix.get_mut()[965] = 100; // start matrix.data_mut()[965] = 100; // start
matrix.get_mut()[982] = 100; // end matrix.data_mut()[982] = 100; // end
matrix.get_mut()[984] = 100; // start matrix.data_mut()[984] = 100; // start
matrix.get_mut()[1001] = 100; // end matrix.data_mut()[1001] = 100; // end
matrix.get_mut()[1002] = 100; // start matrix.data_mut()[1002] = 100; // start
matrix.get_mut()[1018] = 100; // end matrix.data_mut()[1018] = 100; // end
matrix.get_mut()[1020] = 100; // start matrix.data_mut()[1020] = 100; // start
matrix.get_mut()[1036] = 100; // end matrix.data_mut()[1036] = 100; // end
matrix.get_mut()[1037] = 100; // start matrix.data_mut()[1037] = 100; // start
matrix.get_mut()[1052] = 100; // end matrix.data_mut()[1052] = 100; // end
matrix.get_mut()[1054] = 100; // start matrix.data_mut()[1054] = 100; // start
matrix.get_mut()[1069] = 100; // end matrix.data_mut()[1069] = 100; // end
matrix.get_mut()[1070] = 100; // start matrix.data_mut()[1070] = 100; // start
matrix.get_mut()[1084] = 100; // end matrix.data_mut()[1084] = 100; // end
matrix.get_mut()[1086] = 100; // start matrix.data_mut()[1086] = 100; // start
matrix.get_mut()[1100] = 100; // end matrix.data_mut()[1100] = 100; // end
matrix.get_mut()[1101] = 100; // start matrix.data_mut()[1101] = 100; // start
matrix.get_mut()[1114] = 100; // end matrix.data_mut()[1114] = 100; // end
matrix.get_mut()[1116] = 100; // start matrix.data_mut()[1116] = 100; // start
matrix.get_mut()[1129] = 100; // end matrix.data_mut()[1129] = 100; // end
matrix.get_mut()[1130] = 100; // start matrix.data_mut()[1130] = 100; // start
matrix.get_mut()[1142] = 100; // end matrix.data_mut()[1142] = 100; // end
matrix.get_mut()[1144] = 100; // start matrix.data_mut()[1144] = 100; // start
matrix.get_mut()[1156] = 100; // end matrix.data_mut()[1156] = 100; // end
matrix.get_mut()[1157] = 100; // start matrix.data_mut()[1157] = 100; // start
matrix.get_mut()[1168] = 100; // end matrix.data_mut()[1168] = 100; // end
matrix.get_mut()[1170] = 100; // start matrix.data_mut()[1170] = 100; // start
matrix.get_mut()[1181] = 100; // end matrix.data_mut()[1181] = 100; // end
matrix.get_mut()[1182] = 100; // start matrix.data_mut()[1182] = 100; // start
matrix.get_mut()[1192] = 100; // end matrix.data_mut()[1192] = 100; // end
matrix.get_mut()[1194] = 100; // start matrix.data_mut()[1194] = 100; // start
matrix.get_mut()[1204] = 100; // end matrix.data_mut()[1204] = 100; // end
matrix.get_mut()[1205] = 100; // start matrix.data_mut()[1205] = 100; // start
matrix.get_mut()[1214] = 100; // end matrix.data_mut()[1214] = 100; // end
matrix.get_mut()[1216] = 100; // start matrix.data_mut()[1216] = 100; // start
matrix.get_mut()[1225] = 100; // end matrix.data_mut()[1225] = 100; // end
matrix.get_mut()[1226] = 100; // start matrix.data_mut()[1226] = 100; // start
matrix.get_mut()[1234] = 100; // end matrix.data_mut()[1234] = 100; // end
matrix.get_mut()[1236] = 100; // start matrix.data_mut()[1236] = 100; // start
for n in matrix.get_mut()[1237..1244].iter_mut() { for n in matrix.data_mut()[1237..1244].iter_mut() {
*n = 250; *n = 250;
} }
matrix.get_mut()[1244] = 100; // end matrix.data_mut()[1244] = 100; // end
println!("{:?}", &matrix); println!("{:?}", &matrix);
client.proxies().anime().write(matrix).unwrap(); client.proxies().anime().write(matrix).unwrap();
+7 -3
View File
@@ -1,12 +1,14 @@
use std::convert::TryFrom;
use std::{env, error::Error, path::Path, process::exit}; use std::{env, error::Error, path::Path, process::exit};
use rog_anime::{ use rog_anime::{
usb::get_anime_type,
AnimeDataBuffer, {AnimeImage, Vec2}, AnimeDataBuffer, {AnimeImage, Vec2},
}; };
use rog_dbus::RogDbusClient; use rog_dbus::RogDbusClientBlocking;
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
let (client, _) = RogDbusClient::new().unwrap(); let (client, _) = RogDbusClientBlocking::new().unwrap();
let args: Vec<String> = env::args().into_iter().collect(); let args: Vec<String> = env::args().into_iter().collect();
if args.len() != 7 { if args.len() != 7 {
@@ -15,6 +17,7 @@ fn main() -> Result<(), Box<dyn Error>> {
exit(-1); exit(-1);
} }
let anime_type = get_anime_type()?;
let matrix = AnimeImage::from_png( let matrix = AnimeImage::from_png(
Path::new(&args[1]), Path::new(&args[1]),
args[2].parse::<f32>().unwrap(), args[2].parse::<f32>().unwrap(),
@@ -24,12 +27,13 @@ fn main() -> Result<(), Box<dyn Error>> {
args[5].parse::<f32>().unwrap(), args[5].parse::<f32>().unwrap(),
), ),
args[6].parse::<f32>().unwrap(), args[6].parse::<f32>().unwrap(),
anime_type,
)?; )?;
client client
.proxies() .proxies()
.anime() .anime()
.write(<AnimeDataBuffer>::from(&matrix)) .write(<AnimeDataBuffer>::try_from(&matrix)?)
.unwrap(); .unwrap();
Ok(()) Ok(())
+7 -3
View File
@@ -1,14 +1,16 @@
use std::convert::TryFrom;
use std::{ use std::{
env, error::Error, f32::consts::PI, path::Path, process::exit, thread::sleep, time::Duration, env, error::Error, f32::consts::PI, path::Path, process::exit, thread::sleep, time::Duration,
}; };
use rog_anime::{ use rog_anime::{
usb::get_anime_type,
AnimeDataBuffer, {AnimeImage, Vec2}, AnimeDataBuffer, {AnimeImage, Vec2},
}; };
use rog_dbus::RogDbusClient; use rog_dbus::RogDbusClientBlocking;
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
let (client, _) = RogDbusClient::new().unwrap(); let (client, _) = RogDbusClientBlocking::new().unwrap();
let args: Vec<String> = env::args().into_iter().collect(); let args: Vec<String> = env::args().into_iter().collect();
if args.len() != 7 { if args.len() != 7 {
@@ -17,6 +19,7 @@ fn main() -> Result<(), Box<dyn Error>> {
exit(-1); exit(-1);
} }
let anime_type = get_anime_type()?;
let mut matrix = AnimeImage::from_png( let mut matrix = AnimeImage::from_png(
Path::new(&args[1]), Path::new(&args[1]),
args[2].parse::<f32>().unwrap(), args[2].parse::<f32>().unwrap(),
@@ -26,6 +29,7 @@ fn main() -> Result<(), Box<dyn Error>> {
args[5].parse::<f32>().unwrap(), args[5].parse::<f32>().unwrap(),
), ),
args[6].parse::<f32>().unwrap(), args[6].parse::<f32>().unwrap(),
anime_type,
)?; )?;
loop { loop {
@@ -38,7 +42,7 @@ fn main() -> Result<(), Box<dyn Error>> {
client client
.proxies() .proxies()
.anime() .anime()
.write(<AnimeDataBuffer>::from(&matrix)) .write(<AnimeDataBuffer>::try_from(&matrix)?)
.unwrap(); .unwrap();
sleep(Duration::from_micros(500)); sleep(Duration::from_micros(500));
} }
+2
View File
@@ -16,6 +16,8 @@ pub struct AnimeCommand {
pub boot_enable: Option<bool>, pub boot_enable: Option<bool>,
#[options(meta = "", help = "set global AniMe brightness value")] #[options(meta = "", help = "set global AniMe brightness value")]
pub brightness: Option<f32>, pub brightness: Option<f32>,
#[options(help = "clear the display")]
pub clear: bool,
#[options(command)] #[options(command)]
pub command: Option<AnimeActions>, pub command: Option<AnimeActions>,
} }
+100 -54
View File
@@ -2,6 +2,69 @@ use gumdrop::Options;
use rog_aura::{error::Error, AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed}; use rog_aura::{error::Error, AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed};
use std::str::FromStr; use std::str::FromStr;
#[derive(Options)]
pub struct LedPowerCommand1 {
#[options(help = "print help message")]
pub help: bool,
#[options(meta = "", help = "Control if LEDs enabled while awake <true/false>")]
pub awake: Option<bool>,
#[options(meta = "", help = "Use with awake option <true/false>")]
pub keyboard: Option<bool>,
#[options(meta = "", help = "Use with awake option <true/false>")]
pub lightbar: Option<bool>,
#[options(meta = "", help = "Control boot animations <true/false>")]
pub boot: Option<bool>,
#[options(meta = "", help = "Control suspend animations <true/false>")]
pub sleep: Option<bool>,
}
#[derive(Options)]
pub struct LedPowerCommand2 {
#[options(help = "print help message")]
pub help: bool,
#[options(command)]
pub command: Option<SetAuraEnabled>,
}
#[derive(Options)]
pub enum SetAuraEnabled {
/// Applies to both old and new models
#[options(help = "set <keyboard, logo, lightbar> to enabled while device is awake")]
Awake(AuraEnabled),
#[options(help = "set <keyboard, logo, lightbar> to enabled while the device is booting")]
Boot(AuraEnabled),
#[options(help = "set <keyboard, logo, lightbar> to animate while the device is suspended")]
Sleep(AuraEnabled),
#[options(help = "set <keyboard, logo, lightbar> to animate while the device is shutdown")]
Shutdown(AuraEnabled),
}
#[derive(Debug, Clone, Default, Options)]
pub struct AuraEnabled {
#[options(help = "print help message")]
pub help: bool,
#[options(meta = "", help = "<true/false>")]
pub keyboard: Option<bool>,
#[options(meta = "", help = "<true/false>")]
pub logo: Option<bool>,
#[options(meta = "", help = "<true/false>")]
pub lightbar: Option<bool>,
}
// impl FromStr for AuraEnabled {
// type Err = Error;
// fn from_str(s: &str) -> Result<Self, Self::Err> {
// let s = s.to_lowercase();
// Ok(Self {
// help: false,
// keyboard: None,
// logo: None,
// lightbar: None,
// })
// }
// }
#[derive(Options)] #[derive(Options)]
pub struct LedBrightness { pub struct LedBrightness {
level: Option<u32>, level: Option<u32>,
@@ -50,7 +113,14 @@ pub struct SingleSpeed {
help: bool, help: bool,
#[options(no_long, meta = "WORD", help = "set the speed: low, med, high")] #[options(no_long, meta = "WORD", help = "set the speed: low, med, high")]
pub speed: Speed, pub speed: Speed,
#[options(
no_long,
meta = "",
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
)]
pub zone: AuraZone,
} }
#[derive(Debug, Clone, Options, Default)] #[derive(Debug, Clone, Options, Default)]
pub struct SingleSpeedDirection { pub struct SingleSpeedDirection {
#[options(help = "print help message")] #[options(help = "print help message")]
@@ -59,6 +129,12 @@ pub struct SingleSpeedDirection {
pub direction: Direction, pub direction: Direction,
#[options(no_long, meta = "", help = "set the speed: low, med, high")] #[options(no_long, meta = "", help = "set the speed: low, med, high")]
pub speed: Speed, pub speed: Speed,
#[options(
no_long,
meta = "",
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
)]
pub zone: AuraZone,
} }
#[derive(Debug, Clone, Default, Options)] #[derive(Debug, Clone, Default, Options)]
@@ -67,6 +143,12 @@ pub struct SingleColour {
help: bool, help: bool,
#[options(no_long, meta = "", help = "set the RGB value e.g, ff00ff")] #[options(no_long, meta = "", help = "set the RGB value e.g, ff00ff")]
pub colour: Colour, pub colour: Colour,
#[options(
no_long,
meta = "",
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
)]
pub zone: AuraZone,
} }
#[derive(Debug, Clone, Default, Options)] #[derive(Debug, Clone, Default, Options)]
@@ -77,6 +159,12 @@ pub struct SingleColourSpeed {
pub colour: Colour, pub colour: Colour,
#[options(no_long, meta = "", help = "set the speed: low, med, high")] #[options(no_long, meta = "", help = "set the speed: low, med, high")]
pub speed: Speed, pub speed: Speed,
#[options(
no_long,
meta = "",
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
)]
pub zone: AuraZone,
} }
#[derive(Debug, Clone, Options, Default)] #[derive(Debug, Clone, Options, Default)]
@@ -89,10 +177,16 @@ pub struct TwoColourSpeed {
pub colour2: Colour, pub colour2: Colour,
#[options(no_long, meta = "", help = "set the speed: low, med, high")] #[options(no_long, meta = "", help = "set the speed: low, med, high")]
pub speed: Speed, pub speed: Speed,
#[options(
no_long,
meta = "",
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
)]
pub zone: AuraZone,
} }
#[derive(Debug, Clone, Default, Options)] #[derive(Debug, Clone, Default, Options)]
pub struct MultiColour { pub struct MultiZone {
#[options(help = "print help message")] #[options(help = "print help message")]
help: bool, help: bool,
#[options(short = "a", meta = "", help = "set the RGB value e.g, ff00ff")] #[options(short = "a", meta = "", help = "set the RGB value e.g, ff00ff")]
@@ -152,10 +246,6 @@ pub enum SetAuraBuiltin {
Comet(SingleColour), Comet(SingleColour),
#[options(help = "set a wide vertical line zooming from left")] #[options(help = "set a wide vertical line zooming from left")]
Flash(SingleColour), Flash(SingleColour),
#[options(help = "4-zone multi-colour")]
MultiStatic(MultiColour),
#[options(help = "4-zone multi-colour breathing")]
MultiBreathe(MultiColourSpeed),
} }
impl Default for SetAuraBuiltin { impl Default for SetAuraBuiltin {
@@ -168,6 +258,7 @@ impl From<&SingleColour> for AuraEffect {
fn from(aura: &SingleColour) -> Self { fn from(aura: &SingleColour) -> Self {
Self { Self {
colour1: aura.colour, colour1: aura.colour,
zone: aura.zone,
..Default::default() ..Default::default()
} }
} }
@@ -177,6 +268,7 @@ impl From<&SingleSpeed> for AuraEffect {
fn from(aura: &SingleSpeed) -> Self { fn from(aura: &SingleSpeed) -> Self {
Self { Self {
speed: aura.speed, speed: aura.speed,
zone: aura.zone,
..Default::default() ..Default::default()
} }
} }
@@ -187,6 +279,7 @@ impl From<&SingleColourSpeed> for AuraEffect {
Self { Self {
colour1: aura.colour, colour1: aura.colour,
speed: aura.speed, speed: aura.speed,
zone: aura.zone,
..Default::default() ..Default::default()
} }
} }
@@ -197,6 +290,7 @@ impl From<&TwoColourSpeed> for AuraEffect {
Self { Self {
colour1: aura.colour, colour1: aura.colour,
colour2: aura.colour2, colour2: aura.colour2,
zone: aura.zone,
..Default::default() ..Default::default()
} }
} }
@@ -207,6 +301,7 @@ impl From<&SingleSpeedDirection> for AuraEffect {
Self { Self {
speed: aura.speed, speed: aura.speed,
direction: aura.direction, direction: aura.direction,
zone: aura.zone,
..Default::default() ..Default::default()
} }
} }
@@ -275,55 +370,6 @@ impl From<&SetAuraBuiltin> for AuraEffect {
data.mode = AuraModeNum::Flash; data.mode = AuraModeNum::Flash;
data data
} }
_ => AuraEffect::default(),
} }
} }
} }
impl From<&SetAuraBuiltin> for Vec<AuraEffect> {
fn from(aura: &SetAuraBuiltin) -> Vec<AuraEffect> {
let mut zones = vec![AuraEffect::default(); 4];
match aura {
SetAuraBuiltin::MultiStatic(data) => {
zones[0].mode = AuraModeNum::Static;
zones[0].zone = AuraZone::One;
zones[0].colour1 = data.colour1;
zones[1].mode = AuraModeNum::Static;
zones[1].zone = AuraZone::Two;
zones[1].colour1 = data.colour2;
zones[2].mode = AuraModeNum::Static;
zones[2].zone = AuraZone::Three;
zones[2].colour1 = data.colour3;
zones[3].mode = AuraModeNum::Static;
zones[3].zone = AuraZone::Four;
zones[3].colour1 = data.colour4;
}
SetAuraBuiltin::MultiBreathe(data) => {
zones[0].mode = AuraModeNum::Breathe;
zones[0].zone = AuraZone::One;
zones[0].colour1 = data.colour1;
zones[0].speed = data.speed;
zones[1].mode = AuraModeNum::Breathe;
zones[1].zone = AuraZone::Two;
zones[1].colour1 = data.colour2;
zones[1].speed = data.speed;
zones[2].mode = AuraModeNum::Breathe;
zones[2].zone = AuraZone::Three;
zones[2].colour1 = data.colour3;
zones[2].speed = data.speed;
zones[3].mode = AuraModeNum::Breathe;
zones[3].zone = AuraZone::Four;
zones[3].colour1 = data.colour4;
zones[3].speed = data.speed;
}
_ => {}
}
zones
}
}
+18 -13
View File
@@ -1,6 +1,6 @@
use crate::{ use crate::{
anime_cli::AnimeCommand, anime_cli::AnimeCommand,
aura_cli::{LedBrightness, SetAuraBuiltin}, aura_cli::{LedBrightness, LedPowerCommand1, LedPowerCommand2, SetAuraBuiltin},
profiles_cli::{FanCurveCommand, ProfileCommand}, profiles_cli::{FanCurveCommand, ProfileCommand},
}; };
use gumdrop::Options; use gumdrop::Options;
@@ -29,6 +29,10 @@ pub struct CliStart {
pub enum CliCommand { pub enum CliCommand {
#[options(help = "Set the keyboard lighting from built-in modes")] #[options(help = "Set the keyboard lighting from built-in modes")]
LedMode(LedModeCommand), LedMode(LedModeCommand),
#[options(help = "Set the LED power states")]
LedPow1(LedPowerCommand1),
#[options(help = "Set the LED power states")]
LedPow2(LedPowerCommand2),
#[options(help = "Set or select platform_profile")] #[options(help = "Set or select platform_profile")]
Profile(ProfileCommand), Profile(ProfileCommand),
#[options(help = "Set, select, or modify fan curves if supported")] #[options(help = "Set, select, or modify fan curves if supported")]
@@ -49,16 +53,6 @@ pub struct LedModeCommand {
pub next_mode: bool, pub next_mode: bool,
#[options(help = "switch to previous aura mode")] #[options(help = "switch to previous aura mode")]
pub prev_mode: bool, pub prev_mode: bool,
#[options(
meta = "",
help = "set the keyboard LED to enabled while the device is awake"
)]
pub awake_enable: Option<bool>,
#[options(
meta = "",
help = "set the keyboard LED suspend animation to enabled while the device is suspended"
)]
pub sleep_enable: Option<bool>,
#[options(command)] #[options(command)]
pub command: Option<SetAuraBuiltin>, pub command: Option<SetAuraBuiltin>,
} }
@@ -75,18 +69,29 @@ pub struct BiosCommand {
pub help: bool, pub help: bool,
#[options( #[options(
meta = "", meta = "",
short = "S",
no_long, no_long,
help = "set bios POST sound: asusctl -p <true/false>" help = "set bios POST sound: asusctl -p <true/false>"
)] )]
pub post_sound_set: Option<bool>, pub post_sound_set: Option<bool>,
#[options(no_long, help = "read bios POST sound")] #[options(no_long, short = "s", help = "read bios POST sound")]
pub post_sound_get: bool, pub post_sound_get: bool,
#[options( #[options(
meta = "", meta = "",
short = "D",
no_long, no_long,
help = "activate dGPU dedicated/G-Sync: asusctl -d <true/false>, reboot required" help = "activate dGPU dedicated/G-Sync: asusctl -d <true/false>, reboot required"
)] )]
pub dedicated_gfx_set: Option<bool>, pub dedicated_gfx_set: Option<bool>,
#[options(no_long, help = "get GPU mode")] #[options(no_long, short = "d", help = "get GPU mode")]
pub dedicated_gfx_get: bool, pub dedicated_gfx_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,
} }
+318 -70
View File
@@ -1,14 +1,19 @@
use std::convert::TryFrom;
use std::process::Command; use std::process::Command;
use std::thread::sleep; use std::thread::sleep;
use std::{env::args, path::Path}; use std::{env::args, path::Path};
use aura_cli::{LedPowerCommand1, LedPowerCommand2};
use gumdrop::{Opt, Options}; use gumdrop::{Opt, Options};
use anime_cli::{AnimeActions, AnimeCommand}; use anime_cli::{AnimeActions, AnimeCommand};
use profiles_cli::{FanCurveCommand, ProfileCommand}; use profiles_cli::{FanCurveCommand, ProfileCommand};
use rog_anime::usb::get_anime_type;
use rog_anime::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, Vec2}; use rog_anime::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, Vec2};
use rog_aura::usb::{AuraDev1866, AuraDev19b6, AuraDevTuf, AuraDevice, AuraPowerDev};
use rog_aura::{self, AuraEffect}; use rog_aura::{self, AuraEffect};
use rog_dbus::RogDbusClient; use rog_dbus::RogDbusClientBlocking;
use rog_profiles::error::ProfileError; use rog_profiles::error::ProfileError;
use rog_supported::SupportedFunctions; use rog_supported::SupportedFunctions;
use rog_supported::{ use rog_supported::{
@@ -16,7 +21,7 @@ use rog_supported::{
RogBiosSupportedFunctions, RogBiosSupportedFunctions,
}; };
use crate::aura_cli::{LedBrightness, SetAuraBuiltin}; use crate::aura_cli::LedBrightness;
use crate::cli_opts::*; use crate::cli_opts::*;
mod anime_cli; mod anime_cli;
@@ -24,8 +29,6 @@ mod aura_cli;
mod cli_opts; mod cli_opts;
mod profiles_cli; mod profiles_cli;
const CONFIG_ADVICE: &str = "A config file need to be removed so a new one can be generated";
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<String> = args().skip(1).collect(); let args: Vec<String> = args().skip(1).collect();
@@ -47,7 +50,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
} }
let (dbus, _) = RogDbusClient::new() let (dbus, _) = RogDbusClientBlocking::new()
.map_err(|e| { .map_err(|e| {
print_error_help(Box::new(e), None); print_error_help(Box::new(e), None);
std::process::exit(3); std::process::exit(3);
@@ -57,7 +60,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let supported = dbus let supported = dbus
.proxies() .proxies()
.supported() .supported()
.get_supported_functions() .supported_functions()
.map_err(|e| { .map_err(|e| {
print_error_help(Box::new(e), None); print_error_help(Box::new(e), None);
std::process::exit(4); std::process::exit(4);
@@ -79,15 +82,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
fn print_error_help(err: Box<dyn std::error::Error>, supported: Option<&SupportedFunctions>) { fn print_error_help(err: Box<dyn std::error::Error>, supported: Option<&SupportedFunctions>) {
if do_diagnose("asusd") { check_service("asusd");
println!("\nError: {}\n", err); println!("\nError: {}\n", err);
print_versions(); print_versions();
println!();
print_laptop_info();
if let Some(supported) = supported {
println!(); println!();
print_laptop_info(); println!("Supported laptop functions:\n\n{}", supported);
if let Some(supported) = supported {
println!();
println!("Supported laptop functions:\n\n{}", supported);
}
} }
} }
@@ -112,7 +114,7 @@ fn print_laptop_info() {
println!("Board name: {}", board_name.trim()); println!("Board name: {}", board_name.trim());
} }
fn do_diagnose(name: &str) -> bool { fn check_service(name: &str) -> bool {
if name != "asusd" && !check_systemd_unit_enabled(name) { if name != "asusd" && !check_systemd_unit_enabled(name) {
println!( println!(
"\n\x1b[0;31m{} is not enabled, enable it with `systemctl enable {}\x1b[0m", "\n\x1b[0;31m{} is not enabled, enable it with `systemctl enable {}\x1b[0m",
@@ -125,13 +127,6 @@ fn do_diagnose(name: &str) -> bool {
name, name name, name
); );
return true; return true;
} else {
println!("\nSome error happened (sorry)");
println!(
"Please use `systemctl status {}` and `journalctl -b -u {}` for more information",
name, name
);
println!("{}", CONFIG_ADVICE);
} }
false false
} }
@@ -139,10 +134,12 @@ fn do_diagnose(name: &str) -> bool {
fn do_parsed( fn do_parsed(
parsed: &CliStart, parsed: &CliStart,
supported: &SupportedFunctions, supported: &SupportedFunctions,
dbus: &RogDbusClient, dbus: &RogDbusClientBlocking,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
match &parsed.command { match &parsed.command {
Some(CliCommand::LedMode(mode)) => handle_led_mode(dbus, &supported.keyboard_led, mode)?, Some(CliCommand::LedMode(mode)) => handle_led_mode(dbus, &supported.keyboard_led, mode)?,
Some(CliCommand::LedPow1(pow)) => handle_led_power1(dbus, &supported.keyboard_led, pow)?,
Some(CliCommand::LedPow2(pow)) => handle_led_power2(dbus, &supported.keyboard_led, pow)?,
Some(CliCommand::Profile(cmd)) => handle_profile(dbus, &supported.platform_profile, cmd)?, Some(CliCommand::Profile(cmd)) => handle_profile(dbus, &supported.platform_profile, cmd)?,
Some(CliCommand::FanCurve(cmd)) => { Some(CliCommand::FanCurve(cmd)) => {
handle_fan_curve(dbus, &supported.platform_profile, cmd)? handle_fan_curve(dbus, &supported.platform_profile, cmd)?
@@ -161,8 +158,32 @@ fn do_parsed(
println!("{}", CliStart::usage()); println!("{}", CliStart::usage());
println!(); println!();
if let Some(cmdlist) = CliStart::command_list() { if let Some(cmdlist) = CliStart::command_list() {
println!("{}", cmdlist); let commands: Vec<String> = cmdlist.lines().map(|s| s.to_string()).collect();
for command in commands.iter().filter(|command| {
if !matches!(
supported.keyboard_led.prod_id,
AuraDevice::X1854
| AuraDevice::X1869
| AuraDevice::X1866
| AuraDevice::Tuf
) && command.trim().starts_with("led-pow-1")
{
return false;
}
if supported.keyboard_led.prod_id != AuraDevice::X19B6
&& command.trim().starts_with("led-pow-2")
{
return false;
}
true
}) {
println!("{}", command);
}
} }
println!("\nExtra help can be requested on any command or subcommand:");
println!(" asusctl led-mode --help");
println!(" asusctl led-mode static --help");
} }
} }
} }
@@ -170,13 +191,13 @@ fn do_parsed(
if let Some(brightness) = &parsed.kbd_bright { if let Some(brightness) = &parsed.kbd_bright {
match brightness.level() { match brightness.level() {
None => { None => {
let level = dbus.proxies().led().get_led_brightness()?; let level = dbus.proxies().led().led_brightness()?;
println!("Current keyboard led brightness: {}", level); println!("Current keyboard led brightness: {}", level);
} }
Some(level) => dbus Some(level) => dbus
.proxies() .proxies()
.led() .led()
.set_led_brightness(<rog_aura::LedBrightness>::from(level))?, .set_brightness(<rog_aura::LedBrightness>::from(level))?,
} }
} }
@@ -193,7 +214,7 @@ fn do_parsed(
} }
if let Some(chg_limit) = parsed.chg_limit { if let Some(chg_limit) = parsed.chg_limit {
dbus.proxies().charge().write_limit(chg_limit)?; dbus.proxies().charge().set_limit(chg_limit)?;
} }
Ok(()) Ok(())
@@ -206,7 +227,7 @@ fn do_gfx() -> Result<(), Box<dyn std::error::Error>> {
} }
fn handle_anime( fn handle_anime(
dbus: &RogDbusClient, dbus: &RogDbusClientBlocking,
_supported: &AnimeSupportedFunctions, _supported: &AnimeSupportedFunctions,
cmd: &AnimeCommand, cmd: &AnimeCommand,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
@@ -228,9 +249,18 @@ fn handle_anime(
dbus.proxies().anime().set_boot_on_off(anime_boot)? dbus.proxies().anime().set_boot_on_off(anime_boot)?
} }
if let Some(bright) = cmd.brightness { if let Some(bright) = cmd.brightness {
dbus.proxies().anime().set_brightness(bright as f32)? verify_brightness(bright);
dbus.proxies().anime().set_brightness(bright)?
} }
if cmd.clear {
let anime_type = get_anime_type()?;
let data = vec![0u8; anime_type.data_length()];
let tmp = AnimeDataBuffer::from_vec(anime_type, data)?;
dbus.proxies().anime().write(tmp)?;
}
if let Some(action) = cmd.command.as_ref() { if let Some(action) = cmd.command.as_ref() {
let anime_type = get_anime_type()?;
match action { match action {
AnimeActions::Image(image) => { AnimeActions::Image(image) => {
if image.help_requested() || image.path.is_empty() { if image.help_requested() || image.path.is_empty() {
@@ -240,6 +270,7 @@ fn handle_anime(
} }
std::process::exit(1); std::process::exit(1);
} }
verify_brightness(image.bright);
let matrix = AnimeImage::from_png( let matrix = AnimeImage::from_png(
Path::new(&image.path), Path::new(&image.path),
@@ -247,11 +278,12 @@ fn handle_anime(
image.angle, image.angle,
Vec2::new(image.x_pos, image.y_pos), Vec2::new(image.x_pos, image.y_pos),
image.bright, image.bright,
anime_type,
)?; )?;
dbus.proxies() dbus.proxies()
.anime() .anime()
.write(<AnimeDataBuffer>::from(&matrix))?; .write(<AnimeDataBuffer>::try_from(&matrix)?)?;
} }
AnimeActions::PixelImage(image) => { AnimeActions::PixelImage(image) => {
if image.help_requested() || image.path.is_empty() { if image.help_requested() || image.path.is_empty() {
@@ -261,12 +293,18 @@ fn handle_anime(
} }
std::process::exit(1); std::process::exit(1);
} }
verify_brightness(image.bright);
let matrix = AnimeDiagonal::from_png(Path::new(&image.path), None, image.bright)?; let matrix = AnimeDiagonal::from_png(
Path::new(&image.path),
None,
image.bright,
anime_type,
)?;
dbus.proxies() dbus.proxies()
.anime() .anime()
.write(<AnimeDataBuffer>::from(&matrix))?; .write(matrix.into_data_buffer(anime_type)?)?;
} }
AnimeActions::Gif(gif) => { AnimeActions::Gif(gif) => {
if gif.help_requested() || gif.path.is_empty() { if gif.help_requested() || gif.path.is_empty() {
@@ -276,6 +314,7 @@ fn handle_anime(
} }
std::process::exit(1); std::process::exit(1);
} }
verify_brightness(gif.bright);
let matrix = AnimeGif::from_gif( let matrix = AnimeGif::from_gif(
Path::new(&gif.path), Path::new(&gif.path),
@@ -284,6 +323,7 @@ fn handle_anime(
Vec2::new(gif.x_pos, gif.y_pos), Vec2::new(gif.x_pos, gif.y_pos),
AnimTime::Count(1), AnimTime::Count(1),
gif.bright, gif.bright,
anime_type,
)?; )?;
let mut loops = gif.loops as i32; let mut loops = gif.loops as i32;
@@ -308,11 +348,13 @@ fn handle_anime(
} }
std::process::exit(1); std::process::exit(1);
} }
verify_brightness(gif.bright);
let matrix = AnimeGif::from_diagonal_gif( let matrix = AnimeGif::from_diagonal_gif(
Path::new(&gif.path), Path::new(&gif.path),
AnimTime::Count(1), AnimTime::Count(1),
gif.bright, gif.bright,
anime_type,
)?; )?;
let mut loops = gif.loops as i32; let mut loops = gif.loops as i32;
@@ -334,17 +376,22 @@ fn handle_anime(
Ok(()) Ok(())
} }
fn verify_brightness(brightness: f32) {
if brightness < 0.0 || brightness > 1.0 {
println!(
"Image and global brightness must be between 0.0 and 1.0 (inclusive), was {}",
brightness
);
std::process::exit(1);
}
}
fn handle_led_mode( fn handle_led_mode(
dbus: &RogDbusClient, dbus: &RogDbusClientBlocking,
supported: &LedSupportedFunctions, supported: &LedSupportedFunctions,
mode: &LedModeCommand, mode: &LedModeCommand,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
if mode.command.is_none() if mode.command.is_none() && !mode.prev_mode && !mode.next_mode {
&& !mode.prev_mode
&& !mode.next_mode
&& mode.sleep_enable.is_none()
&& mode.awake_enable.is_none()
{
if !mode.help { if !mode.help {
println!("Missing arg or command\n"); println!("Missing arg or command\n");
} }
@@ -355,11 +402,14 @@ fn handle_led_mode(
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_string()).collect(); let commands: Vec<String> = cmdlist.lines().map(|s| s.to_string()).collect();
for command in commands.iter().filter(|command| { for command in commands.iter().filter(|command| {
for mode in &supported.stock_led_modes { for mode in &supported.stock_led_modes {
if command.contains(&<&str>::from(mode).to_lowercase()) { if command
.trim()
.starts_with(&<&str>::from(mode).to_lowercase())
{
return true; return true;
} }
} }
if supported.multizone_led_mode { if !supported.multizone_led_mode.is_empty() && command.trim().starts_with("multi") {
return true; return true;
} }
false false
@@ -385,33 +435,221 @@ fn handle_led_mode(
println!("{}", mode.self_usage()); println!("{}", mode.self_usage());
return Ok(()); return Ok(());
} }
match mode { dbus.proxies()
SetAuraBuiltin::MultiStatic(_) | SetAuraBuiltin::MultiBreathe(_) => { .led()
let zones = <Vec<AuraEffect>>::from(mode); .set_led_mode(&<AuraEffect>::from(mode))?;
for eff in zones { }
dbus.proxies().led().set_led_mode(&eff)?
Ok(())
}
fn handle_led_power1(
dbus: &RogDbusClientBlocking,
supported: &LedSupportedFunctions,
power: &LedPowerCommand1,
) -> Result<(), Box<dyn std::error::Error>> {
if power.awake.is_none()
&& power.sleep.is_none()
&& power.boot.is_none()
&& power.keyboard.is_none()
&& power.lightbar.is_none()
{
if !power.help {
println!("Missing arg or command\n");
}
println!("{}\n", power.self_usage());
return Ok(());
}
if matches!(
supported.prod_id,
AuraDevice::X1854 | AuraDevice::X1869 | AuraDevice::X1866
) {
handle_led_power_1_do_1866(dbus, power)?;
return Ok(());
}
if matches!(supported.prod_id, AuraDevice::Tuf) {
handle_led_power_1_do_tuf(dbus, power)?;
return Ok(());
}
println!("These options are for keyboards of product ID 0x1866 or TUF only");
return Ok(());
}
fn handle_led_power_1_do_1866(
dbus: &RogDbusClientBlocking,
power: &LedPowerCommand1,
) -> Result<(), Box<dyn std::error::Error>> {
let mut enabled: Vec<AuraDev1866> = Vec::new();
let mut disabled: Vec<AuraDev1866> = Vec::new();
let mut check = |e: Option<bool>, a: AuraDev1866| {
if let Some(arg) = e {
if arg {
enabled.push(a);
} else {
disabled.push(a);
}
}
};
check(power.awake, AuraDev1866::Awake);
check(power.boot, AuraDev1866::Boot);
check(power.sleep, AuraDev1866::Sleep);
check(power.keyboard, AuraDev1866::Keyboard);
check(power.lightbar, AuraDev1866::Lightbar);
let data = AuraPowerDev {
x1866: enabled,
x19b6: vec![],
tuf: vec![],
};
dbus.proxies().led().set_leds_power(data, true)?;
let data = AuraPowerDev {
x1866: disabled,
x19b6: vec![],
tuf: vec![],
};
dbus.proxies().led().set_leds_power(data, false)?;
Ok(())
}
fn handle_led_power_1_do_tuf(
dbus: &RogDbusClientBlocking,
power: &LedPowerCommand1,
) -> Result<(), Box<dyn std::error::Error>> {
let mut enabled: Vec<AuraDevTuf> = Vec::new();
let mut disabled: Vec<AuraDevTuf> = Vec::new();
let mut check = |e: Option<bool>, a: AuraDevTuf| {
if let Some(arg) = e {
if arg {
enabled.push(a);
} else {
disabled.push(a);
}
}
};
check(power.awake, AuraDevTuf::Awake);
check(power.boot, AuraDevTuf::Boot);
check(power.sleep, AuraDevTuf::Sleep);
check(power.keyboard, AuraDevTuf::Keyboard);
let data = AuraPowerDev {
x1866: vec![],
x19b6: vec![],
tuf: enabled,
};
dbg!(&data);
dbus.proxies().led().set_leds_power(data, true)?;
let data = AuraPowerDev {
x1866: vec![],
x19b6: vec![],
tuf: disabled,
};
dbus.proxies().led().set_leds_power(data, false)?;
Ok(())
}
fn handle_led_power2(
dbus: &RogDbusClientBlocking,
supported: &LedSupportedFunctions,
power: &LedPowerCommand2,
) -> Result<(), Box<dyn std::error::Error>> {
if power.command().is_none() {
if !power.help {
println!("Missing arg or command\n");
}
println!("{}\n", power.self_usage());
println!("Commands available");
if let Some(cmdlist) = LedPowerCommand2::command_list() {
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_string()).collect();
for command in commands.iter() {
println!("{}", command);
}
}
println!("\nHelp can also be requested on commands, e.g: boot --help");
return Ok(());
}
if let Some(pow) = power.command.as_ref() {
if pow.help_requested() {
println!("{}", pow.self_usage());
return Ok(());
}
if supported.prod_id != AuraDevice::X19B6 {
println!("This option applies only to keyboards with product ID 0x19b6")
}
let mut enabled: Vec<AuraDev19b6> = Vec::new();
let mut disabled: Vec<AuraDev19b6> = Vec::new();
let mut check = |e: Option<bool>, a: AuraDev19b6| {
if let Some(arg) = e {
if arg {
enabled.push(a);
} else {
disabled.push(a);
} }
} }
_ => dbus };
.proxies()
.led() match pow {
.set_led_mode(&<AuraEffect>::from(mode))?, aura_cli::SetAuraEnabled::Boot(arg) => {
check(arg.keyboard, AuraDev19b6::BootKeyb);
check(arg.logo, AuraDev19b6::BootLogo);
check(arg.lightbar, AuraDev19b6::BootBar);
}
aura_cli::SetAuraEnabled::Sleep(arg) => {
check(arg.keyboard, AuraDev19b6::SleepKeyb);
check(arg.logo, AuraDev19b6::SleepLogo);
check(arg.lightbar, AuraDev19b6::SleepBar);
}
aura_cli::SetAuraEnabled::Awake(arg) => {
check(arg.keyboard, AuraDev19b6::AwakeKeyb);
check(arg.logo, AuraDev19b6::AwakeLogo);
check(arg.lightbar, AuraDev19b6::AwakeBar);
}
aura_cli::SetAuraEnabled::Shutdown(arg) => {
check(arg.keyboard, AuraDev19b6::ShutdownKeyb);
check(arg.logo, AuraDev19b6::ShutdownLogo);
check(arg.lightbar, AuraDev19b6::ShutdownBar);
}
} }
}
if let Some(enable) = mode.awake_enable { if !enabled.is_empty() {
dbus.proxies().led().set_awake_enabled(enable)?; let data = AuraPowerDev {
} tuf: vec![],
x1866: vec![],
x19b6: enabled,
};
dbus.proxies().led().set_leds_power(data, true)?;
}
if let Some(enable) = mode.sleep_enable { if !disabled.is_empty() {
dbus.proxies().led().set_sleep_enabled(enable)?; let data = AuraPowerDev {
tuf: vec![],
x1866: vec![],
x19b6: disabled,
};
dbus.proxies().led().set_leds_power(data, false)?;
}
} }
Ok(()) Ok(())
} }
fn handle_profile( fn handle_profile(
dbus: &RogDbusClient, dbus: &RogDbusClientBlocking,
supported: &PlatformProfileFunctions, supported: &PlatformProfileFunctions,
cmd: &ProfileCommand, cmd: &ProfileCommand,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
@@ -452,15 +690,13 @@ fn handle_profile(
} }
fn handle_fan_curve( fn handle_fan_curve(
dbus: &RogDbusClient, dbus: &RogDbusClientBlocking,
supported: &PlatformProfileFunctions, supported: &PlatformProfileFunctions,
cmd: &FanCurveCommand, cmd: &FanCurveCommand,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
if !supported.fan_curves { if !supported.fan_curves {
println!("Fan-curves not supported by either this kernel or by the laptop."); println!("Fan-curves not supported by either this kernel or by the laptop.");
println!( println!("This requires kernel 5.17 or the fan curve patch listed in the readme.");
"This requires kernel 5.17 (unlreleased) or the fan curve patch listed in the readme."
);
return Err(ProfileError::NotSupported.into()); return Err(ProfileError::NotSupported.into());
} }
@@ -508,7 +744,7 @@ fn handle_fan_curve(
if let Some(mut curve) = cmd.data.clone() { if let Some(mut curve) = cmd.data.clone() {
let fan = cmd.fan.unwrap_or_default(); let fan = cmd.fan.unwrap_or_default();
curve.set_fan(fan); curve.set_fan(fan);
dbus.proxies().profile().set_fan_curve(curve, profile)?; dbus.proxies().profile().set_fan_curve(profile, curve)?;
} }
} }
@@ -516,7 +752,7 @@ fn handle_fan_curve(
} }
fn handle_bios_option( fn handle_bios_option(
dbus: &RogDbusClient, dbus: &RogDbusClientBlocking,
supported: &RogBiosSupportedFunctions, supported: &RogBiosSupportedFunctions,
cmd: &BiosCommand, cmd: &BiosCommand,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
@@ -524,7 +760,9 @@ fn handle_bios_option(
if (cmd.dedicated_gfx_set.is_none() if (cmd.dedicated_gfx_set.is_none()
&& !cmd.dedicated_gfx_get && !cmd.dedicated_gfx_get
&& cmd.post_sound_set.is_none() && cmd.post_sound_set.is_none()
&& !cmd.post_sound_get) && !cmd.post_sound_get
&& cmd.panel_overdrive_set.is_none()
&& !cmd.panel_overdrive_get)
|| cmd.help || cmd.help
{ {
println!("Missing arg or command\n"); println!("Missing arg or command\n");
@@ -535,23 +773,25 @@ fn handle_bios_option(
.collect(); .collect();
for line in usage.iter().filter(|line| { for line in usage.iter().filter(|line| {
line.contains("sound") && supported.post_sound_toggle line.contains("sound") && supported.post_sound
|| line.contains("GPU") && supported.dedicated_gfx_toggle || line.contains("GPU") && supported.dedicated_gfx
|| line.contains("panel") && supported.panel_overdrive
}) { }) {
println!("{}", line); println!("{}", line);
} }
} }
if let Some(opt) = cmd.post_sound_set { if let Some(opt) = cmd.post_sound_set {
dbus.proxies().rog_bios().set_post_sound(opt)?; dbus.proxies().rog_bios().set_post_boot_sound(opt)?;
} }
if cmd.post_sound_get { if cmd.post_sound_get {
let res = dbus.proxies().rog_bios().get_post_sound()? == 1; let res = dbus.proxies().rog_bios().post_boot_sound()? == 1;
println!("Bios POST sound on: {}", res); println!("Bios POST sound on: {}", res);
} }
if let Some(opt) = cmd.dedicated_gfx_set { if let Some(opt) = cmd.dedicated_gfx_set {
println!("Rebuilding initrd to include drivers"); println!("Rebuilding initrd to include drivers");
dbus.proxies().rog_bios().set_dedicated_gfx(opt)?; dbus.proxies().rog_bios().set_dedicated_graphic_mode(opt)?;
println!("The mode change is not active until you reboot, on boot the bios will make the required change"); println!("The mode change is not active until you reboot, on boot the bios will make the required change");
if opt { if opt {
println!( println!(
@@ -562,9 +802,17 @@ fn handle_bios_option(
} }
} }
if cmd.dedicated_gfx_get { if cmd.dedicated_gfx_get {
let res = dbus.proxies().rog_bios().get_dedicated_gfx()? == 1; let res = dbus.proxies().rog_bios().dedicated_graphic_mode()? == 1;
println!("Bios dedicated GPU on: {}", res); println!("Bios dedicated GPU on: {}", res);
} }
if let Some(opt) = cmd.panel_overdrive_set {
dbus.proxies().rog_bios().set_panel_overdrive(opt)?;
}
if cmd.panel_overdrive_get {
let res = dbus.proxies().rog_bios().panel_overdrive()? == 1;
println!("Panel overdrive on: {}", res);
}
} }
Ok(()) Ok(())
} }
+7 -5
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "daemon-user" name = "daemon-user"
version = "1.2.0" version = "1.3.0"
authors = ["Luke D Jones <luke@ljones.dev>"] authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2018" edition = "2018"
description = "Usermode daemon for user settings, anime, per-key lighting" description = "Usermode daemon for user settings, anime, per-key lighting"
@@ -23,8 +23,10 @@ rog_anime = { path = "../rog-anime" }
rog_dbus = { path = "../rog-dbus" } rog_dbus = { path = "../rog-dbus" }
rog_supported = { path = "../rog-supported" } rog_supported = { path = "../rog-supported" }
dirs = "3.0.1" dirs = "^4.0"
zbus = "^1.9.1" zbus = "^2.2"
zvariant = "^2.6" zvariant = "^3.0"
zvariant_derive = "^2.6" zvariant_derive = "^3.0"
smol = "^1.2"
+15 -9
View File
@@ -1,6 +1,6 @@
use rog_anime::error::AnimeError; use rog_anime::error::AnimeError;
use rog_anime::{ActionData, ActionLoader, AnimTime, Fade, Sequences, Vec2}; use rog_anime::{ActionData, ActionLoader, AnimTime, Fade, Sequences, Vec2};
use rog_dbus::RogDbusClient; use rog_dbus::RogDbusClientBlocking;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::time::Duration; use std::time::Duration;
use std::{ use std::{
@@ -66,14 +66,14 @@ pub enum TimeType {
/// and a zbus server behind `Arc<Mutex<T>>` /// and a zbus server behind `Arc<Mutex<T>>`
pub struct CtrlAnimeInner<'a> { pub struct CtrlAnimeInner<'a> {
sequences: Sequences, sequences: Sequences,
client: RogDbusClient<'a>, client: RogDbusClientBlocking<'a>,
do_early_return: Arc<AtomicBool>, do_early_return: Arc<AtomicBool>,
} }
impl<'a> CtrlAnimeInner<'static> { impl<'a> CtrlAnimeInner<'static> {
pub fn new( pub fn new(
sequences: Sequences, sequences: Sequences,
client: RogDbusClient<'static>, client: RogDbusClientBlocking<'static>,
do_early_return: Arc<AtomicBool>, do_early_return: Arc<AtomicBool>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
Ok(Self { Ok(Self {
@@ -91,12 +91,16 @@ impl<'a> CtrlAnimeInner<'static> {
for action in self.sequences.iter() { for action in self.sequences.iter() {
match action { match action {
ActionData::Animation(frames) => { ActionData::Animation(frames) => {
rog_anime::run_animation(frames, self.do_early_return.clone(), &|output| { rog_anime::run_animation(frames, &|output| {
if self.do_early_return.load(Ordering::Acquire) {
return Ok(true); // Do safe exit
}
self.client self.client
.proxies() .proxies()
.anime() .anime()
.write(output) .write(output)
.map_err(|e| AnimeError::Dbus(format!("{}", e))) .map_err(|e| AnimeError::Dbus(format!("{}", e)))
.map(|_| false)
})?; })?;
} }
ActionData::Image(image) => { ActionData::Image(image) => {
@@ -131,7 +135,7 @@ impl<'a> CtrlAnimeInner<'static> {
pub struct CtrlAnime<'a> { pub struct CtrlAnime<'a> {
config: Arc<Mutex<UserAnimeConfig>>, config: Arc<Mutex<UserAnimeConfig>>,
client: RogDbusClient<'a>, client: RogDbusClientBlocking<'a>,
inner: Arc<Mutex<CtrlAnimeInner<'a>>>, inner: Arc<Mutex<CtrlAnimeInner<'a>>>,
/// Must be the same Atomic as in CtrlAnimeInner /// Must be the same Atomic as in CtrlAnimeInner
inner_early_return: Arc<AtomicBool>, inner_early_return: Arc<AtomicBool>,
@@ -141,7 +145,7 @@ impl<'a> CtrlAnime<'static> {
pub fn new( pub fn new(
config: Arc<Mutex<UserAnimeConfig>>, config: Arc<Mutex<UserAnimeConfig>>,
inner: Arc<Mutex<CtrlAnimeInner<'static>>>, inner: Arc<Mutex<CtrlAnimeInner<'static>>>,
client: RogDbusClient<'static>, client: RogDbusClientBlocking<'static>,
inner_early_return: Arc<AtomicBool>, inner_early_return: Arc<AtomicBool>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
Ok(CtrlAnime { Ok(CtrlAnime {
@@ -152,12 +156,14 @@ impl<'a> CtrlAnime<'static> {
}) })
} }
pub fn add_to_server(self, server: &mut zbus::ObjectServer) { pub async fn add_to_server(self, server: &mut zbus::Connection) {
server server
.object_server()
.at( .at(
&ObjectPath::from_str_unchecked("/org/asuslinux/Anime"), &ObjectPath::from_str_unchecked("/org/asuslinux/Anime"),
self, self,
) )
.await
.map_err(|err| { .map_err(|err| {
println!("CtrlAnime: add_to_server {}", err); println!("CtrlAnime: add_to_server {}", err);
err err
@@ -353,13 +359,13 @@ impl CtrlAnime<'static> {
pub fn set_state(&mut self, on: bool) -> zbus::fdo::Result<()> { pub fn set_state(&mut self, on: bool) -> zbus::fdo::Result<()> {
// Operations here need to be in specific order // Operations here need to be in specific order
if on { if on {
self.client.proxies().anime().set_on_off(on)?; self.client.proxies().anime().set_on_off(on).ok();
// Let the inner loop run // Let the inner loop run
self.inner_early_return.store(false, Ordering::SeqCst); self.inner_early_return.store(false, Ordering::SeqCst);
} else { } else {
// Must make the inner run loop return early // Must make the inner run loop return early
self.inner_early_return.store(true, Ordering::SeqCst); self.inner_early_return.store(true, Ordering::SeqCst);
self.client.proxies().anime().set_on_off(on)?; self.client.proxies().anime().set_on_off(on).ok();
} }
Ok(()) Ok(())
} }
+42 -37
View File
@@ -1,13 +1,14 @@
use rog_dbus::RogDbusClient; use rog_anime::usb::get_anime_type;
use rog_dbus::RogDbusClientBlocking;
use rog_user::{ use rog_user::{
ctrl_anime::{CtrlAnime, CtrlAnimeInner}, ctrl_anime::{CtrlAnime, CtrlAnimeInner},
user_config::*, user_config::*,
DBUS_NAME, DBUS_NAME,
}; };
use smol::Executor;
use std::sync::Arc; use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
use std::thread; use zbus::Connection;
use zbus::{fdo, Connection};
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
@@ -17,51 +18,55 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!(" rog-dbus v{}", rog_dbus::VERSION); println!(" rog-dbus v{}", rog_dbus::VERSION);
println!("rog-supported v{}", rog_supported::VERSION); println!("rog-supported v{}", rog_supported::VERSION);
let (client, _) = RogDbusClient::new()?; let (client, _) = RogDbusClientBlocking::new()?;
let supported = client.proxies().supported().get_supported_functions()?; let supported = client.proxies().supported().supported_functions()?;
let mut config = UserConfig::new(); let mut config = UserConfig::new();
config.load_config()?; config.load_config()?;
let anime_config = UserAnimeConfig::load_config(config.active_anime)?; let executor = Executor::new();
let anime = anime_config.create_anime()?;
let anime_config = Arc::new(Mutex::new(anime_config));
// Create server
let connection = Connection::new_session()?;
fdo::DBusProxy::new(&connection)?
.request_name(DBUS_NAME, fdo::RequestNameFlags::ReplaceExisting.into())?;
let mut server = zbus::ObjectServer::new(&connection);
let early_return = Arc::new(AtomicBool::new(false));
// Set up the anime data and run loop/thread // Set up the anime data and run loop/thread
if supported.anime_ctrl.0 { if supported.anime_ctrl.0 {
let early_return = Arc::new(AtomicBool::new(false)); let anime_type = get_anime_type()?;
// Inner behind mutex required for thread safety let anime_config = UserAnimeConfig::load_config(config.active_anime)?;
let inner = Arc::new(Mutex::new(CtrlAnimeInner::new( let anime = anime_config.create_anime(anime_type)?;
anime, let anime_config = Arc::new(Mutex::new(anime_config));
client,
early_return.clone(), executor
)?)); .spawn(async move {
// Need new client object for dbus control part // Create server
let (client, _) = RogDbusClient::new()?; let mut connection = Connection::session().await.unwrap();
let anime_control = CtrlAnime::new(anime_config, inner.clone(), client, early_return)?; connection.request_name(DBUS_NAME).await.unwrap();
anime_control.add_to_server(&mut server);
// Thread using inner // Inner behind mutex required for thread safety
let _anime_thread = thread::Builder::new() let inner = Arc::new(Mutex::new(
.name("Anime User".into()) CtrlAnimeInner::new(anime, client, early_return.clone()).unwrap(),
.spawn(move || loop { ));
if let Ok(inner) = inner.try_lock() { // Need new client object for dbus control part
inner.run().ok(); let (client, _) = RogDbusClientBlocking::new().unwrap();
let anime_control =
CtrlAnime::new(anime_config, inner.clone(), client, early_return).unwrap();
anime_control.add_to_server(&mut connection).await;
loop {
if let Ok(inner) = inner.clone().try_lock() {
inner.run().ok();
}
} }
})?; })
.detach();
} }
if supported.keyboard_led.per_key_led_mode {} // if supported.keyboard_led.per_key_led_mode {
// executor
// .spawn(async move {
// //
// })
// .detach();
// }
loop { loop {
if let Err(err) = server.try_handle_next() { smol::block_on(executor.tick());
println!("{}", err);
}
} }
} }
+3 -3
View File
@@ -4,7 +4,7 @@ use std::{
time::Duration, time::Duration,
}; };
use rog_anime::{ActionLoader, AnimTime, Fade, Sequences, Vec2}; use rog_anime::{ActionLoader, AnimTime, AnimeType, Fade, Sequences, Vec2};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use crate::error::Error; use crate::error::Error;
@@ -16,8 +16,8 @@ pub struct UserAnimeConfig {
} }
impl UserAnimeConfig { impl UserAnimeConfig {
pub fn create_anime(&self) -> Result<Sequences, Error> { pub fn create_anime(&self, anime_type: AnimeType) -> Result<Sequences, Error> {
let mut seq = Sequences::new(); let mut seq = Sequences::new(anime_type);
for (idx, action) in self.anime.iter().enumerate() { for (idx, action) in self.anime.iter().enumerate() {
seq.insert(idx, action)?; seq.insert(idx, action)?;
+12 -9
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "daemon" name = "daemon"
version = "4.0.7" version = "4.3.3"
license = "MPL-2.0" license = "MPL-2.0"
readme = "README.md" readme = "README.md"
authors = ["Luke <luke@ljones.dev>"] authors = ["Luke <luke@ljones.dev>"]
@@ -18,22 +18,25 @@ name = "asusd"
path = "src/daemon.rs" path = "src/daemon.rs"
[dependencies] [dependencies]
rog_anime = { path = "../rog-anime" } rog_anime = { path = "../rog-anime", features = ["dbus"] }
rog_aura = { path = "../rog-aura" } rog_aura = { path = "../rog-aura", features = ["dbus"] }
rog_supported = { path = "../rog-supported" } rog_supported = { path = "../rog-supported" }
rog_profiles = { path = "../rog-profiles" } rog_profiles = { path = "../rog-profiles" }
rog_dbus = { path = "../rog-dbus" } rog_dbus = { path = "../rog-dbus" }
rusb = "^0.8"
async-trait = "^0.1"
smol = "^1.2"
rusb = "^0.9"
udev = "^0.6" udev = "^0.6"
# cli and logging # cli and logging
log = "^0.4" log = "^0.4"
env_logger = "^0.8" env_logger = "^0.9"
zbus = "^1.9.1" zbus = "^2.2"
zvariant = "^2.6" zvariant = "^3.2"
zvariant_derive = { version = "^2.6" } logind-zbus = { version = "^3.0" } #, default-features = false, features = ["non_blocking"] }
logind-zbus = "^0.7.1"
# serialisation # serialisation
serde = "^1.0" serde = "^1.0"
+4 -3
View File
@@ -2,10 +2,11 @@ use log::{error, warn};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::path::PathBuf;
pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf"; pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize, Default)]
pub struct Config { pub struct Config {
/// Save charge limit for restoring on boot /// Save charge limit for restoring on boot
pub bat_charge_limit: u8, pub bat_charge_limit: u8,
@@ -24,8 +25,8 @@ impl Config {
.read(true) .read(true)
.write(true) .write(true)
.create(true) .create(true)
.open(&CONFIG_PATH) .open(&PathBuf::from(CONFIG_PATH))
.unwrap_or_else(|_| panic!("The directory /etc/asusd/ is missing")); // okay to cause panic here .unwrap_or_else(|e| panic!("Error opening {}, {}", CONFIG_PATH, e)); // okay to cause panic here
let mut buf = String::new(); let mut buf = String::new();
let config; let config;
if let Ok(read_len) = file.read_to_string(&mut buf) { if let Ok(read_len) = file.read_to_string(&mut buf) {
+26 -9
View File
@@ -1,7 +1,7 @@
use crate::VERSION; use crate::VERSION;
use log::{error, info, warn}; use log::{error, info, warn};
use rog_anime::Fade;
use rog_anime::{error::AnimeError, ActionData, ActionLoader, AnimTime, Vec2}; use rog_anime::{error::AnimeError, ActionData, ActionLoader, AnimTime, Vec2};
use rog_anime::{AnimeType, Fade};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::io::{Read, Write}; use std::io::{Read, Write};
@@ -80,28 +80,32 @@ pub struct AnimeConfigCached {
} }
impl AnimeConfigCached { impl AnimeConfigCached {
pub fn init_from_config(&mut self, config: &AnimeConfig) -> Result<(), AnimeError> { pub fn init_from_config(
&mut self,
config: &AnimeConfig,
anime_type: AnimeType,
) -> Result<(), AnimeError> {
let mut sys = Vec::with_capacity(config.system.len()); let mut sys = Vec::with_capacity(config.system.len());
for ani in config.system.iter() { for ani in config.system.iter() {
sys.push(ActionData::from_anime_action(ani)?); sys.push(ActionData::from_anime_action(anime_type, ani)?);
} }
self.system = sys; self.system = sys;
let mut boot = Vec::with_capacity(config.boot.len()); let mut boot = Vec::with_capacity(config.boot.len());
for ani in config.boot.iter() { for ani in config.boot.iter() {
boot.push(ActionData::from_anime_action(ani)?); boot.push(ActionData::from_anime_action(anime_type, ani)?);
} }
self.boot = boot; self.boot = boot;
let mut wake = Vec::with_capacity(config.wake.len()); let mut wake = Vec::with_capacity(config.wake.len());
for ani in config.wake.iter() { for ani in config.wake.iter() {
wake.push(ActionData::from_anime_action(ani)?); wake.push(ActionData::from_anime_action(anime_type, ani)?);
} }
self.wake = wake; self.wake = wake;
let mut shutdown = Vec::with_capacity(config.shutdown.len()); let mut shutdown = Vec::with_capacity(config.shutdown.len());
for ani in config.shutdown.iter() { for ani in config.shutdown.iter() {
shutdown.push(ActionData::from_anime_action(ani)?); shutdown.push(ActionData::from_anime_action(anime_type, ani)?);
} }
self.shutdown = shutdown; self.shutdown = shutdown;
Ok(()) Ok(())
@@ -153,17 +157,20 @@ impl AnimeConfig {
if read_len == 0 { if read_len == 0 {
return AnimeConfig::create_default(&mut file); return AnimeConfig::create_default(&mut file);
} else { } else {
if let Ok(data) = serde_json::from_str(&buf) { if let Ok(mut data) = serde_json::from_str(&buf) {
Self::clamp_config_brightness(&mut data);
return data; return data;
} else if let Ok(data) = serde_json::from_str::<AnimeConfigV341>(&buf) { } else if let Ok(data) = serde_json::from_str::<AnimeConfigV341>(&buf) {
let config = data.into_current(); let mut config = data.into_current();
config.write(); config.write();
info!("Updated config version to: {}", VERSION); info!("Updated config version to: {}", VERSION);
Self::clamp_config_brightness(&mut config);
return config; return config;
} else if let Ok(data) = serde_json::from_str::<AnimeConfigV352>(&buf) { } else if let Ok(data) = serde_json::from_str::<AnimeConfigV352>(&buf) {
let config = data.into_current(); let mut config = data.into_current();
config.write(); config.write();
info!("Updated config version to: {}", VERSION); info!("Updated config version to: {}", VERSION);
Self::clamp_config_brightness(&mut config);
return config; return config;
} }
warn!( warn!(
@@ -182,6 +189,16 @@ impl AnimeConfig {
AnimeConfig::create_default(&mut file) AnimeConfig::create_default(&mut file)
} }
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(file: &mut File) -> Self { fn create_default(file: &mut File) -> Self {
// create a default config here // create a default config here
let config = AnimeConfig { let config = AnimeConfig {
+143 -152
View File
@@ -2,22 +2,25 @@ pub mod config;
pub mod zbus; pub mod zbus;
use ::zbus::Connection; use ::zbus::Connection;
use async_trait::async_trait;
use log::{error, info, warn}; use log::{error, info, warn};
use logind_zbus::ManagerProxy; use logind_zbus::manager::ManagerProxy;
use rog_anime::{ use rog_anime::{
error::AnimeError, error::AnimeError,
usb::{ usb::{
pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on, pkts_for_init, PROD_ID, find_node, get_anime_type, pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on,
VENDOR_ID, pkts_for_init, PROD_ID, VENDOR_ID,
}, },
ActionData, AnimeDataBuffer, AnimePacketType, ANIME_DATA_LEN, ActionData, AnimeDataBuffer, AnimePacketType, AnimeType,
}; };
use rog_supported::AnimeSupportedFunctions; use rog_supported::AnimeSupportedFunctions;
use rusb::{Device, DeviceHandle}; use rusb::{Device, DeviceHandle};
use smol::{stream::StreamExt, Executor};
use std::{ use std::{
cell::RefCell, cell::RefCell,
convert::TryFrom,
error::Error, error::Error,
sync::{Arc, Mutex}, sync::{Arc, Mutex, MutexGuard},
thread::sleep, thread::sleep,
}; };
use std::{ use std::{
@@ -39,6 +42,7 @@ impl GetSupported for CtrlAnime {
pub struct CtrlAnime { pub struct CtrlAnime {
_node: String, _node: String,
anime_type: AnimeType,
handle: RefCell<DeviceHandle<rusb::GlobalContext>>, handle: RefCell<DeviceHandle<rusb::GlobalContext>>,
cache: AnimeConfigCached, cache: AnimeConfigCached,
config: AnimeConfig, config: AnimeConfig,
@@ -51,15 +55,17 @@ pub struct CtrlAnime {
impl CtrlAnime { impl CtrlAnime {
#[inline] #[inline]
pub fn new(config: AnimeConfig) -> Result<CtrlAnime, Box<dyn Error>> { pub fn new(config: AnimeConfig) -> Result<CtrlAnime, Box<dyn Error>> {
let node = Self::find_node("193b")?; let node = find_node("193b")?;
let anime_type = get_anime_type()?;
let device = Self::get_dev_handle()?; let device = Self::get_dev_handle()?;
info!("Device has an AniMe Matrix display"); info!("Device has an AniMe Matrix display");
let mut cache = AnimeConfigCached::default(); let mut cache = AnimeConfigCached::default();
cache.init_from_config(&config)?; cache.init_from_config(&config, anime_type)?;
let ctrl = CtrlAnime { let ctrl = CtrlAnime {
_node: node, _node: node,
anime_type,
handle: RefCell::new(device), handle: RefCell::new(device),
cache, cache,
config, config,
@@ -71,34 +77,6 @@ impl CtrlAnime {
Ok(ctrl) Ok(ctrl)
} }
fn find_node(id_product: &str) -> Result<String, RogError> {
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
RogError::Udev("enumerator failed".into(), err)
})?;
enumerator.match_subsystem("usb").map_err(|err| {
warn!("{}", err);
RogError::Udev("match_subsystem failed".into(), err)
})?;
for device in enumerator.scan_devices().map_err(|err| {
warn!("{}", err);
RogError::Udev("scan_devices failed".into(), err)
})? {
if let Some(attr) = device.attribute_value("idProduct") {
if attr == id_product {
if let Some(dev_node) = device.devnode() {
info!("Using device at: {:?} for AniMe control", dev_node);
return Ok(dev_node.to_string_lossy().to_string());
}
}
}
}
Err(RogError::MissingFunction(
"ASUS AniMe device node not found".into(),
))
}
fn get_dev_handle() -> Result<DeviceHandle<rusb::GlobalContext>, Box<dyn Error>> { fn get_dev_handle() -> Result<DeviceHandle<rusb::GlobalContext>, Box<dyn Error>> {
// We don't expect this ID to ever change // We don't expect this ID to ever change
let device = CtrlAnime::get_device(0x0b05, 0x193b)?; let device = CtrlAnime::get_device(0x0b05, 0x193b)?;
@@ -139,6 +117,7 @@ impl CtrlAnime {
warn!("AniMe system actions was empty"); warn!("AniMe system actions was empty");
return; return;
} }
// Loop rules: // Loop rules:
// - Lock the mutex **only when required**. That is, the lock must be held for the shortest duration possible. // - 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 // - An AtomicBool used for thread exit should be checked in every loop, including nested
@@ -148,65 +127,72 @@ impl CtrlAnime {
std::thread::Builder::new() std::thread::Builder::new()
.name("AniMe system thread start".into()) .name("AniMe system thread start".into())
.spawn(move || { .spawn(move || {
info!("AniMe system thread started"); info!("AniMe new system thread started");
// Getting copies of these Atomics is done *in* the thread to ensure // Getting copies of these Atomics is done *in* the thread to ensure
// we don't block other threads/main // we don't block other threads/main
let thread_exit; let thread_exit;
let thread_running; let thread_running;
// First two loops are to ensure we *do* aquire a lock on the mutex let anime_type;
// 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.
loop { loop {
if let Ok(lock) = inner.try_lock() { if let Ok(lock) = inner.try_lock() {
thread_exit = lock.thread_exit.clone(); thread_exit = lock.thread_exit.clone();
thread_running = lock.thread_running.clone(); thread_running = lock.thread_running.clone();
// Make any running loop exit first anime_type = lock.anime_type;
thread_exit.store(true, Ordering::SeqCst);
break; 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);
}
loop { info!("AniMe no previous system thread running (now)");
// wait for other threads to set not running so we know they exited thread_exit.store(false, Ordering::SeqCst);
if !thread_running.load(Ordering::SeqCst) {
thread_exit.store(false, Ordering::SeqCst);
info!("AniMe forced a thread to exit");
break;
}
}
'main: loop { 'main: loop {
if thread_exit.load(Ordering::SeqCst) { thread_running.store(true, Ordering::SeqCst);
break 'main;
}
for action in actions.iter() { for action in actions.iter() {
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
match action { match action {
ActionData::Animation(frames) => { ActionData::Animation(frames) => {
if let Err(err) = rog_anime::run_animation( if let Err(err) = rog_anime::run_animation(frames, &|frame| {
frames, if thread_exit.load(Ordering::Acquire) {
thread_exit.clone(), info!("rog-anime: frame-loop was asked to exit");
&|frame| { return Ok(true); // Do safe exit
inner }
.try_lock() inner
.map(|lock| lock.write_data_buffer(frame)) .try_lock()
.map_err(|err| { .map(|lock| {
warn!("rog_anime::run_animation: {}", err); lock.write_data_buffer(frame)
AnimeError::NoFrames .map_err(|err| {
}) warn!(
}, "rog_anime::run_animation:callback {}",
) { err
warn!("rog_anime::run_animation: {}", err); );
})
.ok();
false // Don't exit yet
})
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
AnimeError::NoFrames
})
}) {
warn!("rog_anime::run_animation:Animation {}", err);
break 'main; break 'main;
}; };
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
} }
ActionData::Image(image) => { ActionData::Image(image) => {
once = false; once = false;
if let Ok(lock) = inner.try_lock() { if let Ok(lock) = inner.try_lock() {
lock.write_data_buffer(image.as_ref().clone()) lock.write_data_buffer(image.as_ref().clone())
.map_err(|e| error!("{}", e))
.ok();
} }
} }
ActionData::Pause(duration) => sleep(*duration), ActionData::Pause(duration) => sleep(*duration),
@@ -216,17 +202,27 @@ impl CtrlAnime {
ActionData::Matrix => {} ActionData::Matrix => {}
} }
} }
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
if once || actions.is_empty() { if once || actions.is_empty() {
break 'main; break 'main;
} }
} }
// Clear the display on exit // Clear the display on exit
if let Ok(lock) = inner.try_lock() { if let Ok(lock) = inner.try_lock() {
let data = AnimeDataBuffer::from_vec([0u8; ANIME_DATA_LEN].to_vec()); if let Ok(data) =
lock.write_data_buffer(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();
}
} }
// Loop ended, set the atmonics // Loop ended, set the atmonics
thread_exit.store(false, Ordering::SeqCst);
thread_running.store(false, Ordering::SeqCst); thread_running.store(false, Ordering::SeqCst);
info!("AniMe system thread exited"); info!("AniMe system thread exited");
}) })
@@ -275,19 +271,20 @@ impl CtrlAnime {
/// Write only a data packet. This will modify the leds brightness using the /// Write only a data packet. This will modify the leds brightness using the
/// global brightness set in config. /// global brightness set in config.
fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) { fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) -> Result<(), RogError> {
for led in buffer.get_mut()[7..].iter_mut() { for led in buffer.data_mut().iter_mut() {
let mut bright = *led as f32 * self.config.brightness; let mut bright = *led as f32 * self.config.brightness;
if bright > 254.0 { if bright > 254.0 {
bright = 254.0; bright = 254.0;
} }
*led = bright as u8; *led = bright as u8;
} }
let data = AnimePacketType::from(buffer); let data = AnimePacketType::try_from(buffer)?;
for row in data.iter() { for row in data.iter() {
self.write_bytes(row); self.write_bytes(row);
} }
self.write_bytes(&pkt_for_flush()); self.write_bytes(&pkt_for_flush());
Ok(())
} }
fn do_initialization(&self) { fn do_initialization(&self) {
@@ -297,89 +294,83 @@ impl CtrlAnime {
} }
} }
pub struct CtrlAnimeTask<'a> { pub struct CtrlAnimeTask {
inner: Arc<Mutex<CtrlAnime>>, inner: Arc<Mutex<CtrlAnime>>,
_c: Connection,
manager: ManagerProxy<'a>,
} }
impl<'a> CtrlAnimeTask<'a> { impl CtrlAnimeTask {
pub fn new(inner: Arc<Mutex<CtrlAnime>>) -> Self { pub async fn new(inner: Arc<Mutex<CtrlAnime>>) -> CtrlAnimeTask {
let connection = Self { inner }
Connection::new_system().expect("CtrlAnimeTask could not create dbus connection");
let manager =
ManagerProxy::new(&connection).expect("CtrlAnimeTask could not create ManagerProxy");
let c1 = inner.clone();
// Run this action when the system starts shutting down
manager
.connect_prepare_for_shutdown(move |shutdown| {
if shutdown {
'outer: loop {
if let Ok(lock) = c1.try_lock() {
lock.thread_exit.store(true, Ordering::SeqCst);
CtrlAnime::run_thread(c1.clone(), lock.cache.shutdown.clone(), false);
break 'outer;
}
}
}
Ok(())
})
.map_err(|err| {
warn!("CtrlAnimeTask: new() {}", err);
err
})
.ok();
let c1 = inner.clone();
// Run this action when the system wakes up from sleep
manager
.connect_prepare_for_sleep(move |sleep| {
if !sleep {
// wait a fraction for things to wake up properly
std::thread::sleep(Duration::from_millis(100));
'outer: loop {
if let Ok(lock) = c1.try_lock() {
lock.thread_exit.store(true, Ordering::SeqCst);
CtrlAnime::run_thread(c1.clone(), lock.cache.wake.clone(), true);
break 'outer;
}
}
}
Ok(())
})
.map_err(|err| {
warn!("CtrlAnimeTask: new() {}", err);
err
})
.ok();
Self {
inner,
_c: connection,
manager,
}
} }
} }
impl<'a> crate::CtrlTask for CtrlAnimeTask<'a> { #[async_trait]
fn do_task(&self) -> Result<(), RogError> { impl crate::CtrlTask for CtrlAnimeTask {
if let Ok(mut lock) = self.inner.try_lock() { async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
// Refresh the config and cache incase the user has edited it let connection = Connection::system()
let config = AnimeConfig::load(); .await
lock.cache .expect("CtrlAnimeTask could not create dbus connection");
.init_from_config(&config)
.map_err(|err| { let manager = ManagerProxy::new(&connection)
warn!("CtrlAnimeTask: do_task {}", err); .await
err .expect("CtrlAnimeTask could not create ManagerProxy");
})
.ok(); let run_action =
} |start: bool, lock: MutexGuard<CtrlAnime>, inner: Arc<Mutex<CtrlAnime>>| {
if start {
info!("CtrlAnimeTask running sleep animation");
CtrlAnime::run_thread(inner.clone(), lock.cache.shutdown.clone(), true);
} else {
info!("CtrlAnimeTask running wake animation");
CtrlAnime::run_thread(inner.clone(), lock.cache.wake.clone(), true);
}
};
let inner = self.inner.clone();
executor
.spawn(async move {
if let Ok(notif) = manager.receive_prepare_for_sleep().await {
notif
.for_each(|event| {
if let Ok(args) = event.args() {
// Loop is required to try an attempt to get the mutex *without* blocking
// other threads - it is possible to end up with deadlocks otherwise.
loop {
if let Ok(lock) = inner.clone().try_lock() {
run_action(args.start, lock, inner.clone());
break;
}
}
}
})
.await;
}
})
.detach();
let manager = ManagerProxy::new(&connection)
.await
.expect("CtrlAnimeTask could not create ManagerProxy");
let inner = self.inner.clone();
executor
.spawn(async move {
if let Ok(notif) = manager.receive_prepare_for_shutdown().await {
notif
.for_each(|event| {
if let Ok(args) = event.args() {
loop {
if let Ok(lock) = inner.clone().try_lock() {
run_action(args.start, lock, inner.clone());
}
}
}
})
.await;
}
})
.detach();
// Check for signals on each task iteration, this will run the callbacks
// if any signal is recieved
self.manager.next_signal()?;
Ok(()) Ok(())
} }
} }
+34 -29
View File
@@ -1,12 +1,12 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use async_trait::async_trait;
use log::warn; use log::warn;
use rog_anime::{ use rog_anime::{
usb::{pkt_for_apply, pkt_for_set_boot, pkt_for_set_on}, usb::{pkt_for_apply, pkt_for_set_boot, pkt_for_set_on},
AnimeDataBuffer, AnimePowerStates, AnimeDataBuffer, AnimePowerStates,
}; };
use zbus::dbus_interface; use zbus::{dbus_interface, Connection, SignalContext};
use zvariant::ObjectPath;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
@@ -15,18 +15,10 @@ use super::CtrlAnime;
pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>); pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>);
/// The struct with the main dbus methods requires this trait /// The struct with the main dbus methods requires this trait
#[async_trait]
impl crate::ZbusAdd for CtrlAnimeZbus { impl crate::ZbusAdd for CtrlAnimeZbus {
fn add_to_server(self, server: &mut zbus::ObjectServer) { async fn add_to_server(self, server: &mut Connection) {
server Self::add_to_server_helper(self, "/org/asuslinux/Anime", server).await;
.at(
&ObjectPath::from_str_unchecked("/org/asuslinux/Anime"),
self,
)
.map_err(|err| {
warn!("CtrlAnimeDisplay: add_to_server {}", err);
err
})
.ok();
} }
} }
@@ -36,14 +28,18 @@ impl crate::ZbusAdd for CtrlAnimeZbus {
#[dbus_interface(name = "org.asuslinux.Daemon")] #[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlAnimeZbus { impl CtrlAnimeZbus {
/// Writes a data stream of length. Will force system thread to exit until it is restarted /// Writes a data stream of length. Will force system thread to exit until it is restarted
fn write(&self, input: AnimeDataBuffer) { fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> {
'outer: loop { 'outer: loop {
if let Ok(lock) = self.0.try_lock() { if let Ok(lock) = self.0.try_lock() {
lock.thread_exit.store(true, Ordering::SeqCst); lock.thread_exit.store(true, Ordering::SeqCst);
lock.write_data_buffer(input); lock.write_data_buffer(input).map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
err
})?;
break 'outer; break 'outer;
} }
} }
Ok(())
} }
/// Set the global AniMe brightness /// Set the global AniMe brightness
@@ -53,8 +49,8 @@ impl CtrlAnimeZbus {
let mut bright = bright; let mut bright = bright;
if bright < 0.0 { if bright < 0.0 {
bright = 0.0 bright = 0.0
} else if bright > 254.0 { } else if bright > 1.0 {
bright = 254.0; bright = 1.0;
} }
lock.config.brightness = bright; lock.config.brightness = bright;
lock.config.write(); lock.config.write();
@@ -64,26 +60,30 @@ impl CtrlAnimeZbus {
} }
/// Set whether the AniMe is displaying images/data /// Set whether the AniMe is displaying images/data
fn set_on_off(&self, status: bool) { async fn set_on_off(&self, #[zbus(signal_context)] ctxt: SignalContext<'_>, status: bool) {
let states;
'outer: loop { 'outer: loop {
if let Ok(mut lock) = self.0.try_lock() { if let Ok(mut lock) = self.0.try_lock() {
lock.write_bytes(&pkt_for_set_on(status)); lock.write_bytes(&pkt_for_set_on(status));
lock.config.awake_enabled = status; lock.config.awake_enabled = status;
lock.config.write(); lock.config.write();
let states = AnimePowerStates { states = Some(AnimePowerStates {
brightness: lock.config.brightness.floor() as u8,
enabled: lock.config.awake_enabled, enabled: lock.config.awake_enabled,
boot_anim_enabled: lock.config.boot_anim_enabled, boot_anim_enabled: lock.config.boot_anim_enabled,
}; });
self.notify_power_states(&states)
.unwrap_or_else(|err| warn!("{}", err));
break 'outer; break 'outer;
} }
} }
if let Some(state) = states {
Self::notify_power_states(&ctxt, state).await.ok();
}
} }
/// Set whether the AniMe will show boot, suspend, or off animations /// Set whether the AniMe will show boot, suspend, or off animations
fn set_boot_on_off(&self, on: bool) { async fn set_boot_on_off(&self, #[zbus(signal_context)] ctxt: SignalContext<'_>, on: bool) {
let states;
'outer: loop { 'outer: loop {
if let Ok(mut lock) = self.0.try_lock() { if let Ok(mut lock) = self.0.try_lock() {
lock.write_bytes(&pkt_for_set_boot(on)); lock.write_bytes(&pkt_for_set_boot(on));
@@ -91,15 +91,17 @@ impl CtrlAnimeZbus {
lock.config.boot_anim_enabled = on; lock.config.boot_anim_enabled = on;
lock.config.write(); lock.config.write();
let states = AnimePowerStates { states = Some(AnimePowerStates {
brightness: lock.config.brightness.floor() as u8,
enabled: lock.config.awake_enabled, enabled: lock.config.awake_enabled,
boot_anim_enabled: lock.config.boot_anim_enabled, boot_anim_enabled: lock.config.boot_anim_enabled,
}; });
self.notify_power_states(&states)
.unwrap_or_else(|err| warn!("{}", err));
break 'outer; break 'outer;
} }
} }
if let Some(state) = states {
Self::notify_power_states(&ctxt, state).await.ok();
}
} }
/// The main loop is the base system set action if the user isn't running /// The main loop is the base system set action if the user isn't running
@@ -116,7 +118,7 @@ impl CtrlAnimeZbus {
} }
} }
/// Get status of if the AniMe LEDs are on /// Get status of if the AniMe LEDs are on/displaying while system is awake
#[dbus_interface(property)] #[dbus_interface(property)]
fn awake_enabled(&self) -> bool { fn awake_enabled(&self) -> bool {
if let Ok(ctrl) = self.0.try_lock() { if let Ok(ctrl) = self.0.try_lock() {
@@ -136,5 +138,8 @@ impl CtrlAnimeZbus {
/// Notify listeners of the status of AniMe LED power and factory system-status animations /// Notify listeners of the status of AniMe LED power and factory system-status animations
#[dbus_interface(signal)] #[dbus_interface(signal)]
fn notify_power_states(&self, data: &AnimePowerStates) -> zbus::Result<()>; async fn notify_power_states(
ctxt: &SignalContext<'_>,
data: AnimePowerStates,
) -> zbus::Result<()>;
} }
+273 -143
View File
@@ -1,74 +1,182 @@
use crate::laptops::LaptopLedData; use crate::laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES};
use log::{error, info, warn}; use log::{error, warn};
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, LedBrightness}; use rog_aura::usb::{AuraDev1866, AuraDev19b6, AuraDevTuf, AuraDevice, AuraPowerDev};
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Direction, LedBrightness, Speed, GRADIENT};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::collections::BTreeMap; use std::collections::{BTreeMap, HashSet};
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::io::{Read, Write}; use std::io::{Read, Write};
use super::controller::CtrlKbdLed;
pub static AURA_CONFIG_PATH: &str = "/etc/asusd/aura.conf"; pub static AURA_CONFIG_PATH: &str = "/etc/asusd/aura.conf";
#[derive(Deserialize, Serialize)] /// Enable/disable LED control in various states such as
pub struct AuraConfigV320 { /// when the device is awake, suspended, shutting down or
pub brightness: u32, /// booting.
pub current_mode: AuraModeNum, #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub builtins: BTreeMap<AuraModeNum, AuraEffect>, pub enum AuraPowerConfig {
pub multizone: Option<AuraMultiZone>, AuraDevTuf(HashSet<AuraDevTuf>),
AuraDev1866(HashSet<AuraDev1866>),
AuraDev19b6(HashSet<AuraDev19b6>),
} }
impl AuraConfigV320 { impl AuraPowerConfig {
pub(crate) fn into_current(self) -> AuraConfig { /// Invalid for TUF laptops
AuraConfig { pub fn to_bytes(control: &Self) -> [u8; 3] {
brightness: <LedBrightness>::from(self.brightness), match control {
current_mode: self.current_mode, AuraPowerConfig::AuraDevTuf(_) => [0, 0, 0],
builtins: self.builtins, AuraPowerConfig::AuraDev1866(c) => {
multizone: self.multizone, let c: Vec<AuraDev1866> = c.iter().map(|v| *v).collect();
awake_enabled: true, AuraDev1866::to_bytes(&c)
sleep_anim_enabled: true, }
} AuraPowerConfig::AuraDev19b6(c) => {
} let c: Vec<AuraDev19b6> = c.iter().map(|v| *v).collect();
} AuraDev19b6::to_bytes(&c)
}
#[derive(Deserialize, Serialize)] }
pub struct AuraConfigV352 { }
pub brightness: LedBrightness,
pub current_mode: AuraModeNum, pub fn to_tuf_bool_array(control: &Self) -> Option<[bool; 5]> {
pub builtins: BTreeMap<AuraModeNum, AuraEffect>, if let Self::AuraDevTuf(c) = control {
pub multizone: Option<AuraMultiZone>, return Some([
} true,
c.contains(&AuraDevTuf::Boot),
impl AuraConfigV352 { c.contains(&AuraDevTuf::Awake),
pub(crate) fn into_current(self) -> AuraConfig { c.contains(&AuraDevTuf::Sleep),
AuraConfig { c.contains(&AuraDevTuf::Keyboard),
brightness: self.brightness, ]);
current_mode: self.current_mode, }
builtins: self.builtins,
multizone: self.multizone, if let Self::AuraDev1866(c) = control {
awake_enabled: true, return Some([
sleep_anim_enabled: true, true,
c.contains(&AuraDev1866::Boot),
c.contains(&AuraDev1866::Awake),
c.contains(&AuraDev1866::Sleep),
c.contains(&AuraDev1866::Keyboard),
]);
}
None
}
pub fn set_tuf(&mut self, power: AuraDevTuf, on: bool) {
if let Self::AuraDevTuf(p) = self {
if on {
p.insert(power);
} else {
p.remove(&power);
}
}
}
pub fn set_0x1866(&mut self, power: AuraDev1866, on: bool) {
if let Self::AuraDev1866(p) = self {
if on {
p.insert(power);
} else {
p.remove(&power);
}
}
}
pub fn set_0x19b6(&mut self, power: AuraDev19b6, on: bool) {
if let Self::AuraDev19b6(p) = self {
if on {
p.insert(power);
} else {
p.remove(&power);
}
}
}
}
impl From<&AuraPowerConfig> for AuraPowerDev {
fn from(config: &AuraPowerConfig) -> Self {
match config {
AuraPowerConfig::AuraDevTuf(d) => AuraPowerDev {
tuf: d.iter().map(|o| *o).collect(),
x1866: vec![],
x19b6: vec![],
},
AuraPowerConfig::AuraDev1866(d) => AuraPowerDev {
tuf: vec![],
x1866: d.iter().map(|o| *o).collect(),
x19b6: vec![],
},
AuraPowerConfig::AuraDev19b6(d) => AuraPowerDev {
tuf: vec![],
x1866: vec![],
x19b6: d.iter().map(|o| *o).collect(),
},
} }
} }
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
#[serde(default)]
pub struct AuraConfig { pub struct AuraConfig {
pub brightness: LedBrightness, pub brightness: LedBrightness,
pub current_mode: AuraModeNum, pub current_mode: AuraModeNum,
pub builtins: BTreeMap<AuraModeNum, AuraEffect>, pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
pub multizone: Option<AuraMultiZone>, pub multizone: Option<BTreeMap<AuraModeNum, Vec<AuraEffect>>>,
pub awake_enabled: bool, pub multizone_on: bool,
pub sleep_anim_enabled: bool, pub enabled: AuraPowerConfig,
} }
impl Default for AuraConfig { impl Default for AuraConfig {
fn default() -> Self { fn default() -> Self {
let mut prod_id = AuraDevice::Unknown;
for prod in ASUS_KEYBOARD_DEVICES.iter() {
if let Ok(_) = CtrlKbdLed::find_led_node(prod) {
prod_id = AuraDevice::from(*prod);
break;
}
}
if CtrlKbdLed::get_tuf_mode_path().is_some() {
prod_id = AuraDevice::Tuf;
}
let enabled = if prod_id == AuraDevice::X19B6 {
AuraPowerConfig::AuraDev19b6(HashSet::from([
AuraDev19b6::BootLogo,
AuraDev19b6::BootKeyb,
AuraDev19b6::SleepLogo,
AuraDev19b6::SleepKeyb,
AuraDev19b6::AwakeLogo,
AuraDev19b6::AwakeKeyb,
AuraDev19b6::ShutdownLogo,
AuraDev19b6::ShutdownKeyb,
AuraDev19b6::AwakeBar,
AuraDev19b6::BootBar,
AuraDev19b6::SleepBar,
AuraDev19b6::ShutdownBar,
]))
} else if prod_id == AuraDevice::Tuf {
AuraPowerConfig::AuraDevTuf(HashSet::from([
AuraDevTuf::Awake,
AuraDevTuf::Boot,
AuraDevTuf::Sleep,
AuraDevTuf::Keyboard,
]))
} else {
AuraPowerConfig::AuraDev1866(HashSet::from([
AuraDev1866::Awake,
AuraDev1866::Boot,
AuraDev1866::Sleep,
AuraDev1866::Keyboard,
AuraDev1866::Lightbar,
]))
};
AuraConfig { AuraConfig {
brightness: LedBrightness::Med, brightness: LedBrightness::Med,
current_mode: AuraModeNum::Static, current_mode: AuraModeNum::Static,
builtins: BTreeMap::new(), builtins: BTreeMap::new(),
multizone: None, multizone: None,
awake_enabled: true, multizone_on: false,
sleep_anim_enabled: true, enabled,
} }
} }
} }
@@ -94,16 +202,6 @@ impl AuraConfig {
} else { } else {
if let Ok(data) = serde_json::from_str(&buf) { if let Ok(data) = serde_json::from_str(&buf) {
return data; return data;
} else if let Ok(data) = serde_json::from_str::<AuraConfigV320>(&buf) {
let config = data.into_current();
config.write();
info!("Updated AuraConfig version");
return config;
} else if let Ok(data) = serde_json::from_str::<AuraConfigV352>(&buf) {
let config = data.into_current();
config.write();
info!("Updated AuraConfig version");
return config;
} }
warn!( warn!(
"Could not deserialise {}.\nWill rename to {}-old and recreate config", "Could not deserialise {}.\nWill rename to {}-old and recreate config",
@@ -129,6 +227,27 @@ impl AuraConfig {
config config
.builtins .builtins
.insert(*n, AuraEffect::default_with_mode(*n)); .insert(*n, AuraEffect::default_with_mode(*n));
if !support_data.multizone.is_empty() {
let mut default = vec![];
for (i, tmp) in support_data.multizone.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,
})
}
if let Some(m) = config.multizone.as_mut() {
m.insert(*n, default);
} else {
let mut tmp = BTreeMap::new();
tmp.insert(*n, default);
config.multizone = Some(tmp);
}
}
} }
// Should be okay to unwrap this as is since it is a Default // Should be okay to unwrap this as is since it is a Default
@@ -162,115 +281,126 @@ impl AuraConfig {
.unwrap_or_else(|err| error!("Could not write config: {}", err)); .unwrap_or_else(|err| error!("Could not write config: {}", err));
} }
/// Multipurpose, will accept AuraEffect with zones and put in the correct store /// Set the mode data, current mode, and if multizone enabled.
///
/// Multipurpose, will accept AuraEffect with zones and put in the correct store.
pub fn set_builtin(&mut self, effect: AuraEffect) { pub fn set_builtin(&mut self, effect: AuraEffect) {
self.current_mode = effect.mode;
match effect.zone() { match effect.zone() {
AuraZone::None => { AuraZone::None => {
self.builtins.insert(*effect.mode(), effect); self.builtins.insert(*effect.mode(), effect);
self.multizone_on = false;
} }
_ => { _ => {
if let Some(multi) = self.multizone.as_mut() { if let Some(multi) = self.multizone.as_mut() {
multi.set(effect) if let Some(fx) = multi.get_mut(effect.mode()) {
for fx in fx.iter_mut() {
if fx.zone == effect.zone {
*fx = effect;
return;
}
}
fx.push(effect);
} else {
multi.insert(*effect.mode(), vec![effect]);
}
} else {
let mut tmp = BTreeMap::new();
tmp.insert(*effect.mode(), vec![effect]);
self.multizone = Some(tmp);
} }
self.multizone_on = true;
} }
} }
} }
pub fn get_multizone(&self, aura_type: AuraModeNum) -> Option<&[AuraEffect; 4]> { pub fn get_multizone(&self, aura_type: AuraModeNum) -> Option<&[AuraEffect]> {
if let Some(multi) = &self.multizone { if let Some(multi) = &self.multizone {
if aura_type == AuraModeNum::Static { return multi.get(&aura_type).map(|v| v.as_slice());
return Some(multi.static_());
} else if aura_type == AuraModeNum::Breathe {
return Some(multi.breathe());
}
} }
None None
} }
} }
#[derive(Deserialize, Serialize)] #[cfg(test)]
pub struct AuraMultiZone { mod tests {
static_: [AuraEffect; 4], use super::AuraConfig;
breathe: [AuraEffect; 4], use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour};
}
impl AuraMultiZone { #[test]
pub fn set(&mut self, effect: AuraEffect) { fn set_multizone_4key_config() {
if effect.mode == AuraModeNum::Static { let mut config = AuraConfig::default();
match effect.zone {
AuraZone::None => {} let mut effect = AuraEffect::default();
AuraZone::One => self.static_[0] = effect, effect.colour1 = Colour(0xff, 0x00, 0xff);
AuraZone::Two => self.static_[1] = effect, effect.zone = AuraZone::Key1;
AuraZone::Three => self.static_[2] = effect, config.set_builtin(effect);
AuraZone::Four => self.static_[3] = effect,
} assert!(config.multizone.is_some());
} else if effect.mode == AuraModeNum::Breathe {
match effect.zone { let mut effect = AuraEffect::default();
AuraZone::None => {} effect.colour1 = Colour(0x00, 0xff, 0xff);
AuraZone::One => self.breathe[0] = effect, effect.zone = AuraZone::Key2;
AuraZone::Two => self.breathe[1] = effect, config.set_builtin(effect);
AuraZone::Three => self.breathe[2] = effect,
AuraZone::Four => self.breathe[3] = effect, let mut effect = AuraEffect::default();
} effect.colour1 = Colour(0xff, 0xff, 0x00);
} effect.zone = AuraZone::Key3;
config.set_builtin(effect);
let mut effect = AuraEffect::default();
effect.colour1 = Colour(0x00, 0xff, 0x00);
effect.zone = AuraZone::Key4;
let effect_clone = effect.clone();
config.set_builtin(effect);
// This should replace existing
config.set_builtin(effect_clone);
let res = config.multizone.unwrap();
let sta = res.get(&AuraModeNum::Static).unwrap();
assert_eq!(sta.len(), 4);
assert_eq!(sta[0].colour1, Colour(0xff, 0x00, 0xff));
assert_eq!(sta[1].colour1, Colour(0x00, 0xff, 0xff));
assert_eq!(sta[2].colour1, Colour(0xff, 0xff, 0x00));
assert_eq!(sta[3].colour1, Colour(0x00, 0xff, 0x00));
} }
pub fn static_(&self) -> &[AuraEffect; 4] { #[test]
&self.static_ fn set_multizone_multimode_config() {
} let mut config = AuraConfig::default();
pub fn breathe(&self) -> &[AuraEffect; 4] { let mut effect = AuraEffect::default();
&self.breathe effect.zone = AuraZone::Key1;
} config.set_builtin(effect);
}
assert!(config.multizone.is_some());
impl Default for AuraMultiZone {
fn default() -> Self { let mut effect = AuraEffect::default();
Self { effect.zone = AuraZone::Key2;
static_: [ effect.mode = AuraModeNum::Breathe;
AuraEffect { config.set_builtin(effect);
mode: AuraModeNum::Static,
zone: AuraZone::One, let mut effect = AuraEffect::default();
..Default::default() effect.zone = AuraZone::Key3;
}, effect.mode = AuraModeNum::Comet;
AuraEffect { config.set_builtin(effect);
mode: AuraModeNum::Static,
zone: AuraZone::Two, let mut effect = AuraEffect::default();
..Default::default() effect.zone = AuraZone::Key4;
}, effect.mode = AuraModeNum::Pulse;
AuraEffect { config.set_builtin(effect);
mode: AuraModeNum::Static,
zone: AuraZone::Three, let res = config.multizone.unwrap();
..Default::default() let sta = res.get(&AuraModeNum::Static).unwrap();
}, assert_eq!(sta.len(), 1);
AuraEffect {
mode: AuraModeNum::Static, let sta = res.get(&AuraModeNum::Breathe).unwrap();
zone: AuraZone::Four, assert_eq!(sta.len(), 1);
..Default::default()
}, let sta = res.get(&AuraModeNum::Comet).unwrap();
], assert_eq!(sta.len(), 1);
breathe: [
AuraEffect { let sta = res.get(&AuraModeNum::Pulse).unwrap();
mode: AuraModeNum::Breathe, assert_eq!(sta.len(), 1);
zone: AuraZone::One,
..Default::default()
},
AuraEffect {
mode: AuraModeNum::Breathe,
zone: AuraZone::Two,
..Default::default()
},
AuraEffect {
mode: AuraModeNum::Breathe,
zone: AuraZone::Three,
..Default::default()
},
AuraEffect {
mode: AuraModeNum::Breathe,
zone: AuraZone::Four,
..Default::default()
},
],
}
} }
} }
+422 -128
View File
@@ -1,43 +1,61 @@
// Only these two packets must be 17 bytes // Only these two packets must be 17 bytes
static KBD_BRIGHT_PATH: &str = "/sys/class/leds/asus::kbd_backlight/brightness"; static KBD_BRIGHT_PATH: &str = "/sys/class/leds/asus::kbd_backlight/brightness";
static TUF_RGB_MODE_PATH: &str = "/sys/devices/platform/asus-nb-wmi/tuf_krgb_mode";
static TUF_RGB_STATE_PATH: &str = "/sys/devices/platform/asus-nb-wmi/tuf_krgb_state";
use crate::{ use crate::{
error::RogError, error::RogError,
laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES}, laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES},
CtrlTask, CtrlTask,
}; };
use log::{info, warn}; use async_trait::async_trait;
use logind_zbus::ManagerProxy; use log::{error, info, warn};
use logind_zbus::manager::ManagerProxy;
use rog_aura::{ use rog_aura::{
usb::{ usb::{AuraDevice, LED_APPLY, LED_SET},
LED_APPLY, LED_AWAKE_OFF_SLEEP_OFF, LED_AWAKE_OFF_SLEEP_ON, LED_AWAKE_ON_SLEEP_OFF,
LED_AWAKE_ON_SLEEP_ON, LED_SET,
},
AuraEffect, LedBrightness, LED_MSG_LEN, AuraEffect, LedBrightness, LED_MSG_LEN,
}; };
use rog_aura::{AuraZone, Direction, Speed, GRADIENT};
use rog_supported::LedSupportedFunctions; use rog_supported::LedSupportedFunctions;
use std::io::{Read, Write}; use smol::{stream::StreamExt, Executor};
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
use std::{fs::OpenOptions, thread::spawn}; use std::{
collections::BTreeMap,
io::{Read, Write},
};
use std::{fs::OpenOptions, sync::MutexGuard};
use zbus::Connection; use zbus::Connection;
use crate::GetSupported; use crate::GetSupported;
use super::config::AuraConfig; use super::config::{AuraConfig, AuraPowerConfig};
impl GetSupported for CtrlKbdLed { impl GetSupported for CtrlKbdLed {
type A = LedSupportedFunctions; type A = LedSupportedFunctions;
fn get_supported() -> Self::A { fn get_supported() -> Self::A {
// let mode = <&str>::from(&<AuraModes>::from(*mode)); // let mode = <&str>::from(&<AuraModes>::from(*mode));
let multizone_led_mode = false;
let per_key_led_mode = false;
let laptop = LaptopLedData::get_data(); let laptop = LaptopLedData::get_data();
let stock_led_modes = laptop.standard; let stock_led_modes = laptop.standard;
let multizone_led_mode = laptop.multizone;
let per_key_led_mode = laptop.per_key;
let mut prod_id = AuraDevice::Unknown;
for prod in ASUS_KEYBOARD_DEVICES.iter() {
if let Ok(_) = Self::find_led_node(prod) {
prod_id = AuraDevice::from(*prod);
break;
}
}
if Self::get_tuf_mode_path().is_some() {
prod_id = AuraDevice::Tuf;
}
LedSupportedFunctions { LedSupportedFunctions {
prod_id,
brightness_set: CtrlKbdLed::get_kbd_bright_path().is_some(), brightness_set: CtrlKbdLed::get_kbd_bright_path().is_some(),
stock_led_modes, stock_led_modes,
multizone_led_mode, multizone_led_mode,
@@ -46,58 +64,30 @@ impl GetSupported for CtrlKbdLed {
} }
} }
#[derive(Debug, PartialEq, PartialOrd)]
pub enum LEDNode {
Tuf,
Rog(String),
None,
}
pub struct CtrlKbdLed { pub struct CtrlKbdLed {
pub led_node: Option<String>, // TODO: config stores the keyboard type as an AuraPower, use or update this
pub led_prod: Option<String>,
pub led_node: LEDNode,
pub bright_node: String, pub bright_node: String,
pub supported_modes: LaptopLedData, pub supported_modes: LaptopLedData,
pub flip_effect_write: bool, pub flip_effect_write: bool,
pub config: AuraConfig, pub config: AuraConfig,
} }
pub struct CtrlKbdLedTask<'a> { pub struct CtrlKbdLedTask {
inner: Arc<Mutex<CtrlKbdLed>>, inner: Arc<Mutex<CtrlKbdLed>>,
_c: Connection,
manager: ManagerProxy<'a>,
} }
impl<'a> CtrlKbdLedTask<'a> { impl CtrlKbdLedTask {
pub fn new(inner: Arc<Mutex<CtrlKbdLed>>) -> Self { pub fn new(inner: Arc<Mutex<CtrlKbdLed>>) -> Self {
let connection = Self { inner }
Connection::new_system().expect("CtrlKbdLedTask could not create dbus connection");
let manager =
ManagerProxy::new(&connection).expect("CtrlKbdLedTask could not create ManagerProxy");
let c1 = inner.clone();
// Run this action when the system wakes up from sleep
manager
.connect_prepare_for_sleep(move |sleep| {
if !sleep {
let c1 = c1.clone();
spawn(move || {
// wait a fraction for things to wake up properly
//std::thread::sleep(Duration::from_millis(100));
loop {
if let Ok(ref mut lock) = c1.try_lock() {
lock.set_brightness(lock.config.brightness).ok();
break;
}
}
});
}
Ok(())
})
.map_err(|err| {
warn!("CtrlAnimeTask: new() {}", err);
err
})
.ok();
Self {
inner,
_c: connection,
manager,
}
} }
fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> { fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> {
@@ -125,12 +115,70 @@ impl<'a> CtrlKbdLedTask<'a> {
} }
} }
impl<'a> CtrlTask for CtrlKbdLedTask<'a> { #[async_trait]
fn do_task(&self) -> Result<(), RogError> { impl CtrlTask for CtrlKbdLedTask {
self.manager.next_signal()?; async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
if let Ok(ref mut lock) = self.inner.try_lock() { let connection = Connection::system()
return Self::update_config(lock); .await
} .expect("CtrlKbdLedTask could not create dbus connection");
let manager = ManagerProxy::new(&connection)
.await
.expect("CtrlKbdLedTask could not create ManagerProxy");
let load_save = |start: bool, mut lock: MutexGuard<CtrlKbdLed>| {
// If waking up
if !start {
info!("CtrlKbdLedTask reloading brightness and modes");
lock.set_brightness(lock.config.brightness)
.map_err(|e| error!("CtrlKbdLedTask: {e}"))
.ok();
lock.write_current_config_mode()
.map_err(|e| error!("CtrlKbdLedTask: {e}"))
.ok();
} else if start {
info!("CtrlKbdLedTask saving last brightness");
Self::update_config(&mut lock)
.map_err(|e| error!("CtrlKbdLedTask: {e}"))
.ok();
}
};
let inner = self.inner.clone();
executor
.spawn(async move {
if let Ok(notif) = manager.receive_prepare_for_sleep().await {
notif
.for_each(|event| {
if let Ok(args) = event.args() {
loop {
// Loop so that we do aquire the lock but also don't block other
// threads (prevents potential deadlocks)
if let Ok(lock) = inner.clone().try_lock() {
load_save(args.start, lock);
break;
}
}
}
})
.await;
}
if let Ok(notif) = manager.receive_prepare_for_shutdown().await {
notif
.for_each(|event| {
if let Ok(args) = event.args() {
loop {
if let Ok(lock) = inner.clone().try_lock() {
load_save(args.start, lock);
break;
}
}
}
})
.await;
}
})
.detach();
Ok(()) Ok(())
} }
} }
@@ -140,13 +188,9 @@ pub struct CtrlKbdLedReloader(pub Arc<Mutex<CtrlKbdLed>>);
impl crate::Reloadable for CtrlKbdLedReloader { impl crate::Reloadable for CtrlKbdLedReloader {
fn reload(&mut self) -> Result<(), RogError> { fn reload(&mut self) -> Result<(), RogError> {
if let Ok(mut ctrl) = self.0.try_lock() { if let Ok(mut ctrl) = self.0.try_lock() {
let current = ctrl.config.current_mode; ctrl.write_current_config_mode()?;
if let Some(mode) = ctrl.config.builtins.get(&current).cloned() { ctrl.set_power_states(&ctrl.config)
ctrl.do_command(mode).ok(); .map_err(|err| warn!("{err}"))
}
ctrl.set_states_enabled(ctrl.config.awake_enabled, ctrl.config.sleep_anim_enabled)
.map_err(|err| warn!("{}", err))
.ok(); .ok();
} }
Ok(()) Ok(())
@@ -162,36 +206,46 @@ impl CtrlKbdLedZbus {
} }
impl CtrlKbdLed { impl CtrlKbdLed {
#[inline]
pub fn new(supported_modes: LaptopLedData, config: AuraConfig) -> Result<Self, RogError> { pub fn new(supported_modes: LaptopLedData, config: AuraConfig) -> Result<Self, RogError> {
// TODO: return error if *all* nodes are None let mut led_prod = None;
let mut led_node = None; let mut led_node = None;
for prod in ASUS_KEYBOARD_DEVICES.iter() { for prod in ASUS_KEYBOARD_DEVICES.iter() {
match Self::find_led_node(prod) { match Self::find_led_node(prod) {
Ok(node) => { Ok(node) => {
led_prod = Some(prod.to_string());
led_node = Some(node); led_node = Some(node);
info!("Looked for keyboard controller 0x{prod}: Found");
break; break;
} }
Err(err) => warn!("led_node: {}", err), Err(err) => info!("Looked for keyboard controller 0x{prod}: {err}"),
} }
} }
let bright_node = Self::get_kbd_bright_path(); let bright_node = Self::get_kbd_bright_path();
let tuf_node = Self::get_tuf_mode_path().is_some() || Self::get_tuf_state_path().is_some();
if led_node.is_none() && bright_node.is_none() { if led_node.is_none() && tuf_node && bright_node.is_none() {
return Err(RogError::MissingFunction( let dmi = sysfs_class::DmiId::default();
"All keyboard features missing, you may require a v5.11 series kernel or newer" if let Ok(prod_family) = dmi.product_family() {
.into(), if prod_family.contains("TUF") {
)); warn!("A kernel patch is in progress for TUF RGB support");
}
}
return Err(RogError::NoAuraKeyboard);
} }
if bright_node.is_none() { let led_node = if let Some(rog) = led_node {
return Err(RogError::MissingFunction( info!("Found ROG USB keyboard");
"No brightness control, you may require a v5.11 series kernel or newer".into(), LEDNode::Rog(rog)
)); } else if tuf_node {
} info!("Found TUF keyboard");
LEDNode::Tuf
} else {
LEDNode::None
};
let ctrl = CtrlKbdLed { let ctrl = CtrlKbdLed {
led_prod,
led_node, led_node,
bright_node: bright_node.unwrap(), // If was none then we already returned above bright_node: bright_node.unwrap(), // If was none then we already returned above
supported_modes, supported_modes,
@@ -208,6 +262,20 @@ impl CtrlKbdLed {
None None
} }
pub fn get_tuf_mode_path() -> Option<String> {
if Path::new(TUF_RGB_MODE_PATH).exists() {
return Some(TUF_RGB_MODE_PATH.to_string());
}
None
}
fn get_tuf_state_path() -> Option<String> {
if Path::new(TUF_RGB_STATE_PATH).exists() {
return Some(TUF_RGB_STATE_PATH.to_string());
}
None
}
pub(super) fn get_brightness(&self) -> Result<u8, RogError> { pub(super) fn get_brightness(&self) -> Result<u8, RogError> {
let mut file = OpenOptions::new() let mut file = OpenOptions::new()
.read(true) .read(true)
@@ -263,27 +331,32 @@ impl CtrlKbdLed {
self.set_brightness(self.config.brightness) self.set_brightness(self.config.brightness)
} }
/// Set if awake/on LED active, and/or sleep animation active /// Set combination state for boot animation/sleep animation/all leds/keys leds/side leds LED active
pub(super) fn set_states_enabled(&self, awake: bool, sleep: bool) -> Result<(), RogError> { pub(super) fn set_power_states(&self, config: &AuraConfig) -> Result<(), RogError> {
let bytes = if awake && sleep { if LEDNode::Tuf == self.led_node {
LED_AWAKE_ON_SLEEP_ON if let Some(path) = Self::get_tuf_state_path() {
} else if awake && !sleep { let mut file = OpenOptions::new().write(true).open(path)?;
LED_AWAKE_ON_SLEEP_OFF if let Some(pwr) = AuraPowerConfig::to_tuf_bool_array(&config.enabled) {
} else if !awake && sleep { let buf = format!(
LED_AWAKE_OFF_SLEEP_ON "1 {} {} {} {}",
} else if !awake && !sleep { pwr[1] as u8, pwr[2] as u8, pwr[3] as u8, pwr[4] as u8,
LED_AWAKE_OFF_SLEEP_OFF );
file.write_all(buf.as_bytes())?;
}
}
} else { } else {
LED_AWAKE_ON_SLEEP_ON let bytes = AuraPowerConfig::to_bytes(&config.enabled);
}; let message = [0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2]];
self.write_bytes(&bytes)?;
self.write_bytes(&LED_SET)?; self.write_bytes(&message)?;
// Changes won't persist unless apply is set self.write_bytes(&LED_SET)?;
self.write_bytes(&LED_APPLY)?; // Changes won't persist unless apply is set
self.write_bytes(&LED_APPLY)?;
}
Ok(()) Ok(())
} }
fn find_led_node(id_product: &str) -> Result<String, RogError> { pub(crate) fn find_led_node(id_product: &str) -> Result<String, RogError> {
let mut enumerator = udev::Enumerator::new().map_err(|err| { let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err); warn!("{}", err);
RogError::Udev("enumerator failed".into(), err) RogError::Udev("enumerator failed".into(), err)
@@ -321,14 +394,29 @@ impl CtrlKbdLed {
)) ))
} }
pub(crate) fn do_command(&mut self, mode: AuraEffect) -> Result<(), RogError> { /// Set an Aura effect if the effect mode or zone is supported.
self.set_and_save(mode) ///
/// On success the aura config file is read to refresh cached values, then the effect is
/// stored and config written to disk.
pub(crate) fn set_effect(&mut self, effect: AuraEffect) -> Result<(), RogError> {
if !self.supported_modes.standard.contains(&effect.mode) {
return Err(RogError::AuraEffectNotSupported);
} else if effect.zone != AuraZone::None
&& !self.supported_modes.multizone.contains(&effect.zone)
{
return Err(RogError::AuraEffectNotSupported);
}
self.write_mode(&effect)?;
self.config.read(); // refresh config if successful
self.config.set_builtin(effect);
self.config.write();
Ok(())
} }
/// Should only be used if the bytes you are writing are verified correct /// Should only be used if the bytes you are writing are verified correct
#[inline]
fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> { fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
if let Some(led_node) = &self.led_node { if let LEDNode::Rog(led_node) = &self.led_node {
if let Ok(mut file) = OpenOptions::new().write(true).open(led_node) { if let Ok(mut file) = OpenOptions::new().write(true).open(led_node) {
// println!("write: {:02x?}", &message); // println!("write: {:02x?}", &message);
return file return file
@@ -336,11 +424,10 @@ impl CtrlKbdLed {
.map_err(|err| RogError::Write("write_bytes".into(), err)); .map_err(|err| RogError::Write("write_bytes".into(), err));
} }
} }
Err(RogError::NotSupported) Err(RogError::NoAuraNode)
} }
/// Write an effect block /// Write an effect block. This is for per-key
#[inline]
fn _write_effect(&mut self, effect: &[Vec<u8>]) -> Result<(), RogError> { fn _write_effect(&mut self, effect: &[Vec<u8>]) -> Result<(), RogError> {
if self.flip_effect_write { if self.flip_effect_write {
for row in effect.iter().rev() { for row in effect.iter().rev() {
@@ -355,20 +442,6 @@ impl CtrlKbdLed {
Ok(()) Ok(())
} }
/// Used to set a builtin mode and save the settings for it
///
/// This needs to be universal so that settings applied by dbus stick
#[inline]
fn set_and_save(&mut self, mode: AuraEffect) -> Result<(), RogError> {
self.config.read();
self.write_mode(&mode)?;
self.config.current_mode = *mode.mode();
self.config.set_builtin(mode);
self.config.write();
Ok(())
}
#[inline]
pub(super) fn toggle_mode(&mut self, reverse: bool) -> Result<(), RogError> { pub(super) fn toggle_mode(&mut self, reverse: bool) -> Result<(), RogError> {
let current = self.config.current_mode; let current = self.config.current_mode;
if let Some(idx) = self if let Some(idx) = self
@@ -394,26 +467,247 @@ impl CtrlKbdLed {
let next = self.supported_modes.standard[idx]; let next = self.supported_modes.standard[idx];
self.config.read(); self.config.read();
if let Some(data) = self.config.builtins.get(&next) { // if self.config.builtins.contains_key(&next) {
self.write_mode(data)?; self.config.current_mode = next;
self.config.current_mode = next; self.write_current_config_mode()?;
} // }
self.config.write(); self.config.write();
} }
Ok(()) Ok(())
} }
#[inline]
fn write_mode(&self, mode: &AuraEffect) -> Result<(), RogError> { fn write_mode(&self, mode: &AuraEffect) -> Result<(), RogError> {
if !self.supported_modes.standard.contains(mode.mode()) { if LEDNode::Tuf == self.led_node {
return Err(RogError::NotSupported); let mut file = OpenOptions::new().write(true).open(TUF_RGB_MODE_PATH)?;
let buf = format!(
"1 {} {} {} {} {}",
mode.mode as u8, mode.colour1.0, mode.colour1.1, mode.colour1.2, mode.speed as u8,
);
file.write_all(buf.as_bytes())?;
} else {
let bytes: [u8; LED_MSG_LEN] = mode.into();
self.write_bytes(&bytes)?;
self.write_bytes(&LED_SET)?;
// Changes won't persist unless apply is set
self.write_bytes(&LED_APPLY)?;
}
Ok(())
}
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_ref() {
if let Some(set) = multizones.get(&mode) {
for mode in set {
self.write_mode(mode)?;
}
}
}
} else {
let mode = self.config.current_mode;
if let Some(effect) = self.config.builtins.get(&mode) {
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_modes.multizone.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);
} }
let bytes: [u8; LED_MSG_LEN] = mode.into();
self.write_bytes(&bytes)?;
self.write_bytes(&LED_SET)?;
// Changes won't persist unless apply is set
self.write_bytes(&LED_APPLY)?;
Ok(()) Ok(())
} }
} }
#[cfg(test)]
mod tests {
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour};
use crate::{
ctrl_aura::{config::AuraConfig, controller::LEDNode},
laptops::LaptopLedData,
};
use super::CtrlKbdLed;
#[test]
// #[ignore = "Must be manually run due to detection stage"]
fn check_set_mode_errors() {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::default();
let supported_modes = LaptopLedData {
prod_family: "".into(),
board_names: vec![],
standard: vec![AuraModeNum::Static],
multizone: vec![],
per_key: false,
};
let mut controller = CtrlKbdLed {
led_prod: None,
led_node: LEDNode::None,
bright_node: String::new(),
supported_modes,
flip_effect_write: false,
config,
};
let mut effect = AuraEffect::default();
effect.colour1 = Colour(0xff, 0x00, 0xff);
effect.zone = AuraZone::None;
// This error comes from write_bytes because we don't have a keyboard node stored
assert_eq!(
controller
.set_effect(effect.clone())
.unwrap_err()
.to_string(),
"No Aura keyboard node found"
);
effect.mode = AuraModeNum::Laser;
assert_eq!(
controller
.set_effect(effect.clone())
.unwrap_err()
.to_string(),
"Aura effect not supported"
);
effect.mode = AuraModeNum::Static;
effect.zone = AuraZone::Key2;
assert_eq!(
controller
.set_effect(effect.clone())
.unwrap_err()
.to_string(),
"Aura effect not supported"
);
controller.supported_modes.multizone.push(AuraZone::Key2);
assert_eq!(
controller
.set_effect(effect.clone())
.unwrap_err()
.to_string(),
"No Aura keyboard node found"
);
}
#[test]
fn create_multizone_if_no_config() {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::default();
let supported_modes = LaptopLedData {
prod_family: "".into(),
board_names: vec![],
standard: vec![AuraModeNum::Static],
multizone: vec![],
per_key: false,
};
let mut controller = CtrlKbdLed {
led_prod: None,
led_node: LEDNode::None,
bright_node: String::new(),
supported_modes,
flip_effect_write: false,
config,
};
assert!(controller.config.multizone.is_none());
assert!(controller.create_multizone_default().is_err());
assert!(controller.config.multizone.is_none());
controller.supported_modes.multizone.push(AuraZone::Key1);
controller.supported_modes.multizone.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]
fn next_mode_create_multizone_if_no_config() {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::default();
let supported_modes = LaptopLedData {
prod_family: "".into(),
board_names: vec![],
standard: vec![AuraModeNum::Static],
multizone: vec![AuraZone::Key1, AuraZone::Key2],
per_key: false,
};
let mut controller = CtrlKbdLed {
led_prod: None,
led_node: LEDNode::None,
bright_node: String::new(),
supported_modes,
flip_effect_write: false,
config,
};
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 Aura keyboard node found"
);
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);
}
}
+152 -100
View File
@@ -1,18 +1,16 @@
use log::{error, warn}; use std::collections::BTreeMap;
use rog_aura::{AuraEffect, LedBrightness, LedPowerStates};
use zbus::dbus_interface; use async_trait::async_trait;
use zvariant::ObjectPath; use log::warn;
use rog_aura::{usb::AuraPowerDev, AuraEffect, AuraModeNum, LedBrightness};
use zbus::{dbus_interface, Connection, SignalContext};
use super::controller::CtrlKbdLedZbus; use super::controller::CtrlKbdLedZbus;
#[async_trait]
impl crate::ZbusAdd for CtrlKbdLedZbus { impl crate::ZbusAdd for CtrlKbdLedZbus {
fn add_to_server(self, server: &mut zbus::ObjectServer) { async fn add_to_server(self, server: &mut Connection) {
server Self::add_to_server_helper(self, "/org/asuslinux/Aura", server).await;
.at(&ObjectPath::from_str_unchecked("/org/asuslinux/Led"), self)
.map_err(|err| {
error!("DbusKbdLed: add_to_server {}", err);
})
.ok();
} }
} }
@@ -22,7 +20,7 @@ impl crate::ZbusAdd for CtrlKbdLedZbus {
#[dbus_interface(name = "org.asuslinux.Daemon")] #[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlKbdLedZbus { impl CtrlKbdLedZbus {
/// Set the keyboard brightness level (0-3) /// Set the keyboard brightness level (0-3)
fn set_brightness(&mut self, brightness: LedBrightness) { async fn set_brightness(&mut self, brightness: LedBrightness) {
if let Ok(ctrl) = self.0.try_lock() { if let Ok(ctrl) = self.0.try_lock() {
ctrl.set_brightness(brightness) ctrl.set_brightness(brightness)
.map_err(|err| warn!("{}", err)) .map_err(|err| warn!("{}", err))
@@ -30,140 +28,191 @@ impl CtrlKbdLedZbus {
} }
} }
/// Set the keyboard LED to enabled while the device is awake /// Set a variety of states, input is array of enum.
fn set_awake_enabled(&mut self, enabled: bool) { /// `enabled` sets if the sent array should be disabled or enabled
///
/// ```text
/// pub struct AuraPowerDev {
/// pub x1866: Vec<AuraDev1866>,
/// pub x19b6: Vec<AuraDev19b6>,
/// }
/// pub enum AuraDev1866 {
/// Awake,
/// Keyboard,
/// Lightbar,
/// Boot,
/// Sleep,
/// }
/// enum AuraDev19b6 {
/// BootLogo,
/// BootKeyb,
/// AwakeLogo,
/// AwakeKeyb,
/// SleepLogo,
/// SleepKeyb,
/// ShutdownLogo,
/// ShutdownKeyb,
/// AwakeBar,
/// BootBar,
/// SleepBar,
/// ShutdownBar,
/// }
/// ```
async fn set_leds_power(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
options: AuraPowerDev,
enabled: bool,
) -> zbus::fdo::Result<()> {
let mut states = None;
if let Ok(mut ctrl) = self.0.try_lock() { if let Ok(mut ctrl) = self.0.try_lock() {
ctrl.set_states_enabled(enabled, ctrl.config.sleep_anim_enabled) for p in options.tuf {
.map_err(|err| warn!("{}", err)) ctrl.config.enabled.set_tuf(p, enabled);
.ok(); }
ctrl.config.awake_enabled = enabled; for p in options.x1866 {
ctrl.config.enabled.set_0x1866(p, enabled);
}
for p in options.x19b6 {
ctrl.config.enabled.set_0x19b6(p, enabled);
}
ctrl.config.write(); ctrl.config.write();
let states = LedPowerStates { ctrl.set_power_states(&ctrl.config).map_err(|e| {
enabled: ctrl.config.awake_enabled, warn!("{}", e);
sleep_anim_enabled: ctrl.config.sleep_anim_enabled, e
}; })?;
self.notify_power_states(&states)
states = Some(AuraPowerDev::from(&ctrl.config.enabled));
}
// Need to pull state out like this due to MutexGuard
if let Some(states) = states {
Self::notify_power_states(&ctxt, &states)
.await
.unwrap_or_else(|err| warn!("{}", err)); .unwrap_or_else(|err| warn!("{}", err));
} }
Ok(())
} }
/// Set the keyboard LED suspend animation to enabled while the device is suspended async fn set_led_mode(
fn set_sleep_enabled(&mut self, enabled: bool) { &mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
effect: AuraEffect,
) -> zbus::fdo::Result<()> {
let mut led = None;
if let Ok(mut ctrl) = self.0.try_lock() { if let Ok(mut ctrl) = self.0.try_lock() {
ctrl.set_states_enabled(ctrl.config.awake_enabled, enabled) ctrl.set_effect(effect).map_err(|e| {
.map_err(|err| warn!("{}", err)) warn!("{}", e);
.ok(); e
ctrl.config.sleep_anim_enabled = enabled; })?;
ctrl.config.write(); if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
let states = LedPowerStates { led = Some(mode.clone());
enabled: ctrl.config.awake_enabled,
sleep_anim_enabled: ctrl.config.sleep_anim_enabled,
};
self.notify_power_states(&states)
.unwrap_or_else(|err| warn!("{}", err));
}
}
fn set_led_mode(&mut self, effect: AuraEffect) {
if let Ok(mut ctrl) = self.0.try_lock() {
match ctrl.do_command(effect) {
Ok(_) => {
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
self.notify_led(mode.clone())
.unwrap_or_else(|err| warn!("{}", err));
}
}
Err(err) => {
warn!("{}", err);
}
} }
} }
if let Some(led) = led {
Self::notify_led(&ctxt, led)
.await
.unwrap_or_else(|err| warn!("{}", err));
}
Ok(())
} }
fn next_led_mode(&self) { async fn next_led_mode(
if let Ok(mut ctrl) = self.0.try_lock() { &self,
ctrl.toggle_mode(false) #[zbus(signal_context)] ctxt: SignalContext<'_>,
.unwrap_or_else(|err| warn!("{}", err)); ) -> zbus::fdo::Result<()> {
let mut led = None;
if let Ok(mut ctrl) = self.0.lock() {
ctrl.toggle_mode(false).map_err(|e| {
warn!("{}", e);
e
})?;
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
self.notify_led(mode.clone()) led = Some(mode.clone());
.unwrap_or_else(|err| warn!("{}", err));
} }
} }
if let Some(led) = led {
Self::notify_led(&ctxt, led)
.await
.unwrap_or_else(|err| warn!("{}", err));
}
Ok(())
} }
fn prev_led_mode(&self) { async fn prev_led_mode(
if let Ok(mut ctrl) = self.0.try_lock() { &self,
ctrl.toggle_mode(true) #[zbus(signal_context)] ctxt: SignalContext<'_>,
.unwrap_or_else(|err| warn!("{}", err)); ) -> zbus::fdo::Result<()> {
let mut led = None;
if let Ok(mut ctrl) = self.0.lock() {
ctrl.toggle_mode(true).map_err(|e| {
warn!("{}", e);
e
})?;
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
self.notify_led(mode.clone()) led = Some(mode.clone());
.unwrap_or_else(|err| warn!("{}", err));
} }
} }
} if let Some(led) = led {
Self::notify_led(&ctxt, led)
fn next_led_brightness(&self) { .await
if let Ok(mut ctrl) = self.0.try_lock() {
ctrl.next_brightness()
.unwrap_or_else(|err| warn!("{}", err)); .unwrap_or_else(|err| warn!("{}", err));
} }
Ok(())
} }
fn prev_led_brightness(&self) { async fn next_led_brightness(&self) -> zbus::fdo::Result<()> {
if let Ok(mut ctrl) = self.0.try_lock() { if let Ok(mut ctrl) = self.0.try_lock() {
ctrl.prev_brightness() ctrl.next_brightness().map_err(|e| {
.unwrap_or_else(|err| warn!("{}", err)); warn!("{}", e);
e
})?;
} }
Ok(())
} }
#[dbus_interface(property)] async fn prev_led_brightness(&self) -> zbus::fdo::Result<()> {
fn awake_enabled(&self) -> bool { if let Ok(mut ctrl) = self.0.try_lock() {
if let Ok(ctrl) = self.0.try_lock() { ctrl.prev_brightness().map_err(|e| {
return ctrl.config.awake_enabled; warn!("{}", e);
e
})?;
} }
true Ok(())
} }
#[dbus_interface(property)] // As property doesn't work for AuraPowerDev (complexity of serialization?)
fn sleep_enabled(&self) -> bool { // #[dbus_interface(property)]
if let Ok(ctrl) = self.0.try_lock() { async fn leds_enabled(&self) -> AuraPowerDev {
return ctrl.config.sleep_anim_enabled; loop {
if let Ok(ctrl) = self.0.try_lock() {
return AuraPowerDev::from(&ctrl.config.enabled);
}
} }
true
} }
/// Return the current mode data /// Return the current mode data
#[dbus_interface(property)] async fn led_mode(&self) -> AuraModeNum {
fn led_mode(&self) -> String {
if let Ok(ctrl) = self.0.try_lock() { if let Ok(ctrl) = self.0.try_lock() {
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { return ctrl.config.current_mode;
if let Ok(json) = serde_json::to_string(&mode) {
return json;
}
}
} }
warn!("SetKeyBacklight could not deserialise"); AuraModeNum::Static
"SetKeyBacklight could not deserialise".to_string()
} }
/// Return a list of available modes /// Return a list of available modes
#[dbus_interface(property)] async fn led_modes(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
fn led_modes(&self) -> String { loop {
if let Ok(ctrl) = self.0.try_lock() { if let Ok(ctrl) = self.0.try_lock() {
if let Ok(json) = serde_json::to_string(&ctrl.config.builtins) { return ctrl.config.builtins.clone();
return json;
} }
} }
warn!("SetKeyBacklight could not deserialise");
"SetKeyBacklight could not serialise".to_string()
} }
/// Return the current LED brightness /// Return the current LED brightness
#[dbus_interface(property)] #[dbus_interface(property)]
fn led_brightness(&self) -> i8 { async fn led_brightness(&self) -> i8 {
if let Ok(ctrl) = self.0.try_lock() { if let Ok(ctrl) = self.0.try_lock() {
return ctrl.get_brightness().map(|n| n as i8).unwrap_or(-1); return ctrl.get_brightness().map(|n| n as i8).unwrap_or(-1);
} }
@@ -172,8 +221,11 @@ impl CtrlKbdLedZbus {
} }
#[dbus_interface(signal)] #[dbus_interface(signal)]
fn notify_led(&self, data: AuraEffect) -> zbus::Result<()>; async fn notify_led(signal_ctxt: &SignalContext<'_>, data: AuraEffect) -> zbus::Result<()>;
#[dbus_interface(signal)] #[dbus_interface(signal)]
fn notify_power_states(&self, data: &LedPowerStates) -> zbus::Result<()>; async fn notify_power_states(
signal_ctxt: &SignalContext<'_>,
data: &AuraPowerDev,
) -> zbus::Result<()>;
} }
+95 -26
View File
@@ -1,14 +1,19 @@
use crate::CtrlTask;
use crate::{config::Config, error::RogError, GetSupported}; use crate::{config::Config, error::RogError, GetSupported};
//use crate::dbus::DbusEvents; use async_trait::async_trait;
use log::{info, warn}; use log::{info, warn};
use logind_zbus::manager::ManagerProxy;
use rog_supported::ChargeSupportedFunctions; use rog_supported::ChargeSupportedFunctions;
use smol::stream::StreamExt;
use smol::Executor;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io::Write; use std::io::Write;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
use zbus::dbus_interface; use zbus::dbus_interface;
use zvariant::ObjectPath; use zbus::Connection;
use zbus::SignalContext;
static BAT_CHARGE_PATH0: &str = "/sys/class/power_supply/BAT0/charge_control_end_threshold"; static BAT_CHARGE_PATH0: &str = "/sys/class/power_supply/BAT0/charge_control_end_threshold";
static BAT_CHARGE_PATH1: &str = "/sys/class/power_supply/BAT1/charge_control_end_threshold"; static BAT_CHARGE_PATH1: &str = "/sys/class/power_supply/BAT1/charge_control_end_threshold";
@@ -30,28 +35,27 @@ pub struct CtrlCharge {
#[dbus_interface(name = "org.asuslinux.Daemon")] #[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlCharge { impl CtrlCharge {
pub fn set_limit(&mut self, limit: u8) -> Result<(), RogError> { async fn set_limit(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
limit: u8,
) -> zbus::fdo::Result<()> {
if !(20..=100).contains(&limit) { if !(20..=100).contains(&limit) {
return Err(RogError::ChargeLimit(limit)); return Err(RogError::ChargeLimit(limit))?;
} }
if let Ok(mut config) = self.config.try_lock() { if let Ok(mut config) = self.config.try_lock() {
self.set(limit, &mut config) Self::set(limit, &mut config)
.map_err(|err| {
warn!("CtrlCharge: set_limit {}", err);
err
})
.ok();
self.notify_charge(limit)
.map_err(|err| { .map_err(|err| {
warn!("CtrlCharge: set_limit {}", err); warn!("CtrlCharge: set_limit {}", err);
err err
}) })
.ok(); .ok();
} }
Self::notify_charge(&ctxt, limit).await?;
Ok(()) Ok(())
} }
pub fn limit(&self) -> i8 { fn limit(&self) -> i8 {
if let Ok(config) = self.config.try_lock() { if let Ok(config) = self.config.try_lock() {
return config.bat_charge_limit as i8; return config.bat_charge_limit as i8;
} }
@@ -59,21 +63,13 @@ impl CtrlCharge {
} }
#[dbus_interface(signal)] #[dbus_interface(signal)]
pub fn notify_charge(&self, limit: u8) -> zbus::Result<()> {} async fn notify_charge(ctxt: &SignalContext<'_>, limit: u8) -> zbus::Result<()>;
} }
#[async_trait]
impl crate::ZbusAdd for CtrlCharge { impl crate::ZbusAdd for CtrlCharge {
fn add_to_server(self, server: &mut zbus::ObjectServer) { async fn add_to_server(self, server: &mut Connection) {
server Self::add_to_server_helper(self, "/org/asuslinux/Charge", server).await;
.at(
&ObjectPath::from_str_unchecked("/org/asuslinux/Charge"),
self,
)
.map_err(|err| {
warn!("CtrlCharge: add_to_server {}", err);
err
})
.ok();
} }
} }
@@ -81,7 +77,7 @@ impl crate::Reloadable for CtrlCharge {
fn reload(&mut self) -> Result<(), RogError> { fn reload(&mut self) -> Result<(), RogError> {
if let Ok(mut config) = self.config.try_lock() { if let Ok(mut config) = self.config.try_lock() {
config.read(); config.read();
self.set(config.bat_charge_limit, &mut config)?; Self::set(config.bat_charge_limit, &mut config)?;
} }
Ok(()) Ok(())
} }
@@ -108,7 +104,7 @@ impl CtrlCharge {
} }
} }
pub(super) fn set(&self, limit: u8, config: &mut Config) -> Result<(), RogError> { pub(super) fn set(limit: u8, config: &mut Config) -> Result<(), RogError> {
if !(20..=100).contains(&limit) { if !(20..=100).contains(&limit) {
return Err(RogError::ChargeLimit(limit)); return Err(RogError::ChargeLimit(limit));
} }
@@ -130,3 +126,76 @@ impl CtrlCharge {
Ok(()) Ok(())
} }
} }
#[async_trait]
impl CtrlTask for CtrlCharge {
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
let connection = Connection::system()
.await
.expect("CtrlCharge could not create dbus connection");
let manager = ManagerProxy::new(&connection)
.await
.expect("CtrlCharge could not create ManagerProxy");
let config1 = self.config.clone();
executor
.spawn(async move {
if let Ok(notif) = manager.receive_prepare_for_sleep().await {
notif
.for_each(|event| {
if let Ok(args) = event.args() {
// If waking up
if !args.start {
info!("CtrlCharge reloading charge limit");
if let Ok(mut lock) = config1.try_lock() {
Self::set(lock.bat_charge_limit, &mut lock)
.map_err(|err| {
warn!("CtrlCharge: set_limit {}", err);
err
})
.ok();
}
}
}
})
.await;
}
})
.detach();
let manager = ManagerProxy::new(&connection)
.await
.expect("CtrlCharge could not create ManagerProxy");
let config = self.config.clone();
executor
.spawn(async move {
if let Ok(notif) = manager.receive_prepare_for_shutdown().await {
notif
.for_each(|event| {
if let Ok(args) = event.args() {
// If waking up - intention is to catch hibernation event
if !args.start {
info!("CtrlCharge reloading charge limit");
loop {
if let Ok(mut lock) = config.clone().try_lock() {
Self::set(lock.bat_charge_limit, &mut lock)
.map_err(|err| {
warn!("CtrlCharge: set_limit {}", err);
err
})
.ok();
break;
}
}
}
}
})
.await;
}
})
.detach();
Ok(())
}
}
-14
View File
@@ -23,17 +23,6 @@ impl ProfileConfig {
} }
} }
pub fn set_defaults_and_save(&mut self) {
self.active_profile = Profile::get_active_profile().unwrap_or(Profile::Balanced);
if let Ok(res) = FanCurveProfiles::is_supported() {
if res {
let curves = FanCurveProfiles::default();
self.fan_curves = Some(curves);
}
}
self.write();
}
pub fn load(config_path: String) -> Self { pub fn load(config_path: String) -> Self {
let mut file = OpenOptions::new() let mut file = OpenOptions::new()
.read(true) .read(true)
@@ -46,7 +35,6 @@ impl ProfileConfig {
if let Ok(read_len) = file.read_to_string(&mut buf) { if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len == 0 { if read_len == 0 {
config = Self::new(config_path); config = Self::new(config_path);
config.set_defaults_and_save();
} else if let Ok(data) = toml::from_str(&buf) { } else if let Ok(data) = toml::from_str(&buf) {
config = data; config = data;
config.config_path = config_path; config.config_path = config_path;
@@ -63,11 +51,9 @@ impl ProfileConfig {
) )
}); });
config = Self::new(config_path); config = Self::new(config_path);
config.set_defaults_and_save();
} }
} else { } else {
config = Self::new(config_path); config = Self::new(config_path);
config.set_defaults_and_save();
} }
config config
} }
+51 -23
View File
@@ -2,10 +2,12 @@ use std::sync::{Arc, Mutex};
use crate::error::RogError; use crate::error::RogError;
use crate::{CtrlTask, GetSupported}; use crate::{CtrlTask, GetSupported};
use async_trait::async_trait;
use log::{info, warn}; use log::{info, warn};
use rog_profiles::error::ProfileError; use rog_profiles::error::ProfileError;
use rog_profiles::{FanCurveProfiles, Profile}; use rog_profiles::{FanCurveProfiles, Profile};
use rog_supported::PlatformProfileFunctions; use rog_supported::PlatformProfileFunctions;
use smol::Executor;
use super::config::ProfileConfig; use super::config::ProfileConfig;
@@ -18,12 +20,7 @@ impl GetSupported for CtrlPlatformProfile {
fn get_supported() -> Self::A { fn get_supported() -> Self::A {
if !Profile::is_platform_profile_supported() { if !Profile::is_platform_profile_supported() {
warn!( warn!("platform_profile kernel interface not found, your laptop does not support this, or the interface is missing.");
r#"
platform_profile kernel interface not found, your laptop does not support this, or the interface is missing.
To enable profile support you require a kernel version 5.15.2 minimum.
"#
);
} }
let res = FanCurveProfiles::is_supported(); let res = FanCurveProfiles::is_supported();
@@ -33,14 +30,7 @@ To enable profile support you require a kernel version 5.15.2 minimum.
}; };
if !fan_curve_supported { if !fan_curve_supported {
info!( info!("fan curves kernel interface not found, your laptop does not support this, or the interface is missing.");
r#"
fan curves kernel interface not found, your laptop does not support this, or the interface is missing.
To enable fan-curve support you require a kernel with the following patch applied:
https://lkml.org/lkml/2021/10/23/250
This patch has been accepted upstream for 5.17 kernel release.
"#
);
} }
PlatformProfileFunctions { PlatformProfileFunctions {
@@ -66,12 +56,45 @@ impl crate::Reloadable for CtrlPlatformProfile {
} }
impl CtrlPlatformProfile { impl CtrlPlatformProfile {
pub fn new(config: ProfileConfig) -> Result<Self, RogError> { pub fn new(mut config: ProfileConfig) -> Result<Self, RogError> {
if Profile::is_platform_profile_supported() { if Profile::is_platform_profile_supported() {
info!("Device has profile control available"); info!("Device has profile control available");
if FanCurveProfiles::get_device().is_ok() { if FanCurveProfiles::get_device().is_ok() {
info!("Device has fan curves available"); info!("Device has fan curves available");
if config.fan_curves.is_none() {
let active = Profile::get_active_profile().unwrap_or(Profile::Balanced);
let dev = FanCurveProfiles::get_device()?;
let mut curves = FanCurveProfiles::default();
warn!("No default fan-curves: cycling profiles to set defaults");
Profile::set_profile(Profile::Balanced)?;
curves.read_from_dev_profile(Profile::Balanced, &dev);
info!(
"{:?}: {}",
config.active_profile,
String::from(curves.get_fan_curves_for(Profile::Balanced))
);
Profile::set_profile(Profile::Performance)?;
curves.read_from_dev_profile(Profile::Performance, &dev);
info!(
"{:?}: {}",
config.active_profile,
String::from(curves.get_fan_curves_for(Profile::Performance))
);
Profile::set_profile(Profile::Quiet)?;
curves.read_from_dev_profile(Profile::Quiet, &dev);
info!(
"{:?}: {}",
config.active_profile,
String::from(curves.get_fan_curves_for(Profile::Quiet))
);
Profile::set_profile(active)?;
config.fan_curves = Some(curves);
config.write();
info!("Set fan curve defaults");
}
} }
return Ok(CtrlPlatformProfile { config }); return Ok(CtrlPlatformProfile { config });
@@ -135,16 +158,21 @@ impl CtrlProfileTask {
} }
} }
#[async_trait]
impl CtrlTask for CtrlProfileTask { impl CtrlTask for CtrlProfileTask {
fn do_task(&self) -> Result<(), RogError> { async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
if let Ok(ref mut lock) = self.ctrl.try_lock() { let ctrl = self.ctrl.clone();
let new_profile = Profile::get_active_profile().unwrap(); self.repeating_task(666, executor, move || {
if new_profile != lock.config.active_profile { if let Ok(ref mut lock) = ctrl.try_lock() {
lock.config.active_profile = new_profile; let new_profile = Profile::get_active_profile().unwrap();
lock.write_profile_curve_to_platform()?; if new_profile != lock.config.active_profile {
lock.save_config(); lock.config.active_profile = new_profile;
lock.write_profile_curve_to_platform().unwrap();
lock.save_config();
}
} }
} })
.await;
Ok(()) Ok(())
} }
} }
+23 -25
View File
@@ -1,12 +1,14 @@
use async_trait::async_trait;
use log::warn; use log::warn;
use rog_profiles::fan_curve_set::CurveData; use rog_profiles::fan_curve_set::CurveData;
use rog_profiles::fan_curve_set::FanCurveSet; use rog_profiles::fan_curve_set::FanCurveSet;
use rog_profiles::Profile; use rog_profiles::Profile;
use zbus::Connection;
use zbus::SignalContext;
use std::sync::Arc; use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
use zbus::{dbus_interface, fdo::Error}; use zbus::{dbus_interface, fdo::Error};
use zvariant::ObjectPath;
use super::controller::CtrlPlatformProfile; use super::controller::CtrlPlatformProfile;
@@ -37,13 +39,17 @@ impl ProfileZbus {
/// Toggle to next platform_profile. Names provided by `Profiles`. /// Toggle to next platform_profile. Names provided by `Profiles`.
/// If fan-curves are supported will also activate a fan curve for profile. /// If fan-curves are supported will also activate a fan curve for profile.
fn next_profile(&mut self) { async fn next_profile(&mut self, #[zbus(signal_context)] ctxt: SignalContext<'_>) {
let mut profile = None;
if let Ok(mut ctrl) = self.inner.try_lock() { if let Ok(mut ctrl) = self.inner.try_lock() {
ctrl.set_next_profile() ctrl.set_next_profile()
.unwrap_or_else(|err| warn!("{}", err)); .unwrap_or_else(|err| warn!("{}", err));
ctrl.save_config(); ctrl.save_config();
profile = Some(ctrl.config.active_profile);
}
if let Some(profile) = profile {
Self::notify_profile(&ctxt, profile).await.ok();
} }
self.do_notification();
} }
/// Fetch the active profile name /// Fetch the active profile name
@@ -58,7 +64,12 @@ impl ProfileZbus {
} }
/// Set this platform_profile name as active /// Set this platform_profile name as active
fn set_active_profile(&self, profile: Profile) { async fn set_active_profile(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
profile: Profile,
) {
let mut tmp = None;
if let Ok(mut ctrl) = self.inner.try_lock() { if let Ok(mut ctrl) = self.inner.try_lock() {
// Read first just incase the user has modified the config before calling this // Read first just incase the user has modified the config before calling this
ctrl.config.read(); ctrl.config.read();
@@ -71,8 +82,11 @@ impl ProfileZbus {
.ok(); .ok();
ctrl.save_config(); ctrl.save_config();
tmp = Some(ctrl.config.active_profile);
}
if let Some(profile) = tmp {
Self::notify_profile(&ctxt, profile).await.ok();
} }
self.do_notification();
} }
/// Get a list of profiles that have fan-curves enabled. /// Get a list of profiles that have fan-curves enabled.
@@ -161,29 +175,13 @@ impl ProfileZbus {
} }
#[dbus_interface(signal)] #[dbus_interface(signal)]
fn notify_profile(&self, profile: &Profile) -> zbus::Result<()> {} async fn notify_profile(signal_ctxt: &SignalContext<'_>, profile: Profile) -> zbus::Result<()> {
}
impl ProfileZbus {
fn do_notification(&self) {
if let Ok(ctrl) = self.inner.try_lock() {
self.notify_profile(&ctrl.config.active_profile)
.unwrap_or_else(|err| warn!("{}", err));
}
} }
} }
#[async_trait]
impl crate::ZbusAdd for ProfileZbus { impl crate::ZbusAdd for ProfileZbus {
fn add_to_server(self, server: &mut zbus::ObjectServer) { async fn add_to_server(self, server: &mut Connection) {
server Self::add_to_server_helper(self, "/org/asuslinux/Profile", server).await;
.at(
&ObjectPath::from_str_unchecked("/org/asuslinux/Profile"),
self,
)
.map_err(|err| {
warn!("DbusFanAndCpu: add_to_server {}", err);
err
})
.ok();
} }
} }
+119 -31
View File
@@ -1,4 +1,5 @@
use crate::{config::Config, error::RogError, GetSupported}; use crate::{config::Config, error::RogError, GetSupported};
use async_trait::async_trait;
use log::{error, info, warn}; use log::{error, info, warn};
use rog_supported::RogBiosSupportedFunctions; use rog_supported::RogBiosSupportedFunctions;
use std::fs::OpenOptions; use std::fs::OpenOptions;
@@ -8,8 +9,8 @@ use std::path::Path;
use std::process::Command; use std::process::Command;
use std::sync::Arc; use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
use zbus::dbus_interface; use zbus::Connection;
use zvariant::ObjectPath; use zbus::{dbus_interface, SignalContext};
const INITRAMFS_PATH: &str = "/usr/sbin/update-initramfs"; const INITRAMFS_PATH: &str = "/usr/sbin/update-initramfs";
const DRACUT_PATH: &str = "/usr/bin/dracut"; const DRACUT_PATH: &str = "/usr/bin/dracut";
@@ -18,6 +19,9 @@ static ASUS_SWITCH_GRAPHIC_MODE: &str =
"/sys/firmware/efi/efivars/AsusSwitchGraphicMode-607005d5-3f75-4b2e-98f0-85ba66797a3e"; "/sys/firmware/efi/efivars/AsusSwitchGraphicMode-607005d5-3f75-4b2e-98f0-85ba66797a3e";
static ASUS_POST_LOGO_SOUND: &str = static ASUS_POST_LOGO_SOUND: &str =
"/sys/firmware/efi/efivars/AsusPostLogoSound-607005d5-3f75-4b2e-98f0-85ba66797a3e"; "/sys/firmware/efi/efivars/AsusPostLogoSound-607005d5-3f75-4b2e-98f0-85ba66797a3e";
static ASUS_PANEL_OD_PATH: &str = "/sys/devices/platform/asus-nb-wmi/panel_od";
static ASUS_DGPU_DISABLE_PATH: &str = "/sys/devices/platform/asus-nb-wmi/dgpu_disable";
static ASUS_EGPU_ENABLE_PATH: &str = "/sys/devices/platform/asus-nb-wmi/egpu_enable";
pub struct CtrlRogBios { pub struct CtrlRogBios {
_config: Arc<Mutex<Config>>, _config: Arc<Mutex<Config>>,
@@ -28,30 +32,34 @@ impl GetSupported for CtrlRogBios {
fn get_supported() -> Self::A { fn get_supported() -> Self::A {
RogBiosSupportedFunctions { RogBiosSupportedFunctions {
post_sound_toggle: Path::new(ASUS_POST_LOGO_SOUND).exists(), post_sound: Path::new(ASUS_POST_LOGO_SOUND).exists(),
dedicated_gfx_toggle: Path::new(ASUS_SWITCH_GRAPHIC_MODE).exists(), dedicated_gfx: Path::new(ASUS_SWITCH_GRAPHIC_MODE).exists(),
panel_overdrive: Path::new(ASUS_PANEL_OD_PATH).exists(),
dgpu_disable: Path::new(ASUS_DGPU_DISABLE_PATH).exists(),
egpu_enable: Path::new(ASUS_EGPU_ENABLE_PATH).exists(),
} }
} }
} }
#[dbus_interface(name = "org.asuslinux.Daemon")] #[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlRogBios { impl CtrlRogBios {
pub fn set_dedicated_graphic_mode(&mut self, dedicated: bool) { async fn set_dedicated_graphic_mode(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
dedicated: bool,
) {
self.set_gfx_mode(dedicated) self.set_gfx_mode(dedicated)
.map_err(|err| { .map_err(|err| {
warn!("CtrlRogBios: set_asus_switch_graphic_mode {}", err); warn!("CtrlRogBios: set_asus_switch_graphic_mode {}", err);
err err
}) })
.ok(); .ok();
self.notify_dedicated_graphic_mode(dedicated) Self::notify_dedicated_graphic_mode(&ctxt, dedicated)
.map_err(|err| { .await
warn!("CtrlRogBios: notify_asus_switch_graphic_mode {}", err);
err
})
.ok(); .ok();
} }
pub fn dedicated_graphic_mode(&self) -> i8 { fn dedicated_graphic_mode(&self) -> i8 {
Self::get_gfx_mode() Self::get_gfx_mode()
.map_err(|err| { .map_err(|err| {
warn!("CtrlRogBios: get_gfx_mode {}", err); warn!("CtrlRogBios: get_gfx_mode {}", err);
@@ -61,24 +69,27 @@ impl CtrlRogBios {
} }
#[dbus_interface(signal)] #[dbus_interface(signal)]
pub fn notify_dedicated_graphic_mode(&self, dedicated: bool) -> zbus::Result<()> {} async fn notify_dedicated_graphic_mode(
signal_ctxt: &SignalContext<'_>,
dedicated: bool,
) -> zbus::Result<()> {
}
pub fn set_post_boot_sound(&mut self, on: bool) { async fn set_post_boot_sound(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
on: bool,
) {
Self::set_boot_sound(on) Self::set_boot_sound(on)
.map_err(|err| { .map_err(|err| {
warn!("CtrlRogBios: set_post_boot_sound {}", err); warn!("CtrlRogBios: set_post_boot_sound {}", err);
err err
}) })
.ok(); .ok();
self.notify_post_boot_sound(on) Self::notify_post_boot_sound(&ctxt, on).await.ok();
.map_err(|err| {
warn!("CtrlRogBios: notify_post_boot_sound {}", err);
err
})
.ok();
} }
pub fn post_boot_sound(&self) -> i8 { fn post_boot_sound(&self) -> i8 {
Self::get_boot_sound() Self::get_boot_sound()
.map_err(|err| { .map_err(|err| {
warn!("CtrlRogBios: get_boot_sound {}", err); warn!("CtrlRogBios: get_boot_sound {}", err);
@@ -88,21 +99,59 @@ impl CtrlRogBios {
} }
#[dbus_interface(signal)] #[dbus_interface(signal)]
pub fn notify_post_boot_sound(&self, dedicated: bool) -> zbus::Result<()> {} async fn notify_post_boot_sound(ctxt: &SignalContext<'_>, on: bool) -> zbus::Result<()> {}
}
impl crate::ZbusAdd for CtrlRogBios { async fn set_panel_overdrive(
fn add_to_server(self, server: &mut zbus::ObjectServer) { &mut self,
server #[zbus(signal_context)] ctxt: SignalContext<'_>,
.at( overdrive: bool,
&ObjectPath::from_str_unchecked("/org/asuslinux/RogBios"), ) {
self, if self
) .set_panel_od(overdrive)
.map_err(|err| { .map_err(|err| {
warn!("CtrlRogBios: add_to_server {}", err); warn!("CtrlRogBios: set_panel_overdrive {}", err);
err err
}) })
.ok(); .is_ok()
{
Self::notify_panel_overdrive(&ctxt, overdrive).await.ok();
}
}
fn panel_overdrive(&self) -> i8 {
let path = ASUS_PANEL_OD_PATH;
if let Ok(mut file) = OpenOptions::new().read(true).open(path).map_err(|err| {
warn!("CtrlRogBios: panel_overdrive {}", err);
err
}) {
let mut buf = Vec::new();
file.read_to_end(&mut buf)
.map_err(|err| {
warn!("CtrlRogBios: set_panel_overdrive {}", err);
err
})
.ok();
if buf.len() >= 1 {
let tmp = String::from_utf8_lossy(&buf[0..1]);
return tmp.parse::<i8>().unwrap_or(-1);
}
}
-1
}
#[dbus_interface(signal)]
async fn notify_panel_overdrive(
signal_ctxt: &SignalContext<'_>,
overdrive: bool,
) -> zbus::Result<()> {
}
}
#[async_trait]
impl crate::ZbusAdd for CtrlRogBios {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, "/org/asuslinux/RogBios", server).await;
} }
} }
@@ -308,4 +357,43 @@ impl CtrlRogBios {
} }
Ok(()) Ok(())
} }
fn set_panel_od(&mut self, overdrive: bool) -> Result<(), RogError> {
let path = ASUS_PANEL_OD_PATH;
let mut file = OpenOptions::new().write(true).open(path).map_err(|err| {
warn!("CtrlRogBios: set_panel_overdrive {}", err);
err
})?;
let s = if overdrive { '1' } else { '0' };
file.write(&[s as u8]).map_err(|err| {
warn!("CtrlRogBios: set_panel_overdrive {}", err);
err
})?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::CtrlRogBios;
use crate::config::Config;
use std::sync::{Arc, Mutex};
#[test]
#[ignore = "Must be manually tested"]
fn set_multizone_4key_config() {
let config = Config::default();
let controller = CtrlRogBios {
_config: Arc::new(Mutex::new(config)),
};
let res = controller.panel_overdrive();
assert_eq!(res, 1);
// controller.set_panel_od(false).unwrap();
// let res = controller.panel_overdrive();
// assert_eq!(res, 0);
}
} }
+6 -14
View File
@@ -1,8 +1,8 @@
use log::warn; use async_trait::async_trait;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use zbus::dbus_interface; use zbus::dbus_interface;
use zvariant::ObjectPath; use zbus::Connection;
use zvariant_derive::Type; use zvariant::Type;
use crate::{ use crate::{
ctrl_anime::CtrlAnime, ctrl_aura::controller::CtrlKbdLed, ctrl_charge::CtrlCharge, ctrl_anime::CtrlAnime, ctrl_aura::controller::CtrlKbdLed, ctrl_charge::CtrlCharge,
@@ -30,18 +30,10 @@ impl SupportedFunctions {
} }
} }
#[async_trait]
impl crate::ZbusAdd for SupportedFunctions { impl crate::ZbusAdd for SupportedFunctions {
fn add_to_server(self, server: &mut zbus::ObjectServer) { async fn add_to_server(self, server: &mut Connection) {
server Self::add_to_server_helper(self, "/org/asuslinux/Supported", server).await;
.at(
&ObjectPath::from_str_unchecked("/org/asuslinux/Supported"),
self,
)
.map_err(|err| {
warn!("SupportedFunctions: add_to_server {}", err);
err
})
.ok();
} }
} }
+30 -50
View File
@@ -1,14 +1,13 @@
use std::env;
use std::error::Error; use std::error::Error;
use std::io::Write; use std::io::Write;
use std::sync::Arc; use std::sync::{Arc, Mutex};
use std::sync::Mutex;
use std::thread::sleep;
use std::time::Duration;
use std::{env, thread};
use ::zbus::{fdo, Connection, ObjectServer}; use ::zbus::Connection;
use daemon::ctrl_profiles::controller::CtrlProfileTask;
use log::LevelFilter; use log::LevelFilter;
use log::{error, info, warn}; use log::{error, info, warn};
use smol::Executor;
use daemon::ctrl_anime::config::AnimeConfig; use daemon::ctrl_anime::config::AnimeConfig;
use daemon::ctrl_anime::zbus::CtrlAnimeZbus; use daemon::ctrl_anime::zbus::CtrlAnimeZbus;
@@ -19,9 +18,7 @@ use daemon::ctrl_aura::controller::{
}; };
use daemon::ctrl_charge::CtrlCharge; use daemon::ctrl_charge::CtrlCharge;
use daemon::ctrl_profiles::config::ProfileConfig; use daemon::ctrl_profiles::config::ProfileConfig;
use daemon::ctrl_profiles::controller::CtrlProfileTask;
use daemon::ctrl_rog_bios::CtrlRogBios; use daemon::ctrl_rog_bios::CtrlRogBios;
use daemon::error::RogError;
use daemon::{ use daemon::{
config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported, config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported,
}; };
@@ -64,25 +61,25 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
info!(" rog-profiles v{}", rog_profiles::VERSION); info!(" rog-profiles v{}", rog_profiles::VERSION);
info!("rog-supported v{}", rog_supported::VERSION); info!("rog-supported v{}", rog_supported::VERSION);
start_daemon()?; let mut executor = Executor::new();
smol::block_on(start_daemon(&mut executor))?;
Ok(()) Ok(())
} }
/// The actual main loop for the daemon /// The actual main loop for the daemon
fn start_daemon() -> Result<(), Box<dyn Error>> { async fn start_daemon(executor: &mut Executor<'_>) -> Result<(), Box<dyn Error>> {
let supported = SupportedFunctions::get_supported(); let supported = SupportedFunctions::get_supported();
print_board_info(); print_board_info();
println!("{}", serde_json::to_string_pretty(&supported)?); println!("{}", serde_json::to_string_pretty(&supported)?);
// Start zbus server // Start zbus server
let connection = Connection::new_system()?; let mut connection = Connection::system().await?;
let fdo_connection = fdo::DBusProxy::new(&connection)?;
let mut object_server = ObjectServer::new(&connection);
let config = Config::load(); let config = Config::load();
let config = Arc::new(Mutex::new(config)); let config = Arc::new(Mutex::new(config));
supported.add_to_server(&mut object_server); supported.add_to_server(&mut connection).await;
match CtrlRogBios::new(config.clone()) { match CtrlRogBios::new(config.clone()) {
Ok(mut ctrl) => { Ok(mut ctrl) => {
@@ -90,20 +87,23 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
ctrl.reload() ctrl.reload()
.unwrap_or_else(|err| warn!("Battery charge limit: {}", err)); .unwrap_or_else(|err| warn!("Battery charge limit: {}", err));
// Then register to dbus server // Then register to dbus server
ctrl.add_to_server(&mut object_server); ctrl.add_to_server(&mut connection).await;
} }
Err(err) => { Err(err) => {
error!("rog_bios_control: {}", err); error!("rog_bios_control: {}", err);
} }
} }
match CtrlCharge::new(config) { match CtrlCharge::new(config.clone()) {
Ok(mut ctrl) => { Ok(mut ctrl) => {
// Do a reload of any settings // Do a reload of any settings
ctrl.reload() ctrl.reload()
.unwrap_or_else(|err| warn!("Battery charge limit: {}", err)); .unwrap_or_else(|err| warn!("Battery charge limit: {}", err));
// Then register to dbus server // Then register to dbus server
ctrl.add_to_server(&mut object_server); ctrl.add_to_server(&mut connection).await;
let task = CtrlCharge::new(config)?;
task.create_tasks(executor).await.ok();
} }
Err(err) => { Err(err) => {
error!("charge_control: {}", err); error!("charge_control: {}", err);
@@ -118,17 +118,11 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
.unwrap_or_else(|err| warn!("Profile control: {}", err)); .unwrap_or_else(|err| warn!("Profile control: {}", err));
let tmp = Arc::new(Mutex::new(ctrl)); let tmp = Arc::new(Mutex::new(ctrl));
ProfileZbus::new(tmp.clone()).add_to_server(&mut object_server); let task = CtrlProfileTask::new(tmp.clone());
task.create_tasks(executor).await.ok();
let task = CtrlProfileTask::new(tmp); let task = ProfileZbus::new(tmp.clone());
thread::Builder::new().name("profile tasks".into()).spawn( task.add_to_server(&mut connection).await;
move || -> Result<(), RogError> {
loop {
task.do_task()?;
sleep(Duration::from_millis(100));
}
},
)?;
} }
Err(err) => { Err(err) => {
error!("Profile control: {}", err); error!("Profile control: {}", err);
@@ -148,16 +142,10 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
.unwrap_or_else(|err| warn!("AniMe: {}", err)); .unwrap_or_else(|err| warn!("AniMe: {}", err));
let zbus = CtrlAnimeZbus(inner.clone()); let zbus = CtrlAnimeZbus(inner.clone());
zbus.add_to_server(&mut object_server); zbus.add_to_server(&mut connection).await;
let task = CtrlAnimeTask::new(inner); let task = CtrlAnimeTask::new(inner).await;
thread::Builder::new().name("anime tasks".into()).spawn( task.create_tasks(executor).await.ok();
move || -> Result<(), RogError> {
loop {
task.do_task()?;
}
},
)?;
} }
Err(err) => { Err(err) => {
error!("AniMe control: {}", err); error!("AniMe control: {}", err);
@@ -175,16 +163,12 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
.reload() .reload()
.unwrap_or_else(|err| warn!("Keyboard LED control: {}", err)); .unwrap_or_else(|err| warn!("Keyboard LED control: {}", err));
CtrlKbdLedZbus::new(inner.clone()).add_to_server(&mut object_server); CtrlKbdLedZbus::new(inner.clone())
.add_to_server(&mut connection)
.await;
let task = CtrlKbdLedTask::new(inner); let task = CtrlKbdLedTask::new(inner);
thread::Builder::new().name("keyboard tasks".into()).spawn( task.create_tasks(executor).await.ok();
move || -> Result<(), RogError> {
loop {
task.do_task()?;
}
},
)?;
} }
Err(err) => { Err(err) => {
error!("Keyboard control: {}", err); error!("Keyboard control: {}", err);
@@ -192,12 +176,8 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
} }
// Request dbus name after finishing initalizing all functions // Request dbus name after finishing initalizing all functions
fdo_connection.request_name(DBUS_NAME, fdo::RequestNameFlags::ReplaceExisting.into())?; connection.request_name(DBUS_NAME).await?;
// Loop to check errors and iterate zbus server
loop { loop {
if let Err(err) = object_server.try_handle_next() { smol::block_on(executor.tick());
error!("{}", err);
}
} }
} }
+15
View File
@@ -1,3 +1,4 @@
use rog_anime::error::AnimeError;
use rog_profiles::error::ProfileError; use rog_profiles::error::ProfileError;
use std::convert::From; use std::convert::From;
use std::fmt; use std::fmt;
@@ -23,6 +24,10 @@ pub enum RogError {
Io(std::io::Error), Io(std::io::Error),
Zbus(zbus::Error), Zbus(zbus::Error),
ChargeLimit(u8), ChargeLimit(u8),
AuraEffectNotSupported,
NoAuraKeyboard,
NoAuraNode,
Anime(AnimeError),
} }
impl fmt::Display for RogError { impl fmt::Display for RogError {
@@ -48,6 +53,10 @@ impl fmt::Display for RogError {
RogError::Io(detail) => write!(f, "std::io error: {}", detail), RogError::Io(detail) => write!(f, "std::io error: {}", detail),
RogError::Zbus(detail) => write!(f, "Zbus error: {}", detail), RogError::Zbus(detail) => write!(f, "Zbus error: {}", detail),
RogError::ChargeLimit(value) => write!(f, "Invalid charging limit, not in range 20-100%: {}", value), RogError::ChargeLimit(value) => write!(f, "Invalid charging limit, not in range 20-100%: {}", value),
RogError::AuraEffectNotSupported => write!(f, "Aura effect not supported"),
RogError::NoAuraKeyboard => write!(f, "No supported Aura keyboard"),
RogError::NoAuraNode => write!(f, "No Aura keyboard node found"),
RogError::Anime(deets) => write!(f, "AniMe Matrix error: {}", deets),
} }
} }
} }
@@ -60,6 +69,12 @@ impl From<ProfileError> for RogError {
} }
} }
impl From<AnimeError> for RogError {
fn from(err: AnimeError) -> Self {
RogError::Anime(err)
}
}
impl From<zbus::Error> for RogError { impl From<zbus::Error> for RogError {
fn from(err: zbus::Error) -> Self { fn from(err: zbus::Error) -> Self {
RogError::Zbus(err) RogError::Zbus(err)
+73 -9
View File
@@ -1,10 +1,11 @@
use log::{info, warn}; use log::{info, warn};
use rog_aura::AuraModeNum; use rog_aura::{AuraModeNum, AuraZone};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io::Read; use std::io::Read;
pub const ASUS_LED_MODE_CONF: &str = "/etc/asusd/asusd-ledmodes.toml"; pub const ASUS_LED_MODE_CONF: &str = "/etc/asusd/asusd-ledmodes.toml";
pub const ASUS_LED_MODE_USER_CONF: &str = "/etc/asusd/asusd-user-ledmodes.toml";
pub const ASUS_KEYBOARD_DEVICES: [&str; 4] = ["1866", "1869", "1854", "19b6"]; pub const ASUS_KEYBOARD_DEVICES: [&str; 4] = ["1866", "1869", "1854", "19b6"];
pub fn print_board_info() { pub fn print_board_info() {
@@ -32,17 +33,18 @@ pub fn print_modes(supported_modes: &[u8]) {
} }
} }
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Default, Deserialize, Serialize)]
struct LedSupportFile { struct LedSupportFile {
led_data: Vec<LaptopLedData>, led_data: Vec<LaptopLedData>,
} }
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Default, Deserialize, Serialize)]
#[serde(default)]
pub struct LaptopLedData { pub struct LaptopLedData {
pub prod_family: String, pub prod_family: String,
pub board_names: Vec<String>, pub board_names: Vec<String>,
pub standard: Vec<AuraModeNum>, pub standard: Vec<AuraModeNum>,
pub multizone: bool, pub multizone: Vec<AuraZone>,
pub per_key: bool, pub per_key: bool,
} }
@@ -62,7 +64,7 @@ impl LaptopLedData {
prod_family, prod_family,
board_names: vec![board_name], board_names: vec![board_name],
standard: vec![], standard: vec![],
multizone: false, multizone: vec![],
per_key: false, per_key: false,
} }
} }
@@ -85,19 +87,81 @@ impl LedSupportFile {
} }
fn load_from_config() -> Option<Self> { fn load_from_config() -> Option<Self> {
let mut loaded = false;
let mut data = LedSupportFile::default();
// Load user configs first so they are first to be checked
if let Ok(mut file) = OpenOptions::new().read(true).open(&ASUS_LED_MODE_USER_CONF) {
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {
if l == 0 {
warn!("{} is empty", ASUS_LED_MODE_USER_CONF);
} else {
if let Ok(mut tmp) = toml::from_str::<LedSupportFile>(&buf) {
data.led_data.append(&mut tmp.led_data);
}
info!(
"Loaded user-defined LED support data from {}",
ASUS_LED_MODE_USER_CONF
);
}
}
}
// Load and append the default LED support data
if let Ok(mut file) = OpenOptions::new().read(true).open(&ASUS_LED_MODE_CONF) { if let Ok(mut file) = OpenOptions::new().read(true).open(&ASUS_LED_MODE_CONF) {
let mut buf = String::new(); let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) { if let Ok(l) = file.read_to_string(&mut buf) {
if l == 0 { if l == 0 {
warn!("{} is empty", ASUS_LED_MODE_CONF); warn!("{} is empty", ASUS_LED_MODE_CONF);
} else { } else {
return Some(toml::from_str(&buf).unwrap_or_else(|_| { let mut tmp: LedSupportFile = toml::from_str(&buf)
panic!("Could not deserialise {}", ASUS_LED_MODE_CONF) .unwrap_or_else(|_| panic!("Could not deserialise {}", ASUS_LED_MODE_CONF));
})); data.led_data.append(&mut tmp.led_data);
loaded = true;
info!(
"Loaded default LED support data from {}",
ASUS_LED_MODE_CONF
);
} }
} }
} }
warn!("Does {} exist?", ASUS_LED_MODE_CONF);
if loaded {
return Some(data);
}
warn!("Does {} exist?", ASUS_LED_MODE_USER_CONF);
None None
} }
} }
#[cfg(test)]
mod tests {
use std::{fs::OpenOptions, io::Read, path::PathBuf};
use super::LaptopLedData;
use rog_aura::{AuraModeNum, AuraZone};
#[test]
fn check_data_parse() {
let led = LaptopLedData {
prod_family: "Test".to_owned(),
board_names: vec!["Test".to_owned()],
standard: vec![AuraModeNum::Static],
multizone: vec![AuraZone::Key1, AuraZone::Logo, AuraZone::BarLeft],
per_key: false,
};
let toml = toml::to_string_pretty(&led).unwrap();
println!("{toml}");
let mut data = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
data.push("../data/asusd-ledmodes.toml");
let mut file = OpenOptions::new().read(true).open(&data).unwrap();
let mut buf = String::new();
file.read_to_string(&mut buf).unwrap();
let x = toml::to_string_pretty(&buf).unwrap();
println!("{x}");
}
}
+45 -3
View File
@@ -29,9 +29,15 @@ pub mod ctrl_supported;
pub mod error; pub mod error;
use std::time::Duration;
use crate::error::RogError; use crate::error::RogError;
use async_trait::async_trait;
use config::Config; use config::Config;
use zbus::ObjectServer; use log::warn;
use smol::{stream::StreamExt, Executor, Timer};
use zbus::Connection;
use zvariant::ObjectPath;
pub static VERSION: &str = env!("CARGO_PKG_VERSION"); pub static VERSION: &str = env!("CARGO_PKG_VERSION");
@@ -39,12 +45,48 @@ pub trait Reloadable {
fn reload(&mut self) -> Result<(), RogError>; fn reload(&mut self) -> Result<(), RogError>;
} }
#[async_trait]
pub trait ZbusAdd { pub trait ZbusAdd {
fn add_to_server(self, server: &mut ObjectServer); async fn add_to_server(self, server: &mut Connection);
async fn add_to_server_helper(
iface: impl zbus::Interface,
path: &str,
server: &mut Connection,
) {
server
.object_server()
.at(&ObjectPath::from_str_unchecked(path), iface)
.await
.map_err(|err| {
warn!("{}: add_to_server {}", path, err);
err
})
.ok();
}
} }
/// Set up a task to run on the async executor
#[async_trait]
pub trait CtrlTask { pub trait CtrlTask {
fn do_task(&self) -> Result<(), RogError>; /// Implement to set up various tasks that may be required, using the `Executor`.
/// No blocking loops are allowed, or they must be run on a separate thread.
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError>;
/// Create a timed repeating task
async fn repeating_task(
&self,
millis: u64,
executor: &mut Executor,
mut task: impl FnMut() + Send + 'static,
) {
let timer = Timer::interval(Duration::from_millis(millis));
executor
.spawn(async move {
timer.for_each(|_| task()).await;
})
.detach();
}
} }
pub trait CtrlTaskComplex { pub trait CtrlTaskComplex {
-50
View File
@@ -1,50 +0,0 @@
function _asusctl() {
local line
_arguments -C \
{-h,--help}'[print help message]' \
{-v,--version}'[print version number]' \
{-k,--kbd-bright}':[Set keyboard brightness (off, low, med, high)]' \
{-p,--pwr-profile}':[Set power profile (silent, normal, boost)]' \
{-c,--chg-limit}':[Set charging limit (20-100)]' \
': :((led-mode\:"Set the keyboard lighting from built-in modes" profile\:"Create and configure profiles" graphics\:"Set the graphics mode"))' \
'*::arg:->args'
case $line[1] in
led-mode)
_arguments ': :((static\:"set a single static colour"
breathe\:"pulse between one or two colours"
strobe\:"strobe through all colours"
rainbow\:"rainbow cycling in one of four directions"
star\:"rain pattern mimicking raindrops"
rain\:"rain pattern of three preset colours"
highlight\:"pressed keys are highlighted to fade"
laser\:"pressed keys generate horizontal laser"
ripple\:"pressed keys ripple outwards like a splash"
pulse\:"set a rapid pulse"
comet\:"set a vertical line zooming from left"
flash\:"set a wide vertical line zooming from left"
multi-static\:"4-zone multi-colour"))' \
{-h,--help}'[print help message]' \
'-c:[set the RGB value e.g, ff00ff]' \
'-s:[set the speed (low, med, high)]'
;;
profile)
_arguments {-h,--help}'[print help message]' \
{-c,--create}"[create the profile if it doesn't exist]" \
{-t,--turbo}':[enable or disable cpu turbo]' \
{-m,--min-percentage}':[set min cpu scaling (intel)]' \
{-M,--max-percentage}':[set max cpu scaling (intel)]' \
{-p,--preset}':[<silent, normal, boost>]' \
{-C,--curve}':[set fan curve]'
;;
graphics)
_arguments {-h,--help}'[print help message]' \
{-m,--mode}':[Set graphics mode (nvidia, hybrid, compute, integrated)]' \
{-g,--get}'[Get the current mode]' \
{-p,--pow}'[Get the current power status]' \
{-f,--force}'[Do not ask for confirmation]'
;;
esac
}
compdef _asusctl asusctl
-13
View File
@@ -1,13 +0,0 @@
[Unit]
Description=ASUS Notebook Control
After=basic.target syslog.target
[Service]
Environment=IS_SERVICE=1
ExecStart=/usr/bin/asusd
Restart=on-failure
Type=dbus
BusName=org.asuslinux.Daemon
[Install]
WantedBy=multi-user.target
+73 -23
View File
@@ -1,99 +1,142 @@
[[led_data]]
prod_family = "TUF"
board_names = ["FA507"]
standard = ["Static", "Breathe", "Strobe", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "TUF Gaming"
board_names = ["FX505D"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = []
per_key = false
[[led_data]] [[led_data]]
prod_family = "Zephyrus S" prod_family = "Zephyrus S"
board_names = ["GX502", "GX701", "G531", "GL531", "G532"] board_names = ["GX502", "GX701", "G531", "GL531", "G532"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"] standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = false multizone = []
per_key = true per_key = true
[[led_data]] [[led_data]]
prod_family = "Zephyrus M" prod_family = "Zephyrus M"
board_names = ["GU502GV"] board_names = ["GU502GV"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"] standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = false multizone = []
per_key = true per_key = true
[[led_data]] [[led_data]]
prod_family = "Zephyrus M" prod_family = "Zephyrus M"
board_names = ["GM501GS"] board_names = ["GM501GS"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"] standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = true multizone = ["Key1", "Key2", "Key3", "Key4"]
per_key = false per_key = false
[[led_data]] [[led_data]]
prod_family = "ROG Zephyrus M15" prod_family = "ROG Zephyrus M15"
board_names = ["GU502LW"] board_names = ["GU502LW", "GU502LV"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"] standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = false multizone = []
per_key = true per_key = true
[[led_data]] [[led_data]]
prod_family = "ROG Zephyrus M15" prod_family = "ROG Zephyrus M15"
board_names = ["GU502LU"] board_names = ["GU502LU"]
standard = ["Static", "Breathe", "Strobe", "Pulse"] standard = ["Static", "Breathe", "Strobe", "Pulse"]
multizone = false multizone = []
per_key = false per_key = false
[[led_data]]
prod_family = "ROG Zephyrus S17"
board_names = ["GX703HS"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = []
per_key = false
[[led_data]] [[led_data]]
prod_family = "Zephyrus" prod_family = "Zephyrus"
board_names = ["GM501GM", "GX531"] board_names = ["GM501GM", "GX531"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"] standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = true multizone = ["Key1", "Key2", "Key3", "Key4"]
per_key = false per_key = false
[[led_data]] [[led_data]]
prod_family = "ROG Strix" prod_family = "ROG Strix"
board_names = ["G531GW", "G533QR", "G533QS", "G733QS", "G733QR", "G513QR", "G713QR"] board_names = ["G531GW", "G533QR", "G533QS", "G733QS", "G733QR", "G513QR", "G713QR", "G513QM", "G713IC"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"] standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = false multizone = []
per_key = true per_key = true
[[led_data]] [[led_data]]
prod_family = "ROG Strix" prod_family = "ROG Strix"
board_names = ["G513QE", "GX531", "G512LV", "G712LV", "G712LW", "G513IH", "G513QY", "G713QM"] board_names = ["G513QE", "GX531", "G512LV", "G712LV", "G712LW", "G513IH", "G513QY", "G713QM", "G512", "G713RW"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"] standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = true multizone = ["Key1", "Key2", "Key3", "Key4"]
per_key = false per_key = false
[[led_data]] [[led_data]]
prod_family = "ROG Strix" prod_family = "ROG Strix"
board_names = ["G512LI", "G712LI", "G531GD"] board_names = ["G512LI", "G712LI", "G531GD"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"] standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = false multizone = []
per_key = false per_key = false
[[led_data]]
prod_family = "ROG Strix"
board_names = ["G513IM"]
standard = ["Flash", "Static", "Breathe", "Strobe", "Rainbow"]
multizone = []
per_key = true
[[led_data]] [[led_data]]
prod_family = "Strix" prod_family = "Strix"
board_names = ["G731GV", "G731GW", "G531GV"] board_names = ["G731GV", "G731GW", "G531GV"]
standard = ["Static", "Breathe", "Strobe", "Rainbow"] standard = ["Static", "Breathe", "Strobe", "Rainbow"]
multizone = true multizone = ["Key1", "Key2", "Key3", "Key4"]
per_key = false
[[led_data]]
prod_family = "Strix"
board_names = ["GL504G"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = ["Key1", "Key2", "Key3", "Key4", "Logo", "BarLeft", "BarRight"]
per_key = false per_key = false
[[led_data]] [[led_data]]
prod_family = "Strix" prod_family = "Strix"
board_names = ["G731GT", "G731GU", "G531GT", "G531GU"] board_names = ["G731GT", "G731GU", "G531GT", "G531GU"]
standard = ["Static", "Breathe", "Strobe", "Rainbow"] standard = ["Static", "Breathe", "Strobe", "Rainbow"]
multizone = false multizone = []
per_key = false per_key = false
[[led_data]] [[led_data]]
prod_family = "Strix Scar" prod_family = "Strix Scar"
board_names = ["G531", "G731"] board_names = ["G531", "G731"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"] standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = true multizone = ["Key1", "Key2", "Key3", "Key4"]
per_key = true per_key = true
[[led_data]] [[led_data]]
prod_family = "ROG" prod_family = "ROG"
board_names = ["GL553VE"] board_names = ["GL553VE"]
standard = ["Static", "Breathe", "Strobe"] standard = ["Static", "Breathe", "Strobe"]
multizone = true multizone = ["Key1", "Key2", "Key3", "Key4"]
per_key = false per_key = false
[[led_data]] [[led_data]]
prod_family = "ROG Zephyrus G14" prod_family = "ROG Zephyrus G14"
board_names = ["GA401Q"] board_names = ["GA401Q"]
standard = ["Static", "Breathe", "Pulse"] standard = ["Static", "Breathe", "Pulse"]
multizone = false multizone = []
per_key = false
[[led_data]]
prod_family = "ROG Zephyrus G14"
board_names = ["GA402R"]
standard = ["Static", "Breathe", "Pulse", "Rainbow"]
multizone = []
per_key = false per_key = false
# GA503QE at higher priority (first match) than GA503Q # GA503QE at higher priority (first match) than GA503Q
@@ -101,33 +144,40 @@ per_key = false
prod_family = "ROG Zephyrus G15" prod_family = "ROG Zephyrus G15"
board_names = ["GA503QE"] board_names = ["GA503QE"]
standard = ["Static", "Breathe", "Pulse"] standard = ["Static", "Breathe", "Pulse"]
multizone = false multizone = []
per_key = false per_key = false
[[led_data]] [[led_data]]
prod_family = "ROG Zephyrus G15" prod_family = "ROG Zephyrus G15"
board_names = ["GA503Q"] board_names = ["GA503Q", "GA503R"]
standard = ["Static", "Breathe", "Pulse", "Rainbow", "Strobe"] standard = ["Static", "Breathe", "Pulse", "Rainbow", "Strobe"]
multizone = false multizone = []
per_key = false per_key = false
[[led_data]] [[led_data]]
prod_family = "ROG Zephyrus" prod_family = "ROG Zephyrus"
board_names = ["GX550L"] board_names = ["GX550L"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"] standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = false multizone = []
per_key = true per_key = true
[[led_data]] [[led_data]]
prod_family = "ROG Zephyrus Duo 15 SE" prod_family = "ROG Zephyrus Duo 15 SE"
board_names = ["GX551Q"] board_names = ["GX551Q"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"] standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = false multizone = []
per_key = true per_key = true
[[led_data]] [[led_data]]
prod_family = "ROG Flow X13" prod_family = "ROG Flow X13"
board_names = ["GV301QH", "GV301QE"] board_names = ["GV301QH", "GV301QE"]
standard = ["Static", "Breathe", "Pulse"] standard = ["Static", "Breathe", "Pulse"]
multizone = false multizone = []
per_key = false
[[led_data]]
prod_family = "ROG Strix"
board_names = ["G513IC"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = []
per_key = false per_key = false
+19 -2
View File
@@ -1,2 +1,19 @@
ACTION=="add|change", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="0b05", ENV{ID_MODEL_ID}=="1[89][a-zA-Z0-9][a-zA-Z0-9]|193b", ENV{ID_TYPE}=="hid", TAG+="systemd", ENV{SYSTEMD_WANTS}="asusd.service" #ACTION=="add|change", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="0b05", ENV{ID_MODEL_ID}=="1[89][a-zA-Z0-9][a-zA-Z0-9]|193b", ENV{ID_TYPE}=="hid", TAG+="systemd", ENV{SYSTEMD_WANTS}="asusd.service"
ACTION=="add|remove", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="0b05", ENV{ID_MODEL_ID}=="1[89][a-zA-Z0-9][a-zA-Z0-9]|193b", RUN+="systemctl restart asusd.service" #ACTION=="add|remove", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="0b05", ENV{ID_MODEL_ID}=="1[89][a-zA-Z0-9][a-zA-Z0-9]|193b", RUN+="systemctl restart asusd.service"
ENV{DMI_VENDOR}="$attr{[dmi/id]sys_vendor}"
ENV{DMI_VENDOR}!="ASUSTeK COMPUTER INC.", GOTO="asusd_end"
ENV{DMI_FAMILY}="$attr{[dmi/id]product_family}"
ENV{DMI_FAMILY}=="*TUF*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*ROG*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Zephyrus*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Strix*", GOTO="asusd_start"
# No match so
GOTO="asusd_end"
LABEL="asusd_start"
ACTION=="add|change", DRIVER=="asus-nb-wmi", TAG+="systemd", ENV{SYSTEMD_WANTS}="asusd.service"
ACTION=="add|remove", DRIVER=="asus-nb-wmi", TAG+="systemd", RUN+="systemctl restart asusd.service"
LABEL="asusd_end"
-77
View File
@@ -1,77 +0,0 @@
# Author: AlenPaulVarghese <alenpaul2001@gmail.com>
set -l progname asusctl
set -l noopt "not __fish_contains_opt -s -s h -s v -s s -s k -s f -s c help version show-supported kbd-bright fan-mode chg-limit; and not __fish_seen_subcommand_from led-mode profile graphics;"
set -l gmod_options '__fish_contains_opt -s m mode;'
set -l fan_options '__fish_contains_opt -s f fan-mode;'
set -l led_options '__fish_seen_subcommand_from led-mode;'
set -l profile_options '__fish_seen_subcommand_from profile;'
set -l keyboard_options '__fish_contains_opt -s k kbd-bright;'
set -l graphics_options '__fish_seen_subcommand_from graphics;'
set -l fan_modes 'silent normal boost'
set -l brightness_modes 'off low med high'
set -l led_modes 'static breathe strobe rainbow comet'
set -l graphics_modes 'nvidia hybird compute integrated'
complete -c $progname -e
complete -c $progname -f
# asusctl completion
complete -c $progname -s h -f -l help -n "$noopt" -d "print help message"
complete -c $progname -s v -f -l version -n "$noopt" -d "show program version number"
complete -c $progname -s s -f -l show-supported -n "$noopt" -d "show supported functions of this laptop"
complete -c $progname -s k -f -l kbd-bright -n "$noopt" -d "set led brightness"
complete -c $progname -s f -f -l fan-mode -n "$noopt" -d "set fan mode independent of profile"
complete -c $progname -s c -f -l chg-limit -n "$noopt" -d "set charge limit <20-100>"
complete -c $progname -f -a "led-mode" -n "$noopt" -d "Set the keyboard lighting from built-in modes"
complete -c $progname -f -a "profile" -n "$noopt" -d "Create and configure profiles"
complete -c $progname -f -a "graphics" -n "$noopt" -d "Set the graphics mode"
# brightness completion
complete -c $progname -n "$keyboard_options" -d "available brightness modes" -a "$brightness_modes"
# fan completion
complete -c $progname -n "$fan_options" -d "available fan modes" -a $fan_modes
# graphics completion
set -l gopt 'not __fish_contains_opt -s h -s g -s m -s p help mode get pow;'
complete -c $progname -n "$graphics_options and $gopt" -a "-h" -d "print help message"
complete -c $progname -n "$graphics_options and $gopt" -a "-g" -d "Get the current mode"
complete -c $progname -s h -f -l help -n "$graphics_options and $gopt" -d "print help message"
complete -c $progname -s m -f -l mode -n "$graphics_options and $gopt" -d "Set graphics mode: <nvidia, hybrid, compute, integrated>"
complete -c $progname -s g -f -l get -n "$graphics_options and $gopt" -d "Get the current mode"
complete -c $progname -s p -f -l pow -n "$graphics_options and $gopt" -d "Get the current power status"
complete -c $progname -n "$graphics_options and $gmod_options" -d "available graphics modes" -a "$graphics_modes"
# led-mode completion
complete -c $progname -n "$led_options" -a "-h" -d "print help message"
complete -c $progname -n "$led_options" -a "-n" -d "switch to next aura mode"
complete -c $progname -s h -f -l help -n "$led_options" -d "print help message"
complete -c $progname -s n -f -l next-mode -n "$led_options" -d "switch to nex aura mode"
complete -c $progname -s p -f -l prev-mode -n "$led_options" -d "switch to previous aura mode"
complete -c $progname -n "$led_options" -d "available led modes" -a "$led_modes"
# profile completion
set -l popt 'not __fish_contains_opt -s h -s n -s c -s t -s m -s M -s f help next create turbo min-percentage max-percentage fan-preset;'
complete -c $progname -n "$profile_options and $popt" -a "-h" -d "print help message"
complete -c $progname -n "$profile_options and $popt" -a "-n" -d "toggle to next profile in list"
complete -c $progname -s h -f -l help -n "$profile_options and $popt" -d "print help message"
complete -c $progname -s n -f -l next -n "$profile_options and $popt" -d "toggle to next profile in list"
complete -c $progname -s c -f -l create -n "$profile_options and $popt" -d "create the profile if it doesn't exist"
complete -c $progname -s t -f -l turbo -n "$profile_options and $popt" -d "enable or disable cpu turbo"
complete -c $progname -s m -f -l min-percentage -n "$profile_options and $popt" -d "set min cpu scaling (intel)"
complete -c $progname -s M -f -l max-percentage -n "$profile_options and $popt" -d "set max cpu scaling (intel)"
complete -c $progname -s f -f -l fan-preset -n "$profile_options and $popt" -d "<silent, normal, boost>"
complete -c $progname -n "$profile_option and __fish_contains_opt fan-preset" -d "available fan modes" -a $fan_modes
+40 -12
View File
@@ -28,18 +28,53 @@ impl CtrlAnime {
} }
``` ```
The task trait: The task trait. There are three ways to implement this:
```rust ```rust
pub struct CtrlAnimeTask(Arc<Mutex<CtrlAnime>>); pub struct CtrlAnimeTask(Arc<Mutex<CtrlAnime>>);
impl crate::CtrlTask for CtrlAnimeTask { impl crate::CtrlTask for CtrlAnimeTask {
fn do_task(&self) -> Result<(), RogError> { // This will run once only
fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
if let Ok(lock) = self.inner.try_lock() { if let Ok(lock) = self.inner.try_lock() {
<some action> <some action>
} }
Ok(()) Ok(())
} }
// This will run until the notification stream closes (which in most cases will be never)
fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
let connection = Connection::system().await.unwrap();
let manager = ManagerProxy::new(&connection).await.unwrap();
let inner = self.inner.clone();
executor
.spawn(async move {
// A notification from logind dbus interface
if let Ok(p) = manager.receive_prepare_for_sleep().await {
// A stream that will continuously output events
p.for_each(|_| {
if let Ok(lock) = inner.try_lock() {
// Do stuff here
}
})
.await;
}
})
.detach();
}
// This task will run every 500 milliseconds
fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
let inner = self.inner.clone();
// This is a provided free trait to help set up a repeating task
self.repeating_task(500, executor, move || {
if let Ok(lock) = inner.try_lock() {
// Do stuff here
}
})
.await;
}
} }
``` ```
@@ -61,18 +96,11 @@ The Zbus requirements:
```rust ```rust
pub struct CtrlAnimeZbus(Arc<Mutex<CtrlAnime>>); pub struct CtrlAnimeZbus(Arc<Mutex<CtrlAnime>>);
#[async_trait]
impl crate::ZbusAdd for CtrlAnimeZbus { impl crate::ZbusAdd for CtrlAnimeZbus {
fn add_to_server(self, server: &mut zbus::ObjectServer) { fn add_to_server(self, server: &mut zbus::ObjectServer) {
server // This is a provided free helper trait with pre-set body. It will move self in-to.
.at( Self::add_to_server_helper(self, "/org/asuslinux/Anime", server).await;
&ObjectPath::from_str_unchecked("/org/asuslinux/Anime"),
self,
)
.map_err(|err| {
warn!("CtrlAnimeDisplay: add_to_server {}", err);
err
})
.ok();
} }
} }
+11 -6
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "rog_anime" name = "rog_anime"
version = "1.3.0" version = "1.3.5"
license = "MPL-2.0" license = "MPL-2.0"
readme = "README.md" readme = "README.md"
authors = ["Luke <luke@ljones.dev>"] authors = ["Luke <luke@ljones.dev>"]
@@ -13,18 +13,23 @@ edition = "2018"
exclude = ["data"] exclude = ["data"]
[features] [features]
default = ["dbus"] default = ["dbus", "detect"]
dbus = ["zvariant", "zvariant_derive"] dbus = ["zvariant", "zbus"]
detect = ["udev", "sysfs-class"]
[dependencies] [dependencies]
png_pong = "^0.8.0" png_pong = "^0.8.0"
pix = "0.13" pix = "0.13"
gif = "^0.11.2" gif = "^0.11.2"
log = "*"
serde = "^1.0" serde = "^1.0"
serde_derive = "^1.0" serde_derive = "^1.0"
glam = { version = "0.14.0", features = ["serde"] } glam = { version = "^0.21.2", features = ["serde"] }
zvariant = { version = "^2.6", optional = true } zvariant = { version = "^3.0", optional = true }
zvariant_derive = { version = "^2.6", optional = true } zbus = { version = "^2.2", optional = true }
udev = { version = "^0.6", optional = true }
sysfs-class = { version = "^0.1", optional = true }
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

+95 -42
View File
@@ -1,17 +1,18 @@
use std::{ use std::{
sync::{ convert::TryFrom,
atomic::{AtomicBool, Ordering},
Arc,
},
thread::sleep, thread::sleep,
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use log::info;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "dbus")] #[cfg(feature = "dbus")]
use zvariant_derive::Type; use zvariant::Type;
use crate::{error::AnimeError, AnimTime, AnimeGif}; use crate::{
error::{AnimeError, Result},
AnimTime, AnimeGif,
};
/// The first 7 bytes of a USB packet are accounted for by `USB_PREFIX1` and `USB_PREFIX2` /// The first 7 bytes of a USB packet are accounted for by `USB_PREFIX1` and `USB_PREFIX2`
const BLOCK_START: usize = 7; const BLOCK_START: usize = 7;
@@ -19,48 +20,85 @@ const BLOCK_START: usize = 7;
const BLOCK_END: usize = 634; const BLOCK_END: usize = 634;
/// Individual usable data length of each USB packet /// Individual usable data length of each USB packet
const PANE_LEN: usize = BLOCK_END - BLOCK_START; const PANE_LEN: usize = BLOCK_END - BLOCK_START;
/// The length of usable data
pub const ANIME_DATA_LEN: usize = PANE_LEN * 2;
/// First packet is for GA401 + GA402
const USB_PREFIX1: [u8; 7] = [0x5e, 0xc0, 0x02, 0x01, 0x00, 0x73, 0x02]; const USB_PREFIX1: [u8; 7] = [0x5e, 0xc0, 0x02, 0x01, 0x00, 0x73, 0x02];
/// Second packet is for GA401 + GA402
const USB_PREFIX2: [u8; 7] = [0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02]; const USB_PREFIX2: [u8; 7] = [0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02];
/// Third packet is for GA402 matrix
const USB_PREFIX3: [u8; 7] = [0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02];
#[cfg_attr(feature = "dbus", derive(Type))] #[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, PartialEq, Copy, Clone, Deserialize, Serialize)] #[derive(Debug, PartialEq, Copy, Clone, Deserialize, Serialize)]
pub struct AnimePowerStates { pub struct AnimePowerStates {
pub brightness: u8,
pub enabled: bool, pub enabled: bool,
pub boot_anim_enabled: bool, pub boot_anim_enabled: bool,
} }
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub enum AnimeType {
GA401,
GA402,
}
impl AnimeType {
/// The width of diagonal images
pub fn width(&self) -> usize {
match self {
AnimeType::GA401 => 74,
AnimeType::GA402 => 74,
}
}
/// The height of diagonal images
pub fn height(&self) -> usize {
match self {
AnimeType::GA401 => 36,
AnimeType::GA402 => 39,
}
}
/// The length of usable data for this type
pub fn data_length(&self) -> usize {
match self {
AnimeType::GA401 => PANE_LEN * 2,
AnimeType::GA402 => PANE_LEN * 3,
}
}
}
/// The minimal serializable data that can be transferred over wire types. /// The minimal serializable data that can be transferred over wire types.
/// Other data structures in `rog_anime` will convert to this. /// Other data structures in `rog_anime` will convert to this.
#[cfg_attr(feature = "dbus", derive(Type))] #[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AnimeDataBuffer(Vec<u8>); pub struct AnimeDataBuffer {
data: Vec<u8>,
impl Default for AnimeDataBuffer { anime: AnimeType,
fn default() -> Self {
Self::new()
}
} }
impl AnimeDataBuffer { impl AnimeDataBuffer {
#[inline] #[inline]
pub fn new() -> Self { pub fn new(anime: AnimeType) -> Self {
AnimeDataBuffer(vec![0u8; ANIME_DATA_LEN]) let len = anime.data_length();
AnimeDataBuffer {
data: vec![0u8; len],
anime,
}
} }
/// Get the inner data buffer /// Get the inner data buffer
#[inline] #[inline]
pub fn get(&self) -> &[u8] { pub fn data(&self) -> &[u8] {
&self.0 &self.data
} }
/// Get a mutable slice of the inner buffer /// Get a mutable slice of the inner buffer
#[inline] #[inline]
pub fn get_mut(&mut self) -> &mut [u8] { pub fn data_mut(&mut self) -> &mut [u8] {
&mut self.0 &mut self.data
} }
/// Create from a vector of bytes /// Create from a vector of bytes
@@ -68,35 +106,51 @@ impl AnimeDataBuffer {
/// # Panics /// # Panics
/// Will panic if the vector length is not `ANIME_DATA_LEN` /// Will panic if the vector length is not `ANIME_DATA_LEN`
#[inline] #[inline]
pub fn from_vec(input: Vec<u8>) -> Self { pub fn from_vec(anime: AnimeType, data: Vec<u8>) -> Result<Self> {
assert_eq!(input.len(), ANIME_DATA_LEN); if data.len() != anime.data_length() {
Self(input) return Err(AnimeError::DataBufferLength);
}
Ok(Self { data, anime })
} }
} }
/// The two packets to be written to USB /// The two packets to be written to USB
pub type AnimePacketType = [[u8; 640]; 2]; pub type AnimePacketType = Vec<[u8; 640]>;
impl From<AnimeDataBuffer> for AnimePacketType { impl TryFrom<AnimeDataBuffer> for AnimePacketType {
#[inline] type Error = AnimeError;
fn from(anime: AnimeDataBuffer) -> Self {
assert!(anime.0.len() == ANIME_DATA_LEN); fn try_from(anime: AnimeDataBuffer) -> std::result::Result<Self, Self::Error> {
let mut buffers = [[0; 640]; 2]; if anime.data.len() != anime.anime.data_length() {
for (idx, chunk) in anime.0.as_slice().chunks(PANE_LEN).enumerate() { return Err(AnimeError::DataBufferLength);
}
let mut buffers = match anime.anime {
AnimeType::GA401 => vec![[0; 640]; 2],
AnimeType::GA402 => vec![[0; 640]; 3],
};
for (idx, chunk) in anime.data.as_slice().chunks(PANE_LEN).enumerate() {
buffers[idx][BLOCK_START..BLOCK_END].copy_from_slice(chunk); buffers[idx][BLOCK_START..BLOCK_END].copy_from_slice(chunk);
} }
buffers[0][..7].copy_from_slice(&USB_PREFIX1); buffers[0][..7].copy_from_slice(&USB_PREFIX1);
buffers[1][..7].copy_from_slice(&USB_PREFIX2); buffers[1][..7].copy_from_slice(&USB_PREFIX2);
buffers
if matches!(anime.anime, AnimeType::GA402) {
buffers[2][..7].copy_from_slice(&USB_PREFIX3);
}
Ok(buffers)
} }
} }
/// This runs the animations as a blocking loop by using the `callback` to write data /// This runs the animations as a blocking loop by using the `callback` to write data
///
/// If `callback` is `Ok(true)` then `run_animation` will exit the animation loop early.
pub fn run_animation( pub fn run_animation(
frames: &AnimeGif, frames: &AnimeGif,
do_early_return: Arc<AtomicBool>, callback: &dyn Fn(AnimeDataBuffer) -> Result<bool>,
callback: &dyn Fn(AnimeDataBuffer) -> Result<(), AnimeError>, ) -> Result<()> {
) -> Result<(), AnimeError> {
let mut count = 0; let mut count = 0;
let start = Instant::now(); let start = Instant::now();
@@ -140,14 +194,11 @@ pub fn run_animation(
'animation: loop { 'animation: loop {
for frame in frames.frames() { for frame in frames.frames() {
let frame_start = Instant::now(); let frame_start = Instant::now();
if do_early_return.load(Ordering::SeqCst) {
return Ok(());
}
let mut output = frame.frame().clone(); let mut output = frame.frame().clone();
if let AnimTime::Fade(_) = frames.duration() { if let AnimTime::Fade(_) = frames.duration() {
if frame_start <= start + fade_in { if frame_start <= start + fade_in {
for pixel in output.get_mut() { for pixel in output.data_mut() {
*pixel = (*pixel as f32 * fade_in_accum) as u8; *pixel = (*pixel as f32 * fade_in_accum) as u8;
} }
fade_in_accum = fade_in_step * (frame_start - start).as_secs_f32(); fade_in_accum = fade_in_step * (frame_start - start).as_secs_f32();
@@ -158,18 +209,20 @@ pub fn run_animation(
} else { } else {
fade_out_accum = 0.0; fade_out_accum = 0.0;
} }
for pixel in output.get_mut() { for pixel in output.data_mut() {
*pixel = (*pixel as f32 * fade_out_accum) as u8; *pixel = (*pixel as f32 * fade_out_accum) as u8;
} }
} }
} }
callback(output)?; if matches!(callback(output), Ok(true)) {
info!("rog-anime: frame-loop callback asked to exit early");
return Ok(());
}
if timed && Instant::now().duration_since(start) > run_time { if timed && Instant::now().duration_since(start) > run_time {
break 'animation; break 'animation;
} }
sleep(frame.delay()); sleep(frame.delay());
} }
if let AnimTime::Count(times) = frames.duration() { if let AnimTime::Count(times) = frames.duration() {
+554 -83
View File
@@ -1,40 +1,36 @@
use std::{path::Path, time::Duration}; use std::{path::Path, time::Duration};
use crate::{ use crate::{
data::{AnimeDataBuffer, ANIME_DATA_LEN}, data::AnimeDataBuffer,
error::AnimeError, error::{AnimeError, Result},
AnimeType,
}; };
const WIDTH: usize = 74;
const HEIGHT: usize = 36;
/// Mostly intended to be used with ASUS gifs, but can be used for other purposes (like images) /// Mostly intended to be used with ASUS gifs, but can be used for other purposes (like images)
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AnimeDiagonal([[u8; WIDTH]; HEIGHT], Option<Duration>); pub struct AnimeDiagonal(AnimeType, Vec<Vec<u8>>, Option<Duration>);
impl Default for AnimeDiagonal {
#[inline]
fn default() -> Self {
Self::new(None)
}
}
impl AnimeDiagonal { impl AnimeDiagonal {
#[inline] #[inline]
pub fn new(duration: Option<Duration>) -> Self { pub fn new(anime_type: AnimeType, duration: Option<Duration>) -> Self {
Self([[0u8; WIDTH]; HEIGHT], duration) Self(
anime_type,
vec![vec![0; anime_type.width()]; anime_type.height()],
duration,
)
} }
#[inline] #[inline]
pub fn get_mut(&mut self) -> &mut [[u8; WIDTH]; HEIGHT] { pub fn get_mut(&mut self) -> &mut Vec<Vec<u8>> {
&mut self.0 &mut self.1
} }
/// Get a full diagonal row where `x` `y` is the starting point and `len` is the length of data. /// Get a full diagonal row where `x` `y` is the starting point and `len` is the length of data.
fn get_row(&self, x: usize, y: usize, len: usize) -> Vec<u8> { fn get_row(&self, x: usize, y: usize, len: usize) -> Vec<u8> {
let mut buf = Vec::with_capacity(len); let mut buf = Vec::with_capacity(len);
for i in 0..len { for i in 0..len {
let val = self.0[HEIGHT - y - i - 1][x + i]; let y = self.0.height() - y - i - 1;
let val = self.1[y][x + i];
buf.push(val); buf.push(val);
} }
buf buf
@@ -47,13 +43,14 @@ impl AnimeDiagonal {
path: &Path, path: &Path,
duration: Option<Duration>, duration: Option<Duration>,
bright: f32, bright: f32,
) -> Result<Self, AnimeError> { anime_type: AnimeType,
) -> Result<Self> {
let data = std::fs::read(path)?; let data = std::fs::read(path)?;
let data = std::io::Cursor::new(data); let data = std::io::Cursor::new(data);
let decoder = png_pong::Decoder::new(data)?.into_steps(); let decoder = png_pong::Decoder::new(data)?.into_steps();
let png_pong::Step { raster, delay: _ } = decoder.last().ok_or(AnimeError::NoFrames)??; let png_pong::Step { raster, delay: _ } = decoder.last().ok_or(AnimeError::NoFrames)??;
let mut matrix = AnimeDiagonal::new(duration); let mut matrix = AnimeDiagonal::new(anime_type, duration);
match raster { match raster {
png_pong::PngRaster::Gray8(ras) => { png_pong::PngRaster::Gray8(ras) => {
@@ -100,7 +97,9 @@ impl AnimeDiagonal {
+ (<u8>::from(px.two()) / 3) as f32 + (<u8>::from(px.two()) / 3) as f32
+ (<u8>::from(px.three()) / 3) as f32 + (<u8>::from(px.three()) / 3) as f32
}; };
matrix.0[y][x] = (v * bright) as u8; if y < matrix.1.len() && x < matrix.1[y].len() {
matrix.1[y][x] = (v * bright) as u8;
}
} }
} }
} }
@@ -123,75 +122,547 @@ impl AnimeDiagonal {
+ ((<u16>::from(px.two()) / 3) >> 8) as f32 + ((<u16>::from(px.two()) / 3) >> 8) as f32
+ ((<u16>::from(px.three()) / 3) >> 8) as f32 + ((<u16>::from(px.three()) / 3) >> 8) as f32
}; };
matrix.0[y][x] = (v * bright) as u8; matrix.1[y][x] = (v * bright) as u8;
} }
} }
} }
}
impl From<&AnimeDiagonal> for AnimeDataBuffer { /// Convert to a data buffer that can be sent over dbus
#[inline]
pub fn into_data_buffer(&self, anime_type: AnimeType) -> Result<AnimeDataBuffer> {
match anime_type {
AnimeType::GA401 => self.into_ga401_packets(),
AnimeType::GA402 => self.into_ga402_packets(),
}
}
/// Do conversion from the nested Vec in AnimeMatrix to the two required /// Do conversion from the nested Vec in AnimeMatrix to the two required
/// packets suitable for sending over USB /// packets suitable for sending over USB
#[inline] fn into_ga401_packets(&self) -> Result<AnimeDataBuffer> {
fn from(anime: &AnimeDiagonal) -> Self { let mut buf = vec![0u8; AnimeType::GA401.data_length()];
let mut buf = vec![0u8; ANIME_DATA_LEN];
buf[1..=32].copy_from_slice(&anime.get_row(0, 3, 32)); buf[1..=32].copy_from_slice(&self.get_row(0, 3, 32));
buf[34..=66].copy_from_slice(&anime.get_row(0, 2, 33)); buf[34..=66].copy_from_slice(&self.get_row(0, 2, 33));
buf[69..=101].copy_from_slice(&anime.get_row(1, 2, 33)); // ?! buf[69..=101].copy_from_slice(&self.get_row(1, 2, 33)); // ?!
buf[102..=134].copy_from_slice(&anime.get_row(1, 1, 33)); buf[102..=134].copy_from_slice(&self.get_row(1, 1, 33));
buf[137..=169].copy_from_slice(&anime.get_row(2, 1, 33)); buf[137..=169].copy_from_slice(&self.get_row(2, 1, 33));
buf[170..=202].copy_from_slice(&anime.get_row(2, 0, 33)); buf[170..=202].copy_from_slice(&self.get_row(2, 0, 33));
buf[204..=236].copy_from_slice(&anime.get_row(3, 0, 33)); buf[204..=236].copy_from_slice(&self.get_row(3, 0, 33)); // This and above cause overflow?
buf[237..=268].copy_from_slice(&anime.get_row(4, 0, 32)); buf[237..=268].copy_from_slice(&self.get_row(4, 0, 32));
buf[270..=301].copy_from_slice(&anime.get_row(5, 0, 32)); buf[270..=301].copy_from_slice(&self.get_row(5, 0, 32));
buf[302..=332].copy_from_slice(&anime.get_row(6, 0, 31)); buf[302..=332].copy_from_slice(&self.get_row(6, 0, 31));
buf[334..=364].copy_from_slice(&anime.get_row(7, 0, 31)); buf[334..=364].copy_from_slice(&self.get_row(7, 0, 31));
buf[365..=394].copy_from_slice(&anime.get_row(8, 0, 30)); buf[365..=394].copy_from_slice(&self.get_row(8, 0, 30));
buf[396..=425].copy_from_slice(&anime.get_row(9, 0, 30)); buf[396..=425].copy_from_slice(&self.get_row(9, 0, 30));
buf[426..=454].copy_from_slice(&anime.get_row(10, 0, 29)); buf[426..=454].copy_from_slice(&self.get_row(10, 0, 29));
buf[456..=484].copy_from_slice(&anime.get_row(11, 0, 29)); buf[456..=484].copy_from_slice(&self.get_row(11, 0, 29));
buf[485..=512].copy_from_slice(&anime.get_row(12, 0, 28)); buf[485..=512].copy_from_slice(&self.get_row(12, 0, 28));
buf[514..=541].copy_from_slice(&anime.get_row(13, 0, 28)); buf[514..=541].copy_from_slice(&self.get_row(13, 0, 28));
buf[542..=568].copy_from_slice(&anime.get_row(14, 0, 27)); buf[542..=568].copy_from_slice(&self.get_row(14, 0, 27));
buf[570..=596].copy_from_slice(&anime.get_row(15, 0, 27)); buf[570..=596].copy_from_slice(&self.get_row(15, 0, 27));
buf[597..=622].copy_from_slice(&anime.get_row(16, 0, 26)); buf[597..=622].copy_from_slice(&self.get_row(16, 0, 26));
buf[624..=649].copy_from_slice(&anime.get_row(17, 0, 26)); buf[624..=649].copy_from_slice(&self.get_row(17, 0, 26));
buf[650..=674].copy_from_slice(&anime.get_row(18, 0, 25)); buf[650..=674].copy_from_slice(&self.get_row(18, 0, 25));
buf[676..=700].copy_from_slice(&anime.get_row(19, 0, 25)); buf[676..=700].copy_from_slice(&self.get_row(19, 0, 25));
buf[701..=724].copy_from_slice(&anime.get_row(20, 0, 24)); buf[701..=724].copy_from_slice(&self.get_row(20, 0, 24));
buf[726..=749].copy_from_slice(&anime.get_row(21, 0, 24)); buf[726..=749].copy_from_slice(&self.get_row(21, 0, 24));
buf[750..=772].copy_from_slice(&anime.get_row(22, 0, 23)); buf[750..=772].copy_from_slice(&self.get_row(22, 0, 23));
buf[774..=796].copy_from_slice(&anime.get_row(23, 0, 23)); buf[774..=796].copy_from_slice(&self.get_row(23, 0, 23));
buf[797..=818].copy_from_slice(&anime.get_row(24, 0, 22)); buf[797..=818].copy_from_slice(&self.get_row(24, 0, 22));
buf[820..=841].copy_from_slice(&anime.get_row(25, 0, 22)); buf[820..=841].copy_from_slice(&self.get_row(25, 0, 22));
buf[842..=862].copy_from_slice(&anime.get_row(26, 0, 21)); buf[842..=862].copy_from_slice(&self.get_row(26, 0, 21));
buf[864..=884].copy_from_slice(&anime.get_row(27, 0, 21)); buf[864..=884].copy_from_slice(&self.get_row(27, 0, 21));
buf[885..=904].copy_from_slice(&anime.get_row(28, 0, 20)); buf[885..=904].copy_from_slice(&self.get_row(28, 0, 20));
buf[906..=925].copy_from_slice(&anime.get_row(29, 0, 20)); buf[906..=925].copy_from_slice(&self.get_row(29, 0, 20));
buf[926..=944].copy_from_slice(&anime.get_row(30, 0, 19)); buf[926..=944].copy_from_slice(&self.get_row(30, 0, 19));
buf[946..=964].copy_from_slice(&anime.get_row(31, 0, 19)); buf[946..=964].copy_from_slice(&self.get_row(31, 0, 19));
buf[965..=982].copy_from_slice(&anime.get_row(32, 0, 18)); buf[965..=982].copy_from_slice(&self.get_row(32, 0, 18));
buf[984..=1001].copy_from_slice(&anime.get_row(33, 0, 18)); buf[984..=1001].copy_from_slice(&self.get_row(33, 0, 18));
buf[1002..=1018].copy_from_slice(&anime.get_row(34, 0, 17)); buf[1002..=1018].copy_from_slice(&self.get_row(34, 0, 17));
buf[1020..=1036].copy_from_slice(&anime.get_row(35, 0, 17)); buf[1020..=1036].copy_from_slice(&self.get_row(35, 0, 17));
buf[1037..=1052].copy_from_slice(&anime.get_row(36, 0, 16)); buf[1037..=1052].copy_from_slice(&self.get_row(36, 0, 16));
buf[1054..=1069].copy_from_slice(&anime.get_row(37, 0, 16)); buf[1054..=1069].copy_from_slice(&self.get_row(37, 0, 16));
buf[1070..=1084].copy_from_slice(&anime.get_row(38, 0, 15)); buf[1070..=1084].copy_from_slice(&self.get_row(38, 0, 15));
buf[1086..=1100].copy_from_slice(&anime.get_row(39, 0, 15)); buf[1086..=1100].copy_from_slice(&self.get_row(39, 0, 15));
buf[1101..=1114].copy_from_slice(&anime.get_row(40, 0, 14)); buf[1101..=1114].copy_from_slice(&self.get_row(40, 0, 14));
buf[1116..=1129].copy_from_slice(&anime.get_row(41, 0, 14)); buf[1116..=1129].copy_from_slice(&self.get_row(41, 0, 14));
buf[1130..=1142].copy_from_slice(&anime.get_row(42, 0, 13)); buf[1130..=1142].copy_from_slice(&self.get_row(42, 0, 13));
buf[1144..=1156].copy_from_slice(&anime.get_row(43, 0, 13)); buf[1144..=1156].copy_from_slice(&self.get_row(43, 0, 13));
buf[1157..=1168].copy_from_slice(&anime.get_row(44, 0, 12)); buf[1157..=1168].copy_from_slice(&self.get_row(44, 0, 12));
buf[1170..=1181].copy_from_slice(&anime.get_row(45, 0, 12)); buf[1170..=1181].copy_from_slice(&self.get_row(45, 0, 12));
buf[1182..=1192].copy_from_slice(&anime.get_row(46, 0, 11)); buf[1182..=1192].copy_from_slice(&self.get_row(46, 0, 11));
buf[1194..=1204].copy_from_slice(&anime.get_row(47, 0, 11)); buf[1194..=1204].copy_from_slice(&self.get_row(47, 0, 11));
buf[1205..=1214].copy_from_slice(&anime.get_row(48, 0, 10)); buf[1205..=1214].copy_from_slice(&self.get_row(48, 0, 10));
buf[1216..=1225].copy_from_slice(&anime.get_row(49, 0, 10)); buf[1216..=1225].copy_from_slice(&self.get_row(49, 0, 10));
buf[1226..=1234].copy_from_slice(&anime.get_row(50, 0, 9)); buf[1226..=1234].copy_from_slice(&self.get_row(50, 0, 9));
buf[1236..=1244].copy_from_slice(&anime.get_row(51, 0, 9)); buf[1236..=1244].copy_from_slice(&self.get_row(51, 0, 9));
AnimeDataBuffer::from_vec(buf) AnimeDataBuffer::from_vec(crate::AnimeType::GA401, buf)
}
fn into_ga402_packets(&self) -> Result<AnimeDataBuffer> {
let mut buf = vec![0u8; AnimeType::GA402.data_length()];
let mut start_index: usize = 0;
fn copy_slice(
buf: &mut Vec<u8>,
anime: &AnimeDiagonal,
x: usize,
y: usize,
start_index: &mut usize,
len: usize,
) {
buf[*start_index..*start_index + len].copy_from_slice(&anime.get_row(x, y, len));
*start_index += len;
}
let b = &mut buf;
let a = &self;
copy_slice(b, a, 0, 5, &mut start_index, 34);
copy_slice(b, a, 1, 5, &mut start_index, 34);
copy_slice(b, a, 1, 4, &mut start_index, 34);
copy_slice(b, a, 2, 4, &mut start_index, 34);
copy_slice(b, a, 2, 3, &mut start_index, 34);
copy_slice(b, a, 3, 3, &mut start_index, 34);
copy_slice(b, a, 3, 2, &mut start_index, 34);
copy_slice(b, a, 4, 2, &mut start_index, 34);
copy_slice(b, a, 4, 1, &mut start_index, 34);
copy_slice(b, a, 5, 1, &mut start_index, 34);
copy_slice(b, a, 5, 0, &mut start_index, 34);
copy_slice(b, a, 6, 0, &mut start_index, 34);
copy_slice(b, a, 7, 0, &mut start_index, 33);
copy_slice(b, a, 8, 0, &mut start_index, 33);
copy_slice(b, a, 9, 0, &mut start_index, 32);
copy_slice(b, a, 10, 0, &mut start_index, 32);
copy_slice(b, a, 11, 0, &mut start_index, 31);
copy_slice(b, a, 12, 0, &mut start_index, 31);
copy_slice(b, a, 13, 0, &mut start_index, 30);
copy_slice(b, a, 14, 0, &mut start_index, 30);
copy_slice(b, a, 15, 0, &mut start_index, 29);
copy_slice(b, a, 16, 0, &mut start_index, 29);
copy_slice(b, a, 17, 0, &mut start_index, 28);
copy_slice(b, a, 18, 0, &mut start_index, 28);
copy_slice(b, a, 19, 0, &mut start_index, 27);
copy_slice(b, a, 20, 0, &mut start_index, 27);
copy_slice(b, a, 21, 0, &mut start_index, 26);
copy_slice(b, a, 22, 0, &mut start_index, 26);
copy_slice(b, a, 23, 0, &mut start_index, 25);
copy_slice(b, a, 24, 0, &mut start_index, 25);
copy_slice(b, a, 25, 0, &mut start_index, 24);
copy_slice(b, a, 26, 0, &mut start_index, 24);
copy_slice(b, a, 27, 0, &mut start_index, 23);
copy_slice(b, a, 28, 0, &mut start_index, 23);
copy_slice(b, a, 29, 0, &mut start_index, 22);
copy_slice(b, a, 30, 0, &mut start_index, 22);
copy_slice(b, a, 31, 0, &mut start_index, 21);
copy_slice(b, a, 32, 0, &mut start_index, 21);
copy_slice(b, a, 33, 0, &mut start_index, 20);
copy_slice(b, a, 34, 0, &mut start_index, 20);
copy_slice(b, a, 35, 0, &mut start_index, 19);
copy_slice(b, a, 36, 0, &mut start_index, 19);
copy_slice(b, a, 37, 0, &mut start_index, 18);
copy_slice(b, a, 38, 0, &mut start_index, 18);
copy_slice(b, a, 39, 0, &mut start_index, 17);
copy_slice(b, a, 40, 0, &mut start_index, 17);
copy_slice(b, a, 41, 0, &mut start_index, 16);
copy_slice(b, a, 42, 0, &mut start_index, 16);
copy_slice(b, a, 43, 0, &mut start_index, 15);
copy_slice(b, a, 44, 0, &mut start_index, 15);
copy_slice(b, a, 45, 0, &mut start_index, 14);
copy_slice(b, a, 46, 0, &mut start_index, 14);
copy_slice(b, a, 47, 0, &mut start_index, 13);
copy_slice(b, a, 48, 0, &mut start_index, 13);
copy_slice(b, a, 49, 0, &mut start_index, 12);
copy_slice(b, a, 50, 0, &mut start_index, 12);
copy_slice(b, a, 51, 0, &mut start_index, 11);
copy_slice(b, a, 52, 0, &mut start_index, 11);
copy_slice(b, a, 53, 0, &mut start_index, 10);
copy_slice(b, a, 54, 0, &mut start_index, 10);
copy_slice(b, a, 55, 0, &mut start_index, 9);
AnimeDataBuffer::from_vec(crate::AnimeType::GA402, buf)
}
}
#[cfg(test)]
mod tests {
use std::{convert::TryFrom, path::PathBuf};
use crate::{AnimeDiagonal, AnimePacketType, AnimeType};
#[test]
fn ga401_diagonal_packet_check() {
let pkt0_check = [
0x5e, 0xc0, 0x2, 0x1, 0x0, 0x73, 0x2, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0x0, 0xff, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0x0, 0xff, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff,
0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0,
];
let pkt1_check = [
0x5e, 0xc0, 0x2, 0x74, 0x2, 0x73, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
];
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("test/ga401-diagonal.png");
let matrix = AnimeDiagonal::from_png(&path, None, 255.0, AnimeType::GA401).unwrap();
let data = matrix.into_data_buffer(crate::AnimeType::GA401).unwrap();
let pkt = AnimePacketType::try_from(data).unwrap();
assert_eq!(pkt[0], pkt0_check);
assert_eq!(pkt[1], pkt1_check);
}
#[test]
fn ga402_diagonal_packet_check() {
let pkt0_check = [
0x5e, 0xc0, 0x2, 0x1, 0x0, 0x73, 0x2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0,
];
let pkt1_check = [
0x5e, 0xc0, 0x2, 0x74, 0x2, 0x73, 0x2, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
];
let pkt2_check = [
0x5e, 0xc0, 0x2, 0xe7, 0x4, 0x73, 0x2, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff,
0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff,
0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
];
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("test/ga402-diagonal.png");
let matrix = AnimeDiagonal::from_png(&path, None, 255.0, AnimeType::GA402).unwrap();
let data = matrix.into_data_buffer(crate::AnimeType::GA402).unwrap();
let pkt = AnimePacketType::try_from(data).unwrap();
assert_eq!(pkt[0], pkt0_check);
assert_eq!(pkt[1], pkt1_check);
assert_eq!(pkt[2], pkt2_check);
}
#[test]
#[ignore = "Needs the packets verified with capture"]
fn ga402_diagonal_fullbright_packet_check() {
let pkt0_check = [
0x5e, 0xc0, 0x2, 0x1, 0x0, 0x73, 0x2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x67, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
];
let pkt1_check = [
0x5e, 0xc0, 0x2, 0x74, 0x2, 0x73, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
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, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
];
let pkt2_check = [
0x5e, 0xc0, 0x2, 0xe7, 0x4, 0x73, 0x2, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 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, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 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, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
];
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("test/ga402-diagonal-fullbright.png");
let matrix = AnimeDiagonal::from_png(&path, None, 255.0, AnimeType::GA402).unwrap();
let data = matrix.into_data_buffer(crate::AnimeType::GA402).unwrap();
let pkt = AnimePacketType::try_from(data).unwrap();
assert_eq!(pkt[0], pkt0_check);
assert_eq!(pkt[1], pkt1_check);
assert_eq!(pkt[2], pkt2_check);
} }
} }
+35
View File
@@ -3,6 +3,8 @@ use png_pong::decode::Error as PngError;
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
pub type Result<T> = std::result::Result<T, AnimeError>;
#[derive(Debug)] #[derive(Debug)]
pub enum AnimeError { pub enum AnimeError {
NoFrames, NoFrames,
@@ -13,6 +15,13 @@ pub enum AnimeError {
/// The input was incorrect size, expected size is `IncorrectSize(width, height)` /// The input was incorrect size, expected size is `IncorrectSize(width, height)`
IncorrectSize(u32, u32), IncorrectSize(u32, u32),
Dbus(String), Dbus(String),
Udev(String, std::io::Error),
NoDevice,
UnsupportedDevice,
InvalidBrightness(f32),
DataBufferLength,
PixelGifWidth(usize),
PixelGifHeight(usize),
} }
impl fmt::Display for AnimeError { impl fmt::Display for AnimeError {
@@ -30,6 +39,25 @@ impl fmt::Display for AnimeError {
width, height width, height
), ),
AnimeError::Dbus(detail) => write!(f, "{}", detail), AnimeError::Dbus(detail) => write!(f, "{}", detail),
AnimeError::Udev(deets, error) => write!(f, "udev {}: {}", deets, error),
AnimeError::NoDevice => write!(f, "No AniMe Matrix device found"),
AnimeError::DataBufferLength => write!(
f,
"The data buffer was incorrect length for generating USB packets"
),
AnimeError::UnsupportedDevice => write!(f, "Unsupported AniMe Matrix device found"),
AnimeError::InvalidBrightness(bright) => write!(
f,
"Image brightness must be between 0.0 and 1.0 (inclusive), was {}",
bright
),
AnimeError::PixelGifWidth(n) => {
write!(f, "The gif used for pixel-perfect gif is is wider than {n}")
}
AnimeError::PixelGifHeight(n) => write!(
f,
"The gif used for pixel-perfect gif is is taller than {n}"
),
} }
} }
} }
@@ -56,3 +84,10 @@ impl From<DecodingError> for AnimeError {
AnimeError::Gif(err) AnimeError::Gif(err)
} }
} }
impl From<AnimeError> for zbus::fdo::Error {
#[inline]
fn from(err: AnimeError) -> Self {
zbus::fdo::Error::Failed(format!("{}", err))
}
}
+34 -16
View File
@@ -1,8 +1,10 @@
use glam::Vec2; use glam::Vec2;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::{fs::File, path::Path, time::Duration}; use std::{fs::File, path::Path, time::Duration};
use crate::{error::AnimeError, AnimeDataBuffer, AnimeDiagonal, AnimeImage, Pixel}; use crate::error::AnimeError;
use crate::{error::Result, AnimeDataBuffer, AnimeDiagonal, AnimeImage, AnimeType, Pixel};
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AnimeFrame { pub struct AnimeFrame {
@@ -92,8 +94,9 @@ impl AnimeGif {
file_name: &Path, file_name: &Path,
duration: AnimTime, duration: AnimTime,
brightness: f32, brightness: f32,
) -> Result<Self, AnimeError> { anime_type: AnimeType,
let mut matrix = AnimeDiagonal::new(None); ) -> Result<Self> {
let mut matrix = AnimeDiagonal::new(anime_type, None);
let mut decoder = gif::DecodeOptions::new(); let mut decoder = gif::DecodeOptions::new();
// Configure the decoder such that it will expand the image to RGBA. // Configure the decoder such that it will expand the image to RGBA.
@@ -115,13 +118,22 @@ impl AnimeGif {
// should be t but not in some gifs? What, ASUS, what? // should be t but not in some gifs? What, ASUS, what?
continue; continue;
} }
matrix.get_mut()[y + frame.top as usize][x + frame.left as usize] = let tmp = matrix.get_mut();
(px[0] as f32 * brightness) as u8; let y = y + frame.top as usize;
if y >= tmp.len() {
return Err(AnimeError::PixelGifHeight(tmp.len()));
}
let x = x + frame.left as usize;
if x >= tmp[y].len() {
return Err(AnimeError::PixelGifWidth(tmp[y].len()));
}
matrix.get_mut()[y][x] = (px[0] as f32 * brightness) as u8;
} }
} }
frames.push(AnimeFrame { frames.push(AnimeFrame {
data: <AnimeDataBuffer>::from(&matrix), data: matrix.into_data_buffer(anime_type)?,
delay: Duration::from_millis(wait as u64), delay: Duration::from_millis(wait as u64),
}); });
} }
@@ -132,10 +144,11 @@ impl AnimeGif {
#[inline] #[inline]
pub fn from_diagonal_png( pub fn from_diagonal_png(
file_name: &Path, file_name: &Path,
anime_type: AnimeType,
duration: AnimTime, duration: AnimTime,
brightness: f32, brightness: f32,
) -> Result<Self, AnimeError> { ) -> Result<Self> {
let image = AnimeDiagonal::from_png(file_name, None, brightness)?; let image = AnimeDiagonal::from_png(file_name, None, brightness, anime_type)?;
let mut total = Duration::from_millis(1000); let mut total = Duration::from_millis(1000);
if let AnimTime::Fade(fade) = duration { if let AnimTime::Fade(fade) = duration {
@@ -148,7 +161,7 @@ impl AnimeGif {
let frame_count = total.as_millis() / 30; let frame_count = total.as_millis() / 30;
let single = AnimeFrame { let single = AnimeFrame {
data: <AnimeDataBuffer>::from(&image), data: image.into_data_buffer(anime_type)?,
delay: Duration::from_millis(30), delay: Duration::from_millis(30),
}; };
let frames = vec![single; frame_count as usize]; let frames = vec![single; frame_count as usize];
@@ -166,7 +179,8 @@ impl AnimeGif {
translation: Vec2, translation: Vec2,
duration: AnimTime, duration: AnimTime,
brightness: f32, brightness: f32,
) -> Result<Self, AnimeError> { anime_type: AnimeType,
) -> Result<Self> {
let mut frames = Vec::new(); let mut frames = Vec::new();
let mut decoder = gif::DecodeOptions::new(); let mut decoder = gif::DecodeOptions::new();
@@ -187,7 +201,8 @@ impl AnimeGif {
brightness, brightness,
pixels, pixels,
decoder.width() as u32, decoder.width() as u32,
); anime_type,
)?;
while let Some(frame) = decoder.read_next_frame()? { while let Some(frame) = decoder.read_next_frame()? {
let wait = frame.delay * 10; let wait = frame.delay * 10;
@@ -201,7 +216,8 @@ impl AnimeGif {
brightness, brightness,
pixels, pixels,
width as u32, width as u32,
); anime_type,
)?;
} }
for (y, row) in frame.buffer.chunks(frame.width as usize * 4).enumerate() { for (y, row) in frame.buffer.chunks(frame.width as usize * 4).enumerate() {
for (x, px) in row.chunks(4).enumerate() { for (x, px) in row.chunks(4).enumerate() {
@@ -220,7 +236,7 @@ impl AnimeGif {
image.update(); image.update();
frames.push(AnimeFrame { frames.push(AnimeFrame {
data: <AnimeDataBuffer>::from(&image), data: <AnimeDataBuffer>::try_from(&image)?,
delay: Duration::from_millis(wait as u64), delay: Duration::from_millis(wait as u64),
}); });
} }
@@ -238,8 +254,10 @@ impl AnimeGif {
translation: Vec2, translation: Vec2,
duration: AnimTime, duration: AnimTime,
brightness: f32, brightness: f32,
) -> Result<Self, AnimeError> { anime_type: AnimeType,
let image = AnimeImage::from_png(file_name, scale, angle, translation, brightness)?; ) -> Result<Self> {
let image =
AnimeImage::from_png(file_name, scale, angle, translation, brightness, anime_type)?;
let mut total = Duration::from_millis(1000); let mut total = Duration::from_millis(1000);
if let AnimTime::Fade(fade) = duration { if let AnimTime::Fade(fade) = duration {
@@ -252,7 +270,7 @@ impl AnimeGif {
let frame_count = total.as_millis() / 30; let frame_count = total.as_millis() / 30;
let single = AnimeFrame { let single = AnimeFrame {
data: <AnimeDataBuffer>::from(&image), data: <AnimeDataBuffer>::try_from(&image)?,
delay: Duration::from_millis(30), delay: Duration::from_millis(30),
}; };
let frames = vec![single; frame_count as usize]; let frames = vec![single; frame_count as usize];
+31 -26
View File
@@ -1,8 +1,10 @@
use std::time::Duration; use std::convert::TryFrom;
use crate::data::{AnimeDataBuffer, ANIME_DATA_LEN}; use crate::data::AnimeDataBuffer;
use crate::image::LED_IMAGE_POSITIONS; use crate::error::{AnimeError, Result};
use crate::{AnimeImage, AnimeType};
// TODO: Adjust these sizes as WIDTH_GA401 WIDTH_GA402
const WIDTH: usize = 33; const WIDTH: usize = 33;
const HEIGHT: usize = 55; const HEIGHT: usize = 55;
@@ -14,41 +16,40 @@ const HEIGHT: usize = 55;
/// ///
/// **Note:** the columns in each odd row are offset by half a pixel left /// **Note:** the columns in each odd row are offset by half a pixel left
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AnimeGrid([[u8; WIDTH]; HEIGHT], Option<Duration>); pub struct AnimeGrid {
anime_type: AnimeType,
impl Default for AnimeGrid { data: [[u8; WIDTH]; HEIGHT],
#[inline]
fn default() -> Self {
Self::new(None)
}
} }
impl AnimeGrid { impl AnimeGrid {
#[inline] #[inline]
pub fn new(duration: Option<Duration>) -> Self { pub fn new(anime_type: AnimeType) -> Self {
AnimeGrid([[0u8; WIDTH]; HEIGHT], duration) Self {
anime_type,
data: [[0u8; WIDTH]; HEIGHT],
}
} }
/// Set a position in the grid with a brightness value /// Set a position in the grid with a brightness value
#[inline] #[inline]
pub fn set(&mut self, x: usize, y: usize, b: u8) { pub fn set(&mut self, x: usize, y: usize, b: u8) {
self.0[y][x] = b; self.data[y][x] = b;
} }
#[inline] #[inline]
pub fn get(&self) -> &[[u8; WIDTH]; HEIGHT] { pub fn get(&self) -> &[[u8; WIDTH]; HEIGHT] {
&self.0 &self.data
} }
#[inline] #[inline]
pub fn get_mut(&mut self) -> &mut [[u8; WIDTH]; HEIGHT] { pub fn get_mut(&mut self) -> &mut [[u8; WIDTH]; HEIGHT] {
&mut self.0 &mut self.data
} }
/// Fill the grid with a value /// Fill the grid with a value
#[inline] #[inline]
pub fn fill_with(&mut self, fill: u8) { pub fn fill_with(&mut self, fill: u8) {
for row in self.0.iter_mut() { for row in self.data.iter_mut() {
for x in row.iter_mut() { for x in row.iter_mut() {
*x = fill; *x = fill;
} }
@@ -89,21 +90,25 @@ impl AnimeGrid {
// } // }
} }
impl From<AnimeGrid> for AnimeDataBuffer { impl TryFrom<AnimeGrid> for AnimeDataBuffer {
type Error = AnimeError;
/// Do conversion from the nested Vec in AniMeMatrix to the two required /// Do conversion from the nested Vec in AniMeMatrix to the two required
/// packets suitable for sending over USB /// packets suitable for sending over USB
#[inline] fn try_from(anime: AnimeGrid) -> Result<Self> {
fn from(anime: AnimeGrid) -> Self { let mut buf = vec![0u8; anime.anime_type.data_length()];
let mut buf = vec![0u8; ANIME_DATA_LEN];
for (idx, pos) in LED_IMAGE_POSITIONS.iter().enumerate() { for (idx, pos) in AnimeImage::generate_image_positioning(anime.anime_type)
.iter()
.enumerate()
{
if let Some(pos) = pos { if let Some(pos) = pos {
let x = pos.x().ceil() as usize; let x = pos.x().ceil() as usize;
let y = pos.y().ceil() as usize; let y = pos.y().ceil() as usize;
buf[idx + 1] = anime.0[y][x]; buf[idx + 1] = anime.data[y][x];
} }
} }
AnimeDataBuffer::from_vec(buf) AnimeDataBuffer::from_vec(anime.anime_type, buf)
} }
} }
@@ -113,7 +118,7 @@ mod tests {
#[test] #[test]
fn check_data_alignment() { fn check_data_alignment() {
let mut matrix = AnimeGrid::new(None); let mut matrix = AnimeGrid::new(AnimeType::GA401);
{ {
let tmp = matrix.get_mut(); let tmp = matrix.get_mut();
for row in tmp.iter_mut() { for row in tmp.iter_mut() {
@@ -122,7 +127,7 @@ mod tests {
} }
} }
let matrix = <AnimeDataBuffer>::from(matrix); let matrix = <AnimeDataBuffer>::try_from(matrix).unwrap();
let data_cmp = [ let data_cmp = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -171,6 +176,6 @@ mod tests {
0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0,
0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]; ];
assert_eq!(matrix.get(), &data_cmp); assert_eq!(matrix.data(), &data_cmp);
} }
} }
+422 -1357
View File
File diff suppressed because it is too large Load Diff
+39 -17
View File
@@ -1,9 +1,11 @@
use std::{path::PathBuf, time::Duration};
use glam::Vec2; use glam::Vec2;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::{path::PathBuf, time::Duration};
use crate::{error::AnimeError, AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage}; use crate::{
error::Result, AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType,
};
/// All the possible AniMe actions that can be used. This enum is intended to be /// All the possible AniMe actions that can be used. This enum is intended to be
/// a helper for loading up `ActionData`. /// a helper for loading up `ActionData`.
@@ -63,24 +65,34 @@ pub enum ActionData {
} }
impl ActionData { impl ActionData {
pub fn from_anime_action(action: &ActionLoader) -> Result<ActionData, AnimeError> { pub fn from_anime_action(anime_type: AnimeType, action: &ActionLoader) -> Result<ActionData> {
let a = match action { let a = match action {
ActionLoader::AsusAnimation { ActionLoader::AsusAnimation {
file, file,
time, time,
brightness, brightness,
} => ActionData::Animation(AnimeGif::from_diagonal_gif(file, *time, *brightness)?), } => ActionData::Animation(AnimeGif::from_diagonal_gif(
file,
*time,
*brightness,
anime_type,
)?),
ActionLoader::AsusImage { ActionLoader::AsusImage {
file, file,
time, time,
brightness, brightness,
} => match time { } => match time {
AnimTime::Infinite => { AnimTime::Infinite => {
let image = AnimeDiagonal::from_png(file, None, *brightness)?; let image = AnimeDiagonal::from_png(file, None, *brightness, anime_type)?;
let data = <AnimeDataBuffer>::from(&image); let data = image.into_data_buffer(anime_type)?;
ActionData::Image(Box::new(data)) ActionData::Image(Box::new(data))
} }
_ => ActionData::Animation(AnimeGif::from_diagonal_png(file, *time, *brightness)?), _ => ActionData::Animation(AnimeGif::from_diagonal_png(
file,
anime_type,
*time,
*brightness,
)?),
}, },
ActionLoader::ImageAnimation { ActionLoader::ImageAnimation {
file, file,
@@ -99,6 +111,7 @@ impl ActionData {
*translation, *translation,
*time, *time,
*brightness, *brightness,
anime_type,
)?)); )?));
} }
} }
@@ -109,6 +122,7 @@ impl ActionData {
*translation, *translation,
*time, *time,
*brightness, *brightness,
anime_type,
)?) )?)
} }
ActionLoader::Image { ActionLoader::Image {
@@ -122,9 +136,15 @@ impl ActionData {
match time { match time {
AnimTime::Infinite => { AnimTime::Infinite => {
// If no time then create a plain static image // If no time then create a plain static image
let image = let image = AnimeImage::from_png(
AnimeImage::from_png(file, *scale, *angle, *translation, *brightness)?; file,
let data = <AnimeDataBuffer>::from(&image); *scale,
*angle,
*translation,
*brightness,
anime_type,
)?;
let data = <AnimeDataBuffer>::try_from(&image)?;
ActionData::Image(Box::new(data)) ActionData::Image(Box::new(data))
} }
_ => ActionData::Animation(AnimeGif::from_png( _ => ActionData::Animation(AnimeGif::from_png(
@@ -134,6 +154,7 @@ impl ActionData {
*translation, *translation,
*time, *time,
*brightness, *brightness,
anime_type,
)?), )?),
} }
} }
@@ -144,20 +165,21 @@ impl ActionData {
} }
/// An optimised precomputed set of actions that the user can cycle through /// An optimised precomputed set of actions that the user can cycle through
#[derive(Debug, Deserialize, Serialize, Default)] #[derive(Debug, Deserialize, Serialize)]
pub struct Sequences(Vec<ActionData>); pub struct Sequences(Vec<ActionData>, AnimeType);
impl Sequences { impl Sequences {
#[inline] #[inline]
pub fn new() -> Self { pub fn new(anime_type: AnimeType) -> Self {
Self(Vec::new()) Self(Vec::new(), anime_type)
} }
/// Use a base `AnimeAction` to generate the precomputed data and insert in to /// Use a base `AnimeAction` to generate the precomputed data and insert in to
/// the run buffer /// the run buffer
#[inline] #[inline]
pub fn insert(&mut self, index: usize, action: &ActionLoader) -> Result<(), AnimeError> { pub fn insert(&mut self, index: usize, action: &ActionLoader) -> Result<()> {
self.0.insert(index, ActionData::from_anime_action(action)?); self.0
.insert(index, ActionData::from_anime_action(self.1, action)?);
Ok(()) Ok(())
} }
+45
View File
@@ -7,6 +7,8 @@
//! //!
//! Step 1 need to applied only on fresh system boot. //! Step 1 need to applied only on fresh system boot.
use crate::{error::AnimeError, AnimeType};
const INIT_STR: [u8; 15] = [ const INIT_STR: [u8; 15] = [
0x5e, b'A', b'S', b'U', b'S', b' ', b'T', b'e', b'c', b'h', b'.', b'I', b'n', b'c', b'.', 0x5e, b'A', b'S', b'U', b'S', b' ', b'T', b'e', b'c', b'h', b'.', b'I', b'n', b'c', b'.',
]; ];
@@ -15,6 +17,49 @@ const DEV_PAGE: u8 = 0x5e;
pub const VENDOR_ID: u16 = 0x0b05; pub const VENDOR_ID: u16 = 0x0b05;
pub const PROD_ID: u16 = 0x193b; pub const PROD_ID: u16 = 0x193b;
/// `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.
///
/// The currently known USB device is `19b6`.
#[inline]
pub fn get_anime_type() -> Result<AnimeType, AnimeError> {
let dmi = sysfs_class::DmiId::default();
let board_name = dmi.board_name()?;
if board_name.contains("GA401I") {
return Ok(AnimeType::GA401);
} else if board_name.contains("GA401Q") {
return Ok(AnimeType::GA401);
} else if board_name.contains("GA402R") {
return Ok(AnimeType::GA402);
}
Err(AnimeError::UnsupportedDevice)
}
/// Find the USB device node - known devices so far: `19b6`
#[inline]
pub fn find_node(id_product: &str) -> Result<String, AnimeError> {
let mut enumerator =
udev::Enumerator::new().map_err(|err| AnimeError::Udev("enumerator failed".into(), err))?;
enumerator
.match_subsystem("usb")
.map_err(|err| AnimeError::Udev("match_subsystem failed".into(), err))?;
for device in enumerator
.scan_devices()
.map_err(|err| AnimeError::Udev("scan_devices failed".into(), err))?
{
if let Some(attr) = device.attribute_value("idProduct") {
if attr == id_product {
if let Some(dev_node) = device.devnode() {
return Ok(dev_node.to_string_lossy().to_string());
}
}
}
}
Err(AnimeError::NoDevice)
}
/// Get the two device initialization packets. These are required for device start /// Get the two device initialization packets. These are required for device start
/// after the laptop boots. /// after the laptop boots.
#[inline] #[inline]
Binary file not shown.

After

Width:  |  Height:  |  Size: 981 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

+5 -6
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "rog_aura" name = "rog_aura"
version = "1.1.0" version = "1.2.2"
license = "MPL-2.0" license = "MPL-2.0"
readme = "README.md" readme = "README.md"
authors = ["Luke <luke@ljones.dev>"] authors = ["Luke <luke@ljones.dev>"]
@@ -13,12 +13,11 @@ edition = "2018"
exclude = ["data"] exclude = ["data"]
[features] [features]
default = ["dbus"] default = ["dbus", "toml"]
dbus = ["zvariant", "zvariant_derive"] dbus = ["zvariant"]
[dependencies] [dependencies]
serde = "^1.0" serde = "^1.0"
serde_derive = "^1.0" serde_derive = "^1.0"
toml = { version = "^0.5", optional = true }
zvariant = { version = "^2.6", optional = true } zvariant = { version = "^3.0", optional = true }
zvariant_derive = { version = "^2.6", optional = true }
+161
View File
@@ -0,0 +1,161 @@
matches = [
'G513',
]
locale = "US"
[[rows]]
height = 0.8
row = [
'NormalSpacer',
'FuncSpacer',
'VolDown',
'VolUp',
'MicMute',
'Fan',
'Rog',
]
[[rows]]
height = 0.8
row = [
'Esc',
'FuncSpacer',
'F1',
'F2',
'F3',
'F4',
'FuncSpacer',
'F5',
'F6',
'F7',
'F8',
'FuncSpacer',
'F9',
'F10',
'F11',
'F12',
'RowEndSpacer',
'NumPadDel',
]
[[rows]]
height = 1.0
row = [
'Tilde',
'N1',
'N2',
'N3',
'N4',
'N5',
'N6',
'N7',
'N8',
'N9',
'N0',
'Hyphen',
'Equals',
'BkSpc',
'RowEndSpacer',
'Home',
]
[[rows]]
height = 1.0
row = [
'Tab',
'Q',
'W',
'E',
'R',
'T',
'Y',
'U',
'I',
'O',
'P',
'LBracket',
'RBracket',
'BackSlash',
'RowEndSpacer',
'PgUp',
]
[[rows]]
height = 1.0
row = [
'Caps',
'A',
'S',
'D',
'F',
'G',
'H',
'J',
'K',
'L',
'SemiColon',
'Quote',
'Return',
'RowEndSpacer',
'PgDn',
]
[[rows]]
height = 1.0
row = [
'LShift',
'Z',
'X',
'C',
'V',
'B',
'N',
'M',
'Comma',
'Period',
'FwdSlash',
'Rshift',
'RowEndSpacer',
'End',
]
[[rows]]
height = 1.2
row = [
'LCtrl',
'LFn',
'Meta',
'LAlt',
'Space',
'RAlt',
'PrtSc',
'RCtrl',
'ArrowSpacer',
'Up',
'ArrowSpacer',
'RowEndSpacer',
'RFn',
]
[[rows]]
height = 0.8
row = [
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'Left',
'Down',
'Right',
'ArrowSpacer',
]
+135
View File
@@ -0,0 +1,135 @@
matches = [
'G533',
]
locale = "US"
[[rows]]
height = 0.8
row = [
'NormalSpacer',
'FuncSpacer',
'VolDown',
'VolUp',
'MicMute',
'Fan',
'Rog',
]
[[rows]]
height = 0.8
row = [
'Esc',
'FuncSpacer',
'F1',
'F2',
'F3',
'F4',
'FuncSpacer',
'F5',
'F6',
'F7',
'F8',
'FuncSpacer',
'F9',
'F10',
'F11',
'F12',
'Del',
]
[[rows]]
height = 1.0
row = [
'Tilde',
'N1',
'N2',
'N3',
'N4',
'N5',
'N6',
'N7',
'N8',
'N9',
'N0',
'Hyphen',
'Equals',
'BkSpc',
'MediaPlay',
]
[[rows]]
height = 1.0
row = [
'Tab',
'Q',
'W',
'E',
'R',
'T',
'Y',
'U',
'I',
'O',
'P',
'LBracket',
'RBracket',
'BackSlash',
'MediaStop',
]
[[rows]]
height = 1.0
row = [
'Caps',
'A',
'S',
'D',
'F',
'G',
'H',
'J',
'K',
'L',
'SemiColon',
'Quote',
'Return',
'MediaPrev',
]
[[rows]]
height = 1.0
row = [
'LShift',
'Z',
'X',
'C',
'V',
'B',
'N',
'M',
'Comma',
'Period',
'FwdSlash',
'RshiftSmall',
'UpRegular',
'MediaNext',
]
[[rows]]
height = 1.0
row = [
'LCtrlMed',
'LFn',
'Meta',
'LAlt',
'Space',
'RAlt',
'PrtSc',
'RCtrl',
'ArrowRegularSpacer',
'LeftRegular',
'DownRegular',
'RightRegular',
]
+154
View File
@@ -0,0 +1,154 @@
matches = [
'GA401',
'GA402',
'GU603',
'GV301',
'GA502',
'GA503',
]
locale = "US"
[[rows]]
height = 0.8
row = [
'NormalSpacer',
'FuncSpacer',
'VolDown',
'VolUp',
'MicMute',
'Rog',
]
[[rows]]
height = 0.8
row = [
'Esc',
'FuncSpacer',
'F1',
'F2',
'F3',
'F4',
'FuncSpacer',
'F5',
'F6',
'F7',
'F8',
'FuncSpacer',
'F9',
'F10',
'F11',
'F12',
]
[[rows]]
height = 1.0
row = [
'Tilde',
'N1',
'N2',
'N3',
'N4',
'N5',
'N6',
'N7',
'N8',
'N9',
'N0',
'Hyphen',
'Equals',
'BkSpc',
]
[[rows]]
height = 1.0
row = [
'Tab',
'Q',
'W',
'E',
'R',
'T',
'Y',
'U',
'I',
'O',
'P',
'LBracket',
'RBracket',
'BackSlash',
]
[[rows]]
height = 1.0
row = [
'Caps',
'A',
'S',
'D',
'F',
'G',
'H',
'J',
'K',
'L',
'SemiColon',
'Quote',
'Return',
]
[[rows]]
height = 1.0
row = [
'LShift',
'Z',
'X',
'C',
'V',
'B',
'N',
'M',
'Comma',
'Period',
'FwdSlash',
'Rshift',
]
[[rows]]
height = 1.2
row = [
'LCtrl',
'LFn',
'Meta',
'LAlt',
'Space',
'RAlt',
'PrtSc',
'RCtrl',
'ArrowSpacer',
'Up',
'ArrowSpacer',
]
[[rows]]
height = 0.8
row = [
'FuncSpacer',
'FuncSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'Left',
'Down',
'Right',
'ArrowSpacer',
]
+180
View File
@@ -0,0 +1,180 @@
matches = [
'GL504',
]
locale = "US"
[[rows]]
height = 0.8
row = [
'NormalSpacer',
'FuncSpacer',
'VolDown',
'VolUp',
'MicMute',
'Rog',
]
[[rows]]
height = 0.8
row = [
'Esc',
'FuncSpacer',
'F1',
'F2',
'F3',
'F4',
'FuncSpacer',
'F5',
'F6',
'F7',
'F8',
'FuncSpacer',
'F9',
'F10',
'F11',
'F12',
'RowEndSpacer',
'Del',
'NumPadPause',
'NumPadPrtSc',
'NumPadHome',
]
[[rows]]
height = 1.0
row = [
'Tilde',
'N1',
'N2',
'N3',
'N4',
'N5',
'N6',
'N7',
'N8',
'N9',
'N0',
'Hyphen',
'Equals',
'BkSpc',
'RowEndSpacer',
'NumLock',
'FwdSlash',
'Star',
'Hyphen',
]
[[rows]]
height = 1.0
row = [
'Tab',
'Q',
'W',
'E',
'R',
'T',
'Y',
'U',
'I',
'O',
'P',
'LBracket',
'RBracket',
'BackSlash',
'RowEndSpacer',
'N7',
'N8',
'N9',
'NumPadPlus',
]
[[rows]]
height = 1.0
row = [
'Caps',
'A',
'S',
'D',
'F',
'G',
'H',
'J',
'K',
'L',
'SemiColon',
'Quote',
'Return',
'RowEndSpacer',
'N4',
'N5',
'N6',
'NumPadPlus',
]
[[rows]]
height = 1.0
row = [
'LShift',
'Z',
'X',
'C',
'V',
'B',
'N',
'M',
'Comma',
'Period',
'FwdSlash',
'Rshift',
'RowEndSpacer',
'N1',
'N2',
'N3',
'NumPadEnter',
]
[[rows]]
height = 1.0
row = [
'LCtrl',
'LFn',
'Meta',
'LAlt',
'Space',
'RAlt',
'RFn',
'RFn',
'RCtrlLarge',
'RowEndSpacer',
'UpRegular',
'N0',
'NumPadDel',
'NumPadEnter',
]
[[rows]]
height = 1.0
row = [
'FuncSpacer',
'FuncSpacer',
'FuncSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'LeftRegular',
'RowEndSpacer',
'DownRegular',
'RightRegular',
'NormalSpacer',
]
+172
View File
@@ -0,0 +1,172 @@
matches = [
'GX502',
'GU502',
]
locale = "US"
[[rows]]
height = 0.8
row = [
'NormalSpacer',
'FuncSpacer',
'VolDown',
'VolUp',
'MicMute',
'Rog',
]
[[rows]]
height = 0.8
row = [
'Esc',
'FuncSpacer',
'F1',
'F2',
'F3',
'F4',
'FuncSpacer',
'F5',
'F6',
'F7',
'F8',
'FuncSpacer',
'F9',
'F10',
'F11',
'F12',
'RowEndSpacer',
'Del',
]
[[rows]]
height = 1.0
row = [
'Tilde',
'N1',
'N2',
'N3',
'N4',
'N5',
'N6',
'N7',
'N8',
'N9',
'N0',
'Hyphen',
'Equals',
'BkSpc3_1',
'BkSpc3_2',
'BkSpc3_3',
'RowEndSpacer',
'Home',
]
[[rows]]
height = 1.0
row = [
'Tab',
'Q',
'W',
'E',
'R',
'T',
'Y',
'U',
'I',
'O',
'P',
'LBracket',
'RBracket',
'BackSlash',
'RowEndSpacer',
'PgUp',
]
[[rows]]
height = 1.0
row = [
'Caps',
'A',
'S',
'D',
'F',
'G',
'H',
'J',
'K',
'L',
'SemiColon',
'Quote',
'Return3_1',
'Return3_2',
'Return3_3',
'RowEndSpacer',
'PgDn',
]
[[rows]]
height = 1.0
row = [
'LShift',
'Z',
'X',
'C',
'V',
'B',
'N',
'M',
'Comma',
'Period',
'FwdSlash',
'Rshift3_1',
'Rshift3_2',
'Rshift3_3',
'RowEndSpacer',
'End',
]
[[rows]]
height = 1.2
row = [
'LCtrl',
'LFn',
'Meta',
'LAlt',
'Space5_1',
'Space5_2',
'Space5_3',
'Space5_4',
'Space5_5',
'RAlt',
'PrtSc',
'RCtrl',
'ArrowSpacer',
'Up',
'ArrowSpacer',
'RowEndSpacer',
'RFn',
]
[[rows]]
height = 0.8
row = [
'FuncSpacer',
'FuncSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'Left',
'Down',
'Right',
'ArrowSpacer',
]
+256 -24
View File
@@ -7,17 +7,10 @@ pub const LED_INIT5: [u8; 6] = [0x5e, 0x05, 0x20, 0x31, 0, 0x08];
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::str::FromStr; use std::str::FromStr;
#[cfg(feature = "dbus")] #[cfg(feature = "dbus")]
use zvariant_derive::Type; use zvariant::Type;
use crate::{error::Error, LED_MSG_LEN}; use crate::{error::Error, LED_MSG_LEN};
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, PartialEq, Copy, Clone, Deserialize, Serialize)]
pub struct LedPowerStates {
pub enabled: bool,
pub sleep_anim_enabled: bool,
}
#[cfg_attr(feature = "dbus", derive(Type))] #[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)] #[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
pub enum LedBrightness { pub enum LedBrightness {
@@ -70,6 +63,34 @@ impl FromStr for Colour {
} }
} }
impl From<&[f32; 3]> for Colour {
fn from(c: &[f32; 3]) -> Self {
Self(
(255.0 * c[0]) as u8,
(255.0 * c[1]) as u8,
(255.0 * c[2]) as u8,
)
}
}
impl From<Colour> for [f32; 3] {
fn from(c: Colour) -> Self {
[c.0 as f32 / 255.0, c.1 as f32 / 255.0, c.2 as f32 / 255.0]
}
}
impl From<&[u8; 3]> for Colour {
fn from(c: &[u8; 3]) -> Self {
Self(c[0], c[1], c[2])
}
}
impl From<Colour> for [u8; 3] {
fn from(c: Colour) -> Self {
[c.0, c.1, c.2]
}
}
#[cfg_attr(feature = "dbus", derive(Type))] #[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)] #[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
pub enum Speed { pub enum Speed {
@@ -129,8 +150,11 @@ impl FromStr for Direction {
/// Enum of modes that convert to the actual number required by a USB HID packet /// Enum of modes that convert to the actual number required by a USB HID packet
#[cfg_attr(feature = "dbus", derive(Type))] #[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Deserialize, Serialize)] #[derive(
Debug, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Deserialize, Serialize,
)]
pub enum AuraModeNum { pub enum AuraModeNum {
#[default]
Static = 0, Static = 0,
Breathe = 1, Breathe = 1,
Strobe = 2, Strobe = 2,
@@ -145,18 +169,38 @@ pub enum AuraModeNum {
Flash = 12, Flash = 12,
} }
impl From<AuraModeNum> for String {
fn from(mode: AuraModeNum) -> Self {
match mode {
AuraModeNum::Static => "Static",
AuraModeNum::Breathe => "Breathe",
AuraModeNum::Strobe => "Strobe",
AuraModeNum::Rainbow => "Rainbow",
AuraModeNum::Star => "Stars",
AuraModeNum::Rain => "Rain",
AuraModeNum::Highlight => "Highlight",
AuraModeNum::Laser => "Laser",
AuraModeNum::Ripple => "Ripple",
AuraModeNum::Pulse => "Pulse",
AuraModeNum::Comet => "Comet",
AuraModeNum::Flash => "Flash",
}
.to_string()
}
}
impl From<&AuraModeNum> for &str { impl From<&AuraModeNum> for &str {
fn from(mode: &AuraModeNum) -> Self { fn from(mode: &AuraModeNum) -> Self {
match mode { match mode {
AuraModeNum::Static => "Static", AuraModeNum::Static => "Static",
AuraModeNum::Breathe => "Breathing", AuraModeNum::Breathe => "Breathe",
AuraModeNum::Strobe => "Strobing", AuraModeNum::Strobe => "Strobe",
AuraModeNum::Rainbow => "Rainbow", AuraModeNum::Rainbow => "Rainbow",
AuraModeNum::Star => "Stars", AuraModeNum::Star => "Stars",
AuraModeNum::Rain => "Rain", AuraModeNum::Rain => "Rain",
AuraModeNum::Highlight => "Keypress Highlight", AuraModeNum::Highlight => "Highlight",
AuraModeNum::Laser => "Keypress Laser", AuraModeNum::Laser => "Laser",
AuraModeNum::Ripple => "Keypress Ripple", AuraModeNum::Ripple => "Ripple",
AuraModeNum::Pulse => "Pulse", AuraModeNum::Pulse => "Pulse",
AuraModeNum::Comet => "Comet", AuraModeNum::Comet => "Comet",
AuraModeNum::Flash => "Flash", AuraModeNum::Flash => "Flash",
@@ -167,14 +211,14 @@ impl From<&str> for AuraModeNum {
fn from(mode: &str) -> Self { fn from(mode: &str) -> Self {
match mode { match mode {
"Static" => AuraModeNum::Static, "Static" => AuraModeNum::Static,
"Breathing" => AuraModeNum::Breathe, "Breathe" => AuraModeNum::Breathe,
"Strobing" => AuraModeNum::Strobe, "Strobe" => AuraModeNum::Strobe,
"Rainbow" => AuraModeNum::Rainbow, "Rainbow" => AuraModeNum::Rainbow,
"Stars" => AuraModeNum::Star, "Stars" => AuraModeNum::Star,
"Rain" => AuraModeNum::Rain, "Rain" => AuraModeNum::Rain,
"Keypress Highlight" => AuraModeNum::Highlight, "Highlight" => AuraModeNum::Highlight,
"Keypress Laser" => AuraModeNum::Laser, "Laser" => AuraModeNum::Laser,
"Keypress Ripple" => AuraModeNum::Ripple, "Ripple" => AuraModeNum::Ripple,
"Pulse" => AuraModeNum::Pulse, "Pulse" => AuraModeNum::Pulse,
"Comet" => AuraModeNum::Comet, "Comet" => AuraModeNum::Comet,
"Flash" => AuraModeNum::Flash, "Flash" => AuraModeNum::Flash,
@@ -207,16 +251,60 @@ impl From<u8> for AuraModeNum {
#[cfg_attr(feature = "dbus", derive(Type))] #[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)] #[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
pub enum AuraZone { pub enum AuraZone {
/// Used if keyboard has no zones, or if setting all
None, None,
One, /// Leftmost zone
Two, Key1,
Three, /// Zone after leftmost
Four, Key2,
/// Zone second from right
Key3,
/// Rightmost zone
Key4,
/// Logo on the lid (or elsewhere?)
Logo,
/// The left part of a lightbar (typically on the front of laptop)
BarLeft,
/// The right part of a lightbar
BarRight,
}
impl Default for AuraZone {
fn default() -> Self {
AuraZone::None
}
}
impl FromStr for AuraZone {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
match s.to_ascii_lowercase().as_str() {
"0" => Ok(AuraZone::None),
"none" => Ok(AuraZone::None),
"1" => Ok(AuraZone::Key1),
"one" => Ok(AuraZone::Key1),
"2" => Ok(AuraZone::Key2),
"two" => Ok(AuraZone::Key2),
"3" => Ok(AuraZone::Key3),
"three" => Ok(AuraZone::Key3),
"4" => Ok(AuraZone::Key4),
"four" => Ok(AuraZone::Key4),
"5" => Ok(AuraZone::Logo),
"logo" => Ok(AuraZone::Logo),
"6" => Ok(AuraZone::BarLeft),
"lightbar-left" => Ok(AuraZone::BarLeft),
"7" => Ok(AuraZone::BarRight),
"lightbar-right" => Ok(AuraZone::BarRight),
_ => Err(Error::ParseSpeed),
}
}
} }
/// Default factory modes structure. This easily converts to an USB HID packet with: /// Default factory modes structure. This easily converts to an USB HID packet with:
/// ```rust /// ```rust
/// let bytes: [u8; LED_MSG_LEN] = mode.into(); /// // let bytes: [u8; LED_MSG_LEN] = mode.into();
/// ``` /// ```
#[cfg_attr(feature = "dbus", derive(Type))] #[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
@@ -273,6 +361,53 @@ impl Default for AuraEffect {
} }
} }
pub struct AuraParameters {
pub zone: bool,
pub colour1: bool,
pub colour2: bool,
pub speed: bool,
pub direction: bool,
}
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 => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Breathe => AuraParameters::new(true, true, true, true, false),
AuraModeNum::Strobe => 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::Rain => AuraParameters::new(true, false, false, true, false),
AuraModeNum::Highlight => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Laser => AuraParameters::new(true, true, false, true, false),
AuraModeNum::Ripple => AuraParameters::new(true, true, false, true, false),
AuraModeNum::Pulse => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Comet => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Flash => AuraParameters::new(true, true, false, false, false),
}
}
}
/// Parses `AuraEffect` in to packet data for writing to the USB interface /// Parses `AuraEffect` in to packet data for writing to the USB interface
/// ///
/// Byte structure where colour is RGB, one byte per R, G, B: /// Byte structure where colour is RGB, one byte per R, G, B:
@@ -299,3 +434,100 @@ impl From<&AuraEffect> for [u8; LED_MSG_LEN] {
msg msg
} }
} }
#[cfg(test)]
mod tests {
use crate::{AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed, LED_MSG_LEN};
#[test]
fn check_led_static_packet() {
let st = AuraEffect {
mode: AuraModeNum::Static,
zone: AuraZone::None,
colour1: Colour(0xff, 0x11, 0xdd),
colour2: Colour::default(),
speed: Speed::Med,
direction: Direction::Right,
};
let ar = <[u8; 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,
];
assert_eq!(ar, check);
}
#[test]
fn check_led_static_zone_packet() {
let mut st = AuraEffect {
mode: AuraModeNum::Static,
zone: AuraZone::Key1,
colour1: Colour(0xff, 0, 0),
colour2: Colour(0, 0, 0),
speed: Speed::Low,
direction: Direction::Left,
};
let capture = [
0x5d, 0xb3, 0x01, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.zone = AuraZone::Key2;
st.colour1 = Colour(0xff, 0xff, 0);
let capture = [
0x5d, 0xb3, 0x02, 0x00, 0xff, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.zone = AuraZone::Key3;
st.colour1 = Colour(0, 0xff, 0xff);
let capture = [
0x5d, 0xb3, 0x03, 0x00, 0x00, 0xff, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.zone = AuraZone::Key4;
st.colour1 = Colour(0xff, 0x00, 0xff);
let capture = [
0x5d, 0xb3, 0x04, 0x00, 0xff, 0x00, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.zone = AuraZone::Logo;
st.colour1 = Colour(0x2c, 0xff, 0x00);
let capture = [
0x5d, 0xb3, 0x05, 0x00, 0x2c, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.zone = AuraZone::BarLeft;
st.colour1 = Colour(0xff, 0x00, 0x00);
let capture = [
0x5d, 0xb3, 0x06, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.zone = AuraZone::BarRight;
st.colour1 = Colour(0xff, 0x00, 0xcd);
let capture = [
0x5d, 0xb3, 0x07, 0x00, 0xff, 0x00, 0xcd, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.mode = AuraModeNum::Rainbow;
let capture = [
0x5d, 0xb3, 0x07, 0x03, 0xff, 0x00, 0xcd, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
}
}
+16 -2
View File
@@ -7,7 +7,8 @@ pub enum Error {
ParseSpeed, ParseSpeed,
ParseDirection, ParseDirection,
ParseBrightness, ParseBrightness,
ParseAnime, Io(std::io::Error),
Toml(toml::de::Error),
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@@ -18,9 +19,22 @@ impl fmt::Display for Error {
Error::ParseSpeed => write!(f, "Could not parse speed"), Error::ParseSpeed => write!(f, "Could not parse speed"),
Error::ParseDirection => write!(f, "Could not parse direction"), Error::ParseDirection => write!(f, "Could not parse direction"),
Error::ParseBrightness => write!(f, "Could not parse brightness"), Error::ParseBrightness => write!(f, "Could not parse brightness"),
Error::ParseAnime => write!(f, "Could not parse anime"), Error::Io(io) => write!(f, "IO Error: {io}"),
Error::Toml(e) => write!(f, "TOML Parse Error: {e}"),
} }
} }
} }
impl error::Error for Error {} impl error::Error for Error {}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Self::Io(e)
}
}
impl From<toml::de::Error> for Error {
fn from(e: toml::de::Error) -> Self {
Self::Toml(e)
}
}
+155
View File
@@ -0,0 +1,155 @@
use crate::keys::Key;
impl From<Key> for &str {
fn from(k: Key) -> Self {
(&k).into()
}
}
impl From<&Key> for &str {
fn from(k: &Key) -> Self {
match k {
Key::VolUp => "Volume Up",
Key::VolDown => "Volume Down",
Key::MicMute => "Mute Mic",
Key::Rog => "ROG",
Key::Fan => "Fan Control",
Key::Esc => "Escape",
Key::F1 => "F1",
Key::F2 => "F2",
Key::F3 => "F3",
Key::F4 => "F4",
Key::F5 => "F5",
Key::F6 => "F6",
Key::F7 => "F7",
Key::F8 => "F8",
Key::F9 => "F9",
Key::F10 => "F10",
Key::F11 => "F11",
Key::F12 => "F12",
Key::Del => "Delete",
Key::Tilde => "Tilde",
Key::N1 => "1",
Key::N2 => "2",
Key::N3 => "3",
Key::N4 => "4",
Key::N5 => "5",
Key::N6 => "6",
Key::N7 => "7",
Key::N8 => "8",
Key::N9 => "9",
Key::N0 => "0",
Key::Hyphen => "-",
Key::Equals => "=",
Key::BkSpc => "Backspace",
Key::BkSpc3_1 => "Backspace LED 1",
Key::BkSpc3_2 => "Backspace LED 2",
Key::BkSpc3_3 => "Backspace LED 3",
Key::Home => "Home",
Key::Tab => "Tab",
Key::Q => "Q",
Key::W => "W",
Key::E => "E",
Key::R => "R",
Key::T => "T",
Key::Y => "Y",
Key::U => "U",
Key::I => "I",
Key::O => "O",
Key::P => "P",
Key::LBracket => "[",
Key::RBracket => "]",
Key::BackSlash => "\\",
Key::PgUp => "Page Up",
Key::Caps => "Caps Lock",
Key::A => "A",
Key::S => "S",
Key::D => "D",
Key::F => "F",
Key::G => "G",
Key::H => "H",
Key::J => "J",
Key::K => "K",
Key::L => "L",
Key::SemiColon => ";",
Key::Quote => "'",
Key::Return => "Return",
Key::Return3_1 => "Return LED 1",
Key::Return3_2 => "Return LED 2",
Key::Return3_3 => "Return LED 3",
Key::PgDn => "Page Down",
Key::LShift => "Left Shift",
Key::LShift3_1 => "Left Shift LED 1",
Key::LShift3_2 => "Left Shift LED 2",
Key::LShift3_3 => "Left Shift LED 3",
Key::Z => "Z",
Key::X => "X",
Key::C => "C",
Key::V => "V",
Key::B => "B",
Key::N => "N",
Key::M => "M",
Key::Comma => ",",
Key::Period => ".",
Key::Star => "*",
Key::NumPadDel => "Delete",
Key::NumPadPlus => "+",
Key::NumPadEnter => "Enter",
Key::NumPadPause => "Pause",
Key::NumPadPrtSc => "Print Screen",
Key::NumPadHome => "Home",
Key::NumLock => "Num-Lock",
Key::FwdSlash => "/",
Key::Rshift => "Right Shift",
Key::RshiftSmall => "Right Shift",
Key::Rshift3_1 => "Right Shift LED 1",
Key::Rshift3_2 => "Right Shift LED 2",
Key::Rshift3_3 => "Right Shift LED 3",
Key::End => "End",
Key::LCtrl => "Left Control",
Key::LCtrlMed => "Left Control",
Key::LFn => "Left Fn",
Key::Meta => "Meta",
Key::LAlt => "Left Alt",
Key::Space => "Space",
Key::Space5_1 => "Space LED 1",
Key::Space5_2 => "Space LED 2",
Key::Space5_3 => "Space LED 3",
Key::Space5_4 => "Space LED 4",
Key::Space5_5 => "Space LED 5",
Key::RAlt => "Right Alt",
Key::PrtSc => "Print Screen",
Key::RCtrl => "Right Control",
Key::RCtrlLarge => "Right Control",
Key::Pause => "Pause",
Key::Up => "Up",
Key::Down => "Down",
Key::Left => "Left",
Key::Right => "Right",
Key::UpRegular => "Up",
Key::DownRegular => "Down",
Key::LeftRegular => "Left",
Key::RightRegular => "Right",
Key::UpSplit => "Up",
Key::DownSplit => "Down",
Key::LeftSplit => "Left",
Key::RightSplit => "Right",
Key::RFn => "Right Fn",
Key::MediaPlay => "Media Play",
Key::MediaStop => "Media Stop",
Key::MediaNext => "Media Next",
Key::MediaPrev => "Media Previous",
Key::NormalBlank => "",
Key::NormalSpacer => "",
Key::FuncBlank => "",
Key::FuncSpacer => "",
Key::ArrowBlank => "",
Key::ArrowSpacer => "",
Key::ArrowRegularBlank => "",
Key::ArrowRegularSpacer => "",
Key::ArrowSplitBlank => "",
Key::ArrowSplitSpacer => "",
Key::RowEndSpacer => "",
}
}
}
+355
View File
@@ -0,0 +1,355 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
pub enum Key {
VolUp,
VolDown,
MicMute,
Rog,
Fan,
Esc,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
Del,
Tilde,
N1,
N2,
N3,
N4,
N5,
N6,
N7,
N8,
N9,
N0,
Hyphen,
Equals,
BkSpc,
BkSpc3_1,
BkSpc3_2,
BkSpc3_3,
Home,
Tab,
Q,
W,
E,
R,
T,
Y,
U,
I,
O,
P,
LBracket,
RBracket,
BackSlash,
PgUp,
Caps,
A,
S,
D,
F,
G,
H,
J,
K,
L,
SemiColon,
Quote,
Return,
Return3_1,
Return3_2,
Return3_3,
PgDn,
LShift,
LShift3_1,
LShift3_2,
LShift3_3,
Z,
X,
C,
V,
B,
N,
M,
Comma,
Period,
FwdSlash,
Star,
NumPadDel,
NumPadPlus,
NumPadEnter,
NumPadPause,
NumPadPrtSc,
NumPadHome,
NumLock,
Rshift,
RshiftSmall,
Rshift3_1,
Rshift3_2,
Rshift3_3,
End,
LCtrl,
LCtrlMed,
LFn,
Meta,
LAlt,
Space,
Space5_1,
Space5_2,
Space5_3,
Space5_4,
Space5_5,
Pause,
RAlt,
PrtSc,
RCtrl,
RCtrlLarge,
Up,
Down,
Left,
Right,
UpRegular,
DownRegular,
LeftRegular,
RightRegular,
UpSplit,
DownSplit,
LeftSplit,
RightSplit,
RFn,
MediaPlay,
MediaStop,
MediaNext,
MediaPrev,
NormalBlank,
/// To be ignored by per-key effects
NormalSpacer,
FuncBlank,
/// To be ignored by per-key effects
FuncSpacer,
ArrowBlank,
/// To be ignored by per-key effects
ArrowSpacer,
ArrowRegularBlank,
/// To be ignored by per-key effects
ArrowRegularSpacer,
ArrowSplitBlank,
/// To be ignored by per-key effects
ArrowSplitSpacer,
/// A gap between regular rows and the rightside buttons
RowEndSpacer,
}
/// Types of shapes of LED on keyboards. The shape is used for visual representations
///
/// A post fix of Spacer *must be ignored by per-key effects
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize)]
pub enum KeyShape {
Tilde,
#[default]
Normal,
NormalBlank,
NormalSpacer,
Func,
FuncBlank,
FuncSpacer,
Space,
Space5,
LCtrlMed,
LShift,
/// Used in a group of 3 (LED's)
LShift3,
RShift,
RshiftSmall,
/// Used in a group of 3 (LED's)
RShift3,
Return,
Return3,
Tab,
Caps,
Backspace,
/// Used in a group of 3 (LED's)
Backspace3,
Arrow,
ArrowBlank,
ArrowSpacer,
ArrowSplit,
ArrowSplitBlank,
ArrowSplitSpacer,
ArrowRegularBlank,
ArrowRegularSpacer,
RowEndSpacer,
}
impl KeyShape {
pub const fn width(&self) -> f32 {
match self {
Self::Tilde => 0.8,
Self::Normal => 1.0,
Self::NormalBlank => 1.0,
Self::NormalSpacer => 1.0,
Self::Func => 1.0,
Self::FuncBlank => 1.0,
Self::FuncSpacer => 0.6,
Self::Space => 5.0,
Self::Space5 => 1.0,
Self::LCtrlMed => 1.1,
Self::LShift => 2.0,
Self::LShift3 => 0.67,
Self::RShift => 2.8,
Self::RshiftSmall => 1.8,
Self::RShift3 => 0.93,
Self::Return => 2.2,
Self::Return3 => 0.7333,
Self::Tab => 1.4,
Self::Caps => 1.6,
Self::Backspace => 2.0,
Self::Backspace3 => 0.666,
Self::ArrowRegularBlank | Self::ArrowRegularSpacer => 0.7,
Self::Arrow => 0.8,
Self::ArrowBlank | Self::ArrowSpacer => 1.0,
Self::ArrowSplit | Self::ArrowSplitBlank | Self::ArrowSplitSpacer => 1.0,
Self::RowEndSpacer => 0.1,
}
}
/// A blank is used to space keys out in GUI's and can be used or ignored
/// depednign on the per-key effect
pub const fn is_blank(&self) -> bool {
match self {
Self::NormalBlank
| Self::FuncBlank
| Self::ArrowBlank
| Self::ArrowSplitBlank
| Self::ArrowRegularBlank => true,
_ => false,
}
}
/// A spacer is used to space keys out in GUI's, but ignored in per-key effects
pub const fn is_spacer(&self) -> bool {
match self {
Self::FuncSpacer
| Self::NormalSpacer
| Self::ArrowSpacer
| Self::ArrowSplitSpacer
| Self::ArrowRegularSpacer => true,
_ => false,
}
}
/// All keys with a postfix of some number
pub const fn is_group(&self) -> bool {
match self {
Self::LShift3 | Self::RShift3 => true,
Self::Return3 | Self::Space5 | Self::Backspace3 => true,
_ => false,
}
}
/// Mostly intended as a helper for signalling when to draw a
/// split/compact arrow cluster
pub const fn is_arrow_cluster(&self) -> bool {
match self {
Self::Arrow | Self::ArrowBlank | Self::ArrowSpacer => true,
_ => false,
}
}
pub const fn is_arrow_splits(&self) -> bool {
match self {
Self::ArrowSplit | Self::ArrowSplitBlank | Self::ArrowSplitSpacer => true,
_ => false,
}
}
}
impl From<Key> for KeyShape {
fn from(k: Key) -> Self {
match k {
Key::VolUp
| Key::VolDown
| Key::MicMute
| Key::Rog
| Key::Fan
| Key::Esc
| Key::F1
| Key::F2
| Key::F3
| Key::F4
| Key::F5
| Key::F6
| Key::F7
| Key::F8
| Key::F9
| Key::F10
| Key::F11
| Key::F12
| Key::Del => KeyShape::Func,
Key::Tilde => KeyShape::Tilde,
Key::BkSpc => KeyShape::Backspace,
Key::BkSpc3_1 | Key::BkSpc3_2 | Key::BkSpc3_3 => KeyShape::Backspace3,
Key::Tab | Key::BackSlash => KeyShape::Tab,
Key::Caps => KeyShape::Caps,
Key::Return => KeyShape::Return,
Key::Return3_1 | Key::Return3_2 | Key::Return3_3 => KeyShape::Return3,
Key::LCtrlMed => KeyShape::LCtrlMed,
Key::LShift => KeyShape::LShift,
Key::Rshift | Key::RCtrlLarge => KeyShape::RShift,
Key::RshiftSmall => KeyShape::RshiftSmall,
Key::Rshift3_1 | Key::Rshift3_2 | Key::Rshift3_3 => KeyShape::RShift3,
Key::Space => KeyShape::Space,
Key::Space5_1 | Key::Space5_2 | Key::Space5_3 | Key::Space5_4 | Key::Space5_5 => {
KeyShape::Space5
}
Key::NumPadPause | Key::NumPadPrtSc | Key::NumPadHome | Key::NumPadDel => {
KeyShape::Func
}
Key::NormalBlank => KeyShape::NormalBlank,
Key::NormalSpacer => KeyShape::NormalSpacer,
Key::FuncBlank => KeyShape::FuncBlank,
Key::FuncSpacer => KeyShape::FuncSpacer,
Key::Up | Key::Down | Key::Left | Key::Right => KeyShape::Arrow,
Key::ArrowBlank => KeyShape::ArrowBlank,
Key::ArrowSpacer => KeyShape::ArrowSpacer,
Key::ArrowRegularBlank => KeyShape::ArrowRegularBlank,
Key::ArrowRegularSpacer => KeyShape::ArrowRegularSpacer,
Key::UpSplit | Key::LeftSplit | Key::DownSplit | Key::RightSplit => {
KeyShape::ArrowSplit
}
Key::ArrowSplitBlank => KeyShape::ArrowSplitBlank,
Key::ArrowSplitSpacer => KeyShape::ArrowSplitSpacer,
Key::RowEndSpacer => KeyShape::RowEndSpacer,
_ => KeyShape::Normal,
}
}
}
impl From<&Key> for KeyShape {
fn from(k: &Key) -> Self {
(*k).into()
}
}
+170
View File
@@ -0,0 +1,170 @@
use super::{KeyLayout, KeyRow};
use crate::keys::Key;
impl KeyLayout {
/// Similar to GX502, but not per-key enabled
pub fn g513_layout() -> Self {
Self {
matches: vec!["G513".into()],
locale: "US".to_string(),
rows: vec![
KeyRow::new(
0.8,
vec![
Key::NormalSpacer,
Key::FuncSpacer,
Key::VolDown,
Key::VolUp,
Key::MicMute,
Key::Fan,
Key::Rog,
],
),
KeyRow::new(
0.8,
vec![
Key::Esc,
Key::FuncSpacer,
Key::F1,
Key::F2,
Key::F3,
Key::F4,
Key::FuncSpacer, // not sure which key to put here
Key::F5,
Key::F6,
Key::F7,
Key::F8,
Key::FuncSpacer,
Key::F9,
Key::F10,
Key::F11,
Key::F12,
Key::RowEndSpacer,
Key::Del,
],
),
KeyRow::new(
1.0,
vec![
Key::Tilde,
Key::N1,
Key::N2,
Key::N3,
Key::N4,
Key::N5,
Key::N6,
Key::N7,
Key::N8,
Key::N9,
Key::N0,
Key::Hyphen,
Key::Equals,
Key::BkSpc,
Key::RowEndSpacer,
Key::Home,
],
),
KeyRow::new(
1.0,
vec![
Key::Tab,
Key::Q,
Key::W,
Key::E,
Key::R,
Key::T,
Key::Y,
Key::U,
Key::I,
Key::O,
Key::P,
Key::LBracket,
Key::RBracket,
Key::BackSlash,
Key::RowEndSpacer,
Key::PgUp,
],
),
KeyRow::new(
1.0,
vec![
Key::Caps,
Key::A,
Key::S,
Key::D,
Key::F,
Key::G,
Key::H,
Key::J,
Key::K,
Key::L,
Key::SemiColon,
Key::Quote,
Key::Return,
Key::RowEndSpacer,
Key::PgDn,
],
),
KeyRow::new(
1.0,
vec![
Key::LShift,
Key::Z,
Key::X,
Key::C,
Key::V,
Key::B,
Key::N,
Key::M,
Key::Comma,
Key::Period,
Key::FwdSlash,
Key::Rshift,
Key::RowEndSpacer,
Key::End,
],
),
KeyRow::new(
1.0,
vec![
Key::LCtrl,
Key::LFn,
Key::Meta,
Key::LAlt,
Key::Space,
Key::RAlt,
Key::PrtSc,
Key::RCtrl,
Key::ArrowSpacer,
Key::Up,
Key::ArrowSpacer,
Key::RowEndSpacer,
Key::RFn,
],
),
KeyRow::new(
1.0,
vec![
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::Left,
Key::Down,
Key::Right,
Key::ArrowSpacer,
],
),
],
}
}
}
+156
View File
@@ -0,0 +1,156 @@
use super::{KeyLayout, KeyRow};
use crate::keys::Key;
impl KeyLayout {
pub fn ga401_layout() -> Self {
Self {
matches: vec!["GA401".into(), "GA402".into()],
locale: "US".to_string(),
rows: vec![
KeyRow::new(
0.8,
vec![
Key::NormalSpacer,
Key::FuncSpacer,
Key::VolDown,
Key::VolUp,
Key::MicMute,
Key::Rog,
],
),
KeyRow::new(
0.8,
vec![
Key::Esc,
Key::FuncSpacer,
Key::F1,
Key::F2,
Key::F3,
Key::F4,
Key::FuncSpacer, // not sure which key to put here
Key::F5,
Key::F6,
Key::F7,
Key::F8,
Key::FuncSpacer,
Key::F9,
Key::F10,
Key::F11,
Key::F12,
],
),
KeyRow::new(
1.0,
vec![
Key::Tilde,
Key::N1,
Key::N2,
Key::N3,
Key::N4,
Key::N5,
Key::N6,
Key::N7,
Key::N8,
Key::N9,
Key::N0,
Key::Hyphen,
Key::Equals,
Key::BkSpc,
],
),
KeyRow::new(
1.0,
vec![
Key::Tab,
Key::Q,
Key::W,
Key::E,
Key::R,
Key::T,
Key::Y,
Key::U,
Key::I,
Key::O,
Key::P,
Key::LBracket,
Key::RBracket,
Key::BackSlash,
],
),
KeyRow::new(
1.0,
vec![
Key::Caps,
Key::A,
Key::S,
Key::D,
Key::F,
Key::G,
Key::H,
Key::J,
Key::K,
Key::L,
Key::SemiColon,
Key::Quote,
Key::Return,
],
),
KeyRow::new(
1.0,
vec![
Key::LShift,
Key::Z,
Key::X,
Key::C,
Key::V,
Key::B,
Key::N,
Key::M,
Key::Comma,
Key::Period,
Key::FwdSlash,
Key::Rshift,
],
),
KeyRow::new(
1.0,
vec![
Key::LCtrl,
Key::LFn,
Key::Meta,
Key::LAlt,
Key::Space,
Key::RAlt,
Key::PrtSc,
Key::RCtrl,
Key::ArrowSpacer,
Key::Up,
Key::ArrowSpacer,
],
),
KeyRow::new(
1.2,
vec![
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::Left,
Key::Down,
Key::Right,
Key::ArrowSpacer,
],
),
],
}
}
}
+178
View File
@@ -0,0 +1,178 @@
use super::{KeyLayout, KeyRow};
use crate::keys::Key;
impl KeyLayout {
pub fn gx502_layout() -> Self {
Self {
matches: vec!["GX502".into(), "GU502".into()],
locale: "US".to_string(),
rows: vec![
KeyRow::new(
0.8,
vec![
Key::NormalSpacer,
Key::FuncSpacer,
Key::VolDown,
Key::VolUp,
Key::MicMute,
Key::Rog,
],
),
KeyRow::new(
0.8,
vec![
Key::Esc,
Key::FuncSpacer,
Key::F1,
Key::F2,
Key::F3,
Key::F4,
Key::FuncSpacer, // not sure which key to put here
Key::F5,
Key::F6,
Key::F7,
Key::F8,
Key::FuncSpacer,
Key::F9,
Key::F10,
Key::F11,
Key::F12,
Key::RowEndSpacer,
Key::Del,
],
),
KeyRow::new(
1.0,
vec![
Key::Tilde,
Key::N1,
Key::N2,
Key::N3,
Key::N4,
Key::N5,
Key::N6,
Key::N7,
Key::N8,
Key::N9,
Key::N0,
Key::Hyphen,
Key::Equals,
Key::BkSpc3_1,
Key::BkSpc3_2,
Key::BkSpc3_3,
Key::RowEndSpacer,
Key::Home,
],
),
KeyRow::new(
1.0,
vec![
Key::Tab,
Key::Q,
Key::W,
Key::E,
Key::R,
Key::T,
Key::Y,
Key::U,
Key::I,
Key::O,
Key::P,
Key::LBracket,
Key::RBracket,
Key::BackSlash,
Key::RowEndSpacer,
Key::PgUp,
],
),
KeyRow::new(
1.0,
vec![
Key::Caps,
Key::A,
Key::S,
Key::D,
Key::F,
Key::G,
Key::H,
Key::J,
Key::K,
Key::L,
Key::SemiColon,
Key::Quote,
Key::Return3_1,
Key::Return3_2,
Key::Return3_3,
Key::RowEndSpacer,
Key::PgDn,
],
),
KeyRow::new(
1.0,
vec![
Key::LShift,
Key::Z,
Key::X,
Key::C,
Key::V,
Key::B,
Key::N,
Key::M,
Key::Comma,
Key::Period,
Key::FwdSlash,
Key::Rshift3_1,
Key::Rshift3_2,
Key::Rshift3_3,
Key::RowEndSpacer,
Key::End,
],
),
KeyRow::new(
1.0,
vec![
Key::LCtrl,
Key::LFn,
Key::Meta,
Key::LAlt,
Key::Space5_1,
Key::Space5_2,
Key::Space5_3,
Key::Space5_4,
Key::Space5_5,
Key::RAlt,
Key::PrtSc,
Key::RCtrl,
Key::ArrowSpacer,
Key::Up,
Key::ArrowSpacer,
Key::RowEndSpacer,
Key::RFn,
],
),
KeyRow::new(
1.0,
vec![
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::Left,
Key::Down,
Key::Right,
Key::ArrowSpacer,
],
),
],
}
}
}
+69
View File
@@ -0,0 +1,69 @@
/// Hardcoded layout. Was used to generate a toml default
pub mod g513;
/// Hardcoded layout. Was used to generate a toml default
pub mod ga401;
/// Hardcoded layout. Was used to generate a toml default
pub mod gx502;
use crate::{error::Error, keys::Key};
use serde::{Deserialize, Serialize};
use std::{fs::OpenOptions, io::Read, path::Path, slice::Iter};
#[derive(Debug, Deserialize, Serialize)]
pub struct KeyLayout {
/// A series of board names that this layout can be used for. The board names
/// stored with the layout can be globbed, e.g, GA401 will match all of the
/// GA401I and GA401Q range variants.
///
/// `/sys/class/dmi/id/board_name`
matches: Vec<String>,
locale: String,
rows: Vec<KeyRow>,
}
impl KeyLayout {
pub fn from_file(path: &Path) -> Result<Self, Error> {
let mut file = OpenOptions::new().read(true).open(path)?;
let mut buf = String::new();
let read_len = file.read_to_string(&mut buf)?;
if read_len == 0 {
return Err(Error::Io(std::io::ErrorKind::InvalidData.into()));
} else {
return Ok(toml::from_str::<Self>(&buf)?);
}
}
pub fn matches(&self, board_name: &str) -> bool {
let board = board_name.to_ascii_uppercase();
for tmp in self.matches.iter() {
if board.contains(tmp.as_str()) {
return true;
}
}
false
}
pub fn rows(&self) -> Iter<KeyRow> {
self.rows.iter()
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct KeyRow {
height: f32,
row: Vec<Key>,
}
impl KeyRow {
pub fn new(height: f32, row: Vec<Key>) -> Self {
Self { height, row }
}
pub fn row(&self) -> Iter<Key> {
self.row.iter()
}
pub fn height(&self) -> f32 {
self.height
}
}
+14 -4
View File
@@ -8,11 +8,21 @@ pub use builtin_modes::*;
mod per_key_rgb; mod per_key_rgb;
pub use per_key_rgb::*; pub use per_key_rgb::*;
pub mod error;
pub mod key_to_str;
pub mod keys;
pub use key_to_str::*;
pub mod layouts;
pub mod usb; pub mod usb;
pub mod error;
pub const LED_MSG_LEN: usize = 17; pub const LED_MSG_LEN: usize = 17;
pub static VERSION: &str = env!("CARGO_PKG_VERSION"); pub static VERSION: &str = env!("CARGO_PKG_VERSION");
pub const RED: Colour = Colour(0xff, 0x00, 0x00);
pub const GREEN: Colour = Colour(0x00, 0xff, 0x00);
pub const BLUE: Colour = Colour(0x00, 0x00, 0xff);
pub const VIOLET: Colour = Colour(0x9B, 0x26, 0xB6);
pub const TEAL: Colour = Colour(0x00, 0x7C, 0x80);
pub const YELLOW: Colour = Colour(0xff, 0xef, 0x00);
pub const ORANGE: Colour = Colour(0xff, 0xa4, 0x00);
pub const GRADIENT: [Colour; 7] = [RED, VIOLET, BLUE, TEAL, GREEN, YELLOW, ORANGE];
+109 -173
View File
@@ -1,3 +1,5 @@
use crate::keys::Key;
/// A `KeyColourArray` contains all data to change the full set of keyboard /// A `KeyColourArray` contains all data to change the full set of keyboard
/// key colours individually. /// key colours individually.
/// ///
@@ -45,16 +47,16 @@ impl KeyColourArray {
#[inline] #[inline]
pub fn set(&mut self, key: Key, r: u8, g: u8, b: u8) { pub fn set(&mut self, key: Key, r: u8, g: u8, b: u8) {
if let Some((rr, gg, bb)) = self.key(key) { if let Some(c) = self.rgb(key) {
*rr = r; c[0] = r;
*gg = g; c[1] = g;
*bb = b; c[2] = b;
} }
} }
/// Indexes in to `KeyColourArray` at the correct row and column /// Indexes in to `KeyColourArray` at the correct row and column
/// to set a series of three bytes to the chosen R,G,B values /// to set a series of three bytes to the chosen R,G,B values
pub fn key(&mut self, key: Key) -> Option<(&mut u8, &mut u8, &mut u8)> { pub fn rgb(&mut self, key: Key) -> Option<&mut [u8]> {
// Tuples are indexes in to array // Tuples are indexes in to array
let (row, col) = match key { let (row, col) = match key {
Key::VolDown => (0, 15), Key::VolDown => (0, 15),
@@ -91,9 +93,9 @@ impl KeyColourArray {
Key::N0 => (3, 21), Key::N0 => (3, 21),
Key::Hyphen => (3, 24), Key::Hyphen => (3, 24),
Key::Equals => (3, 27), Key::Equals => (3, 27),
Key::BkSpc1 => (3, 30), Key::BkSpc3_1 => (3, 30),
Key::BkSpc2 => (3, 33), Key::BkSpc3_2 => (3, 33),
Key::BkSpc3 => (3, 36), Key::BkSpc3_3 => (3, 36),
Key::Home => (3, 39), Key::Home => (3, 39),
Key::Tab => (3, 54), Key::Tab => (3, 54),
// //
@@ -125,11 +127,16 @@ impl KeyColourArray {
Key::SemiColon => (5, 51), Key::SemiColon => (5, 51),
Key::Quote => (5, 54), Key::Quote => (5, 54),
// //
Key::Ret1 => (6, 12), Key::Return => (6, 9),
Key::Ret2 => (6, 15), Key::Return3_1 => (6, 12),
Key::Ret3 => (6, 18), Key::Return3_2 => (6, 15),
Key::Return3_3 => (6, 18),
Key::PgDn => (6, 21), Key::PgDn => (6, 21),
Key::LShift => (6, 36), Key::LShift => (6, 36),
// TODO: Find correct locations
Key::LShift3_1 => (6, 36),
Key::LShift3_2 => (6, 36),
Key::LShift3_3 => (6, 36),
Key::Z => (6, 42), Key::Z => (6, 42),
Key::X => (6, 45), Key::X => (6, 45),
Key::C => (6, 48), Key::C => (6, 48),
@@ -141,19 +148,21 @@ impl KeyColourArray {
Key::Comma => (7, 15), Key::Comma => (7, 15),
Key::Period => (7, 18), Key::Period => (7, 18),
Key::FwdSlash => (7, 21), Key::FwdSlash => (7, 21),
Key::Rshift1 => (7, 27), Key::Rshift => (7, 24),
Key::Rshift2 => (7, 30), Key::Rshift3_1 => (7, 27),
Key::Rshift3 => (7, 33), Key::Rshift3_2 => (7, 30),
Key::Rshift3_3 => (7, 33),
Key::End => (7, 36), Key::End => (7, 36),
Key::LCtrl => (7, 51), Key::LCtrl => (7, 51),
Key::LFn => (7, 54), Key::LFn => (7, 54),
// //
Key::Meta => (8, 9), Key::Meta => (8, 9),
Key::LAlt => (8, 12), Key::LAlt => (8, 12),
Key::Space1 => (8, 15), Key::Space5_1 => (8, 15),
Key::Space2 => (8, 18), Key::Space5_2 => (8, 18),
Key::Space3 => (8, 21), Key::Space5_3 => (8, 21),
Key::Space4 => (8, 24), Key::Space5_4 => (8, 24),
Key::Space5_5 => (8, 27),
Key::RAlt => (8, 30), Key::RAlt => (8, 30),
Key::PrtSc => (8, 33), Key::PrtSc => (8, 33),
Key::RCtrl => (8, 36), Key::RCtrl => (8, 36),
@@ -164,16 +173,45 @@ impl KeyColourArray {
// //
Key::Down => (10, 9), Key::Down => (10, 9),
Key::Right => (10, 12), Key::Right => (10, 12),
Key::None => return None, Key::NormalBlank
| Key::FuncBlank
| Key::NormalSpacer
| Key::FuncSpacer
| Key::ArrowBlank
| Key::ArrowSpacer
| Key::UpRegular
| Key::DownRegular
| Key::LeftRegular
| Key::RightRegular
| Key::UpSplit
| Key::DownSplit
| Key::LeftSplit
| Key::RightSplit
| Key::ArrowRegularBlank
| Key::ArrowRegularSpacer
| Key::ArrowSplitBlank
| Key::ArrowSplitSpacer
| Key::RshiftSmall
| Key::LCtrlMed
| Key::MediaPlay
| Key::MediaStop
| Key::MediaPrev
| Key::MediaNext
| Key::Pause
| Key::NumLock
| Key::Star
| Key::NumPadDel
| Key::NumPadPlus
| Key::NumPadEnter
| Key::NumPadPause
| Key::NumPadPrtSc
| Key::NumPadHome
| Key::RCtrlLarge
| Key::RowEndSpacer => return None,
Key::Fan | Key::Space | Key::BkSpc => return None,
}; };
// LOLOLOLOLOLOLOL! Look it's safe okay
unsafe { Some(&mut self.0[row][col..2])
Some((
&mut *(&mut self.0[row][col] as *mut u8),
&mut *(&mut self.0[row][col + 1] as *mut u8),
&mut *(&mut self.0[row][col + 2] as *mut u8),
))
}
} }
#[inline] #[inline]
@@ -182,108 +220,6 @@ impl KeyColourArray {
} }
} }
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Key {
VolUp,
VolDown,
MicMute,
Rog,
Esc,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
Del,
Tilde,
N1,
N2,
N3,
N4,
N5,
N6,
N7,
N8,
N9,
N0,
Hyphen,
Equals,
BkSpc1,
BkSpc2,
BkSpc3,
Home,
Tab,
Q,
W,
E,
R,
T,
Y,
U,
I,
O,
P,
LBracket,
RBracket,
BackSlash,
PgUp,
Caps,
A,
S,
D,
F,
G,
H,
J,
K,
L,
SemiColon,
Quote,
Ret1,
Ret2,
Ret3,
PgDn,
LShift,
Z,
X,
C,
V,
B,
N,
M,
Comma,
Period,
FwdSlash,
Rshift1,
Rshift2,
Rshift3,
End,
LCtrl,
LFn,
Meta,
LAlt,
Space1,
Space2,
Space3,
Space4,
RAlt,
PrtSc,
RCtrl,
Up,
Down,
Left,
Right,
RFn,
None,
}
pub trait KeyLayout { pub trait KeyLayout {
fn get_rows(&self) -> &Vec<[Key; 17]>; fn get_rows(&self) -> &Vec<[Key; 17]>;
} }
@@ -301,37 +237,37 @@ impl Default for GX502Layout {
fn default() -> Self { fn default() -> Self {
GX502Layout(vec![ GX502Layout(vec![
[ [
Key::None, Key::NormalSpacer,
Key::None, Key::FuncSpacer,
Key::VolDown, Key::VolDown,
Key::VolUp, Key::VolUp,
Key::MicMute, Key::MicMute,
Key::Rog, Key::Rog,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
], ],
[ [
Key::Esc, Key::Esc,
Key::None, Key::NormalBlank,
Key::F1, Key::F1,
Key::F2, Key::F2,
Key::F3, Key::F3,
Key::F4, Key::F4,
Key::None, // not sure which key to put here Key::NormalBlank, // not sure which key to put here
Key::F5, Key::F5,
Key::F6, Key::F6,
Key::F7, Key::F7,
Key::F8, Key::F8,
Key::F9, Key::NormalBlank,
Key::F9, Key::F9,
Key::F10, Key::F10,
Key::F11, Key::F11,
@@ -352,9 +288,9 @@ impl Default for GX502Layout {
Key::N0, Key::N0,
Key::Hyphen, Key::Hyphen,
Key::Equals, Key::Equals,
Key::BkSpc1, Key::BkSpc3_1,
Key::BkSpc2, Key::BkSpc3_2,
Key::BkSpc3, Key::BkSpc3_3,
Key::Home, Key::Home,
], ],
[ [
@@ -390,9 +326,9 @@ impl Default for GX502Layout {
Key::SemiColon, Key::SemiColon,
Key::Quote, Key::Quote,
Key::Quote, Key::Quote,
Key::Ret1, Key::Return3_1,
Key::Ret2, Key::Return3_2,
Key::Ret3, Key::Return3_3,
Key::PgDn, Key::PgDn,
], ],
[ [
@@ -409,9 +345,9 @@ impl Default for GX502Layout {
Key::Period, Key::Period,
Key::FwdSlash, Key::FwdSlash,
Key::FwdSlash, Key::FwdSlash,
Key::Rshift1, Key::Rshift3_1,
Key::Rshift2, Key::Rshift3_2,
Key::Rshift3, Key::Rshift3_3,
Key::End, Key::End,
], ],
[ [
@@ -419,11 +355,11 @@ impl Default for GX502Layout {
Key::LFn, Key::LFn,
Key::Meta, Key::Meta,
Key::LAlt, Key::LAlt,
Key::Space1, Key::Space5_1,
Key::Space2, Key::Space5_2,
Key::Space3, Key::Space5_3,
Key::Space4, Key::Space5_4,
Key::Space4, Key::Space5_5,
Key::RAlt, Key::RAlt,
Key::PrtSc, Key::PrtSc,
Key::RCtrl, Key::RCtrl,
@@ -434,23 +370,23 @@ impl Default for GX502Layout {
Key::RFn, Key::RFn,
], ],
[ [
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::None, Key::NormalBlank,
Key::Left, Key::Left,
Key::Down, Key::Down,
Key::Right, Key::Right,
Key::None, Key::NormalBlank,
], ],
]) ])
} }
+338 -12
View File
@@ -1,3 +1,8 @@
use serde::{Deserialize, Serialize};
use std::ops::{BitAnd, BitOr};
#[cfg(feature = "dbus")]
use zvariant::Type;
pub const LED_INIT1: [u8; 2] = [0x5d, 0xb9]; pub const LED_INIT1: [u8; 2] = [0x5d, 0xb9];
pub const LED_INIT2: &str = "]ASUS Tech.Inc."; // ] == 0x5d pub const LED_INIT2: &str = "]ASUS Tech.Inc."; // ] == 0x5d
pub const LED_INIT3: [u8; 6] = [0x5d, 0x05, 0x20, 0x31, 0, 0x08]; pub const LED_INIT3: [u8; 6] = [0x5d, 0x05, 0x20, 0x31, 0, 0x08];
@@ -15,18 +20,339 @@ pub const fn aura_brightness_bytes(brightness: u8) -> [u8; 17] {
] ]
} }
pub const LED_AWAKE_ON_SLEEP_OFF: [u8; 17] = [ #[cfg_attr(feature = "dbus", derive(Type))]
0x5d, 0xbd, 0x01, 0xcf, 0x17, 0x0b, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, #[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize, Default)]
]; pub enum AuraDevice {
Tuf,
X1854,
X1869,
X1866,
#[default]
X19B6,
Unknown,
}
pub const LED_AWAKE_ON_SLEEP_ON: [u8; 17] = [ impl From<&str> for AuraDevice {
0x5d, 0xbd, 0x01, 0xff, 0x1f, 0x0f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, fn from(s: &str) -> Self {
]; match s.to_lowercase().as_str() {
"tuf" => AuraDevice::Tuf,
"1866" => AuraDevice::X1866,
"1869" => AuraDevice::X1869,
"1854" => AuraDevice::X1854,
"19b6" => AuraDevice::X19B6,
"0x1866" => AuraDevice::X1866,
"0x1869" => AuraDevice::X1869,
"0x1854" => AuraDevice::X1854,
"0x19b6" => AuraDevice::X19B6,
_ => AuraDevice::Unknown,
}
}
}
pub const LED_AWAKE_OFF_SLEEP_OFF: [u8; 17] = [ /// This struct is intended as a helper to pass args to generic dbus interface
0x5d, 0xbd, 0x01, 0xc3, 0x13, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, #[cfg_attr(feature = "dbus", derive(Type))]
]; #[derive(Clone, Default, Debug, Serialize, Deserialize)]
pub struct AuraPowerDev {
pub tuf: Vec<AuraDevTuf>,
pub x1866: Vec<AuraDev1866>,
pub x19b6: Vec<AuraDev19b6>,
}
pub const LED_AWAKE_OFF_SLEEP_ON: [u8; 17] = [ #[cfg_attr(feature = "dbus", derive(Type))]
0x5d, 0xbd, 0x01, 0xf3, 0x1b, 0x0d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
]; #[repr(u32)]
pub enum AuraDevTuf {
Boot,
Awake,
Sleep,
Keyboard,
}
impl AuraDevTuf {
pub const fn dev_id() -> &'static str {
"tuf"
}
}
/// # Bits for older 0x1866 keyboard model
///
/// Keybord and Lightbar require Awake, Boot and Sleep apply to both
/// Keybord and Lightbar regardless of if either are enabled (or Awake is enabled)
///
/// | Byte 1 | Byte 2 | Byte 3 | function | hex |
/// |------------|------------|------------|----------|----------|
/// | 0000, 0000 | 0000, 0000 | 0000, 0010 | Awake | 00,00,02 |
/// | 0000, 1000 | 0000, 0000 | 0000, 0000 | Keyboard | 08,00,00 |
/// | 0000, 0100 | 0000, 0101 | 0000, 0000 | Lightbar | 04,05,00 |
/// | 1100, 0011 | 0001, 0010 | 0000, 1001 | Boot/Sht | c3,12,09 |
/// | 0011, 0000 | 0000, 1000 | 0000, 0100 | Sleep | 30,08,04 |
/// | 1111, 1111 | 0001, 1111 | 0000, 1111 | all on | |
///
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum AuraDev1866 {
Awake = 0x000002,
Keyboard = 0x080000,
Lightbar = 0x040500,
Boot = 0xc31209,
Sleep = 0x300804,
}
impl From<AuraDev1866> for u32 {
fn from(a: AuraDev1866) -> Self {
a as u32
}
}
impl AuraDev1866 {
pub fn to_bytes(control: &[Self]) -> [u8; 3] {
let mut a: u32 = 0;
control.iter().for_each(|n| {
a |= *n as u32;
});
[
((a & 0xff0000) >> 16) as u8,
((a & 0xff00) >> 8) as u8,
(a & 0xff) as u8,
]
}
pub const fn dev_id() -> &'static str {
"0x1866"
}
}
impl BitOr<AuraDev1866> for AuraDev1866 {
type Output = u32;
fn bitor(self, rhs: AuraDev1866) -> Self::Output {
return self as u32 | rhs as u32;
}
}
impl BitAnd<AuraDev1866> for AuraDev1866 {
type Output = u32;
fn bitand(self, rhs: AuraDev1866) -> Self::Output {
return self as u32 & rhs as u32;
}
}
/// # Bits for 0x19b6 keyboard model
///
/// byte 4 in the USB packet is for keyboard + logo power states
/// default is on, `ff`
/// Keyboard and logo use the full range of bits (almost)
///
/// | n1 | n2 | hex | action | bit |
/// |------|------|-----|-----------------------|-------|
/// | 0000 | 0000 | 00 | all off | |
/// | 0000 | 0001 | 01 | logo boot | bit 1 |
/// | 0000 | 0010 | 02 | keyboard boot | bit 2 |
/// | 0000 | 0100 | 04 | logo awake | bit 3 |
/// | 0000 | 1000 | 08 | keyboard awake | bit 4 |
/// | 0001 | 0000 | 10 | logo sleep off | bit 5 |
/// | 0010 | 0000 | 20 | keyboard sleep | bit 6 |
/// | 0100 | 0000 | 40 | logo shutdown off | bit 7 |
/// | 1000 | 0000 | 80 | keyboard shutdown off | bit 8 |
///
/// byte 5 = lightbar
///
/// | 1 | 2 | hex | action | bit |
/// |------|------|-----|----------------------|-------|
/// | 0000 | 0010 | 02 | lightbar off boot | bit 2 |
/// | 0000 | 0100 | 04 | lightbar on | bit 3 |
/// | 0000 | 1000 | 08 | lightbar off sleep | bit 4 |
/// | 0001 | 0000 | 10 | lightbar shtdn off | bit 5 |
///
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
#[repr(u16)]
pub enum AuraDev19b6 {
BootLogo = 1,
BootKeyb = 1 << 1,
AwakeLogo = 1 << 2,
AwakeKeyb = 1 << 3,
SleepLogo = 1 << 4,
SleepKeyb = 1 << 5,
ShutdownLogo = 1 << 6,
ShutdownKeyb = 1 << 7,
AwakeBar = 1 << 7 + 2,
BootBar = 1 << 7 + 3,
SleepBar = 1 << 7 + 4,
ShutdownBar = 1 << 7 + 5,
}
impl From<AuraDev19b6> for u16 {
fn from(a: AuraDev19b6) -> Self {
a as u16
}
}
impl AuraDev19b6 {
pub fn to_bytes(control: &[Self]) -> [u8; 3] {
let mut a: u16 = 0;
control.iter().for_each(|n| {
a |= *n as u16;
});
[(a & 0xff) as u8, ((a & 0xff00) >> 8) as u8, 0x00]
}
pub const fn dev_id() -> &'static str {
"0x196b"
}
}
impl BitOr<AuraDev19b6> for AuraDev19b6 {
type Output = u16;
fn bitor(self, rhs: AuraDev19b6) -> Self::Output {
return self as u16 | rhs as u16;
}
}
impl BitAnd<AuraDev19b6> for AuraDev19b6 {
type Output = u16;
fn bitand(self, rhs: AuraDev19b6) -> Self::Output {
return self as u16 & rhs as u16;
}
}
#[cfg(test)]
mod tests {
use crate::usb::AuraDev19b6;
use super::AuraDev1866;
#[test]
fn check_0x1866_control_bytes() {
let bytes = [AuraDev1866::Keyboard, AuraDev1866::Awake];
let bytes = AuraDev1866::to_bytes(&bytes);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x08, 0x00, 0x02]);
let bytes = [AuraDev1866::Lightbar, AuraDev1866::Awake];
let bytes = AuraDev1866::to_bytes(&bytes);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x04, 0x05, 0x02]);
let bytes = [AuraDev1866::Sleep];
let bytes = AuraDev1866::to_bytes(&bytes);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x30, 0x08, 0x04]);
let bytes = [AuraDev1866::Boot];
let bytes = AuraDev1866::to_bytes(&bytes);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0xc3, 0x12, 0x09]);
let bytes = [
AuraDev1866::Keyboard,
AuraDev1866::Lightbar,
AuraDev1866::Awake,
AuraDev1866::Sleep,
AuraDev1866::Boot,
];
let bytes = AuraDev1866::to_bytes(&bytes);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0xff, 0x1f, 0x000f]);
}
#[test]
fn check_0x19b6_control_bytes() {
// All on
let byte1 = [
AuraDev19b6::BootLogo,
AuraDev19b6::BootKeyb,
AuraDev19b6::SleepLogo,
AuraDev19b6::SleepKeyb,
AuraDev19b6::AwakeLogo,
AuraDev19b6::AwakeKeyb,
AuraDev19b6::ShutdownLogo,
AuraDev19b6::ShutdownKeyb,
];
let bytes = AuraDev19b6::to_bytes(&byte1);
println!("{:08b}", bytes[0]);
assert_eq!(bytes[0], 0xff);
//
let byte1 = [
// AuraControl::BootLogo,
AuraDev19b6::BootKeyb,
AuraDev19b6::SleepLogo,
AuraDev19b6::SleepKeyb,
AuraDev19b6::AwakeLogo,
AuraDev19b6::AwakeKeyb,
AuraDev19b6::ShutdownLogo,
AuraDev19b6::ShutdownKeyb,
];
let bytes = AuraDev19b6::to_bytes(&byte1);
println!("{:08b}", bytes[0]);
assert_eq!(bytes[0], 0xfe);
let byte1 = [
AuraDev19b6::BootLogo,
// AuraControl::BootKeyb,
AuraDev19b6::SleepLogo,
AuraDev19b6::SleepKeyb,
AuraDev19b6::AwakeLogo,
AuraDev19b6::AwakeKeyb,
AuraDev19b6::ShutdownLogo,
AuraDev19b6::ShutdownKeyb,
];
let bytes = AuraDev19b6::to_bytes(&byte1);
println!("{:08b}", bytes[0]);
assert_eq!(bytes[0], 0xfd);
let byte1 = [
AuraDev19b6::BootLogo,
AuraDev19b6::BootKeyb,
// AuraControl::SleepLogo,
AuraDev19b6::SleepKeyb,
AuraDev19b6::AwakeLogo,
AuraDev19b6::AwakeKeyb,
AuraDev19b6::ShutdownLogo,
AuraDev19b6::ShutdownKeyb,
];
let bytes = AuraDev19b6::to_bytes(&byte1);
println!("{:08b}", bytes[0]);
assert_eq!(bytes[0], 0xef);
let byte1 = [
AuraDev19b6::BootLogo,
AuraDev19b6::BootKeyb,
AuraDev19b6::SleepLogo,
// AuraControl::SleepKeyb,
AuraDev19b6::AwakeLogo,
AuraDev19b6::AwakeKeyb,
AuraDev19b6::ShutdownLogo,
AuraDev19b6::ShutdownKeyb,
];
let bytes = AuraDev19b6::to_bytes(&byte1);
println!("{:08b}", bytes[0]);
assert_eq!(bytes[0], 0xdf);
let byte2 = [
AuraDev19b6::AwakeBar,
AuraDev19b6::BootBar,
AuraDev19b6::SleepBar,
AuraDev19b6::ShutdownBar,
];
let bytes = AuraDev19b6::to_bytes(&byte2);
println!("{:08b}", bytes[1]);
assert_eq!(bytes[1], 0x1e);
let byte2 = [
AuraDev19b6::AwakeBar,
AuraDev19b6::BootBar,
// AuraControl::SleepBar,
AuraDev19b6::ShutdownBar,
];
let bytes = AuraDev19b6::to_bytes(&byte2);
println!("{:08b}", bytes[1]);
assert_eq!(bytes[1], 0x16);
}
}
+35
View File
@@ -0,0 +1,35 @@
[package]
name = "rog-control-center"
version = "1.0.0"
authors = ["Luke D. Jones <luke@ljones.dev>"]
edition = "2021"
[features]
mocking = []
[dependencies]
egui = { git = "https://github.com/emilk/egui" }
eframe= { git = "https://github.com/emilk/egui" }
#eframe= { git = "https://github.com/emilk/egui", default-features = false, features = ["dark-light", "default_fonts", "wgpu"] }
rog_dbus = { path = "../rog-dbus" }
rog_aura = { path = "../rog-aura" }
rog_profiles = { path = "../rog-profiles" }
rog_supported = { path = "../rog-supported" }
# supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git" }
smol = "^1.2"
serde = "^1.0"
toml = "^0.5"
serde_json = "^1.0"
serde_derive = "^1.0"
zbus = "^2.3"
nix = "^0.20.0"
tempfile = "3.2.0"
dirs = "3.0.1"
[dependencies.notify-rust]
version = "^4.3"
default-features = false
features = ["z"]
+22
View File
@@ -0,0 +1,22 @@
# App template
This is a trial app cut down to bare essentials to show how to create an app that can run in the background
with some user-config options.
egui based. Keep in mind that this is very much a bit of a mess due to experimenting.
## Running
Use `WINIT_UNIX_BACKEND=x11 rog-control-center`. `WINIT_UNIX_BACKEND` is required due to window decorations not updating and the window not really being set as visible/invisible on wayland.
## Build features
For testing some features that are typically not available on all laptops:
```rust
cargo run --features mocking
```
## TODO
- Add notification watch for certain UI elements to enforce an update (for example when a user changes Aura via a hot key).
@@ -0,0 +1,11 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=ROG Control Center
Comment=Make your ASUS ROG Laptop go Brrrrr!
Categories=Settings
Icon=rog-control-center
Exec=env WINIT_UNIX_BACKEND=x11 rog-control-center
Terminal=false
Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

+196
View File
@@ -0,0 +1,196 @@
use std::{
f64::consts::PI,
io::Write,
sync::{
atomic::{AtomicBool, AtomicU8, Ordering},
Arc,
},
time::{Duration, Instant},
};
use egui::{Button, RichText};
use rog_supported::SupportedFunctions;
use crate::{
config::Config, error::Result, get_ipc_file, page_states::PageDataStates, Page,
RogDbusClientBlocking, SHOWING_GUI,
};
pub struct RogApp<'a> {
pub page: Page,
pub states: PageDataStates,
pub supported: SupportedFunctions,
/// Should the app begin showing the GUI
pub begin_show_gui: Arc<AtomicBool>,
/// Is the app GUI closed (and running in bg)
pub running_in_bg: bool,
// TODO: can probably just open and read whenever
pub config: Config,
pub asus_dbus: RogDbusClientBlocking<'a>,
/// Oscillator in percentage
pub oscillator1: Arc<AtomicU8>,
pub oscillator2: Arc<AtomicU8>,
pub oscillator3: Arc<AtomicU8>,
/// Frequency of oscillation
pub oscillator_freq: Arc<AtomicU8>,
/// A toggle that toggles true/false when the oscillator reaches 0
pub oscillator_toggle: Arc<AtomicBool>,
}
impl<'a> RogApp<'a> {
/// Called once before the first frame.
pub fn new(
start_closed: bool,
config: Config,
show_gui: Arc<AtomicBool>,
states: PageDataStates,
_cc: &eframe::CreationContext<'_>,
) -> Result<Self> {
let (dbus, _) = RogDbusClientBlocking::new()?;
let supported = dbus.proxies().supported().supported_functions()?;
// Set up an oscillator to run on a thread.
// Helpful for visual effects like colour pulse.
let oscillator1 = Arc::new(AtomicU8::new(0));
let oscillator2 = Arc::new(AtomicU8::new(0));
let oscillator3 = Arc::new(AtomicU8::new(0));
let oscillator1_1 = oscillator1.clone();
let oscillator1_2 = oscillator2.clone();
let oscillator1_3 = oscillator3.clone();
let oscillator_freq = Arc::new(AtomicU8::new(5));
let oscillator_freq1 = oscillator_freq.clone();
let oscillator_toggle = Arc::new(AtomicBool::new(false));
let oscillator_toggle1 = oscillator_toggle.clone();
std::thread::spawn(move || {
let started = Instant::now();
let mut toggled = false;
loop {
let time = started.elapsed();
// 32 = slow, 16 = med, 8 = fast
let scale = oscillator_freq1.load(Ordering::SeqCst) as f64;
let elapsed1 = (time.as_millis() as f64 + 333.0) / 10000.0;
let elapsed2 = (time.as_millis() as f64 + 666.0) / 10000.0;
let elapsed3 = (time.as_millis() as f64 + 999.0) / 10000.0;
let tmp1 = ((scale * elapsed1 * PI).cos()).abs();
let tmp2 = ((scale * 0.85 * elapsed2 * PI).cos()).abs();
let tmp3 = ((scale * 0.7 * elapsed3 * PI).cos()).abs();
if tmp1 <= 0.1 && !toggled {
let s = oscillator_toggle1.load(Ordering::SeqCst);
oscillator_toggle1.store(!s, Ordering::SeqCst);
toggled = true;
} else if tmp1 > 0.9 {
toggled = false;
}
let tmp1 = (255.0 * tmp1 * 100.0 / 255.0) as u8;
let tmp2 = (255.0 * tmp2 * 100.0 / 255.0) as u8;
let tmp3 = (255.0 * tmp3 * 100.0 / 255.0) as u8;
oscillator1_1.store(tmp1, Ordering::SeqCst);
oscillator1_2.store(tmp2, Ordering::SeqCst);
oscillator1_3.store(tmp3, Ordering::SeqCst);
std::thread::sleep(Duration::from_millis(33));
}
});
Ok(Self {
supported,
states,
page: Page::System,
begin_show_gui: show_gui,
running_in_bg: start_closed,
config,
asus_dbus: dbus,
oscillator1,
oscillator2,
oscillator3,
oscillator_toggle,
oscillator_freq,
})
}
}
impl<'a> eframe::App for RogApp<'a> {
fn on_exit_event(&mut self) -> bool {
if self.config.run_in_background {
self.running_in_bg = true;
get_ipc_file().unwrap().write_all(&[0]).unwrap();
return false;
}
true
}
/// Called each time the UI needs repainting, which may be many times per second.
/// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
let Self {
begin_show_gui: should_show_gui,
supported,
asus_dbus: dbus,
states,
..
} = self;
states
.refresh_if_notfied(supported, dbus)
.map(|repaint| {
if repaint {
ctx.request_repaint();
}
})
.map_err(|e| self.states.error = Some(e.to_string()))
.ok();
let page = self.page;
if should_show_gui.load(Ordering::SeqCst) {
let mut ipc_file = get_ipc_file().unwrap();
ipc_file.write_all(&[SHOWING_GUI]).unwrap();
should_show_gui.store(false, Ordering::SeqCst);
frame.set_visible(true);
self.running_in_bg = false;
}
if self.running_in_bg {
// Request to draw nothing at all
ctx.request_repaint_after(Duration::from_millis(500));
frame.set_visible(false);
return;
}
// Do all GUI display after this point
self.top_bar(ctx, frame);
self.side_panel(ctx);
if let Some(err) = self.states.error.clone() {
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading(RichText::new("Error!").size(28.0));
ui.centered_and_justified(|ui| {
ui.label(RichText::new(format!("The error was: {:?}", err)).size(22.0));
});
});
egui::TopBottomPanel::bottom("error_bar")
.default_height(26.0)
.show(ctx, |ui| {
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
if ui
.add(Button::new(RichText::new("Okay").size(20.0)))
.clicked()
{
self.states.error = None;
}
});
});
} else if page == Page::System {
self.system_page(ctx);
} else if page == Page::AuraEffects {
self.aura_page(ctx);
// TODO: Anime page is not complete
// } else if page == Page::AnimeMatrix {
// self.anime_page(ctx);
} else if page == Page::FanCurves {
self.fan_curve_page(ctx);
}
}
}
+92
View File
@@ -0,0 +1,92 @@
use std::{
fs::{create_dir, OpenOptions},
io::{Read, Write},
};
use serde_derive::{Deserialize, Serialize};
//use log::{error, info, warn};
use crate::error::Error;
const CFG_DIR: &str = "rog";
const CFG_FILE_NAME: &str = "rog-control-center.cfg";
#[derive(Debug, Deserialize, Serialize)]
#[serde(default)]
pub struct Config {
pub run_in_background: bool,
pub startup_in_background: bool,
pub enable_notifications: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
run_in_background: true,
startup_in_background: false,
enable_notifications: true,
}
}
}
impl Config {
pub fn load() -> Result<Config, Error> {
let mut path = if let Some(dir) = dirs::config_dir() {
dir
} else {
return Err(Error::XdgVars);
};
path.push(CFG_DIR);
if !path.exists() {
create_dir(path.clone())?;
}
path.push(CFG_FILE_NAME);
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)?;
let mut buf = String::new();
if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len == 0 {
let default = Config::default();
let t = toml::to_string_pretty(&default).unwrap();
file.write_all(t.as_bytes())?;
return Ok(default);
} else if let Ok(data) = toml::from_str::<Config>(&buf) {
return Ok(data);
}
}
Err(Error::ConfigLoadFail)
}
pub fn save(&self) -> Result<(), Error> {
let mut path = if let Some(dir) = dirs::config_dir() {
dir
} else {
return Err(Error::XdgVars);
};
path.push(CFG_DIR);
if !path.exists() {
create_dir(path.clone())?;
}
path.push(CFG_FILE_NAME);
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&path)?;
let t = toml::to_string_pretty(&self).unwrap();
file.write_all(t.as_bytes())?;
Ok(())
}
}
+47
View File
@@ -0,0 +1,47 @@
use std::fmt;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
Io(std::io::Error),
Nix(nix::Error),
ConfigLoadFail,
ConfigLockFail,
XdgVars,
Zbus(zbus::Error),
}
impl fmt::Display for Error {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Io(err) => write!(f, "Failed to open: {}", err),
Error::Nix(err) => write!(f, "Error: {}", err),
Error::ConfigLoadFail => write!(f, "Failed to load user config"),
Error::ConfigLockFail => write!(f, "Failed to lock user config"),
Error::XdgVars => write!(f, "XDG environment vars appear unset"),
Error::Zbus(err) => write!(f, "Error: {}", err),
}
}
}
impl std::error::Error for Error {}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Error::Io(err)
}
}
impl From<nix::Error> for Error {
fn from(err: nix::Error) -> Self {
Error::Nix(err)
}
}
impl From<zbus::Error> for Error {
fn from(err: zbus::Error) -> Self {
Error::Zbus(err)
}
}
+86
View File
@@ -0,0 +1,86 @@
pub mod app;
use std::{
fs::{remove_dir_all, File, OpenOptions},
io::{Read, Write},
process::exit,
thread::sleep,
time::Duration,
};
pub use app::RogApp;
pub mod config;
pub mod error;
#[cfg(feature = "mocking")]
pub mod mocking;
pub mod notify;
pub mod page_states;
pub mod pages;
pub mod startup_error;
pub mod widgets;
#[cfg(feature = "mocking")]
pub use mocking::RogDbusClientBlocking;
#[cfg(not(feature = "mocking"))]
pub use rog_dbus::RogDbusClientBlocking;
use nix::{sys::stat, unistd};
use tempfile::TempDir;
//use log::{error, info, warn};
pub const SHOWING_GUI: u8 = 1;
pub const SHOW_GUI: u8 = 2;
#[derive(PartialEq, Clone, Copy)]
pub enum Page {
System,
AuraEffects,
AnimeMatrix,
FanCurves,
}
/// Either exit the process, or return with a refreshed tmp-dir
pub fn on_tmp_dir_exists() -> Result<TempDir, std::io::Error> {
let mut buf = [0u8; 4];
let path = std::env::temp_dir().join("rog-gui");
let mut ipc_file = OpenOptions::new()
.read(true)
.write(true)
.create(false)
.open(path.join("ipc.pipe"))?;
// If the app is running this ends up stacked on top of SHOWING_GUI
ipc_file.write_all(&[SHOW_GUI])?;
// tiny sleep to give the app a chance to respond
sleep(Duration::from_millis(10));
ipc_file.read(&mut buf).ok();
// First entry is the actual state
if buf[0] == SHOWING_GUI {
ipc_file.write_all(&[SHOWING_GUI])?; // Store state again as we drained the fifo
exit(0);
} else if buf[0] == SHOW_GUI {
remove_dir_all(&path)?;
return tempfile::Builder::new()
.prefix("rog-gui")
.rand_bytes(0)
.tempdir();
}
exit(-1);
}
pub fn get_ipc_file() -> Result<File, crate::error::Error> {
let tmp_dir = std::env::temp_dir().join("rog-gui");
let fifo_path = tmp_dir.join("ipc.pipe");
if let Err(e) = unistd::mkfifo(&fifo_path, stat::Mode::S_IRWXU) {
if !matches!(e, nix::Error::Sys(nix::errno::Errno::EEXIST)) {
return Err(e)?;
}
}
Ok(OpenOptions::new()
.read(true)
.write(true)
.truncate(true)
.open(&fifo_path)?)
}
+167
View File
@@ -0,0 +1,167 @@
use rog_aura::layouts::KeyLayout;
use rog_control_center::{
config::Config, get_ipc_file, notify::start_notifications, on_tmp_dir_exists,
page_states::PageDataStates, startup_error::AppErrorShow, RogApp, RogDbusClientBlocking,
SHOW_GUI,
};
use std::{
fs::{self, OpenOptions},
io::Read,
path::PathBuf,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
thread::spawn,
time::Duration,
};
#[cfg(not(feature = "mocking"))]
const DATA_DIR: &str = "/usr/share/rog-gui/";
#[cfg(feature = "mocking")]
const DATA_DIR: &str = env!("CARGO_MANIFEST_DIR");
const BOARD_NAME: &str = "/sys/class/dmi/id/board_name";
fn main() -> Result<(), Box<dyn std::error::Error>> {
let native_options = eframe::NativeOptions {
decorated: false,
transparent: false,
min_window_size: Some(egui::vec2(840.0, 600.0)),
max_window_size: Some(egui::vec2(840.0, 600.0)),
..Default::default()
};
let (dbus, _) = RogDbusClientBlocking::new()
.map_err(|e| {
eframe::run_native(
"ROG Control Center",
native_options.clone(),
Box::new(move |_| Box::new(AppErrorShow::new(e.to_string()))),
);
})
.unwrap();
// Startup
let mut config = Config::load()?;
let start_closed = config.startup_in_background;
if config.startup_in_background {
config.run_in_background = true;
config.save()?;
}
// Find and load a matching layout for laptop
let mut file = OpenOptions::new()
.read(true)
.open(PathBuf::from(BOARD_NAME))
.map_err(|e| {
println!("{BOARD_NAME}, {e}");
e
})?;
let mut board_name = String::new();
file.read_to_string(&mut board_name)?;
let mut layout = KeyLayout::ga401_layout(); // default
let mut path = PathBuf::from(DATA_DIR);
#[cfg(feature = "mocking")]
{
board_name = "gl504".to_string();
path.pop();
path.push("rog-aura");
path.push("data");
}
path.push("layouts");
let path = path.as_path();
for p in fs::read_dir(path).map_err(|e| {
println!("{:?}, {e}", path);
e
})? {
let tmp = KeyLayout::from_file(&p?.path()).unwrap();
if tmp.matches(board_name.as_str()) {
layout = tmp;
break;
}
}
// Cheap method to alert to notifications rather than spinning a thread for each
// This is quite different when done in a retained mode app
let charge_notified = Arc::new(AtomicBool::new(false));
let bios_notified = Arc::new(AtomicBool::new(false));
let aura_notified = Arc::new(AtomicBool::new(false));
let anime_notified = Arc::new(AtomicBool::new(false));
let profiles_notified = Arc::new(AtomicBool::new(false));
let fans_notified = Arc::new(AtomicBool::new(false));
let notifs_enabled = Arc::new(AtomicBool::new(config.enable_notifications));
let states = {
let supported = dbus
.proxies()
.supported()
.supported_functions()
.map_err(|e| {
eframe::run_native(
"ROG Control Center",
native_options.clone(),
Box::new(move |_| Box::new(AppErrorShow::new(e.to_string()))),
);
})
.unwrap();
PageDataStates::new(
layout,
notifs_enabled.clone(),
charge_notified.clone(),
bios_notified.clone(),
aura_notified.clone(),
anime_notified.clone(),
profiles_notified.clone(),
fans_notified.clone(),
&supported,
&dbus,
)? // TODO: if error, show alt GUI containing the error message
};
if config.enable_notifications {
start_notifications(
charge_notified,
bios_notified,
aura_notified,
anime_notified,
profiles_notified,
fans_notified,
notifs_enabled,
)?;
}
// tmp-dir must live to the end of program life
let _tmp_dir = match tempfile::Builder::new()
.prefix("rog-gui")
.rand_bytes(0)
.tempdir()
{
Ok(tmp) => tmp,
Err(_) => on_tmp_dir_exists().unwrap(),
};
let should_show_gui = Arc::new(AtomicBool::new(!start_closed));
let should = should_show_gui.clone();
spawn(move || {
// Loop is blocked here until a single byte is read
loop {
let mut buf = [0u8; 4];
if get_ipc_file().unwrap().read(&mut buf).is_ok() && buf[0] == SHOW_GUI {
should_show_gui.store(true, Ordering::SeqCst);
// Give the starting app a change to read or we'll race it
std::thread::sleep(Duration::from_millis(10));
}
}
});
eframe::run_native(
"ROG Control Center",
native_options,
Box::new(move |cc| {
Box::new(RogApp::new(start_closed, config, should, states, cc).unwrap())
}),
);
}
+226
View File
@@ -0,0 +1,226 @@
use std::collections::BTreeMap;
use rog_aura::{
usb::{AuraDev19b6, AuraDevice, AuraPowerDev},
AuraEffect, AuraModeNum, AuraZone,
};
use rog_profiles::fan_curve_set::{CurveData, FanCurveSet};
use rog_supported::{
AnimeSupportedFunctions, ChargeSupportedFunctions, LedSupportedFunctions,
PlatformProfileFunctions, RogBiosSupportedFunctions, SupportedFunctions,
};
use crate::error::Result;
const NOPE: &'static str = "";
pub struct RogDbusClientBlocking<'a> {
_phantom: &'a str,
}
impl<'a> Default for RogDbusClientBlocking<'a> {
fn default() -> Self {
Self {
_phantom: Default::default(),
}
}
}
impl<'a> RogDbusClientBlocking<'a> {
pub fn new() -> Result<(Self, bool)> {
Ok((Self { _phantom: NOPE }, true))
}
pub fn proxies(&self) -> Proxies {
Proxies
}
}
pub struct Proxies;
impl Proxies {
pub fn rog_bios(&self) -> Bios {
Bios
}
pub fn profile(&self) -> Profile {
Profile
}
pub fn led(&self) -> Led {
Led
}
pub fn anime(&self) -> Anime {
Anime
}
pub fn charge(&self) -> Profile {
Profile
}
pub fn supported(&self) -> Supported {
Supported
}
}
pub struct Bios;
impl Bios {
pub fn post_boot_sound(&self) -> Result<i16> {
Ok(1)
}
pub fn dedicated_graphic_mode(&self) -> Result<i16> {
Ok(1)
}
pub fn panel_overdrive(&self) -> Result<i16> {
Ok(1)
}
pub fn set_post_boot_sound(&self, _b: bool) -> Result<()> {
Ok(())
}
pub fn set_dedicated_graphic_mode(&self, _b: bool) -> Result<()> {
Ok(())
}
pub fn set_panel_overdrive(&self, _b: bool) -> Result<()> {
Ok(())
}
}
pub struct Profile;
impl Profile {
pub fn profiles(&self) -> Result<Vec<rog_profiles::Profile>> {
Ok(vec![
rog_profiles::Profile::Balanced,
rog_profiles::Profile::Performance,
rog_profiles::Profile::Quiet,
])
}
pub fn active_profile(&self) -> Result<rog_profiles::Profile> {
Ok(rog_profiles::Profile::Performance)
}
pub fn enabled_fan_profiles(&self) -> Result<Vec<rog_profiles::Profile>> {
Ok(vec![
rog_profiles::Profile::Performance,
rog_profiles::Profile::Balanced,
])
}
pub fn fan_curve_data(&self, _p: rog_profiles::Profile) -> Result<FanCurveSet> {
let mut curve = FanCurveSet::default();
curve.cpu.pwm = [30, 40, 60, 100, 140, 180, 200, 250];
curve.cpu.temp = [20, 30, 40, 50, 70, 80, 90, 100];
curve.gpu.pwm = [40, 80, 100, 140, 170, 200, 230, 250];
curve.gpu.temp = [20, 30, 40, 50, 70, 80, 90, 100];
Ok(curve)
}
pub fn set_fan_curve(&self, _p: rog_profiles::Profile, _c: CurveData) -> Result<()> {
Ok(())
}
pub fn set_fan_curve_enabled(&self, _p: rog_profiles::Profile, _b: bool) -> Result<()> {
Ok(())
}
pub fn limit(&self) -> Result<i16> {
Ok(66)
}
pub fn set_limit(&self, _l: u8) -> Result<()> {
Ok(())
}
pub fn set_active_profile(&self, _p: rog_profiles::Profile) -> Result<()> {
Ok(())
}
}
pub struct Led;
impl Led {
pub fn led_modes(&self) -> Result<BTreeMap<AuraModeNum, AuraEffect>> {
let mut data = BTreeMap::new();
data.insert(AuraModeNum::Static, AuraEffect::default());
data.insert(AuraModeNum::Star, AuraEffect::default());
data.insert(AuraModeNum::Strobe, AuraEffect::default());
data.insert(AuraModeNum::Rain, AuraEffect::default());
data.insert(AuraModeNum::Rainbow, AuraEffect::default());
data.insert(AuraModeNum::Ripple, AuraEffect::default());
data.insert(AuraModeNum::Breathe, AuraEffect::default());
data.insert(AuraModeNum::Comet, AuraEffect::default());
data.insert(AuraModeNum::Flash, AuraEffect::default());
data.insert(AuraModeNum::Laser, AuraEffect::default());
data.insert(AuraModeNum::Pulse, AuraEffect::default());
Ok(data)
}
pub fn led_mode(&self) -> Result<AuraModeNum> {
Ok(AuraModeNum::Rainbow)
}
pub fn led_brightness(&self) -> Result<i16> {
Ok(1)
}
pub fn leds_enabled(&self) -> Result<AuraPowerDev> {
Ok(AuraPowerDev {
tuf: vec![],
x1866: vec![],
x19b6: vec![
AuraDev19b6::BootKeyb,
AuraDev19b6::AwakeKeyb,
AuraDev19b6::SleepLogo,
AuraDev19b6::AwakeLogo,
],
})
}
pub fn set_leds_power(&self, _a: AuraPowerDev, _b: bool) -> Result<()> {
Ok(())
}
pub fn set_led_mode(&self, _a: &AuraEffect) -> Result<()> {
Ok(())
}
}
pub struct Anime;
impl Anime {
pub fn boot_enabled(&self) -> Result<bool> {
Ok(true)
}
pub fn awake_enabled(&self) -> Result<bool> {
Ok(true)
}
pub fn set_on_off(&self, _b: bool) -> Result<()> {
Ok(())
}
pub fn set_boot_on_off(&self, _b: bool) -> Result<()> {
Ok(())
}
}
pub struct Supported;
impl Supported {
pub fn supported_functions(&self) -> Result<SupportedFunctions> {
Ok(SupportedFunctions {
anime_ctrl: AnimeSupportedFunctions(true),
charge_ctrl: ChargeSupportedFunctions {
charge_level_set: true,
},
platform_profile: PlatformProfileFunctions {
platform_profile: true,
fan_curves: true,
},
keyboard_led: LedSupportedFunctions {
prod_id: AuraDevice::X19B6,
brightness_set: true,
stock_led_modes: vec![
AuraModeNum::Rain,
AuraModeNum::Rainbow,
AuraModeNum::Star,
AuraModeNum::Static,
AuraModeNum::Strobe,
],
multizone_led_mode: vec![
AuraZone::Key1,
AuraZone::Key2,
AuraZone::Key3,
AuraZone::Key4,
AuraZone::BarLeft,
AuraZone::BarRight,
AuraZone::Logo,
],
per_key_led_mode: true,
},
rog_bios_ctrl: RogBiosSupportedFunctions {
post_sound: true,
dedicated_gfx: true,
panel_overdrive: true,
dgpu_disable: true,
egpu_enable: true,
},
})
}
}
+256
View File
@@ -0,0 +1,256 @@
//TODO: a lot of app state refresh depends on this so there needs
// to be an extra AtomicBool for checking if notifications are enabled
use notify_rust::{Hint, Notification, NotificationHandle};
use rog_aura::AuraEffect;
use rog_dbus::{
zbus_anime::AnimeProxy, zbus_charge::ChargeProxy, zbus_led::LedProxy,
zbus_profile::ProfileProxy, zbus_rogbios::RogBiosProxy,
};
use rog_profiles::Profile;
use smol::{future, Executor};
use std::{
error::Error,
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
},
thread::spawn,
};
use zbus::export::futures_util::StreamExt;
const NOTIF_HEADER: &str = "ROG Control";
macro_rules! notify {
($notifier:ident, $last_notif:ident, $data:expr) => {
if let Some(notif) = $last_notif.take() {
notif.close();
}
if let Ok(x) = $notifier($data) {
$last_notif.replace(x);
}
};
}
macro_rules! base_notification {
($body:expr) => {
Notification::new()
.summary(NOTIF_HEADER)
.body($body)
.timeout(2000)
.show()
};
}
type SharedHandle = Arc<Mutex<Option<NotificationHandle>>>;
pub fn start_notifications(
charge_notified: Arc<AtomicBool>,
bios_notified: Arc<AtomicBool>,
aura_notified: Arc<AtomicBool>,
anime_notified: Arc<AtomicBool>,
profiles_notified: Arc<AtomicBool>,
_fans_notified: Arc<AtomicBool>,
notifs_enabled: Arc<AtomicBool>,
) -> Result<(), Box<dyn std::error::Error>> {
let last_notification: SharedHandle = Arc::new(Mutex::new(None));
let executor = Executor::new();
// BIOS notif
let last_notif = last_notification.clone();
let notifs_enabled1 = notifs_enabled.clone();
let bios_notified1 = bios_notified.clone();
// TODO: make a macro or generic function or something...
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = RogBiosProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_post_boot_sound().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if notifs_enabled1.load(Ordering::SeqCst) {
if let Ok(ref mut lock) = last_notif.try_lock() {
notify!(do_post_sound_notif, lock, &out.sound());
}
}
bios_notified1.store(true, Ordering::SeqCst);
}
future::ready(())
})
.await;
};
})
.detach();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = RogBiosProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_panel_overdrive().await {
p.for_each(|_| {
bios_notified.store(true, Ordering::SeqCst);
future::ready(())
})
.await;
};
})
.detach();
// Charge notif
let last_notif = last_notification.clone();
let notifs_enabled1 = notifs_enabled.clone();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = ChargeProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_charge().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if notifs_enabled1.load(Ordering::SeqCst) {
if let Ok(ref mut lock) = last_notif.try_lock() {
notify!(do_charge_notif, lock, &out.limit);
}
}
charge_notified.store(true, Ordering::SeqCst);
}
future::ready(())
})
.await;
};
})
.detach();
// Profile notif
let last_notif = last_notification.clone();
let notifs_enabled1 = notifs_enabled.clone();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = ProfileProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_profile().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if notifs_enabled1.load(Ordering::SeqCst) {
if let Ok(ref mut lock) = last_notif.try_lock() {
notify!(do_thermal_notif, lock, &out.profile);
}
}
profiles_notified.store(true, Ordering::SeqCst);
}
future::ready(())
})
.await;
};
})
.detach();
// LED notif
let last_notif = last_notification.clone();
let aura_notif = aura_notified.clone();
let notifs_enabled1 = notifs_enabled.clone();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = LedProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_led().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if notifs_enabled1.load(Ordering::SeqCst) {
if let Ok(ref mut lock) = last_notif.try_lock() {
notify!(do_led_notif, lock, &out.data);
}
}
aura_notif.store(true, Ordering::SeqCst);
}
future::ready(())
})
.await;
};
})
.detach();
let aura_notif = aura_notified.clone();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = LedProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_led().await {
p.for_each(|_| {
aura_notif.store(true, Ordering::SeqCst);
future::ready(())
})
.await;
};
})
.detach();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = LedProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_all_signals().await {
p.for_each(|_| {
aura_notified.store(true, Ordering::SeqCst);
future::ready(())
})
.await;
};
})
.detach();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = AnimeProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_power_states().await {
p.for_each(|_| {
anime_notified.store(true, Ordering::SeqCst);
future::ready(())
})
.await;
};
})
.detach();
spawn(move || loop {
smol::block_on(executor.tick());
});
Ok(())
}
fn do_thermal_notif(profile: &Profile) -> Result<NotificationHandle, Box<dyn Error>> {
let icon = match profile {
Profile::Balanced => "asus_notif_yellow",
Profile::Performance => "asus_notif_red",
Profile::Quiet => "asus_notif_green",
};
let profile: &str = (*profile).into();
let x = Notification::new()
.summary("ASUS ROG")
.body(&format!(
"Thermal profile changed to {}",
profile.to_uppercase(),
))
.hint(Hint::Resident(true))
.timeout(2000)
.hint(Hint::Category("device".into()))
//.hint(Hint::Transient(true))
.icon(icon)
.show()?;
Ok(x)
}
fn do_led_notif(ledmode: &AuraEffect) -> Result<NotificationHandle, notify_rust::error::Error> {
base_notification!(&format!(
"Keyboard LED mode changed to {}",
ledmode.mode_name()
))
}
fn do_charge_notif(limit: &u8) -> Result<NotificationHandle, notify_rust::error::Error> {
base_notification!(&format!("Battery charge limit changed to {}", limit))
}
fn do_post_sound_notif(on: &bool) -> Result<NotificationHandle, notify_rust::error::Error> {
base_notification!(&format!("BIOS Post sound {}", on))
}
+383
View File
@@ -0,0 +1,383 @@
use std::{
collections::{BTreeMap, HashMap, HashSet},
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
};
use egui::Vec2;
use rog_aura::{layouts::KeyLayout, usb::AuraPowerDev, AuraEffect, AuraModeNum};
use rog_profiles::{fan_curve_set::FanCurveSet, FanCurvePU, Profile};
use rog_supported::SupportedFunctions;
use crate::{error::Result, RogDbusClientBlocking};
#[derive(Clone, Debug)]
pub struct BiosState {
/// To be shared to a thread that checks notifications.
/// It's a bit general in that it won't provide *what* was
/// updated, so the full state needs refresh
pub was_notified: Arc<AtomicBool>,
pub post_sound: bool,
pub dedicated_gfx: bool,
pub panel_overdrive: bool,
pub dgpu_disable: bool,
pub egpu_enable: bool,
}
impl BiosState {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<Self> {
Ok(Self {
was_notified,
post_sound: if supported.rog_bios_ctrl.post_sound {
dbus.proxies().rog_bios().post_boot_sound()? != 0
} else {
false
},
dedicated_gfx: if supported.rog_bios_ctrl.dedicated_gfx {
dbus.proxies().rog_bios().dedicated_graphic_mode()? != 0
} else {
false
},
panel_overdrive: if supported.rog_bios_ctrl.panel_overdrive {
dbus.proxies().rog_bios().panel_overdrive()? != 0
} else {
false
},
// TODO: needs supergfx
dgpu_disable: supported.rog_bios_ctrl.dgpu_disable,
egpu_enable: supported.rog_bios_ctrl.egpu_enable,
})
}
}
#[derive(Clone, Debug)]
pub struct ProfilesState {
pub was_notified: Arc<AtomicBool>,
pub list: Vec<Profile>,
pub current: Profile,
}
impl ProfilesState {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<Self> {
Ok(Self {
was_notified,
list: if supported.platform_profile.platform_profile {
dbus.proxies().profile().profiles()?
} else {
vec![]
},
current: if supported.platform_profile.platform_profile {
dbus.proxies().profile().active_profile()?
} else {
Profile::Balanced
},
})
}
}
#[derive(Clone, Debug)]
pub struct FanCurvesState {
pub was_notified: Arc<AtomicBool>,
pub show_curve: Profile,
pub show_graph: FanCurvePU,
pub enabled: HashSet<Profile>,
pub curves: HashMap<Profile, FanCurveSet>,
pub drag_delta: Vec2,
}
impl FanCurvesState {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<Self> {
let profiles = if supported.platform_profile.platform_profile {
dbus.proxies().profile().profiles()?
} else {
vec![Profile::Balanced, Profile::Quiet, Profile::Performance]
};
let enabled = if supported.platform_profile.fan_curves {
HashSet::from_iter(
dbus.proxies()
.profile()
.enabled_fan_profiles()?
.iter()
.cloned(),
)
} else {
HashSet::from([Profile::Balanced, Profile::Quiet, Profile::Performance])
};
let mut curves: HashMap<Profile, FanCurveSet> = HashMap::new();
profiles.iter().for_each(|p| {
if supported.platform_profile.fan_curves {
if let Ok(curve) = dbus.proxies().profile().fan_curve_data(*p) {
curves.insert(*p, curve);
}
} else {
let mut curve = FanCurveSet::default();
curve.cpu.pwm = [30, 40, 60, 100, 140, 180, 200, 250];
curve.cpu.temp = [20, 30, 40, 50, 70, 80, 90, 100];
curve.gpu.pwm = [40, 80, 100, 140, 170, 200, 230, 250];
curve.gpu.temp = [20, 30, 40, 50, 70, 80, 90, 100];
curves.insert(*p, curve);
}
});
let show_curve = if supported.platform_profile.fan_curves {
dbus.proxies().profile().active_profile()?
} else {
Profile::Balanced
};
Ok(Self {
was_notified,
show_curve,
show_graph: FanCurvePU::CPU,
enabled,
curves,
drag_delta: Vec2::default(),
})
}
}
#[derive(Clone, Debug)]
pub struct AuraState {
pub was_notified: Arc<AtomicBool>,
pub current_mode: AuraModeNum,
pub modes: BTreeMap<AuraModeNum, AuraEffect>,
pub enabled: AuraPowerDev,
/// Brightness from 0-3
pub bright: i16,
pub wave_red: [u8; 22],
pub wave_green: [u8; 22],
pub wave_blue: [u8; 22],
}
impl AuraState {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<Self> {
Ok(Self {
was_notified,
current_mode: if !supported.keyboard_led.stock_led_modes.is_empty() {
dbus.proxies().led().led_mode().unwrap_or_default()
} else {
AuraModeNum::Static
},
modes: if !supported.keyboard_led.stock_led_modes.is_empty() {
dbus.proxies().led().led_modes().unwrap_or_default()
} else {
BTreeMap::new()
},
enabled: dbus.proxies().led().leds_enabled().unwrap_or_default(),
bright: if !supported.keyboard_led.brightness_set {
dbus.proxies().led().led_brightness().unwrap_or_default()
} else {
2
},
wave_red: [0u8; 22],
wave_green: [0u8; 22],
wave_blue: [0u8; 22],
})
}
/// Bump value in to the wave and surf all along.
pub fn nudge_wave(&mut self, r: u8, g: u8, b: u8) {
for i in (0..self.wave_red.len()).rev() {
if i > 0 {
self.wave_red[i] = self.wave_red[i - 1];
self.wave_green[i] = self.wave_green[i - 1];
self.wave_blue[i] = self.wave_blue[i - 1];
}
}
self.wave_red[0] = r;
self.wave_green[0] = g;
self.wave_blue[0] = b;
}
}
#[derive(Clone, Debug)]
pub struct AnimeState {
pub was_notified: Arc<AtomicBool>,
pub bright: u8,
pub boot: bool,
pub awake: bool,
pub sleep: bool,
}
impl AnimeState {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<Self> {
Ok(Self {
was_notified,
boot: if supported.anime_ctrl.0 {
dbus.proxies().anime().boot_enabled()?
} else {
false
},
awake: if supported.anime_ctrl.0 {
dbus.proxies().anime().awake_enabled()?
} else {
false
},
// TODO:
sleep: false,
bright: 200,
})
}
}
#[derive(Debug)]
pub struct PageDataStates {
pub keyboard_layout: KeyLayout,
pub notifs_enabled: Arc<AtomicBool>,
pub was_notified: Arc<AtomicBool>,
/// Because much of the app state here is the same as `RogBiosSupportedFunctions`
/// we can re-use that structure.
pub bios: BiosState,
pub aura: AuraState,
pub anime: AnimeState,
pub profiles: ProfilesState,
pub fan_curves: FanCurvesState,
pub charge_limit: i16,
pub error: Option<String>,
}
impl PageDataStates {
pub fn new(
keyboard_layout: KeyLayout,
notifs_enabled: Arc<AtomicBool>,
charge_notified: Arc<AtomicBool>,
bios_notified: Arc<AtomicBool>,
aura_notified: Arc<AtomicBool>,
anime_notified: Arc<AtomicBool>,
profiles_notified: Arc<AtomicBool>,
fans_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<Self> {
Ok(Self {
keyboard_layout,
notifs_enabled,
was_notified: charge_notified,
charge_limit: dbus.proxies().charge().limit()?,
bios: BiosState::new(bios_notified, supported, dbus)?,
aura: AuraState::new(aura_notified, supported, dbus)?,
anime: AnimeState::new(anime_notified, supported, dbus)?,
profiles: ProfilesState::new(profiles_notified, supported, dbus)?,
fan_curves: FanCurvesState::new(fans_notified, supported, dbus)?,
error: None,
})
}
pub fn refresh_if_notfied(
&mut self,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<bool> {
let mut notified = false;
if self.was_notified.load(Ordering::SeqCst) {
self.charge_limit = dbus.proxies().charge().limit()?;
self.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
if self.aura.was_notified.load(Ordering::SeqCst) {
self.aura = AuraState::new(self.aura.was_notified.clone(), supported, dbus)?;
self.aura.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
if self.bios.was_notified.load(Ordering::SeqCst) {
self.bios = BiosState::new(self.bios.was_notified.clone(), supported, dbus)?;
self.bios.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
if self.profiles.was_notified.load(Ordering::SeqCst) {
self.profiles =
ProfilesState::new(self.profiles.was_notified.clone(), supported, dbus)?;
self.profiles.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
if self.fan_curves.was_notified.load(Ordering::SeqCst) {
self.fan_curves =
FanCurvesState::new(self.fan_curves.was_notified.clone(), supported, dbus)?;
self.fan_curves.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
Ok(notified)
}
}
impl Default for PageDataStates {
fn default() -> Self {
Self {
keyboard_layout: KeyLayout::ga401_layout(),
notifs_enabled: Default::default(),
was_notified: Default::default(),
bios: BiosState {
was_notified: Default::default(),
post_sound: Default::default(),
dedicated_gfx: Default::default(),
panel_overdrive: Default::default(),
dgpu_disable: Default::default(),
egpu_enable: Default::default(),
},
aura: AuraState {
was_notified: Default::default(),
current_mode: AuraModeNum::Static,
modes: Default::default(),
enabled: AuraPowerDev {
tuf: vec![],
x1866: vec![],
x19b6: vec![],
},
bright: Default::default(),
wave_red: Default::default(),
wave_green: Default::default(),
wave_blue: Default::default(),
},
anime: AnimeState {
was_notified: Default::default(),
bright: Default::default(),
boot: Default::default(),
awake: Default::default(),
sleep: Default::default(),
},
profiles: ProfilesState {
was_notified: Default::default(),
list: Default::default(),
current: Default::default(),
},
fan_curves: FanCurvesState {
was_notified: Default::default(),
show_curve: Default::default(),
show_graph: Default::default(),
enabled: Default::default(),
curves: Default::default(),
drag_delta: Default::default(),
},
charge_limit: Default::default(),
error: Default::default(),
}
}
}
@@ -0,0 +1,9 @@
use crate::RogApp;
impl<'a> RogApp<'a> {
pub fn anime_page(&mut self, ctx: &egui::Context) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.label("In progress");
});
}
}
+83
View File
@@ -0,0 +1,83 @@
use std::{sync::atomic::Ordering, time::Duration};
use egui::Color32;
use rog_aura::AuraModeNum;
use crate::{
widgets::{aura_modes_group, keyboard},
RogApp,
};
impl<'a> RogApp<'a> {
pub fn aura_page(&mut self, ctx: &egui::Context) {
let Self {
supported,
states,
asus_dbus: dbus,
oscillator1,
oscillator2,
oscillator3,
oscillator_freq,
..
} = self;
let red = oscillator1.load(Ordering::SeqCst) as u32;
let green = oscillator2.load(Ordering::SeqCst) as u32;
let blue = oscillator3.load(Ordering::SeqCst) as u32;
states.aura.nudge_wave(red as u8, green as u8, blue as u8);
// let osc = c.0 * 255 / osc;
// dbg!(osc);
let c1 = states
.aura
.modes
.get(&states.aura.current_mode)
.unwrap()
.colour1;
let c2 = states
.aura
.modes
.get(&states.aura.current_mode)
.unwrap()
.colour2;
let mut colour = Color32::from_rgb(c1.0, c1.1, c1.2);
if states.aura.current_mode == AuraModeNum::Pulse {
colour = Color32::from_rgb(
(red * c1.0 as u32 / 100) as u8,
(red * c1.1 as u32 / 100) as u8,
(red * c1.2 as u32 / 100) as u8,
);
} else if states.aura.current_mode == AuraModeNum::Breathe {
if self.oscillator_toggle.load(Ordering::SeqCst) {
colour = Color32::from_rgb(
(red * c2.0 as u32 / 100) as u8,
(red * c2.1 as u32 / 100) as u8,
(red * c2.2 as u32 / 100) as u8,
);
} else {
colour = Color32::from_rgb(
(red * c1.0 as u32 / 100) as u8,
(red * c1.1 as u32 / 100) as u8,
(red * c1.2 as u32 / 100) as u8,
);
}
} else if states.aura.current_mode == AuraModeNum::Strobe {
colour = Color32::from_rgb(
(red * 255 / 100) as u8,
(green * 255 / 100) as u8,
(blue * 255 / 100) as u8,
);
}
// TODO: animation of colour changes/periods/blending
egui::CentralPanel::default().show(ctx, |ui| {
aura_modes_group(supported, states, oscillator_freq, dbus, ui);
keyboard(ui, &states.keyboard_layout, &mut states.aura, colour);
});
// Only do repaint request if on this page
ctx.request_repaint_after(Duration::from_millis(33));
}
}
@@ -0,0 +1,82 @@
use crate::{
page_states::{FanCurvesState, ProfilesState},
widgets::fan_graphs,
RogApp, RogDbusClientBlocking,
};
use egui::Ui;
use rog_profiles::Profile;
use rog_supported::SupportedFunctions;
impl<'a> RogApp<'a> {
pub fn fan_curve_page(&mut self, ctx: &egui::Context) {
let Self {
supported,
states,
asus_dbus: dbus,
..
} = self;
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("Custom fan curves");
ui.label("A fan curve is only active when the related profile is active and the curve is enabled");
Self::fan_curve(
supported,
&mut states.profiles,
&mut states.fan_curves,
dbus, &mut states.error,
ui,
);
fan_graphs(&mut states.profiles, &mut states.fan_curves, dbus, &mut states.error, ui);
});
}
fn fan_curve(
supported: &SupportedFunctions,
profiles: &mut ProfilesState,
curves: &mut FanCurvesState,
dbus: &RogDbusClientBlocking,
do_error: &mut Option<String>,
ui: &mut Ui,
) {
ui.separator();
ui.label("Enabled fan-curves");
let mut changed = false;
ui.horizontal(|ui| {
let mut item = |p: Profile, curves: &mut FanCurvesState, mut checked: bool| {
if ui
.add(egui::Checkbox::new(&mut checked, format!("{:?}", p)))
.changed()
{
dbus.proxies()
.profile()
.set_fan_curve_enabled(p, checked)
.map_err(|err| {
*do_error = Some(err.to_string());
})
.ok();
if !checked {
curves.enabled.remove(&p);
} else {
curves.enabled.insert(p);
}
changed = true;
}
};
for f in profiles.list.iter() {
item(*f, curves, curves.enabled.contains(f));
}
});
if changed {
let notif = curves.was_notified.clone();
match FanCurvesState::new(notif, supported, dbus) {
Ok(f) => *curves = f,
Err(e) => *do_error = Some(e.to_string()),
}
}
}
}
+9
View File
@@ -0,0 +1,9 @@
mod anime_page;
mod aura_page;
mod fan_curve_page;
mod system_page;
pub use anime_page::*;
pub use aura_page::*;
pub use fan_curve_page::*;
pub use system_page::*;
@@ -0,0 +1,53 @@
use crate::{
widgets::{anime_power_group, aura_power_group, platform_profile, rog_bios_group},
RogApp,
};
impl<'a> RogApp<'a> {
pub fn system_page(&mut self, ctx: &egui::Context) {
let Self {
supported,
states,
asus_dbus: dbus,
..
} = self;
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("Experimental application for asusd");
egui::ScrollArea::vertical().show(ui, |ui| {
ui.spacing_mut().item_spacing = egui::vec2(8.0, 10.0);
let rect = ui.available_rect_before_wrap();
egui::Grid::new("grid_of_bits")
.min_col_width(rect.width() / 2.0)
.show(ui, |ui| {
ui.vertical(|ui| {
ui.separator();
if supported.platform_profile.platform_profile {
platform_profile(states, dbus, ui);
}
});
ui.vertical(|ui| {
ui.separator();
aura_power_group(supported, states, dbus, ui);
});
ui.end_row();
ui.vertical(|ui| {
ui.separator();
rog_bios_group(supported, states, dbus, ui);
});
ui.vertical(|ui| {
ui.separator();
if supported.anime_ctrl.0 {
anime_power_group(supported, states, dbus, ui);
}
});
ui.end_row();
});
});
});
}
}
+36
View File
@@ -0,0 +1,36 @@
use egui::{Button, RichText};
pub struct AppErrorShow {
error: String,
}
impl AppErrorShow {
pub fn new(error: String) -> Self {
Self { error }
}
}
impl eframe::App for AppErrorShow {
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("ROG ERROR");
ui.centered_and_justified(|ui| {
ui.label(RichText::new(format!("The error was: {:?}", self.error)).size(22.0));
});
egui::TopBottomPanel::bottom("error_bar_2")
.default_height(26.0)
.show(ctx, |ui| {
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
if ui
.add(Button::new(RichText::new("Okay").size(20.0)))
.clicked()
{
frame.quit();
}
});
});
});
}
}
@@ -0,0 +1,73 @@
use egui::{RichText, Ui};
use rog_supported::SupportedFunctions;
use crate::{page_states::PageDataStates, RogDbusClientBlocking};
pub fn anime_power_group(
_supported: &SupportedFunctions,
states: &mut PageDataStates,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
ui.heading("AniMe Matrix Settings");
ui.label("Options are incomplete. Awake + Boot should work");
let mut changed = false;
ui.horizontal_wrapped(|ui| {
ui.vertical(|ui| {
let h = 16.0;
ui.set_row_height(22.0);
ui.horizontal_wrapped(|ui| {
ui.label(RichText::new("Brightness").size(h));
});
ui.horizontal_wrapped(|ui| {
ui.label(RichText::new("Boot").size(h));
});
ui.horizontal_wrapped(|ui| {
ui.label(RichText::new("Awake").size(h));
});
ui.horizontal_wrapped(|ui| {
ui.label(RichText::new("Sleep").size(h));
});
});
ui.vertical(|ui| {
ui.set_row_height(22.0);
ui.horizontal_wrapped(|ui| {
if ui
.add(egui::Slider::new(&mut states.anime.bright, 0..=254))
.changed()
{
changed = true;
}
});
ui.horizontal_wrapped(|ui| {
if ui.checkbox(&mut states.anime.boot, "Enable").changed() {
dbus.proxies()
.anime()
.set_boot_on_off(states.anime.boot)
.map_err(|err| {
states.error = Some(err.to_string());
})
.ok();
}
});
ui.horizontal_wrapped(|ui| {
if ui.checkbox(&mut states.anime.awake, "Enable").changed() {
dbus.proxies()
.anime()
.set_on_off(states.anime.awake)
.map_err(|err| {
states.error = Some(err.to_string());
})
.ok();
}
});
ui.horizontal_wrapped(|ui| {
if ui.checkbox(&mut states.anime.sleep, "Enable").changed() {
changed = true;
}
});
});
});
}

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