Compare commits

...

109 Commits

Author SHA1 Message Date
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
Luke D. Jones ba40c3f739 Update dependencies 2021-12-19 21:01:47 +13:00
Luke D. Jones 31eff037a2 Bump to version 4.0.7 2021-12-19 21:00:58 +13:00
Luke D. Jones 630dee0b2a Update notes in CLI tool 2021-12-19 20:57:50 +13:00
Luke Jones 9110f06ed5 Update README.md
Remove whitespace and rustup from fedora build section
2021-12-17 21:18:00 +00:00
Luke Jones 2b0eceaa9d Merge branch 'main' into 'main'
Update of README for building and installing

See merge request asus-linux/asusctl!93
2021-12-17 21:16:56 +00:00
Peter Ross c96e1babe5 Update README.md 2021-12-16 15:23:08 +00:00
Peter Ross 9388cbde5d Update README.md 2021-12-16 15:16:35 +00:00
Luke D. Jones e739cddd6a Update patch links 2021-12-04 16:04:56 +13:00
Luke Jones 8cee6e0fc4 Remove Ubuntu repo instructions 2021-11-14 21:51:56 +00:00
Luke Jones bcf516afeb Merge branch 'sonnyp-main-patch-01923' into 'main'
Update kernel support for Linux 5.15

See merge request asus-linux/asusctl!88
2021-11-12 00:23:54 +00:00
Luke Jones b0e3e81b7f Merge branch 'fix/platform-functions/profile-support' into 'main'
Fix incorrect power profile support validation.

See merge request asus-linux/asusctl!89
2021-11-12 00:23:22 +00:00
Luke Jones ce6a1215a3 Merge branch 'LordVicky-main-patch-24845' into 'main'
Update asusd-ledmodes.toml to support Asus Rog Strix G15 G513QE

See merge request asus-linux/asusctl!90
2021-11-12 00:22:51 +00:00
LordVicky f54c1dc7d0 Fix typo 2021-11-09 07:32:39 +00:00
LordVicky 43aaae8d47 Update asusd-ledmodes.toml to support Asus Rog Strix G15 G513QE 2021-11-09 07:28:57 +00:00
Alexander Narsudinov ca463a2944 Fix incorrect power profile support validation.
Before this patch power profile support validation used wrong flag from PlatformProfileFunctions struct.
2021-11-07 15:11:31 +03:00
Sonny Piers 20e22589dc Update kernel support for Linux 5.15 2021-11-04 11:47:47 +00:00
Luke D. Jones 38d047cb8a Update changelog 2021-11-01 16:03:40 +13:00
Luke D. Jones 1d977199f3 Fix cli for bios/g-sync 2021-11-01 10:57:40 +13:00
Luke Jones 5041019d77 Merge branch 'fluke/anime-cli' into 'main'
Fluke/anime cli

See merge request asus-linux/asusctl!87
2021-10-30 20:28:36 +00:00
Luke D. Jones aa3835d3b3 Bump versions 2021-10-31 09:24:07 +13:00
Luke D. Jones 678505811d Add additional anime cli commands for image types 2021-10-28 23:43:50 +13:00
Luke D. Jones a925cbaed5 Bump version 2021-10-27 23:19:31 +13:00
Luke Jones 7d2201d873 Merge branch 'fluke/bugfixes' into 'main'
Add led modes for G513QR

Closes #144 and #143

See merge request asus-linux/asusctl!86
2021-10-27 10:18:31 +00:00
Luke D. Jones c0c1608d44 Update deps 2021-10-27 23:15:25 +13:00
Luke D. Jones 7bc6c83a04 Check and pass error if charge limit not in 20-100 range
Closes #144
2021-10-27 23:04:44 +13:00
Luke D. Jones 3f0df82f2d Parse percentages in fan curve only if '%' provided otherwise range is 0-255 2021-10-27 22:42:32 +13:00
Luke D. Jones 328ff0251b Add led modes for G513QR
Closes #143
2021-10-27 22:35:48 +13:00
80 changed files with 4178 additions and 3667 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:
+63 -1
View File
@@ -4,7 +4,69 @@ 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.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
### Changed
- Fix incorrect power-profile validation
- Update asusd-ledmodes.toml to support Asus Rog Strix G15 G513QE (@LordVicky)
- Update patch notes and links
# [4.0.6] - 2021-11-01
### Changed
- Fix CLI for bios toggles
### Added
- Extra commands for AniMe: pixel-image, gif, pixel-gif
# [4.0.5] - 2021-10-27
### Changed
- Convert fan curve percentage to 0-255 expected by kernel driver only if '%' char is used, otherwise the expected range for fan power is 0-255
- Use correct error in daemon for invalid charging limit
- Enforce charging limit values in range 20-100
### Added
- LED modes for G513QR
# [4.0.4] - 2021-10-02 # [4.0.4] - 2021-10-02
### Changed ### Changed
Generated
+717 -466
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -2,7 +2,9 @@
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"]
[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"
+8 -8
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
@@ -97,7 +97,7 @@ These options are not written to the config file as they are stored in efivars.
### Profiles ### Profiles
asusctl can support setting a power profile via platform_profile drivers. This requires [power-profiles-daemon](https://gitlab.freedesktop.org/hadess/power-profiles-daemon) v0.9.0 minimum. It also requires the kernel patch for platform_profile support to be applied form [here](https://lkml.org/lkml/2021/8/18/1022) - this patch is included in the "rog" kernels we build for fedora and arch, and will hit kernel 5.15 upstream. asusctl can support setting a power profile via platform_profile drivers. This requires [power-profiles-daemon](https://gitlab.freedesktop.org/hadess/power-profiles-daemon) v0.10.0 minimum. It also requires the kernel patch for platform_profile support to be applied form [here](https://lkml.org/lkml/2021/8/18/1022) - this patch is merged to 5.15 kernel upstream.
A common use of asusctl is to bind the `fn+f5` (fan) key to `asusctl profile -n` to cycle through the 3 profiles: A common use of asusctl is to bind the `fn+f5` (fan) key to `asusctl profile -n` to cycle through the 3 profiles:
1. Balanced 1. Balanced
@@ -106,7 +106,7 @@ A common use of asusctl is to bind the `fn+f5` (fan) key to `asusctl profile -n`
#### Fan curves #### Fan curves
Fan curve support requires a laptop that supports it (this is detected automatically) and the kernel patch from [here](https://lkml.org/lkml/2021/8/29/50) which is still in review as of 29/09/21. As with Profiles, this is included in the kernels we build, and will hit 5.15 kernel upstream. Fan curve support requires a laptop that supports it (this is detected automatically) and the kernel patch from [here](https://lkml.org/lkml/2021/10/23/250) which is accepted for the 5.17 kernel release .
The fan curve format can be of varying formats: The fan curve format can be of varying formats:
@@ -356,4 +356,4 @@ Reference to any ASUS products, services, processes, or other information and/or
The use of ROG and ASUS trademarks within this website and associated tools and libraries is only to provide a recognisable identifier to users to enable them to associate that these tools will work with ASUS ROG laptops. The use of ROG and ASUS trademarks within this website and associated tools and libraries is only to provide a recognisable identifier to users to enable them to associate that these tools will work with ASUS ROG laptops.
--- ---
+20 -23
View File
@@ -5,10 +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.
## Kernel patches required ## Kernel support
1. https://lkml.org/lkml/2021/8/20/232 **The minimum supported kernel version is 5.15**
2. https://lkml.org/lkml/2021/8/18/1022
Fan curve control on laptops with this feature require [this patch](https://lkml.org/lkml/2021/10/23/250) which has been merged for 5.17 upstream.
## Goals ## Goals
@@ -24,10 +25,6 @@ supported (while asusd might still run fine on them). For best support use fedor
Point 4? asusd currently uses a tiny fraction of cpu time, and less than 1Mb of ram, the way Point 4? asusd currently uses a tiny fraction of cpu time, and less than 1Mb of ram, the way
a system-level daemon should. a system-level daemon should.
**NOTICE:**
Various patches are required for keyboard support. See [this post](https://asus-linux.org/blog/updates-2021-05-06/) for details on status and which kernels will have which patches.
## Discord ## Discord
[Discord server link](https://discord.gg/4ZKGd7Un5t) [Discord server link](https://discord.gg/4ZKGd7Un5t)
@@ -61,27 +58,27 @@ will probably suffer another rename once it becomes generic enough to do so.
# BUILDING # BUILDING
Requirements are rust >= 1.47 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:** `apt install libclang-dev libudev-dev` **Ubuntu (unsuported):**
**fedora:** `dnf install clang-devel systemd-devel` apt install libclang-dev libudev-dev
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
make
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/
- openSUSE = https://download.opensuse.org/repositories/home:/luke_nukem:/asus/
- Ubuntu = not supported due to packaging woes, but you can build and install on your own.
Download repositories are available [here](https://download.opensuse.org/repositories/home:/luke_nukem:/asus/) for the latest versions of Fedora, Ubuntu, and openSUSE. =======
### Ubuntu
sudo su -c "echo 'deb https://download.opensuse.org/repositories/home:/luke_nukem:/asus/xUbuntu_21.04/ /' > /etc/apt/sources.list.d/luke_nukem.list"
curl -fsSL https://download.opensuse.org/repositories/home:/luke_nukem:/asus/xUbuntu_21.04/Release.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/home_luke_nukem.gpg > /dev/null
sudo apt-get update
sudo apt-get install asusctl dkms-hid-asus-rog
---
Run `make` then `sudo make install` then reboot.
The default init method is to use the udev rule, this ensures that the service is The default init method is to use the udev rule, this ensures that the service is
started when the device is initialised and ready. started when the device is initialised and ready.
+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"
+96 -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);
}
} }
} }
@@ -102,3 +161,6 @@ fn do_charge_notif(limit: &u8) -> Result<NotificationHandle, notify_rust::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.4" version = "4.0.7"
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.20.5"
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();
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 {
+5 -4
View File
@@ -1,5 +1,5 @@
use rog_anime::{AnimeDataBuffer, AnimeGrid}; use rog_anime::{usb::get_anime_type, AnimeDataBuffer, AnimeGrid};
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
@@ -7,8 +7,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;
+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();
+5 -2
View File
@@ -1,12 +1,13 @@
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 +16,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,6 +26,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,
)?; )?;
client client
+5 -2
View File
@@ -3,12 +3,13 @@ use std::{
}; };
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 +18,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 +28,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 {
+66 -57
View File
@@ -1,54 +1,4 @@
use gumdrop::Options; use gumdrop::Options;
use rog_aura::error::Error;
use std::str::FromStr;
#[derive(Copy, Clone, Debug)]
pub enum AnimeStatusValue {
On,
Off,
}
impl FromStr for AnimeStatusValue {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
match s.as_str() {
"on" => Ok(AnimeStatusValue::On),
"off" => Ok(AnimeStatusValue::Off),
_ => {
print!("Invalid argument, must be one of: on, off");
Err(Error::ParseAnime)
}
}
}
}
impl From<AnimeStatusValue> for bool {
fn from(value: AnimeStatusValue) -> Self {
match value {
AnimeStatusValue::On => true,
AnimeStatusValue::Off => false,
}
}
}
#[derive(Options)]
pub struct AnimeLeds {
#[options(help = "print help message")]
help: bool,
#[options(
no_long,
required,
short = "b",
meta = "",
help = "set all leds brightness value"
)]
led_brightness: u8,
}
impl AnimeLeds {
pub fn led_brightness(&self) -> u8 {
self.led_brightness
}
}
#[derive(Options)] #[derive(Options)]
pub struct AnimeCommand { pub struct AnimeCommand {
@@ -56,21 +6,30 @@ pub struct AnimeCommand {
pub help: bool, pub help: bool,
#[options( #[options(
meta = "", meta = "",
help = "turn on/off the panel (accept/reject write requests)" help = "enable/disable the panel LEDs (does not erase last image)"
)] )]
pub turn: Option<AnimeStatusValue>, pub enable: Option<bool>,
#[options(meta = "", help = "turn on/off the panel at boot (with Asus effect)")] #[options(
pub boot: Option<AnimeStatusValue>, meta = "",
help = "enable/disable system animations (boot/sleep/shutdown)"
)]
pub boot_enable: Option<bool>,
#[options(meta = "", help = "set global AniMe brightness value")]
pub brightness: Option<f32>,
#[options(command)] #[options(command)]
pub command: Option<AnimeActions>, pub command: Option<AnimeActions>,
} }
#[derive(Options)] #[derive(Options)]
pub enum AnimeActions { pub enum AnimeActions {
#[options(help = "change all leds brightness")] #[options(help = "display a PNG image")]
Leds(AnimeLeds),
#[options(help = "display an image png")]
Image(AnimeImage), Image(AnimeImage),
#[options(help = "display a diagonal/pixel-perfect PNG")]
PixelImage(AnimeImageDiagonal),
#[options(help = "display an animated GIF")]
Gif(AnimeGif),
#[options(help = "display an animated diagonal/pixel-perfect GIF")]
PixelGif(AnimeGifDiagonal),
} }
#[derive(Options)] #[derive(Options)]
@@ -90,3 +49,53 @@ pub struct AnimeImage {
#[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")] #[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")]
pub bright: f32, pub bright: f32,
} }
#[derive(Options)]
pub struct AnimeImageDiagonal {
#[options(help = "print help message")]
pub help: bool,
#[options(meta = "", help = "full path to the png to display")]
pub path: String,
#[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")]
pub bright: f32,
}
#[derive(Options)]
pub struct AnimeGif {
#[options(help = "print help message")]
pub help: bool,
#[options(meta = "", help = "full path to the png to display")]
pub path: String,
#[options(meta = "", default = "1.0", help = "scale 1.0 == normal")]
pub scale: f32,
#[options(meta = "", default = "0.0", help = "x position (float)")]
pub x_pos: f32,
#[options(meta = "", default = "0.0", help = "y position (float)")]
pub y_pos: f32,
#[options(meta = "", default = "0.0", help = "the angle in radians")]
pub angle: f32,
#[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")]
pub bright: f32,
#[options(
meta = "",
default = "1",
help = "how many loops to play - 0 is infinite"
)]
pub loops: u32,
}
#[derive(Options)]
pub struct AnimeGifDiagonal {
#[options(help = "print help message")]
pub help: bool,
#[options(meta = "", help = "full path to the png to display")]
pub path: String,
#[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")]
pub bright: f32,
#[options(
meta = "",
default = "1",
help = "how many loops to play - 0 is infinite"
)]
pub loops: u32,
}
+84 -54
View File
@@ -2,6 +2,53 @@ 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 LedPowerCommand {
#[options(help = "print help message")]
pub help: bool,
#[options(command)]
pub command: Option<SetAuraEnabled>,
}
#[derive(Options)]
pub enum SetAuraEnabled {
#[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 enabled while device is awake")]
Awake(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();
// dbg!(s);
// 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 +97,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 +113,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 +127,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 +143,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 +161,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 +230,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 +242,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 +252,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 +263,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 +274,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 +285,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 +354,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
}
}
+3 -11
View File
@@ -1,6 +1,6 @@
use crate::{ use crate::{
anime_cli::AnimeCommand, anime_cli::AnimeCommand,
aura_cli::{LedBrightness, SetAuraBuiltin}, aura_cli::{LedBrightness, LedPowerCommand, SetAuraBuiltin},
profiles_cli::{FanCurveCommand, ProfileCommand}, profiles_cli::{FanCurveCommand, ProfileCommand},
}; };
use gumdrop::Options; use gumdrop::Options;
@@ -29,6 +29,8 @@ 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 keyboard lighting from built-in modes")]
LedPower(LedPowerCommand),
#[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 +51,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>,
} }
+320 -73
View File
@@ -1,24 +1,31 @@
mod anime_cli; use std::process::Command;
mod aura_cli; use std::thread::sleep;
mod cli_opts; use std::{env::args, path::Path};
mod profiles_cli;
use crate::aura_cli::{LedBrightness, SetAuraBuiltin}; use aura_cli::LedPowerCommand;
use crate::cli_opts::*;
use anime_cli::{AnimeActions, AnimeCommand};
use gumdrop::{Opt, Options}; use gumdrop::{Opt, Options};
use anime_cli::{AnimeActions, AnimeCommand};
use profiles_cli::{FanCurveCommand, ProfileCommand}; use profiles_cli::{FanCurveCommand, ProfileCommand};
use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2, ANIME_DATA_LEN}; use rog_anime::usb::get_anime_type;
use rog_anime::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, Vec2};
use rog_aura::usb::AuraControl;
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::{
AnimeSupportedFunctions, LedSupportedFunctions, PlatformProfileFunctions, AnimeSupportedFunctions, LedSupportedFunctions, PlatformProfileFunctions,
RogBiosSupportedFunctions, RogBiosSupportedFunctions,
}; };
use std::process::Command;
use std::{env::args, path::Path}; use crate::aura_cli::LedBrightness;
use crate::cli_opts::*;
mod anime_cli;
mod aura_cli;
mod cli_opts;
mod profiles_cli;
const CONFIG_ADVICE: &str = "A config file need to be removed so a new one can be generated"; const CONFIG_ADVICE: &str = "A config file need to be removed so a new one can be generated";
@@ -43,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);
@@ -53,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);
@@ -135,10 +142,11 @@ 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::LedPower(pow)) => handle_led_power(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)?
@@ -166,13 +174,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.to_string()); 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))?,
} }
} }
@@ -189,7 +197,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(())
@@ -202,32 +210,34 @@ 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>> {
if (cmd.command.is_none() && cmd.boot.is_none() && cmd.turn.is_none()) || cmd.help { if (cmd.command.is_none()
&& cmd.enable.is_none()
&& cmd.boot_enable.is_none()
&& cmd.brightness.is_none())
|| cmd.help
{
println!("Missing arg or command\n\n{}", cmd.self_usage()); println!("Missing arg or command\n\n{}", cmd.self_usage());
if let Some(lst) = cmd.self_command_list() { if let Some(lst) = cmd.self_command_list() {
println!("\n{}", lst); println!("\n{}", lst);
} }
} }
if let Some(anime_turn) = cmd.turn { if let Some(anime_turn) = cmd.enable {
dbus.proxies().anime().set_led_power(anime_turn.into())? dbus.proxies().anime().set_on_off(anime_turn)?
} }
if let Some(anime_boot) = cmd.boot { if let Some(anime_boot) = cmd.boot_enable {
dbus.proxies() dbus.proxies().anime().set_boot_on_off(anime_boot)?
.anime() }
.set_system_animations(anime_boot.into())? if let Some(bright) = cmd.brightness {
verify_brightness(bright);
dbus.proxies().anime().set_brightness(bright)?
} }
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::Leds(anime_leds) => {
let data = AnimeDataBuffer::from_vec(
[anime_leds.led_brightness(); ANIME_DATA_LEN].to_vec(),
);
dbus.proxies().anime().write(data)?;
}
AnimeActions::Image(image) => { AnimeActions::Image(image) => {
if image.help_requested() || image.path.is_empty() { if image.help_requested() || image.path.is_empty() {
println!("Missing arg or command\n\n{}", image.self_usage()); println!("Missing arg or command\n\n{}", image.self_usage());
@@ -236,6 +246,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),
@@ -243,28 +254,120 @@ 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>::from(&matrix))?;
} }
AnimeActions::PixelImage(image) => {
if image.help_requested() || image.path.is_empty() {
println!("Missing arg or command\n\n{}", image.self_usage());
if let Some(lst) = image.self_command_list() {
println!("\n{}", lst);
}
std::process::exit(1);
}
verify_brightness(image.bright);
let matrix = AnimeDiagonal::from_png(
Path::new(&image.path),
None,
image.bright,
anime_type,
)?;
dbus.proxies()
.anime()
.write(matrix.into_data_buffer(anime_type))?;
}
AnimeActions::Gif(gif) => {
if gif.help_requested() || gif.path.is_empty() {
println!("Missing arg or command\n\n{}", gif.self_usage());
if let Some(lst) = gif.self_command_list() {
println!("\n{}", lst);
}
std::process::exit(1);
}
verify_brightness(gif.bright);
let matrix = AnimeGif::from_gif(
Path::new(&gif.path),
gif.scale,
gif.angle,
Vec2::new(gif.x_pos, gif.y_pos),
AnimTime::Count(1),
gif.bright,
anime_type,
)?;
let mut loops = gif.loops as i32;
loop {
for frame in matrix.frames() {
dbus.proxies().anime().write(frame.frame().clone())?;
sleep(frame.delay());
}
if loops >= 0 {
loops -= 1;
}
if loops == 0 {
break;
}
}
}
AnimeActions::PixelGif(gif) => {
if gif.help_requested() || gif.path.is_empty() {
println!("Missing arg or command\n\n{}", gif.self_usage());
if let Some(lst) = gif.self_command_list() {
println!("\n{}", lst);
}
std::process::exit(1);
}
verify_brightness(gif.bright);
let matrix = AnimeGif::from_diagonal_gif(
Path::new(&gif.path),
AnimTime::Count(1),
gif.bright,
anime_type,
)?;
let mut loops = gif.loops as i32;
loop {
for frame in matrix.frames() {
dbus.proxies().anime().write(frame.frame().clone())?;
sleep(frame.delay());
}
if loops >= 0 {
loops -= 1;
}
if loops == 0 {
break;
}
}
}
} }
} }
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");
} }
@@ -275,11 +378,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
@@ -305,45 +411,185 @@ 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_power(
dbus: &RogDbusClientBlocking,
_supported: &LedSupportedFunctions,
power: &LedPowerCommand,
) -> 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) = LedPowerCommand::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(());
}
match pow {
// TODO: make this a macro or something
aura_cli::SetAuraEnabled::Boot(arg) => {
let mut enabled: Vec<AuraControl> = Vec::new();
let mut disabled: Vec<AuraControl> = Vec::new();
arg.keyboard.map(|v| {
if v {
enabled.push(AuraControl::BootKeyb)
} else {
disabled.push(AuraControl::BootKeyb)
}
});
arg.logo.map(|v| {
if v {
enabled.push(AuraControl::BootLogo)
} else {
disabled.push(AuraControl::BootLogo)
}
});
arg.lightbar.map(|v| {
if v {
enabled.push(AuraControl::BootBar)
} else {
disabled.push(AuraControl::BootBar)
}
});
if !enabled.is_empty() {
dbus.proxies().led().set_leds_enabled(enabled)?;
}
if !disabled.is_empty() {
dbus.proxies().led().set_leds_disabled(disabled)?;
}
}
aura_cli::SetAuraEnabled::Sleep(arg) => {
let mut enabled: Vec<AuraControl> = Vec::new();
let mut disabled: Vec<AuraControl> = Vec::new();
arg.keyboard.map(|v| {
if v {
enabled.push(AuraControl::SleepKeyb)
} else {
disabled.push(AuraControl::SleepKeyb)
}
});
arg.logo.map(|v| {
if v {
enabled.push(AuraControl::SleepLogo)
} else {
disabled.push(AuraControl::SleepLogo)
}
});
arg.lightbar.map(|v| {
if v {
enabled.push(AuraControl::SleepBar)
} else {
disabled.push(AuraControl::SleepBar)
}
});
if !enabled.is_empty() {
dbus.proxies().led().set_leds_enabled(enabled)?;
}
if !disabled.is_empty() {
dbus.proxies().led().set_leds_disabled(disabled)?;
}
}
aura_cli::SetAuraEnabled::Awake(arg) => {
let mut enabled: Vec<AuraControl> = Vec::new();
let mut disabled: Vec<AuraControl> = Vec::new();
arg.keyboard.map(|v| {
if v {
enabled.push(AuraControl::AwakeKeyb)
} else {
disabled.push(AuraControl::AwakeKeyb)
}
});
arg.logo.map(|v| {
if v {
enabled.push(AuraControl::AwakeLogo)
} else {
disabled.push(AuraControl::AwakeLogo)
}
});
arg.lightbar.map(|v| {
if v {
enabled.push(AuraControl::AwakeBar)
} else {
disabled.push(AuraControl::AwakeBar)
}
});
if !enabled.is_empty() {
dbus.proxies().led().set_leds_enabled(enabled)?;
}
if !disabled.is_empty() {
dbus.proxies().led().set_leds_disabled(disabled)?;
}
}
aura_cli::SetAuraEnabled::Shutdown(arg) => {
let mut enabled: Vec<AuraControl> = Vec::new();
let mut disabled: Vec<AuraControl> = Vec::new();
arg.keyboard.map(|v| {
if v {
enabled.push(AuraControl::ShutdownKeyb)
} else {
disabled.push(AuraControl::ShutdownKeyb)
}
});
arg.logo.map(|v| {
if v {
enabled.push(AuraControl::ShutdownLogo)
} else {
disabled.push(AuraControl::ShutdownLogo)
}
});
arg.lightbar.map(|v| {
if v {
enabled.push(AuraControl::ShutdownBar)
} else {
disabled.push(AuraControl::ShutdownBar)
}
});
if !enabled.is_empty() {
dbus.proxies().led().set_leds_enabled(enabled)?;
}
if !disabled.is_empty() {
dbus.proxies().led().set_leds_disabled(disabled)?;
} }
} }
_ => dbus
.proxies()
.led()
.set_led_mode(&<AuraEffect>::from(mode))?,
} }
} }
if let Some(enable) = mode.awake_enable {
dbus.proxies().led().set_awake_enabled(enable)?;
}
if let Some(enable) = mode.sleep_enable {
dbus.proxies().led().set_sleep_enabled(enable)?;
}
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>> {
if !supported.fan_curves { if !supported.platform_profile {
println!("Profiles not supported by either this kernel or by the laptop."); println!("Profiles not supported by either this kernel or by the laptop.");
return Err(ProfileError::NotSupported.into()); return Err(ProfileError::NotSupported.into());
} }
println!("Warning: Profiles now depend on power-profiles-daemon v0.9+ which may not be in your install");
println!(" If you have unexpected behaviour or have only two profiles in your desktop control");
println!(" you need to manually install from https://gitlab.freedesktop.org/hadess/power-profiles-daemon");
println!(" Fedora and Arch distros will get the update soon...\n");
if !cmd.next && !cmd.list && cmd.profile_set.is_none() && !cmd.profile_get { if !cmd.next && !cmd.list && cmd.profile_set.is_none() && !cmd.profile_get {
if !cmd.help { if !cmd.help {
println!("Missing arg or command\n"); println!("Missing arg or command\n");
@@ -376,12 +622,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!("This requires kernel 5.17 or the fan curve patch listed in the readme.");
return Err(ProfileError::NotSupported.into()); return Err(ProfileError::NotSupported.into());
} }
@@ -429,7 +676,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)?;
} }
} }
@@ -437,7 +684,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>> {
@@ -456,23 +703,23 @@ 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_toggle
|| !line.contains("GPU") && !supported.dedicated_gfx_toggle || line.contains("GPU") && supported.dedicated_gfx_toggle
}) { }) {
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!(
@@ -483,7 +730,7 @@ 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);
} }
} }
+2 -1
View File
@@ -44,7 +44,8 @@ pub struct FanCurveCommand {
#[options( #[options(
meta = "", meta = "",
help = "data format = 30c:1%,49c:2%,59c:3%,69c:4%,79c:31%,89c:49%,99c:56%,109c:58%. `mod-profile` required" help = "data format = 30c:1%,49c:2%,59c:3%,69c:4%,79c:31%,89c:49%,99c:56%,109c:58%.
`--mod-profile` required. If '%' is omitted the fan range is 0-255"
)] )]
pub data: Option<CurveData>, pub data: Option<CurveData>,
} }
+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_led_power(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_led_power(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.4" version = "4.2.0"
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"
+3 -2
View File
@@ -2,6 +2,7 @@ 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";
@@ -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 {
+122 -149
View File
@@ -2,22 +2,24 @@ 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,
error::Error, error::Error,
sync::{Arc, Mutex}, sync::{Arc, Mutex, MutexGuard},
thread::sleep, thread::sleep,
}; };
use std::{ use std::{
@@ -39,6 +41,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 +54,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 +76,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 +116,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,60 +126,58 @@ 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 false // Don't exit yet
}) })
}, .map_err(|err| {
) { warn!("rog_anime::run_animation:callback {}", err);
warn!("rog_anime::run_animation: {}", 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;
@@ -216,17 +192,20 @@ 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()); let data =
AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()]);
lock.write_data_buffer(data); lock.write_data_buffer(data);
} }
// 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");
}) })
@@ -276,7 +255,7 @@ 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) {
for led in buffer.get_mut()[7..].iter_mut() { for led in buffer.data_mut()[7..].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;
@@ -297,89 +276,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(())
} }
} }
+28 -28
View File
@@ -1,12 +1,11 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use log::warn; use async_trait::async_trait;
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 +14,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();
} }
} }
@@ -53,8 +44,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 +55,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 +86,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 +113,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 +133,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<()>;
} }
+34 -153
View File
@@ -1,63 +1,22 @@
use crate::laptops::LaptopLedData; use crate::laptops::LaptopLedData;
use log::{error, info, warn}; use log::{error, warn};
use rog_aura::usb::AuraControl;
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, LedBrightness}; use rog_aura::{AuraEffect, AuraModeNum, AuraZone, LedBrightness};
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};
pub static AURA_CONFIG_PATH: &str = "/etc/asusd/aura.conf"; pub static AURA_CONFIG_PATH: &str = "/etc/asusd/aura.conf";
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct AuraConfigV320 { #[serde(default)]
pub brightness: u32,
pub current_mode: AuraModeNum,
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
pub multizone: Option<AuraMultiZone>,
}
impl AuraConfigV320 {
pub(crate) fn into_current(self) -> AuraConfig {
AuraConfig {
brightness: <LedBrightness>::from(self.brightness),
current_mode: self.current_mode,
builtins: self.builtins,
multizone: self.multizone,
awake_enabled: true,
sleep_anim_enabled: true,
}
}
}
#[derive(Deserialize, Serialize)]
pub struct AuraConfigV352 {
pub brightness: LedBrightness,
pub current_mode: AuraModeNum,
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
pub multizone: Option<AuraMultiZone>,
}
impl AuraConfigV352 {
pub(crate) fn into_current(self) -> AuraConfig {
AuraConfig {
brightness: self.brightness,
current_mode: self.current_mode,
builtins: self.builtins,
multizone: self.multizone,
awake_enabled: true,
sleep_anim_enabled: true,
}
}
}
#[derive(Deserialize, Serialize)]
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 enabled: HashSet<AuraControl>,
pub sleep_anim_enabled: bool,
} }
impl Default for AuraConfig { impl Default for AuraConfig {
@@ -67,8 +26,20 @@ impl Default for AuraConfig {
current_mode: AuraModeNum::Static, current_mode: AuraModeNum::Static,
builtins: BTreeMap::new(), builtins: BTreeMap::new(),
multizone: None, multizone: None,
awake_enabled: true, enabled: HashSet::from([
sleep_anim_enabled: true, AuraControl::BootLogo,
AuraControl::BootKeyb,
AuraControl::SleepLogo,
AuraControl::SleepKeyb,
AuraControl::AwakeLogo,
AuraControl::AwakeKeyb,
AuraControl::ShutdownLogo,
AuraControl::ShutdownKeyb,
AuraControl::AwakeBar,
AuraControl::BootBar,
AuraControl::SleepBar,
AuraControl::ShutdownBar,
]),
} }
} }
} }
@@ -94,16 +65,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",
@@ -170,107 +131,27 @@ impl AuraConfig {
} }
_ => { _ => {
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.mode == effect.mode {
*fx = effect;
break;
}
}
} else {
let mut tmp = BTreeMap::new();
tmp.insert(*effect.mode(), vec![effect]);
self.multizone = Some(tmp);
}
} }
} }
} }
} }
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)]
pub struct AuraMultiZone {
static_: [AuraEffect; 4],
breathe: [AuraEffect; 4],
}
impl AuraMultiZone {
pub fn set(&mut self, effect: AuraEffect) {
if effect.mode == AuraModeNum::Static {
match effect.zone {
AuraZone::None => {}
AuraZone::One => self.static_[0] = effect,
AuraZone::Two => self.static_[1] = effect,
AuraZone::Three => self.static_[2] = effect,
AuraZone::Four => self.static_[3] = effect,
}
} else if effect.mode == AuraModeNum::Breathe {
match effect.zone {
AuraZone::None => {}
AuraZone::One => self.breathe[0] = effect,
AuraZone::Two => self.breathe[1] = effect,
AuraZone::Three => self.breathe[2] = effect,
AuraZone::Four => self.breathe[3] = effect,
}
}
}
pub fn static_(&self) -> &[AuraEffect; 4] {
&self.static_
}
pub fn breathe(&self) -> &[AuraEffect; 4] {
&self.breathe
}
}
impl Default for AuraMultiZone {
fn default() -> Self {
Self {
static_: [
AuraEffect {
mode: AuraModeNum::Static,
zone: AuraZone::One,
..Default::default()
},
AuraEffect {
mode: AuraModeNum::Static,
zone: AuraZone::Two,
..Default::default()
},
AuraEffect {
mode: AuraModeNum::Static,
zone: AuraZone::Three,
..Default::default()
},
AuraEffect {
mode: AuraModeNum::Static,
zone: AuraZone::Four,
..Default::default()
},
],
breathe: [
AuraEffect {
mode: AuraModeNum::Breathe,
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()
},
],
}
}
}
+102 -72
View File
@@ -6,21 +6,21 @@ use crate::{
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::usb::AuraControl;
use rog_aura::{ use rog_aura::{
usb::{ usb::{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_supported::LedSupportedFunctions; use rog_supported::LedSupportedFunctions;
use smol::{stream::StreamExt, Executor};
use std::io::{Read, Write}; use std::io::{Read, 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 std::{fs::OpenOptions, thread::spawn}; use std::{fs::OpenOptions, sync::MutexGuard};
use zbus::Connection; use zbus::Connection;
use crate::GetSupported; use crate::GetSupported;
@@ -32,10 +32,10 @@ impl GetSupported for CtrlKbdLed {
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;
LedSupportedFunctions { LedSupportedFunctions {
brightness_set: CtrlKbdLed::get_kbd_bright_path().is_some(), brightness_set: CtrlKbdLed::get_kbd_bright_path().is_some(),
@@ -54,50 +54,13 @@ pub struct CtrlKbdLed {
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 +88,81 @@ 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();
if let Some(mode) = lock.config.builtins.get(&lock.config.current_mode) {
lock.write_mode(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();
// let inner = self.inner.clone();
// self.repeating_task(500, executor, move || loop {
// if let Ok(ref mut lock) = inner.try_lock() {
// Self::update_config(lock).unwrap();
// break;
// }
// })
// .await;
Ok(()) Ok(())
} }
} }
@@ -145,8 +177,8 @@ impl crate::Reloadable for CtrlKbdLedReloader {
ctrl.do_command(mode).ok(); ctrl.do_command(mode).ok();
} }
ctrl.set_states_enabled(ctrl.config.awake_enabled, ctrl.config.sleep_anim_enabled) ctrl.set_power_states(&ctrl.config)
.map_err(|err| warn!("{}", err)) .map_err(|err| warn!("{err}"))
.ok(); .ok();
} }
Ok(()) Ok(())
@@ -170,9 +202,10 @@ impl CtrlKbdLed {
match Self::find_led_node(prod) { match Self::find_led_node(prod) {
Ok(node) => { Ok(node) => {
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}"),
} }
} }
@@ -263,20 +296,17 @@ 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 { let set: Vec<AuraControl> = config.enabled.iter().map(|v| *v).collect();
LED_AWAKE_ON_SLEEP_ON let bytes = AuraControl::to_bytes(&set);
} else if awake && !sleep {
LED_AWAKE_ON_SLEEP_OFF // Quite ugly, must be a more idiomatic way to do
} else if !awake && sleep { let message = [
LED_AWAKE_OFF_SLEEP_ON 0x5d, 0xbd, 0x01, bytes[0], bytes[1], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
} else if !awake && !sleep { ];
LED_AWAKE_OFF_SLEEP_OFF
} else { self.write_bytes(&message)?;
LED_AWAKE_ON_SLEEP_ON
};
self.write_bytes(&bytes)?;
self.write_bytes(&LED_SET)?; self.write_bytes(&LED_SET)?;
// Changes won't persist unless apply is set // Changes won't persist unless apply is set
self.write_bytes(&LED_APPLY)?; self.write_bytes(&LED_APPLY)?;
+108 -62
View File
@@ -1,18 +1,14 @@
use log::{error, warn}; use async_trait::async_trait;
use rog_aura::{AuraEffect, LedBrightness, LedPowerStates}; use log::warn;
use zbus::dbus_interface; use rog_aura::{usb::AuraControl, AuraEffect, LedBrightness};
use zvariant::ObjectPath; 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/Led", server).await;
.at(&ObjectPath::from_str_unchecked("/org/asuslinux/Led"), self)
.map_err(|err| {
error!("DbusKbdLed: add_to_server {}", err);
})
.ok();
} }
} }
@@ -22,7 +18,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,48 +26,87 @@ 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) { ///
/// enum AuraControl {
/// BootLogo,
/// BootKeyb,
/// AwakeLogo,
/// AwakeKeyb,
/// SleepLogo,
/// SleepKeyb,
/// ShutdownLogo,
/// ShutdownKeyb,
/// AwakeBar,
/// BootBar,
/// SleepBar,
/// ShutdownBar,
/// }
async fn set_leds_enabled(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
enabled: Vec<AuraControl>,
) {
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 s in enabled {
.map_err(|err| warn!("{}", err)) ctrl.config.enabled.insert(s);
.ok(); }
ctrl.config.awake_enabled = enabled;
ctrl.config.write(); ctrl.config.write();
let states = LedPowerStates { ctrl.set_power_states(&ctrl.config)
enabled: ctrl.config.awake_enabled, .map_err(|err| warn!("{}", err))
sleep_anim_enabled: ctrl.config.sleep_anim_enabled, .ok();
};
self.notify_power_states(&states) let set: Vec<AuraControl> = ctrl.config.enabled.iter().map(|v| *v).collect();
states = Some(set);
}
// 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));
} }
} }
/// Set the keyboard LED suspend animation to enabled while the device is suspended async fn set_leds_disabled(
fn set_sleep_enabled(&mut self, enabled: bool) { &mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
disabled: Vec<AuraControl>,
) {
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(ctrl.config.awake_enabled, enabled) for s in disabled {
ctrl.config.enabled.remove(&s);
}
ctrl.config.write();
ctrl.set_power_states(&ctrl.config)
.map_err(|err| warn!("{}", err)) .map_err(|err| warn!("{}", err))
.ok(); .ok();
ctrl.config.sleep_anim_enabled = enabled;
ctrl.config.write(); let set: Vec<AuraControl> = ctrl.config.enabled.iter().map(|v| *v).collect();
let states = LedPowerStates { states = Some(set);
enabled: ctrl.config.awake_enabled, }
sleep_anim_enabled: ctrl.config.sleep_anim_enabled, // Need to pull state out like this due to MutexGuard
}; if let Some(states) = states {
self.notify_power_states(&states) Self::notify_power_states(&ctxt, &states)
.await
.unwrap_or_else(|err| warn!("{}", err)); .unwrap_or_else(|err| warn!("{}", err));
} }
} }
fn set_led_mode(&mut self, effect: AuraEffect) { async fn set_led_mode(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
effect: AuraEffect,
) {
let mut led = None;
if let Ok(mut ctrl) = self.0.try_lock() { if let Ok(mut ctrl) = self.0.try_lock() {
match ctrl.do_command(effect) { match ctrl.do_command(effect) {
Ok(_) => { Ok(_) => {
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));
} }
} }
Err(err) => { Err(err) => {
@@ -79,40 +114,55 @@ impl CtrlKbdLedZbus {
} }
} }
} }
if let Some(led) = led {
Self::notify_led(&ctxt, led)
.await
.unwrap_or_else(|err| warn!("{}", err));
}
} }
fn next_led_mode(&self) { async fn next_led_mode(&self, #[zbus(signal_context)] ctxt: SignalContext<'_>) {
if let Ok(mut ctrl) = self.0.try_lock() { let mut led = None;
if let Ok(mut ctrl) = self.0.lock() {
ctrl.toggle_mode(false) ctrl.toggle_mode(false)
.unwrap_or_else(|err| warn!("{}", err)); .unwrap_or_else(|err| warn!("{}", err));
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));
}
} }
fn prev_led_mode(&self) { async fn prev_led_mode(&self, #[zbus(signal_context)] ctxt: SignalContext<'_>) {
if let Ok(mut ctrl) = self.0.try_lock() { let mut led = None;
if let Ok(mut ctrl) = self.0.lock() {
ctrl.toggle_mode(true) ctrl.toggle_mode(true)
.unwrap_or_else(|err| warn!("{}", err)); .unwrap_or_else(|err| warn!("{}", err));
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));
}
} }
fn next_led_brightness(&self) { async fn next_led_brightness(&self) {
if let Ok(mut ctrl) = self.0.try_lock() { if let Ok(mut ctrl) = self.0.try_lock() {
ctrl.next_brightness() ctrl.next_brightness()
.unwrap_or_else(|err| warn!("{}", err)); .unwrap_or_else(|err| warn!("{}", err));
} }
} }
fn prev_led_brightness(&self) { async fn prev_led_brightness(&self) {
if let Ok(mut ctrl) = self.0.try_lock() { if let Ok(mut ctrl) = self.0.try_lock() {
ctrl.prev_brightness() ctrl.prev_brightness()
.unwrap_or_else(|err| warn!("{}", err)); .unwrap_or_else(|err| warn!("{}", err));
@@ -120,24 +170,17 @@ impl CtrlKbdLedZbus {
} }
#[dbus_interface(property)] #[dbus_interface(property)]
fn awake_enabled(&self) -> bool { async fn leds_enabled(&self) -> Vec<u8> {
if let Ok(ctrl) = self.0.try_lock() { if let Ok(ctrl) = self.0.try_lock() {
return ctrl.config.awake_enabled; let set: Vec<AuraControl> = ctrl.config.enabled.iter().map(|v| *v).collect();
return AuraControl::to_bytes(&set).to_vec();
} }
true vec![0, 0]
}
#[dbus_interface(property)]
fn sleep_enabled(&self) -> bool {
if let Ok(ctrl) = self.0.try_lock() {
return ctrl.config.sleep_anim_enabled;
}
true
} }
/// Return the current mode data /// Return the current mode data
#[dbus_interface(property)] #[dbus_interface(property)]
fn led_mode(&self) -> String { async 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) { if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
if let Ok(json) = serde_json::to_string(&mode) { if let Ok(json) = serde_json::to_string(&mode) {
@@ -151,7 +194,7 @@ impl CtrlKbdLedZbus {
/// Return a list of available modes /// Return a list of available modes
#[dbus_interface(property)] #[dbus_interface(property)]
fn led_modes(&self) -> String { async fn led_modes(&self) -> String {
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) { if let Ok(json) = serde_json::to_string(&ctrl.config.builtins) {
return json; return json;
@@ -163,7 +206,7 @@ impl CtrlKbdLedZbus {
/// 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 +215,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: &[AuraControl],
) -> zbus::Result<()>;
} }
+99 -29
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,24 +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) { async fn set_limit(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
limit: u8,
) -> zbus::fdo::Result<()> {
if !(20..=100).contains(&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(())
} }
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;
} }
@@ -55,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();
} }
} }
@@ -77,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(())
} }
@@ -104,12 +104,9 @@ 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) {
warn!( return Err(RogError::ChargeLimit(limit));
"Unable to set battery charge limit, must be between 20-100: requested {}",
limit
);
} }
let path = Self::get_battery_path()?; let path = Self::get_battery_path()?;
@@ -129,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(())
}
}
+18 -12
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;
@@ -21,8 +23,7 @@ impl GetSupported for CtrlPlatformProfile {
warn!( warn!(
r#" r#"
platform_profile kernel interface not found, your laptop does not support this, or the interface is missing. 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 with the following patch applied: To enable profile support you require a kernel version 5.15.2 minimum.
https://lkml.org/lkml/2021/8/18/1022
"# "#
); );
} }
@@ -38,8 +39,8 @@ https://lkml.org/lkml/2021/8/18/1022
r#" r#"
fan curves kernel interface not found, your laptop does not support this, or the interface is missing. 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: To enable fan-curve support you require a kernel with the following patch applied:
https://lkml.org/lkml/2021/8/20/232 https://lkml.org/lkml/2021/10/23/250
Please note that as of 24/08/2021 this is not final. This patch has been accepted upstream for 5.17 kernel release.
"# "#
); );
} }
@@ -136,16 +137,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();
} }
} }
+27 -30
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";
@@ -36,22 +37,23 @@ impl GetSupported for CtrlRogBios {
#[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 +63,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 +93,13 @@ 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<()> {}
} }
#[async_trait]
impl crate::ZbusAdd for CtrlRogBios { impl crate::ZbusAdd for CtrlRogBios {
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/RogBios", server).await;
.at(
&ObjectPath::from_str_unchecked("/org/asuslinux/RogBios"),
self,
)
.map_err(|err| {
warn!("CtrlRogBios: add_to_server {}", err);
err
})
.ok();
} }
} }
+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);
}
} }
} }
+2
View File
@@ -22,6 +22,7 @@ pub enum RogError {
Modprobe(String), Modprobe(String),
Io(std::io::Error), Io(std::io::Error),
Zbus(zbus::Error), Zbus(zbus::Error),
ChargeLimit(u8),
} }
impl fmt::Display for RogError { impl fmt::Display for RogError {
@@ -46,6 +47,7 @@ impl fmt::Display for RogError {
RogError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail), RogError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail),
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),
} }
} }
} }
+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 {
+59 -23
View File
@@ -2,98 +2,127 @@
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", "G713QR"] board_names = ["G531GW", "G533QR", "G533QS", "G733QS", "G733QR", "G513QR", "G713QR", "G513QM"]
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 = ["GX531", "G512LV", "G712LV", "G712LW", "G513IH", "G513QY", "G713QM"] board_names = ["G513QE", "GX531", "G512LV", "G712LV", "G712LW", "G513IH", "G513QY", "G713QM", "G512"]
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 +130,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
+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();
} }
} }
+10 -6
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "rog_anime" name = "rog_anime"
version = "1.1.0" version = "1.3.4"
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,22 @@ edition = "2018"
exclude = ["data"] exclude = ["data"]
[features] [features]
default = ["dbus"] default = ["dbus", "detect"]
dbus = ["zvariant", "zvariant_derive"] dbus = ["zvariant"]
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.20.5", features = ["serde"] }
zvariant = { version = "^2.6", optional = true } zvariant = { version = "^3.0", optional = true }
zvariant_derive = { version = "^2.6", 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

+80 -36
View File
@@ -1,15 +1,12 @@
use std::{ use std::{
sync::{
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, AnimTime, AnimeGif};
@@ -19,48 +16,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,34 +102,45 @@ 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>) -> Self {
assert_eq!(input.len(), ANIME_DATA_LEN); assert_eq!(data.len(), anime.data_length());
Self(input)
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 From<AnimeDataBuffer> for AnimePacketType {
#[inline] #[inline]
fn from(anime: AnimeDataBuffer) -> Self { fn from(anime: AnimeDataBuffer) -> Self {
assert!(anime.0.len() == ANIME_DATA_LEN); assert_eq!(anime.data.len(), anime.anime.data_length());
let mut buffers = [[0; 640]; 2];
for (idx, chunk) in anime.0.as_slice().chunks(PANE_LEN).enumerate() { 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);
if matches!(anime.anime, AnimeType::GA402) {
buffers[2][..7].copy_from_slice(&USB_PREFIX3);
}
buffers 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, AnimeError>,
callback: &dyn Fn(AnimeDataBuffer) -> Result<(), AnimeError>,
) -> Result<(), AnimeError> { ) -> Result<(), AnimeError> {
let mut count = 0; let mut count = 0;
let start = Instant::now(); let start = Instant::now();
@@ -140,14 +185,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 +200,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() {
+551 -84
View File
@@ -1,40 +1,32 @@
use std::{path::Path, time::Duration}; use std::{path::Path, time::Duration};
use crate::{ use crate::{data::AnimeDataBuffer, error::AnimeError, AnimeType};
data::{AnimeDataBuffer, ANIME_DATA_LEN},
error::AnimeError,
};
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 +39,14 @@ impl AnimeDiagonal {
path: &Path, path: &Path,
duration: Option<Duration>, duration: Option<Duration>,
bright: f32, bright: f32,
anime_type: AnimeType,
) -> Result<Self, AnimeError> { ) -> Result<Self, AnimeError> {
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 +93,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 +118,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) -> 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) -> 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) -> 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::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);
let pkt = AnimePacketType::from(data);
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);
let pkt = AnimePacketType::from(data);
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);
let pkt = AnimePacketType::from(data);
assert_eq!(pkt[0], pkt0_check);
assert_eq!(pkt[1], pkt1_check);
assert_eq!(pkt[2], pkt2_check);
} }
} }
+12
View File
@@ -13,6 +13,10 @@ 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),
} }
impl fmt::Display for AnimeError { impl fmt::Display for AnimeError {
@@ -30,6 +34,14 @@ 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::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
),
} }
} }
} }
+19 -12
View File
@@ -2,7 +2,7 @@ use glam::Vec2;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
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, AnimeDataBuffer, AnimeDiagonal, AnimeImage, AnimeType, Pixel};
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AnimeFrame { pub struct AnimeFrame {
@@ -88,12 +88,13 @@ pub struct AnimeGif(Vec<AnimeFrame>, AnimTime);
impl AnimeGif { impl AnimeGif {
/// Create an animation using the 74x36 ASUS gif format /// Create an animation using the 74x36 ASUS gif format
#[inline] #[inline]
pub fn create_diagonal_gif( pub fn from_diagonal_gif(
file_name: &Path, file_name: &Path,
duration: AnimTime, duration: AnimTime,
brightness: f32, brightness: f32,
anime_type: AnimeType,
) -> Result<Self, AnimeError> { ) -> Result<Self, AnimeError> {
let mut matrix = AnimeDiagonal::new(None); 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.
@@ -121,7 +122,7 @@ impl AnimeGif {
} }
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),
}); });
} }
@@ -130,12 +131,13 @@ impl AnimeGif {
/// Create an animation using the 74x36 ASUS gif format from a png /// Create an animation using the 74x36 ASUS gif format from a png
#[inline] #[inline]
pub fn create_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, AnimeError> {
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 +150,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];
@@ -159,13 +161,14 @@ impl AnimeGif {
/// Create an animation using a gif of any size. This method must precompute the /// Create an animation using a gif of any size. This method must precompute the
/// result. /// result.
#[inline] #[inline]
pub fn create_png_gif( pub fn from_gif(
file_name: &Path, file_name: &Path,
scale: f32, scale: f32,
angle: f32, angle: f32,
translation: Vec2, translation: Vec2,
duration: AnimTime, duration: AnimTime,
brightness: f32, brightness: f32,
anime_type: AnimeType,
) -> Result<Self, AnimeError> { ) -> Result<Self, AnimeError> {
let mut frames = Vec::new(); let mut frames = Vec::new();
@@ -187,7 +190,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 +205,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() {
@@ -231,15 +236,17 @@ impl AnimeGif {
/// will be 1 second long. If `AnimTime::Cycles` is specified for `duration` then this can /// will be 1 second long. If `AnimTime::Cycles` is specified for `duration` then this can
/// be considered how many seconds the image will show for. /// be considered how many seconds the image will show for.
#[inline] #[inline]
pub fn create_png_static( pub fn from_png(
file_name: &Path, file_name: &Path,
scale: f32, scale: f32,
angle: f32, angle: f32,
translation: Vec2, translation: Vec2,
duration: AnimTime, duration: AnimTime,
brightness: f32, brightness: f32,
anime_type: AnimeType,
) -> Result<Self, AnimeError> { ) -> Result<Self, AnimeError> {
let image = AnimeImage::from_png(file_name, scale, angle, translation, brightness)?; 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 {
+24 -23
View File
@@ -1,8 +1,7 @@
use std::time::Duration; use crate::data::AnimeDataBuffer;
use crate::{AnimeImage, AnimeType};
use crate::data::{AnimeDataBuffer, ANIME_DATA_LEN};
use crate::image::LED_IMAGE_POSITIONS;
// 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 +13,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;
} }
@@ -94,16 +92,19 @@ impl From<AnimeGrid> for AnimeDataBuffer {
/// packets suitable for sending over USB /// packets suitable for sending over USB
#[inline] #[inline]
fn from(anime: AnimeGrid) -> Self { fn from(anime: AnimeGrid) -> Self {
let mut buf = vec![0u8; ANIME_DATA_LEN]; let mut buf = vec![0u8; anime.anime_type.data_length()];
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 +114,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() {
@@ -171,6 +172,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);
} }
} }
+413 -1354
View File
File diff suppressed because it is too large Load Diff
+41 -18
View File
@@ -3,7 +3,9 @@ use std::{path::PathBuf, time::Duration};
use glam::Vec2; use glam::Vec2;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use crate::{error::AnimeError, AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage}; use crate::{
error::AnimeError, 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,26 +65,37 @@ 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, AnimeError> {
let a = match action { let a = match action {
ActionLoader::AsusAnimation { ActionLoader::AsusAnimation {
file, file,
time, time,
brightness, brightness,
} => ActionData::Animation(AnimeGif::create_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(
ActionData::Animation(AnimeGif::create_diagonal_png(file, *time, *brightness)?) file,
} anime_type,
*time,
*brightness,
)?),
}, },
ActionLoader::ImageAnimation { ActionLoader::ImageAnimation {
file, file,
@@ -94,23 +107,25 @@ impl ActionData {
} => { } => {
if let Some(ext) = file.extension() { if let Some(ext) = file.extension() {
if ext.to_string_lossy().to_lowercase() == "png" { if ext.to_string_lossy().to_lowercase() == "png" {
return Ok(ActionData::Animation(AnimeGif::create_png_static( return Ok(ActionData::Animation(AnimeGif::from_png(
file, file,
*scale, *scale,
*angle, *angle,
*translation, *translation,
*time, *time,
*brightness, *brightness,
anime_type,
)?)); )?));
} }
} }
ActionData::Animation(AnimeGif::create_png_gif( ActionData::Animation(AnimeGif::from_gif(
file, file,
*scale, *scale,
*angle, *angle,
*translation, *translation,
*time, *time,
*brightness, *brightness,
anime_type,
)?) )?)
} }
ActionLoader::Image { ActionLoader::Image {
@@ -124,18 +139,25 @@ 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,
*scale,
*angle,
*translation,
*brightness,
anime_type,
)?;
let data = <AnimeDataBuffer>::from(&image); let data = <AnimeDataBuffer>::from(&image);
ActionData::Image(Box::new(data)) ActionData::Image(Box::new(data))
} }
_ => ActionData::Animation(AnimeGif::create_png_static( _ => ActionData::Animation(AnimeGif::from_png(
file, file,
*scale, *scale,
*angle, *angle,
*translation, *translation,
*time, *time,
*brightness, *brightness,
anime_type,
)?), )?),
} }
} }
@@ -146,20 +168,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<(), AnimeError> {
self.0.insert(index, ActionData::from_anime_action(action)?); self.0
.insert(index, ActionData::from_anime_action(self.1, action)?);
Ok(()) Ok(())
} }
+43
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,47 @@ 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("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

+3 -4
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "rog_aura" name = "rog_aura"
version = "1.1.0" version = "1.1.1"
license = "MPL-2.0" license = "MPL-2.0"
readme = "README.md" readme = "README.md"
authors = ["Luke <luke@ljones.dev>"] authors = ["Luke <luke@ljones.dev>"]
@@ -14,11 +14,10 @@ exclude = ["data"]
[features] [features]
default = ["dbus"] default = ["dbus"]
dbus = ["zvariant", "zvariant_derive"] dbus = ["zvariant"]
[dependencies] [dependencies]
serde = "^1.0" serde = "^1.0"
serde_derive = "^1.0" serde_derive = "^1.0"
zvariant = { version = "^2.6", optional = true } zvariant = { version = "^3.0", optional = true }
zvariant_derive = { version = "^2.6", optional = true }
+177 -23
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 {
@@ -145,18 +138,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 +180,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 +220,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)]
@@ -299,3 +356,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]);
}
}
+178 -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,179 @@ pub const fn aura_brightness_bytes(brightness: u8) -> [u8; 17] {
] ]
} }
pub const LED_AWAKE_ON_SLEEP_OFF: [u8; 17] = [ /// Enable/disable LED control in various states such as
0x5d, 0xbd, 0x01, 0xcf, 0x17, 0x0b, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /// when the device is awake, suspended, shutting down or
]; /// booting.
///
/// ```text
// 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 |
pub const LED_AWAKE_ON_SLEEP_ON: [u8; 17] = [ // byte 5 = lightbar
0x5d, 0xbd, 0x01, 0xff, 0x1f, 0x0f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // | 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 AuraControl {
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,
}
pub const LED_AWAKE_OFF_SLEEP_OFF: [u8; 17] = [ impl From<AuraControl> for u16 {
0x5d, 0xbd, 0x01, 0xc3, 0x13, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, fn from(a: AuraControl) -> Self {
]; a as u16
}
}
pub const LED_AWAKE_OFF_SLEEP_ON: [u8; 17] = [ impl AuraControl {
0x5d, 0xbd, 0x01, 0xf3, 0x1b, 0x0d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pub fn to_bytes(control: &[Self]) -> [u8; 2] {
]; let mut a: u16 = 0;
control.iter().for_each(|n| {
a |= *n as u16;
});
[(a & 0xff) as u8, ((a & 0xff00) >> 8) as u8]
}
}
impl BitOr<AuraControl> for AuraControl {
type Output = u16;
fn bitor(self, rhs: AuraControl) -> Self::Output {
return self as u16 | rhs as u16;
}
}
impl BitAnd<AuraControl> for AuraControl {
type Output = u16;
fn bitand(self, rhs: AuraControl) -> Self::Output {
return self as u16 & rhs as u16;
}
}
#[cfg(test)]
mod tests {
use crate::usb::AuraControl;
#[test]
fn check_led_control_bytes() {
// All on
let byte1 = [
AuraControl::BootLogo,
AuraControl::BootKeyb,
AuraControl::SleepLogo,
AuraControl::SleepKeyb,
AuraControl::AwakeLogo,
AuraControl::AwakeKeyb,
AuraControl::ShutdownLogo,
AuraControl::ShutdownKeyb,
];
let bytes = AuraControl::to_bytes(&byte1);
println!("{:08b}", bytes[0]);
assert_eq!(bytes[0], 0xff);
//
let byte1 = [
// AuraControl::BootLogo,
AuraControl::BootKeyb,
AuraControl::SleepLogo,
AuraControl::SleepKeyb,
AuraControl::AwakeLogo,
AuraControl::AwakeKeyb,
AuraControl::ShutdownLogo,
AuraControl::ShutdownKeyb,
];
let bytes = AuraControl::to_bytes(&byte1);
println!("{:08b}", bytes[0]);
assert_eq!(bytes[0], 0xfe);
let byte1 = [
AuraControl::BootLogo,
// AuraControl::BootKeyb,
AuraControl::SleepLogo,
AuraControl::SleepKeyb,
AuraControl::AwakeLogo,
AuraControl::AwakeKeyb,
AuraControl::ShutdownLogo,
AuraControl::ShutdownKeyb,
];
let bytes = AuraControl::to_bytes(&byte1);
println!("{:08b}", bytes[0]);
assert_eq!(bytes[0], 0xfd);
let byte1 = [
AuraControl::BootLogo,
AuraControl::BootKeyb,
// AuraControl::SleepLogo,
AuraControl::SleepKeyb,
AuraControl::AwakeLogo,
AuraControl::AwakeKeyb,
AuraControl::ShutdownLogo,
AuraControl::ShutdownKeyb,
];
let bytes = AuraControl::to_bytes(&byte1);
println!("{:08b}", bytes[0]);
assert_eq!(bytes[0], 0xef);
let byte1 = [
AuraControl::BootLogo,
AuraControl::BootKeyb,
AuraControl::SleepLogo,
// AuraControl::SleepKeyb,
AuraControl::AwakeLogo,
AuraControl::AwakeKeyb,
AuraControl::ShutdownLogo,
AuraControl::ShutdownKeyb,
];
let bytes = AuraControl::to_bytes(&byte1);
println!("{:08b}", bytes[0]);
assert_eq!(bytes[0], 0xdf);
let byte2 = [
AuraControl::AwakeBar,
AuraControl::BootBar,
AuraControl::SleepBar,
AuraControl::ShutdownBar,
];
let bytes = AuraControl::to_bytes(&byte2);
println!("{:08b}", bytes[1]);
assert_eq!(bytes[1], 0x1e);
let byte2 = [
AuraControl::AwakeBar,
AuraControl::BootBar,
// AuraControl::SleepBar,
AuraControl::ShutdownBar,
];
let bytes = AuraControl::to_bytes(&byte2);
println!("{:08b}", bytes[1]);
assert_eq!(bytes[1], 0x16);
}
}
+4 -4
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "rog_dbus" name = "rog_dbus"
version = "4.0.2" version = "4.2.0"
license = "MPL-2.0" license = "MPL-2.0"
readme = "README.md" readme = "README.md"
authors = ["Luke <luke@ljones.dev>"] authors = ["Luke <luke@ljones.dev>"]
@@ -14,6 +14,6 @@ rog_anime = { path = "../rog-anime" }
rog_aura = { path = "../rog-aura" } rog_aura = { path = "../rog-aura" }
rog_profiles = { path = "../rog-profiles" } rog_profiles = { path = "../rog-profiles" }
rog_supported = { path = "../rog-supported" } rog_supported = { path = "../rog-supported" }
zbus = "^1.9" zbus = "^2.2"
zbus_macros = "^1.9" zbus_macros = "^2.0"
zvariant = "^2.8" zvariant = "^3.0"
+87 -101
View File
@@ -9,51 +9,109 @@ pub mod zbus_profile;
pub mod zbus_rogbios; pub mod zbus_rogbios;
pub mod zbus_supported; pub mod zbus_supported;
use rog_anime::AnimePowerStates; // use rog_anime::AnimePowerStates;
use rog_aura::{AuraEffect, LedPowerStates}; // use rog_aura::{AuraEffect, LedPowerStates};
use rog_profiles::Profile; // use rog_profiles::Profile;
use std::sync::mpsc::{channel, Receiver}; // use std::sync::mpsc::{channel, Receiver};
use zbus::{Connection, Result, SignalReceiver}; use zbus::{blocking, Connection, Result};
pub static VERSION: &str = env!("CARGO_PKG_VERSION"); pub static VERSION: &str = env!("CARGO_PKG_VERSION");
pub struct DbusProxiesBlocking<'a> {
anime: zbus_anime::AnimeProxyBlocking<'a>,
charge: zbus_charge::ChargeProxyBlocking<'a>,
led: zbus_led::LedProxyBlocking<'a>,
profile: zbus_profile::ProfileProxyBlocking<'a>,
rog_bios: zbus_rogbios::RogBiosProxyBlocking<'a>,
supported: zbus_supported::SupportedProxyBlocking<'a>,
}
impl<'a> DbusProxiesBlocking<'a> {
#[inline]
pub fn new() -> Result<(Self, blocking::Connection)> {
let conn = blocking::Connection::system()?;
Ok((
DbusProxiesBlocking {
anime: zbus_anime::AnimeProxyBlocking::new(&conn)?,
led: zbus_led::LedProxyBlocking::new(&conn)?,
charge: zbus_charge::ChargeProxyBlocking::new(&conn)?,
profile: zbus_profile::ProfileProxyBlocking::new(&conn)?,
rog_bios: zbus_rogbios::RogBiosProxyBlocking::new(&conn)?,
supported: zbus_supported::SupportedProxyBlocking::new(&conn)?,
},
conn,
))
}
pub fn anime(&self) -> &zbus_anime::AnimeProxyBlocking<'a> {
&self.anime
}
pub fn charge(&self) -> &zbus_charge::ChargeProxyBlocking<'a> {
&self.charge
}
pub fn led(&self) -> &zbus_led::LedProxyBlocking<'a> {
&self.led
}
pub fn profile(&self) -> &zbus_profile::ProfileProxyBlocking<'a> {
&self.profile
}
pub fn rog_bios(&self) -> &zbus_rogbios::RogBiosProxyBlocking<'a> {
&self.rog_bios
}
pub fn supported(&self) -> &zbus_supported::SupportedProxyBlocking<'a> {
&self.supported
}
}
/// This is the main way to communicate with the DBUS interface
pub struct RogDbusClientBlocking<'a> {
proxies: DbusProxiesBlocking<'a>,
}
impl<'a> RogDbusClientBlocking<'a> {
#[inline]
pub fn new() -> Result<(Self, blocking::Connection)> {
let (proxies, conn) = DbusProxiesBlocking::new()?;
Ok((RogDbusClientBlocking { proxies }, conn))
}
pub fn proxies(&self) -> &DbusProxiesBlocking {
&self.proxies
}
}
pub struct DbusProxies<'a> { pub struct DbusProxies<'a> {
anime: zbus_anime::AnimeProxy<'a>, anime: zbus_anime::AnimeProxy<'a>,
charge: zbus_charge::ChargeProxy<'a>, charge: zbus_charge::ChargeProxy<'a>,
led: zbus_led::LedProxy<'a>, led: zbus_led::LedProxy<'a>,
profile: zbus_profile::ProfileProxy<'a>, profile: zbus_profile::ProfileProxy<'a>,
rog_bios: zbus_rogbios::RogBiosProxy<'a>, rog_bios: zbus_rogbios::RogBiosProxy<'a>,
supported: zbus_supported::SupportProxy<'a>, supported: zbus_supported::SupportedProxy<'a>,
} }
impl<'a> DbusProxies<'a> { impl<'a> DbusProxies<'a> {
#[inline] #[inline]
pub fn new() -> Result<(Self, Connection)> { pub async fn new() -> Result<(DbusProxies<'a>, Connection)> {
let conn = Connection::new_system()?; let conn = Connection::system().await?;
Ok(( Ok((
DbusProxies { DbusProxies {
anime: zbus_anime::AnimeProxy::new(&conn)?, anime: zbus_anime::AnimeProxy::new(&conn).await?,
led: zbus_led::LedProxy::new(&conn)?, led: zbus_led::LedProxy::new(&conn).await?,
charge: zbus_charge::ChargeProxy::new(&conn)?, charge: zbus_charge::ChargeProxy::new(&conn).await?,
profile: zbus_profile::ProfileProxy::new(&conn)?, profile: zbus_profile::ProfileProxy::new(&conn).await?,
rog_bios: zbus_rogbios::RogBiosProxy::new(&conn)?, rog_bios: zbus_rogbios::RogBiosProxy::new(&conn).await?,
supported: zbus_supported::SupportProxy::new(&conn)?, supported: zbus_supported::SupportedProxy::new(&conn).await?,
}, },
conn, conn,
)) ))
} }
pub fn setup_recv(&'a self, conn: Connection) -> SignalReceiver<'a, 'a> {
let mut recv = SignalReceiver::new(conn);
recv.receive_for(self.anime.proxy());
recv.receive_for(self.led.proxy());
recv.receive_for(self.charge.proxy());
recv.receive_for(self.profile.proxy());
recv.receive_for(self.rog_bios.proxy());
recv.receive_for(self.supported.proxy());
recv
}
pub fn anime(&self) -> &zbus_anime::AnimeProxy<'a> { pub fn anime(&self) -> &zbus_anime::AnimeProxy<'a> {
&self.anime &self.anime
} }
@@ -74,96 +132,24 @@ impl<'a> DbusProxies<'a> {
&self.rog_bios &self.rog_bios
} }
pub fn supported(&self) -> &zbus_supported::SupportProxy<'a> { pub fn supported(&self) -> &zbus_supported::SupportedProxy<'a> {
&self.supported &self.supported
} }
} }
// Signals separated out
pub struct Signals {
pub profile: Receiver<Profile>,
pub led_mode: Receiver<AuraEffect>,
pub led_power_state: Receiver<LedPowerStates>,
pub anime_power_state: Receiver<AnimePowerStates>,
pub charge: Receiver<u8>,
pub bios_gsync: Receiver<bool>,
pub bios_sound: Receiver<bool>,
}
impl Signals {
#[inline]
pub fn new(proxies: &DbusProxies) -> Result<Self> {
Ok(Signals {
profile: {
let (tx, rx) = channel();
proxies.profile.connect_notify_profile(tx)?;
rx
},
charge: {
let (tx, rx) = channel();
proxies.charge.connect_notify_charge(tx)?;
rx
},
led_mode: {
let (tx, rx) = channel();
proxies.led.connect_notify_led(tx)?;
rx
},
led_power_state: {
let (tx, rx) = channel();
proxies.led.connect_notify_power_states(tx)?;
rx
},
anime_power_state: {
let (tx, rx) = channel();
proxies.anime.connect_notify_power_states(tx)?;
rx
},
bios_gsync: {
let (tx, rx) = channel();
proxies.rog_bios.connect_notify_dedicated_graphic_mode(tx)?;
rx
},
bios_sound: {
let (tx, rx) = channel();
proxies.rog_bios.connect_notify_post_boot_sound(tx)?;
rx
},
})
}
}
/// This is the main way to communicate with the DBUS interface /// This is the main way to communicate with the DBUS interface
pub struct RogDbusClient<'a> { pub struct RogDbusClient<'a> {
proxies: DbusProxies<'a>, proxies: DbusProxies<'a>,
signals: Signals,
} }
impl<'a> RogDbusClient<'a> { impl<'a> RogDbusClient<'a> {
#[inline] #[inline]
pub fn new() -> Result<(Self, Connection)> { pub async fn new() -> Result<(RogDbusClient<'a>, Connection)> {
let (proxies, conn) = DbusProxies::new()?; let (proxies, conn) = DbusProxies::new().await?;
let signals = Signals::new(&proxies)?; Ok((RogDbusClient { proxies }, conn))
Ok((RogDbusClient { proxies, signals }, conn))
} }
pub fn proxies(&self) -> &DbusProxies { pub fn proxies(&self) -> &DbusProxies {
&self.proxies &self.proxies
} }
pub fn signals(&self) -> &Signals {
&self.signals
}
pub fn setup_recv(&'a self, conn: Connection) -> SignalReceiver<'a, 'a> {
let mut recv = SignalReceiver::new(conn);
recv.receive_for(self.proxies.anime.proxy());
recv.receive_for(self.proxies.led.proxy());
recv.receive_for(self.proxies.charge.proxy());
recv.receive_for(self.proxies.profile.proxy());
recv.receive_for(self.proxies.rog_bios.proxy());
recv.receive_for(self.proxies.supported.proxy());
recv
}
} }
+4 -61
View File
@@ -1,12 +1,11 @@
use rog_anime::{AnimeDataBuffer, AnimePowerStates}; use rog_anime::{AnimeDataBuffer, AnimePowerStates};
use std::sync::mpsc::Sender; use zbus_macros::dbus_proxy;
use zbus::{dbus_proxy, Connection, Result};
#[dbus_proxy( #[dbus_proxy(
interface = "org.asuslinux.Daemon", interface = "org.asuslinux.Daemon",
default_path = "/org/asuslinux/Anime" default_path = "/org/asuslinux/Anime"
)] )]
trait Daemon { trait Anime {
/// 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, status: bool) -> zbus::Result<()>; fn set_boot_on_off(&self, status: bool) -> zbus::Result<()>;
@@ -17,7 +16,7 @@ trait Daemon {
fn set_on_off(&self, status: bool) -> zbus::Result<()>; fn set_on_off(&self, status: bool) -> zbus::Result<()>;
/// 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: &[u8]) -> zbus::Result<()>; fn write(&self, input: AnimeDataBuffer) -> zbus::Result<()>;
/// Get status of if the AniMe LEDs are on /// Get status of if the AniMe LEDs are on
#[dbus_proxy(property)] #[dbus_proxy(property)]
@@ -29,61 +28,5 @@ trait Daemon {
/// 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_proxy(signal)] #[dbus_proxy(signal)]
fn notify_power_states(&self, data: AnimePowerStates) -> zbus::Result<()>; fn power_states(&self, data: AnimePowerStates) -> zbus::Result<()>;
}
pub struct AnimeProxy<'a>(DaemonProxy<'a>);
impl<'a> AnimeProxy<'a> {
#[inline]
pub fn new(conn: &Connection) -> Result<Self> {
Ok(AnimeProxy(DaemonProxy::new(conn)?))
}
#[inline]
pub fn proxy(&self) -> &DaemonProxy<'a> {
&self.0
}
/// Set whether the AniMe will show boot, suspend, or off animations
#[inline]
pub fn set_system_animations(&self, on: bool) -> Result<()> {
self.0.set_boot_on_off(on)
}
/// Set whether the AniMe is displaying images/data
#[inline]
pub fn set_led_power(&self, on: bool) -> Result<()> {
self.0.set_on_off(on)
}
/// Writes a data stream of length. Will force system thread to exit until it is restarted
#[inline]
pub fn write(&self, input: AnimeDataBuffer) -> Result<()> {
self.0.write(input.get())
}
/// Get status of if the AniMe LEDs are on
#[inline]
pub fn awake_enabled(&self) -> Result<bool> {
self.0.awake_enabled()
}
/// Get the status of if factory system-status animations are enabled
#[inline]
pub fn boot_enabled(&self) -> Result<bool> {
self.0.boot_enabled()
}
#[inline]
pub fn connect_notify_power_states(
&self,
send: Sender<AnimePowerStates>,
) -> zbus::fdo::Result<()> {
self.0.connect_notify_power_states(move |data| {
send.send(data)
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
Ok(())
})
}
} }
+3 -38
View File
@@ -19,15 +19,13 @@
//! //!
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces. //! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
use std::sync::mpsc::Sender; use zbus_macros::dbus_proxy;
use zbus::{dbus_proxy, Connection, Result};
#[dbus_proxy( #[dbus_proxy(
interface = "org.asuslinux.Daemon", interface = "org.asuslinux.Daemon",
default_path = "/org/asuslinux/Charge" default_path = "/org/asuslinux/Charge"
)] )]
trait Daemon { trait Charge {
/// Limit method /// Limit method
fn limit(&self) -> zbus::Result<i16>; fn limit(&self) -> zbus::Result<i16>;
@@ -36,38 +34,5 @@ trait Daemon {
/// NotifyCharge signal /// NotifyCharge signal
#[dbus_proxy(signal)] #[dbus_proxy(signal)]
fn notify_charge(&self, limit: u8) -> zbus::Result<()>; fn notify_charge(&self, limit: u8) -> zbus::Result<u8>;
}
pub struct ChargeProxy<'a>(DaemonProxy<'a>);
impl<'a> ChargeProxy<'a> {
#[inline]
pub fn new(conn: &Connection) -> Result<Self> {
Ok(ChargeProxy(DaemonProxy::new(conn)?))
}
#[inline]
pub fn proxy(&self) -> &DaemonProxy<'a> {
&self.0
}
#[inline]
pub fn write_limit(&self, level: u8) -> Result<()> {
self.0.set_limit(level)
}
#[inline]
pub fn get_limit(&self) -> Result<i16> {
self.0.limit()
}
#[inline]
pub fn connect_notify_charge(&self, send: Sender<u8>) -> zbus::fdo::Result<()> {
self.0.connect_notify_charge(move |data| {
send.send(data)
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
Ok(())
})
}
} }
+12 -99
View File
@@ -19,11 +19,10 @@
//! //!
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces. //! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
use std::sync::mpsc::Sender; use zbus::{blocking::Connection, Result};
use zbus_macros::dbus_proxy;
use zbus::{dbus_proxy, Connection, Result}; use rog_aura::{usb::AuraControl, AuraEffect, KeyColourArray, LedBrightness};
use rog_aura::{AuraEffect, KeyColourArray, LedBrightness, LedPowerStates};
const BLOCKING_TIME: u64 = 40; // 100ms = 10 FPS, max 50ms = 20 FPS, 40ms = 25 FPS const BLOCKING_TIME: u64 = 40; // 100ms = 10 FPS, max 50ms = 20 FPS, 40ms = 25 FPS
@@ -31,7 +30,7 @@ const BLOCKING_TIME: u64 = 40; // 100ms = 10 FPS, max 50ms = 20 FPS, 40ms = 25 F
interface = "org.asuslinux.Daemon", interface = "org.asuslinux.Daemon",
default_path = "/org/asuslinux/Led" default_path = "/org/asuslinux/Led"
)] )]
trait Daemon { trait Led {
/// NextLedMode method /// NextLedMode method
fn next_led_mode(&self) -> zbus::Result<()>; fn next_led_mode(&self) -> zbus::Result<()>;
@@ -50,18 +49,16 @@ trait Daemon {
/// SetLedMode method /// SetLedMode method
fn set_led_mode(&self, effect: &AuraEffect) -> zbus::Result<()>; fn set_led_mode(&self, effect: &AuraEffect) -> zbus::Result<()>;
/// SetAwakeEnabled method fn set_leds_enabled(&self, enabled: Vec<AuraControl>) -> zbus::Result<()>;
fn set_awake_enabled(&self, enabled: bool) -> zbus::Result<()>;
/// SetSleepEnabled method fn set_leds_disabled(&self, disabled: Vec<AuraControl>) -> zbus::Result<()>;
fn set_sleep_enabled(&self, enabled: bool) -> zbus::Result<()>;
/// NotifyLed signal /// NotifyLed signal
#[dbus_proxy(signal)] #[dbus_proxy(signal)]
fn notify_led(&self, data: AuraEffect) -> zbus::Result<()>; fn notify_led(&self, data: AuraEffect) -> zbus::Result<()>;
#[dbus_proxy(signal)] #[dbus_proxy(signal)]
fn notify_power_states(&self, data: LedPowerStates) -> zbus::Result<()>; fn notify_power_states(&self, data: Vec<AuraControl>) -> zbus::Result<()>;
/// LedBrightness property /// LedBrightness property
#[dbus_proxy(property)] #[dbus_proxy(property)]
@@ -76,85 +73,22 @@ trait Daemon {
fn led_modes(&self) -> zbus::Result<String>; fn led_modes(&self) -> zbus::Result<String>;
#[dbus_proxy(property)] #[dbus_proxy(property)]
fn awake_enabled(&self) -> zbus::Result<bool>; fn leds_enabled(&self) -> zbus::Result<Vec<u8>>;
#[dbus_proxy(property)]
fn sleep_enabled(&self) -> zbus::Result<bool>;
} }
pub struct LedProxy<'a>(DaemonProxy<'a>); pub struct LedProxyPerkey<'a>(LedProxyBlocking<'a>);
impl<'a> LedProxy<'a> { impl<'a> LedProxyPerkey<'a> {
#[inline] #[inline]
pub fn new(conn: &Connection) -> Result<Self> { pub fn new(conn: &Connection) -> Result<Self> {
Ok(LedProxy(DaemonProxy::new(conn)?)) Ok(LedProxyPerkey(LedProxyBlocking::new(conn)?))
} }
#[inline] #[inline]
pub fn proxy(&self) -> &DaemonProxy<'a> { pub fn proxy(&self) -> &LedProxyBlocking<'a> {
&self.0 &self.0
} }
#[inline]
pub fn get_led_brightness(&self) -> Result<i16> {
self.0.led_brightness()
}
#[inline]
pub fn set_led_brightness(&self, level: LedBrightness) -> Result<()> {
self.0.set_brightness(level)?;
Ok(())
}
/// Set the keyboard LED to enabled while the device is awake
#[inline]
pub fn set_awake_enabled(&self, enabled: bool) -> Result<()> {
self.0.set_awake_enabled(enabled)?;
Ok(())
}
/// Set the keyboard LED suspend animation to enabled while the device is suspended
#[inline]
pub fn set_sleep_enabled(&self, enabled: bool) -> Result<()> {
self.0.set_sleep_enabled(enabled)?;
Ok(())
}
#[inline]
pub fn next_led_mode(&self) -> Result<()> {
self.0.next_led_mode()
}
#[inline]
pub fn prev_led_mode(&self) -> Result<()> {
self.0.prev_led_mode()
}
#[inline]
pub fn next_led_brightness(&self) -> Result<()> {
self.0.next_led_brightness()
}
#[inline]
pub fn prev_led_brightness(&self) -> Result<()> {
self.0.prev_led_brightness()
}
#[inline]
pub fn set_led_mode(&self, mode: &AuraEffect) -> Result<()> {
self.0.set_led_mode(mode)
}
#[inline]
pub fn awake_enabled(&self) -> Result<bool> {
self.0.awake_enabled()
}
#[inline]
pub fn sleep_enabled(&self) -> Result<bool> {
self.0.sleep_enabled()
}
/// Write a single colour block. /// Write a single colour block.
/// ///
/// Intentionally blocks for 10ms after sending to allow the block to /// Intentionally blocks for 10ms after sending to allow the block to
@@ -186,25 +120,4 @@ impl<'a> LedProxy<'a> {
// self.0.set_led_mode(&serde_json::to_string(&mode).unwrap()) // self.0.set_led_mode(&serde_json::to_string(&mode).unwrap())
Ok(()) Ok(())
} }
#[inline]
pub fn connect_notify_led(&self, send: Sender<AuraEffect>) -> zbus::fdo::Result<()> {
self.0.connect_notify_led(move |data| {
send.send(data)
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
Ok(())
})
}
#[inline]
pub fn connect_notify_power_states(
&self,
send: Sender<LedPowerStates>,
) -> zbus::fdo::Result<()> {
self.0.connect_notify_power_states(move |data| {
send.send(data)
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
Ok(())
})
}
} }
+3 -73
View File
@@ -19,19 +19,17 @@
//! //!
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces. //! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
use std::sync::mpsc::Sender;
use rog_profiles::{ use rog_profiles::{
fan_curve_set::{CurveData, FanCurveSet}, fan_curve_set::{CurveData, FanCurveSet},
Profile, Profile,
}; };
use zbus::{dbus_proxy, Connection, Result}; use zbus_macros::dbus_proxy;
#[dbus_proxy( #[dbus_proxy(
interface = "org.asuslinux.Daemon", interface = "org.asuslinux.Daemon",
default_path = "/org/asuslinux/Profile" default_path = "/org/asuslinux/Profile"
)] )]
trait Daemon { trait Profile {
/// Get the fan-curve data for the currently active Profile /// Get the fan-curve data for the currently active Profile
fn fan_curve_data(&self, profile: Profile) -> zbus::Result<FanCurveSet>; fn fan_curve_data(&self, profile: Profile) -> zbus::Result<FanCurveSet>;
@@ -66,73 +64,5 @@ trait Daemon {
/// NotifyProfile signal /// NotifyProfile signal
#[dbus_proxy(signal)] #[dbus_proxy(signal)]
fn notify_profile(&self, profile: Profile) -> zbus::Result<()>; fn notify_profile(&self, profile: Profile) -> zbus::Result<Profile>;
}
pub struct ProfileProxy<'a>(DaemonProxy<'a>);
impl<'a> ProfileProxy<'a> {
#[inline]
pub fn new(conn: &Connection) -> Result<Self> {
Ok(ProfileProxy(DaemonProxy::new(conn)?))
}
#[inline]
pub fn proxy(&self) -> &DaemonProxy<'a> {
&self.0
}
#[inline]
pub fn active_profile(&self) -> zbus::Result<Profile> {
self.0.active_profile()
}
#[inline]
pub fn enabled_fan_profiles(&self) -> zbus::Result<Vec<Profile>> {
self.0.enabled_fan_profiles()
}
#[inline]
pub fn fan_curve_data(&self, profile: Profile) -> zbus::Result<FanCurveSet> {
self.0.fan_curve_data(profile)
}
#[inline]
pub fn next_profile(&self) -> Result<()> {
self.0.next_profile()
}
#[inline]
pub fn profiles(&self) -> Result<Vec<Profile>> {
self.0.profiles()
}
#[inline]
pub fn set_active_profile(&self, profile: Profile) -> zbus::Result<()> {
self.0.set_active_profile(profile)
}
#[inline]
pub fn set_fan_curve_enabled(&self, profile: Profile, enabled: bool) -> zbus::Result<()> {
self.0.set_fan_curve_enabled(profile, enabled)
}
#[inline]
pub fn set_fan_curve(&self, curve: CurveData, profile: Profile) -> zbus::Result<()> {
self.0.set_fan_curve(profile, curve)
}
#[inline]
pub fn set_active_curve_to_defaults(&self) -> zbus::Result<()> {
self.0.set_active_curve_to_defaults()
}
#[inline]
pub fn connect_notify_profile(&self, send: Sender<Profile>) -> zbus::fdo::Result<()> {
self.0.connect_notify_profile(move |data| {
send.send(data)
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
Ok(())
})
}
} }
+3 -60
View File
@@ -19,15 +19,13 @@
//! //!
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces. //! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
use std::sync::mpsc::Sender; use zbus_macros::dbus_proxy;
use zbus::{dbus_proxy, Connection, Result};
#[dbus_proxy( #[dbus_proxy(
interface = "org.asuslinux.Daemon", interface = "org.asuslinux.Daemon",
default_path = "/org/asuslinux/RogBios" default_path = "/org/asuslinux/RogBios"
)] )]
trait Daemon { trait RogBios {
/// DedicatedGraphicMode method /// DedicatedGraphicMode method
fn dedicated_graphic_mode(&self) -> zbus::Result<i16>; fn dedicated_graphic_mode(&self) -> zbus::Result<i16>;
@@ -46,60 +44,5 @@ trait Daemon {
/// NotifyPostBootSound signal /// NotifyPostBootSound signal
#[dbus_proxy(signal)] #[dbus_proxy(signal)]
fn notify_post_boot_sound(&self, dedicated: bool) -> zbus::Result<()>; fn notify_post_boot_sound(&self, sound: bool) -> zbus::Result<()>;
}
pub struct RogBiosProxy<'a>(DaemonProxy<'a>);
impl<'a> RogBiosProxy<'a> {
#[inline]
pub fn new(conn: &Connection) -> Result<Self> {
Ok(RogBiosProxy(DaemonProxy::new(conn)?))
}
#[inline]
pub fn proxy(&self) -> &DaemonProxy<'a> {
&self.0
}
#[inline]
pub fn get_dedicated_gfx(&self) -> Result<i16> {
self.0.dedicated_graphic_mode()
}
#[inline]
pub fn set_dedicated_gfx(&self, on: bool) -> Result<()> {
self.0.set_dedicated_graphic_mode(on)
}
#[inline]
pub fn get_post_sound(&self) -> Result<i16> {
self.0.post_boot_sound()
}
#[inline]
pub fn set_post_sound(&self, on: bool) -> Result<()> {
self.0.set_post_boot_sound(on)
}
#[inline]
pub fn connect_notify_dedicated_graphic_mode(
&self,
send: Sender<bool>,
) -> zbus::fdo::Result<()> {
self.0.connect_notify_dedicated_graphic_mode(move |data| {
send.send(data)
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
Ok(())
})
}
#[inline]
pub fn connect_notify_post_boot_sound(&self, send: Sender<bool>) -> zbus::fdo::Result<()> {
self.0.connect_notify_post_boot_sound(move |data| {
send.send(data)
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
Ok(())
})
}
} }
+2 -21
View File
@@ -20,32 +20,13 @@
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces. //! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
use rog_supported::SupportedFunctions; use rog_supported::SupportedFunctions;
use zbus::{dbus_proxy, Connection, Result}; use zbus_macros::dbus_proxy;
#[dbus_proxy( #[dbus_proxy(
interface = "org.asuslinux.Daemon", interface = "org.asuslinux.Daemon",
default_path = "/org/asuslinux/Supported" default_path = "/org/asuslinux/Supported"
)] )]
trait Daemon { trait Supported {
/// SupportedFunctions method /// SupportedFunctions method
fn supported_functions(&self) -> zbus::Result<SupportedFunctions>; fn supported_functions(&self) -> zbus::Result<SupportedFunctions>;
} }
pub struct SupportProxy<'a>(DaemonProxy<'a>);
impl<'a> SupportProxy<'a> {
#[inline]
pub fn new(conn: &Connection) -> Result<Self> {
Ok(SupportProxy(DaemonProxy::new(conn)?))
}
#[inline]
pub fn proxy(&self) -> &DaemonProxy<'a> {
&self.0
}
#[inline]
pub fn get_supported_functions(&self) -> Result<SupportedFunctions> {
self.0.supported_functions()
}
}
+3 -3
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "rog_profiles" name = "rog_profiles"
version = "1.1.2" version = "1.1.3"
authors = ["Luke D. Jones <luke@ljones.dev>"] authors = ["Luke D. Jones <luke@ljones.dev>"]
edition = "2018" edition = "2018"
@@ -13,5 +13,5 @@ udev = "^0.6"
serde = "^1.0" serde = "^1.0"
serde_derive = "^1.0" serde_derive = "^1.0"
zvariant = { version = "^2.6", optional = true } zvariant = { version = "^3.0", optional = true }
zvariant_derive = { version = "^2.6", optional = true } zvariant_derive = { version = "^3.0", optional = true }
+4 -1
View File
@@ -12,6 +12,7 @@ pub enum ProfileError {
ParseFanCurveDigit(std::num::ParseIntError), ParseFanCurveDigit(std::num::ParseIntError),
/// (pwm/temp, prev, next) /// (pwm/temp, prev, next)
ParseFanCurvePrevHigher(&'static str, u8, u8), ParseFanCurvePrevHigher(&'static str, u8, u8),
ParseFanCurvePercentOver100(u8),
// Zbus(zbus::Error), // Zbus(zbus::Error),
} }
@@ -34,7 +35,9 @@ impl fmt::Display for ProfileError {
"Invalid {}, previous value {} is higher than next value {}", "Invalid {}, previous value {} is higher than next value {}",
part, prev, next part, prev, next
), ),
// Error::Zbus(detail) => write!(f, "Zbus error: {}", detail), ProfileError::ParseFanCurvePercentOver100(value) => {
write!(f, "Invalid percentage, {} is higher than 100", value)
} // Error::Zbus(detail) => write!(f, "Zbus error: {}", detail),
} }
} }
} }
+22 -5
View File
@@ -37,14 +37,24 @@ pub struct CurveData {
impl std::str::FromStr for CurveData { impl std::str::FromStr for CurveData {
type Err = ProfileError; type Err = ProfileError;
/// Parse a string to the correct values that the fan curve kernel driver expects
///
/// If the fan curve is given with percentage char '%' then the fan power values are converted
/// otherwise the expected fan power range is 0-255.
///
/// Temperature range is 0-255 in degrees C. You don't want to be setting over 100.
fn from_str(input: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let mut temp = [0u8; 8]; let mut temp = [0u8; 8];
let mut pwm = [0u8; 8]; let mut pwm = [0u8; 8];
let mut temp_prev = 0; let mut temp_prev = 0;
let mut pwm_prev = 0; let mut pwm_prev = 0;
let mut percentages = false;
for (index, value) in input.split(',').enumerate() { for (index, value) in input.split(',').enumerate() {
for (select, num) in value.splitn(2, |c| c == 'c' || c == ':').enumerate() { for (select, num) in value.splitn(2, |c| c == 'c' || c == ':').enumerate() {
if num.contains('%') {
percentages = true;
}
let r = num.trim_matches(|c| c == 'c' || c == ':' || c == '%'); let r = num.trim_matches(|c| c == 'c' || c == ':' || c == '%');
let r = r.parse::<u8>().map_err(ProfileError::ParseFanCurveDigit)?; let r = r.parse::<u8>().map_err(ProfileError::ParseFanCurveDigit)?;
@@ -59,15 +69,22 @@ impl std::str::FromStr for CurveData {
temp_prev = r; temp_prev = r;
temp[index] = r; temp[index] = r;
} else { } else {
if pwm_prev > r { let mut p = r;
if percentages {
if r > 100 {
return Err(ProfileError::ParseFanCurvePercentOver100(r));
}
p = (p as f32 * 2.55).round() as u8;
}
if pwm_prev > p {
return Err(ProfileError::ParseFanCurvePrevHigher( return Err(ProfileError::ParseFanCurvePrevHigher(
"percentage", "percentage",
pwm_prev, pwm_prev,
r, p,
)); ));
} }
pwm_prev = r; pwm_prev = p;
pwm[index] = r; pwm[index] = p;
} }
} }
} }
@@ -205,7 +222,7 @@ mod tests {
.unwrap(); .unwrap();
assert_eq!(curve.fan, FanCurvePU::CPU); assert_eq!(curve.fan, FanCurvePU::CPU);
assert_eq!(curve.temp, [30, 49, 59, 69, 79, 89, 99, 109]); assert_eq!(curve.temp, [30, 49, 59, 69, 79, 89, 99, 109]);
assert_eq!(curve.pwm, [1, 2, 3, 4, 31, 49, 56, 58]); assert_eq!(curve.pwm, [3, 5, 8, 10, 79, 125, 143, 148]);
} }
#[test] #[test]
+2 -2
View File
@@ -13,5 +13,5 @@ edition = "2018"
rog_aura = { path = "../rog-aura" } rog_aura = { path = "../rog-aura" }
serde = "^1.0" serde = "^1.0"
serde_derive = "^1.0" serde_derive = "^1.0"
zvariant = "^2.6" zvariant = "^3.0"
zvariant_derive = "^2.6" zvariant_derive = "^3.0"
+3 -3
View File
@@ -1,6 +1,6 @@
pub static VERSION: &str = env!("CARGO_PKG_VERSION"); pub static VERSION: &str = env!("CARGO_PKG_VERSION");
use rog_aura::AuraModeNum; use rog_aura::{AuraModeNum, AuraZone};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::fmt; use std::fmt;
use zvariant_derive::Type; use zvariant_derive::Type;
@@ -32,7 +32,7 @@ pub struct PlatformProfileFunctions {
pub struct LedSupportedFunctions { pub struct LedSupportedFunctions {
pub brightness_set: bool, pub brightness_set: bool,
pub stock_led_modes: Vec<AuraModeNum>, pub stock_led_modes: Vec<AuraModeNum>,
pub multizone_led_mode: bool, pub multizone_led_mode: Vec<AuraZone>,
pub per_key_led_mode: bool, pub per_key_led_mode: bool,
} }
@@ -80,7 +80,7 @@ impl fmt::Display for LedSupportedFunctions {
writeln!(f, "LED:")?; writeln!(f, "LED:")?;
writeln!(f, "\tBrightness control: {}", self.brightness_set)?; writeln!(f, "\tBrightness control: {}", self.brightness_set)?;
writeln!(f, "\tStock LED modes: {:?}", self.stock_led_modes)?; writeln!(f, "\tStock LED modes: {:?}", self.stock_led_modes)?;
writeln!(f, "\tMultizone LED mode: {}", self.multizone_led_mode)?; writeln!(f, "\tMultizone LED mode: {:?}", self.multizone_led_mode)?;
writeln!(f, "\tPer key LED mode: {}", self.per_key_led_mode) writeln!(f, "\tPer key LED mode: {}", self.per_key_led_mode)
} }
} }
@@ -1,5 +1,5 @@
use rog_aura::{GX502Layout, Key, KeyColourArray, KeyLayout}; use rog_aura::{GX502Layout, Key, KeyColourArray, KeyLayout};
use rog_dbus::RogDbusClient; use rog_dbus::RogDbusClientBlocking;
use std::collections::LinkedList; use std::collections::LinkedList;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -52,7 +52,7 @@ impl Ball {
} }
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let (dbus, _) = RogDbusClient::new()?; let (dbus, _) = RogDbusClientBlocking::new()?;
let mut colours = KeyColourArray::new(); let mut colours = KeyColourArray::new();
@@ -60,8 +60,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut balls = [Ball::new(2, 1, 12), Ball::new(4, 6, 12)]; let mut balls = [Ball::new(2, 1, 12), Ball::new(4, 6, 12)];
dbus.proxies().led().init_effect()?;
let rows = layout.get_rows(); let rows = layout.get_rows();
loop { loop {
for (n, ball) in balls.iter_mut().enumerate() { for (n, ball) in balls.iter_mut().enumerate() {