Compare commits
200 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 773c9902a5 | |||
| e05d5bd143 | |||
| 3e244d7d3d | |||
| ba4589f986 | |||
| 083134fc73 | |||
| 3cc04fba60 | |||
| 3a00e4f1a3 | |||
| eb78fb613c | |||
| d0b9aee85a | |||
| 3e94ef05fb | |||
| fbb025875b | |||
| ae816bd13c | |||
| 14f0693511 | |||
| de7fb4a942 | |||
| 4164b4645d | |||
| 649b14fd0d | |||
| 6d97ef13a1 | |||
| 7abad979c8 | |||
| 0ec1574219 | |||
| 03042dd5c3 | |||
| 3330e4973f | |||
| 5e06aeabe9 | |||
| e99d8766fc | |||
| 8f65b7e334 | |||
| 5a54b830bf | |||
| 85e08510f7 | |||
| d56eeb7fb2 | |||
| bbc520a7f2 | |||
| 10e43c64ca | |||
| 38be25174a | |||
| 2584d69930 | |||
| 523f39cf9c | |||
| 669760223e | |||
| 71ec13fa9f | |||
| 409528b286 | |||
| 17df3cf01d | |||
| 808a1d2470 | |||
| 840c500b5e | |||
| 42dc360d16 | |||
| f6183597c9 | |||
| 79a45c4f10 | |||
| 19370215c0 | |||
| 030dd661b8 | |||
| 1fc12d9855 | |||
| 6c1b2b70ea | |||
| 23f9af35bf | |||
| 526626b80c | |||
| 10eaaac54b | |||
| 901a3ddcc9 | |||
| e6ebf72a11 | |||
| cd7e748c88 | |||
| a313359ef6 | |||
| f222eef6b7 | |||
| e6f3aeb851 | |||
| 22605e57cc | |||
| 02fb7addf4 | |||
| bdbb403a0e | |||
| 7a8bede92f | |||
| a71a40b509 | |||
| 42fc5a5392 | |||
| 5017a0ea9b | |||
| 05f7b0060f | |||
| 1043da5328 | |||
| 959ad35afa | |||
| 1e10255d01 | |||
| c72693bc53 | |||
| 2297aad5e5 | |||
| f39c0db680 | |||
| 23353c77f3 | |||
| fee1486db6 | |||
| 39c4253b24 | |||
| 84e924f46a | |||
| 43364398b4 | |||
| 3f09f221f4 | |||
| 4ed922154b | |||
| c0e36295b7 | |||
| ef04549c8e | |||
| a117c300d5 | |||
| 6956f7dffc | |||
| fe49913550 | |||
| 89ddade2a3 | |||
| bcb3d53ade | |||
| 9ab9028d79 | |||
| 8de6447562 | |||
| 2512cea19d | |||
| eabe78af71 | |||
| 16c4ee609e | |||
| 37d586c5de | |||
| e66847c263 | |||
| a2c8a226a4 | |||
| f3f6fadfe2 | |||
| ff76c356c5 | |||
| a22808aff3 | |||
| f39fd6dfbb | |||
| 95598f2a76 | |||
| 535e104ccc | |||
| 522cee42e5 | |||
| 51656dc13f | |||
| abd412b5d5 | |||
| 4d8c05cd92 | |||
| f2ca8081af | |||
| 7539011be5 | |||
| 5117427143 | |||
| e95986b830 | |||
| 5311972345 | |||
| 967295fba7 | |||
| 5403c5fb4f | |||
| 65986c3114 | |||
| 13a90b00f3 | |||
| 2ee7fc9910 | |||
| a0a0efabbb | |||
| 9a50278b98 | |||
| 9519a35e32 | |||
| 578d5fd541 | |||
| 642bc5dda1 | |||
| 88274abdb5 | |||
| aea65f5c5f | |||
| edfbfde13b | |||
| dcc676d60a | |||
| 561f61116c | |||
| 6a4594466b | |||
| af216ee08c | |||
| e493113450 | |||
| 74e1d5bdc4 | |||
| 5c0ad3e590 | |||
| 6e872ecab9 | |||
| 46a4cde77f | |||
| fc7c444107 | |||
| 822438e0d2 | |||
| de43a37e9e | |||
| d4b2d2f403 | |||
| 6e33eab136 | |||
| 1e4bc85fee | |||
| 31fff75f08 | |||
| f0620154c8 | |||
| 711aa1e4be | |||
| 854f2d75b3 | |||
| a85e2f6130 | |||
| bac2ba6f09 | |||
| 47e5270f9c | |||
| 68cbf09e9f | |||
| 9f18c88153 | |||
| c6caafdcb7 | |||
| b22a3e1a59 | |||
| b6934bbf63 | |||
| 3cd6eb13a9 | |||
| 272be2aaad | |||
| 99dd6ce77f | |||
| fc14455da4 | |||
| 26a52dae23 | |||
| 75864d33a6 | |||
| 63a97b6665 | |||
| 21a37a3bb0 | |||
| de586b5368 | |||
| ba40c3f739 | |||
| 31eff037a2 | |||
| 630dee0b2a | |||
| 9110f06ed5 | |||
| 2b0eceaa9d | |||
| c96e1babe5 | |||
| 9388cbde5d | |||
| e739cddd6a | |||
| 8cee6e0fc4 | |||
| bcf516afeb | |||
| b0e3e81b7f | |||
| ce6a1215a3 | |||
| f54c1dc7d0 | |||
| 43aaae8d47 | |||
| ca463a2944 | |||
| 20e22589dc | |||
| 38d047cb8a | |||
| 1d977199f3 | |||
| 5041019d77 | |||
| aa3835d3b3 | |||
| 678505811d | |||
| a925cbaed5 | |||
| 7d2201d873 | |||
| c0c1608d44 | |||
| 7bc6c83a04 | |||
| 3f0df82f2d | |||
| 328ff0251b | |||
| c52582a413 | |||
| 3aa6eee306 | |||
| 7d47faba0e | |||
| f6fb477898 | |||
| 8963960d4b | |||
| ef973f676b | |||
| 812f9ea30e | |||
| ff843b1241 | |||
| 59e7af149d | |||
| 6f14c85287 | |||
| ac0dec4dbf | |||
| e3d192412e | |||
| 9fadb6db30 | |||
| f8a1b71866 | |||
| c0a55acba7 | |||
| 4f232de634 | |||
| d351ebdaa0 | |||
| ab195e1d84 | |||
| 7041d77256 |
@@ -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:
|
||||||
|
|||||||
@@ -4,7 +4,128 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased ]
|
||||||
|
|
||||||
|
## [4.3.3] - 2022-08-02
|
||||||
|
### Added
|
||||||
|
- `rog-control-center` has now been moved in to the main workspace due to
|
||||||
|
the heavy dependencies on most of the rog crates
|
||||||
|
- Preliminary support of TUF RGB keyboards + power states
|
||||||
|
- Support for G713RW LED modes (Author: jarvis2709)
|
||||||
|
- Support for G713IC LED modes
|
||||||
|
### Changed
|
||||||
|
- The udev rules have been changed to make asusd load with all gamer variants when asus-nb-wmi is loaded
|
||||||
|
- TUF, ROG, Zephyrus, Strix
|
||||||
|
|
||||||
|
## [4.3.0] - 2022-07-21
|
||||||
|
### Added
|
||||||
|
- Clear command for anime `asusctl anime --clear` will clear the display
|
||||||
|
- Re-added support for LED power states on `0x1866` type keyboards
|
||||||
|
### Changed
|
||||||
|
- Make rog-anime more error tolerent. Remove various asserts and return errors instead
|
||||||
|
- Return error if a pixel-gif is larger than the anime-display dimensions
|
||||||
|
- Both Anime and Aura dbus interfaces are changed a little
|
||||||
|
- Aura power has changed, all power related settings are now in one method
|
||||||
|
- Anime methods will now return an error (if errored)
|
||||||
|
- /org/asuslinux/Led renamed to /org/asuslinux/Aura
|
||||||
|
|
||||||
|
## [4.2.1] - 2022-07-18
|
||||||
|
### Added
|
||||||
|
- Add panel overdrive support (autodetects if supported)
|
||||||
|
- Add detection of dgpu_disable and egpu_enable for diagnostic
|
||||||
|
### Changed
|
||||||
|
- Fixed save and restore of multizone LED settings
|
||||||
|
- Create defaults for multizone
|
||||||
|
|
||||||
|
## [4.2.0] - 2022-07-16
|
||||||
|
### Added
|
||||||
|
- Support for GA402 Anime Matrix display (Author: I-Al-Istannen & Luke Jones)
|
||||||
|
- Support for power-config of all LED zones. See `asusctrl led-power --help` (Author: Luke Jones, With much help from: @MNS26)
|
||||||
|
- Full support for multizone LED <logo, keyboard, lightbar> (Author: Luke Jones, With much help from: @MNS26)
|
||||||
|
- Add ability to load extra data from `/etc/asusd/asusd-user-ledmodes.toml` for LED support if file exits
|
||||||
|
- Support for G513IM LED modes
|
||||||
|
- Support for GX703HS LED modes
|
||||||
|
### Changed
|
||||||
|
- Dbus interface for Aura config has been changed, all power control is done with `SetLedsEnabled` and `SetLedsDisabled`
|
||||||
|
- Data for anime-matrix now requires passing the laptop model as enum
|
||||||
|
- Extra unit tests for anime stuff to help verify things
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Support for GA503R LED modes
|
||||||
|
### Changed
|
||||||
|
- Refactor LED and AniMe tasks
|
||||||
|
- Reload keyboard brightness on resume from sleep/hiber
|
||||||
|
|
||||||
|
## [4.1.1] - 2022-06-21
|
||||||
|
### Changed
|
||||||
|
- Fixes to anime matrix system thread cancelation
|
||||||
|
|
||||||
|
## [4.1.0] - 2022-06-20
|
||||||
|
### Changed
|
||||||
|
- Huge refactor to use zbus 2.2 + zvariant 3.0 in system-daemon.
|
||||||
|
- Daemons with tasks now use `smol` for async ops.
|
||||||
|
- Fixes to fan-curve settings from CLI (Author: Armas Span)
|
||||||
|
- Add brightness to anime zbus notification
|
||||||
|
- Adjust how threads in AniMe matrix controller work
|
||||||
|
- Use proper power-state packet for keyboard LED's (Author: Martin Piffault)
|
||||||
|
### Added
|
||||||
|
- Support for GA402R LED modes
|
||||||
|
- Support for GU502LV LED modes
|
||||||
|
- Support for G512 LED modes
|
||||||
|
- Support for G513IC LED modes (Author: dada513)
|
||||||
|
- Support for G513QM LED modes (Author: Martin Piffault)
|
||||||
|
- Add side-LED toggle support (Author: Martin Piffault)
|
||||||
|
- Support reloading keyboard mode on wake (from sleep/hiber)
|
||||||
|
- Support reloading charge-level on wake (from sleep/hiber)
|
||||||
|
- Support running AniMe animation blocks on wake/sleep and boot/shutdown events
|
||||||
|
|
||||||
|
# [4.0.7] - 2021-12-19
|
||||||
|
### 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
|
||||||
|
### Changed
|
||||||
|
- Add missing Profile commands
|
||||||
|
- Spawn tasks on individual threads to prevent blocking
|
||||||
|
- Don't force fan-curve default on reload
|
||||||
|
- Begin obsoleting the graphics switch command in favour of supergfxctl
|
||||||
|
- Slim down the notification daemon to pure ASUS notifications
|
||||||
|
|
||||||
|
# [4.0.3] - 2021-09-16
|
||||||
|
### Changed
|
||||||
|
- Don't show fan-curve warning if fan-curve available
|
||||||
|
- Add G713QR to Strix led-modes
|
||||||
|
- Fix part of CLI fan-curve control
|
||||||
|
|
||||||
|
# [4.0.2] - 2021-09-14
|
||||||
|
### Changed
|
||||||
|
- Backup old configs to *-old if parse fails
|
||||||
|
- Prevent some types of crashes related to unpatched kernels
|
||||||
|
- Add better help for graphics errors
|
||||||
|
- Add better help for asusctl general errors
|
||||||
|
- Implement fan-curve dbus API
|
||||||
|
- Implement partial fan-curve control via CLI tool
|
||||||
|
+ Set fan curve for profile + fan gpu/cpu
|
||||||
|
|
||||||
|
# [4.0.1] - 2021-09-11
|
||||||
|
### Changed
|
||||||
|
- Fix asusd-ledmodes.toml
|
||||||
|
|
||||||
# [4.0.0] - 2021-09-10
|
# [4.0.0] - 2021-09-10
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-supported", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles"]
|
members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-supported", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "rog-control-center"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
# thin = 57s, asusd = 9.0M
|
||||||
|
# fat = 72s, asusd = 6.4M
|
||||||
|
lto = "fat"
|
||||||
debug = false
|
debug = false
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|||||||
@@ -60,17 +60,17 @@ The defaults are located at `/etc/asusd/asusd-ledmodes.toml`, and on `asusd` sta
|
|||||||
Example:
|
Example:
|
||||||
```toml
|
```toml
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "ROG Zephyrus M15"
|
prod_family = "Strix"
|
||||||
board_names = ["GU502LU"]
|
board_names = ["GL504G"]
|
||||||
standard = ["Static", "Breathe", "Strobe", "Pulse"]
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
||||||
multizone = false
|
multizone = ["Key1", "Key2", "Key3", "Key4", "Logo", "BarLeft", "BarRight"]
|
||||||
per_key = false
|
per_key = false
|
||||||
```
|
```
|
||||||
|
|
||||||
1. `prod_family`: you can find this in `journalctl -b -u asusd`, or `cat /sys/class/dmi/id/product_name`. It should be copied as written. There can be multiple `led-data` groups of the same `prod_family` with differing `board_names`.
|
1. `prod_family`: you can find this in `journalctl -b -u asusd`, or `cat /sys/class/dmi/id/product_name`. It should be copied as written. There can be multiple `led-data` groups of the same `prod_family` with differing `board_names`.
|
||||||
2. `board_names`: is an array of board names in this product family. Find this in the journal as above or by `cat /sys/class/dmi/id/board_name`.
|
2. `board_names`: is an array of board names in this product family. Find this in the journal as above or by `cat /sys/class/dmi/id/board_name`.
|
||||||
3. `standard` are the factory preset modes, the names should corrospond to Armory Crate names
|
3. `standard` are the factory preset modes, the names should corrospond to Armory Crate names
|
||||||
4. `multizone`: some keyboards have 4 zones of LED control, this enables setting a colour in each zone. The keyboard must support this or it has no effect.
|
4. `multizone`: some models have 4 to 7 zones of LED control as shown in the example. If the laptop has no zones then an empty array will suffice.
|
||||||
5. `per_key`: enable per-key RGB effects. The keyboard must support this or it has no effect.
|
5. `per_key`: enable per-key RGB effects. The keyboard must support this or it has no effect.
|
||||||
|
|
||||||
##### /etc/asusd/aura.conf
|
##### /etc/asusd/aura.conf
|
||||||
@@ -90,14 +90,14 @@ where the number is a percentage.
|
|||||||
|
|
||||||
Some options that you find in Armory Crate are available under this controller, so far there is:
|
Some options that you find in Armory Crate are available under this controller, so far there is:
|
||||||
|
|
||||||
- POST sound: this is the sound you here on bios boot post
|
- POST sound: this is the sound you hear on bios boot post
|
||||||
- G-Sync: this controls if the dGPU (Nvidia) is the *only* GPU, making it the main GPU and disabling the iGPU
|
- G-Sync: this controls if the dGPU (Nvidia) is the *only* GPU, making it the main GPU and disabling the iGPU
|
||||||
|
|
||||||
These options are not written to the config file as they are stored in efivars. The only way to change these is to use the exposed safe dbus methods, or use the `asusctl` CLI tool.
|
These options are not written to the config file as they are stored in efivars. The only way to change these is to use the exposed safe dbus methods, or use the `asusctl` CLI tool.
|
||||||
|
|
||||||
### 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:
|
||||||
|
|
||||||
@@ -293,7 +293,7 @@ A plain non-float integer.
|
|||||||
|
|
||||||
## asusctl
|
## asusctl
|
||||||
|
|
||||||
`asusctl` is a commandline interface which intends to be the main method of interacting with `asusd`. I can be used in any place a terminal app can be used.
|
`asusctl` is a commandline interface which intends to be the main method of interacting with `asusd`. It can be used in any place a terminal app can be used.
|
||||||
|
|
||||||
This program will query `asusd` for the `Support` level of the laptop and show or hide options according to this support level.
|
This program will query `asusd` for the `Support` level of the laptop and show or hide options according to this support level.
|
||||||
|
|
||||||
@@ -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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ datarootdir = $(prefix)/share
|
|||||||
libdir = $(exec_prefix)/lib
|
libdir = $(exec_prefix)/lib
|
||||||
zshcpl = $(datarootdir)/zsh/site-functions
|
zshcpl = $(datarootdir)/zsh/site-functions
|
||||||
|
|
||||||
|
BIN_ROG := rog-control-center
|
||||||
BIN_C := asusctl
|
BIN_C := asusctl
|
||||||
BIN_D := asusd
|
BIN_D := asusd
|
||||||
BIN_U := asusd-user
|
BIN_U := asusd-user
|
||||||
@@ -39,6 +40,11 @@ distclean:
|
|||||||
rm -rf .cargo vendor vendor.tar.xz
|
rm -rf .cargo vendor vendor.tar.xz
|
||||||
|
|
||||||
install:
|
install:
|
||||||
|
$(INSTALL_PROGRAM) "./target/release/$(BIN_ROG)" "$(DESTDIR)$(bindir)/$(BIN_ROG)"
|
||||||
|
$(INSTALL_DATA) "./rog-control-center/data/$(BIN_ROG).desktop" "$(DESTDIR)$(datarootdir)/applications/$(BIN_ROG).desktop"
|
||||||
|
$(INSTALL_DATA) "./rog-control-center/data/$(BIN_ROG).png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/$(BIN_ROG).png"
|
||||||
|
cd rog-aura/data/layouts && find . -type f -name "*.toml" -exec $(INSTALL_DATA) "{}" "$(DESTDIR)$(datarootdir)/rog-gui/layouts/{}" \;
|
||||||
|
|
||||||
$(INSTALL_PROGRAM) "./target/release/$(BIN_C)" "$(DESTDIR)$(bindir)/$(BIN_C)"
|
$(INSTALL_PROGRAM) "./target/release/$(BIN_C)" "$(DESTDIR)$(bindir)/$(BIN_C)"
|
||||||
$(INSTALL_PROGRAM) "./target/release/$(BIN_D)" "$(DESTDIR)$(bindir)/$(BIN_D)"
|
$(INSTALL_PROGRAM) "./target/release/$(BIN_D)" "$(DESTDIR)$(bindir)/$(BIN_D)"
|
||||||
$(INSTALL_PROGRAM) "./target/release/$(BIN_U)" "$(DESTDIR)$(bindir)/$(BIN_U)"
|
$(INSTALL_PROGRAM) "./target/release/$(BIN_U)" "$(DESTDIR)$(bindir)/$(BIN_U)"
|
||||||
@@ -63,11 +69,13 @@ install:
|
|||||||
$(INSTALL_DATA) "./data/icons/scalable/gpu-vfio.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-vfio.svg"
|
$(INSTALL_DATA) "./data/icons/scalable/gpu-vfio.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-vfio.svg"
|
||||||
$(INSTALL_DATA) "./data/icons/scalable/notification-reboot.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/notification-reboot.svg"
|
$(INSTALL_DATA) "./data/icons/scalable/notification-reboot.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/notification-reboot.svg"
|
||||||
|
|
||||||
$(INSTALL_DATA) "./data/_asusctl" "$(DESTDIR)$(zshcpl)/_asusctl"
|
cd rog-anime/data && find "./anime" -type f -exec $(INSTALL_DATA) "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \;
|
||||||
$(INSTALL_DATA) "./data/completions/asusctl.fish" "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish"
|
|
||||||
cd rog-anime/data && find "./anime" -type f -exec install -Dm 755 "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \;
|
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
|
rm -f "$(DESTDIR)$(bindir)/$(BIN_ROG)"
|
||||||
|
rm -r "$(DESTDIR)$(datarootdir)/applications/$(BIN_ROG).desktop"
|
||||||
|
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/$(BIN_ROG).png"
|
||||||
|
|
||||||
rm -f "$(DESTDIR)$(bindir)/$(BIN_C)"
|
rm -f "$(DESTDIR)$(bindir)/$(BIN_C)"
|
||||||
rm -f "$(DESTDIR)$(bindir)/$(BIN_D)"
|
rm -f "$(DESTDIR)$(bindir)/$(BIN_D)"
|
||||||
rm -f "$(DESTDIR)$(bindir)/$(BIN_N)"
|
rm -f "$(DESTDIR)$(bindir)/$(BIN_N)"
|
||||||
@@ -85,9 +93,8 @@ uninstall:
|
|||||||
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-nvidia.svg"
|
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-nvidia.svg"
|
||||||
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-vfio.svg"
|
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-vfio.svg"
|
||||||
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/notification-reboot.svg"
|
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/notification-reboot.svg"
|
||||||
rm -f "$(DESTDIR)$(zshcpl)/_asusctl"
|
|
||||||
rm -f "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish"
|
|
||||||
rm -rf "$(DESTDIR)$(datarootdir)/asusd"
|
rm -rf "$(DESTDIR)$(datarootdir)/asusd"
|
||||||
|
rm -rf "$(DESTDIR)$(datarootdir)/rog-gui"
|
||||||
|
|
||||||
update:
|
update:
|
||||||
cargo update
|
cargo update
|
||||||
|
|||||||
@@ -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
|
Now includes a GUI, `rog-control-center`.
|
||||||
|
|
||||||
1. https://lkml.org/lkml/2021/8/20/232
|
## Kernel support
|
||||||
2. https://lkml.org/lkml/2021/8/18/1022
|
|
||||||
|
**The minimum supported kernel version is 5.17**
|
||||||
|
|
||||||
## 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)
|
||||||
@@ -45,6 +42,16 @@ then it may work without tweaks. Technically all other functions except the LED
|
|||||||
and AniMe parts should work regardless of your latop make. Eventually this project
|
and AniMe parts should work regardless of your latop make. Eventually this project
|
||||||
will probably suffer another rename once it becomes generic enough to do so.
|
will probably suffer another rename once it becomes generic enough to do so.
|
||||||
|
|
||||||
|
### Unsupported (e.g, TUF laptops)
|
||||||
|
|
||||||
|
- `sudo systemctl mask asusd && sudo systemctl stop asusd`
|
||||||
|
- `copy asusd-alt.service to /etc/systemd/system/`
|
||||||
|
- `sudo systemctl enable asusd-alt`
|
||||||
|
- `sudo systemctl start asusd-alt`
|
||||||
|
- `asusctl -s`
|
||||||
|
|
||||||
|
`asusd-alt.service` is in the `data` dir of this repo.
|
||||||
|
|
||||||
## Implemented
|
## Implemented
|
||||||
|
|
||||||
- [X] System daemon
|
- [X] System daemon
|
||||||
@@ -61,27 +68,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.
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
[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" }
|
||||||
supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", tag = "2.0.0" }
|
smol = "^1.2"
|
||||||
|
|
||||||
[dependencies.notify-rust]
|
[dependencies.notify-rust]
|
||||||
version = "^4.3"
|
version = "^4.3"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["z"]
|
features = ["z"]
|
||||||
|
|||||||
@@ -1,15 +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::sync::mpsc::channel;
|
use std::{
|
||||||
use std::thread::sleep;
|
error::Error,
|
||||||
use std::time::Duration;
|
sync::{Arc, Mutex},
|
||||||
use std::{process, thread};
|
};
|
||||||
use supergfxctl::gfx_vendors::{GfxRequiredUserAction, GfxVendors};
|
use zbus::export::futures_util::StreamExt;
|
||||||
use supergfxctl::zbus_proxy::GfxProxy;
|
|
||||||
use zbus::Connection;
|
|
||||||
|
|
||||||
const NOTIF_HEADER: &str = "ROG Control";
|
const NOTIF_HEADER: &str = "ROG Control";
|
||||||
|
|
||||||
@@ -19,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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -34,99 +35,99 @@ 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);
|
||||||
println!("supergfxctl version {}", supergfxctl::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();
|
||||||
|
|
||||||
gfx_thread()?;
|
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gfx_thread() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let mut last_notification: Option<NotificationHandle> = None;
|
|
||||||
|
|
||||||
let conn = Connection::new_system()?;
|
|
||||||
let proxy = GfxProxy::new(&conn)?;
|
|
||||||
|
|
||||||
let (tx1, rx1) = channel();
|
|
||||||
proxy.connect_notify_gfx(tx1)?;
|
|
||||||
|
|
||||||
let (tx2, rx2) = channel();
|
|
||||||
proxy.connect_notify_action(tx2)?;
|
|
||||||
|
|
||||||
thread::spawn(move || loop {
|
|
||||||
if proxy
|
|
||||||
.next_signal()
|
|
||||||
.map_err(|e| println!("Error: {}", e))
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(data) = rx1.try_recv() {
|
|
||||||
notify!(do_gfx_notif, last_notification, &data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(data) = rx2.try_recv() {
|
|
||||||
match data {
|
|
||||||
GfxRequiredUserAction::Logout | GfxRequiredUserAction::Reboot => {
|
|
||||||
do_gfx_action_notif(&data)
|
|
||||||
.map_err(|e| {
|
|
||||||
println!("Error: {}", e);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
GfxRequiredUserAction::Integrated => {
|
|
||||||
base_notification!(
|
|
||||||
"You must be in integrated mode first to switch to the requested mode"
|
|
||||||
)
|
|
||||||
.map_err(|e| {
|
|
||||||
println!("Error: {}", e);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
GfxRequiredUserAction::None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn do_thermal_notif(profile: &Profile) -> Result<NotificationHandle, Box<dyn Error>> {
|
fn do_thermal_notif(profile: &Profile) -> Result<NotificationHandle, Box<dyn Error>> {
|
||||||
let icon = match profile {
|
let icon = match profile {
|
||||||
Profile::Balanced => "asus_notif_yellow",
|
Profile::Balanced => "asus_notif_yellow",
|
||||||
@@ -160,51 +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_gfx_notif(vendor: &GfxVendors) -> Result<NotificationHandle, notify_rust::error::Error> {
|
fn do_post_sound_notif(on: &bool) -> Result<NotificationHandle, notify_rust::error::Error> {
|
||||||
let icon = match vendor {
|
base_notification!(&format!("BIOS Post sound {}", on))
|
||||||
GfxVendors::Nvidia => "/usr/share/icons/hicolor/scalable/status/gpu-nvidia.svg",
|
|
||||||
GfxVendors::Integrated => "/usr/share/icons/hicolor/scalable/status/gpu-integrated.svg",
|
|
||||||
GfxVendors::Compute => "/usr/share/icons/hicolor/scalable/status/gpu-compute.svg",
|
|
||||||
GfxVendors::Vfio => "/usr/share/icons/hicolor/scalable/status/gpu-vfio.svg",
|
|
||||||
GfxVendors::Hybrid => "/usr/share/icons/hicolor/scalable/status/gpu-hybrid.svg",
|
|
||||||
};
|
|
||||||
Notification::new()
|
|
||||||
.summary(NOTIF_HEADER)
|
|
||||||
.body(&format!(
|
|
||||||
"Graphics mode changed to {}",
|
|
||||||
<&str>::from(vendor)
|
|
||||||
))
|
|
||||||
.timeout(2000)
|
|
||||||
.icon(icon)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn do_gfx_action_notif(vendor: &GfxRequiredUserAction) -> Result<(), notify_rust::error::Error> {
|
|
||||||
let mut notif = Notification::new()
|
|
||||||
.summary(NOTIF_HEADER)
|
|
||||||
.timeout(2000)
|
|
||||||
.urgency(notify_rust::Urgency::Critical)
|
|
||||||
.icon("/usr/share/icons/hicolor/scalable/status/notification-reboot.svg")
|
|
||||||
.finalize();
|
|
||||||
|
|
||||||
if matches!(vendor, GfxRequiredUserAction::Logout) {
|
|
||||||
notif.action("logout", "Logout now?");
|
|
||||||
} else if matches!(vendor, GfxRequiredUserAction::Reboot) {
|
|
||||||
notif.action("reboot", "Reboot now?");
|
|
||||||
}
|
|
||||||
|
|
||||||
notif.body("Graphics mode changed");
|
|
||||||
notif.show()?.wait_for_action(|action| match action {
|
|
||||||
"logout" => {
|
|
||||||
process::Command::new("gnome-session-quit").spawn().ok();
|
|
||||||
}
|
|
||||||
"reboot" => {
|
|
||||||
process::Command::new("systemctl")
|
|
||||||
.arg("reboot")
|
|
||||||
.spawn()
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "asusctl"
|
name = "asusctl"
|
||||||
version = "4.0.0"
|
version = "4.3.3"
|
||||||
authors = ["Luke D Jones <luke@ljones.dev>"]
|
authors = ["Luke D Jones <luke@ljones.dev>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
zbus = "^1.9.1"
|
zbus = "^2.2"
|
||||||
rog_anime = { path = "../rog-anime" }
|
rog_anime = { path = "../rog-anime" }
|
||||||
rog_aura = { path = "../rog-aura" }
|
rog_aura = { path = "../rog-aura" }
|
||||||
rog_dbus = { path = "../rog-dbus" }
|
rog_dbus = { path = "../rog-dbus" }
|
||||||
@@ -15,12 +15,12 @@ rog_profiles = { path = "../rog-profiles" }
|
|||||||
rog_supported = { path = "../rog-supported" }
|
rog_supported = { path = "../rog-supported" }
|
||||||
daemon = { path = "../daemon" }
|
daemon = { path = "../daemon" }
|
||||||
gumdrop = "^0.8"
|
gumdrop = "^0.8"
|
||||||
supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", tag = "2.0.0" }
|
toml = "^0.5.8"
|
||||||
|
|
||||||
sysfs-class = "^0.1.2"
|
sysfs-class = "^0.1.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tinybmp = "^0.2.3"
|
tinybmp = "^0.3.3"
|
||||||
glam = "0.14.0"
|
glam = "^0.21.2"
|
||||||
rog_dbus = { path = "../rog-dbus" }
|
rog_dbus = { path = "../rog-dbus" }
|
||||||
gif = "^0.11.2"
|
gif = "^0.11.2"
|
||||||
|
|||||||
@@ -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(())
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::{thread::sleep, time::Duration};
|
use std::{thread::sleep, time::Duration};
|
||||||
|
|
||||||
use rog_anime::{AnimeDataBuffer, AnimeDiagonal};
|
use rog_anime::{usb::get_anime_type, AnimeDiagonal, AnimeType};
|
||||||
use rog_dbus::RogDbusClient;
|
use rog_dbus::RogDbusClientBlocking;
|
||||||
|
|
||||||
// In usable data:
|
// In usable data:
|
||||||
// Top row start at 1, ends at 32
|
// Top row start at 1, ends at 32
|
||||||
@@ -9,10 +9,10 @@ use rog_dbus::RogDbusClient;
|
|||||||
// 74w x 36h diagonal used by the windows app
|
// 74w x 36h diagonal used by the windows app
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let (client, _) = RogDbusClient::new().unwrap();
|
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||||
|
|
||||||
for step in (2..50).rev() {
|
for step in (2..50).rev() {
|
||||||
let mut matrix = AnimeDiagonal::new(None);
|
let mut matrix = AnimeDiagonal::new(AnimeType::GA401, None);
|
||||||
for c in (0..60).into_iter().step_by(step) {
|
for c in (0..60).into_iter().step_by(step) {
|
||||||
for i in matrix.get_mut().iter_mut() {
|
for i in matrix.get_mut().iter_mut() {
|
||||||
i[c] = 50;
|
i[c] = 50;
|
||||||
@@ -25,8 +25,12 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let m = <AnimeDataBuffer>::from(&matrix);
|
let anime_type = get_anime_type().unwrap();
|
||||||
client.proxies().anime().write(m).unwrap();
|
client
|
||||||
|
.proxies()
|
||||||
|
.anime()
|
||||||
|
.write(matrix.into_data_buffer(anime_type).unwrap())
|
||||||
|
.unwrap();
|
||||||
sleep(Duration::from_millis(300));
|
sleep(Duration::from_millis(300));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use rog_anime::{AnimeDataBuffer, AnimeGrid};
|
use rog_anime::{usb::get_anime_type, AnimeDataBuffer, AnimeGrid};
|
||||||
use rog_dbus::RogDbusClient;
|
use rog_dbus::RogDbusClientBlocking;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
// In usable data:
|
// In usable data:
|
||||||
// Top row start at 1, ends at 32
|
// Top row start at 1, ends at 32
|
||||||
@@ -7,8 +8,9 @@ use rog_dbus::RogDbusClient;
|
|||||||
// 74w x 36h diagonal used by the windows app
|
// 74w x 36h diagonal used by the windows app
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let (client, _) = RogDbusClient::new().unwrap();
|
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||||
let mut matrix = AnimeGrid::new(None);
|
let anime_type = get_anime_type().unwrap();
|
||||||
|
let mut matrix = AnimeGrid::new(anime_type);
|
||||||
let tmp = matrix.get_mut();
|
let tmp = matrix.get_mut();
|
||||||
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
@@ -38,7 +40,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let matrix = <AnimeDataBuffer>::from(matrix);
|
let matrix = <AnimeDataBuffer>::try_from(matrix).unwrap();
|
||||||
|
|
||||||
client.proxies().anime().write(matrix).unwrap();
|
client.proxies().anime().write(matrix).unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
|
use std::convert::TryFrom;
|
||||||
use std::{env, error::Error, path::Path, process::exit};
|
use std::{env, error::Error, path::Path, process::exit};
|
||||||
|
|
||||||
use rog_anime::{
|
use rog_anime::{
|
||||||
|
usb::get_anime_type,
|
||||||
AnimeDataBuffer, {AnimeImage, Vec2},
|
AnimeDataBuffer, {AnimeImage, Vec2},
|
||||||
};
|
};
|
||||||
use rog_dbus::RogDbusClient;
|
use rog_dbus::RogDbusClientBlocking;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let (client, _) = RogDbusClient::new().unwrap();
|
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||||
|
|
||||||
let args: Vec<String> = env::args().into_iter().collect();
|
let args: Vec<String> = env::args().into_iter().collect();
|
||||||
if args.len() != 7 {
|
if args.len() != 7 {
|
||||||
@@ -15,6 +17,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let anime_type = get_anime_type()?;
|
||||||
let matrix = AnimeImage::from_png(
|
let matrix = AnimeImage::from_png(
|
||||||
Path::new(&args[1]),
|
Path::new(&args[1]),
|
||||||
args[2].parse::<f32>().unwrap(),
|
args[2].parse::<f32>().unwrap(),
|
||||||
@@ -24,12 +27,13 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
args[5].parse::<f32>().unwrap(),
|
args[5].parse::<f32>().unwrap(),
|
||||||
),
|
),
|
||||||
args[6].parse::<f32>().unwrap(),
|
args[6].parse::<f32>().unwrap(),
|
||||||
|
anime_type,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
client
|
client
|
||||||
.proxies()
|
.proxies()
|
||||||
.anime()
|
.anime()
|
||||||
.write(<AnimeDataBuffer>::from(&matrix))
|
.write(<AnimeDataBuffer>::try_from(&matrix)?)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
|
use std::convert::TryFrom;
|
||||||
use std::{
|
use std::{
|
||||||
env, error::Error, f32::consts::PI, path::Path, process::exit, thread::sleep, time::Duration,
|
env, error::Error, f32::consts::PI, path::Path, process::exit, thread::sleep, time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rog_anime::{
|
use rog_anime::{
|
||||||
|
usb::get_anime_type,
|
||||||
AnimeDataBuffer, {AnimeImage, Vec2},
|
AnimeDataBuffer, {AnimeImage, Vec2},
|
||||||
};
|
};
|
||||||
use rog_dbus::RogDbusClient;
|
use rog_dbus::RogDbusClientBlocking;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let (client, _) = RogDbusClient::new().unwrap();
|
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||||
|
|
||||||
let args: Vec<String> = env::args().into_iter().collect();
|
let args: Vec<String> = env::args().into_iter().collect();
|
||||||
if args.len() != 7 {
|
if args.len() != 7 {
|
||||||
@@ -17,6 +19,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let anime_type = get_anime_type()?;
|
||||||
let mut matrix = AnimeImage::from_png(
|
let mut matrix = AnimeImage::from_png(
|
||||||
Path::new(&args[1]),
|
Path::new(&args[1]),
|
||||||
args[2].parse::<f32>().unwrap(),
|
args[2].parse::<f32>().unwrap(),
|
||||||
@@ -26,6 +29,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
args[5].parse::<f32>().unwrap(),
|
args[5].parse::<f32>().unwrap(),
|
||||||
),
|
),
|
||||||
args[6].parse::<f32>().unwrap(),
|
args[6].parse::<f32>().unwrap(),
|
||||||
|
anime_type,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@@ -38,7 +42,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
client
|
client
|
||||||
.proxies()
|
.proxies()
|
||||||
.anime()
|
.anime()
|
||||||
.write(<AnimeDataBuffer>::from(&matrix))
|
.write(<AnimeDataBuffer>::try_from(&matrix)?)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
sleep(Duration::from_micros(500));
|
sleep(Duration::from_micros(500));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,32 @@ 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(help = "clear the display")]
|
||||||
|
pub clear: bool,
|
||||||
#[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 +51,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,
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,69 @@ use gumdrop::Options;
|
|||||||
use rog_aura::{error::Error, AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed};
|
use rog_aura::{error::Error, AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[derive(Options)]
|
||||||
|
pub struct LedPowerCommand1 {
|
||||||
|
#[options(help = "print help message")]
|
||||||
|
pub help: bool,
|
||||||
|
#[options(meta = "", help = "Control if LEDs enabled while awake <true/false>")]
|
||||||
|
pub awake: Option<bool>,
|
||||||
|
#[options(meta = "", help = "Use with awake option <true/false>")]
|
||||||
|
pub keyboard: Option<bool>,
|
||||||
|
#[options(meta = "", help = "Use with awake option <true/false>")]
|
||||||
|
pub lightbar: Option<bool>,
|
||||||
|
#[options(meta = "", help = "Control boot animations <true/false>")]
|
||||||
|
pub boot: Option<bool>,
|
||||||
|
#[options(meta = "", help = "Control suspend animations <true/false>")]
|
||||||
|
pub sleep: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Options)]
|
||||||
|
pub struct LedPowerCommand2 {
|
||||||
|
#[options(help = "print help message")]
|
||||||
|
pub help: bool,
|
||||||
|
#[options(command)]
|
||||||
|
pub command: Option<SetAuraEnabled>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Options)]
|
||||||
|
pub enum SetAuraEnabled {
|
||||||
|
/// Applies to both old and new models
|
||||||
|
#[options(help = "set <keyboard, logo, lightbar> to enabled while device is awake")]
|
||||||
|
Awake(AuraEnabled),
|
||||||
|
#[options(help = "set <keyboard, logo, lightbar> to enabled while the device is booting")]
|
||||||
|
Boot(AuraEnabled),
|
||||||
|
#[options(help = "set <keyboard, logo, lightbar> to animate while the device is suspended")]
|
||||||
|
Sleep(AuraEnabled),
|
||||||
|
#[options(help = "set <keyboard, logo, lightbar> to animate while the device is shutdown")]
|
||||||
|
Shutdown(AuraEnabled),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Options)]
|
||||||
|
pub struct AuraEnabled {
|
||||||
|
#[options(help = "print help message")]
|
||||||
|
pub help: bool,
|
||||||
|
#[options(meta = "", help = "<true/false>")]
|
||||||
|
pub keyboard: Option<bool>,
|
||||||
|
#[options(meta = "", help = "<true/false>")]
|
||||||
|
pub logo: Option<bool>,
|
||||||
|
#[options(meta = "", help = "<true/false>")]
|
||||||
|
pub lightbar: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl FromStr for AuraEnabled {
|
||||||
|
// type Err = Error;
|
||||||
|
|
||||||
|
// fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
// let s = s.to_lowercase();
|
||||||
|
// Ok(Self {
|
||||||
|
// help: false,
|
||||||
|
// keyboard: None,
|
||||||
|
// logo: None,
|
||||||
|
// lightbar: None,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
#[derive(Options)]
|
#[derive(Options)]
|
||||||
pub struct LedBrightness {
|
pub struct LedBrightness {
|
||||||
level: Option<u32>,
|
level: Option<u32>,
|
||||||
@@ -50,7 +113,14 @@ pub struct SingleSpeed {
|
|||||||
help: bool,
|
help: bool,
|
||||||
#[options(no_long, meta = "WORD", help = "set the speed: low, med, high")]
|
#[options(no_long, meta = "WORD", help = "set the speed: low, med, high")]
|
||||||
pub speed: Speed,
|
pub speed: Speed,
|
||||||
|
#[options(
|
||||||
|
no_long,
|
||||||
|
meta = "",
|
||||||
|
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
|
||||||
|
)]
|
||||||
|
pub zone: AuraZone,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Options, Default)]
|
#[derive(Debug, Clone, Options, Default)]
|
||||||
pub struct SingleSpeedDirection {
|
pub struct SingleSpeedDirection {
|
||||||
#[options(help = "print help message")]
|
#[options(help = "print help message")]
|
||||||
@@ -59,6 +129,12 @@ pub struct SingleSpeedDirection {
|
|||||||
pub direction: Direction,
|
pub direction: Direction,
|
||||||
#[options(no_long, meta = "", help = "set the speed: low, med, high")]
|
#[options(no_long, meta = "", help = "set the speed: low, med, high")]
|
||||||
pub speed: Speed,
|
pub speed: Speed,
|
||||||
|
#[options(
|
||||||
|
no_long,
|
||||||
|
meta = "",
|
||||||
|
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
|
||||||
|
)]
|
||||||
|
pub zone: AuraZone,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Options)]
|
#[derive(Debug, Clone, Default, Options)]
|
||||||
@@ -67,6 +143,12 @@ pub struct SingleColour {
|
|||||||
help: bool,
|
help: bool,
|
||||||
#[options(no_long, meta = "", help = "set the RGB value e.g, ff00ff")]
|
#[options(no_long, meta = "", help = "set the RGB value e.g, ff00ff")]
|
||||||
pub colour: Colour,
|
pub colour: Colour,
|
||||||
|
#[options(
|
||||||
|
no_long,
|
||||||
|
meta = "",
|
||||||
|
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
|
||||||
|
)]
|
||||||
|
pub zone: AuraZone,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Options)]
|
#[derive(Debug, Clone, Default, Options)]
|
||||||
@@ -77,6 +159,12 @@ pub struct SingleColourSpeed {
|
|||||||
pub colour: Colour,
|
pub colour: Colour,
|
||||||
#[options(no_long, meta = "", help = "set the speed: low, med, high")]
|
#[options(no_long, meta = "", help = "set the speed: low, med, high")]
|
||||||
pub speed: Speed,
|
pub speed: Speed,
|
||||||
|
#[options(
|
||||||
|
no_long,
|
||||||
|
meta = "",
|
||||||
|
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
|
||||||
|
)]
|
||||||
|
pub zone: AuraZone,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Options, Default)]
|
#[derive(Debug, Clone, Options, Default)]
|
||||||
@@ -89,10 +177,16 @@ pub struct TwoColourSpeed {
|
|||||||
pub colour2: Colour,
|
pub colour2: Colour,
|
||||||
#[options(no_long, meta = "", help = "set the speed: low, med, high")]
|
#[options(no_long, meta = "", help = "set the speed: low, med, high")]
|
||||||
pub speed: Speed,
|
pub speed: Speed,
|
||||||
|
#[options(
|
||||||
|
no_long,
|
||||||
|
meta = "",
|
||||||
|
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
|
||||||
|
)]
|
||||||
|
pub zone: AuraZone,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Options)]
|
#[derive(Debug, Clone, Default, Options)]
|
||||||
pub struct MultiColour {
|
pub struct MultiZone {
|
||||||
#[options(help = "print help message")]
|
#[options(help = "print help message")]
|
||||||
help: bool,
|
help: bool,
|
||||||
#[options(short = "a", meta = "", help = "set the RGB value e.g, ff00ff")]
|
#[options(short = "a", meta = "", help = "set the RGB value e.g, ff00ff")]
|
||||||
@@ -152,10 +246,6 @@ pub enum SetAuraBuiltin {
|
|||||||
Comet(SingleColour),
|
Comet(SingleColour),
|
||||||
#[options(help = "set a wide vertical line zooming from left")]
|
#[options(help = "set a wide vertical line zooming from left")]
|
||||||
Flash(SingleColour),
|
Flash(SingleColour),
|
||||||
#[options(help = "4-zone multi-colour")]
|
|
||||||
MultiStatic(MultiColour),
|
|
||||||
#[options(help = "4-zone multi-colour breathing")]
|
|
||||||
MultiBreathe(MultiColourSpeed),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SetAuraBuiltin {
|
impl Default for SetAuraBuiltin {
|
||||||
@@ -168,6 +258,7 @@ impl From<&SingleColour> for AuraEffect {
|
|||||||
fn from(aura: &SingleColour) -> Self {
|
fn from(aura: &SingleColour) -> Self {
|
||||||
Self {
|
Self {
|
||||||
colour1: aura.colour,
|
colour1: aura.colour,
|
||||||
|
zone: aura.zone,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,6 +268,7 @@ impl From<&SingleSpeed> for AuraEffect {
|
|||||||
fn from(aura: &SingleSpeed) -> Self {
|
fn from(aura: &SingleSpeed) -> Self {
|
||||||
Self {
|
Self {
|
||||||
speed: aura.speed,
|
speed: aura.speed,
|
||||||
|
zone: aura.zone,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,6 +279,7 @@ impl From<&SingleColourSpeed> for AuraEffect {
|
|||||||
Self {
|
Self {
|
||||||
colour1: aura.colour,
|
colour1: aura.colour,
|
||||||
speed: aura.speed,
|
speed: aura.speed,
|
||||||
|
zone: aura.zone,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,6 +290,7 @@ impl From<&TwoColourSpeed> for AuraEffect {
|
|||||||
Self {
|
Self {
|
||||||
colour1: aura.colour,
|
colour1: aura.colour,
|
||||||
colour2: aura.colour2,
|
colour2: aura.colour2,
|
||||||
|
zone: aura.zone,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,6 +301,7 @@ impl From<&SingleSpeedDirection> for AuraEffect {
|
|||||||
Self {
|
Self {
|
||||||
speed: aura.speed,
|
speed: aura.speed,
|
||||||
direction: aura.direction,
|
direction: aura.direction,
|
||||||
|
zone: aura.zone,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,55 +370,6 @@ impl From<&SetAuraBuiltin> for AuraEffect {
|
|||||||
data.mode = AuraModeNum::Flash;
|
data.mode = AuraModeNum::Flash;
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
_ => AuraEffect::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&SetAuraBuiltin> for Vec<AuraEffect> {
|
|
||||||
fn from(aura: &SetAuraBuiltin) -> Vec<AuraEffect> {
|
|
||||||
let mut zones = vec![AuraEffect::default(); 4];
|
|
||||||
match aura {
|
|
||||||
SetAuraBuiltin::MultiStatic(data) => {
|
|
||||||
zones[0].mode = AuraModeNum::Static;
|
|
||||||
zones[0].zone = AuraZone::One;
|
|
||||||
zones[0].colour1 = data.colour1;
|
|
||||||
|
|
||||||
zones[1].mode = AuraModeNum::Static;
|
|
||||||
zones[1].zone = AuraZone::Two;
|
|
||||||
zones[1].colour1 = data.colour2;
|
|
||||||
|
|
||||||
zones[2].mode = AuraModeNum::Static;
|
|
||||||
zones[2].zone = AuraZone::Three;
|
|
||||||
zones[2].colour1 = data.colour3;
|
|
||||||
|
|
||||||
zones[3].mode = AuraModeNum::Static;
|
|
||||||
zones[3].zone = AuraZone::Four;
|
|
||||||
zones[3].colour1 = data.colour4;
|
|
||||||
}
|
|
||||||
SetAuraBuiltin::MultiBreathe(data) => {
|
|
||||||
zones[0].mode = AuraModeNum::Breathe;
|
|
||||||
zones[0].zone = AuraZone::One;
|
|
||||||
zones[0].colour1 = data.colour1;
|
|
||||||
zones[0].speed = data.speed;
|
|
||||||
|
|
||||||
zones[1].mode = AuraModeNum::Breathe;
|
|
||||||
zones[1].zone = AuraZone::Two;
|
|
||||||
zones[1].colour1 = data.colour2;
|
|
||||||
zones[1].speed = data.speed;
|
|
||||||
|
|
||||||
zones[2].mode = AuraModeNum::Breathe;
|
|
||||||
zones[2].zone = AuraZone::Three;
|
|
||||||
zones[2].colour1 = data.colour3;
|
|
||||||
zones[2].speed = data.speed;
|
|
||||||
|
|
||||||
zones[3].mode = AuraModeNum::Breathe;
|
|
||||||
zones[3].zone = AuraZone::Four;
|
|
||||||
zones[3].colour1 = data.colour4;
|
|
||||||
zones[3].speed = data.speed;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
zones
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
anime_cli::AnimeCommand,
|
anime_cli::AnimeCommand,
|
||||||
aura_cli::{LedBrightness, SetAuraBuiltin},
|
aura_cli::{LedBrightness, LedPowerCommand1, LedPowerCommand2, SetAuraBuiltin},
|
||||||
profiles_cli::ProfileCommand,
|
profiles_cli::{FanCurveCommand, ProfileCommand},
|
||||||
};
|
};
|
||||||
use gumdrop::Options;
|
use gumdrop::Options;
|
||||||
use supergfxctl::gfx_vendors::GfxVendors;
|
|
||||||
|
|
||||||
#[derive(Default, Options)]
|
#[derive(Default, Options)]
|
||||||
pub struct CliStart {
|
pub struct CliStart {
|
||||||
@@ -20,7 +19,7 @@ pub struct CliStart {
|
|||||||
pub next_kbd_bright: bool,
|
pub next_kbd_bright: bool,
|
||||||
#[options(help = "Toggle to previous keyboard brightness")]
|
#[options(help = "Toggle to previous keyboard brightness")]
|
||||||
pub prev_kbd_bright: bool,
|
pub prev_kbd_bright: bool,
|
||||||
#[options(meta = "", help = "<20-100>")]
|
#[options(meta = "", help = "Set your battery charge limit <20-100>")]
|
||||||
pub chg_limit: Option<u8>,
|
pub chg_limit: Option<u8>,
|
||||||
#[options(command)]
|
#[options(command)]
|
||||||
pub command: Option<CliCommand>,
|
pub command: Option<CliCommand>,
|
||||||
@@ -30,9 +29,15 @@ 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 = "Create and configure profiles")]
|
#[options(help = "Set the LED power states")]
|
||||||
|
LedPow1(LedPowerCommand1),
|
||||||
|
#[options(help = "Set the LED power states")]
|
||||||
|
LedPow2(LedPowerCommand2),
|
||||||
|
#[options(help = "Set or select platform_profile")]
|
||||||
Profile(ProfileCommand),
|
Profile(ProfileCommand),
|
||||||
#[options(help = "Set the graphics mode")]
|
#[options(help = "Set, select, or modify fan curves if supported")]
|
||||||
|
FanCurve(FanCurveCommand),
|
||||||
|
#[options(help = "Set the graphics mode (obsoleted by supergfxctl)")]
|
||||||
Graphics(GraphicsCommand),
|
Graphics(GraphicsCommand),
|
||||||
#[options(name = "anime", help = "Manage AniMe Matrix")]
|
#[options(name = "anime", help = "Manage AniMe Matrix")]
|
||||||
Anime(AnimeCommand),
|
Anime(AnimeCommand),
|
||||||
@@ -48,16 +53,6 @@ pub struct LedModeCommand {
|
|||||||
pub next_mode: bool,
|
pub next_mode: bool,
|
||||||
#[options(help = "switch to previous aura mode")]
|
#[options(help = "switch to previous aura mode")]
|
||||||
pub prev_mode: bool,
|
pub prev_mode: bool,
|
||||||
#[options(
|
|
||||||
meta = "",
|
|
||||||
help = "set the keyboard LED to enabled while the device is awake"
|
|
||||||
)]
|
|
||||||
pub awake_enable: Option<bool>,
|
|
||||||
#[options(
|
|
||||||
meta = "",
|
|
||||||
help = "set the keyboard LED suspend animation to enabled while the device is suspended"
|
|
||||||
)]
|
|
||||||
pub sleep_enable: Option<bool>,
|
|
||||||
#[options(command)]
|
#[options(command)]
|
||||||
pub command: Option<SetAuraBuiltin>,
|
pub command: Option<SetAuraBuiltin>,
|
||||||
}
|
}
|
||||||
@@ -66,17 +61,6 @@ pub struct LedModeCommand {
|
|||||||
pub struct GraphicsCommand {
|
pub struct GraphicsCommand {
|
||||||
#[options(help = "print help message")]
|
#[options(help = "print help message")]
|
||||||
pub help: bool,
|
pub help: bool,
|
||||||
#[options(
|
|
||||||
meta = "",
|
|
||||||
help = "Set graphics mode: <nvidia, hybrid, compute, integrated>"
|
|
||||||
)]
|
|
||||||
pub mode: Option<GfxVendors>,
|
|
||||||
#[options(help = "Get the current mode")]
|
|
||||||
pub get: bool,
|
|
||||||
#[options(help = "Get the current power status")]
|
|
||||||
pub pow: bool,
|
|
||||||
#[options(help = "Do not ask for confirmation")]
|
|
||||||
pub force: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Options, Debug)]
|
#[derive(Options, Debug)]
|
||||||
@@ -85,18 +69,29 @@ pub struct BiosCommand {
|
|||||||
pub help: bool,
|
pub help: bool,
|
||||||
#[options(
|
#[options(
|
||||||
meta = "",
|
meta = "",
|
||||||
|
short = "S",
|
||||||
no_long,
|
no_long,
|
||||||
help = "set bios POST sound: asusctl -p <true/false>"
|
help = "set bios POST sound: asusctl -p <true/false>"
|
||||||
)]
|
)]
|
||||||
pub post_sound_set: Option<bool>,
|
pub post_sound_set: Option<bool>,
|
||||||
#[options(no_long, help = "read bios POST sound")]
|
#[options(no_long, short = "s", help = "read bios POST sound")]
|
||||||
pub post_sound_get: bool,
|
pub post_sound_get: bool,
|
||||||
#[options(
|
#[options(
|
||||||
meta = "",
|
meta = "",
|
||||||
|
short = "D",
|
||||||
no_long,
|
no_long,
|
||||||
help = "activate dGPU dedicated/G-Sync: asusctl -d <true/false>, reboot required"
|
help = "activate dGPU dedicated/G-Sync: asusctl -d <true/false>, reboot required"
|
||||||
)]
|
)]
|
||||||
pub dedicated_gfx_set: Option<bool>,
|
pub dedicated_gfx_set: Option<bool>,
|
||||||
#[options(no_long, help = "get GPU mode")]
|
#[options(no_long, short = "d", help = "get GPU mode")]
|
||||||
pub dedicated_gfx_get: bool,
|
pub dedicated_gfx_get: bool,
|
||||||
|
#[options(
|
||||||
|
meta = "",
|
||||||
|
short = "O",
|
||||||
|
no_long,
|
||||||
|
help = "Set device panel overdrive <true/false>"
|
||||||
|
)]
|
||||||
|
pub panel_overdrive_set: Option<bool>,
|
||||||
|
#[options(no_long, short = "o", help = "get panel overdrive")]
|
||||||
|
pub panel_overdrive_get: bool,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,33 @@
|
|||||||
mod anime_cli;
|
use std::convert::TryFrom;
|
||||||
mod aura_cli;
|
use std::process::Command;
|
||||||
mod cli_opts;
|
use std::thread::sleep;
|
||||||
mod profiles_cli;
|
use std::{env::args, path::Path};
|
||||||
|
|
||||||
use crate::aura_cli::{LedBrightness, SetAuraBuiltin};
|
use aura_cli::{LedPowerCommand1, LedPowerCommand2};
|
||||||
use crate::cli_opts::*;
|
|
||||||
use anime_cli::{AnimeActions, AnimeCommand};
|
|
||||||
use gumdrop::{Opt, Options};
|
use gumdrop::{Opt, Options};
|
||||||
use profiles_cli::ProfileCommand;
|
|
||||||
use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2, ANIME_DATA_LEN};
|
use anime_cli::{AnimeActions, AnimeCommand};
|
||||||
|
use profiles_cli::{FanCurveCommand, ProfileCommand};
|
||||||
|
use rog_anime::usb::get_anime_type;
|
||||||
|
use rog_anime::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, Vec2};
|
||||||
|
|
||||||
|
use rog_aura::usb::{AuraDev1866, AuraDev19b6, AuraDevTuf, AuraDevice, AuraPowerDev};
|
||||||
use rog_aura::{self, AuraEffect};
|
use rog_aura::{self, AuraEffect};
|
||||||
use rog_dbus::RogDbusClient;
|
use rog_dbus::RogDbusClientBlocking;
|
||||||
|
use rog_profiles::error::ProfileError;
|
||||||
use rog_supported::SupportedFunctions;
|
use rog_supported::SupportedFunctions;
|
||||||
use rog_supported::{
|
use rog_supported::{
|
||||||
AnimeSupportedFunctions, LedSupportedFunctions, PlatformProfileFunctions,
|
AnimeSupportedFunctions, LedSupportedFunctions, PlatformProfileFunctions,
|
||||||
RogBiosSupportedFunctions,
|
RogBiosSupportedFunctions,
|
||||||
};
|
};
|
||||||
use std::{env::args, path::Path, sync::mpsc::channel};
|
|
||||||
use supergfxctl::{
|
|
||||||
gfx_vendors::GfxRequiredUserAction,
|
|
||||||
special::{get_asus_gsync_gfx_mode, has_asus_gsync_gfx_mode},
|
|
||||||
zbus_proxy::GfxProxy,
|
|
||||||
};
|
|
||||||
use zbus::Connection;
|
|
||||||
|
|
||||||
const PLEASE: &str =
|
use crate::aura_cli::LedBrightness;
|
||||||
"Please use `systemctl status asusd` and `journalctl -b -u asusd` for more information";
|
use crate::cli_opts::*;
|
||||||
const CONFIG_ADVICE: &str = "A config file need to be removed so a new one can be generated";
|
|
||||||
|
mod anime_cli;
|
||||||
|
mod aura_cli;
|
||||||
|
mod cli_opts;
|
||||||
|
mod profiles_cli;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let args: Vec<String> = args().skip(1).collect();
|
let args: Vec<String> = args().skip(1).collect();
|
||||||
@@ -49,50 +50,47 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (dbus, _) = RogDbusClient::new().map_err(|e| {
|
let (dbus, _) = RogDbusClientBlocking::new()
|
||||||
println!("\nIs asusd running?\n");
|
.map_err(|e| {
|
||||||
println!("{}", PLEASE);
|
print_error_help(Box::new(e), None);
|
||||||
println!("{}\n", CONFIG_ADVICE);
|
std::process::exit(3);
|
||||||
e
|
})
|
||||||
})?;
|
.unwrap();
|
||||||
|
|
||||||
let supported = dbus
|
let supported = dbus
|
||||||
.proxies()
|
.proxies()
|
||||||
.supported()
|
.supported()
|
||||||
.get_supported_functions()
|
.supported_functions()
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
println!("\nIs asusd running?\n");
|
print_error_help(Box::new(e), None);
|
||||||
println!("{}", PLEASE);
|
std::process::exit(4);
|
||||||
println!("{}\n", CONFIG_ADVICE);
|
})
|
||||||
e
|
.unwrap();
|
||||||
})?;
|
|
||||||
|
|
||||||
if parsed.version {
|
if parsed.version {
|
||||||
print_versions();
|
print_versions();
|
||||||
println!();
|
println!();
|
||||||
print_laptop_info();
|
print_laptop_info();
|
||||||
println!("{}\n", PLEASE);
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(err) = do_parsed(&parsed, &supported, &dbus) {
|
if let Err(err) = do_parsed(&parsed, &supported, &dbus) {
|
||||||
print_error_help(err, &supported);
|
print_error_help(err, Some(&supported));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_error_help(err: Box<dyn std::error::Error>, supported: &SupportedFunctions) {
|
fn print_error_help(err: Box<dyn std::error::Error>, supported: Option<&SupportedFunctions>) {
|
||||||
println!("Error: {}\n", err);
|
check_service("asusd");
|
||||||
|
println!("\nError: {}\n", err);
|
||||||
print_versions();
|
print_versions();
|
||||||
println!();
|
println!();
|
||||||
print_laptop_info();
|
print_laptop_info();
|
||||||
println!();
|
if let Some(supported) = supported {
|
||||||
println!("Supported laptop functions:\n\n{}", supported);
|
println!();
|
||||||
println!();
|
println!("Supported laptop functions:\n\n{}", supported);
|
||||||
println!("{}", PLEASE);
|
}
|
||||||
println!("The above may give some indication that an option is not supported");
|
|
||||||
println!("or that a config file must be removed or fixed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_versions() {
|
fn print_versions() {
|
||||||
@@ -105,7 +103,6 @@ fn print_versions() {
|
|||||||
println!(" rog-dbus v{}", rog_dbus::VERSION);
|
println!(" rog-dbus v{}", rog_dbus::VERSION);
|
||||||
println!(" rog-profiles v{}", rog_profiles::VERSION);
|
println!(" rog-profiles v{}", rog_profiles::VERSION);
|
||||||
println!("rog-supported v{}", rog_supported::VERSION);
|
println!("rog-supported v{}", rog_supported::VERSION);
|
||||||
println!(" supergfxctl v{}", supergfxctl::VERSION);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_laptop_info() {
|
fn print_laptop_info() {
|
||||||
@@ -117,15 +114,37 @@ fn print_laptop_info() {
|
|||||||
println!("Board name: {}", board_name.trim());
|
println!("Board name: {}", board_name.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_service(name: &str) -> bool {
|
||||||
|
if name != "asusd" && !check_systemd_unit_enabled(name) {
|
||||||
|
println!(
|
||||||
|
"\n\x1b[0;31m{} is not enabled, enable it with `systemctl enable {}\x1b[0m",
|
||||||
|
name, name
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} else if !check_systemd_unit_active(name) {
|
||||||
|
println!(
|
||||||
|
"\n\x1b[0;31m{} is not running, start it with `systemctl start {}\x1b[0m",
|
||||||
|
name, name
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn do_parsed(
|
fn do_parsed(
|
||||||
parsed: &CliStart,
|
parsed: &CliStart,
|
||||||
supported: &SupportedFunctions,
|
supported: &SupportedFunctions,
|
||||||
dbus: &RogDbusClient,
|
dbus: &RogDbusClientBlocking,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
match &parsed.command {
|
match &parsed.command {
|
||||||
Some(CliCommand::LedMode(mode)) => handle_led_mode(dbus, &supported.keyboard_led, mode)?,
|
Some(CliCommand::LedMode(mode)) => handle_led_mode(dbus, &supported.keyboard_led, mode)?,
|
||||||
|
Some(CliCommand::LedPow1(pow)) => handle_led_power1(dbus, &supported.keyboard_led, pow)?,
|
||||||
|
Some(CliCommand::LedPow2(pow)) => handle_led_power2(dbus, &supported.keyboard_led, pow)?,
|
||||||
Some(CliCommand::Profile(cmd)) => handle_profile(dbus, &supported.platform_profile, cmd)?,
|
Some(CliCommand::Profile(cmd)) => handle_profile(dbus, &supported.platform_profile, cmd)?,
|
||||||
Some(CliCommand::Graphics(cmd)) => do_gfx(cmd)?,
|
Some(CliCommand::FanCurve(cmd)) => {
|
||||||
|
handle_fan_curve(dbus, &supported.platform_profile, cmd)?
|
||||||
|
}
|
||||||
|
Some(CliCommand::Graphics(_)) => do_gfx()?,
|
||||||
Some(CliCommand::Anime(cmd)) => handle_anime(dbus, &supported.anime_ctrl, cmd)?,
|
Some(CliCommand::Anime(cmd)) => handle_anime(dbus, &supported.anime_ctrl, cmd)?,
|
||||||
Some(CliCommand::Bios(cmd)) => handle_bios_option(dbus, &supported.rog_bios_ctrl, cmd)?,
|
Some(CliCommand::Bios(cmd)) => handle_bios_option(dbus, &supported.rog_bios_ctrl, cmd)?,
|
||||||
None => {
|
None => {
|
||||||
@@ -139,8 +158,32 @@ fn do_parsed(
|
|||||||
println!("{}", CliStart::usage());
|
println!("{}", CliStart::usage());
|
||||||
println!();
|
println!();
|
||||||
if let Some(cmdlist) = CliStart::command_list() {
|
if let Some(cmdlist) = CliStart::command_list() {
|
||||||
println!("{}", cmdlist);
|
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_string()).collect();
|
||||||
|
for command in commands.iter().filter(|command| {
|
||||||
|
if !matches!(
|
||||||
|
supported.keyboard_led.prod_id,
|
||||||
|
AuraDevice::X1854
|
||||||
|
| AuraDevice::X1869
|
||||||
|
| AuraDevice::X1866
|
||||||
|
| AuraDevice::Tuf
|
||||||
|
) && command.trim().starts_with("led-pow-1")
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if supported.keyboard_led.prod_id != AuraDevice::X19B6
|
||||||
|
&& command.trim().starts_with("led-pow-2")
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}) {
|
||||||
|
println!("{}", command);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("\nExtra help can be requested on any command or subcommand:");
|
||||||
|
println!(" asusctl led-mode --help");
|
||||||
|
println!(" asusctl led-mode static --help");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,13 +191,13 @@ fn do_parsed(
|
|||||||
if let Some(brightness) = &parsed.kbd_bright {
|
if let Some(brightness) = &parsed.kbd_bright {
|
||||||
match brightness.level() {
|
match brightness.level() {
|
||||||
None => {
|
None => {
|
||||||
let level = dbus.proxies().led().get_led_brightness()?;
|
let level = dbus.proxies().led().led_brightness()?;
|
||||||
println!("Current keyboard led brightness: {}", level.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))?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,103 +214,54 @@ 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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_gfx(command: &GraphicsCommand) -> Result<(), Box<dyn std::error::Error>> {
|
fn do_gfx() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
if command.mode.is_none() && !command.get && !command.pow && !command.force || command.help {
|
println!("Please use supergfxctl for graphics switching. supergfxctl is the result of making asusctl graphics switching generic so all laptops can use it");
|
||||||
println!("{}", command.self_usage());
|
println!("This command will be removed in future");
|
||||||
}
|
|
||||||
|
|
||||||
let conn = Connection::new_system()?;
|
|
||||||
let proxy = GfxProxy::new(&conn)?;
|
|
||||||
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
proxy.connect_notify_action(tx)?;
|
|
||||||
|
|
||||||
if let Some(mode) = command.mode {
|
|
||||||
if has_asus_gsync_gfx_mode() && get_asus_gsync_gfx_mode()? == 1 {
|
|
||||||
println!("You can not change modes until you turn dedicated/G-Sync off and reboot");
|
|
||||||
std::process::exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("If anything fails check `journalctl -b -u asusd`\n");
|
|
||||||
|
|
||||||
proxy.gfx_write_mode(&mode).map_err(|err|{
|
|
||||||
println!("Graphics mode change error. You may be in an invalid state.");
|
|
||||||
println!("Check mode with `asusctl graphics -g` and switch to opposite\nmode to correct it, e.g: if integrated, switch to hybrid, or if nvidia, switch to integrated.\n");
|
|
||||||
err
|
|
||||||
})?;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
proxy.next_signal()?;
|
|
||||||
|
|
||||||
if let Ok(res) = rx.try_recv() {
|
|
||||||
match res {
|
|
||||||
GfxRequiredUserAction::Integrated => {
|
|
||||||
println!(
|
|
||||||
"You must change to Integrated before you can change to {}",
|
|
||||||
<&str>::from(mode)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
GfxRequiredUserAction::Logout | GfxRequiredUserAction::Reboot => {
|
|
||||||
println!(
|
|
||||||
"Graphics mode changed to {}. User action required is: {}",
|
|
||||||
<&str>::from(mode),
|
|
||||||
<&str>::from(&res)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
GfxRequiredUserAction::None => {
|
|
||||||
println!("Graphics mode changed to {}", <&str>::from(mode));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::process::exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if command.get {
|
|
||||||
let res = proxy.gfx_get_mode()?;
|
|
||||||
println!("Current graphics mode: {}", <&str>::from(res));
|
|
||||||
}
|
|
||||||
if command.pow {
|
|
||||||
let res = proxy.gfx_get_pwr()?;
|
|
||||||
println!("Current power status: {}", <&str>::from(&res));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
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 cmd.clear {
|
||||||
|
let anime_type = get_anime_type()?;
|
||||||
|
let data = vec![0u8; anime_type.data_length()];
|
||||||
|
let tmp = AnimeDataBuffer::from_vec(anime_type, data)?;
|
||||||
|
dbus.proxies().anime().write(tmp)?;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(action) = cmd.command.as_ref() {
|
if let Some(action) = cmd.command.as_ref() {
|
||||||
|
let anime_type = get_anime_type()?;
|
||||||
match action {
|
match action {
|
||||||
AnimeActions::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());
|
||||||
@@ -276,6 +270,7 @@ fn handle_anime(
|
|||||||
}
|
}
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
verify_brightness(image.bright);
|
||||||
|
|
||||||
let matrix = AnimeImage::from_png(
|
let matrix = AnimeImage::from_png(
|
||||||
Path::new(&image.path),
|
Path::new(&image.path),
|
||||||
@@ -283,28 +278,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>::try_from(&matrix)?)?;
|
||||||
|
}
|
||||||
|
AnimeActions::PixelImage(image) => {
|
||||||
|
if image.help_requested() || image.path.is_empty() {
|
||||||
|
println!("Missing arg or command\n\n{}", image.self_usage());
|
||||||
|
if 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");
|
||||||
}
|
}
|
||||||
@@ -315,11 +402,14 @@ fn handle_led_mode(
|
|||||||
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_string()).collect();
|
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_string()).collect();
|
||||||
for command in commands.iter().filter(|command| {
|
for command in commands.iter().filter(|command| {
|
||||||
for mode in &supported.stock_led_modes {
|
for mode in &supported.stock_led_modes {
|
||||||
if command.contains(&<&str>::from(mode).to_lowercase()) {
|
if command
|
||||||
|
.trim()
|
||||||
|
.starts_with(&<&str>::from(mode).to_lowercase())
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if supported.multizone_led_mode {
|
if !supported.multizone_led_mode.is_empty() && command.trim().starts_with("multi") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
@@ -345,74 +435,324 @@ fn handle_led_mode(
|
|||||||
println!("{}", mode.self_usage());
|
println!("{}", mode.self_usage());
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
match mode {
|
dbus.proxies()
|
||||||
SetAuraBuiltin::MultiStatic(_) | SetAuraBuiltin::MultiBreathe(_) => {
|
.led()
|
||||||
let zones = <Vec<AuraEffect>>::from(mode);
|
.set_led_mode(&<AuraEffect>::from(mode))?;
|
||||||
for eff in zones {
|
}
|
||||||
dbus.proxies().led().set_led_mode(&eff)?
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_led_power1(
|
||||||
|
dbus: &RogDbusClientBlocking,
|
||||||
|
supported: &LedSupportedFunctions,
|
||||||
|
power: &LedPowerCommand1,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
if power.awake.is_none()
|
||||||
|
&& power.sleep.is_none()
|
||||||
|
&& power.boot.is_none()
|
||||||
|
&& power.keyboard.is_none()
|
||||||
|
&& power.lightbar.is_none()
|
||||||
|
{
|
||||||
|
if !power.help {
|
||||||
|
println!("Missing arg or command\n");
|
||||||
|
}
|
||||||
|
println!("{}\n", power.self_usage());
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches!(
|
||||||
|
supported.prod_id,
|
||||||
|
AuraDevice::X1854 | AuraDevice::X1869 | AuraDevice::X1866
|
||||||
|
) {
|
||||||
|
handle_led_power_1_do_1866(dbus, power)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches!(supported.prod_id, AuraDevice::Tuf) {
|
||||||
|
handle_led_power_1_do_tuf(dbus, power)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("These options are for keyboards of product ID 0x1866 or TUF only");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_led_power_1_do_1866(
|
||||||
|
dbus: &RogDbusClientBlocking,
|
||||||
|
power: &LedPowerCommand1,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let mut enabled: Vec<AuraDev1866> = Vec::new();
|
||||||
|
let mut disabled: Vec<AuraDev1866> = Vec::new();
|
||||||
|
|
||||||
|
let mut check = |e: Option<bool>, a: AuraDev1866| {
|
||||||
|
if let Some(arg) = e {
|
||||||
|
if arg {
|
||||||
|
enabled.push(a);
|
||||||
|
} else {
|
||||||
|
disabled.push(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
check(power.awake, AuraDev1866::Awake);
|
||||||
|
check(power.boot, AuraDev1866::Boot);
|
||||||
|
check(power.sleep, AuraDev1866::Sleep);
|
||||||
|
check(power.keyboard, AuraDev1866::Keyboard);
|
||||||
|
check(power.lightbar, AuraDev1866::Lightbar);
|
||||||
|
|
||||||
|
let data = AuraPowerDev {
|
||||||
|
x1866: enabled,
|
||||||
|
x19b6: vec![],
|
||||||
|
tuf: vec![],
|
||||||
|
};
|
||||||
|
dbus.proxies().led().set_leds_power(data, true)?;
|
||||||
|
|
||||||
|
let data = AuraPowerDev {
|
||||||
|
x1866: disabled,
|
||||||
|
x19b6: vec![],
|
||||||
|
tuf: vec![],
|
||||||
|
};
|
||||||
|
dbus.proxies().led().set_leds_power(data, false)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_led_power_1_do_tuf(
|
||||||
|
dbus: &RogDbusClientBlocking,
|
||||||
|
power: &LedPowerCommand1,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let mut enabled: Vec<AuraDevTuf> = Vec::new();
|
||||||
|
let mut disabled: Vec<AuraDevTuf> = Vec::new();
|
||||||
|
|
||||||
|
let mut check = |e: Option<bool>, a: AuraDevTuf| {
|
||||||
|
if let Some(arg) = e {
|
||||||
|
if arg {
|
||||||
|
enabled.push(a);
|
||||||
|
} else {
|
||||||
|
disabled.push(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
check(power.awake, AuraDevTuf::Awake);
|
||||||
|
check(power.boot, AuraDevTuf::Boot);
|
||||||
|
check(power.sleep, AuraDevTuf::Sleep);
|
||||||
|
check(power.keyboard, AuraDevTuf::Keyboard);
|
||||||
|
|
||||||
|
let data = AuraPowerDev {
|
||||||
|
x1866: vec![],
|
||||||
|
x19b6: vec![],
|
||||||
|
tuf: enabled,
|
||||||
|
};
|
||||||
|
dbg!(&data);
|
||||||
|
dbus.proxies().led().set_leds_power(data, true)?;
|
||||||
|
|
||||||
|
let data = AuraPowerDev {
|
||||||
|
x1866: vec![],
|
||||||
|
x19b6: vec![],
|
||||||
|
tuf: disabled,
|
||||||
|
};
|
||||||
|
dbus.proxies().led().set_leds_power(data, false)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_led_power2(
|
||||||
|
dbus: &RogDbusClientBlocking,
|
||||||
|
supported: &LedSupportedFunctions,
|
||||||
|
power: &LedPowerCommand2,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
if power.command().is_none() {
|
||||||
|
if !power.help {
|
||||||
|
println!("Missing arg or command\n");
|
||||||
|
}
|
||||||
|
println!("{}\n", power.self_usage());
|
||||||
|
println!("Commands available");
|
||||||
|
|
||||||
|
if let Some(cmdlist) = LedPowerCommand2::command_list() {
|
||||||
|
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_string()).collect();
|
||||||
|
for command in commands.iter() {
|
||||||
|
println!("{}", command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("\nHelp can also be requested on commands, e.g: boot --help");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(pow) = power.command.as_ref() {
|
||||||
|
if pow.help_requested() {
|
||||||
|
println!("{}", pow.self_usage());
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if supported.prod_id != AuraDevice::X19B6 {
|
||||||
|
println!("This option applies only to keyboards with product ID 0x19b6")
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut enabled: Vec<AuraDev19b6> = Vec::new();
|
||||||
|
let mut disabled: Vec<AuraDev19b6> = Vec::new();
|
||||||
|
let mut check = |e: Option<bool>, a: AuraDev19b6| {
|
||||||
|
if let Some(arg) = e {
|
||||||
|
if arg {
|
||||||
|
enabled.push(a);
|
||||||
|
} else {
|
||||||
|
disabled.push(a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => dbus
|
};
|
||||||
.proxies()
|
|
||||||
.led()
|
match pow {
|
||||||
.set_led_mode(&<AuraEffect>::from(mode))?,
|
aura_cli::SetAuraEnabled::Boot(arg) => {
|
||||||
|
check(arg.keyboard, AuraDev19b6::BootKeyb);
|
||||||
|
check(arg.logo, AuraDev19b6::BootLogo);
|
||||||
|
check(arg.lightbar, AuraDev19b6::BootBar);
|
||||||
|
}
|
||||||
|
aura_cli::SetAuraEnabled::Sleep(arg) => {
|
||||||
|
check(arg.keyboard, AuraDev19b6::SleepKeyb);
|
||||||
|
check(arg.logo, AuraDev19b6::SleepLogo);
|
||||||
|
check(arg.lightbar, AuraDev19b6::SleepBar);
|
||||||
|
}
|
||||||
|
aura_cli::SetAuraEnabled::Awake(arg) => {
|
||||||
|
check(arg.keyboard, AuraDev19b6::AwakeKeyb);
|
||||||
|
check(arg.logo, AuraDev19b6::AwakeLogo);
|
||||||
|
check(arg.lightbar, AuraDev19b6::AwakeBar);
|
||||||
|
}
|
||||||
|
aura_cli::SetAuraEnabled::Shutdown(arg) => {
|
||||||
|
check(arg.keyboard, AuraDev19b6::ShutdownKeyb);
|
||||||
|
check(arg.logo, AuraDev19b6::ShutdownLogo);
|
||||||
|
check(arg.lightbar, AuraDev19b6::ShutdownBar);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(enable) = mode.awake_enable {
|
if !enabled.is_empty() {
|
||||||
dbus.proxies().led().set_awake_enabled(enable)?;
|
let data = AuraPowerDev {
|
||||||
}
|
tuf: vec![],
|
||||||
|
x1866: vec![],
|
||||||
|
x19b6: enabled,
|
||||||
|
};
|
||||||
|
dbus.proxies().led().set_leds_power(data, true)?;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(enable) = mode.sleep_enable {
|
if !disabled.is_empty() {
|
||||||
dbus.proxies().led().set_sleep_enabled(enable)?;
|
let data = AuraPowerDev {
|
||||||
|
tuf: vec![],
|
||||||
|
x1866: vec![],
|
||||||
|
x19b6: disabled,
|
||||||
|
};
|
||||||
|
dbus.proxies().led().set_leds_power(data, false)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_profile(
|
fn handle_profile(
|
||||||
dbus: &RogDbusClient,
|
dbus: &RogDbusClientBlocking,
|
||||||
supported: &PlatformProfileFunctions,
|
supported: &PlatformProfileFunctions,
|
||||||
cmd: &ProfileCommand,
|
cmd: &ProfileCommand,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
println!("Warning: Profiles should work fine but now depend on power-profiles-daemon v0.9+");
|
if !supported.platform_profile {
|
||||||
println!("Warning: Fan-curve support is coming in a 4.1.x release");
|
println!("Profiles not supported by either this kernel or by the laptop.");
|
||||||
if !cmd.next && !cmd.list {
|
return Err(ProfileError::NotSupported.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
}
|
}
|
||||||
let usage: Vec<String> = ProfileCommand::usage()
|
println!("{}", ProfileCommand::usage());
|
||||||
.lines()
|
|
||||||
.map(|s| s.to_string())
|
|
||||||
.collect();
|
|
||||||
for line in usage
|
|
||||||
.iter()
|
|
||||||
.filter(|line| !line.contains("--curve") || supported.fan_curves)
|
|
||||||
{
|
|
||||||
println!("{}", line);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(lst) = cmd.self_command_list() {
|
if let Some(lst) = cmd.self_command_list() {
|
||||||
println!("\n{}", lst);
|
println!("\n{}", lst);
|
||||||
}
|
}
|
||||||
|
|
||||||
// println!("Note: turbo, frequency, fan preset and fan curve options will apply to");
|
|
||||||
// println!(" to the currently active profile unless a profile name is specified");
|
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.next {
|
if cmd.next {
|
||||||
dbus.proxies().profile().next_profile()?;
|
dbus.proxies().profile().next_profile()?;
|
||||||
|
} else if let Some(profile) = cmd.profile_set {
|
||||||
|
dbus.proxies().profile().set_active_profile(profile)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.list {
|
if cmd.list {
|
||||||
let res = dbus.proxies().profile().profiles()?;
|
let res = dbus.proxies().profile().profiles()?;
|
||||||
res.iter().for_each(|p| println!("{:?}", p));
|
res.iter().for_each(|p| println!("{:?}", p));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cmd.profile_get {
|
||||||
|
let res = dbus.proxies().profile().active_profile()?;
|
||||||
|
println!("Active profile is {:?}", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_fan_curve(
|
||||||
|
dbus: &RogDbusClientBlocking,
|
||||||
|
supported: &PlatformProfileFunctions,
|
||||||
|
cmd: &FanCurveCommand,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
if !supported.fan_curves {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.get_enabled && !cmd.default && cmd.mod_profile.is_none() {
|
||||||
|
if !cmd.help {
|
||||||
|
println!("Missing arg or command\n");
|
||||||
|
}
|
||||||
|
println!("{}", FanCurveCommand::usage());
|
||||||
|
|
||||||
|
if let Some(lst) = cmd.self_command_list() {
|
||||||
|
println!("\n{}", lst);
|
||||||
|
}
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd.enabled.is_some() || cmd.fan.is_some() || cmd.data.is_some())
|
||||||
|
&& cmd.mod_profile.is_none()
|
||||||
|
{
|
||||||
|
println!("--enabled, --fan, and --data options require --mod-profile");
|
||||||
|
std::process::exit(666);
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.get_enabled {
|
||||||
|
let res = dbus.proxies().profile().enabled_fan_profiles()?;
|
||||||
|
println!("{:?}", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.default {
|
||||||
|
dbus.proxies().profile().set_active_curve_to_defaults()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(profile) = cmd.mod_profile {
|
||||||
|
if cmd.enabled.is_none() && cmd.data.is_none() {
|
||||||
|
let data = dbus.proxies().profile().fan_curve_data(profile)?;
|
||||||
|
let data = toml::to_string(&data)?;
|
||||||
|
println!("\nFan curves for {:?}\n\n{}", profile, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(enabled) = cmd.enabled {
|
||||||
|
dbus.proxies()
|
||||||
|
.profile()
|
||||||
|
.set_fan_curve_enabled(profile, enabled)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mut curve) = cmd.data.clone() {
|
||||||
|
let fan = cmd.fan.unwrap_or_default();
|
||||||
|
curve.set_fan(fan);
|
||||||
|
dbus.proxies().profile().set_fan_curve(profile, curve)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
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>> {
|
||||||
@@ -420,7 +760,9 @@ fn handle_bios_option(
|
|||||||
if (cmd.dedicated_gfx_set.is_none()
|
if (cmd.dedicated_gfx_set.is_none()
|
||||||
&& !cmd.dedicated_gfx_get
|
&& !cmd.dedicated_gfx_get
|
||||||
&& cmd.post_sound_set.is_none()
|
&& cmd.post_sound_set.is_none()
|
||||||
&& !cmd.post_sound_get)
|
&& !cmd.post_sound_get
|
||||||
|
&& cmd.panel_overdrive_set.is_none()
|
||||||
|
&& !cmd.panel_overdrive_get)
|
||||||
|| cmd.help
|
|| cmd.help
|
||||||
{
|
{
|
||||||
println!("Missing arg or command\n");
|
println!("Missing arg or command\n");
|
||||||
@@ -431,23 +773,25 @@ fn handle_bios_option(
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
for line in usage.iter().filter(|line| {
|
for line in usage.iter().filter(|line| {
|
||||||
!line.contains("sound") && !supported.post_sound_toggle
|
line.contains("sound") && supported.post_sound
|
||||||
|| !line.contains("GPU") && !supported.dedicated_gfx_toggle
|
|| line.contains("GPU") && supported.dedicated_gfx
|
||||||
|
|| line.contains("panel") && supported.panel_overdrive
|
||||||
}) {
|
}) {
|
||||||
println!("{}", line);
|
println!("{}", line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(opt) = cmd.post_sound_set {
|
if let Some(opt) = cmd.post_sound_set {
|
||||||
dbus.proxies().rog_bios().set_post_sound(opt)?;
|
dbus.proxies().rog_bios().set_post_boot_sound(opt)?;
|
||||||
}
|
}
|
||||||
if cmd.post_sound_get {
|
if cmd.post_sound_get {
|
||||||
let res = dbus.proxies().rog_bios().get_post_sound()? == 1;
|
let res = dbus.proxies().rog_bios().post_boot_sound()? == 1;
|
||||||
println!("Bios POST sound on: {}", res);
|
println!("Bios POST sound on: {}", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(opt) = cmd.dedicated_gfx_set {
|
if let Some(opt) = cmd.dedicated_gfx_set {
|
||||||
println!("Rebuilding initrd to include drivers");
|
println!("Rebuilding initrd to include drivers");
|
||||||
dbus.proxies().rog_bios().set_dedicated_gfx(opt)?;
|
dbus.proxies().rog_bios().set_dedicated_graphic_mode(opt)?;
|
||||||
println!("The mode change is not active until you reboot, on boot the bios will make the required change");
|
println!("The mode change is not active until you reboot, on boot the bios will make the required change");
|
||||||
if opt {
|
if opt {
|
||||||
println!(
|
println!(
|
||||||
@@ -458,9 +802,41 @@ fn handle_bios_option(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cmd.dedicated_gfx_get {
|
if cmd.dedicated_gfx_get {
|
||||||
let res = dbus.proxies().rog_bios().get_dedicated_gfx()? == 1;
|
let res = dbus.proxies().rog_bios().dedicated_graphic_mode()? == 1;
|
||||||
println!("Bios dedicated GPU on: {}", res);
|
println!("Bios dedicated GPU on: {}", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(opt) = cmd.panel_overdrive_set {
|
||||||
|
dbus.proxies().rog_bios().set_panel_overdrive(opt)?;
|
||||||
|
}
|
||||||
|
if cmd.panel_overdrive_get {
|
||||||
|
let res = dbus.proxies().rog_bios().panel_overdrive()? == 1;
|
||||||
|
println!("Panel overdrive on: {}", res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_systemd_unit_active(name: &str) -> bool {
|
||||||
|
if let Ok(out) = Command::new("systemctl")
|
||||||
|
.arg("is-active")
|
||||||
|
.arg(name)
|
||||||
|
.output()
|
||||||
|
{
|
||||||
|
let buf = String::from_utf8_lossy(&out.stdout);
|
||||||
|
return !buf.contains("inactive") && !buf.contains("failed");
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_systemd_unit_enabled(name: &str) -> bool {
|
||||||
|
if let Ok(out) = Command::new("systemctl")
|
||||||
|
.arg("is-enabled")
|
||||||
|
.arg(name)
|
||||||
|
.output()
|
||||||
|
{
|
||||||
|
let buf = String::from_utf8_lossy(&out.stdout);
|
||||||
|
return buf.contains("enabled");
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use gumdrop::Options;
|
use gumdrop::Options;
|
||||||
|
use rog_profiles::{fan_curve_set::CurveData, FanCurvePU, Profile};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Options)]
|
#[derive(Debug, Clone, Options)]
|
||||||
pub struct ProfileCommand {
|
pub struct ProfileCommand {
|
||||||
@@ -8,4 +9,43 @@ pub struct ProfileCommand {
|
|||||||
pub next: bool,
|
pub next: bool,
|
||||||
#[options(help = "list available profiles")]
|
#[options(help = "list available profiles")]
|
||||||
pub list: bool,
|
pub list: bool,
|
||||||
|
|
||||||
|
#[options(help = "get profile")]
|
||||||
|
pub profile_get: bool,
|
||||||
|
#[options(meta = "", help = "set the active profile")]
|
||||||
|
pub profile_set: Option<Profile>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Options)]
|
||||||
|
pub struct FanCurveCommand {
|
||||||
|
#[options(help = "print help message")]
|
||||||
|
pub help: bool,
|
||||||
|
|
||||||
|
#[options(help = "get enabled fan profiles")]
|
||||||
|
pub get_enabled: bool,
|
||||||
|
#[options(help = "set the active profile's fan curve to default")]
|
||||||
|
pub default: bool,
|
||||||
|
|
||||||
|
#[options(
|
||||||
|
meta = "",
|
||||||
|
help = "profile to modify fan-curve for. Shows data if no options provided"
|
||||||
|
)]
|
||||||
|
pub mod_profile: Option<Profile>,
|
||||||
|
#[options(
|
||||||
|
meta = "",
|
||||||
|
help = "enable or disable <true/false> fan curve. `mod-profile` required"
|
||||||
|
)]
|
||||||
|
pub enabled: Option<bool>,
|
||||||
|
#[options(
|
||||||
|
meta = "",
|
||||||
|
help = "select fan <cpu/gpu> to modify. `mod-profile` required"
|
||||||
|
)]
|
||||||
|
pub fan: Option<FanCurvePU>,
|
||||||
|
|
||||||
|
#[options(
|
||||||
|
meta = "",
|
||||||
|
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>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)?;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "daemon"
|
name = "daemon"
|
||||||
version = "4.0.0"
|
version = "4.3.3"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = ["Luke <luke@ljones.dev>"]
|
authors = ["Luke <luke@ljones.dev>"]
|
||||||
@@ -18,22 +18,25 @@ name = "asusd"
|
|||||||
path = "src/daemon.rs"
|
path = "src/daemon.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rog_anime = { path = "../rog-anime" }
|
rog_anime = { path = "../rog-anime", features = ["dbus"] }
|
||||||
rog_aura = { path = "../rog-aura" }
|
rog_aura = { path = "../rog-aura", features = ["dbus"] }
|
||||||
rog_supported = { path = "../rog-supported" }
|
rog_supported = { path = "../rog-supported" }
|
||||||
rog_profiles = { path = "../rog-profiles" }
|
rog_profiles = { path = "../rog-profiles" }
|
||||||
rog_dbus = { path = "../rog-dbus" }
|
rog_dbus = { path = "../rog-dbus" }
|
||||||
rusb = "^0.8"
|
|
||||||
|
async-trait = "^0.1"
|
||||||
|
smol = "^1.2"
|
||||||
|
|
||||||
|
rusb = "^0.9"
|
||||||
udev = "^0.6"
|
udev = "^0.6"
|
||||||
|
|
||||||
# cli and logging
|
# cli and logging
|
||||||
log = "^0.4"
|
log = "^0.4"
|
||||||
env_logger = "^0.8"
|
env_logger = "^0.9"
|
||||||
|
|
||||||
zbus = "^1.9.1"
|
zbus = "^2.2"
|
||||||
zvariant = "^2.6"
|
zvariant = "^3.2"
|
||||||
zvariant_derive = { version = "^2.6" }
|
logind-zbus = { version = "^3.0" } #, default-features = false, features = ["non_blocking"] }
|
||||||
logind-zbus = "^0.7.1"
|
|
||||||
|
|
||||||
# serialisation
|
# serialisation
|
||||||
serde = "^1.0"
|
serde = "^1.0"
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ use log::{error, warn};
|
|||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
|
pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize, Default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
/// Save charge limit for restoring on boot
|
/// Save charge limit for restoring on boot
|
||||||
pub bat_charge_limit: u8,
|
pub bat_charge_limit: u8,
|
||||||
@@ -24,8 +25,8 @@ impl Config {
|
|||||||
.read(true)
|
.read(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.create(true)
|
.create(true)
|
||||||
.open(&CONFIG_PATH)
|
.open(&PathBuf::from(CONFIG_PATH))
|
||||||
.unwrap_or_else(|_| panic!("The directory /etc/asusd/ is missing")); // okay to cause panic here
|
.unwrap_or_else(|e| panic!("Error opening {}, {}", CONFIG_PATH, e)); // okay to cause panic here
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
let config;
|
let config;
|
||||||
if let Ok(read_len) = file.read_to_string(&mut buf) {
|
if let Ok(read_len) = file.read_to_string(&mut buf) {
|
||||||
@@ -34,8 +35,18 @@ impl Config {
|
|||||||
} else if let Ok(data) = serde_json::from_str(&buf) {
|
} else if let Ok(data) = serde_json::from_str(&buf) {
|
||||||
config = data;
|
config = data;
|
||||||
} else {
|
} else {
|
||||||
warn!("Could not deserialise {}", CONFIG_PATH);
|
warn!(
|
||||||
panic!("Please remove {} then restart asusd", CONFIG_PATH);
|
"Could not deserialise {}.\nWill rename to {}-old and recreate config",
|
||||||
|
CONFIG_PATH, CONFIG_PATH
|
||||||
|
);
|
||||||
|
let cfg_old = CONFIG_PATH.to_string() + "-old";
|
||||||
|
std::fs::rename(CONFIG_PATH, cfg_old).unwrap_or_else(|err| {
|
||||||
|
panic!(
|
||||||
|
"Could not rename. Please remove {} then restart service: Error {}",
|
||||||
|
CONFIG_PATH, err
|
||||||
|
)
|
||||||
|
});
|
||||||
|
config = Self::new();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
config = Self::new()
|
config = Self::new()
|
||||||
|
|||||||
@@ -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,29 +157,48 @@ 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;
|
||||||
}
|
}
|
||||||
AnimeConfig::write_backup(buf);
|
|
||||||
warn!(
|
warn!(
|
||||||
"Could not deserialise {}. Backed up as *-old",
|
"Could not deserialise {}.\nWill rename to {}-old and recreate config",
|
||||||
ANIME_CONFIG_PATH
|
ANIME_CONFIG_PATH, ANIME_CONFIG_PATH
|
||||||
);
|
);
|
||||||
|
let cfg_old = ANIME_CONFIG_PATH.to_string() + "-old";
|
||||||
|
std::fs::rename(ANIME_CONFIG_PATH, cfg_old).unwrap_or_else(|err| {
|
||||||
|
panic!(
|
||||||
|
"Could not rename. Please remove {} then restart service: Error {}",
|
||||||
|
ANIME_CONFIG_PATH, err
|
||||||
|
)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 {
|
||||||
@@ -246,12 +269,4 @@ impl AnimeConfig {
|
|||||||
file.write_all(json.as_bytes())
|
file.write_all(json.as_bytes())
|
||||||
.unwrap_or_else(|err| error!("Could not write config: {}", err));
|
.unwrap_or_else(|err| error!("Could not write config: {}", err));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_backup(buf: String) {
|
|
||||||
let mut path = ANIME_CONFIG_PATH.to_string();
|
|
||||||
path.push_str("-old");
|
|
||||||
let mut file = File::create(&path).expect("Couldn't overwrite config");
|
|
||||||
file.write_all(buf.as_bytes())
|
|
||||||
.unwrap_or_else(|err| error!("Could not write config: {}", err));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,21 +2,25 @@ pub mod config;
|
|||||||
pub mod zbus;
|
pub mod zbus;
|
||||||
|
|
||||||
use ::zbus::Connection;
|
use ::zbus::Connection;
|
||||||
|
use async_trait::async_trait;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use logind_zbus::ManagerProxy;
|
use logind_zbus::manager::ManagerProxy;
|
||||||
use rog_anime::{
|
use rog_anime::{
|
||||||
|
error::AnimeError,
|
||||||
usb::{
|
usb::{
|
||||||
pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on, pkts_for_init, PROD_ID,
|
find_node, get_anime_type, pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on,
|
||||||
VENDOR_ID,
|
pkts_for_init, PROD_ID, VENDOR_ID,
|
||||||
},
|
},
|
||||||
ActionData, AnimeDataBuffer, AnimePacketType, ANIME_DATA_LEN,
|
ActionData, AnimeDataBuffer, AnimePacketType, AnimeType,
|
||||||
};
|
};
|
||||||
use rog_supported::AnimeSupportedFunctions;
|
use rog_supported::AnimeSupportedFunctions;
|
||||||
use rusb::{Device, DeviceHandle};
|
use rusb::{Device, DeviceHandle};
|
||||||
|
use smol::{stream::StreamExt, Executor};
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
|
convert::TryFrom,
|
||||||
error::Error,
|
error::Error,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex, MutexGuard},
|
||||||
thread::sleep,
|
thread::sleep,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
@@ -38,6 +42,7 @@ impl GetSupported for CtrlAnime {
|
|||||||
|
|
||||||
pub struct CtrlAnime {
|
pub struct CtrlAnime {
|
||||||
_node: String,
|
_node: String,
|
||||||
|
anime_type: AnimeType,
|
||||||
handle: RefCell<DeviceHandle<rusb::GlobalContext>>,
|
handle: RefCell<DeviceHandle<rusb::GlobalContext>>,
|
||||||
cache: AnimeConfigCached,
|
cache: AnimeConfigCached,
|
||||||
config: AnimeConfig,
|
config: AnimeConfig,
|
||||||
@@ -50,15 +55,17 @@ pub struct CtrlAnime {
|
|||||||
impl CtrlAnime {
|
impl CtrlAnime {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(config: AnimeConfig) -> Result<CtrlAnime, Box<dyn Error>> {
|
pub fn new(config: AnimeConfig) -> Result<CtrlAnime, Box<dyn Error>> {
|
||||||
let node = Self::find_node("193b")?;
|
let node = find_node("193b")?;
|
||||||
|
let anime_type = get_anime_type()?;
|
||||||
let device = Self::get_dev_handle()?;
|
let device = Self::get_dev_handle()?;
|
||||||
|
|
||||||
info!("Device has an AniMe Matrix display");
|
info!("Device has an AniMe Matrix display");
|
||||||
let mut cache = AnimeConfigCached::default();
|
let mut cache = AnimeConfigCached::default();
|
||||||
cache.init_from_config(&config)?;
|
cache.init_from_config(&config, anime_type)?;
|
||||||
|
|
||||||
let ctrl = CtrlAnime {
|
let ctrl = CtrlAnime {
|
||||||
_node: node,
|
_node: node,
|
||||||
|
anime_type,
|
||||||
handle: RefCell::new(device),
|
handle: RefCell::new(device),
|
||||||
cache,
|
cache,
|
||||||
config,
|
config,
|
||||||
@@ -70,34 +77,6 @@ impl CtrlAnime {
|
|||||||
Ok(ctrl)
|
Ok(ctrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_node(id_product: &str) -> Result<String, RogError> {
|
|
||||||
let mut enumerator = udev::Enumerator::new().map_err(|err| {
|
|
||||||
warn!("{}", err);
|
|
||||||
RogError::Udev("enumerator failed".into(), err)
|
|
||||||
})?;
|
|
||||||
enumerator.match_subsystem("usb").map_err(|err| {
|
|
||||||
warn!("{}", err);
|
|
||||||
RogError::Udev("match_subsystem failed".into(), err)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
for device in enumerator.scan_devices().map_err(|err| {
|
|
||||||
warn!("{}", err);
|
|
||||||
RogError::Udev("scan_devices failed".into(), err)
|
|
||||||
})? {
|
|
||||||
if let Some(attr) = device.attribute_value("idProduct") {
|
|
||||||
if attr == id_product {
|
|
||||||
if let Some(dev_node) = device.devnode() {
|
|
||||||
info!("Using device at: {:?} for AniMe control", dev_node);
|
|
||||||
return Ok(dev_node.to_string_lossy().to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(RogError::MissingFunction(
|
|
||||||
"ASUS AniMe device node not found".into(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_dev_handle() -> Result<DeviceHandle<rusb::GlobalContext>, Box<dyn Error>> {
|
fn get_dev_handle() -> Result<DeviceHandle<rusb::GlobalContext>, Box<dyn Error>> {
|
||||||
// We don't expect this ID to ever change
|
// We don't expect this ID to ever change
|
||||||
let device = CtrlAnime::get_device(0x0b05, 0x193b)?;
|
let device = CtrlAnime::get_device(0x0b05, 0x193b)?;
|
||||||
@@ -138,6 +117,7 @@ impl CtrlAnime {
|
|||||||
warn!("AniMe system actions was empty");
|
warn!("AniMe system actions was empty");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop rules:
|
// Loop rules:
|
||||||
// - Lock the mutex **only when required**. That is, the lock must be held for the shortest duration possible.
|
// - Lock the mutex **only when required**. That is, the lock must be held for the shortest duration possible.
|
||||||
// - An AtomicBool used for thread exit should be checked in every loop, including nested
|
// - An AtomicBool used for thread exit should be checked in every loop, including nested
|
||||||
@@ -147,60 +127,72 @@ impl CtrlAnime {
|
|||||||
std::thread::Builder::new()
|
std::thread::Builder::new()
|
||||||
.name("AniMe system thread start".into())
|
.name("AniMe system thread start".into())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
info!("AniMe system thread started");
|
info!("AniMe new system thread started");
|
||||||
// Getting copies of these Atomics is done *in* the thread to ensure
|
// Getting copies of these Atomics is done *in* the thread to ensure
|
||||||
// we don't block other threads/main
|
// we don't block other threads/main
|
||||||
let thread_exit;
|
let thread_exit;
|
||||||
let thread_running;
|
let thread_running;
|
||||||
// First two loops are to ensure we *do* aquire a lock on the mutex
|
let anime_type;
|
||||||
// The reason the loop is required is because the USB writes can block
|
|
||||||
// for up to 10ms. We can't fail to get the atomics.
|
|
||||||
loop {
|
loop {
|
||||||
if let Ok(lock) = inner.try_lock() {
|
if let Ok(lock) = inner.try_lock() {
|
||||||
thread_exit = lock.thread_exit.clone();
|
thread_exit = lock.thread_exit.clone();
|
||||||
thread_running = lock.thread_running.clone();
|
thread_running = lock.thread_running.clone();
|
||||||
// Make any running loop exit first
|
anime_type = lock.anime_type;
|
||||||
thread_exit.store(true, Ordering::SeqCst);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// First two loops are to ensure we *do* aquire a lock on the mutex
|
||||||
|
// The reason the loop is required is because the USB writes can block
|
||||||
|
// for up to 10ms. We can't fail to get the atomics.
|
||||||
|
while thread_running.load(Ordering::SeqCst) {
|
||||||
|
// Make any running loop exit first
|
||||||
|
thread_exit.store(true, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
info!("AniMe no previous system thread running (now)");
|
||||||
// wait for other threads to set not running so we know they exited
|
thread_exit.store(false, Ordering::SeqCst);
|
||||||
if !thread_running.load(Ordering::SeqCst) {
|
|
||||||
thread_exit.store(false, Ordering::SeqCst);
|
|
||||||
info!("AniMe forced a thread to exit");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
'main: loop {
|
'main: loop {
|
||||||
if thread_exit.load(Ordering::SeqCst) {
|
thread_running.store(true, Ordering::SeqCst);
|
||||||
break 'main;
|
|
||||||
}
|
|
||||||
for action in actions.iter() {
|
for action in actions.iter() {
|
||||||
|
if thread_exit.load(Ordering::SeqCst) {
|
||||||
|
break 'main;
|
||||||
|
}
|
||||||
match action {
|
match action {
|
||||||
ActionData::Animation(frames) => {
|
ActionData::Animation(frames) => {
|
||||||
if rog_anime::run_animation(frames, thread_exit.clone(), &|frame| {
|
if let Err(err) = rog_anime::run_animation(frames, &|frame| {
|
||||||
if let Ok(lock) = inner.try_lock() {
|
if thread_exit.load(Ordering::Acquire) {
|
||||||
lock.write_data_buffer(frame);
|
info!("rog-anime: frame-loop was asked to exit");
|
||||||
|
return Ok(true); // Do safe exit
|
||||||
}
|
}
|
||||||
Ok(())
|
inner
|
||||||
})
|
.try_lock()
|
||||||
.map_err(|err| warn!("rog_anime::run_animation: {}", err))
|
.map(|lock| {
|
||||||
.is_err()
|
lock.write_data_buffer(frame)
|
||||||
{
|
.map_err(|err| {
|
||||||
|
warn!(
|
||||||
|
"rog_anime::run_animation:callback {}",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
false // Don't exit yet
|
||||||
|
})
|
||||||
|
.map_err(|err| {
|
||||||
|
warn!("rog_anime::run_animation:callback {}", err);
|
||||||
|
AnimeError::NoFrames
|
||||||
|
})
|
||||||
|
}) {
|
||||||
|
warn!("rog_anime::run_animation:Animation {}", err);
|
||||||
break 'main;
|
break 'main;
|
||||||
};
|
};
|
||||||
|
|
||||||
if thread_exit.load(Ordering::SeqCst) {
|
|
||||||
break 'main;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ActionData::Image(image) => {
|
ActionData::Image(image) => {
|
||||||
once = false;
|
once = false;
|
||||||
if let Ok(lock) = inner.try_lock() {
|
if let Ok(lock) = inner.try_lock() {
|
||||||
lock.write_data_buffer(image.as_ref().clone())
|
lock.write_data_buffer(image.as_ref().clone())
|
||||||
|
.map_err(|e| error!("{}", e))
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ActionData::Pause(duration) => sleep(*duration),
|
ActionData::Pause(duration) => sleep(*duration),
|
||||||
@@ -210,17 +202,27 @@ impl CtrlAnime {
|
|||||||
ActionData::Matrix => {}
|
ActionData::Matrix => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if thread_exit.load(Ordering::SeqCst) {
|
||||||
|
break 'main;
|
||||||
|
}
|
||||||
if once || actions.is_empty() {
|
if once || actions.is_empty() {
|
||||||
break 'main;
|
break 'main;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Clear the display on exit
|
// Clear the display on exit
|
||||||
if let Ok(lock) = inner.try_lock() {
|
if let Ok(lock) = inner.try_lock() {
|
||||||
let data = AnimeDataBuffer::from_vec([0u8; ANIME_DATA_LEN].to_vec());
|
if let Ok(data) =
|
||||||
lock.write_data_buffer(data);
|
AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()])
|
||||||
|
.map_err(|e| error!("{}", e))
|
||||||
|
{
|
||||||
|
lock.write_data_buffer(data)
|
||||||
|
.map_err(|err| {
|
||||||
|
warn!("rog_anime::run_animation:callback {}", err);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Loop ended, set the atmonics
|
// Loop ended, set the atmonics
|
||||||
thread_exit.store(false, Ordering::SeqCst);
|
|
||||||
thread_running.store(false, Ordering::SeqCst);
|
thread_running.store(false, Ordering::SeqCst);
|
||||||
info!("AniMe system thread exited");
|
info!("AniMe system thread exited");
|
||||||
})
|
})
|
||||||
@@ -269,19 +271,20 @@ impl CtrlAnime {
|
|||||||
|
|
||||||
/// Write only a data packet. This will modify the leds brightness using the
|
/// Write only a data packet. This will modify the leds brightness using the
|
||||||
/// global brightness set in config.
|
/// global brightness set in config.
|
||||||
fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) {
|
fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) -> Result<(), RogError> {
|
||||||
for led in buffer.get_mut()[7..].iter_mut() {
|
for led in buffer.data_mut().iter_mut() {
|
||||||
let mut bright = *led as f32 * self.config.brightness;
|
let mut bright = *led as f32 * self.config.brightness;
|
||||||
if bright > 254.0 {
|
if bright > 254.0 {
|
||||||
bright = 254.0;
|
bright = 254.0;
|
||||||
}
|
}
|
||||||
*led = bright as u8;
|
*led = bright as u8;
|
||||||
}
|
}
|
||||||
let data = AnimePacketType::from(buffer);
|
let data = AnimePacketType::try_from(buffer)?;
|
||||||
for row in data.iter() {
|
for row in data.iter() {
|
||||||
self.write_bytes(row);
|
self.write_bytes(row);
|
||||||
}
|
}
|
||||||
self.write_bytes(&pkt_for_flush());
|
self.write_bytes(&pkt_for_flush());
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_initialization(&self) {
|
fn do_initialization(&self) {
|
||||||
@@ -291,89 +294,83 @@ impl CtrlAnime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CtrlAnimeTask<'a> {
|
pub struct CtrlAnimeTask {
|
||||||
inner: Arc<Mutex<CtrlAnime>>,
|
inner: Arc<Mutex<CtrlAnime>>,
|
||||||
_c: Connection,
|
|
||||||
manager: ManagerProxy<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CtrlAnimeTask<'a> {
|
impl CtrlAnimeTask {
|
||||||
pub fn new(inner: Arc<Mutex<CtrlAnime>>) -> Self {
|
pub async fn new(inner: Arc<Mutex<CtrlAnime>>) -> CtrlAnimeTask {
|
||||||
let connection =
|
Self { inner }
|
||||||
Connection::new_system().expect("CtrlAnimeTask could not create dbus connection");
|
|
||||||
|
|
||||||
let manager =
|
|
||||||
ManagerProxy::new(&connection).expect("CtrlAnimeTask could not create ManagerProxy");
|
|
||||||
|
|
||||||
let c1 = inner.clone();
|
|
||||||
// Run this action when the system starts shutting down
|
|
||||||
manager
|
|
||||||
.connect_prepare_for_shutdown(move |shutdown| {
|
|
||||||
if shutdown {
|
|
||||||
'outer: loop {
|
|
||||||
if let Ok(lock) = c1.try_lock() {
|
|
||||||
lock.thread_exit.store(true, Ordering::SeqCst);
|
|
||||||
CtrlAnime::run_thread(c1.clone(), lock.cache.shutdown.clone(), false);
|
|
||||||
break 'outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!("CtrlAnimeTask: new() {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
let c1 = inner.clone();
|
|
||||||
// Run this action when the system wakes up from sleep
|
|
||||||
manager
|
|
||||||
.connect_prepare_for_sleep(move |sleep| {
|
|
||||||
if !sleep {
|
|
||||||
// wait a fraction for things to wake up properly
|
|
||||||
std::thread::sleep(Duration::from_millis(100));
|
|
||||||
'outer: loop {
|
|
||||||
if let Ok(lock) = c1.try_lock() {
|
|
||||||
lock.thread_exit.store(true, Ordering::SeqCst);
|
|
||||||
CtrlAnime::run_thread(c1.clone(), lock.cache.wake.clone(), true);
|
|
||||||
break 'outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!("CtrlAnimeTask: new() {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
inner,
|
|
||||||
_c: connection,
|
|
||||||
manager,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> crate::CtrlTask for CtrlAnimeTask<'a> {
|
#[async_trait]
|
||||||
fn do_task(&self) -> Result<(), RogError> {
|
impl crate::CtrlTask for CtrlAnimeTask {
|
||||||
if let Ok(mut lock) = self.inner.try_lock() {
|
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
|
||||||
// Refresh the config and cache incase the user has edited it
|
let connection = Connection::system()
|
||||||
let config = AnimeConfig::load();
|
.await
|
||||||
lock.cache
|
.expect("CtrlAnimeTask could not create dbus connection");
|
||||||
.init_from_config(&config)
|
|
||||||
.map_err(|err| {
|
let manager = ManagerProxy::new(&connection)
|
||||||
warn!("CtrlAnimeTask: do_task {}", err);
|
.await
|
||||||
err
|
.expect("CtrlAnimeTask could not create ManagerProxy");
|
||||||
})
|
|
||||||
.ok();
|
let run_action =
|
||||||
}
|
|start: bool, lock: MutexGuard<CtrlAnime>, inner: Arc<Mutex<CtrlAnime>>| {
|
||||||
|
if start {
|
||||||
|
info!("CtrlAnimeTask running sleep animation");
|
||||||
|
CtrlAnime::run_thread(inner.clone(), lock.cache.shutdown.clone(), true);
|
||||||
|
} else {
|
||||||
|
info!("CtrlAnimeTask running wake animation");
|
||||||
|
CtrlAnime::run_thread(inner.clone(), lock.cache.wake.clone(), true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let inner = self.inner.clone();
|
||||||
|
executor
|
||||||
|
.spawn(async move {
|
||||||
|
if let Ok(notif) = manager.receive_prepare_for_sleep().await {
|
||||||
|
notif
|
||||||
|
.for_each(|event| {
|
||||||
|
if let Ok(args) = event.args() {
|
||||||
|
// Loop is required to try an attempt to get the mutex *without* blocking
|
||||||
|
// other threads - it is possible to end up with deadlocks otherwise.
|
||||||
|
loop {
|
||||||
|
if let Ok(lock) = inner.clone().try_lock() {
|
||||||
|
run_action(args.start, lock, inner.clone());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
let manager = ManagerProxy::new(&connection)
|
||||||
|
.await
|
||||||
|
.expect("CtrlAnimeTask could not create ManagerProxy");
|
||||||
|
|
||||||
|
let inner = self.inner.clone();
|
||||||
|
executor
|
||||||
|
.spawn(async move {
|
||||||
|
if let Ok(notif) = manager.receive_prepare_for_shutdown().await {
|
||||||
|
notif
|
||||||
|
.for_each(|event| {
|
||||||
|
if let Ok(args) = event.args() {
|
||||||
|
loop {
|
||||||
|
if let Ok(lock) = inner.clone().try_lock() {
|
||||||
|
run_action(args.start, lock, inner.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
// Check for signals on each task iteration, this will run the callbacks
|
|
||||||
// if any signal is recieved
|
|
||||||
self.manager.next_signal()?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use rog_anime::{
|
use rog_anime::{
|
||||||
usb::{pkt_for_apply, pkt_for_set_boot, pkt_for_set_on},
|
usb::{pkt_for_apply, pkt_for_set_boot, pkt_for_set_on},
|
||||||
AnimeDataBuffer, AnimePowerStates,
|
AnimeDataBuffer, AnimePowerStates,
|
||||||
};
|
};
|
||||||
use zbus::dbus_interface;
|
use zbus::{dbus_interface, Connection, SignalContext};
|
||||||
use zvariant::ObjectPath;
|
|
||||||
|
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
@@ -15,18 +15,10 @@ use super::CtrlAnime;
|
|||||||
pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>);
|
pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>);
|
||||||
|
|
||||||
/// The struct with the main dbus methods requires this trait
|
/// The struct with the main dbus methods requires this trait
|
||||||
|
#[async_trait]
|
||||||
impl crate::ZbusAdd for CtrlAnimeZbus {
|
impl crate::ZbusAdd for CtrlAnimeZbus {
|
||||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
async fn add_to_server(self, server: &mut Connection) {
|
||||||
server
|
Self::add_to_server_helper(self, "/org/asuslinux/Anime", server).await;
|
||||||
.at(
|
|
||||||
&ObjectPath::from_str_unchecked("/org/asuslinux/Anime"),
|
|
||||||
self,
|
|
||||||
)
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!("CtrlAnimeDisplay: add_to_server {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,14 +28,18 @@ impl crate::ZbusAdd for CtrlAnimeZbus {
|
|||||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||||
impl CtrlAnimeZbus {
|
impl CtrlAnimeZbus {
|
||||||
/// Writes a data stream of length. Will force system thread to exit until it is restarted
|
/// Writes a data stream of length. Will force system thread to exit until it is restarted
|
||||||
fn write(&self, input: AnimeDataBuffer) {
|
fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> {
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
if let Ok(lock) = self.0.try_lock() {
|
if let Ok(lock) = self.0.try_lock() {
|
||||||
lock.thread_exit.store(true, Ordering::SeqCst);
|
lock.thread_exit.store(true, Ordering::SeqCst);
|
||||||
lock.write_data_buffer(input);
|
lock.write_data_buffer(input).map_err(|err| {
|
||||||
|
warn!("rog_anime::run_animation:callback {}", err);
|
||||||
|
err
|
||||||
|
})?;
|
||||||
break 'outer;
|
break 'outer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the global AniMe brightness
|
/// Set the global AniMe brightness
|
||||||
@@ -53,8 +49,8 @@ impl CtrlAnimeZbus {
|
|||||||
let mut bright = bright;
|
let mut bright = bright;
|
||||||
if bright < 0.0 {
|
if bright < 0.0 {
|
||||||
bright = 0.0
|
bright = 0.0
|
||||||
} else if bright > 254.0 {
|
} else if bright > 1.0 {
|
||||||
bright = 254.0;
|
bright = 1.0;
|
||||||
}
|
}
|
||||||
lock.config.brightness = bright;
|
lock.config.brightness = bright;
|
||||||
lock.config.write();
|
lock.config.write();
|
||||||
@@ -64,26 +60,30 @@ impl CtrlAnimeZbus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set whether the AniMe is displaying images/data
|
/// Set whether the AniMe is displaying images/data
|
||||||
fn set_on_off(&self, status: bool) {
|
async fn set_on_off(&self, #[zbus(signal_context)] ctxt: SignalContext<'_>, status: bool) {
|
||||||
|
let states;
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
if let Ok(mut lock) = self.0.try_lock() {
|
if let Ok(mut lock) = self.0.try_lock() {
|
||||||
lock.write_bytes(&pkt_for_set_on(status));
|
lock.write_bytes(&pkt_for_set_on(status));
|
||||||
lock.config.awake_enabled = status;
|
lock.config.awake_enabled = status;
|
||||||
lock.config.write();
|
lock.config.write();
|
||||||
|
|
||||||
let states = AnimePowerStates {
|
states = Some(AnimePowerStates {
|
||||||
|
brightness: lock.config.brightness.floor() as u8,
|
||||||
enabled: lock.config.awake_enabled,
|
enabled: lock.config.awake_enabled,
|
||||||
boot_anim_enabled: lock.config.boot_anim_enabled,
|
boot_anim_enabled: lock.config.boot_anim_enabled,
|
||||||
};
|
});
|
||||||
self.notify_power_states(&states)
|
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
|
||||||
break 'outer;
|
break 'outer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(state) = states {
|
||||||
|
Self::notify_power_states(&ctxt, state).await.ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set whether the AniMe will show boot, suspend, or off animations
|
/// Set whether the AniMe will show boot, suspend, or off animations
|
||||||
fn set_boot_on_off(&self, on: bool) {
|
async fn set_boot_on_off(&self, #[zbus(signal_context)] ctxt: SignalContext<'_>, on: bool) {
|
||||||
|
let states;
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
if let Ok(mut lock) = self.0.try_lock() {
|
if let Ok(mut lock) = self.0.try_lock() {
|
||||||
lock.write_bytes(&pkt_for_set_boot(on));
|
lock.write_bytes(&pkt_for_set_boot(on));
|
||||||
@@ -91,15 +91,17 @@ impl CtrlAnimeZbus {
|
|||||||
lock.config.boot_anim_enabled = on;
|
lock.config.boot_anim_enabled = on;
|
||||||
lock.config.write();
|
lock.config.write();
|
||||||
|
|
||||||
let states = AnimePowerStates {
|
states = Some(AnimePowerStates {
|
||||||
|
brightness: lock.config.brightness.floor() as u8,
|
||||||
enabled: lock.config.awake_enabled,
|
enabled: lock.config.awake_enabled,
|
||||||
boot_anim_enabled: lock.config.boot_anim_enabled,
|
boot_anim_enabled: lock.config.boot_anim_enabled,
|
||||||
};
|
});
|
||||||
self.notify_power_states(&states)
|
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
|
||||||
break 'outer;
|
break 'outer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(state) = states {
|
||||||
|
Self::notify_power_states(&ctxt, state).await.ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The main loop is the base system set action if the user isn't running
|
/// The main loop is the base system set action if the user isn't running
|
||||||
@@ -116,7 +118,7 @@ impl CtrlAnimeZbus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get status of if the AniMe LEDs are on
|
/// Get status of if the AniMe LEDs are on/displaying while system is awake
|
||||||
#[dbus_interface(property)]
|
#[dbus_interface(property)]
|
||||||
fn awake_enabled(&self) -> bool {
|
fn awake_enabled(&self) -> bool {
|
||||||
if let Ok(ctrl) = self.0.try_lock() {
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
@@ -136,5 +138,8 @@ impl CtrlAnimeZbus {
|
|||||||
|
|
||||||
/// Notify listeners of the status of AniMe LED power and factory system-status animations
|
/// Notify listeners of the status of AniMe LED power and factory system-status animations
|
||||||
#[dbus_interface(signal)]
|
#[dbus_interface(signal)]
|
||||||
fn notify_power_states(&self, data: &AnimePowerStates) -> zbus::Result<()>;
|
async fn notify_power_states(
|
||||||
|
ctxt: &SignalContext<'_>,
|
||||||
|
data: AnimePowerStates,
|
||||||
|
) -> zbus::Result<()>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,74 +1,182 @@
|
|||||||
use crate::laptops::LaptopLedData;
|
use crate::laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES};
|
||||||
use log::{error, info, warn};
|
use log::{error, warn};
|
||||||
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, LedBrightness};
|
use rog_aura::usb::{AuraDev1866, AuraDev19b6, AuraDevTuf, AuraDevice, AuraPowerDev};
|
||||||
|
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Direction, LedBrightness, Speed, GRADIENT};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, HashSet};
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
|
use super::controller::CtrlKbdLed;
|
||||||
|
|
||||||
pub static AURA_CONFIG_PATH: &str = "/etc/asusd/aura.conf";
|
pub static AURA_CONFIG_PATH: &str = "/etc/asusd/aura.conf";
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
/// Enable/disable LED control in various states such as
|
||||||
pub struct AuraConfigV320 {
|
/// when the device is awake, suspended, shutting down or
|
||||||
pub brightness: u32,
|
/// booting.
|
||||||
pub current_mode: AuraModeNum,
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
|
pub enum AuraPowerConfig {
|
||||||
pub multizone: Option<AuraMultiZone>,
|
AuraDevTuf(HashSet<AuraDevTuf>),
|
||||||
|
AuraDev1866(HashSet<AuraDev1866>),
|
||||||
|
AuraDev19b6(HashSet<AuraDev19b6>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuraConfigV320 {
|
impl AuraPowerConfig {
|
||||||
pub(crate) fn into_current(self) -> AuraConfig {
|
/// Invalid for TUF laptops
|
||||||
AuraConfig {
|
pub fn to_bytes(control: &Self) -> [u8; 3] {
|
||||||
brightness: <LedBrightness>::from(self.brightness),
|
match control {
|
||||||
current_mode: self.current_mode,
|
AuraPowerConfig::AuraDevTuf(_) => [0, 0, 0],
|
||||||
builtins: self.builtins,
|
AuraPowerConfig::AuraDev1866(c) => {
|
||||||
multizone: self.multizone,
|
let c: Vec<AuraDev1866> = c.iter().map(|v| *v).collect();
|
||||||
awake_enabled: true,
|
AuraDev1866::to_bytes(&c)
|
||||||
sleep_anim_enabled: true,
|
}
|
||||||
}
|
AuraPowerConfig::AuraDev19b6(c) => {
|
||||||
}
|
let c: Vec<AuraDev19b6> = c.iter().map(|v| *v).collect();
|
||||||
}
|
AuraDev19b6::to_bytes(&c)
|
||||||
|
}
|
||||||
#[derive(Deserialize, Serialize)]
|
}
|
||||||
pub struct AuraConfigV352 {
|
}
|
||||||
pub brightness: LedBrightness,
|
|
||||||
pub current_mode: AuraModeNum,
|
pub fn to_tuf_bool_array(control: &Self) -> Option<[bool; 5]> {
|
||||||
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
|
if let Self::AuraDevTuf(c) = control {
|
||||||
pub multizone: Option<AuraMultiZone>,
|
return Some([
|
||||||
}
|
true,
|
||||||
|
c.contains(&AuraDevTuf::Boot),
|
||||||
impl AuraConfigV352 {
|
c.contains(&AuraDevTuf::Awake),
|
||||||
pub(crate) fn into_current(self) -> AuraConfig {
|
c.contains(&AuraDevTuf::Sleep),
|
||||||
AuraConfig {
|
c.contains(&AuraDevTuf::Keyboard),
|
||||||
brightness: self.brightness,
|
]);
|
||||||
current_mode: self.current_mode,
|
}
|
||||||
builtins: self.builtins,
|
|
||||||
multizone: self.multizone,
|
if let Self::AuraDev1866(c) = control {
|
||||||
awake_enabled: true,
|
return Some([
|
||||||
sleep_anim_enabled: true,
|
true,
|
||||||
|
c.contains(&AuraDev1866::Boot),
|
||||||
|
c.contains(&AuraDev1866::Awake),
|
||||||
|
c.contains(&AuraDev1866::Sleep),
|
||||||
|
c.contains(&AuraDev1866::Keyboard),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_tuf(&mut self, power: AuraDevTuf, on: bool) {
|
||||||
|
if let Self::AuraDevTuf(p) = self {
|
||||||
|
if on {
|
||||||
|
p.insert(power);
|
||||||
|
} else {
|
||||||
|
p.remove(&power);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set_0x1866(&mut self, power: AuraDev1866, on: bool) {
|
||||||
|
if let Self::AuraDev1866(p) = self {
|
||||||
|
if on {
|
||||||
|
p.insert(power);
|
||||||
|
} else {
|
||||||
|
p.remove(&power);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_0x19b6(&mut self, power: AuraDev19b6, on: bool) {
|
||||||
|
if let Self::AuraDev19b6(p) = self {
|
||||||
|
if on {
|
||||||
|
p.insert(power);
|
||||||
|
} else {
|
||||||
|
p.remove(&power);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&AuraPowerConfig> for AuraPowerDev {
|
||||||
|
fn from(config: &AuraPowerConfig) -> Self {
|
||||||
|
match config {
|
||||||
|
AuraPowerConfig::AuraDevTuf(d) => AuraPowerDev {
|
||||||
|
tuf: d.iter().map(|o| *o).collect(),
|
||||||
|
x1866: vec![],
|
||||||
|
x19b6: vec![],
|
||||||
|
},
|
||||||
|
AuraPowerConfig::AuraDev1866(d) => AuraPowerDev {
|
||||||
|
tuf: vec![],
|
||||||
|
x1866: d.iter().map(|o| *o).collect(),
|
||||||
|
x19b6: vec![],
|
||||||
|
},
|
||||||
|
AuraPowerConfig::AuraDev19b6(d) => AuraPowerDev {
|
||||||
|
tuf: vec![],
|
||||||
|
x1866: vec![],
|
||||||
|
x19b6: d.iter().map(|o| *o).collect(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
|
#[serde(default)]
|
||||||
pub struct AuraConfig {
|
pub struct AuraConfig {
|
||||||
pub brightness: LedBrightness,
|
pub brightness: LedBrightness,
|
||||||
pub current_mode: AuraModeNum,
|
pub current_mode: AuraModeNum,
|
||||||
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
|
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
|
||||||
pub multizone: Option<AuraMultiZone>,
|
pub multizone: Option<BTreeMap<AuraModeNum, Vec<AuraEffect>>>,
|
||||||
pub awake_enabled: bool,
|
pub multizone_on: bool,
|
||||||
pub sleep_anim_enabled: bool,
|
pub enabled: AuraPowerConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AuraConfig {
|
impl Default for AuraConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
let mut prod_id = AuraDevice::Unknown;
|
||||||
|
for prod in ASUS_KEYBOARD_DEVICES.iter() {
|
||||||
|
if let Ok(_) = CtrlKbdLed::find_led_node(prod) {
|
||||||
|
prod_id = AuraDevice::from(*prod);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if CtrlKbdLed::get_tuf_mode_path().is_some() {
|
||||||
|
prod_id = AuraDevice::Tuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
let enabled = if prod_id == AuraDevice::X19B6 {
|
||||||
|
AuraPowerConfig::AuraDev19b6(HashSet::from([
|
||||||
|
AuraDev19b6::BootLogo,
|
||||||
|
AuraDev19b6::BootKeyb,
|
||||||
|
AuraDev19b6::SleepLogo,
|
||||||
|
AuraDev19b6::SleepKeyb,
|
||||||
|
AuraDev19b6::AwakeLogo,
|
||||||
|
AuraDev19b6::AwakeKeyb,
|
||||||
|
AuraDev19b6::ShutdownLogo,
|
||||||
|
AuraDev19b6::ShutdownKeyb,
|
||||||
|
AuraDev19b6::AwakeBar,
|
||||||
|
AuraDev19b6::BootBar,
|
||||||
|
AuraDev19b6::SleepBar,
|
||||||
|
AuraDev19b6::ShutdownBar,
|
||||||
|
]))
|
||||||
|
} else if prod_id == AuraDevice::Tuf {
|
||||||
|
AuraPowerConfig::AuraDevTuf(HashSet::from([
|
||||||
|
AuraDevTuf::Awake,
|
||||||
|
AuraDevTuf::Boot,
|
||||||
|
AuraDevTuf::Sleep,
|
||||||
|
AuraDevTuf::Keyboard,
|
||||||
|
]))
|
||||||
|
} else {
|
||||||
|
AuraPowerConfig::AuraDev1866(HashSet::from([
|
||||||
|
AuraDev1866::Awake,
|
||||||
|
AuraDev1866::Boot,
|
||||||
|
AuraDev1866::Sleep,
|
||||||
|
AuraDev1866::Keyboard,
|
||||||
|
AuraDev1866::Lightbar,
|
||||||
|
]))
|
||||||
|
};
|
||||||
|
|
||||||
AuraConfig {
|
AuraConfig {
|
||||||
brightness: LedBrightness::Med,
|
brightness: LedBrightness::Med,
|
||||||
current_mode: AuraModeNum::Static,
|
current_mode: AuraModeNum::Static,
|
||||||
builtins: BTreeMap::new(),
|
builtins: BTreeMap::new(),
|
||||||
multizone: None,
|
multizone: None,
|
||||||
awake_enabled: true,
|
multizone_on: false,
|
||||||
sleep_anim_enabled: true,
|
enabled,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,19 +202,18 @@ 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!("Could not deserialise {}", AURA_CONFIG_PATH);
|
warn!(
|
||||||
panic!("Please remove {} then restart asusd", AURA_CONFIG_PATH);
|
"Could not deserialise {}.\nWill rename to {}-old and recreate config",
|
||||||
|
AURA_CONFIG_PATH, AURA_CONFIG_PATH
|
||||||
|
);
|
||||||
|
let cfg_old = AURA_CONFIG_PATH.to_string() + "-old";
|
||||||
|
std::fs::rename(AURA_CONFIG_PATH, cfg_old).unwrap_or_else(|err| {
|
||||||
|
panic!(
|
||||||
|
"Could not rename. Please remove {} then restart service: Error {}",
|
||||||
|
AURA_CONFIG_PATH, err
|
||||||
|
)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AuraConfig::create_default(&mut file, supported_led_modes)
|
AuraConfig::create_default(&mut file, supported_led_modes)
|
||||||
@@ -120,6 +227,27 @@ impl AuraConfig {
|
|||||||
config
|
config
|
||||||
.builtins
|
.builtins
|
||||||
.insert(*n, AuraEffect::default_with_mode(*n));
|
.insert(*n, AuraEffect::default_with_mode(*n));
|
||||||
|
|
||||||
|
if !support_data.multizone.is_empty() {
|
||||||
|
let mut default = vec![];
|
||||||
|
for (i, tmp) in support_data.multizone.iter().enumerate() {
|
||||||
|
default.push(AuraEffect {
|
||||||
|
mode: *n,
|
||||||
|
zone: *tmp,
|
||||||
|
colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]),
|
||||||
|
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
|
||||||
|
speed: Speed::Med,
|
||||||
|
direction: Direction::Left,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if let Some(m) = config.multizone.as_mut() {
|
||||||
|
m.insert(*n, default);
|
||||||
|
} else {
|
||||||
|
let mut tmp = BTreeMap::new();
|
||||||
|
tmp.insert(*n, default);
|
||||||
|
config.multizone = Some(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should be okay to unwrap this as is since it is a Default
|
// Should be okay to unwrap this as is since it is a Default
|
||||||
@@ -153,115 +281,126 @@ impl AuraConfig {
|
|||||||
.unwrap_or_else(|err| error!("Could not write config: {}", err));
|
.unwrap_or_else(|err| error!("Could not write config: {}", err));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Multipurpose, will accept AuraEffect with zones and put in the correct store
|
/// Set the mode data, current mode, and if multizone enabled.
|
||||||
|
///
|
||||||
|
/// Multipurpose, will accept AuraEffect with zones and put in the correct store.
|
||||||
pub fn set_builtin(&mut self, effect: AuraEffect) {
|
pub fn set_builtin(&mut self, effect: AuraEffect) {
|
||||||
|
self.current_mode = effect.mode;
|
||||||
match effect.zone() {
|
match effect.zone() {
|
||||||
AuraZone::None => {
|
AuraZone::None => {
|
||||||
self.builtins.insert(*effect.mode(), effect);
|
self.builtins.insert(*effect.mode(), effect);
|
||||||
|
self.multizone_on = false;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if let Some(multi) = self.multizone.as_mut() {
|
if let Some(multi) = self.multizone.as_mut() {
|
||||||
multi.set(effect)
|
if let Some(fx) = multi.get_mut(effect.mode()) {
|
||||||
|
for fx in fx.iter_mut() {
|
||||||
|
if fx.zone == effect.zone {
|
||||||
|
*fx = effect;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fx.push(effect);
|
||||||
|
} else {
|
||||||
|
multi.insert(*effect.mode(), vec![effect]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut tmp = BTreeMap::new();
|
||||||
|
tmp.insert(*effect.mode(), vec![effect]);
|
||||||
|
self.multizone = Some(tmp);
|
||||||
}
|
}
|
||||||
|
self.multizone_on = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_multizone(&self, aura_type: AuraModeNum) -> Option<&[AuraEffect; 4]> {
|
pub fn get_multizone(&self, aura_type: AuraModeNum) -> Option<&[AuraEffect]> {
|
||||||
if let Some(multi) = &self.multizone {
|
if let Some(multi) = &self.multizone {
|
||||||
if aura_type == AuraModeNum::Static {
|
return multi.get(&aura_type).map(|v| v.as_slice());
|
||||||
return Some(multi.static_());
|
|
||||||
} else if aura_type == AuraModeNum::Breathe {
|
|
||||||
return Some(multi.breathe());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[cfg(test)]
|
||||||
pub struct AuraMultiZone {
|
mod tests {
|
||||||
static_: [AuraEffect; 4],
|
use super::AuraConfig;
|
||||||
breathe: [AuraEffect; 4],
|
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour};
|
||||||
}
|
|
||||||
|
|
||||||
impl AuraMultiZone {
|
#[test]
|
||||||
pub fn set(&mut self, effect: AuraEffect) {
|
fn set_multizone_4key_config() {
|
||||||
if effect.mode == AuraModeNum::Static {
|
let mut config = AuraConfig::default();
|
||||||
match effect.zone {
|
|
||||||
AuraZone::None => {}
|
let mut effect = AuraEffect::default();
|
||||||
AuraZone::One => self.static_[0] = effect,
|
effect.colour1 = Colour(0xff, 0x00, 0xff);
|
||||||
AuraZone::Two => self.static_[1] = effect,
|
effect.zone = AuraZone::Key1;
|
||||||
AuraZone::Three => self.static_[2] = effect,
|
config.set_builtin(effect);
|
||||||
AuraZone::Four => self.static_[3] = effect,
|
|
||||||
}
|
assert!(config.multizone.is_some());
|
||||||
} else if effect.mode == AuraModeNum::Breathe {
|
|
||||||
match effect.zone {
|
let mut effect = AuraEffect::default();
|
||||||
AuraZone::None => {}
|
effect.colour1 = Colour(0x00, 0xff, 0xff);
|
||||||
AuraZone::One => self.breathe[0] = effect,
|
effect.zone = AuraZone::Key2;
|
||||||
AuraZone::Two => self.breathe[1] = effect,
|
config.set_builtin(effect);
|
||||||
AuraZone::Three => self.breathe[2] = effect,
|
|
||||||
AuraZone::Four => self.breathe[3] = effect,
|
let mut effect = AuraEffect::default();
|
||||||
}
|
effect.colour1 = Colour(0xff, 0xff, 0x00);
|
||||||
}
|
effect.zone = AuraZone::Key3;
|
||||||
|
config.set_builtin(effect);
|
||||||
|
|
||||||
|
let mut effect = AuraEffect::default();
|
||||||
|
effect.colour1 = Colour(0x00, 0xff, 0x00);
|
||||||
|
effect.zone = AuraZone::Key4;
|
||||||
|
let effect_clone = effect.clone();
|
||||||
|
config.set_builtin(effect);
|
||||||
|
// This should replace existing
|
||||||
|
config.set_builtin(effect_clone);
|
||||||
|
|
||||||
|
let res = config.multizone.unwrap();
|
||||||
|
let sta = res.get(&AuraModeNum::Static).unwrap();
|
||||||
|
assert_eq!(sta.len(), 4);
|
||||||
|
assert_eq!(sta[0].colour1, Colour(0xff, 0x00, 0xff));
|
||||||
|
assert_eq!(sta[1].colour1, Colour(0x00, 0xff, 0xff));
|
||||||
|
assert_eq!(sta[2].colour1, Colour(0xff, 0xff, 0x00));
|
||||||
|
assert_eq!(sta[3].colour1, Colour(0x00, 0xff, 0x00));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn static_(&self) -> &[AuraEffect; 4] {
|
#[test]
|
||||||
&self.static_
|
fn set_multizone_multimode_config() {
|
||||||
}
|
let mut config = AuraConfig::default();
|
||||||
|
|
||||||
pub fn breathe(&self) -> &[AuraEffect; 4] {
|
let mut effect = AuraEffect::default();
|
||||||
&self.breathe
|
effect.zone = AuraZone::Key1;
|
||||||
}
|
config.set_builtin(effect);
|
||||||
}
|
|
||||||
|
assert!(config.multizone.is_some());
|
||||||
impl Default for AuraMultiZone {
|
|
||||||
fn default() -> Self {
|
let mut effect = AuraEffect::default();
|
||||||
Self {
|
effect.zone = AuraZone::Key2;
|
||||||
static_: [
|
effect.mode = AuraModeNum::Breathe;
|
||||||
AuraEffect {
|
config.set_builtin(effect);
|
||||||
mode: AuraModeNum::Static,
|
|
||||||
zone: AuraZone::One,
|
let mut effect = AuraEffect::default();
|
||||||
..Default::default()
|
effect.zone = AuraZone::Key3;
|
||||||
},
|
effect.mode = AuraModeNum::Comet;
|
||||||
AuraEffect {
|
config.set_builtin(effect);
|
||||||
mode: AuraModeNum::Static,
|
|
||||||
zone: AuraZone::Two,
|
let mut effect = AuraEffect::default();
|
||||||
..Default::default()
|
effect.zone = AuraZone::Key4;
|
||||||
},
|
effect.mode = AuraModeNum::Pulse;
|
||||||
AuraEffect {
|
config.set_builtin(effect);
|
||||||
mode: AuraModeNum::Static,
|
|
||||||
zone: AuraZone::Three,
|
let res = config.multizone.unwrap();
|
||||||
..Default::default()
|
let sta = res.get(&AuraModeNum::Static).unwrap();
|
||||||
},
|
assert_eq!(sta.len(), 1);
|
||||||
AuraEffect {
|
|
||||||
mode: AuraModeNum::Static,
|
let sta = res.get(&AuraModeNum::Breathe).unwrap();
|
||||||
zone: AuraZone::Four,
|
assert_eq!(sta.len(), 1);
|
||||||
..Default::default()
|
|
||||||
},
|
let sta = res.get(&AuraModeNum::Comet).unwrap();
|
||||||
],
|
assert_eq!(sta.len(), 1);
|
||||||
breathe: [
|
|
||||||
AuraEffect {
|
let sta = res.get(&AuraModeNum::Pulse).unwrap();
|
||||||
mode: AuraModeNum::Breathe,
|
assert_eq!(sta.len(), 1);
|
||||||
zone: AuraZone::One,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AuraEffect {
|
|
||||||
mode: AuraModeNum::Breathe,
|
|
||||||
zone: AuraZone::Two,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AuraEffect {
|
|
||||||
mode: AuraModeNum::Breathe,
|
|
||||||
zone: AuraZone::Three,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AuraEffect {
|
|
||||||
mode: AuraModeNum::Breathe,
|
|
||||||
zone: AuraZone::Four,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,61 @@
|
|||||||
// Only these two packets must be 17 bytes
|
// Only these two packets must be 17 bytes
|
||||||
static KBD_BRIGHT_PATH: &str = "/sys/class/leds/asus::kbd_backlight/brightness";
|
static KBD_BRIGHT_PATH: &str = "/sys/class/leds/asus::kbd_backlight/brightness";
|
||||||
|
static TUF_RGB_MODE_PATH: &str = "/sys/devices/platform/asus-nb-wmi/tuf_krgb_mode";
|
||||||
|
static TUF_RGB_STATE_PATH: &str = "/sys/devices/platform/asus-nb-wmi/tuf_krgb_state";
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::RogError,
|
error::RogError,
|
||||||
laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES},
|
laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES},
|
||||||
CtrlTask,
|
CtrlTask,
|
||||||
};
|
};
|
||||||
use log::{info, warn};
|
use async_trait::async_trait;
|
||||||
use logind_zbus::ManagerProxy;
|
use log::{error, info, warn};
|
||||||
|
use logind_zbus::manager::ManagerProxy;
|
||||||
use rog_aura::{
|
use rog_aura::{
|
||||||
usb::{
|
usb::{AuraDevice, LED_APPLY, LED_SET},
|
||||||
LED_APPLY, LED_AWAKE_OFF_SLEEP_OFF, LED_AWAKE_OFF_SLEEP_ON, LED_AWAKE_ON_SLEEP_OFF,
|
|
||||||
LED_AWAKE_ON_SLEEP_ON, LED_SET,
|
|
||||||
},
|
|
||||||
AuraEffect, LedBrightness, LED_MSG_LEN,
|
AuraEffect, LedBrightness, LED_MSG_LEN,
|
||||||
};
|
};
|
||||||
|
use rog_aura::{AuraZone, Direction, Speed, GRADIENT};
|
||||||
use rog_supported::LedSupportedFunctions;
|
use rog_supported::LedSupportedFunctions;
|
||||||
use std::io::{Read, Write};
|
use smol::{stream::StreamExt, Executor};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::{fs::OpenOptions, thread::spawn};
|
use std::{
|
||||||
|
collections::BTreeMap,
|
||||||
|
io::{Read, Write},
|
||||||
|
};
|
||||||
|
use std::{fs::OpenOptions, sync::MutexGuard};
|
||||||
use zbus::Connection;
|
use zbus::Connection;
|
||||||
|
|
||||||
use crate::GetSupported;
|
use crate::GetSupported;
|
||||||
|
|
||||||
use super::config::AuraConfig;
|
use super::config::{AuraConfig, AuraPowerConfig};
|
||||||
|
|
||||||
impl GetSupported for CtrlKbdLed {
|
impl GetSupported for CtrlKbdLed {
|
||||||
type A = LedSupportedFunctions;
|
type A = LedSupportedFunctions;
|
||||||
|
|
||||||
fn get_supported() -> Self::A {
|
fn get_supported() -> Self::A {
|
||||||
// let mode = <&str>::from(&<AuraModes>::from(*mode));
|
// let mode = <&str>::from(&<AuraModes>::from(*mode));
|
||||||
let multizone_led_mode = false;
|
|
||||||
let per_key_led_mode = false;
|
|
||||||
let laptop = LaptopLedData::get_data();
|
let laptop = LaptopLedData::get_data();
|
||||||
let stock_led_modes = laptop.standard;
|
let stock_led_modes = laptop.standard;
|
||||||
|
let multizone_led_mode = laptop.multizone;
|
||||||
|
let per_key_led_mode = laptop.per_key;
|
||||||
|
|
||||||
|
let mut prod_id = AuraDevice::Unknown;
|
||||||
|
for prod in ASUS_KEYBOARD_DEVICES.iter() {
|
||||||
|
if let Ok(_) = Self::find_led_node(prod) {
|
||||||
|
prod_id = AuraDevice::from(*prod);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if Self::get_tuf_mode_path().is_some() {
|
||||||
|
prod_id = AuraDevice::Tuf;
|
||||||
|
}
|
||||||
|
|
||||||
LedSupportedFunctions {
|
LedSupportedFunctions {
|
||||||
|
prod_id,
|
||||||
brightness_set: CtrlKbdLed::get_kbd_bright_path().is_some(),
|
brightness_set: CtrlKbdLed::get_kbd_bright_path().is_some(),
|
||||||
stock_led_modes,
|
stock_led_modes,
|
||||||
multizone_led_mode,
|
multizone_led_mode,
|
||||||
@@ -46,58 +64,30 @@ impl GetSupported for CtrlKbdLed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, PartialOrd)]
|
||||||
|
pub enum LEDNode {
|
||||||
|
Tuf,
|
||||||
|
Rog(String),
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct CtrlKbdLed {
|
pub struct CtrlKbdLed {
|
||||||
pub led_node: Option<String>,
|
// TODO: config stores the keyboard type as an AuraPower, use or update this
|
||||||
|
pub led_prod: Option<String>,
|
||||||
|
pub led_node: LEDNode,
|
||||||
pub bright_node: String,
|
pub bright_node: String,
|
||||||
pub supported_modes: LaptopLedData,
|
pub supported_modes: LaptopLedData,
|
||||||
pub flip_effect_write: bool,
|
pub flip_effect_write: bool,
|
||||||
pub config: AuraConfig,
|
pub config: AuraConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CtrlKbdLedTask<'a> {
|
pub struct CtrlKbdLedTask {
|
||||||
inner: Arc<Mutex<CtrlKbdLed>>,
|
inner: Arc<Mutex<CtrlKbdLed>>,
|
||||||
_c: Connection,
|
|
||||||
manager: ManagerProxy<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CtrlKbdLedTask<'a> {
|
impl CtrlKbdLedTask {
|
||||||
pub fn new(inner: Arc<Mutex<CtrlKbdLed>>) -> Self {
|
pub fn new(inner: Arc<Mutex<CtrlKbdLed>>) -> Self {
|
||||||
let connection =
|
Self { inner }
|
||||||
Connection::new_system().expect("CtrlKbdLedTask could not create dbus connection");
|
|
||||||
|
|
||||||
let manager =
|
|
||||||
ManagerProxy::new(&connection).expect("CtrlKbdLedTask could not create ManagerProxy");
|
|
||||||
|
|
||||||
let c1 = inner.clone();
|
|
||||||
// Run this action when the system wakes up from sleep
|
|
||||||
manager
|
|
||||||
.connect_prepare_for_sleep(move |sleep| {
|
|
||||||
if !sleep {
|
|
||||||
let c1 = c1.clone();
|
|
||||||
spawn(move || {
|
|
||||||
// wait a fraction for things to wake up properly
|
|
||||||
//std::thread::sleep(Duration::from_millis(100));
|
|
||||||
loop {
|
|
||||||
if let Ok(ref mut lock) = c1.try_lock() {
|
|
||||||
lock.set_brightness(lock.config.brightness).ok();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!("CtrlAnimeTask: new() {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
inner,
|
|
||||||
_c: connection,
|
|
||||||
manager,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> {
|
fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> {
|
||||||
@@ -125,12 +115,70 @@ impl<'a> CtrlKbdLedTask<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CtrlTask for CtrlKbdLedTask<'a> {
|
#[async_trait]
|
||||||
fn do_task(&self) -> Result<(), RogError> {
|
impl CtrlTask for CtrlKbdLedTask {
|
||||||
self.manager.next_signal()?;
|
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
|
||||||
if let Ok(ref mut lock) = self.inner.try_lock() {
|
let connection = Connection::system()
|
||||||
return Self::update_config(lock);
|
.await
|
||||||
}
|
.expect("CtrlKbdLedTask could not create dbus connection");
|
||||||
|
|
||||||
|
let manager = ManagerProxy::new(&connection)
|
||||||
|
.await
|
||||||
|
.expect("CtrlKbdLedTask could not create ManagerProxy");
|
||||||
|
|
||||||
|
let load_save = |start: bool, mut lock: MutexGuard<CtrlKbdLed>| {
|
||||||
|
// If waking up
|
||||||
|
if !start {
|
||||||
|
info!("CtrlKbdLedTask reloading brightness and modes");
|
||||||
|
lock.set_brightness(lock.config.brightness)
|
||||||
|
.map_err(|e| error!("CtrlKbdLedTask: {e}"))
|
||||||
|
.ok();
|
||||||
|
lock.write_current_config_mode()
|
||||||
|
.map_err(|e| error!("CtrlKbdLedTask: {e}"))
|
||||||
|
.ok();
|
||||||
|
} else if start {
|
||||||
|
info!("CtrlKbdLedTask saving last brightness");
|
||||||
|
Self::update_config(&mut lock)
|
||||||
|
.map_err(|e| error!("CtrlKbdLedTask: {e}"))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let inner = self.inner.clone();
|
||||||
|
executor
|
||||||
|
.spawn(async move {
|
||||||
|
if let Ok(notif) = manager.receive_prepare_for_sleep().await {
|
||||||
|
notif
|
||||||
|
.for_each(|event| {
|
||||||
|
if let Ok(args) = event.args() {
|
||||||
|
loop {
|
||||||
|
// Loop so that we do aquire the lock but also don't block other
|
||||||
|
// threads (prevents potential deadlocks)
|
||||||
|
if let Ok(lock) = inner.clone().try_lock() {
|
||||||
|
load_save(args.start, lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
if let Ok(notif) = manager.receive_prepare_for_shutdown().await {
|
||||||
|
notif
|
||||||
|
.for_each(|event| {
|
||||||
|
if let Ok(args) = event.args() {
|
||||||
|
loop {
|
||||||
|
if let Ok(lock) = inner.clone().try_lock() {
|
||||||
|
load_save(args.start, lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,13 +188,9 @@ pub struct CtrlKbdLedReloader(pub Arc<Mutex<CtrlKbdLed>>);
|
|||||||
impl crate::Reloadable for CtrlKbdLedReloader {
|
impl crate::Reloadable for CtrlKbdLedReloader {
|
||||||
fn reload(&mut self) -> Result<(), RogError> {
|
fn reload(&mut self) -> Result<(), RogError> {
|
||||||
if let Ok(mut ctrl) = self.0.try_lock() {
|
if let Ok(mut ctrl) = self.0.try_lock() {
|
||||||
let current = ctrl.config.current_mode;
|
ctrl.write_current_config_mode()?;
|
||||||
if let Some(mode) = ctrl.config.builtins.get(¤t).cloned() {
|
ctrl.set_power_states(&ctrl.config)
|
||||||
ctrl.do_command(mode).ok();
|
.map_err(|err| warn!("{err}"))
|
||||||
}
|
|
||||||
|
|
||||||
ctrl.set_states_enabled(ctrl.config.awake_enabled, ctrl.config.sleep_anim_enabled)
|
|
||||||
.map_err(|err| warn!("{}", err))
|
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -162,36 +206,46 @@ impl CtrlKbdLedZbus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CtrlKbdLed {
|
impl CtrlKbdLed {
|
||||||
#[inline]
|
|
||||||
pub fn new(supported_modes: LaptopLedData, config: AuraConfig) -> Result<Self, RogError> {
|
pub fn new(supported_modes: LaptopLedData, config: AuraConfig) -> Result<Self, RogError> {
|
||||||
// TODO: return error if *all* nodes are None
|
let mut led_prod = None;
|
||||||
let mut led_node = None;
|
let mut led_node = None;
|
||||||
for prod in ASUS_KEYBOARD_DEVICES.iter() {
|
for prod in ASUS_KEYBOARD_DEVICES.iter() {
|
||||||
match Self::find_led_node(prod) {
|
match Self::find_led_node(prod) {
|
||||||
Ok(node) => {
|
Ok(node) => {
|
||||||
|
led_prod = Some(prod.to_string());
|
||||||
led_node = Some(node);
|
led_node = Some(node);
|
||||||
|
info!("Looked for keyboard controller 0x{prod}: Found");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(err) => warn!("led_node: {}", err),
|
Err(err) => info!("Looked for keyboard controller 0x{prod}: {err}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let bright_node = Self::get_kbd_bright_path();
|
let bright_node = Self::get_kbd_bright_path();
|
||||||
|
let tuf_node = Self::get_tuf_mode_path().is_some() || Self::get_tuf_state_path().is_some();
|
||||||
|
|
||||||
if led_node.is_none() && bright_node.is_none() {
|
if led_node.is_none() && tuf_node && bright_node.is_none() {
|
||||||
return Err(RogError::MissingFunction(
|
let dmi = sysfs_class::DmiId::default();
|
||||||
"All keyboard features missing, you may require a v5.11 series kernel or newer"
|
if let Ok(prod_family) = dmi.product_family() {
|
||||||
.into(),
|
if prod_family.contains("TUF") {
|
||||||
));
|
warn!("A kernel patch is in progress for TUF RGB support");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err(RogError::NoAuraKeyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
if bright_node.is_none() {
|
let led_node = if let Some(rog) = led_node {
|
||||||
return Err(RogError::MissingFunction(
|
info!("Found ROG USB keyboard");
|
||||||
"No brightness control, you may require a v5.11 series kernel or newer".into(),
|
LEDNode::Rog(rog)
|
||||||
));
|
} else if tuf_node {
|
||||||
}
|
info!("Found TUF keyboard");
|
||||||
|
LEDNode::Tuf
|
||||||
|
} else {
|
||||||
|
LEDNode::None
|
||||||
|
};
|
||||||
|
|
||||||
let ctrl = CtrlKbdLed {
|
let ctrl = CtrlKbdLed {
|
||||||
|
led_prod,
|
||||||
led_node,
|
led_node,
|
||||||
bright_node: bright_node.unwrap(), // If was none then we already returned above
|
bright_node: bright_node.unwrap(), // If was none then we already returned above
|
||||||
supported_modes,
|
supported_modes,
|
||||||
@@ -208,6 +262,20 @@ impl CtrlKbdLed {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_tuf_mode_path() -> Option<String> {
|
||||||
|
if Path::new(TUF_RGB_MODE_PATH).exists() {
|
||||||
|
return Some(TUF_RGB_MODE_PATH.to_string());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_tuf_state_path() -> Option<String> {
|
||||||
|
if Path::new(TUF_RGB_STATE_PATH).exists() {
|
||||||
|
return Some(TUF_RGB_STATE_PATH.to_string());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn get_brightness(&self) -> Result<u8, RogError> {
|
pub(super) fn get_brightness(&self) -> Result<u8, RogError> {
|
||||||
let mut file = OpenOptions::new()
|
let mut file = OpenOptions::new()
|
||||||
.read(true)
|
.read(true)
|
||||||
@@ -263,27 +331,32 @@ impl CtrlKbdLed {
|
|||||||
self.set_brightness(self.config.brightness)
|
self.set_brightness(self.config.brightness)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set if awake/on LED active, and/or sleep animation active
|
/// Set combination state for boot animation/sleep animation/all leds/keys leds/side leds LED active
|
||||||
pub(super) fn set_states_enabled(&self, awake: bool, sleep: bool) -> Result<(), RogError> {
|
pub(super) fn set_power_states(&self, config: &AuraConfig) -> Result<(), RogError> {
|
||||||
let bytes = if awake && sleep {
|
if LEDNode::Tuf == self.led_node {
|
||||||
LED_AWAKE_ON_SLEEP_ON
|
if let Some(path) = Self::get_tuf_state_path() {
|
||||||
} else if awake && !sleep {
|
let mut file = OpenOptions::new().write(true).open(path)?;
|
||||||
LED_AWAKE_ON_SLEEP_OFF
|
if let Some(pwr) = AuraPowerConfig::to_tuf_bool_array(&config.enabled) {
|
||||||
} else if !awake && sleep {
|
let buf = format!(
|
||||||
LED_AWAKE_OFF_SLEEP_ON
|
"1 {} {} {} {}",
|
||||||
} else if !awake && !sleep {
|
pwr[1] as u8, pwr[2] as u8, pwr[3] as u8, pwr[4] as u8,
|
||||||
LED_AWAKE_OFF_SLEEP_OFF
|
);
|
||||||
|
file.write_all(buf.as_bytes())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
LED_AWAKE_ON_SLEEP_ON
|
let bytes = AuraPowerConfig::to_bytes(&config.enabled);
|
||||||
};
|
let message = [0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2]];
|
||||||
self.write_bytes(&bytes)?;
|
|
||||||
self.write_bytes(&LED_SET)?;
|
self.write_bytes(&message)?;
|
||||||
// Changes won't persist unless apply is set
|
self.write_bytes(&LED_SET)?;
|
||||||
self.write_bytes(&LED_APPLY)?;
|
// Changes won't persist unless apply is set
|
||||||
|
self.write_bytes(&LED_APPLY)?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_led_node(id_product: &str) -> Result<String, RogError> {
|
pub(crate) fn find_led_node(id_product: &str) -> Result<String, RogError> {
|
||||||
let mut enumerator = udev::Enumerator::new().map_err(|err| {
|
let mut enumerator = udev::Enumerator::new().map_err(|err| {
|
||||||
warn!("{}", err);
|
warn!("{}", err);
|
||||||
RogError::Udev("enumerator failed".into(), err)
|
RogError::Udev("enumerator failed".into(), err)
|
||||||
@@ -321,14 +394,29 @@ impl CtrlKbdLed {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn do_command(&mut self, mode: AuraEffect) -> Result<(), RogError> {
|
/// Set an Aura effect if the effect mode or zone is supported.
|
||||||
self.set_and_save(mode)
|
///
|
||||||
|
/// On success the aura config file is read to refresh cached values, then the effect is
|
||||||
|
/// stored and config written to disk.
|
||||||
|
pub(crate) fn set_effect(&mut self, effect: AuraEffect) -> Result<(), RogError> {
|
||||||
|
if !self.supported_modes.standard.contains(&effect.mode) {
|
||||||
|
return Err(RogError::AuraEffectNotSupported);
|
||||||
|
} else if effect.zone != AuraZone::None
|
||||||
|
&& !self.supported_modes.multizone.contains(&effect.zone)
|
||||||
|
{
|
||||||
|
return Err(RogError::AuraEffectNotSupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.write_mode(&effect)?;
|
||||||
|
self.config.read(); // refresh config if successful
|
||||||
|
self.config.set_builtin(effect);
|
||||||
|
self.config.write();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Should only be used if the bytes you are writing are verified correct
|
/// Should only be used if the bytes you are writing are verified correct
|
||||||
#[inline]
|
|
||||||
fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
|
fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
|
||||||
if let Some(led_node) = &self.led_node {
|
if let LEDNode::Rog(led_node) = &self.led_node {
|
||||||
if let Ok(mut file) = OpenOptions::new().write(true).open(led_node) {
|
if let Ok(mut file) = OpenOptions::new().write(true).open(led_node) {
|
||||||
// println!("write: {:02x?}", &message);
|
// println!("write: {:02x?}", &message);
|
||||||
return file
|
return file
|
||||||
@@ -336,11 +424,10 @@ impl CtrlKbdLed {
|
|||||||
.map_err(|err| RogError::Write("write_bytes".into(), err));
|
.map_err(|err| RogError::Write("write_bytes".into(), err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(RogError::NotSupported)
|
Err(RogError::NoAuraNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write an effect block
|
/// Write an effect block. This is for per-key
|
||||||
#[inline]
|
|
||||||
fn _write_effect(&mut self, effect: &[Vec<u8>]) -> Result<(), RogError> {
|
fn _write_effect(&mut self, effect: &[Vec<u8>]) -> Result<(), RogError> {
|
||||||
if self.flip_effect_write {
|
if self.flip_effect_write {
|
||||||
for row in effect.iter().rev() {
|
for row in effect.iter().rev() {
|
||||||
@@ -355,20 +442,6 @@ impl CtrlKbdLed {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to set a builtin mode and save the settings for it
|
|
||||||
///
|
|
||||||
/// This needs to be universal so that settings applied by dbus stick
|
|
||||||
#[inline]
|
|
||||||
fn set_and_save(&mut self, mode: AuraEffect) -> Result<(), RogError> {
|
|
||||||
self.config.read();
|
|
||||||
self.write_mode(&mode)?;
|
|
||||||
self.config.current_mode = *mode.mode();
|
|
||||||
self.config.set_builtin(mode);
|
|
||||||
self.config.write();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(super) fn toggle_mode(&mut self, reverse: bool) -> Result<(), RogError> {
|
pub(super) fn toggle_mode(&mut self, reverse: bool) -> Result<(), RogError> {
|
||||||
let current = self.config.current_mode;
|
let current = self.config.current_mode;
|
||||||
if let Some(idx) = self
|
if let Some(idx) = self
|
||||||
@@ -394,26 +467,247 @@ impl CtrlKbdLed {
|
|||||||
let next = self.supported_modes.standard[idx];
|
let next = self.supported_modes.standard[idx];
|
||||||
|
|
||||||
self.config.read();
|
self.config.read();
|
||||||
if let Some(data) = self.config.builtins.get(&next) {
|
// if self.config.builtins.contains_key(&next) {
|
||||||
self.write_mode(data)?;
|
self.config.current_mode = next;
|
||||||
self.config.current_mode = next;
|
self.write_current_config_mode()?;
|
||||||
}
|
// }
|
||||||
self.config.write();
|
self.config.write();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn write_mode(&self, mode: &AuraEffect) -> Result<(), RogError> {
|
fn write_mode(&self, mode: &AuraEffect) -> Result<(), RogError> {
|
||||||
if !self.supported_modes.standard.contains(mode.mode()) {
|
if LEDNode::Tuf == self.led_node {
|
||||||
return Err(RogError::NotSupported);
|
let mut file = OpenOptions::new().write(true).open(TUF_RGB_MODE_PATH)?;
|
||||||
|
let buf = format!(
|
||||||
|
"1 {} {} {} {} {}",
|
||||||
|
mode.mode as u8, mode.colour1.0, mode.colour1.1, mode.colour1.2, mode.speed as u8,
|
||||||
|
);
|
||||||
|
file.write_all(buf.as_bytes())?;
|
||||||
|
} else {
|
||||||
|
let bytes: [u8; LED_MSG_LEN] = mode.into();
|
||||||
|
self.write_bytes(&bytes)?;
|
||||||
|
self.write_bytes(&LED_SET)?;
|
||||||
|
// Changes won't persist unless apply is set
|
||||||
|
self.write_bytes(&LED_APPLY)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_current_config_mode(&mut self) -> Result<(), RogError> {
|
||||||
|
if self.config.multizone_on {
|
||||||
|
let mode = self.config.current_mode;
|
||||||
|
let mut create = false;
|
||||||
|
// There is no multizone config for this mode so create one here
|
||||||
|
// using the colours of rainbow if it exists, or first available
|
||||||
|
// mode, or random
|
||||||
|
if self.config.multizone.is_none() {
|
||||||
|
create = true;
|
||||||
|
} else if let Some(multizones) = self.config.multizone.as_ref() {
|
||||||
|
if !multizones.contains_key(&mode) {
|
||||||
|
create = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if create {
|
||||||
|
info!("No user-set config for zone founding, attempting a default");
|
||||||
|
self.create_multizone_default()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(multizones) = self.config.multizone.as_ref() {
|
||||||
|
if let Some(set) = multizones.get(&mode) {
|
||||||
|
for mode in set {
|
||||||
|
self.write_mode(mode)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mode = self.config.current_mode;
|
||||||
|
if let Some(effect) = self.config.builtins.get(&mode) {
|
||||||
|
self.write_mode(effect)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a default for the `current_mode` if multizone and no config exists.
|
||||||
|
fn create_multizone_default(&mut self) -> Result<(), RogError> {
|
||||||
|
let mut default = vec![];
|
||||||
|
for (i, tmp) in self.supported_modes.multizone.iter().enumerate() {
|
||||||
|
default.push(AuraEffect {
|
||||||
|
mode: self.config.current_mode,
|
||||||
|
zone: *tmp,
|
||||||
|
colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]),
|
||||||
|
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
|
||||||
|
speed: Speed::Med,
|
||||||
|
direction: Direction::Left,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if default.is_empty() {
|
||||||
|
return Err(RogError::AuraEffectNotSupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(multizones) = self.config.multizone.as_mut() {
|
||||||
|
multizones.insert(self.config.current_mode, default);
|
||||||
|
} else {
|
||||||
|
let mut tmp = BTreeMap::new();
|
||||||
|
tmp.insert(self.config.current_mode, default);
|
||||||
|
self.config.multizone = Some(tmp);
|
||||||
}
|
}
|
||||||
let bytes: [u8; LED_MSG_LEN] = mode.into();
|
|
||||||
self.write_bytes(&bytes)?;
|
|
||||||
self.write_bytes(&LED_SET)?;
|
|
||||||
// Changes won't persist unless apply is set
|
|
||||||
self.write_bytes(&LED_APPLY)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ctrl_aura::{config::AuraConfig, controller::LEDNode},
|
||||||
|
laptops::LaptopLedData,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::CtrlKbdLed;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// #[ignore = "Must be manually run due to detection stage"]
|
||||||
|
fn check_set_mode_errors() {
|
||||||
|
// Checking to ensure set_mode errors when unsupported modes are tried
|
||||||
|
let config = AuraConfig::default();
|
||||||
|
let supported_modes = LaptopLedData {
|
||||||
|
prod_family: "".into(),
|
||||||
|
board_names: vec![],
|
||||||
|
standard: vec![AuraModeNum::Static],
|
||||||
|
multizone: vec![],
|
||||||
|
per_key: false,
|
||||||
|
};
|
||||||
|
let mut controller = CtrlKbdLed {
|
||||||
|
led_prod: None,
|
||||||
|
led_node: LEDNode::None,
|
||||||
|
bright_node: String::new(),
|
||||||
|
supported_modes,
|
||||||
|
flip_effect_write: false,
|
||||||
|
config,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut effect = AuraEffect::default();
|
||||||
|
effect.colour1 = Colour(0xff, 0x00, 0xff);
|
||||||
|
effect.zone = AuraZone::None;
|
||||||
|
|
||||||
|
// This error comes from write_bytes because we don't have a keyboard node stored
|
||||||
|
assert_eq!(
|
||||||
|
controller
|
||||||
|
.set_effect(effect.clone())
|
||||||
|
.unwrap_err()
|
||||||
|
.to_string(),
|
||||||
|
"No Aura keyboard node found"
|
||||||
|
);
|
||||||
|
|
||||||
|
effect.mode = AuraModeNum::Laser;
|
||||||
|
assert_eq!(
|
||||||
|
controller
|
||||||
|
.set_effect(effect.clone())
|
||||||
|
.unwrap_err()
|
||||||
|
.to_string(),
|
||||||
|
"Aura effect not supported"
|
||||||
|
);
|
||||||
|
|
||||||
|
effect.mode = AuraModeNum::Static;
|
||||||
|
effect.zone = AuraZone::Key2;
|
||||||
|
assert_eq!(
|
||||||
|
controller
|
||||||
|
.set_effect(effect.clone())
|
||||||
|
.unwrap_err()
|
||||||
|
.to_string(),
|
||||||
|
"Aura effect not supported"
|
||||||
|
);
|
||||||
|
|
||||||
|
controller.supported_modes.multizone.push(AuraZone::Key2);
|
||||||
|
assert_eq!(
|
||||||
|
controller
|
||||||
|
.set_effect(effect.clone())
|
||||||
|
.unwrap_err()
|
||||||
|
.to_string(),
|
||||||
|
"No Aura keyboard node found"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_multizone_if_no_config() {
|
||||||
|
// Checking to ensure set_mode errors when unsupported modes are tried
|
||||||
|
let config = AuraConfig::default();
|
||||||
|
let supported_modes = LaptopLedData {
|
||||||
|
prod_family: "".into(),
|
||||||
|
board_names: vec![],
|
||||||
|
standard: vec![AuraModeNum::Static],
|
||||||
|
multizone: vec![],
|
||||||
|
per_key: false,
|
||||||
|
};
|
||||||
|
let mut controller = CtrlKbdLed {
|
||||||
|
led_prod: None,
|
||||||
|
led_node: LEDNode::None,
|
||||||
|
bright_node: String::new(),
|
||||||
|
supported_modes,
|
||||||
|
flip_effect_write: false,
|
||||||
|
config,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(controller.config.multizone.is_none());
|
||||||
|
assert!(controller.create_multizone_default().is_err());
|
||||||
|
assert!(controller.config.multizone.is_none());
|
||||||
|
|
||||||
|
controller.supported_modes.multizone.push(AuraZone::Key1);
|
||||||
|
controller.supported_modes.multizone.push(AuraZone::Key2);
|
||||||
|
assert!(controller.create_multizone_default().is_ok());
|
||||||
|
assert!(controller.config.multizone.is_some());
|
||||||
|
|
||||||
|
let m = controller.config.multizone.unwrap();
|
||||||
|
assert!(m.contains_key(&AuraModeNum::Static));
|
||||||
|
let e = m.get(&AuraModeNum::Static).unwrap();
|
||||||
|
assert_eq!(e.len(), 2);
|
||||||
|
assert_eq!(e[0].zone, AuraZone::Key1);
|
||||||
|
assert_eq!(e[1].zone, AuraZone::Key2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn next_mode_create_multizone_if_no_config() {
|
||||||
|
// Checking to ensure set_mode errors when unsupported modes are tried
|
||||||
|
let config = AuraConfig::default();
|
||||||
|
let supported_modes = LaptopLedData {
|
||||||
|
prod_family: "".into(),
|
||||||
|
board_names: vec![],
|
||||||
|
standard: vec![AuraModeNum::Static],
|
||||||
|
multizone: vec![AuraZone::Key1, AuraZone::Key2],
|
||||||
|
per_key: false,
|
||||||
|
};
|
||||||
|
let mut controller = CtrlKbdLed {
|
||||||
|
led_prod: None,
|
||||||
|
led_node: LEDNode::None,
|
||||||
|
bright_node: String::new(),
|
||||||
|
supported_modes,
|
||||||
|
flip_effect_write: false,
|
||||||
|
config,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(controller.config.multizone.is_none());
|
||||||
|
controller.config.multizone_on = true;
|
||||||
|
// This is called in toggle_mode. It will error here because we have no
|
||||||
|
// keyboard node in tests.
|
||||||
|
assert_eq!(
|
||||||
|
controller
|
||||||
|
.write_current_config_mode()
|
||||||
|
.unwrap_err()
|
||||||
|
.to_string(),
|
||||||
|
"No Aura keyboard node found"
|
||||||
|
);
|
||||||
|
assert!(controller.config.multizone.is_some());
|
||||||
|
|
||||||
|
let m = controller.config.multizone.unwrap();
|
||||||
|
assert!(m.contains_key(&AuraModeNum::Static));
|
||||||
|
let e = m.get(&AuraModeNum::Static).unwrap();
|
||||||
|
assert_eq!(e.len(), 2);
|
||||||
|
assert_eq!(e[0].zone, AuraZone::Key1);
|
||||||
|
assert_eq!(e[1].zone, AuraZone::Key2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
use log::{error, warn};
|
use std::collections::BTreeMap;
|
||||||
use rog_aura::{AuraEffect, LedBrightness, LedPowerStates};
|
|
||||||
use zbus::dbus_interface;
|
use async_trait::async_trait;
|
||||||
use zvariant::ObjectPath;
|
use log::warn;
|
||||||
|
use rog_aura::{usb::AuraPowerDev, AuraEffect, AuraModeNum, LedBrightness};
|
||||||
|
use zbus::{dbus_interface, Connection, SignalContext};
|
||||||
|
|
||||||
use super::controller::CtrlKbdLedZbus;
|
use super::controller::CtrlKbdLedZbus;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl crate::ZbusAdd for CtrlKbdLedZbus {
|
impl crate::ZbusAdd for CtrlKbdLedZbus {
|
||||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
async fn add_to_server(self, server: &mut Connection) {
|
||||||
server
|
Self::add_to_server_helper(self, "/org/asuslinux/Aura", server).await;
|
||||||
.at(&ObjectPath::from_str_unchecked("/org/asuslinux/Led"), self)
|
|
||||||
.map_err(|err| {
|
|
||||||
error!("DbusKbdLed: add_to_server {}", err);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,7 +20,7 @@ impl crate::ZbusAdd for CtrlKbdLedZbus {
|
|||||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||||
impl CtrlKbdLedZbus {
|
impl CtrlKbdLedZbus {
|
||||||
/// Set the keyboard brightness level (0-3)
|
/// Set the keyboard brightness level (0-3)
|
||||||
fn set_brightness(&mut self, brightness: LedBrightness) {
|
async fn set_brightness(&mut self, brightness: LedBrightness) {
|
||||||
if let Ok(ctrl) = self.0.try_lock() {
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
ctrl.set_brightness(brightness)
|
ctrl.set_brightness(brightness)
|
||||||
.map_err(|err| warn!("{}", err))
|
.map_err(|err| warn!("{}", err))
|
||||||
@@ -30,140 +28,191 @@ impl CtrlKbdLedZbus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the keyboard LED to enabled while the device is awake
|
/// Set a variety of states, input is array of enum.
|
||||||
fn set_awake_enabled(&mut self, enabled: bool) {
|
/// `enabled` sets if the sent array should be disabled or enabled
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// pub struct AuraPowerDev {
|
||||||
|
/// pub x1866: Vec<AuraDev1866>,
|
||||||
|
/// pub x19b6: Vec<AuraDev19b6>,
|
||||||
|
/// }
|
||||||
|
/// pub enum AuraDev1866 {
|
||||||
|
/// Awake,
|
||||||
|
/// Keyboard,
|
||||||
|
/// Lightbar,
|
||||||
|
/// Boot,
|
||||||
|
/// Sleep,
|
||||||
|
/// }
|
||||||
|
/// enum AuraDev19b6 {
|
||||||
|
/// BootLogo,
|
||||||
|
/// BootKeyb,
|
||||||
|
/// AwakeLogo,
|
||||||
|
/// AwakeKeyb,
|
||||||
|
/// SleepLogo,
|
||||||
|
/// SleepKeyb,
|
||||||
|
/// ShutdownLogo,
|
||||||
|
/// ShutdownKeyb,
|
||||||
|
/// AwakeBar,
|
||||||
|
/// BootBar,
|
||||||
|
/// SleepBar,
|
||||||
|
/// ShutdownBar,
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
async fn set_leds_power(
|
||||||
|
&mut self,
|
||||||
|
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||||
|
options: AuraPowerDev,
|
||||||
|
enabled: bool,
|
||||||
|
) -> zbus::fdo::Result<()> {
|
||||||
|
let mut states = None;
|
||||||
if let Ok(mut ctrl) = self.0.try_lock() {
|
if let Ok(mut ctrl) = self.0.try_lock() {
|
||||||
ctrl.set_states_enabled(enabled, ctrl.config.sleep_anim_enabled)
|
for p in options.tuf {
|
||||||
.map_err(|err| warn!("{}", err))
|
ctrl.config.enabled.set_tuf(p, enabled);
|
||||||
.ok();
|
}
|
||||||
ctrl.config.awake_enabled = enabled;
|
for p in options.x1866 {
|
||||||
|
ctrl.config.enabled.set_0x1866(p, enabled);
|
||||||
|
}
|
||||||
|
for p in options.x19b6 {
|
||||||
|
ctrl.config.enabled.set_0x19b6(p, enabled);
|
||||||
|
}
|
||||||
|
|
||||||
ctrl.config.write();
|
ctrl.config.write();
|
||||||
|
|
||||||
let states = LedPowerStates {
|
ctrl.set_power_states(&ctrl.config).map_err(|e| {
|
||||||
enabled: ctrl.config.awake_enabled,
|
warn!("{}", e);
|
||||||
sleep_anim_enabled: ctrl.config.sleep_anim_enabled,
|
e
|
||||||
};
|
})?;
|
||||||
self.notify_power_states(&states)
|
|
||||||
|
states = Some(AuraPowerDev::from(&ctrl.config.enabled));
|
||||||
|
}
|
||||||
|
// Need to pull state out like this due to MutexGuard
|
||||||
|
if let Some(states) = states {
|
||||||
|
Self::notify_power_states(&ctxt, &states)
|
||||||
|
.await
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the keyboard LED suspend animation to enabled while the device is suspended
|
async fn set_led_mode(
|
||||||
fn set_sleep_enabled(&mut self, enabled: bool) {
|
&mut self,
|
||||||
|
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||||
|
effect: AuraEffect,
|
||||||
|
) -> zbus::fdo::Result<()> {
|
||||||
|
let mut led = None;
|
||||||
if let Ok(mut ctrl) = self.0.try_lock() {
|
if let Ok(mut ctrl) = self.0.try_lock() {
|
||||||
ctrl.set_states_enabled(ctrl.config.awake_enabled, enabled)
|
ctrl.set_effect(effect).map_err(|e| {
|
||||||
.map_err(|err| warn!("{}", err))
|
warn!("{}", e);
|
||||||
.ok();
|
e
|
||||||
ctrl.config.sleep_anim_enabled = enabled;
|
})?;
|
||||||
ctrl.config.write();
|
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
|
||||||
let states = LedPowerStates {
|
led = Some(mode.clone());
|
||||||
enabled: ctrl.config.awake_enabled,
|
|
||||||
sleep_anim_enabled: ctrl.config.sleep_anim_enabled,
|
|
||||||
};
|
|
||||||
self.notify_power_states(&states)
|
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_led_mode(&mut self, effect: AuraEffect) {
|
|
||||||
if let Ok(mut ctrl) = self.0.try_lock() {
|
|
||||||
match ctrl.do_command(effect) {
|
|
||||||
Ok(_) => {
|
|
||||||
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
|
|
||||||
self.notify_led(mode.clone())
|
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
warn!("{}", err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(led) = led {
|
||||||
|
Self::notify_led(&ctxt, led)
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_led_mode(&self) {
|
async fn next_led_mode(
|
||||||
if let Ok(mut ctrl) = self.0.try_lock() {
|
&self,
|
||||||
ctrl.toggle_mode(false)
|
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
) -> zbus::fdo::Result<()> {
|
||||||
|
let mut led = None;
|
||||||
|
if let Ok(mut ctrl) = self.0.lock() {
|
||||||
|
ctrl.toggle_mode(false).map_err(|e| {
|
||||||
|
warn!("{}", e);
|
||||||
|
e
|
||||||
|
})?;
|
||||||
|
|
||||||
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
|
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
|
||||||
self.notify_led(mode.clone())
|
led = Some(mode.clone());
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(led) = led {
|
||||||
|
Self::notify_led(&ctxt, led)
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prev_led_mode(&self) {
|
async fn prev_led_mode(
|
||||||
if let Ok(mut ctrl) = self.0.try_lock() {
|
&self,
|
||||||
ctrl.toggle_mode(true)
|
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
) -> zbus::fdo::Result<()> {
|
||||||
|
let mut led = None;
|
||||||
|
if let Ok(mut ctrl) = self.0.lock() {
|
||||||
|
ctrl.toggle_mode(true).map_err(|e| {
|
||||||
|
warn!("{}", e);
|
||||||
|
e
|
||||||
|
})?;
|
||||||
|
|
||||||
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
|
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
|
||||||
self.notify_led(mode.clone())
|
led = Some(mode.clone());
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if let Some(led) = led {
|
||||||
|
Self::notify_led(&ctxt, led)
|
||||||
fn next_led_brightness(&self) {
|
.await
|
||||||
if let Ok(mut ctrl) = self.0.try_lock() {
|
|
||||||
ctrl.next_brightness()
|
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prev_led_brightness(&self) {
|
async fn next_led_brightness(&self) -> zbus::fdo::Result<()> {
|
||||||
if let Ok(mut ctrl) = self.0.try_lock() {
|
if let Ok(mut ctrl) = self.0.try_lock() {
|
||||||
ctrl.prev_brightness()
|
ctrl.next_brightness().map_err(|e| {
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
warn!("{}", e);
|
||||||
|
e
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[dbus_interface(property)]
|
async fn prev_led_brightness(&self) -> zbus::fdo::Result<()> {
|
||||||
fn awake_enabled(&self) -> bool {
|
if let Ok(mut ctrl) = self.0.try_lock() {
|
||||||
if let Ok(ctrl) = self.0.try_lock() {
|
ctrl.prev_brightness().map_err(|e| {
|
||||||
return ctrl.config.awake_enabled;
|
warn!("{}", e);
|
||||||
|
e
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
true
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[dbus_interface(property)]
|
// As property doesn't work for AuraPowerDev (complexity of serialization?)
|
||||||
fn sleep_enabled(&self) -> bool {
|
// #[dbus_interface(property)]
|
||||||
if let Ok(ctrl) = self.0.try_lock() {
|
async fn leds_enabled(&self) -> AuraPowerDev {
|
||||||
return ctrl.config.sleep_anim_enabled;
|
loop {
|
||||||
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
|
return AuraPowerDev::from(&ctrl.config.enabled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the current mode data
|
/// Return the current mode data
|
||||||
#[dbus_interface(property)]
|
async fn led_mode(&self) -> AuraModeNum {
|
||||||
fn led_mode(&self) -> String {
|
|
||||||
if let Ok(ctrl) = self.0.try_lock() {
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
|
return ctrl.config.current_mode;
|
||||||
if let Ok(json) = serde_json::to_string(&mode) {
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
warn!("SetKeyBacklight could not deserialise");
|
AuraModeNum::Static
|
||||||
"SetKeyBacklight could not deserialise".to_string()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a list of available modes
|
/// Return a list of available modes
|
||||||
#[dbus_interface(property)]
|
async fn led_modes(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
|
||||||
fn led_modes(&self) -> String {
|
loop {
|
||||||
if let Ok(ctrl) = self.0.try_lock() {
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
if let Ok(json) = serde_json::to_string(&ctrl.config.builtins) {
|
return ctrl.config.builtins.clone();
|
||||||
return json;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
warn!("SetKeyBacklight could not deserialise");
|
|
||||||
"SetKeyBacklight could not serialise".to_string()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the current LED brightness
|
/// Return the current LED brightness
|
||||||
#[dbus_interface(property)]
|
#[dbus_interface(property)]
|
||||||
fn led_brightness(&self) -> i8 {
|
async fn led_brightness(&self) -> i8 {
|
||||||
if let Ok(ctrl) = self.0.try_lock() {
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
return ctrl.get_brightness().map(|n| n as i8).unwrap_or(-1);
|
return ctrl.get_brightness().map(|n| n as i8).unwrap_or(-1);
|
||||||
}
|
}
|
||||||
@@ -172,8 +221,11 @@ impl CtrlKbdLedZbus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[dbus_interface(signal)]
|
#[dbus_interface(signal)]
|
||||||
fn notify_led(&self, data: AuraEffect) -> zbus::Result<()>;
|
async fn notify_led(signal_ctxt: &SignalContext<'_>, data: AuraEffect) -> zbus::Result<()>;
|
||||||
|
|
||||||
#[dbus_interface(signal)]
|
#[dbus_interface(signal)]
|
||||||
fn notify_power_states(&self, data: &LedPowerStates) -> zbus::Result<()>;
|
async fn notify_power_states(
|
||||||
|
signal_ctxt: &SignalContext<'_>,
|
||||||
|
data: &AuraPowerDev,
|
||||||
|
) -> zbus::Result<()>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use log::{error, warn};
|
use log::{error, warn};
|
||||||
use rog_profiles::fan_curve_set::FanCurveSet;
|
|
||||||
use rog_profiles::{FanCurveProfiles, Profile};
|
use rog_profiles::{FanCurveProfiles, Profile};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
@@ -17,20 +16,11 @@ pub struct ProfileConfig {
|
|||||||
|
|
||||||
impl ProfileConfig {
|
impl ProfileConfig {
|
||||||
fn new(config_path: String) -> Self {
|
fn new(config_path: String) -> Self {
|
||||||
let mut platform = ProfileConfig {
|
Self {
|
||||||
config_path,
|
config_path,
|
||||||
active_profile: Profile::get_active_profile().unwrap_or(Profile::Balanced),
|
active_profile: Profile::Balanced,
|
||||||
fan_curves: None,
|
fan_curves: None,
|
||||||
};
|
|
||||||
|
|
||||||
if let Ok(res) = FanCurveSet::is_supported() {
|
|
||||||
if res {
|
|
||||||
let curves = FanCurveProfiles::default();
|
|
||||||
platform.fan_curves = Some(curves);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
platform
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load(config_path: String) -> Self {
|
pub fn load(config_path: String) -> Self {
|
||||||
@@ -49,13 +39,22 @@ impl ProfileConfig {
|
|||||||
config = data;
|
config = data;
|
||||||
config.config_path = config_path;
|
config.config_path = config_path;
|
||||||
} else {
|
} else {
|
||||||
warn!("Could not deserialise {}", config_path);
|
warn!(
|
||||||
panic!("Please remove {} then restart service", config_path);
|
"Could not deserialise {}.\nWill rename to {}-old and recreate config",
|
||||||
|
config_path, config_path
|
||||||
|
);
|
||||||
|
let cfg_old = config_path.clone() + "-old";
|
||||||
|
std::fs::rename(config_path.clone(), cfg_old).unwrap_or_else(|err| {
|
||||||
|
panic!(
|
||||||
|
"Could not rename. Please remove {} then restart service: Error {}",
|
||||||
|
config_path, err
|
||||||
|
)
|
||||||
|
});
|
||||||
|
config = Self::new(config_path);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
config = Self::new(config_path)
|
config = Self::new(config_path);
|
||||||
}
|
}
|
||||||
config.write();
|
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use crate::error::RogError;
|
use crate::error::RogError;
|
||||||
use crate::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::fan_curve_set::FanCurveSet;
|
use rog_profiles::{FanCurveProfiles, Profile};
|
||||||
use rog_profiles::Profile;
|
|
||||||
use rog_supported::PlatformProfileFunctions;
|
use rog_supported::PlatformProfileFunctions;
|
||||||
use udev::Device;
|
use smol::Executor;
|
||||||
|
|
||||||
use super::config::ProfileConfig;
|
use super::config::ProfileConfig;
|
||||||
|
|
||||||
pub struct CtrlPlatformProfile {
|
pub struct CtrlPlatformProfile {
|
||||||
pub config: ProfileConfig,
|
pub config: ProfileConfig,
|
||||||
pub fan_device: Option<Device>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GetSupported for CtrlPlatformProfile {
|
impl GetSupported for CtrlPlatformProfile {
|
||||||
@@ -19,30 +20,17 @@ impl GetSupported for CtrlPlatformProfile {
|
|||||||
|
|
||||||
fn get_supported() -> Self::A {
|
fn get_supported() -> Self::A {
|
||||||
if !Profile::is_platform_profile_supported() {
|
if !Profile::is_platform_profile_supported() {
|
||||||
warn!(
|
warn!("platform_profile kernel interface not found, your laptop does not support this, or the interface is missing.");
|
||||||
r#"
|
|
||||||
platform_profile kernel interface not found, your laptop does not support this, or the interface is missing.
|
|
||||||
To enable profile support you require a kernel with the following patch applied:
|
|
||||||
https://lkml.org/lkml/2021/8/18/1022
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = FanCurveSet::is_supported();
|
let res = FanCurveProfiles::is_supported();
|
||||||
let mut fan_curve_supported = res.is_err();
|
let mut fan_curve_supported = res.is_err();
|
||||||
if let Ok(r) = res {
|
if let Ok(r) = res {
|
||||||
fan_curve_supported = r;
|
fan_curve_supported = r;
|
||||||
};
|
};
|
||||||
|
|
||||||
if fan_curve_supported {
|
if !fan_curve_supported {
|
||||||
info!(
|
info!("fan curves kernel interface not found, your laptop does not support this, or the interface is missing.");
|
||||||
r#"
|
|
||||||
fan curves kernel interface not found, your laptop does not support this, or the interface is missing.
|
|
||||||
To enable fan-curve support you require a kernel with the following patch applied:
|
|
||||||
https://lkml.org/lkml/2021/8/20/232
|
|
||||||
Please note that as of 24/08/2021 this is not final.
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PlatformProfileFunctions {
|
PlatformProfileFunctions {
|
||||||
@@ -55,9 +43,12 @@ Please note that as of 24/08/2021 this is not final.
|
|||||||
impl crate::Reloadable for CtrlPlatformProfile {
|
impl crate::Reloadable for CtrlPlatformProfile {
|
||||||
/// Fetch the active profile and use that to set all related components up
|
/// Fetch the active profile and use that to set all related components up
|
||||||
fn reload(&mut self) -> Result<(), RogError> {
|
fn reload(&mut self) -> Result<(), RogError> {
|
||||||
if let Some(curves) = &self.config.fan_curves {
|
if let Some(curves) = &mut self.config.fan_curves {
|
||||||
if let Ok(mut device) = FanCurveSet::get_device() {
|
if let Ok(mut device) = FanCurveProfiles::get_device() {
|
||||||
curves.write_to_platform(self.config.active_profile, &mut device);
|
// There is a possibility that the curve was default zeroed, so this call initialises
|
||||||
|
// the data from system read and we need to save it after
|
||||||
|
curves.write_profile_curve_to_platform(self.config.active_profile, &mut device)?;
|
||||||
|
self.config.write();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -65,30 +56,53 @@ impl crate::Reloadable for CtrlPlatformProfile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CtrlPlatformProfile {
|
impl CtrlPlatformProfile {
|
||||||
pub fn new(mut config: ProfileConfig, fan_device: Option<Device>) -> Result<Self, RogError> {
|
pub fn new(mut config: ProfileConfig) -> Result<Self, RogError> {
|
||||||
if Profile::is_platform_profile_supported() {
|
if Profile::is_platform_profile_supported() {
|
||||||
info!("Device has profile control available");
|
info!("Device has profile control available");
|
||||||
|
|
||||||
if let Some(ref device) = fan_device {
|
if FanCurveProfiles::get_device().is_ok() {
|
||||||
let profile = config.active_profile;
|
info!("Device has fan curves available");
|
||||||
config
|
if config.fan_curves.is_none() {
|
||||||
.fan_curves
|
let active = Profile::get_active_profile().unwrap_or(Profile::Balanced);
|
||||||
.as_mut()
|
let dev = FanCurveProfiles::get_device()?;
|
||||||
.unwrap()
|
let mut curves = FanCurveProfiles::default();
|
||||||
.read_from_dev_profile(profile, device);
|
|
||||||
}
|
|
||||||
config.write();
|
|
||||||
|
|
||||||
return Ok(CtrlPlatformProfile { config, fan_device });
|
warn!("No default fan-curves: cycling profiles to set defaults");
|
||||||
|
Profile::set_profile(Profile::Balanced)?;
|
||||||
|
curves.read_from_dev_profile(Profile::Balanced, &dev);
|
||||||
|
info!(
|
||||||
|
"{:?}: {}",
|
||||||
|
config.active_profile,
|
||||||
|
String::from(curves.get_fan_curves_for(Profile::Balanced))
|
||||||
|
);
|
||||||
|
Profile::set_profile(Profile::Performance)?;
|
||||||
|
curves.read_from_dev_profile(Profile::Performance, &dev);
|
||||||
|
info!(
|
||||||
|
"{:?}: {}",
|
||||||
|
config.active_profile,
|
||||||
|
String::from(curves.get_fan_curves_for(Profile::Performance))
|
||||||
|
);
|
||||||
|
Profile::set_profile(Profile::Quiet)?;
|
||||||
|
curves.read_from_dev_profile(Profile::Quiet, &dev);
|
||||||
|
info!(
|
||||||
|
"{:?}: {}",
|
||||||
|
config.active_profile,
|
||||||
|
String::from(curves.get_fan_curves_for(Profile::Quiet))
|
||||||
|
);
|
||||||
|
|
||||||
|
Profile::set_profile(active)?;
|
||||||
|
config.fan_curves = Some(curves);
|
||||||
|
config.write();
|
||||||
|
info!("Set fan curve defaults");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(CtrlPlatformProfile { config });
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(ProfileError::NotSupported.into())
|
Err(ProfileError::NotSupported.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_device(&self) -> Option<Device> {
|
|
||||||
self.fan_device.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn save_config(&self) {
|
pub fn save_config(&self) {
|
||||||
self.config.write();
|
self.config.write();
|
||||||
}
|
}
|
||||||
@@ -96,8 +110,6 @@ impl CtrlPlatformProfile {
|
|||||||
/// Toggle to next profile in list. This will first read the config, switch, then write out
|
/// Toggle to next profile in list. This will first read the config, switch, then write out
|
||||||
pub(super) fn set_next_profile(&mut self) -> Result<(), RogError> {
|
pub(super) fn set_next_profile(&mut self) -> Result<(), RogError> {
|
||||||
// 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
|
||||||
self.config.read();
|
|
||||||
|
|
||||||
match self.config.active_profile {
|
match self.config.active_profile {
|
||||||
Profile::Balanced => {
|
Profile::Balanced => {
|
||||||
Profile::set_profile(Profile::Performance)?;
|
Profile::set_profile(Profile::Performance)?;
|
||||||
@@ -112,9 +124,55 @@ impl CtrlPlatformProfile {
|
|||||||
self.config.active_profile = Profile::Balanced;
|
self.config.active_profile = Profile::Balanced;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.write_profile_curve_to_platform()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
self.config.write();
|
/// Set the curve for the active profile active
|
||||||
|
pub(super) fn write_profile_curve_to_platform(&mut self) -> Result<(), RogError> {
|
||||||
|
if let Some(curves) = &mut self.config.fan_curves {
|
||||||
|
if let Ok(mut device) = FanCurveProfiles::get_device() {
|
||||||
|
curves.write_profile_curve_to_platform(self.config.active_profile, &mut device)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_active_curve_to_defaults(&mut self) -> Result<(), RogError> {
|
||||||
|
if let Some(curves) = self.config.fan_curves.as_mut() {
|
||||||
|
if let Ok(mut device) = FanCurveProfiles::get_device() {
|
||||||
|
curves.set_active_curve_to_defaults(self.config.active_profile, &mut device)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CtrlProfileTask {
|
||||||
|
ctrl: Arc<Mutex<CtrlPlatformProfile>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CtrlProfileTask {
|
||||||
|
pub fn new(ctrl: Arc<Mutex<CtrlPlatformProfile>>) -> Self {
|
||||||
|
Self { ctrl }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl CtrlTask for CtrlProfileTask {
|
||||||
|
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
|
||||||
|
let ctrl = self.ctrl.clone();
|
||||||
|
self.repeating_task(666, executor, move || {
|
||||||
|
if let Ok(ref mut lock) = ctrl.try_lock() {
|
||||||
|
let new_profile = Profile::get_active_profile().unwrap();
|
||||||
|
if new_profile != lock.config.active_profile {
|
||||||
|
lock.config.active_profile = new_profile;
|
||||||
|
lock.write_profile_curve_to_platform().unwrap();
|
||||||
|
lock.save_config();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -35,13 +37,19 @@ impl ProfileZbus {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Toggle to next platform_profile. Names provided by `Profiles`
|
/// Toggle to next platform_profile. Names provided by `Profiles`.
|
||||||
fn next_profile(&mut self) {
|
/// If fan-curves are supported will also activate a fan curve for profile.
|
||||||
|
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();
|
||||||
|
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
|
||||||
@@ -56,18 +64,29 @@ 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();
|
||||||
Profile::set_profile(profile)
|
Profile::set_profile(profile)
|
||||||
.map_err(|e| warn!("Profile::set_profile, {}", e))
|
.map_err(|e| warn!("set_profile, {}", e))
|
||||||
.ok();
|
.ok();
|
||||||
ctrl.config.active_profile = profile;
|
ctrl.config.active_profile = profile;
|
||||||
|
ctrl.write_profile_curve_to_platform()
|
||||||
|
.map_err(|e| warn!("write_profile_curve_to_platform, {}", e))
|
||||||
|
.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.
|
||||||
@@ -84,14 +103,23 @@ impl ProfileZbus {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a list of profiles that have fan-curves enabled.
|
/// Set a profile fan curve enabled status. Will also activate a fan curve if in the
|
||||||
fn set_enabled_fan_profiles(&mut self, profiles: Vec<Profile>) -> zbus::fdo::Result<()> {
|
/// same profile mode
|
||||||
|
fn set_fan_curve_enabled(&mut self, profile: Profile, enabled: bool) -> zbus::fdo::Result<()> {
|
||||||
if let Ok(mut ctrl) = self.inner.try_lock() {
|
if let Ok(mut ctrl) = self.inner.try_lock() {
|
||||||
ctrl.config.read();
|
ctrl.config.read();
|
||||||
if let Some(curves) = &mut ctrl.config.fan_curves {
|
return if let Some(curves) = &mut ctrl.config.fan_curves {
|
||||||
curves.set_enabled_curve_profiles(profiles);
|
curves.set_profile_curve_enabled(profile, enabled);
|
||||||
}
|
|
||||||
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
|
ctrl.write_profile_curve_to_platform()
|
||||||
|
.map_err(|e| warn!("write_profile_curve_to_platform, {}", e))
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
ctrl.save_config();
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::Failed(UNSUPPORTED_MSG.to_string()))
|
||||||
|
};
|
||||||
}
|
}
|
||||||
Err(Error::Failed(
|
Err(Error::Failed(
|
||||||
"Failed to get enabled fan curve names".to_string(),
|
"Failed to get enabled fan curve names".to_string(),
|
||||||
@@ -99,72 +127,61 @@ impl ProfileZbus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the fan-curve data for the currently active Profile
|
/// Get the fan-curve data for the currently active Profile
|
||||||
fn active_fan_curve_data(&mut self) -> zbus::fdo::Result<FanCurveSet> {
|
fn fan_curve_data(&mut self, profile: Profile) -> zbus::fdo::Result<FanCurveSet> {
|
||||||
if let Ok(mut ctrl) = self.inner.try_lock() {
|
if let Ok(mut ctrl) = self.inner.try_lock() {
|
||||||
ctrl.config.read();
|
ctrl.config.read();
|
||||||
if let Some(curves) = &ctrl.config.fan_curves {
|
if let Some(curves) = &ctrl.config.fan_curves {
|
||||||
return Ok((*curves.get_active_fan_curves()).clone());
|
let curve = curves.get_fan_curves_for(profile);
|
||||||
|
return Ok(curve.clone());
|
||||||
}
|
}
|
||||||
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
|
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
|
||||||
}
|
}
|
||||||
Err(Error::Failed("Failed to get fan curve data".to_string()))
|
Err(Error::Failed("Failed to get fan curve data".to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get fan-curve data for each Profile as an array of objects
|
/// Set the fan curve for the specified profile.
|
||||||
fn fan_curves(&self) -> zbus::fdo::Result<Vec<FanCurveSet>> {
|
/// Will also activate the fan curve if the user is in the same mode.
|
||||||
|
fn set_fan_curve(&self, profile: Profile, curve: CurveData) -> zbus::fdo::Result<()> {
|
||||||
if let Ok(mut ctrl) = self.inner.try_lock() {
|
if let Ok(mut ctrl) = self.inner.try_lock() {
|
||||||
ctrl.config.read();
|
ctrl.config.read();
|
||||||
if let Some(curves) = &ctrl.config.fan_curves {
|
if let Some(curves) = &mut ctrl.config.fan_curves {
|
||||||
return Ok(curves.get_all_fan_curves());
|
curves
|
||||||
}
|
.save_fan_curve(curve, profile)
|
||||||
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
|
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
|
||||||
}
|
|
||||||
Err(Error::Failed("Failed to get all fan curves".to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set this fan-curve data
|
|
||||||
fn set_fan_curve(&self, curve: CurveData) -> zbus::fdo::Result<()> {
|
|
||||||
if let Ok(mut ctrl) = self.inner.try_lock() {
|
|
||||||
ctrl.config.read();
|
|
||||||
let profile = ctrl.config.active_profile;
|
|
||||||
if let Some(mut device) = ctrl.get_device() {
|
|
||||||
if let Some(curves) = &mut ctrl.config.fan_curves {
|
|
||||||
curves.write_and_set_fan_curve(curve, profile, &mut device);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
|
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
|
||||||
}
|
}
|
||||||
|
ctrl.write_profile_curve_to_platform()
|
||||||
|
.map_err(|e| warn!("Profile::set_profile, {}", e))
|
||||||
|
.ok();
|
||||||
ctrl.save_config();
|
ctrl.save_config();
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
Err(Error::Failed("Failed to set fan curves".to_string()))
|
/// Reset the stored (self) and device curve to the defaults of the platform.
|
||||||
|
///
|
||||||
|
/// Each platform_profile has a different default and the defualt can be read
|
||||||
|
/// only for the currently active profile.
|
||||||
|
fn set_active_curve_to_defaults(&self) -> zbus::fdo::Result<()> {
|
||||||
|
if let Ok(mut ctrl) = self.inner.try_lock() {
|
||||||
|
ctrl.config.read();
|
||||||
|
ctrl.set_active_curve_to_defaults()
|
||||||
|
.map_err(|e| warn!("Profile::set_active_curve_to_defaults, {}", e))
|
||||||
|
.ok();
|
||||||
|
ctrl.save_config();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::{config::Config, error::RogError, GetSupported};
|
use crate::{config::Config, error::RogError, GetSupported};
|
||||||
|
use async_trait::async_trait;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use rog_supported::RogBiosSupportedFunctions;
|
use rog_supported::RogBiosSupportedFunctions;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
@@ -8,8 +9,8 @@ use std::path::Path;
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use zbus::dbus_interface;
|
use zbus::Connection;
|
||||||
use zvariant::ObjectPath;
|
use zbus::{dbus_interface, SignalContext};
|
||||||
|
|
||||||
const INITRAMFS_PATH: &str = "/usr/sbin/update-initramfs";
|
const INITRAMFS_PATH: &str = "/usr/sbin/update-initramfs";
|
||||||
const DRACUT_PATH: &str = "/usr/bin/dracut";
|
const DRACUT_PATH: &str = "/usr/bin/dracut";
|
||||||
@@ -18,6 +19,9 @@ static ASUS_SWITCH_GRAPHIC_MODE: &str =
|
|||||||
"/sys/firmware/efi/efivars/AsusSwitchGraphicMode-607005d5-3f75-4b2e-98f0-85ba66797a3e";
|
"/sys/firmware/efi/efivars/AsusSwitchGraphicMode-607005d5-3f75-4b2e-98f0-85ba66797a3e";
|
||||||
static ASUS_POST_LOGO_SOUND: &str =
|
static ASUS_POST_LOGO_SOUND: &str =
|
||||||
"/sys/firmware/efi/efivars/AsusPostLogoSound-607005d5-3f75-4b2e-98f0-85ba66797a3e";
|
"/sys/firmware/efi/efivars/AsusPostLogoSound-607005d5-3f75-4b2e-98f0-85ba66797a3e";
|
||||||
|
static ASUS_PANEL_OD_PATH: &str = "/sys/devices/platform/asus-nb-wmi/panel_od";
|
||||||
|
static ASUS_DGPU_DISABLE_PATH: &str = "/sys/devices/platform/asus-nb-wmi/dgpu_disable";
|
||||||
|
static ASUS_EGPU_ENABLE_PATH: &str = "/sys/devices/platform/asus-nb-wmi/egpu_enable";
|
||||||
|
|
||||||
pub struct CtrlRogBios {
|
pub struct CtrlRogBios {
|
||||||
_config: Arc<Mutex<Config>>,
|
_config: Arc<Mutex<Config>>,
|
||||||
@@ -28,30 +32,34 @@ impl GetSupported for CtrlRogBios {
|
|||||||
|
|
||||||
fn get_supported() -> Self::A {
|
fn get_supported() -> Self::A {
|
||||||
RogBiosSupportedFunctions {
|
RogBiosSupportedFunctions {
|
||||||
post_sound_toggle: Path::new(ASUS_POST_LOGO_SOUND).exists(),
|
post_sound: Path::new(ASUS_POST_LOGO_SOUND).exists(),
|
||||||
dedicated_gfx_toggle: Path::new(ASUS_SWITCH_GRAPHIC_MODE).exists(),
|
dedicated_gfx: Path::new(ASUS_SWITCH_GRAPHIC_MODE).exists(),
|
||||||
|
panel_overdrive: Path::new(ASUS_PANEL_OD_PATH).exists(),
|
||||||
|
dgpu_disable: Path::new(ASUS_DGPU_DISABLE_PATH).exists(),
|
||||||
|
egpu_enable: Path::new(ASUS_EGPU_ENABLE_PATH).exists(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||||
impl CtrlRogBios {
|
impl CtrlRogBios {
|
||||||
pub fn set_dedicated_graphic_mode(&mut self, dedicated: bool) {
|
async fn set_dedicated_graphic_mode(
|
||||||
|
&mut self,
|
||||||
|
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||||
|
dedicated: bool,
|
||||||
|
) {
|
||||||
self.set_gfx_mode(dedicated)
|
self.set_gfx_mode(dedicated)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
warn!("CtrlRogBios: set_asus_switch_graphic_mode {}", err);
|
warn!("CtrlRogBios: set_asus_switch_graphic_mode {}", err);
|
||||||
err
|
err
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
self.notify_dedicated_graphic_mode(dedicated)
|
Self::notify_dedicated_graphic_mode(&ctxt, dedicated)
|
||||||
.map_err(|err| {
|
.await
|
||||||
warn!("CtrlRogBios: notify_asus_switch_graphic_mode {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dedicated_graphic_mode(&self) -> i8 {
|
fn dedicated_graphic_mode(&self) -> i8 {
|
||||||
Self::get_gfx_mode()
|
Self::get_gfx_mode()
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
warn!("CtrlRogBios: get_gfx_mode {}", err);
|
warn!("CtrlRogBios: get_gfx_mode {}", err);
|
||||||
@@ -61,24 +69,27 @@ impl CtrlRogBios {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[dbus_interface(signal)]
|
#[dbus_interface(signal)]
|
||||||
pub fn notify_dedicated_graphic_mode(&self, dedicated: bool) -> zbus::Result<()> {}
|
async fn notify_dedicated_graphic_mode(
|
||||||
|
signal_ctxt: &SignalContext<'_>,
|
||||||
|
dedicated: bool,
|
||||||
|
) -> zbus::Result<()> {
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_post_boot_sound(&mut self, on: bool) {
|
async fn set_post_boot_sound(
|
||||||
|
&mut self,
|
||||||
|
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||||
|
on: bool,
|
||||||
|
) {
|
||||||
Self::set_boot_sound(on)
|
Self::set_boot_sound(on)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
warn!("CtrlRogBios: set_post_boot_sound {}", err);
|
warn!("CtrlRogBios: set_post_boot_sound {}", err);
|
||||||
err
|
err
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
self.notify_post_boot_sound(on)
|
Self::notify_post_boot_sound(&ctxt, on).await.ok();
|
||||||
.map_err(|err| {
|
|
||||||
warn!("CtrlRogBios: notify_post_boot_sound {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn post_boot_sound(&self) -> i8 {
|
fn post_boot_sound(&self) -> i8 {
|
||||||
Self::get_boot_sound()
|
Self::get_boot_sound()
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
warn!("CtrlRogBios: get_boot_sound {}", err);
|
warn!("CtrlRogBios: get_boot_sound {}", err);
|
||||||
@@ -88,21 +99,59 @@ impl CtrlRogBios {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[dbus_interface(signal)]
|
#[dbus_interface(signal)]
|
||||||
pub fn notify_post_boot_sound(&self, dedicated: bool) -> zbus::Result<()> {}
|
async fn notify_post_boot_sound(ctxt: &SignalContext<'_>, on: bool) -> zbus::Result<()> {}
|
||||||
}
|
|
||||||
|
|
||||||
impl crate::ZbusAdd for CtrlRogBios {
|
async fn set_panel_overdrive(
|
||||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
&mut self,
|
||||||
server
|
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||||
.at(
|
overdrive: bool,
|
||||||
&ObjectPath::from_str_unchecked("/org/asuslinux/RogBios"),
|
) {
|
||||||
self,
|
if self
|
||||||
)
|
.set_panel_od(overdrive)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
warn!("CtrlRogBios: add_to_server {}", err);
|
warn!("CtrlRogBios: set_panel_overdrive {}", err);
|
||||||
err
|
err
|
||||||
})
|
})
|
||||||
.ok();
|
.is_ok()
|
||||||
|
{
|
||||||
|
Self::notify_panel_overdrive(&ctxt, overdrive).await.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn panel_overdrive(&self) -> i8 {
|
||||||
|
let path = ASUS_PANEL_OD_PATH;
|
||||||
|
if let Ok(mut file) = OpenOptions::new().read(true).open(path).map_err(|err| {
|
||||||
|
warn!("CtrlRogBios: panel_overdrive {}", err);
|
||||||
|
err
|
||||||
|
}) {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
file.read_to_end(&mut buf)
|
||||||
|
.map_err(|err| {
|
||||||
|
warn!("CtrlRogBios: set_panel_overdrive {}", err);
|
||||||
|
err
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
if buf.len() >= 1 {
|
||||||
|
let tmp = String::from_utf8_lossy(&buf[0..1]);
|
||||||
|
return tmp.parse::<i8>().unwrap_or(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[dbus_interface(signal)]
|
||||||
|
async fn notify_panel_overdrive(
|
||||||
|
signal_ctxt: &SignalContext<'_>,
|
||||||
|
overdrive: bool,
|
||||||
|
) -> zbus::Result<()> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl crate::ZbusAdd for CtrlRogBios {
|
||||||
|
async fn add_to_server(self, server: &mut Connection) {
|
||||||
|
Self::add_to_server_helper(self, "/org/asuslinux/RogBios", server).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,4 +357,43 @@ impl CtrlRogBios {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_panel_od(&mut self, overdrive: bool) -> Result<(), RogError> {
|
||||||
|
let path = ASUS_PANEL_OD_PATH;
|
||||||
|
let mut file = OpenOptions::new().write(true).open(path).map_err(|err| {
|
||||||
|
warn!("CtrlRogBios: set_panel_overdrive {}", err);
|
||||||
|
err
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let s = if overdrive { '1' } else { '0' };
|
||||||
|
file.write(&[s as u8]).map_err(|err| {
|
||||||
|
warn!("CtrlRogBios: set_panel_overdrive {}", err);
|
||||||
|
err
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::CtrlRogBios;
|
||||||
|
use crate::config::Config;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore = "Must be manually tested"]
|
||||||
|
fn set_multizone_4key_config() {
|
||||||
|
let config = Config::default();
|
||||||
|
|
||||||
|
let controller = CtrlRogBios {
|
||||||
|
_config: Arc::new(Mutex::new(config)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = controller.panel_overdrive();
|
||||||
|
assert_eq!(res, 1);
|
||||||
|
|
||||||
|
// controller.set_panel_od(false).unwrap();
|
||||||
|
// let res = controller.panel_overdrive();
|
||||||
|
// assert_eq!(res, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
use std::env;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use ::zbus::Connection;
|
||||||
|
use daemon::ctrl_profiles::controller::CtrlProfileTask;
|
||||||
|
use log::LevelFilter;
|
||||||
|
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;
|
||||||
use daemon::ctrl_anime::*;
|
use daemon::ctrl_anime::*;
|
||||||
@@ -7,6 +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_rog_bios::CtrlRogBios;
|
||||||
use daemon::{
|
use daemon::{
|
||||||
config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported,
|
config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported,
|
||||||
};
|
};
|
||||||
@@ -14,20 +26,9 @@ use daemon::{
|
|||||||
ctrl_profiles::{controller::CtrlPlatformProfile, zbus::ProfileZbus},
|
ctrl_profiles::{controller::CtrlPlatformProfile, zbus::ProfileZbus},
|
||||||
laptops::LaptopLedData,
|
laptops::LaptopLedData,
|
||||||
};
|
};
|
||||||
|
|
||||||
use ::zbus::{fdo, Connection, ObjectServer};
|
|
||||||
use daemon::{CtrlTask, Reloadable, ZbusAdd};
|
use daemon::{CtrlTask, Reloadable, ZbusAdd};
|
||||||
use log::LevelFilter;
|
|
||||||
use log::{error, info, warn};
|
|
||||||
use rog_dbus::DBUS_NAME;
|
use rog_dbus::DBUS_NAME;
|
||||||
use rog_profiles::fan_curve_set::FanCurveSet;
|
use rog_profiles::Profile;
|
||||||
use std::env;
|
|
||||||
use std::error::Error;
|
|
||||||
use std::io::Write;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
use daemon::ctrl_rog_bios::CtrlRogBios;
|
|
||||||
|
|
||||||
static PROFILE_CONFIG_PATH: &str = "/etc/asusd/profile.conf";
|
static PROFILE_CONFIG_PATH: &str = "/etc/asusd/profile.conf";
|
||||||
|
|
||||||
@@ -60,27 +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)?);
|
||||||
|
|
||||||
// Collect tasks for task thread
|
|
||||||
let mut tasks: Vec<Box<dyn CtrlTask + Send>> = Vec::new();
|
|
||||||
// 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) => {
|
||||||
@@ -88,43 +87,49 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let fan_device = if let Ok(res) = FanCurveSet::get_device() {
|
if Profile::is_platform_profile_supported() {
|
||||||
Some(res)
|
let profile_config = ProfileConfig::load(PROFILE_CONFIG_PATH.into());
|
||||||
} else {
|
match CtrlPlatformProfile::new(profile_config) {
|
||||||
None
|
Ok(mut ctrl) => {
|
||||||
};
|
ctrl.reload()
|
||||||
let profile_config = ProfileConfig::load(PROFILE_CONFIG_PATH.into());
|
.unwrap_or_else(|err| warn!("Profile control: {}", err));
|
||||||
match CtrlPlatformProfile::new(profile_config, fan_device) {
|
|
||||||
Ok(mut ctrl) => {
|
|
||||||
ctrl.reload()
|
|
||||||
.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).add_to_server(&mut object_server);
|
let task = CtrlProfileTask::new(tmp.clone());
|
||||||
}
|
task.create_tasks(executor).await.ok();
|
||||||
Err(err) => {
|
|
||||||
error!("Profile control: {}", err);
|
let task = ProfileZbus::new(tmp.clone());
|
||||||
|
task.add_to_server(&mut connection).await;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("Profile control: {}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
warn!("platform_profile support not found. This requires kernel 5.15.x or the patch applied: https://lkml.org/lkml/2021/8/18/1022");
|
||||||
}
|
}
|
||||||
|
|
||||||
match CtrlAnime::new(AnimeConfig::load()) {
|
match CtrlAnime::new(AnimeConfig::load()) {
|
||||||
@@ -137,9 +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;
|
||||||
|
|
||||||
tasks.push(Box::new(CtrlAnimeTask::new(inner)));
|
let task = CtrlAnimeTask::new(inner).await;
|
||||||
|
task.create_tasks(executor).await.ok();
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("AniMe control: {}", err);
|
error!("AniMe control: {}", err);
|
||||||
@@ -157,42 +163,21 @@ 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);
|
||||||
tasks.push(Box::new(task));
|
task.create_tasks(executor).await.ok();
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Keyboard control: {}", err);
|
error!("Keyboard control: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement messaging between threads to check fails
|
|
||||||
|
|
||||||
// Run tasks
|
|
||||||
let handle = std::thread::Builder::new()
|
|
||||||
.name("asusd watch".to_string())
|
|
||||||
.spawn(move || loop {
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
|
||||||
|
|
||||||
for ctrl in tasks.iter() {
|
|
||||||
ctrl.do_task()
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!("do_task error: {}", err);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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) = &handle {
|
smol::block_on(executor.tick());
|
||||||
error!("{}", err);
|
|
||||||
}
|
|
||||||
if let Err(err) = object_server.try_handle_next() {
|
|
||||||
error!("{}", err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use rog_anime::error::AnimeError;
|
||||||
use rog_profiles::error::ProfileError;
|
use rog_profiles::error::ProfileError;
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
@@ -22,6 +23,11 @@ pub enum RogError {
|
|||||||
Modprobe(String),
|
Modprobe(String),
|
||||||
Io(std::io::Error),
|
Io(std::io::Error),
|
||||||
Zbus(zbus::Error),
|
Zbus(zbus::Error),
|
||||||
|
ChargeLimit(u8),
|
||||||
|
AuraEffectNotSupported,
|
||||||
|
NoAuraKeyboard,
|
||||||
|
NoAuraNode,
|
||||||
|
Anime(AnimeError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for RogError {
|
impl fmt::Display for RogError {
|
||||||
@@ -46,6 +52,11 @@ 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),
|
||||||
|
RogError::AuraEffectNotSupported => write!(f, "Aura effect not supported"),
|
||||||
|
RogError::NoAuraKeyboard => write!(f, "No supported Aura keyboard"),
|
||||||
|
RogError::NoAuraNode => write!(f, "No Aura keyboard node found"),
|
||||||
|
RogError::Anime(deets) => write!(f, "AniMe Matrix error: {}", deets),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,6 +69,12 @@ impl From<ProfileError> for RogError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<AnimeError> for RogError {
|
||||||
|
fn from(err: AnimeError) -> Self {
|
||||||
|
RogError::Anime(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<zbus::Error> for RogError {
|
impl From<zbus::Error> for RogError {
|
||||||
fn from(err: zbus::Error) -> Self {
|
fn from(err: zbus::Error) -> Self {
|
||||||
RogError::Zbus(err)
|
RogError::Zbus(err)
|
||||||
|
|||||||
@@ -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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,11 +27,17 @@ pub mod laptops;
|
|||||||
/// Fetch all supported functions for the laptop
|
/// Fetch all supported functions for the laptop
|
||||||
pub mod ctrl_supported;
|
pub mod ctrl_supported;
|
||||||
|
|
||||||
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 {
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
function _asusctl() {
|
|
||||||
local line
|
|
||||||
|
|
||||||
_arguments -C \
|
|
||||||
{-h,--help}'[print help message]' \
|
|
||||||
{-v,--version}'[print version number]' \
|
|
||||||
{-k,--kbd-bright}':[Set keyboard brightness (off, low, med, high)]' \
|
|
||||||
{-p,--pwr-profile}':[Set power profile (silent, normal, boost)]' \
|
|
||||||
{-c,--chg-limit}':[Set charging limit (20-100)]' \
|
|
||||||
': :((led-mode\:"Set the keyboard lighting from built-in modes" profile\:"Create and configure profiles" graphics\:"Set the graphics mode"))' \
|
|
||||||
'*::arg:->args'
|
|
||||||
case $line[1] in
|
|
||||||
led-mode)
|
|
||||||
_arguments ': :((static\:"set a single static colour"
|
|
||||||
breathe\:"pulse between one or two colours"
|
|
||||||
strobe\:"strobe through all colours"
|
|
||||||
rainbow\:"rainbow cycling in one of four directions"
|
|
||||||
star\:"rain pattern mimicking raindrops"
|
|
||||||
rain\:"rain pattern of three preset colours"
|
|
||||||
highlight\:"pressed keys are highlighted to fade"
|
|
||||||
laser\:"pressed keys generate horizontal laser"
|
|
||||||
ripple\:"pressed keys ripple outwards like a splash"
|
|
||||||
pulse\:"set a rapid pulse"
|
|
||||||
comet\:"set a vertical line zooming from left"
|
|
||||||
flash\:"set a wide vertical line zooming from left"
|
|
||||||
multi-static\:"4-zone multi-colour"))' \
|
|
||||||
{-h,--help}'[print help message]' \
|
|
||||||
'-c:[set the RGB value e.g, ff00ff]' \
|
|
||||||
'-s:[set the speed (low, med, high)]'
|
|
||||||
;;
|
|
||||||
profile)
|
|
||||||
_arguments {-h,--help}'[print help message]' \
|
|
||||||
{-c,--create}"[create the profile if it doesn't exist]" \
|
|
||||||
{-t,--turbo}':[enable or disable cpu turbo]' \
|
|
||||||
{-m,--min-percentage}':[set min cpu scaling (intel)]' \
|
|
||||||
{-M,--max-percentage}':[set max cpu scaling (intel)]' \
|
|
||||||
{-p,--preset}':[<silent, normal, boost>]' \
|
|
||||||
{-C,--curve}':[set fan curve]'
|
|
||||||
|
|
||||||
;;
|
|
||||||
graphics)
|
|
||||||
_arguments {-h,--help}'[print help message]' \
|
|
||||||
{-m,--mode}':[Set graphics mode (nvidia, hybrid, compute, integrated)]' \
|
|
||||||
{-g,--get}'[Get the current mode]' \
|
|
||||||
{-p,--pow}'[Get the current power status]' \
|
|
||||||
{-f,--force}'[Do not ask for confirmation]'
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
compdef _asusctl asusctl
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=ASUS Notebook Control
|
|
||||||
After=basic.target syslog.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Environment=IS_SERVICE=1
|
|
||||||
ExecStart=/usr/bin/asusd
|
|
||||||
Restart=on-failure
|
|
||||||
Type=dbus
|
|
||||||
BusName=org.asuslinux.Daemon
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
@@ -1,118 +1,183 @@
|
|||||||
|
[[led_data]]
|
||||||
|
prod_family = "TUF"
|
||||||
|
board_names = ["FA507"]
|
||||||
|
standard = ["Static", "Breathe", "Strobe", "Pulse"]
|
||||||
|
multizone = []
|
||||||
|
per_key = false
|
||||||
|
|
||||||
|
[[led_data]]
|
||||||
|
prod_family = "TUF Gaming"
|
||||||
|
board_names = ["FX505D"]
|
||||||
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
||||||
|
multizone = []
|
||||||
|
per_key = false
|
||||||
|
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "Zephyrus S"
|
prod_family = "Zephyrus S"
|
||||||
board_names = ["GX502", "GX701", "G531", "GL531", "G532"]
|
board_names = ["GX502", "GX701", "G531", "GL531", "G532"]
|
||||||
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
|
||||||
multizone = false
|
multizone = []
|
||||||
per_key = true
|
per_key = true
|
||||||
|
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "Zephyrus M"
|
prod_family = "Zephyrus M"
|
||||||
board_names = ["GU502GV"]
|
board_names = ["GU502GV"]
|
||||||
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
|
||||||
multizone = false
|
multizone = []
|
||||||
per_key = true
|
per_key = true
|
||||||
|
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "Zephyrus M"
|
prod_family = "Zephyrus M"
|
||||||
board_names = ["GM501GS"]
|
board_names = ["GM501GS"]
|
||||||
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
||||||
multizone = true
|
multizone = ["Key1", "Key2", "Key3", "Key4"]
|
||||||
per_key = false
|
per_key = false
|
||||||
|
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "ROG Zephyrus M15"
|
prod_family = "ROG Zephyrus M15"
|
||||||
board_names = ["GU502LW"]
|
board_names = ["GU502LW", "GU502LV"]
|
||||||
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
|
||||||
multizone = false
|
multizone = []
|
||||||
per_key = true
|
per_key = true
|
||||||
|
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "ROG Zephyrus M15"
|
prod_family = "ROG Zephyrus M15"
|
||||||
board_names = ["GU502LU"]
|
board_names = ["GU502LU"]
|
||||||
standard = ["Static", "Breathe", "Strobe", "Pulse"]
|
standard = ["Static", "Breathe", "Strobe", "Pulse"]
|
||||||
multizone = false
|
multizone = []
|
||||||
per_key = false
|
per_key = false
|
||||||
|
|
||||||
|
[[led_data]]
|
||||||
|
prod_family = "ROG Zephyrus S17"
|
||||||
|
board_names = ["GX703HS"]
|
||||||
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
|
||||||
|
multizone = []
|
||||||
|
per_key = false
|
||||||
|
|
||||||
|
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "Zephyrus"
|
prod_family = "Zephyrus"
|
||||||
board_names = ["GM501GM", "GX531"]
|
board_names = ["GM501GM", "GX531"]
|
||||||
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
||||||
multizone = true
|
multizone = ["Key1", "Key2", "Key3", "Key4"]
|
||||||
per_key = false
|
per_key = false
|
||||||
|
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "ROG Strix"
|
prod_family = "ROG Strix"
|
||||||
board_names = ["G531GW", "G533QR", "G733QS", "G733QR"]
|
board_names = ["G531GW", "G533QR", "G533QS", "G733QS", "G733QR", "G513QR", "G713QR", "G513QM", "G713IC"]
|
||||||
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
|
||||||
multizone = false
|
multizone = []
|
||||||
per_key = true
|
per_key = true
|
||||||
|
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "ROG Strix"
|
prod_family = "ROG Strix"
|
||||||
board_names = ["GX531", "G512LV", "G712LV", "G712LW", "G513IH", "G513QY"]
|
board_names = ["G513QE", "GX531", "G512LV", "G712LV", "G712LW", "G513IH", "G513QY", "G713QM", "G512", "G713RW"]
|
||||||
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
||||||
multizone = true
|
multizone = ["Key1", "Key2", "Key3", "Key4"]
|
||||||
per_key = false
|
per_key = false
|
||||||
|
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "ROG Strix"
|
prod_family = "ROG Strix"
|
||||||
board_names = ["G512LI", "G712LI", "G531GD"]
|
board_names = ["G512LI", "G712LI", "G531GD"]
|
||||||
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
||||||
multizone = false
|
multizone = []
|
||||||
per_key = false
|
per_key = false
|
||||||
|
|
||||||
|
[[led_data]]
|
||||||
|
prod_family = "ROG Strix"
|
||||||
|
board_names = ["G513IM"]
|
||||||
|
standard = ["Flash", "Static", "Breathe", "Strobe", "Rainbow"]
|
||||||
|
multizone = []
|
||||||
|
per_key = true
|
||||||
|
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "Strix"
|
prod_family = "Strix"
|
||||||
board_names = ["G731GV", "G731GW", "G531GV"]
|
board_names = ["G731GV", "G731GW", "G531GV"]
|
||||||
standard = ["Static", "Breathe", "Strobe", "Rainbow"]
|
standard = ["Static", "Breathe", "Strobe", "Rainbow"]
|
||||||
multizone = true
|
multizone = ["Key1", "Key2", "Key3", "Key4"]
|
||||||
|
per_key = false
|
||||||
|
|
||||||
|
[[led_data]]
|
||||||
|
prod_family = "Strix"
|
||||||
|
board_names = ["GL504G"]
|
||||||
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
||||||
|
multizone = ["Key1", "Key2", "Key3", "Key4", "Logo", "BarLeft", "BarRight"]
|
||||||
per_key = false
|
per_key = false
|
||||||
|
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "Strix"
|
prod_family = "Strix"
|
||||||
board_names = ["G731GT", "G731GU", "G531GT", "G531GU"]
|
board_names = ["G731GT", "G731GU", "G531GT", "G531GU"]
|
||||||
standard = ["Static", "Breathe", "Strobe", "Rainbow"]
|
standard = ["Static", "Breathe", "Strobe", "Rainbow"]
|
||||||
multizone = false
|
multizone = []
|
||||||
per_key = false
|
per_key = false
|
||||||
|
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "Strix Scar"
|
prod_family = "Strix Scar"
|
||||||
board_names = ["G531", "G731"]
|
board_names = ["G531", "G731"]
|
||||||
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
|
||||||
multizone = true
|
multizone = ["Key1", "Key2", "Key3", "Key4"]
|
||||||
per_key = true
|
per_key = true
|
||||||
|
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "ROG"
|
prod_family = "ROG"
|
||||||
board_names = ["GL553VE"]
|
board_names = ["GL553VE"]
|
||||||
standard = ["Static", "Breathe", "Strobe"]
|
standard = ["Static", "Breathe", "Strobe"]
|
||||||
multizone = true
|
multizone = ["Key1", "Key2", "Key3", "Key4"]
|
||||||
per_key = false
|
per_key = false
|
||||||
|
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "ROG Zephyrus G14"
|
prod_family = "ROG Zephyrus G14"
|
||||||
board_names = ["GA401Q"]
|
board_names = ["GA401Q"]
|
||||||
standard = ["Static", "Breathe", "Pulse"]
|
standard = ["Static", "Breathe", "Pulse"]
|
||||||
multizone = false
|
multizone = []
|
||||||
|
per_key = false
|
||||||
|
|
||||||
|
[[led_data]]
|
||||||
|
prod_family = "ROG Zephyrus G14"
|
||||||
|
board_names = ["GA402R"]
|
||||||
|
standard = ["Static", "Breathe", "Pulse", "Rainbow"]
|
||||||
|
multizone = []
|
||||||
|
per_key = false
|
||||||
|
|
||||||
|
# GA503QE at higher priority (first match) than GA503Q
|
||||||
|
[[led_data]]
|
||||||
|
prod_family = "ROG Zephyrus G15"
|
||||||
|
board_names = ["GA503QE"]
|
||||||
|
standard = ["Static", "Breathe", "Pulse"]
|
||||||
|
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_name = ["GX551Q"]
|
board_names = ["GX551Q"]
|
||||||
standard ["Static", "Breathe", "Pulse", "Rainbow", "Strobe"]
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
||||||
multizone = false
|
multizone = []
|
||||||
per_key = true
|
per_key = true
|
||||||
|
|
||||||
|
[[led_data]]
|
||||||
|
prod_family = "ROG Flow X13"
|
||||||
|
board_names = ["GV301QH", "GV301QE"]
|
||||||
|
standard = ["Static", "Breathe", "Pulse"]
|
||||||
|
multizone = []
|
||||||
|
per_key = false
|
||||||
|
|
||||||
|
[[led_data]]
|
||||||
|
prod_family = "ROG Strix"
|
||||||
|
board_names = ["G513IC"]
|
||||||
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
||||||
|
multizone = []
|
||||||
|
per_key = false
|
||||||
|
|||||||
@@ -1,2 +1,19 @@
|
|||||||
ACTION=="add|change", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="0b05", ENV{ID_MODEL_ID}=="1[89][a-zA-Z0-9][a-zA-Z0-9]|193b", ENV{ID_TYPE}=="hid", TAG+="systemd", ENV{SYSTEMD_WANTS}="asusd.service"
|
#ACTION=="add|change", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="0b05", ENV{ID_MODEL_ID}=="1[89][a-zA-Z0-9][a-zA-Z0-9]|193b", ENV{ID_TYPE}=="hid", TAG+="systemd", ENV{SYSTEMD_WANTS}="asusd.service"
|
||||||
ACTION=="add|remove", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="0b05", ENV{ID_MODEL_ID}=="1[89][a-zA-Z0-9][a-zA-Z0-9]|193b", RUN+="systemctl restart asusd.service"
|
#ACTION=="add|remove", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="0b05", ENV{ID_MODEL_ID}=="1[89][a-zA-Z0-9][a-zA-Z0-9]|193b", RUN+="systemctl restart asusd.service"
|
||||||
|
|
||||||
|
ENV{DMI_VENDOR}="$attr{[dmi/id]sys_vendor}"
|
||||||
|
ENV{DMI_VENDOR}!="ASUSTeK COMPUTER INC.", GOTO="asusd_end"
|
||||||
|
|
||||||
|
ENV{DMI_FAMILY}="$attr{[dmi/id]product_family}"
|
||||||
|
ENV{DMI_FAMILY}=="*TUF*", GOTO="asusd_start"
|
||||||
|
ENV{DMI_FAMILY}=="*ROG*", GOTO="asusd_start"
|
||||||
|
ENV{DMI_FAMILY}=="*Zephyrus*", GOTO="asusd_start"
|
||||||
|
ENV{DMI_FAMILY}=="*Strix*", GOTO="asusd_start"
|
||||||
|
# No match so
|
||||||
|
GOTO="asusd_end"
|
||||||
|
|
||||||
|
LABEL="asusd_start"
|
||||||
|
ACTION=="add|change", DRIVER=="asus-nb-wmi", TAG+="systemd", ENV{SYSTEMD_WANTS}="asusd.service"
|
||||||
|
ACTION=="add|remove", DRIVER=="asus-nb-wmi", TAG+="systemd", RUN+="systemctl restart asusd.service"
|
||||||
|
|
||||||
|
LABEL="asusd_end"
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
# Author: AlenPaulVarghese <alenpaul2001@gmail.com>
|
|
||||||
|
|
||||||
set -l progname asusctl
|
|
||||||
|
|
||||||
set -l noopt "not __fish_contains_opt -s -s h -s v -s s -s k -s f -s c help version show-supported kbd-bright fan-mode chg-limit; and not __fish_seen_subcommand_from led-mode profile graphics;"
|
|
||||||
|
|
||||||
|
|
||||||
set -l gmod_options '__fish_contains_opt -s m mode;'
|
|
||||||
set -l fan_options '__fish_contains_opt -s f fan-mode;'
|
|
||||||
set -l led_options '__fish_seen_subcommand_from led-mode;'
|
|
||||||
set -l profile_options '__fish_seen_subcommand_from profile;'
|
|
||||||
set -l keyboard_options '__fish_contains_opt -s k kbd-bright;'
|
|
||||||
set -l graphics_options '__fish_seen_subcommand_from graphics;'
|
|
||||||
|
|
||||||
set -l fan_modes 'silent normal boost'
|
|
||||||
set -l brightness_modes 'off low med high'
|
|
||||||
set -l led_modes 'static breathe strobe rainbow comet'
|
|
||||||
set -l graphics_modes 'nvidia hybird compute integrated'
|
|
||||||
|
|
||||||
|
|
||||||
complete -c $progname -e
|
|
||||||
complete -c $progname -f
|
|
||||||
|
|
||||||
# asusctl completion
|
|
||||||
complete -c $progname -s h -f -l help -n "$noopt" -d "print help message"
|
|
||||||
complete -c $progname -s v -f -l version -n "$noopt" -d "show program version number"
|
|
||||||
complete -c $progname -s s -f -l show-supported -n "$noopt" -d "show supported functions of this laptop"
|
|
||||||
complete -c $progname -s k -f -l kbd-bright -n "$noopt" -d "set led brightness"
|
|
||||||
complete -c $progname -s f -f -l fan-mode -n "$noopt" -d "set fan mode independent of profile"
|
|
||||||
complete -c $progname -s c -f -l chg-limit -n "$noopt" -d "set charge limit <20-100>"
|
|
||||||
complete -c $progname -f -a "led-mode" -n "$noopt" -d "Set the keyboard lighting from built-in modes"
|
|
||||||
complete -c $progname -f -a "profile" -n "$noopt" -d "Create and configure profiles"
|
|
||||||
complete -c $progname -f -a "graphics" -n "$noopt" -d "Set the graphics mode"
|
|
||||||
|
|
||||||
# brightness completion
|
|
||||||
complete -c $progname -n "$keyboard_options" -d "available brightness modes" -a "$brightness_modes"
|
|
||||||
|
|
||||||
# fan completion
|
|
||||||
complete -c $progname -n "$fan_options" -d "available fan modes" -a $fan_modes
|
|
||||||
|
|
||||||
# graphics completion
|
|
||||||
set -l gopt 'not __fish_contains_opt -s h -s g -s m -s p help mode get pow;'
|
|
||||||
|
|
||||||
complete -c $progname -n "$graphics_options and $gopt" -a "-h" -d "print help message"
|
|
||||||
complete -c $progname -n "$graphics_options and $gopt" -a "-g" -d "Get the current mode"
|
|
||||||
|
|
||||||
complete -c $progname -s h -f -l help -n "$graphics_options and $gopt" -d "print help message"
|
|
||||||
complete -c $progname -s m -f -l mode -n "$graphics_options and $gopt" -d "Set graphics mode: <nvidia, hybrid, compute, integrated>"
|
|
||||||
complete -c $progname -s g -f -l get -n "$graphics_options and $gopt" -d "Get the current mode"
|
|
||||||
complete -c $progname -s p -f -l pow -n "$graphics_options and $gopt" -d "Get the current power status"
|
|
||||||
|
|
||||||
complete -c $progname -n "$graphics_options and $gmod_options" -d "available graphics modes" -a "$graphics_modes"
|
|
||||||
|
|
||||||
# led-mode completion
|
|
||||||
complete -c $progname -n "$led_options" -a "-h" -d "print help message"
|
|
||||||
complete -c $progname -n "$led_options" -a "-n" -d "switch to next aura mode"
|
|
||||||
|
|
||||||
complete -c $progname -s h -f -l help -n "$led_options" -d "print help message"
|
|
||||||
complete -c $progname -s n -f -l next-mode -n "$led_options" -d "switch to nex aura mode"
|
|
||||||
complete -c $progname -s p -f -l prev-mode -n "$led_options" -d "switch to previous aura mode"
|
|
||||||
complete -c $progname -n "$led_options" -d "available led modes" -a "$led_modes"
|
|
||||||
|
|
||||||
# profile completion
|
|
||||||
set -l popt 'not __fish_contains_opt -s h -s n -s c -s t -s m -s M -s f help next create turbo min-percentage max-percentage fan-preset;'
|
|
||||||
|
|
||||||
complete -c $progname -n "$profile_options and $popt" -a "-h" -d "print help message"
|
|
||||||
complete -c $progname -n "$profile_options and $popt" -a "-n" -d "toggle to next profile in list"
|
|
||||||
|
|
||||||
complete -c $progname -s h -f -l help -n "$profile_options and $popt" -d "print help message"
|
|
||||||
complete -c $progname -s n -f -l next -n "$profile_options and $popt" -d "toggle to next profile in list"
|
|
||||||
complete -c $progname -s c -f -l create -n "$profile_options and $popt" -d "create the profile if it doesn't exist"
|
|
||||||
complete -c $progname -s t -f -l turbo -n "$profile_options and $popt" -d "enable or disable cpu turbo"
|
|
||||||
complete -c $progname -s m -f -l min-percentage -n "$profile_options and $popt" -d "set min cpu scaling (intel)"
|
|
||||||
complete -c $progname -s M -f -l max-percentage -n "$profile_options and $popt" -d "set max cpu scaling (intel)"
|
|
||||||
complete -c $progname -s f -f -l fan-preset -n "$profile_options and $popt" -d "<silent, normal, boost>"
|
|
||||||
complete -c $progname -n "$profile_option and __fish_contains_opt fan-preset" -d "available fan modes" -a $fan_modes
|
|
||||||
|
|
||||||
@@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rog_anime"
|
name = "rog_anime"
|
||||||
version = "1.1.0"
|
version = "1.3.5"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = ["Luke <luke@ljones.dev>"]
|
authors = ["Luke <luke@ljones.dev>"]
|
||||||
@@ -13,18 +13,23 @@ edition = "2018"
|
|||||||
exclude = ["data"]
|
exclude = ["data"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["dbus"]
|
default = ["dbus", "detect"]
|
||||||
dbus = ["zvariant", "zvariant_derive"]
|
dbus = ["zvariant", "zbus"]
|
||||||
|
detect = ["udev", "sysfs-class"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
png_pong = "^0.8.0"
|
png_pong = "^0.8.0"
|
||||||
pix = "0.13"
|
pix = "0.13"
|
||||||
gif = "^0.11.2"
|
gif = "^0.11.2"
|
||||||
|
log = "*"
|
||||||
|
|
||||||
serde = "^1.0"
|
serde = "^1.0"
|
||||||
serde_derive = "^1.0"
|
serde_derive = "^1.0"
|
||||||
|
|
||||||
glam = { version = "0.14.0", features = ["serde"] }
|
glam = { version = "^0.21.2", features = ["serde"] }
|
||||||
|
|
||||||
zvariant = { version = "^2.6", optional = true }
|
zvariant = { version = "^3.0", optional = true }
|
||||||
zvariant_derive = { version = "^2.6", optional = true }
|
zbus = { version = "^2.2", optional = true }
|
||||||
|
|
||||||
|
udev = { version = "^0.6", optional = true }
|
||||||
|
sysfs-class = { version = "^0.1", optional = true }
|
||||||
|
After Width: | Height: | Size: 7.2 KiB |
@@ -1,17 +1,18 @@
|
|||||||
use std::{
|
use std::{
|
||||||
sync::{
|
convert::TryFrom,
|
||||||
atomic::{AtomicBool, Ordering},
|
|
||||||
Arc,
|
|
||||||
},
|
|
||||||
thread::sleep,
|
thread::sleep,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use log::info;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
#[cfg(feature = "dbus")]
|
#[cfg(feature = "dbus")]
|
||||||
use zvariant_derive::Type;
|
use zvariant::Type;
|
||||||
|
|
||||||
use crate::{error::AnimeError, AnimTime, AnimeGif};
|
use crate::{
|
||||||
|
error::{AnimeError, Result},
|
||||||
|
AnimTime, AnimeGif,
|
||||||
|
};
|
||||||
|
|
||||||
/// The first 7 bytes of a USB packet are accounted for by `USB_PREFIX1` and `USB_PREFIX2`
|
/// The first 7 bytes of a USB packet are accounted for by `USB_PREFIX1` and `USB_PREFIX2`
|
||||||
const BLOCK_START: usize = 7;
|
const BLOCK_START: usize = 7;
|
||||||
@@ -19,48 +20,85 @@ const BLOCK_START: usize = 7;
|
|||||||
const BLOCK_END: usize = 634;
|
const BLOCK_END: usize = 634;
|
||||||
/// Individual usable data length of each USB packet
|
/// Individual usable data length of each USB packet
|
||||||
const PANE_LEN: usize = BLOCK_END - BLOCK_START;
|
const PANE_LEN: usize = BLOCK_END - BLOCK_START;
|
||||||
/// The length of usable data
|
|
||||||
pub const ANIME_DATA_LEN: usize = PANE_LEN * 2;
|
|
||||||
|
|
||||||
|
/// First packet is for GA401 + GA402
|
||||||
const USB_PREFIX1: [u8; 7] = [0x5e, 0xc0, 0x02, 0x01, 0x00, 0x73, 0x02];
|
const USB_PREFIX1: [u8; 7] = [0x5e, 0xc0, 0x02, 0x01, 0x00, 0x73, 0x02];
|
||||||
|
/// Second packet is for GA401 + GA402
|
||||||
const USB_PREFIX2: [u8; 7] = [0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02];
|
const USB_PREFIX2: [u8; 7] = [0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02];
|
||||||
|
/// Third packet is for GA402 matrix
|
||||||
|
const USB_PREFIX3: [u8; 7] = [0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02];
|
||||||
|
|
||||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||||
#[derive(Debug, PartialEq, Copy, Clone, Deserialize, Serialize)]
|
#[derive(Debug, PartialEq, Copy, Clone, Deserialize, Serialize)]
|
||||||
|
|
||||||
pub struct AnimePowerStates {
|
pub struct AnimePowerStates {
|
||||||
|
pub brightness: u8,
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub boot_anim_enabled: bool,
|
pub boot_anim_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
|
||||||
|
pub enum AnimeType {
|
||||||
|
GA401,
|
||||||
|
GA402,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnimeType {
|
||||||
|
/// The width of diagonal images
|
||||||
|
pub fn width(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
AnimeType::GA401 => 74,
|
||||||
|
AnimeType::GA402 => 74,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The height of diagonal images
|
||||||
|
pub fn height(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
AnimeType::GA401 => 36,
|
||||||
|
AnimeType::GA402 => 39,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The length of usable data for this type
|
||||||
|
pub fn data_length(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
AnimeType::GA401 => PANE_LEN * 2,
|
||||||
|
AnimeType::GA402 => PANE_LEN * 3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The minimal serializable data that can be transferred over wire types.
|
/// The minimal serializable data that can be transferred over wire types.
|
||||||
/// Other data structures in `rog_anime` will convert to this.
|
/// Other data structures in `rog_anime` will convert to this.
|
||||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct AnimeDataBuffer(Vec<u8>);
|
pub struct AnimeDataBuffer {
|
||||||
|
data: Vec<u8>,
|
||||||
impl Default for AnimeDataBuffer {
|
anime: AnimeType,
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnimeDataBuffer {
|
impl AnimeDataBuffer {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> Self {
|
pub fn new(anime: AnimeType) -> Self {
|
||||||
AnimeDataBuffer(vec![0u8; ANIME_DATA_LEN])
|
let len = anime.data_length();
|
||||||
|
|
||||||
|
AnimeDataBuffer {
|
||||||
|
data: vec![0u8; len],
|
||||||
|
anime,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the inner data buffer
|
/// Get the inner data buffer
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get(&self) -> &[u8] {
|
pub fn data(&self) -> &[u8] {
|
||||||
&self.0
|
&self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable slice of the inner buffer
|
/// Get a mutable slice of the inner buffer
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_mut(&mut self) -> &mut [u8] {
|
pub fn data_mut(&mut self) -> &mut [u8] {
|
||||||
&mut self.0
|
&mut self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create from a vector of bytes
|
/// Create from a vector of bytes
|
||||||
@@ -68,35 +106,51 @@ impl AnimeDataBuffer {
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
/// Will panic if the vector length is not `ANIME_DATA_LEN`
|
/// Will panic if the vector length is not `ANIME_DATA_LEN`
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_vec(input: Vec<u8>) -> Self {
|
pub fn from_vec(anime: AnimeType, data: Vec<u8>) -> Result<Self> {
|
||||||
assert_eq!(input.len(), ANIME_DATA_LEN);
|
if data.len() != anime.data_length() {
|
||||||
Self(input)
|
return Err(AnimeError::DataBufferLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self { data, anime })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The two packets to be written to USB
|
/// The two packets to be written to USB
|
||||||
pub type AnimePacketType = [[u8; 640]; 2];
|
pub type AnimePacketType = Vec<[u8; 640]>;
|
||||||
|
|
||||||
impl From<AnimeDataBuffer> for AnimePacketType {
|
impl TryFrom<AnimeDataBuffer> for AnimePacketType {
|
||||||
#[inline]
|
type Error = AnimeError;
|
||||||
fn from(anime: AnimeDataBuffer) -> Self {
|
|
||||||
assert!(anime.0.len() == ANIME_DATA_LEN);
|
fn try_from(anime: AnimeDataBuffer) -> std::result::Result<Self, Self::Error> {
|
||||||
let mut buffers = [[0; 640]; 2];
|
if anime.data.len() != anime.anime.data_length() {
|
||||||
for (idx, chunk) in anime.0.as_slice().chunks(PANE_LEN).enumerate() {
|
return Err(AnimeError::DataBufferLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buffers = match anime.anime {
|
||||||
|
AnimeType::GA401 => vec![[0; 640]; 2],
|
||||||
|
AnimeType::GA402 => vec![[0; 640]; 3],
|
||||||
|
};
|
||||||
|
|
||||||
|
for (idx, chunk) in anime.data.as_slice().chunks(PANE_LEN).enumerate() {
|
||||||
buffers[idx][BLOCK_START..BLOCK_END].copy_from_slice(chunk);
|
buffers[idx][BLOCK_START..BLOCK_END].copy_from_slice(chunk);
|
||||||
}
|
}
|
||||||
buffers[0][..7].copy_from_slice(&USB_PREFIX1);
|
buffers[0][..7].copy_from_slice(&USB_PREFIX1);
|
||||||
buffers[1][..7].copy_from_slice(&USB_PREFIX2);
|
buffers[1][..7].copy_from_slice(&USB_PREFIX2);
|
||||||
buffers
|
|
||||||
|
if matches!(anime.anime, AnimeType::GA402) {
|
||||||
|
buffers[2][..7].copy_from_slice(&USB_PREFIX3);
|
||||||
|
}
|
||||||
|
Ok(buffers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This runs the animations as a blocking loop by using the `callback` to write data
|
/// This runs the animations as a blocking loop by using the `callback` to write data
|
||||||
|
///
|
||||||
|
/// If `callback` is `Ok(true)` then `run_animation` will exit the animation loop early.
|
||||||
pub fn run_animation(
|
pub fn run_animation(
|
||||||
frames: &AnimeGif,
|
frames: &AnimeGif,
|
||||||
do_early_return: Arc<AtomicBool>,
|
callback: &dyn Fn(AnimeDataBuffer) -> Result<bool>,
|
||||||
callback: &dyn Fn(AnimeDataBuffer) -> Result<(), AnimeError>,
|
) -> Result<()> {
|
||||||
) -> Result<(), AnimeError> {
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
@@ -140,14 +194,11 @@ pub fn run_animation(
|
|||||||
'animation: loop {
|
'animation: loop {
|
||||||
for frame in frames.frames() {
|
for frame in frames.frames() {
|
||||||
let frame_start = Instant::now();
|
let frame_start = Instant::now();
|
||||||
if do_early_return.load(Ordering::SeqCst) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let mut output = frame.frame().clone();
|
let mut output = frame.frame().clone();
|
||||||
|
|
||||||
if let AnimTime::Fade(_) = frames.duration() {
|
if let AnimTime::Fade(_) = frames.duration() {
|
||||||
if frame_start <= start + fade_in {
|
if frame_start <= start + fade_in {
|
||||||
for pixel in output.get_mut() {
|
for pixel in output.data_mut() {
|
||||||
*pixel = (*pixel as f32 * fade_in_accum) as u8;
|
*pixel = (*pixel as f32 * fade_in_accum) as u8;
|
||||||
}
|
}
|
||||||
fade_in_accum = fade_in_step * (frame_start - start).as_secs_f32();
|
fade_in_accum = fade_in_step * (frame_start - start).as_secs_f32();
|
||||||
@@ -158,18 +209,20 @@ pub fn run_animation(
|
|||||||
} else {
|
} else {
|
||||||
fade_out_accum = 0.0;
|
fade_out_accum = 0.0;
|
||||||
}
|
}
|
||||||
for pixel in output.get_mut() {
|
for pixel in output.data_mut() {
|
||||||
*pixel = (*pixel as f32 * fade_out_accum) as u8;
|
*pixel = (*pixel as f32 * fade_out_accum) as u8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(output)?;
|
if matches!(callback(output), Ok(true)) {
|
||||||
|
info!("rog-anime: frame-loop callback asked to exit early");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
if timed && Instant::now().duration_since(start) > run_time {
|
if timed && Instant::now().duration_since(start) > run_time {
|
||||||
break 'animation;
|
break 'animation;
|
||||||
}
|
}
|
||||||
|
|
||||||
sleep(frame.delay());
|
sleep(frame.delay());
|
||||||
}
|
}
|
||||||
if let AnimTime::Count(times) = frames.duration() {
|
if let AnimTime::Count(times) = frames.duration() {
|
||||||
|
|||||||
@@ -1,40 +1,36 @@
|
|||||||
use std::{path::Path, time::Duration};
|
use std::{path::Path, time::Duration};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
data::{AnimeDataBuffer, ANIME_DATA_LEN},
|
data::AnimeDataBuffer,
|
||||||
error::AnimeError,
|
error::{AnimeError, Result},
|
||||||
|
AnimeType,
|
||||||
};
|
};
|
||||||
|
|
||||||
const WIDTH: usize = 74;
|
|
||||||
const HEIGHT: usize = 36;
|
|
||||||
|
|
||||||
/// Mostly intended to be used with ASUS gifs, but can be used for other purposes (like images)
|
/// Mostly intended to be used with ASUS gifs, but can be used for other purposes (like images)
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AnimeDiagonal([[u8; WIDTH]; HEIGHT], Option<Duration>);
|
pub struct AnimeDiagonal(AnimeType, Vec<Vec<u8>>, Option<Duration>);
|
||||||
|
|
||||||
impl Default for AnimeDiagonal {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AnimeDiagonal {
|
impl AnimeDiagonal {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(duration: Option<Duration>) -> Self {
|
pub fn new(anime_type: AnimeType, duration: Option<Duration>) -> Self {
|
||||||
Self([[0u8; WIDTH]; HEIGHT], duration)
|
Self(
|
||||||
|
anime_type,
|
||||||
|
vec![vec![0; anime_type.width()]; anime_type.height()],
|
||||||
|
duration,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_mut(&mut self) -> &mut [[u8; WIDTH]; HEIGHT] {
|
pub fn get_mut(&mut self) -> &mut Vec<Vec<u8>> {
|
||||||
&mut self.0
|
&mut self.1
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a full diagonal row where `x` `y` is the starting point and `len` is the length of data.
|
/// Get a full diagonal row where `x` `y` is the starting point and `len` is the length of data.
|
||||||
fn get_row(&self, x: usize, y: usize, len: usize) -> Vec<u8> {
|
fn get_row(&self, x: usize, y: usize, len: usize) -> Vec<u8> {
|
||||||
let mut buf = Vec::with_capacity(len);
|
let mut buf = Vec::with_capacity(len);
|
||||||
for i in 0..len {
|
for i in 0..len {
|
||||||
let val = self.0[HEIGHT - y - i - 1][x + i];
|
let y = self.0.height() - y - i - 1;
|
||||||
|
let val = self.1[y][x + i];
|
||||||
buf.push(val);
|
buf.push(val);
|
||||||
}
|
}
|
||||||
buf
|
buf
|
||||||
@@ -47,13 +43,14 @@ impl AnimeDiagonal {
|
|||||||
path: &Path,
|
path: &Path,
|
||||||
duration: Option<Duration>,
|
duration: Option<Duration>,
|
||||||
bright: f32,
|
bright: f32,
|
||||||
) -> Result<Self, AnimeError> {
|
anime_type: AnimeType,
|
||||||
|
) -> Result<Self> {
|
||||||
let data = std::fs::read(path)?;
|
let data = std::fs::read(path)?;
|
||||||
let data = std::io::Cursor::new(data);
|
let data = std::io::Cursor::new(data);
|
||||||
let decoder = png_pong::Decoder::new(data)?.into_steps();
|
let decoder = png_pong::Decoder::new(data)?.into_steps();
|
||||||
let png_pong::Step { raster, delay: _ } = decoder.last().ok_or(AnimeError::NoFrames)??;
|
let png_pong::Step { raster, delay: _ } = decoder.last().ok_or(AnimeError::NoFrames)??;
|
||||||
|
|
||||||
let mut matrix = AnimeDiagonal::new(duration);
|
let mut matrix = AnimeDiagonal::new(anime_type, duration);
|
||||||
|
|
||||||
match raster {
|
match raster {
|
||||||
png_pong::PngRaster::Gray8(ras) => {
|
png_pong::PngRaster::Gray8(ras) => {
|
||||||
@@ -100,7 +97,9 @@ impl AnimeDiagonal {
|
|||||||
+ (<u8>::from(px.two()) / 3) as f32
|
+ (<u8>::from(px.two()) / 3) as f32
|
||||||
+ (<u8>::from(px.three()) / 3) as f32
|
+ (<u8>::from(px.three()) / 3) as f32
|
||||||
};
|
};
|
||||||
matrix.0[y][x] = (v * bright) as u8;
|
if y < matrix.1.len() && x < matrix.1[y].len() {
|
||||||
|
matrix.1[y][x] = (v * bright) as u8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,75 +122,547 @@ impl AnimeDiagonal {
|
|||||||
+ ((<u16>::from(px.two()) / 3) >> 8) as f32
|
+ ((<u16>::from(px.two()) / 3) >> 8) as f32
|
||||||
+ ((<u16>::from(px.three()) / 3) >> 8) as f32
|
+ ((<u16>::from(px.three()) / 3) >> 8) as f32
|
||||||
};
|
};
|
||||||
matrix.0[y][x] = (v * bright) as u8;
|
matrix.1[y][x] = (v * bright) as u8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&AnimeDiagonal> for AnimeDataBuffer {
|
/// Convert to a data buffer that can be sent over dbus
|
||||||
|
#[inline]
|
||||||
|
pub fn into_data_buffer(&self, anime_type: AnimeType) -> Result<AnimeDataBuffer> {
|
||||||
|
match anime_type {
|
||||||
|
AnimeType::GA401 => self.into_ga401_packets(),
|
||||||
|
AnimeType::GA402 => self.into_ga402_packets(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Do conversion from the nested Vec in AnimeMatrix to the two required
|
/// Do conversion from the nested Vec in AnimeMatrix to the two required
|
||||||
/// packets suitable for sending over USB
|
/// packets suitable for sending over USB
|
||||||
#[inline]
|
fn into_ga401_packets(&self) -> Result<AnimeDataBuffer> {
|
||||||
fn from(anime: &AnimeDiagonal) -> Self {
|
let mut buf = vec![0u8; AnimeType::GA401.data_length()];
|
||||||
let mut buf = vec![0u8; ANIME_DATA_LEN];
|
|
||||||
|
|
||||||
buf[1..=32].copy_from_slice(&anime.get_row(0, 3, 32));
|
buf[1..=32].copy_from_slice(&self.get_row(0, 3, 32));
|
||||||
buf[34..=66].copy_from_slice(&anime.get_row(0, 2, 33));
|
buf[34..=66].copy_from_slice(&self.get_row(0, 2, 33));
|
||||||
buf[69..=101].copy_from_slice(&anime.get_row(1, 2, 33)); // ?!
|
buf[69..=101].copy_from_slice(&self.get_row(1, 2, 33)); // ?!
|
||||||
buf[102..=134].copy_from_slice(&anime.get_row(1, 1, 33));
|
buf[102..=134].copy_from_slice(&self.get_row(1, 1, 33));
|
||||||
buf[137..=169].copy_from_slice(&anime.get_row(2, 1, 33));
|
buf[137..=169].copy_from_slice(&self.get_row(2, 1, 33));
|
||||||
buf[170..=202].copy_from_slice(&anime.get_row(2, 0, 33));
|
buf[170..=202].copy_from_slice(&self.get_row(2, 0, 33));
|
||||||
buf[204..=236].copy_from_slice(&anime.get_row(3, 0, 33));
|
buf[204..=236].copy_from_slice(&self.get_row(3, 0, 33)); // This and above cause overflow?
|
||||||
buf[237..=268].copy_from_slice(&anime.get_row(4, 0, 32));
|
buf[237..=268].copy_from_slice(&self.get_row(4, 0, 32));
|
||||||
buf[270..=301].copy_from_slice(&anime.get_row(5, 0, 32));
|
buf[270..=301].copy_from_slice(&self.get_row(5, 0, 32));
|
||||||
buf[302..=332].copy_from_slice(&anime.get_row(6, 0, 31));
|
buf[302..=332].copy_from_slice(&self.get_row(6, 0, 31));
|
||||||
buf[334..=364].copy_from_slice(&anime.get_row(7, 0, 31));
|
buf[334..=364].copy_from_slice(&self.get_row(7, 0, 31));
|
||||||
buf[365..=394].copy_from_slice(&anime.get_row(8, 0, 30));
|
buf[365..=394].copy_from_slice(&self.get_row(8, 0, 30));
|
||||||
buf[396..=425].copy_from_slice(&anime.get_row(9, 0, 30));
|
buf[396..=425].copy_from_slice(&self.get_row(9, 0, 30));
|
||||||
buf[426..=454].copy_from_slice(&anime.get_row(10, 0, 29));
|
buf[426..=454].copy_from_slice(&self.get_row(10, 0, 29));
|
||||||
buf[456..=484].copy_from_slice(&anime.get_row(11, 0, 29));
|
buf[456..=484].copy_from_slice(&self.get_row(11, 0, 29));
|
||||||
buf[485..=512].copy_from_slice(&anime.get_row(12, 0, 28));
|
buf[485..=512].copy_from_slice(&self.get_row(12, 0, 28));
|
||||||
buf[514..=541].copy_from_slice(&anime.get_row(13, 0, 28));
|
buf[514..=541].copy_from_slice(&self.get_row(13, 0, 28));
|
||||||
buf[542..=568].copy_from_slice(&anime.get_row(14, 0, 27));
|
buf[542..=568].copy_from_slice(&self.get_row(14, 0, 27));
|
||||||
buf[570..=596].copy_from_slice(&anime.get_row(15, 0, 27));
|
buf[570..=596].copy_from_slice(&self.get_row(15, 0, 27));
|
||||||
buf[597..=622].copy_from_slice(&anime.get_row(16, 0, 26));
|
buf[597..=622].copy_from_slice(&self.get_row(16, 0, 26));
|
||||||
buf[624..=649].copy_from_slice(&anime.get_row(17, 0, 26));
|
buf[624..=649].copy_from_slice(&self.get_row(17, 0, 26));
|
||||||
buf[650..=674].copy_from_slice(&anime.get_row(18, 0, 25));
|
buf[650..=674].copy_from_slice(&self.get_row(18, 0, 25));
|
||||||
buf[676..=700].copy_from_slice(&anime.get_row(19, 0, 25));
|
buf[676..=700].copy_from_slice(&self.get_row(19, 0, 25));
|
||||||
buf[701..=724].copy_from_slice(&anime.get_row(20, 0, 24));
|
buf[701..=724].copy_from_slice(&self.get_row(20, 0, 24));
|
||||||
buf[726..=749].copy_from_slice(&anime.get_row(21, 0, 24));
|
buf[726..=749].copy_from_slice(&self.get_row(21, 0, 24));
|
||||||
buf[750..=772].copy_from_slice(&anime.get_row(22, 0, 23));
|
buf[750..=772].copy_from_slice(&self.get_row(22, 0, 23));
|
||||||
buf[774..=796].copy_from_slice(&anime.get_row(23, 0, 23));
|
buf[774..=796].copy_from_slice(&self.get_row(23, 0, 23));
|
||||||
buf[797..=818].copy_from_slice(&anime.get_row(24, 0, 22));
|
buf[797..=818].copy_from_slice(&self.get_row(24, 0, 22));
|
||||||
buf[820..=841].copy_from_slice(&anime.get_row(25, 0, 22));
|
buf[820..=841].copy_from_slice(&self.get_row(25, 0, 22));
|
||||||
buf[842..=862].copy_from_slice(&anime.get_row(26, 0, 21));
|
buf[842..=862].copy_from_slice(&self.get_row(26, 0, 21));
|
||||||
buf[864..=884].copy_from_slice(&anime.get_row(27, 0, 21));
|
buf[864..=884].copy_from_slice(&self.get_row(27, 0, 21));
|
||||||
buf[885..=904].copy_from_slice(&anime.get_row(28, 0, 20));
|
buf[885..=904].copy_from_slice(&self.get_row(28, 0, 20));
|
||||||
buf[906..=925].copy_from_slice(&anime.get_row(29, 0, 20));
|
buf[906..=925].copy_from_slice(&self.get_row(29, 0, 20));
|
||||||
buf[926..=944].copy_from_slice(&anime.get_row(30, 0, 19));
|
buf[926..=944].copy_from_slice(&self.get_row(30, 0, 19));
|
||||||
buf[946..=964].copy_from_slice(&anime.get_row(31, 0, 19));
|
buf[946..=964].copy_from_slice(&self.get_row(31, 0, 19));
|
||||||
buf[965..=982].copy_from_slice(&anime.get_row(32, 0, 18));
|
buf[965..=982].copy_from_slice(&self.get_row(32, 0, 18));
|
||||||
buf[984..=1001].copy_from_slice(&anime.get_row(33, 0, 18));
|
buf[984..=1001].copy_from_slice(&self.get_row(33, 0, 18));
|
||||||
buf[1002..=1018].copy_from_slice(&anime.get_row(34, 0, 17));
|
buf[1002..=1018].copy_from_slice(&self.get_row(34, 0, 17));
|
||||||
buf[1020..=1036].copy_from_slice(&anime.get_row(35, 0, 17));
|
buf[1020..=1036].copy_from_slice(&self.get_row(35, 0, 17));
|
||||||
buf[1037..=1052].copy_from_slice(&anime.get_row(36, 0, 16));
|
buf[1037..=1052].copy_from_slice(&self.get_row(36, 0, 16));
|
||||||
buf[1054..=1069].copy_from_slice(&anime.get_row(37, 0, 16));
|
buf[1054..=1069].copy_from_slice(&self.get_row(37, 0, 16));
|
||||||
buf[1070..=1084].copy_from_slice(&anime.get_row(38, 0, 15));
|
buf[1070..=1084].copy_from_slice(&self.get_row(38, 0, 15));
|
||||||
buf[1086..=1100].copy_from_slice(&anime.get_row(39, 0, 15));
|
buf[1086..=1100].copy_from_slice(&self.get_row(39, 0, 15));
|
||||||
buf[1101..=1114].copy_from_slice(&anime.get_row(40, 0, 14));
|
buf[1101..=1114].copy_from_slice(&self.get_row(40, 0, 14));
|
||||||
buf[1116..=1129].copy_from_slice(&anime.get_row(41, 0, 14));
|
buf[1116..=1129].copy_from_slice(&self.get_row(41, 0, 14));
|
||||||
buf[1130..=1142].copy_from_slice(&anime.get_row(42, 0, 13));
|
buf[1130..=1142].copy_from_slice(&self.get_row(42, 0, 13));
|
||||||
buf[1144..=1156].copy_from_slice(&anime.get_row(43, 0, 13));
|
buf[1144..=1156].copy_from_slice(&self.get_row(43, 0, 13));
|
||||||
buf[1157..=1168].copy_from_slice(&anime.get_row(44, 0, 12));
|
buf[1157..=1168].copy_from_slice(&self.get_row(44, 0, 12));
|
||||||
buf[1170..=1181].copy_from_slice(&anime.get_row(45, 0, 12));
|
buf[1170..=1181].copy_from_slice(&self.get_row(45, 0, 12));
|
||||||
buf[1182..=1192].copy_from_slice(&anime.get_row(46, 0, 11));
|
buf[1182..=1192].copy_from_slice(&self.get_row(46, 0, 11));
|
||||||
buf[1194..=1204].copy_from_slice(&anime.get_row(47, 0, 11));
|
buf[1194..=1204].copy_from_slice(&self.get_row(47, 0, 11));
|
||||||
buf[1205..=1214].copy_from_slice(&anime.get_row(48, 0, 10));
|
buf[1205..=1214].copy_from_slice(&self.get_row(48, 0, 10));
|
||||||
buf[1216..=1225].copy_from_slice(&anime.get_row(49, 0, 10));
|
buf[1216..=1225].copy_from_slice(&self.get_row(49, 0, 10));
|
||||||
buf[1226..=1234].copy_from_slice(&anime.get_row(50, 0, 9));
|
buf[1226..=1234].copy_from_slice(&self.get_row(50, 0, 9));
|
||||||
buf[1236..=1244].copy_from_slice(&anime.get_row(51, 0, 9));
|
buf[1236..=1244].copy_from_slice(&self.get_row(51, 0, 9));
|
||||||
|
|
||||||
AnimeDataBuffer::from_vec(buf)
|
AnimeDataBuffer::from_vec(crate::AnimeType::GA401, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_ga402_packets(&self) -> Result<AnimeDataBuffer> {
|
||||||
|
let mut buf = vec![0u8; AnimeType::GA402.data_length()];
|
||||||
|
let mut start_index: usize = 0;
|
||||||
|
|
||||||
|
fn copy_slice(
|
||||||
|
buf: &mut Vec<u8>,
|
||||||
|
anime: &AnimeDiagonal,
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
start_index: &mut usize,
|
||||||
|
len: usize,
|
||||||
|
) {
|
||||||
|
buf[*start_index..*start_index + len].copy_from_slice(&anime.get_row(x, y, len));
|
||||||
|
*start_index += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
let b = &mut buf;
|
||||||
|
let a = &self;
|
||||||
|
copy_slice(b, a, 0, 5, &mut start_index, 34);
|
||||||
|
copy_slice(b, a, 1, 5, &mut start_index, 34);
|
||||||
|
copy_slice(b, a, 1, 4, &mut start_index, 34);
|
||||||
|
copy_slice(b, a, 2, 4, &mut start_index, 34);
|
||||||
|
copy_slice(b, a, 2, 3, &mut start_index, 34);
|
||||||
|
copy_slice(b, a, 3, 3, &mut start_index, 34);
|
||||||
|
copy_slice(b, a, 3, 2, &mut start_index, 34);
|
||||||
|
copy_slice(b, a, 4, 2, &mut start_index, 34);
|
||||||
|
copy_slice(b, a, 4, 1, &mut start_index, 34);
|
||||||
|
copy_slice(b, a, 5, 1, &mut start_index, 34);
|
||||||
|
copy_slice(b, a, 5, 0, &mut start_index, 34);
|
||||||
|
copy_slice(b, a, 6, 0, &mut start_index, 34);
|
||||||
|
copy_slice(b, a, 7, 0, &mut start_index, 33);
|
||||||
|
copy_slice(b, a, 8, 0, &mut start_index, 33);
|
||||||
|
copy_slice(b, a, 9, 0, &mut start_index, 32);
|
||||||
|
copy_slice(b, a, 10, 0, &mut start_index, 32);
|
||||||
|
copy_slice(b, a, 11, 0, &mut start_index, 31);
|
||||||
|
copy_slice(b, a, 12, 0, &mut start_index, 31);
|
||||||
|
copy_slice(b, a, 13, 0, &mut start_index, 30);
|
||||||
|
copy_slice(b, a, 14, 0, &mut start_index, 30);
|
||||||
|
copy_slice(b, a, 15, 0, &mut start_index, 29);
|
||||||
|
copy_slice(b, a, 16, 0, &mut start_index, 29);
|
||||||
|
copy_slice(b, a, 17, 0, &mut start_index, 28);
|
||||||
|
copy_slice(b, a, 18, 0, &mut start_index, 28);
|
||||||
|
copy_slice(b, a, 19, 0, &mut start_index, 27);
|
||||||
|
copy_slice(b, a, 20, 0, &mut start_index, 27);
|
||||||
|
copy_slice(b, a, 21, 0, &mut start_index, 26);
|
||||||
|
copy_slice(b, a, 22, 0, &mut start_index, 26);
|
||||||
|
copy_slice(b, a, 23, 0, &mut start_index, 25);
|
||||||
|
copy_slice(b, a, 24, 0, &mut start_index, 25);
|
||||||
|
copy_slice(b, a, 25, 0, &mut start_index, 24);
|
||||||
|
copy_slice(b, a, 26, 0, &mut start_index, 24);
|
||||||
|
copy_slice(b, a, 27, 0, &mut start_index, 23);
|
||||||
|
copy_slice(b, a, 28, 0, &mut start_index, 23);
|
||||||
|
copy_slice(b, a, 29, 0, &mut start_index, 22);
|
||||||
|
copy_slice(b, a, 30, 0, &mut start_index, 22);
|
||||||
|
copy_slice(b, a, 31, 0, &mut start_index, 21);
|
||||||
|
copy_slice(b, a, 32, 0, &mut start_index, 21);
|
||||||
|
copy_slice(b, a, 33, 0, &mut start_index, 20);
|
||||||
|
copy_slice(b, a, 34, 0, &mut start_index, 20);
|
||||||
|
copy_slice(b, a, 35, 0, &mut start_index, 19);
|
||||||
|
copy_slice(b, a, 36, 0, &mut start_index, 19);
|
||||||
|
copy_slice(b, a, 37, 0, &mut start_index, 18);
|
||||||
|
copy_slice(b, a, 38, 0, &mut start_index, 18);
|
||||||
|
copy_slice(b, a, 39, 0, &mut start_index, 17);
|
||||||
|
copy_slice(b, a, 40, 0, &mut start_index, 17);
|
||||||
|
copy_slice(b, a, 41, 0, &mut start_index, 16);
|
||||||
|
copy_slice(b, a, 42, 0, &mut start_index, 16);
|
||||||
|
copy_slice(b, a, 43, 0, &mut start_index, 15);
|
||||||
|
copy_slice(b, a, 44, 0, &mut start_index, 15);
|
||||||
|
copy_slice(b, a, 45, 0, &mut start_index, 14);
|
||||||
|
copy_slice(b, a, 46, 0, &mut start_index, 14);
|
||||||
|
copy_slice(b, a, 47, 0, &mut start_index, 13);
|
||||||
|
copy_slice(b, a, 48, 0, &mut start_index, 13);
|
||||||
|
copy_slice(b, a, 49, 0, &mut start_index, 12);
|
||||||
|
copy_slice(b, a, 50, 0, &mut start_index, 12);
|
||||||
|
copy_slice(b, a, 51, 0, &mut start_index, 11);
|
||||||
|
copy_slice(b, a, 52, 0, &mut start_index, 11);
|
||||||
|
copy_slice(b, a, 53, 0, &mut start_index, 10);
|
||||||
|
copy_slice(b, a, 54, 0, &mut start_index, 10);
|
||||||
|
copy_slice(b, a, 55, 0, &mut start_index, 9);
|
||||||
|
|
||||||
|
AnimeDataBuffer::from_vec(crate::AnimeType::GA402, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::{convert::TryFrom, path::PathBuf};
|
||||||
|
|
||||||
|
use crate::{AnimeDiagonal, AnimePacketType, AnimeType};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ga401_diagonal_packet_check() {
|
||||||
|
let pkt0_check = [
|
||||||
|
0x5e, 0xc0, 0x2, 0x1, 0x0, 0x73, 0x2, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0x0, 0xff, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0x0, 0xff, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff,
|
||||||
|
0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0,
|
||||||
|
];
|
||||||
|
let pkt1_check = [
|
||||||
|
0x5e, 0xc0, 0x2, 0x74, 0x2, 0x73, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||||
|
path.push("test/ga401-diagonal.png");
|
||||||
|
|
||||||
|
let matrix = AnimeDiagonal::from_png(&path, None, 255.0, AnimeType::GA401).unwrap();
|
||||||
|
let data = matrix.into_data_buffer(crate::AnimeType::GA401).unwrap();
|
||||||
|
let pkt = AnimePacketType::try_from(data).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(pkt[0], pkt0_check);
|
||||||
|
assert_eq!(pkt[1], pkt1_check);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ga402_diagonal_packet_check() {
|
||||||
|
let pkt0_check = [
|
||||||
|
0x5e, 0xc0, 0x2, 0x1, 0x0, 0x73, 0x2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0,
|
||||||
|
];
|
||||||
|
let pkt1_check = [
|
||||||
|
0x5e, 0xc0, 0x2, 0x74, 0x2, 0x73, 0x2, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
];
|
||||||
|
let pkt2_check = [
|
||||||
|
0x5e, 0xc0, 0x2, 0xe7, 0x4, 0x73, 0x2, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff,
|
||||||
|
0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff,
|
||||||
|
0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||||
|
path.push("test/ga402-diagonal.png");
|
||||||
|
|
||||||
|
let matrix = AnimeDiagonal::from_png(&path, None, 255.0, AnimeType::GA402).unwrap();
|
||||||
|
let data = matrix.into_data_buffer(crate::AnimeType::GA402).unwrap();
|
||||||
|
let pkt = AnimePacketType::try_from(data).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(pkt[0], pkt0_check);
|
||||||
|
assert_eq!(pkt[1], pkt1_check);
|
||||||
|
assert_eq!(pkt[2], pkt2_check);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore = "Needs the packets verified with capture"]
|
||||||
|
fn ga402_diagonal_fullbright_packet_check() {
|
||||||
|
let pkt0_check = [
|
||||||
|
0x5e, 0xc0, 0x2, 0x1, 0x0, 0x73, 0x2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x67, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
];
|
||||||
|
let pkt1_check = [
|
||||||
|
0x5e, 0xc0, 0x2, 0x74, 0x2, 0x73, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
];
|
||||||
|
let pkt2_check = [
|
||||||
|
0x5e, 0xc0, 0x2, 0xe7, 0x4, 0x73, 0x2, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||||
|
path.push("test/ga402-diagonal-fullbright.png");
|
||||||
|
|
||||||
|
let matrix = AnimeDiagonal::from_png(&path, None, 255.0, AnimeType::GA402).unwrap();
|
||||||
|
let data = matrix.into_data_buffer(crate::AnimeType::GA402).unwrap();
|
||||||
|
let pkt = AnimePacketType::try_from(data).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(pkt[0], pkt0_check);
|
||||||
|
assert_eq!(pkt[1], pkt1_check);
|
||||||
|
assert_eq!(pkt[2], pkt2_check);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ use png_pong::decode::Error as PngError;
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, AnimeError>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AnimeError {
|
pub enum AnimeError {
|
||||||
NoFrames,
|
NoFrames,
|
||||||
@@ -13,6 +15,13 @@ pub enum AnimeError {
|
|||||||
/// The input was incorrect size, expected size is `IncorrectSize(width, height)`
|
/// The input was incorrect size, expected size is `IncorrectSize(width, height)`
|
||||||
IncorrectSize(u32, u32),
|
IncorrectSize(u32, u32),
|
||||||
Dbus(String),
|
Dbus(String),
|
||||||
|
Udev(String, std::io::Error),
|
||||||
|
NoDevice,
|
||||||
|
UnsupportedDevice,
|
||||||
|
InvalidBrightness(f32),
|
||||||
|
DataBufferLength,
|
||||||
|
PixelGifWidth(usize),
|
||||||
|
PixelGifHeight(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for AnimeError {
|
impl fmt::Display for AnimeError {
|
||||||
@@ -30,6 +39,25 @@ impl fmt::Display for AnimeError {
|
|||||||
width, height
|
width, height
|
||||||
),
|
),
|
||||||
AnimeError::Dbus(detail) => write!(f, "{}", detail),
|
AnimeError::Dbus(detail) => write!(f, "{}", detail),
|
||||||
|
AnimeError::Udev(deets, error) => write!(f, "udev {}: {}", deets, error),
|
||||||
|
AnimeError::NoDevice => write!(f, "No AniMe Matrix device found"),
|
||||||
|
AnimeError::DataBufferLength => write!(
|
||||||
|
f,
|
||||||
|
"The data buffer was incorrect length for generating USB packets"
|
||||||
|
),
|
||||||
|
AnimeError::UnsupportedDevice => write!(f, "Unsupported AniMe Matrix device found"),
|
||||||
|
AnimeError::InvalidBrightness(bright) => write!(
|
||||||
|
f,
|
||||||
|
"Image brightness must be between 0.0 and 1.0 (inclusive), was {}",
|
||||||
|
bright
|
||||||
|
),
|
||||||
|
AnimeError::PixelGifWidth(n) => {
|
||||||
|
write!(f, "The gif used for pixel-perfect gif is is wider than {n}")
|
||||||
|
}
|
||||||
|
AnimeError::PixelGifHeight(n) => write!(
|
||||||
|
f,
|
||||||
|
"The gif used for pixel-perfect gif is is taller than {n}"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,3 +84,10 @@ impl From<DecodingError> for AnimeError {
|
|||||||
AnimeError::Gif(err)
|
AnimeError::Gif(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<AnimeError> for zbus::fdo::Error {
|
||||||
|
#[inline]
|
||||||
|
fn from(err: AnimeError) -> Self {
|
||||||
|
zbus::fdo::Error::Failed(format!("{}", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use std::convert::TryFrom;
|
||||||
use std::{fs::File, path::Path, time::Duration};
|
use std::{fs::File, path::Path, time::Duration};
|
||||||
|
|
||||||
use crate::{error::AnimeError, AnimeDataBuffer, AnimeDiagonal, AnimeImage, Pixel};
|
use crate::error::AnimeError;
|
||||||
|
use crate::{error::Result, AnimeDataBuffer, AnimeDiagonal, AnimeImage, AnimeType, Pixel};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct AnimeFrame {
|
pub struct AnimeFrame {
|
||||||
@@ -88,12 +90,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,
|
||||||
) -> Result<Self, AnimeError> {
|
anime_type: AnimeType,
|
||||||
let mut matrix = AnimeDiagonal::new(None);
|
) -> Result<Self> {
|
||||||
|
let mut matrix = AnimeDiagonal::new(anime_type, None);
|
||||||
|
|
||||||
let mut decoder = gif::DecodeOptions::new();
|
let mut decoder = gif::DecodeOptions::new();
|
||||||
// Configure the decoder such that it will expand the image to RGBA.
|
// Configure the decoder such that it will expand the image to RGBA.
|
||||||
@@ -115,13 +118,22 @@ impl AnimeGif {
|
|||||||
// should be t but not in some gifs? What, ASUS, what?
|
// should be t but not in some gifs? What, ASUS, what?
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
matrix.get_mut()[y + frame.top as usize][x + frame.left as usize] =
|
let tmp = matrix.get_mut();
|
||||||
(px[0] as f32 * brightness) as u8;
|
let y = y + frame.top as usize;
|
||||||
|
if y >= tmp.len() {
|
||||||
|
return Err(AnimeError::PixelGifHeight(tmp.len()));
|
||||||
|
}
|
||||||
|
let x = x + frame.left as usize;
|
||||||
|
if x >= tmp[y].len() {
|
||||||
|
return Err(AnimeError::PixelGifWidth(tmp[y].len()));
|
||||||
|
}
|
||||||
|
|
||||||
|
matrix.get_mut()[y][x] = (px[0] as f32 * brightness) as u8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
frames.push(AnimeFrame {
|
frames.push(AnimeFrame {
|
||||||
data: <AnimeDataBuffer>::from(&matrix),
|
data: matrix.into_data_buffer(anime_type)?,
|
||||||
delay: Duration::from_millis(wait as u64),
|
delay: Duration::from_millis(wait as u64),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -130,12 +142,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> {
|
||||||
let image = AnimeDiagonal::from_png(file_name, None, brightness)?;
|
let image = AnimeDiagonal::from_png(file_name, None, brightness, anime_type)?;
|
||||||
|
|
||||||
let mut total = Duration::from_millis(1000);
|
let mut total = Duration::from_millis(1000);
|
||||||
if let AnimTime::Fade(fade) = duration {
|
if let AnimTime::Fade(fade) = duration {
|
||||||
@@ -148,7 +161,7 @@ impl AnimeGif {
|
|||||||
let frame_count = total.as_millis() / 30;
|
let frame_count = total.as_millis() / 30;
|
||||||
|
|
||||||
let single = AnimeFrame {
|
let single = AnimeFrame {
|
||||||
data: <AnimeDataBuffer>::from(&image),
|
data: image.into_data_buffer(anime_type)?,
|
||||||
delay: Duration::from_millis(30),
|
delay: Duration::from_millis(30),
|
||||||
};
|
};
|
||||||
let frames = vec![single; frame_count as usize];
|
let frames = vec![single; frame_count as usize];
|
||||||
@@ -159,14 +172,15 @@ 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,
|
||||||
) -> Result<Self, AnimeError> {
|
anime_type: AnimeType,
|
||||||
|
) -> Result<Self> {
|
||||||
let mut frames = Vec::new();
|
let mut frames = Vec::new();
|
||||||
|
|
||||||
let mut decoder = gif::DecodeOptions::new();
|
let mut decoder = gif::DecodeOptions::new();
|
||||||
@@ -187,7 +201,8 @@ impl AnimeGif {
|
|||||||
brightness,
|
brightness,
|
||||||
pixels,
|
pixels,
|
||||||
decoder.width() as u32,
|
decoder.width() as u32,
|
||||||
);
|
anime_type,
|
||||||
|
)?;
|
||||||
|
|
||||||
while let Some(frame) = decoder.read_next_frame()? {
|
while let Some(frame) = decoder.read_next_frame()? {
|
||||||
let wait = frame.delay * 10;
|
let wait = frame.delay * 10;
|
||||||
@@ -201,7 +216,8 @@ impl AnimeGif {
|
|||||||
brightness,
|
brightness,
|
||||||
pixels,
|
pixels,
|
||||||
width as u32,
|
width as u32,
|
||||||
);
|
anime_type,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
for (y, row) in frame.buffer.chunks(frame.width as usize * 4).enumerate() {
|
for (y, row) in frame.buffer.chunks(frame.width as usize * 4).enumerate() {
|
||||||
for (x, px) in row.chunks(4).enumerate() {
|
for (x, px) in row.chunks(4).enumerate() {
|
||||||
@@ -220,7 +236,7 @@ impl AnimeGif {
|
|||||||
image.update();
|
image.update();
|
||||||
|
|
||||||
frames.push(AnimeFrame {
|
frames.push(AnimeFrame {
|
||||||
data: <AnimeDataBuffer>::from(&image),
|
data: <AnimeDataBuffer>::try_from(&image)?,
|
||||||
delay: Duration::from_millis(wait as u64),
|
delay: Duration::from_millis(wait as u64),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -231,15 +247,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,
|
||||||
) -> Result<Self, AnimeError> {
|
anime_type: AnimeType,
|
||||||
let image = AnimeImage::from_png(file_name, scale, angle, translation, brightness)?;
|
) -> Result<Self> {
|
||||||
|
let image =
|
||||||
|
AnimeImage::from_png(file_name, scale, angle, translation, brightness, anime_type)?;
|
||||||
|
|
||||||
let mut total = Duration::from_millis(1000);
|
let mut total = Duration::from_millis(1000);
|
||||||
if let AnimTime::Fade(fade) = duration {
|
if let AnimTime::Fade(fade) = duration {
|
||||||
@@ -252,7 +270,7 @@ impl AnimeGif {
|
|||||||
let frame_count = total.as_millis() / 30;
|
let frame_count = total.as_millis() / 30;
|
||||||
|
|
||||||
let single = AnimeFrame {
|
let single = AnimeFrame {
|
||||||
data: <AnimeDataBuffer>::from(&image),
|
data: <AnimeDataBuffer>::try_from(&image)?,
|
||||||
delay: Duration::from_millis(30),
|
delay: Duration::from_millis(30),
|
||||||
};
|
};
|
||||||
let frames = vec![single; frame_count as usize];
|
let frames = vec![single; frame_count as usize];
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
use std::time::Duration;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use crate::data::{AnimeDataBuffer, ANIME_DATA_LEN};
|
use crate::data::AnimeDataBuffer;
|
||||||
use crate::image::LED_IMAGE_POSITIONS;
|
use crate::error::{AnimeError, Result};
|
||||||
|
use crate::{AnimeImage, AnimeType};
|
||||||
|
|
||||||
|
// TODO: Adjust these sizes as WIDTH_GA401 WIDTH_GA402
|
||||||
const WIDTH: usize = 33;
|
const WIDTH: usize = 33;
|
||||||
const HEIGHT: usize = 55;
|
const HEIGHT: usize = 55;
|
||||||
|
|
||||||
@@ -14,41 +16,40 @@ const HEIGHT: usize = 55;
|
|||||||
///
|
///
|
||||||
/// **Note:** the columns in each odd row are offset by half a pixel left
|
/// **Note:** the columns in each odd row are offset by half a pixel left
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AnimeGrid([[u8; WIDTH]; HEIGHT], Option<Duration>);
|
pub struct AnimeGrid {
|
||||||
|
anime_type: AnimeType,
|
||||||
impl Default for AnimeGrid {
|
data: [[u8; WIDTH]; HEIGHT],
|
||||||
#[inline]
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new(None)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnimeGrid {
|
impl AnimeGrid {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(duration: Option<Duration>) -> Self {
|
pub fn new(anime_type: AnimeType) -> Self {
|
||||||
AnimeGrid([[0u8; WIDTH]; HEIGHT], duration)
|
Self {
|
||||||
|
anime_type,
|
||||||
|
data: [[0u8; WIDTH]; HEIGHT],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a position in the grid with a brightness value
|
/// Set a position in the grid with a brightness value
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set(&mut self, x: usize, y: usize, b: u8) {
|
pub fn set(&mut self, x: usize, y: usize, b: u8) {
|
||||||
self.0[y][x] = b;
|
self.data[y][x] = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get(&self) -> &[[u8; WIDTH]; HEIGHT] {
|
pub fn get(&self) -> &[[u8; WIDTH]; HEIGHT] {
|
||||||
&self.0
|
&self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_mut(&mut self) -> &mut [[u8; WIDTH]; HEIGHT] {
|
pub fn get_mut(&mut self) -> &mut [[u8; WIDTH]; HEIGHT] {
|
||||||
&mut self.0
|
&mut self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fill the grid with a value
|
/// Fill the grid with a value
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn fill_with(&mut self, fill: u8) {
|
pub fn fill_with(&mut self, fill: u8) {
|
||||||
for row in self.0.iter_mut() {
|
for row in self.data.iter_mut() {
|
||||||
for x in row.iter_mut() {
|
for x in row.iter_mut() {
|
||||||
*x = fill;
|
*x = fill;
|
||||||
}
|
}
|
||||||
@@ -89,21 +90,25 @@ impl AnimeGrid {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<AnimeGrid> for AnimeDataBuffer {
|
impl TryFrom<AnimeGrid> for AnimeDataBuffer {
|
||||||
|
type Error = AnimeError;
|
||||||
|
|
||||||
/// Do conversion from the nested Vec in AniMeMatrix to the two required
|
/// Do conversion from the nested Vec in AniMeMatrix to the two required
|
||||||
/// packets suitable for sending over USB
|
/// packets suitable for sending over USB
|
||||||
#[inline]
|
fn try_from(anime: AnimeGrid) -> Result<Self> {
|
||||||
fn from(anime: AnimeGrid) -> Self {
|
let mut buf = vec![0u8; anime.anime_type.data_length()];
|
||||||
let mut buf = vec![0u8; ANIME_DATA_LEN];
|
|
||||||
|
|
||||||
for (idx, pos) in LED_IMAGE_POSITIONS.iter().enumerate() {
|
for (idx, pos) in AnimeImage::generate_image_positioning(anime.anime_type)
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
if let Some(pos) = pos {
|
if let Some(pos) = pos {
|
||||||
let x = pos.x().ceil() as usize;
|
let x = pos.x().ceil() as usize;
|
||||||
let y = pos.y().ceil() as usize;
|
let y = pos.y().ceil() as usize;
|
||||||
buf[idx + 1] = anime.0[y][x];
|
buf[idx + 1] = anime.data[y][x];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AnimeDataBuffer::from_vec(buf)
|
AnimeDataBuffer::from_vec(anime.anime_type, buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +118,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_data_alignment() {
|
fn check_data_alignment() {
|
||||||
let mut matrix = AnimeGrid::new(None);
|
let mut matrix = AnimeGrid::new(AnimeType::GA401);
|
||||||
{
|
{
|
||||||
let tmp = matrix.get_mut();
|
let tmp = matrix.get_mut();
|
||||||
for row in tmp.iter_mut() {
|
for row in tmp.iter_mut() {
|
||||||
@@ -122,7 +127,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let matrix = <AnimeDataBuffer>::from(matrix);
|
let matrix = <AnimeDataBuffer>::try_from(matrix).unwrap();
|
||||||
|
|
||||||
let data_cmp = [
|
let data_cmp = [
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
@@ -171,6 +176,6 @@ mod tests {
|
|||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
];
|
];
|
||||||
assert_eq!(matrix.get(), &data_cmp);
|
assert_eq!(matrix.data(), &data_cmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
use std::{path::PathBuf, time::Duration};
|
|
||||||
|
|
||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::{path::PathBuf, time::Duration};
|
||||||
|
|
||||||
use crate::{error::AnimeError, AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage};
|
use crate::{
|
||||||
|
error::Result, AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType,
|
||||||
|
};
|
||||||
|
|
||||||
/// All the possible AniMe actions that can be used. This enum is intended to be
|
/// All the possible AniMe actions that can be used. This enum is intended to be
|
||||||
/// a helper for loading up `ActionData`.
|
/// a helper for loading up `ActionData`.
|
||||||
@@ -63,26 +65,34 @@ pub enum ActionData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ActionData {
|
impl ActionData {
|
||||||
pub fn from_anime_action(action: &ActionLoader) -> Result<ActionData, AnimeError> {
|
pub fn from_anime_action(anime_type: AnimeType, action: &ActionLoader) -> Result<ActionData> {
|
||||||
let a = match action {
|
let a = match action {
|
||||||
ActionLoader::AsusAnimation {
|
ActionLoader::AsusAnimation {
|
||||||
file,
|
file,
|
||||||
time,
|
time,
|
||||||
brightness,
|
brightness,
|
||||||
} => ActionData::Animation(AnimeGif::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 +104,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 +136,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,
|
||||||
let data = <AnimeDataBuffer>::from(&image);
|
*scale,
|
||||||
|
*angle,
|
||||||
|
*translation,
|
||||||
|
*brightness,
|
||||||
|
anime_type,
|
||||||
|
)?;
|
||||||
|
let data = <AnimeDataBuffer>::try_from(&image)?;
|
||||||
ActionData::Image(Box::new(data))
|
ActionData::Image(Box::new(data))
|
||||||
}
|
}
|
||||||
_ => ActionData::Animation(AnimeGif::create_png_static(
|
_ => ActionData::Animation(AnimeGif::from_png(
|
||||||
file,
|
file,
|
||||||
*scale,
|
*scale,
|
||||||
*angle,
|
*angle,
|
||||||
*translation,
|
*translation,
|
||||||
*time,
|
*time,
|
||||||
*brightness,
|
*brightness,
|
||||||
|
anime_type,
|
||||||
)?),
|
)?),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,20 +165,21 @@ impl ActionData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An optimised precomputed set of actions that the user can cycle through
|
/// An optimised precomputed set of actions that the user can cycle through
|
||||||
#[derive(Debug, Deserialize, Serialize, Default)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct Sequences(Vec<ActionData>);
|
pub struct Sequences(Vec<ActionData>, AnimeType);
|
||||||
|
|
||||||
impl Sequences {
|
impl Sequences {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> Self {
|
pub fn new(anime_type: AnimeType) -> Self {
|
||||||
Self(Vec::new())
|
Self(Vec::new(), anime_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use a base `AnimeAction` to generate the precomputed data and insert in to
|
/// Use a base `AnimeAction` to generate the precomputed data and insert in to
|
||||||
/// the run buffer
|
/// the run buffer
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert(&mut self, index: usize, action: &ActionLoader) -> Result<(), AnimeError> {
|
pub fn insert(&mut self, index: usize, action: &ActionLoader) -> Result<()> {
|
||||||
self.0.insert(index, ActionData::from_anime_action(action)?);
|
self.0
|
||||||
|
.insert(index, ActionData::from_anime_action(self.1, action)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
//!
|
//!
|
||||||
//! Step 1 need to applied only on fresh system boot.
|
//! Step 1 need to applied only on fresh system boot.
|
||||||
|
|
||||||
|
use crate::{error::AnimeError, AnimeType};
|
||||||
|
|
||||||
const INIT_STR: [u8; 15] = [
|
const INIT_STR: [u8; 15] = [
|
||||||
0x5e, b'A', b'S', b'U', b'S', b' ', b'T', b'e', b'c', b'h', b'.', b'I', b'n', b'c', b'.',
|
0x5e, b'A', b'S', b'U', b'S', b' ', b'T', b'e', b'c', b'h', b'.', b'I', b'n', b'c', b'.',
|
||||||
];
|
];
|
||||||
@@ -15,6 +17,49 @@ const DEV_PAGE: u8 = 0x5e;
|
|||||||
pub const VENDOR_ID: u16 = 0x0b05;
|
pub const VENDOR_ID: u16 = 0x0b05;
|
||||||
pub const PROD_ID: u16 = 0x193b;
|
pub const PROD_ID: u16 = 0x193b;
|
||||||
|
|
||||||
|
/// `get_anime_type` is very broad, matching on part of the laptop board name only. For this
|
||||||
|
/// reason `find_node()` must be used also to verify if the USB device is available.
|
||||||
|
///
|
||||||
|
/// The currently known USB device is `19b6`.
|
||||||
|
#[inline]
|
||||||
|
pub fn get_anime_type() -> Result<AnimeType, AnimeError> {
|
||||||
|
let dmi = sysfs_class::DmiId::default();
|
||||||
|
let board_name = dmi.board_name()?;
|
||||||
|
|
||||||
|
if board_name.contains("GA401I") {
|
||||||
|
return Ok(AnimeType::GA401);
|
||||||
|
} else if board_name.contains("GA401Q") {
|
||||||
|
return Ok(AnimeType::GA401);
|
||||||
|
} else if board_name.contains("GA402R") {
|
||||||
|
return Ok(AnimeType::GA402);
|
||||||
|
}
|
||||||
|
Err(AnimeError::UnsupportedDevice)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the USB device node - known devices so far: `19b6`
|
||||||
|
#[inline]
|
||||||
|
pub fn find_node(id_product: &str) -> Result<String, AnimeError> {
|
||||||
|
let mut enumerator =
|
||||||
|
udev::Enumerator::new().map_err(|err| AnimeError::Udev("enumerator failed".into(), err))?;
|
||||||
|
enumerator
|
||||||
|
.match_subsystem("usb")
|
||||||
|
.map_err(|err| AnimeError::Udev("match_subsystem failed".into(), err))?;
|
||||||
|
|
||||||
|
for device in enumerator
|
||||||
|
.scan_devices()
|
||||||
|
.map_err(|err| AnimeError::Udev("scan_devices failed".into(), err))?
|
||||||
|
{
|
||||||
|
if let Some(attr) = device.attribute_value("idProduct") {
|
||||||
|
if attr == id_product {
|
||||||
|
if let Some(dev_node) = device.devnode() {
|
||||||
|
return Ok(dev_node.to_string_lossy().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(AnimeError::NoDevice)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the two device initialization packets. These are required for device start
|
/// Get the two device initialization packets. These are required for device start
|
||||||
/// after the laptop boots.
|
/// after the laptop boots.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
|
After Width: | Height: | Size: 981 B |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 189 B |
|
After Width: | Height: | Size: 7.5 KiB |
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rog_aura"
|
name = "rog_aura"
|
||||||
version = "1.1.0"
|
version = "1.2.2"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = ["Luke <luke@ljones.dev>"]
|
authors = ["Luke <luke@ljones.dev>"]
|
||||||
@@ -13,12 +13,11 @@ edition = "2018"
|
|||||||
exclude = ["data"]
|
exclude = ["data"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["dbus"]
|
default = ["dbus", "toml"]
|
||||||
dbus = ["zvariant", "zvariant_derive"]
|
dbus = ["zvariant"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = "^1.0"
|
serde = "^1.0"
|
||||||
serde_derive = "^1.0"
|
serde_derive = "^1.0"
|
||||||
|
toml = { version = "^0.5", optional = true }
|
||||||
zvariant = { version = "^2.6", optional = true }
|
zvariant = { version = "^3.0", optional = true }
|
||||||
zvariant_derive = { version = "^2.6", optional = true }
|
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
matches = [
|
||||||
|
'G513',
|
||||||
|
]
|
||||||
|
|
||||||
|
locale = "US"
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 0.8
|
||||||
|
row = [
|
||||||
|
'NormalSpacer',
|
||||||
|
'FuncSpacer',
|
||||||
|
'VolDown',
|
||||||
|
'VolUp',
|
||||||
|
'MicMute',
|
||||||
|
'Fan',
|
||||||
|
'Rog',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 0.8
|
||||||
|
row = [
|
||||||
|
'Esc',
|
||||||
|
'FuncSpacer',
|
||||||
|
'F1',
|
||||||
|
'F2',
|
||||||
|
'F3',
|
||||||
|
'F4',
|
||||||
|
'FuncSpacer',
|
||||||
|
'F5',
|
||||||
|
'F6',
|
||||||
|
'F7',
|
||||||
|
'F8',
|
||||||
|
'FuncSpacer',
|
||||||
|
'F9',
|
||||||
|
'F10',
|
||||||
|
'F11',
|
||||||
|
'F12',
|
||||||
|
'RowEndSpacer',
|
||||||
|
'NumPadDel',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'Tilde',
|
||||||
|
'N1',
|
||||||
|
'N2',
|
||||||
|
'N3',
|
||||||
|
'N4',
|
||||||
|
'N5',
|
||||||
|
'N6',
|
||||||
|
'N7',
|
||||||
|
'N8',
|
||||||
|
'N9',
|
||||||
|
'N0',
|
||||||
|
'Hyphen',
|
||||||
|
'Equals',
|
||||||
|
'BkSpc',
|
||||||
|
'RowEndSpacer',
|
||||||
|
'Home',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'Tab',
|
||||||
|
'Q',
|
||||||
|
'W',
|
||||||
|
'E',
|
||||||
|
'R',
|
||||||
|
'T',
|
||||||
|
'Y',
|
||||||
|
'U',
|
||||||
|
'I',
|
||||||
|
'O',
|
||||||
|
'P',
|
||||||
|
'LBracket',
|
||||||
|
'RBracket',
|
||||||
|
'BackSlash',
|
||||||
|
'RowEndSpacer',
|
||||||
|
'PgUp',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'Caps',
|
||||||
|
'A',
|
||||||
|
'S',
|
||||||
|
'D',
|
||||||
|
'F',
|
||||||
|
'G',
|
||||||
|
'H',
|
||||||
|
'J',
|
||||||
|
'K',
|
||||||
|
'L',
|
||||||
|
'SemiColon',
|
||||||
|
'Quote',
|
||||||
|
'Return',
|
||||||
|
'RowEndSpacer',
|
||||||
|
'PgDn',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'LShift',
|
||||||
|
'Z',
|
||||||
|
'X',
|
||||||
|
'C',
|
||||||
|
'V',
|
||||||
|
'B',
|
||||||
|
'N',
|
||||||
|
'M',
|
||||||
|
'Comma',
|
||||||
|
'Period',
|
||||||
|
'FwdSlash',
|
||||||
|
'Rshift',
|
||||||
|
'RowEndSpacer',
|
||||||
|
'End',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.2
|
||||||
|
row = [
|
||||||
|
'LCtrl',
|
||||||
|
'LFn',
|
||||||
|
'Meta',
|
||||||
|
'LAlt',
|
||||||
|
'Space',
|
||||||
|
'RAlt',
|
||||||
|
'PrtSc',
|
||||||
|
'RCtrl',
|
||||||
|
'ArrowSpacer',
|
||||||
|
'Up',
|
||||||
|
'ArrowSpacer',
|
||||||
|
'RowEndSpacer',
|
||||||
|
'RFn',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 0.8
|
||||||
|
row = [
|
||||||
|
'ArrowSpacer',
|
||||||
|
'ArrowSpacer',
|
||||||
|
'ArrowSpacer',
|
||||||
|
'ArrowSpacer',
|
||||||
|
'ArrowSpacer',
|
||||||
|
'ArrowSpacer',
|
||||||
|
'ArrowSpacer',
|
||||||
|
'ArrowSpacer',
|
||||||
|
'ArrowSpacer',
|
||||||
|
'ArrowSpacer',
|
||||||
|
'ArrowSpacer',
|
||||||
|
'ArrowSpacer',
|
||||||
|
'ArrowSpacer',
|
||||||
|
'Left',
|
||||||
|
'Down',
|
||||||
|
'Right',
|
||||||
|
'ArrowSpacer',
|
||||||
|
]
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
matches = [
|
||||||
|
'G533',
|
||||||
|
]
|
||||||
|
|
||||||
|
locale = "US"
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 0.8
|
||||||
|
row = [
|
||||||
|
'NormalSpacer',
|
||||||
|
'FuncSpacer',
|
||||||
|
'VolDown',
|
||||||
|
'VolUp',
|
||||||
|
'MicMute',
|
||||||
|
'Fan',
|
||||||
|
'Rog',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 0.8
|
||||||
|
row = [
|
||||||
|
'Esc',
|
||||||
|
'FuncSpacer',
|
||||||
|
'F1',
|
||||||
|
'F2',
|
||||||
|
'F3',
|
||||||
|
'F4',
|
||||||
|
'FuncSpacer',
|
||||||
|
'F5',
|
||||||
|
'F6',
|
||||||
|
'F7',
|
||||||
|
'F8',
|
||||||
|
'FuncSpacer',
|
||||||
|
'F9',
|
||||||
|
'F10',
|
||||||
|
'F11',
|
||||||
|
'F12',
|
||||||
|
'Del',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'Tilde',
|
||||||
|
'N1',
|
||||||
|
'N2',
|
||||||
|
'N3',
|
||||||
|
'N4',
|
||||||
|
'N5',
|
||||||
|
'N6',
|
||||||
|
'N7',
|
||||||
|
'N8',
|
||||||
|
'N9',
|
||||||
|
'N0',
|
||||||
|
'Hyphen',
|
||||||
|
'Equals',
|
||||||
|
'BkSpc',
|
||||||
|
'MediaPlay',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'Tab',
|
||||||
|
'Q',
|
||||||
|
'W',
|
||||||
|
'E',
|
||||||
|
'R',
|
||||||
|
'T',
|
||||||
|
'Y',
|
||||||
|
'U',
|
||||||
|
'I',
|
||||||
|
'O',
|
||||||
|
'P',
|
||||||
|
'LBracket',
|
||||||
|
'RBracket',
|
||||||
|
'BackSlash',
|
||||||
|
'MediaStop',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'Caps',
|
||||||
|
'A',
|
||||||
|
'S',
|
||||||
|
'D',
|
||||||
|
'F',
|
||||||
|
'G',
|
||||||
|
'H',
|
||||||
|
'J',
|
||||||
|
'K',
|
||||||
|
'L',
|
||||||
|
'SemiColon',
|
||||||
|
'Quote',
|
||||||
|
'Return',
|
||||||
|
'MediaPrev',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'LShift',
|
||||||
|
'Z',
|
||||||
|
'X',
|
||||||
|
'C',
|
||||||
|
'V',
|
||||||
|
'B',
|
||||||
|
'N',
|
||||||
|
'M',
|
||||||
|
'Comma',
|
||||||
|
'Period',
|
||||||
|
'FwdSlash',
|
||||||
|
'RshiftSmall',
|
||||||
|
'UpRegular',
|
||||||
|
'MediaNext',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'LCtrlMed',
|
||||||
|
'LFn',
|
||||||
|
'Meta',
|
||||||
|
'LAlt',
|
||||||
|
'Space',
|
||||||
|
'RAlt',
|
||||||
|
'PrtSc',
|
||||||
|
'RCtrl',
|
||||||
|
'ArrowRegularSpacer',
|
||||||
|
'LeftRegular',
|
||||||
|
'DownRegular',
|
||||||
|
'RightRegular',
|
||||||
|
]
|
||||||
|
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
matches = [
|
||||||
|
'GA401',
|
||||||
|
'GA402',
|
||||||
|
'GU603',
|
||||||
|
'GV301',
|
||||||
|
'GA502',
|
||||||
|
'GA503',
|
||||||
|
]
|
||||||
|
|
||||||
|
locale = "US"
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 0.8
|
||||||
|
row = [
|
||||||
|
'NormalSpacer',
|
||||||
|
'FuncSpacer',
|
||||||
|
'VolDown',
|
||||||
|
'VolUp',
|
||||||
|
'MicMute',
|
||||||
|
'Rog',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 0.8
|
||||||
|
row = [
|
||||||
|
'Esc',
|
||||||
|
'FuncSpacer',
|
||||||
|
'F1',
|
||||||
|
'F2',
|
||||||
|
'F3',
|
||||||
|
'F4',
|
||||||
|
'FuncSpacer',
|
||||||
|
'F5',
|
||||||
|
'F6',
|
||||||
|
'F7',
|
||||||
|
'F8',
|
||||||
|
'FuncSpacer',
|
||||||
|
'F9',
|
||||||
|
'F10',
|
||||||
|
'F11',
|
||||||
|
'F12',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'Tilde',
|
||||||
|
'N1',
|
||||||
|
'N2',
|
||||||
|
'N3',
|
||||||
|
'N4',
|
||||||
|
'N5',
|
||||||
|
'N6',
|
||||||
|
'N7',
|
||||||
|
'N8',
|
||||||
|
'N9',
|
||||||
|
'N0',
|
||||||
|
'Hyphen',
|
||||||
|
'Equals',
|
||||||
|
'BkSpc',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'Tab',
|
||||||
|
'Q',
|
||||||
|
'W',
|
||||||
|
'E',
|
||||||
|
'R',
|
||||||
|
'T',
|
||||||
|
'Y',
|
||||||
|
'U',
|
||||||
|
'I',
|
||||||
|
'O',
|
||||||
|
'P',
|
||||||
|
'LBracket',
|
||||||
|
'RBracket',
|
||||||
|
'BackSlash',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'Caps',
|
||||||
|
'A',
|
||||||
|
'S',
|
||||||
|
'D',
|
||||||
|
'F',
|
||||||
|
'G',
|
||||||
|
'H',
|
||||||
|
'J',
|
||||||
|
'K',
|
||||||
|
'L',
|
||||||
|
'SemiColon',
|
||||||
|
'Quote',
|
||||||
|
'Return',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'LShift',
|
||||||
|
'Z',
|
||||||
|
'X',
|
||||||
|
'C',
|
||||||
|
'V',
|
||||||
|
'B',
|
||||||
|
'N',
|
||||||
|
'M',
|
||||||
|
'Comma',
|
||||||
|
'Period',
|
||||||
|
'FwdSlash',
|
||||||
|
'Rshift',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.2
|
||||||
|
row = [
|
||||||
|
'LCtrl',
|
||||||
|
'LFn',
|
||||||
|
'Meta',
|
||||||
|
'LAlt',
|
||||||
|
'Space',
|
||||||
|
'RAlt',
|
||||||
|
'PrtSc',
|
||||||
|
'RCtrl',
|
||||||
|
'ArrowSpacer',
|
||||||
|
'Up',
|
||||||
|
'ArrowSpacer',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 0.8
|
||||||
|
row = [
|
||||||
|
'FuncSpacer',
|
||||||
|
'FuncSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'Left',
|
||||||
|
'Down',
|
||||||
|
'Right',
|
||||||
|
'ArrowSpacer',
|
||||||
|
]
|
||||||
|
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
matches = [
|
||||||
|
'GL504',
|
||||||
|
]
|
||||||
|
|
||||||
|
locale = "US"
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 0.8
|
||||||
|
row = [
|
||||||
|
'NormalSpacer',
|
||||||
|
'FuncSpacer',
|
||||||
|
'VolDown',
|
||||||
|
'VolUp',
|
||||||
|
'MicMute',
|
||||||
|
'Rog',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 0.8
|
||||||
|
row = [
|
||||||
|
'Esc',
|
||||||
|
'FuncSpacer',
|
||||||
|
'F1',
|
||||||
|
'F2',
|
||||||
|
'F3',
|
||||||
|
'F4',
|
||||||
|
'FuncSpacer',
|
||||||
|
'F5',
|
||||||
|
'F6',
|
||||||
|
'F7',
|
||||||
|
'F8',
|
||||||
|
'FuncSpacer',
|
||||||
|
'F9',
|
||||||
|
'F10',
|
||||||
|
'F11',
|
||||||
|
'F12',
|
||||||
|
'RowEndSpacer',
|
||||||
|
'Del',
|
||||||
|
'NumPadPause',
|
||||||
|
'NumPadPrtSc',
|
||||||
|
'NumPadHome',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'Tilde',
|
||||||
|
'N1',
|
||||||
|
'N2',
|
||||||
|
'N3',
|
||||||
|
'N4',
|
||||||
|
'N5',
|
||||||
|
'N6',
|
||||||
|
'N7',
|
||||||
|
'N8',
|
||||||
|
'N9',
|
||||||
|
'N0',
|
||||||
|
'Hyphen',
|
||||||
|
'Equals',
|
||||||
|
'BkSpc',
|
||||||
|
'RowEndSpacer',
|
||||||
|
'NumLock',
|
||||||
|
'FwdSlash',
|
||||||
|
'Star',
|
||||||
|
'Hyphen',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'Tab',
|
||||||
|
'Q',
|
||||||
|
'W',
|
||||||
|
'E',
|
||||||
|
'R',
|
||||||
|
'T',
|
||||||
|
'Y',
|
||||||
|
'U',
|
||||||
|
'I',
|
||||||
|
'O',
|
||||||
|
'P',
|
||||||
|
'LBracket',
|
||||||
|
'RBracket',
|
||||||
|
'BackSlash',
|
||||||
|
'RowEndSpacer',
|
||||||
|
'N7',
|
||||||
|
'N8',
|
||||||
|
'N9',
|
||||||
|
'NumPadPlus',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'Caps',
|
||||||
|
'A',
|
||||||
|
'S',
|
||||||
|
'D',
|
||||||
|
'F',
|
||||||
|
'G',
|
||||||
|
'H',
|
||||||
|
'J',
|
||||||
|
'K',
|
||||||
|
'L',
|
||||||
|
'SemiColon',
|
||||||
|
'Quote',
|
||||||
|
'Return',
|
||||||
|
'RowEndSpacer',
|
||||||
|
'N4',
|
||||||
|
'N5',
|
||||||
|
'N6',
|
||||||
|
'NumPadPlus',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'LShift',
|
||||||
|
'Z',
|
||||||
|
'X',
|
||||||
|
'C',
|
||||||
|
'V',
|
||||||
|
'B',
|
||||||
|
'N',
|
||||||
|
'M',
|
||||||
|
'Comma',
|
||||||
|
'Period',
|
||||||
|
'FwdSlash',
|
||||||
|
'Rshift',
|
||||||
|
'RowEndSpacer',
|
||||||
|
'N1',
|
||||||
|
'N2',
|
||||||
|
'N3',
|
||||||
|
'NumPadEnter',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'LCtrl',
|
||||||
|
'LFn',
|
||||||
|
'Meta',
|
||||||
|
'LAlt',
|
||||||
|
'Space',
|
||||||
|
'RAlt',
|
||||||
|
'RFn',
|
||||||
|
'RFn',
|
||||||
|
'RCtrlLarge',
|
||||||
|
'RowEndSpacer',
|
||||||
|
'UpRegular',
|
||||||
|
'N0',
|
||||||
|
'NumPadDel',
|
||||||
|
'NumPadEnter',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'FuncSpacer',
|
||||||
|
'FuncSpacer',
|
||||||
|
'FuncSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'LeftRegular',
|
||||||
|
'RowEndSpacer',
|
||||||
|
'DownRegular',
|
||||||
|
'RightRegular',
|
||||||
|
'NormalSpacer',
|
||||||
|
]
|
||||||
|
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
matches = [
|
||||||
|
'GX502',
|
||||||
|
'GU502',
|
||||||
|
]
|
||||||
|
|
||||||
|
locale = "US"
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 0.8
|
||||||
|
row = [
|
||||||
|
'NormalSpacer',
|
||||||
|
'FuncSpacer',
|
||||||
|
'VolDown',
|
||||||
|
'VolUp',
|
||||||
|
'MicMute',
|
||||||
|
'Rog',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 0.8
|
||||||
|
row = [
|
||||||
|
'Esc',
|
||||||
|
'FuncSpacer',
|
||||||
|
'F1',
|
||||||
|
'F2',
|
||||||
|
'F3',
|
||||||
|
'F4',
|
||||||
|
'FuncSpacer',
|
||||||
|
'F5',
|
||||||
|
'F6',
|
||||||
|
'F7',
|
||||||
|
'F8',
|
||||||
|
'FuncSpacer',
|
||||||
|
'F9',
|
||||||
|
'F10',
|
||||||
|
'F11',
|
||||||
|
'F12',
|
||||||
|
'RowEndSpacer',
|
||||||
|
'Del',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'Tilde',
|
||||||
|
'N1',
|
||||||
|
'N2',
|
||||||
|
'N3',
|
||||||
|
'N4',
|
||||||
|
'N5',
|
||||||
|
'N6',
|
||||||
|
'N7',
|
||||||
|
'N8',
|
||||||
|
'N9',
|
||||||
|
'N0',
|
||||||
|
'Hyphen',
|
||||||
|
'Equals',
|
||||||
|
'BkSpc3_1',
|
||||||
|
'BkSpc3_2',
|
||||||
|
'BkSpc3_3',
|
||||||
|
'RowEndSpacer',
|
||||||
|
'Home',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'Tab',
|
||||||
|
'Q',
|
||||||
|
'W',
|
||||||
|
'E',
|
||||||
|
'R',
|
||||||
|
'T',
|
||||||
|
'Y',
|
||||||
|
'U',
|
||||||
|
'I',
|
||||||
|
'O',
|
||||||
|
'P',
|
||||||
|
'LBracket',
|
||||||
|
'RBracket',
|
||||||
|
'BackSlash',
|
||||||
|
'RowEndSpacer',
|
||||||
|
'PgUp',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'Caps',
|
||||||
|
'A',
|
||||||
|
'S',
|
||||||
|
'D',
|
||||||
|
'F',
|
||||||
|
'G',
|
||||||
|
'H',
|
||||||
|
'J',
|
||||||
|
'K',
|
||||||
|
'L',
|
||||||
|
'SemiColon',
|
||||||
|
'Quote',
|
||||||
|
'Return3_1',
|
||||||
|
'Return3_2',
|
||||||
|
'Return3_3',
|
||||||
|
'RowEndSpacer',
|
||||||
|
'PgDn',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.0
|
||||||
|
row = [
|
||||||
|
'LShift',
|
||||||
|
'Z',
|
||||||
|
'X',
|
||||||
|
'C',
|
||||||
|
'V',
|
||||||
|
'B',
|
||||||
|
'N',
|
||||||
|
'M',
|
||||||
|
'Comma',
|
||||||
|
'Period',
|
||||||
|
'FwdSlash',
|
||||||
|
'Rshift3_1',
|
||||||
|
'Rshift3_2',
|
||||||
|
'Rshift3_3',
|
||||||
|
'RowEndSpacer',
|
||||||
|
'End',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 1.2
|
||||||
|
row = [
|
||||||
|
'LCtrl',
|
||||||
|
'LFn',
|
||||||
|
'Meta',
|
||||||
|
'LAlt',
|
||||||
|
'Space5_1',
|
||||||
|
'Space5_2',
|
||||||
|
'Space5_3',
|
||||||
|
'Space5_4',
|
||||||
|
'Space5_5',
|
||||||
|
'RAlt',
|
||||||
|
'PrtSc',
|
||||||
|
'RCtrl',
|
||||||
|
'ArrowSpacer',
|
||||||
|
'Up',
|
||||||
|
'ArrowSpacer',
|
||||||
|
'RowEndSpacer',
|
||||||
|
'RFn',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rows]]
|
||||||
|
height = 0.8
|
||||||
|
row = [
|
||||||
|
'FuncSpacer',
|
||||||
|
'FuncSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'NormalSpacer',
|
||||||
|
'Left',
|
||||||
|
'Down',
|
||||||
|
'Right',
|
||||||
|
'ArrowSpacer',
|
||||||
|
]
|
||||||
|
|
||||||
@@ -7,17 +7,10 @@ pub const LED_INIT5: [u8; 6] = [0x5e, 0x05, 0x20, 0x31, 0, 0x08];
|
|||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
#[cfg(feature = "dbus")]
|
#[cfg(feature = "dbus")]
|
||||||
use zvariant_derive::Type;
|
use zvariant::Type;
|
||||||
|
|
||||||
use crate::{error::Error, LED_MSG_LEN};
|
use crate::{error::Error, LED_MSG_LEN};
|
||||||
|
|
||||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
|
||||||
#[derive(Debug, PartialEq, Copy, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct LedPowerStates {
|
|
||||||
pub enabled: bool,
|
|
||||||
pub sleep_anim_enabled: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub enum LedBrightness {
|
pub enum LedBrightness {
|
||||||
@@ -70,6 +63,34 @@ impl FromStr for Colour {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&[f32; 3]> for Colour {
|
||||||
|
fn from(c: &[f32; 3]) -> Self {
|
||||||
|
Self(
|
||||||
|
(255.0 * c[0]) as u8,
|
||||||
|
(255.0 * c[1]) as u8,
|
||||||
|
(255.0 * c[2]) as u8,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Colour> for [f32; 3] {
|
||||||
|
fn from(c: Colour) -> Self {
|
||||||
|
[c.0 as f32 / 255.0, c.1 as f32 / 255.0, c.2 as f32 / 255.0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&[u8; 3]> for Colour {
|
||||||
|
fn from(c: &[u8; 3]) -> Self {
|
||||||
|
Self(c[0], c[1], c[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Colour> for [u8; 3] {
|
||||||
|
fn from(c: Colour) -> Self {
|
||||||
|
[c.0, c.1, c.2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub enum Speed {
|
pub enum Speed {
|
||||||
@@ -129,8 +150,11 @@ impl FromStr for Direction {
|
|||||||
|
|
||||||
/// Enum of modes that convert to the actual number required by a USB HID packet
|
/// Enum of modes that convert to the actual number required by a USB HID packet
|
||||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Deserialize, Serialize)]
|
#[derive(
|
||||||
|
Debug, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Deserialize, Serialize,
|
||||||
|
)]
|
||||||
pub enum AuraModeNum {
|
pub enum AuraModeNum {
|
||||||
|
#[default]
|
||||||
Static = 0,
|
Static = 0,
|
||||||
Breathe = 1,
|
Breathe = 1,
|
||||||
Strobe = 2,
|
Strobe = 2,
|
||||||
@@ -145,18 +169,38 @@ pub enum AuraModeNum {
|
|||||||
Flash = 12,
|
Flash = 12,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<AuraModeNum> for String {
|
||||||
|
fn from(mode: AuraModeNum) -> Self {
|
||||||
|
match mode {
|
||||||
|
AuraModeNum::Static => "Static",
|
||||||
|
AuraModeNum::Breathe => "Breathe",
|
||||||
|
AuraModeNum::Strobe => "Strobe",
|
||||||
|
AuraModeNum::Rainbow => "Rainbow",
|
||||||
|
AuraModeNum::Star => "Stars",
|
||||||
|
AuraModeNum::Rain => "Rain",
|
||||||
|
AuraModeNum::Highlight => "Highlight",
|
||||||
|
AuraModeNum::Laser => "Laser",
|
||||||
|
AuraModeNum::Ripple => "Ripple",
|
||||||
|
AuraModeNum::Pulse => "Pulse",
|
||||||
|
AuraModeNum::Comet => "Comet",
|
||||||
|
AuraModeNum::Flash => "Flash",
|
||||||
|
}
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&AuraModeNum> for &str {
|
impl From<&AuraModeNum> for &str {
|
||||||
fn from(mode: &AuraModeNum) -> Self {
|
fn from(mode: &AuraModeNum) -> Self {
|
||||||
match mode {
|
match mode {
|
||||||
AuraModeNum::Static => "Static",
|
AuraModeNum::Static => "Static",
|
||||||
AuraModeNum::Breathe => "Breathing",
|
AuraModeNum::Breathe => "Breathe",
|
||||||
AuraModeNum::Strobe => "Strobing",
|
AuraModeNum::Strobe => "Strobe",
|
||||||
AuraModeNum::Rainbow => "Rainbow",
|
AuraModeNum::Rainbow => "Rainbow",
|
||||||
AuraModeNum::Star => "Stars",
|
AuraModeNum::Star => "Stars",
|
||||||
AuraModeNum::Rain => "Rain",
|
AuraModeNum::Rain => "Rain",
|
||||||
AuraModeNum::Highlight => "Keypress Highlight",
|
AuraModeNum::Highlight => "Highlight",
|
||||||
AuraModeNum::Laser => "Keypress Laser",
|
AuraModeNum::Laser => "Laser",
|
||||||
AuraModeNum::Ripple => "Keypress Ripple",
|
AuraModeNum::Ripple => "Ripple",
|
||||||
AuraModeNum::Pulse => "Pulse",
|
AuraModeNum::Pulse => "Pulse",
|
||||||
AuraModeNum::Comet => "Comet",
|
AuraModeNum::Comet => "Comet",
|
||||||
AuraModeNum::Flash => "Flash",
|
AuraModeNum::Flash => "Flash",
|
||||||
@@ -167,14 +211,14 @@ impl From<&str> for AuraModeNum {
|
|||||||
fn from(mode: &str) -> Self {
|
fn from(mode: &str) -> Self {
|
||||||
match mode {
|
match mode {
|
||||||
"Static" => AuraModeNum::Static,
|
"Static" => AuraModeNum::Static,
|
||||||
"Breathing" => AuraModeNum::Breathe,
|
"Breathe" => AuraModeNum::Breathe,
|
||||||
"Strobing" => AuraModeNum::Strobe,
|
"Strobe" => AuraModeNum::Strobe,
|
||||||
"Rainbow" => AuraModeNum::Rainbow,
|
"Rainbow" => AuraModeNum::Rainbow,
|
||||||
"Stars" => AuraModeNum::Star,
|
"Stars" => AuraModeNum::Star,
|
||||||
"Rain" => AuraModeNum::Rain,
|
"Rain" => AuraModeNum::Rain,
|
||||||
"Keypress Highlight" => AuraModeNum::Highlight,
|
"Highlight" => AuraModeNum::Highlight,
|
||||||
"Keypress Laser" => AuraModeNum::Laser,
|
"Laser" => AuraModeNum::Laser,
|
||||||
"Keypress Ripple" => AuraModeNum::Ripple,
|
"Ripple" => AuraModeNum::Ripple,
|
||||||
"Pulse" => AuraModeNum::Pulse,
|
"Pulse" => AuraModeNum::Pulse,
|
||||||
"Comet" => AuraModeNum::Comet,
|
"Comet" => AuraModeNum::Comet,
|
||||||
"Flash" => AuraModeNum::Flash,
|
"Flash" => AuraModeNum::Flash,
|
||||||
@@ -207,16 +251,60 @@ impl From<u8> for AuraModeNum {
|
|||||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub enum AuraZone {
|
pub enum AuraZone {
|
||||||
|
/// Used if keyboard has no zones, or if setting all
|
||||||
None,
|
None,
|
||||||
One,
|
/// Leftmost zone
|
||||||
Two,
|
Key1,
|
||||||
Three,
|
/// Zone after leftmost
|
||||||
Four,
|
Key2,
|
||||||
|
/// Zone second from right
|
||||||
|
Key3,
|
||||||
|
/// Rightmost zone
|
||||||
|
Key4,
|
||||||
|
/// Logo on the lid (or elsewhere?)
|
||||||
|
Logo,
|
||||||
|
/// The left part of a lightbar (typically on the front of laptop)
|
||||||
|
BarLeft,
|
||||||
|
/// The right part of a lightbar
|
||||||
|
BarRight,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AuraZone {
|
||||||
|
fn default() -> Self {
|
||||||
|
AuraZone::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for AuraZone {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let s = s.to_lowercase();
|
||||||
|
match s.to_ascii_lowercase().as_str() {
|
||||||
|
"0" => Ok(AuraZone::None),
|
||||||
|
"none" => Ok(AuraZone::None),
|
||||||
|
"1" => Ok(AuraZone::Key1),
|
||||||
|
"one" => Ok(AuraZone::Key1),
|
||||||
|
"2" => Ok(AuraZone::Key2),
|
||||||
|
"two" => Ok(AuraZone::Key2),
|
||||||
|
"3" => Ok(AuraZone::Key3),
|
||||||
|
"three" => Ok(AuraZone::Key3),
|
||||||
|
"4" => Ok(AuraZone::Key4),
|
||||||
|
"four" => Ok(AuraZone::Key4),
|
||||||
|
"5" => Ok(AuraZone::Logo),
|
||||||
|
"logo" => Ok(AuraZone::Logo),
|
||||||
|
"6" => Ok(AuraZone::BarLeft),
|
||||||
|
"lightbar-left" => Ok(AuraZone::BarLeft),
|
||||||
|
"7" => Ok(AuraZone::BarRight),
|
||||||
|
"lightbar-right" => Ok(AuraZone::BarRight),
|
||||||
|
_ => Err(Error::ParseSpeed),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default factory modes structure. This easily converts to an USB HID packet with:
|
/// Default factory modes structure. This easily converts to an USB HID packet with:
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// let bytes: [u8; LED_MSG_LEN] = mode.into();
|
/// // let bytes: [u8; LED_MSG_LEN] = mode.into();
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
@@ -273,6 +361,53 @@ impl Default for AuraEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct AuraParameters {
|
||||||
|
pub zone: bool,
|
||||||
|
pub colour1: bool,
|
||||||
|
pub colour2: bool,
|
||||||
|
pub speed: bool,
|
||||||
|
pub direction: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuraParameters {
|
||||||
|
pub const fn new(
|
||||||
|
zone: bool,
|
||||||
|
colour1: bool,
|
||||||
|
colour2: bool,
|
||||||
|
speed: bool,
|
||||||
|
direction: bool,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
zone,
|
||||||
|
colour1,
|
||||||
|
colour2,
|
||||||
|
speed,
|
||||||
|
direction,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuraEffect {
|
||||||
|
/// A helper to provide detail on what effects have which parameters, e.g the static
|
||||||
|
/// factory mode accepts only one colour.
|
||||||
|
pub const fn allowed_parameters(mode: AuraModeNum) -> AuraParameters {
|
||||||
|
match mode {
|
||||||
|
AuraModeNum::Static => AuraParameters::new(true, true, false, false, false),
|
||||||
|
AuraModeNum::Breathe => AuraParameters::new(true, true, true, true, false),
|
||||||
|
AuraModeNum::Strobe => AuraParameters::new(true, false, false, true, false),
|
||||||
|
AuraModeNum::Rainbow => AuraParameters::new(true, false, false, true, true),
|
||||||
|
AuraModeNum::Star => AuraParameters::new(true, true, true, true, true),
|
||||||
|
AuraModeNum::Rain => AuraParameters::new(true, false, false, true, false),
|
||||||
|
AuraModeNum::Highlight => AuraParameters::new(true, true, false, false, false),
|
||||||
|
AuraModeNum::Laser => AuraParameters::new(true, true, false, true, false),
|
||||||
|
AuraModeNum::Ripple => AuraParameters::new(true, true, false, true, false),
|
||||||
|
AuraModeNum::Pulse => AuraParameters::new(true, true, false, false, false),
|
||||||
|
AuraModeNum::Comet => AuraParameters::new(true, true, false, false, false),
|
||||||
|
AuraModeNum::Flash => AuraParameters::new(true, true, false, false, false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses `AuraEffect` in to packet data for writing to the USB interface
|
/// Parses `AuraEffect` in to packet data for writing to the USB interface
|
||||||
///
|
///
|
||||||
/// Byte structure where colour is RGB, one byte per R, G, B:
|
/// Byte structure where colour is RGB, one byte per R, G, B:
|
||||||
@@ -299,3 +434,100 @@ impl From<&AuraEffect> for [u8; LED_MSG_LEN] {
|
|||||||
msg
|
msg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed, LED_MSG_LEN};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_led_static_packet() {
|
||||||
|
let st = AuraEffect {
|
||||||
|
mode: AuraModeNum::Static,
|
||||||
|
zone: AuraZone::None,
|
||||||
|
colour1: Colour(0xff, 0x11, 0xdd),
|
||||||
|
colour2: Colour::default(),
|
||||||
|
speed: Speed::Med,
|
||||||
|
direction: Direction::Right,
|
||||||
|
};
|
||||||
|
let ar = <[u8; LED_MSG_LEN]>::from(&st);
|
||||||
|
|
||||||
|
println!("{:02x?}", ar);
|
||||||
|
let check = [
|
||||||
|
0x5d, 0xb3, 0x0, 0x0, 0xff, 0x11, 0xdd, 0xeb, 0x0, 0x0, 0xa6, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0,
|
||||||
|
];
|
||||||
|
assert_eq!(ar, check);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_led_static_zone_packet() {
|
||||||
|
let mut st = AuraEffect {
|
||||||
|
mode: AuraModeNum::Static,
|
||||||
|
zone: AuraZone::Key1,
|
||||||
|
colour1: Colour(0xff, 0, 0),
|
||||||
|
colour2: Colour(0, 0, 0),
|
||||||
|
speed: Speed::Low,
|
||||||
|
direction: Direction::Left,
|
||||||
|
};
|
||||||
|
let capture = [
|
||||||
|
0x5d, 0xb3, 0x01, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0,
|
||||||
|
];
|
||||||
|
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
|
||||||
|
|
||||||
|
st.zone = AuraZone::Key2;
|
||||||
|
st.colour1 = Colour(0xff, 0xff, 0);
|
||||||
|
let capture = [
|
||||||
|
0x5d, 0xb3, 0x02, 0x00, 0xff, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0,
|
||||||
|
];
|
||||||
|
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
|
||||||
|
|
||||||
|
st.zone = AuraZone::Key3;
|
||||||
|
st.colour1 = Colour(0, 0xff, 0xff);
|
||||||
|
let capture = [
|
||||||
|
0x5d, 0xb3, 0x03, 0x00, 0x00, 0xff, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0,
|
||||||
|
];
|
||||||
|
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
|
||||||
|
|
||||||
|
st.zone = AuraZone::Key4;
|
||||||
|
st.colour1 = Colour(0xff, 0x00, 0xff);
|
||||||
|
let capture = [
|
||||||
|
0x5d, 0xb3, 0x04, 0x00, 0xff, 0x00, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0,
|
||||||
|
];
|
||||||
|
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
|
||||||
|
|
||||||
|
st.zone = AuraZone::Logo;
|
||||||
|
st.colour1 = Colour(0x2c, 0xff, 0x00);
|
||||||
|
let capture = [
|
||||||
|
0x5d, 0xb3, 0x05, 0x00, 0x2c, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0,
|
||||||
|
];
|
||||||
|
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
|
||||||
|
|
||||||
|
st.zone = AuraZone::BarLeft;
|
||||||
|
st.colour1 = Colour(0xff, 0x00, 0x00);
|
||||||
|
let capture = [
|
||||||
|
0x5d, 0xb3, 0x06, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0,
|
||||||
|
];
|
||||||
|
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
|
||||||
|
|
||||||
|
st.zone = AuraZone::BarRight;
|
||||||
|
st.colour1 = Colour(0xff, 0x00, 0xcd);
|
||||||
|
let capture = [
|
||||||
|
0x5d, 0xb3, 0x07, 0x00, 0xff, 0x00, 0xcd, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0,
|
||||||
|
];
|
||||||
|
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
|
||||||
|
|
||||||
|
st.mode = AuraModeNum::Rainbow;
|
||||||
|
let capture = [
|
||||||
|
0x5d, 0xb3, 0x07, 0x03, 0xff, 0x00, 0xcd, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0,
|
||||||
|
];
|
||||||
|
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ pub enum Error {
|
|||||||
ParseSpeed,
|
ParseSpeed,
|
||||||
ParseDirection,
|
ParseDirection,
|
||||||
ParseBrightness,
|
ParseBrightness,
|
||||||
ParseAnime,
|
Io(std::io::Error),
|
||||||
|
Toml(toml::de::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
@@ -18,9 +19,22 @@ impl fmt::Display for Error {
|
|||||||
Error::ParseSpeed => write!(f, "Could not parse speed"),
|
Error::ParseSpeed => write!(f, "Could not parse speed"),
|
||||||
Error::ParseDirection => write!(f, "Could not parse direction"),
|
Error::ParseDirection => write!(f, "Could not parse direction"),
|
||||||
Error::ParseBrightness => write!(f, "Could not parse brightness"),
|
Error::ParseBrightness => write!(f, "Could not parse brightness"),
|
||||||
Error::ParseAnime => write!(f, "Could not parse anime"),
|
Error::Io(io) => write!(f, "IO Error: {io}"),
|
||||||
|
Error::Toml(e) => write!(f, "TOML Parse Error: {e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for Error {}
|
impl error::Error for Error {}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for Error {
|
||||||
|
fn from(e: std::io::Error) -> Self {
|
||||||
|
Self::Io(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<toml::de::Error> for Error {
|
||||||
|
fn from(e: toml::de::Error) -> Self {
|
||||||
|
Self::Toml(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
use crate::keys::Key;
|
||||||
|
|
||||||
|
impl From<Key> for &str {
|
||||||
|
fn from(k: Key) -> Self {
|
||||||
|
(&k).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Key> for &str {
|
||||||
|
fn from(k: &Key) -> Self {
|
||||||
|
match k {
|
||||||
|
Key::VolUp => "Volume Up",
|
||||||
|
Key::VolDown => "Volume Down",
|
||||||
|
Key::MicMute => "Mute Mic",
|
||||||
|
Key::Rog => "ROG",
|
||||||
|
Key::Fan => "Fan Control",
|
||||||
|
Key::Esc => "Escape",
|
||||||
|
Key::F1 => "F1",
|
||||||
|
Key::F2 => "F2",
|
||||||
|
Key::F3 => "F3",
|
||||||
|
Key::F4 => "F4",
|
||||||
|
Key::F5 => "F5",
|
||||||
|
Key::F6 => "F6",
|
||||||
|
Key::F7 => "F7",
|
||||||
|
Key::F8 => "F8",
|
||||||
|
Key::F9 => "F9",
|
||||||
|
Key::F10 => "F10",
|
||||||
|
Key::F11 => "F11",
|
||||||
|
Key::F12 => "F12",
|
||||||
|
Key::Del => "Delete",
|
||||||
|
Key::Tilde => "Tilde",
|
||||||
|
Key::N1 => "1",
|
||||||
|
Key::N2 => "2",
|
||||||
|
Key::N3 => "3",
|
||||||
|
Key::N4 => "4",
|
||||||
|
Key::N5 => "5",
|
||||||
|
Key::N6 => "6",
|
||||||
|
Key::N7 => "7",
|
||||||
|
Key::N8 => "8",
|
||||||
|
Key::N9 => "9",
|
||||||
|
Key::N0 => "0",
|
||||||
|
Key::Hyphen => "-",
|
||||||
|
Key::Equals => "=",
|
||||||
|
Key::BkSpc => "Backspace",
|
||||||
|
Key::BkSpc3_1 => "Backspace LED 1",
|
||||||
|
Key::BkSpc3_2 => "Backspace LED 2",
|
||||||
|
Key::BkSpc3_3 => "Backspace LED 3",
|
||||||
|
Key::Home => "Home",
|
||||||
|
Key::Tab => "Tab",
|
||||||
|
Key::Q => "Q",
|
||||||
|
Key::W => "W",
|
||||||
|
Key::E => "E",
|
||||||
|
Key::R => "R",
|
||||||
|
Key::T => "T",
|
||||||
|
Key::Y => "Y",
|
||||||
|
Key::U => "U",
|
||||||
|
Key::I => "I",
|
||||||
|
Key::O => "O",
|
||||||
|
Key::P => "P",
|
||||||
|
Key::LBracket => "[",
|
||||||
|
Key::RBracket => "]",
|
||||||
|
Key::BackSlash => "\\",
|
||||||
|
Key::PgUp => "Page Up",
|
||||||
|
Key::Caps => "Caps Lock",
|
||||||
|
Key::A => "A",
|
||||||
|
Key::S => "S",
|
||||||
|
Key::D => "D",
|
||||||
|
Key::F => "F",
|
||||||
|
Key::G => "G",
|
||||||
|
Key::H => "H",
|
||||||
|
Key::J => "J",
|
||||||
|
Key::K => "K",
|
||||||
|
Key::L => "L",
|
||||||
|
Key::SemiColon => ";",
|
||||||
|
Key::Quote => "'",
|
||||||
|
Key::Return => "Return",
|
||||||
|
Key::Return3_1 => "Return LED 1",
|
||||||
|
Key::Return3_2 => "Return LED 2",
|
||||||
|
Key::Return3_3 => "Return LED 3",
|
||||||
|
Key::PgDn => "Page Down",
|
||||||
|
Key::LShift => "Left Shift",
|
||||||
|
Key::LShift3_1 => "Left Shift LED 1",
|
||||||
|
Key::LShift3_2 => "Left Shift LED 2",
|
||||||
|
Key::LShift3_3 => "Left Shift LED 3",
|
||||||
|
Key::Z => "Z",
|
||||||
|
Key::X => "X",
|
||||||
|
Key::C => "C",
|
||||||
|
Key::V => "V",
|
||||||
|
Key::B => "B",
|
||||||
|
Key::N => "N",
|
||||||
|
Key::M => "M",
|
||||||
|
Key::Comma => ",",
|
||||||
|
Key::Period => ".",
|
||||||
|
Key::Star => "*",
|
||||||
|
Key::NumPadDel => "Delete",
|
||||||
|
Key::NumPadPlus => "+",
|
||||||
|
Key::NumPadEnter => "Enter",
|
||||||
|
Key::NumPadPause => "Pause",
|
||||||
|
Key::NumPadPrtSc => "Print Screen",
|
||||||
|
Key::NumPadHome => "Home",
|
||||||
|
Key::NumLock => "Num-Lock",
|
||||||
|
Key::FwdSlash => "/",
|
||||||
|
Key::Rshift => "Right Shift",
|
||||||
|
Key::RshiftSmall => "Right Shift",
|
||||||
|
Key::Rshift3_1 => "Right Shift LED 1",
|
||||||
|
Key::Rshift3_2 => "Right Shift LED 2",
|
||||||
|
Key::Rshift3_3 => "Right Shift LED 3",
|
||||||
|
Key::End => "End",
|
||||||
|
Key::LCtrl => "Left Control",
|
||||||
|
Key::LCtrlMed => "Left Control",
|
||||||
|
Key::LFn => "Left Fn",
|
||||||
|
Key::Meta => "Meta",
|
||||||
|
Key::LAlt => "Left Alt",
|
||||||
|
Key::Space => "Space",
|
||||||
|
Key::Space5_1 => "Space LED 1",
|
||||||
|
Key::Space5_2 => "Space LED 2",
|
||||||
|
Key::Space5_3 => "Space LED 3",
|
||||||
|
Key::Space5_4 => "Space LED 4",
|
||||||
|
Key::Space5_5 => "Space LED 5",
|
||||||
|
Key::RAlt => "Right Alt",
|
||||||
|
Key::PrtSc => "Print Screen",
|
||||||
|
Key::RCtrl => "Right Control",
|
||||||
|
Key::RCtrlLarge => "Right Control",
|
||||||
|
Key::Pause => "Pause",
|
||||||
|
Key::Up => "Up",
|
||||||
|
Key::Down => "Down",
|
||||||
|
Key::Left => "Left",
|
||||||
|
Key::Right => "Right",
|
||||||
|
Key::UpRegular => "Up",
|
||||||
|
Key::DownRegular => "Down",
|
||||||
|
Key::LeftRegular => "Left",
|
||||||
|
Key::RightRegular => "Right",
|
||||||
|
Key::UpSplit => "Up",
|
||||||
|
Key::DownSplit => "Down",
|
||||||
|
Key::LeftSplit => "Left",
|
||||||
|
Key::RightSplit => "Right",
|
||||||
|
Key::RFn => "Right Fn",
|
||||||
|
Key::MediaPlay => "Media Play",
|
||||||
|
Key::MediaStop => "Media Stop",
|
||||||
|
Key::MediaNext => "Media Next",
|
||||||
|
Key::MediaPrev => "Media Previous",
|
||||||
|
Key::NormalBlank => "",
|
||||||
|
Key::NormalSpacer => "",
|
||||||
|
Key::FuncBlank => "",
|
||||||
|
Key::FuncSpacer => "",
|
||||||
|
Key::ArrowBlank => "",
|
||||||
|
Key::ArrowSpacer => "",
|
||||||
|
Key::ArrowRegularBlank => "",
|
||||||
|
Key::ArrowRegularSpacer => "",
|
||||||
|
Key::ArrowSplitBlank => "",
|
||||||
|
Key::ArrowSplitSpacer => "",
|
||||||
|
Key::RowEndSpacer => "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,355 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum Key {
|
||||||
|
VolUp,
|
||||||
|
VolDown,
|
||||||
|
MicMute,
|
||||||
|
Rog,
|
||||||
|
Fan,
|
||||||
|
Esc,
|
||||||
|
F1,
|
||||||
|
F2,
|
||||||
|
F3,
|
||||||
|
F4,
|
||||||
|
F5,
|
||||||
|
F6,
|
||||||
|
F7,
|
||||||
|
F8,
|
||||||
|
F9,
|
||||||
|
F10,
|
||||||
|
F11,
|
||||||
|
F12,
|
||||||
|
Del,
|
||||||
|
Tilde,
|
||||||
|
N1,
|
||||||
|
N2,
|
||||||
|
N3,
|
||||||
|
N4,
|
||||||
|
N5,
|
||||||
|
N6,
|
||||||
|
N7,
|
||||||
|
N8,
|
||||||
|
N9,
|
||||||
|
N0,
|
||||||
|
Hyphen,
|
||||||
|
Equals,
|
||||||
|
BkSpc,
|
||||||
|
BkSpc3_1,
|
||||||
|
BkSpc3_2,
|
||||||
|
BkSpc3_3,
|
||||||
|
Home,
|
||||||
|
Tab,
|
||||||
|
Q,
|
||||||
|
W,
|
||||||
|
E,
|
||||||
|
R,
|
||||||
|
T,
|
||||||
|
Y,
|
||||||
|
U,
|
||||||
|
I,
|
||||||
|
O,
|
||||||
|
P,
|
||||||
|
LBracket,
|
||||||
|
RBracket,
|
||||||
|
BackSlash,
|
||||||
|
PgUp,
|
||||||
|
Caps,
|
||||||
|
A,
|
||||||
|
S,
|
||||||
|
D,
|
||||||
|
F,
|
||||||
|
G,
|
||||||
|
H,
|
||||||
|
J,
|
||||||
|
K,
|
||||||
|
L,
|
||||||
|
SemiColon,
|
||||||
|
Quote,
|
||||||
|
Return,
|
||||||
|
Return3_1,
|
||||||
|
Return3_2,
|
||||||
|
Return3_3,
|
||||||
|
PgDn,
|
||||||
|
LShift,
|
||||||
|
LShift3_1,
|
||||||
|
LShift3_2,
|
||||||
|
LShift3_3,
|
||||||
|
Z,
|
||||||
|
X,
|
||||||
|
C,
|
||||||
|
V,
|
||||||
|
B,
|
||||||
|
N,
|
||||||
|
M,
|
||||||
|
Comma,
|
||||||
|
Period,
|
||||||
|
FwdSlash,
|
||||||
|
Star,
|
||||||
|
NumPadDel,
|
||||||
|
NumPadPlus,
|
||||||
|
NumPadEnter,
|
||||||
|
NumPadPause,
|
||||||
|
NumPadPrtSc,
|
||||||
|
NumPadHome,
|
||||||
|
NumLock,
|
||||||
|
Rshift,
|
||||||
|
RshiftSmall,
|
||||||
|
Rshift3_1,
|
||||||
|
Rshift3_2,
|
||||||
|
Rshift3_3,
|
||||||
|
End,
|
||||||
|
LCtrl,
|
||||||
|
LCtrlMed,
|
||||||
|
LFn,
|
||||||
|
Meta,
|
||||||
|
LAlt,
|
||||||
|
Space,
|
||||||
|
Space5_1,
|
||||||
|
Space5_2,
|
||||||
|
Space5_3,
|
||||||
|
Space5_4,
|
||||||
|
Space5_5,
|
||||||
|
Pause,
|
||||||
|
RAlt,
|
||||||
|
PrtSc,
|
||||||
|
RCtrl,
|
||||||
|
RCtrlLarge,
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
UpRegular,
|
||||||
|
DownRegular,
|
||||||
|
LeftRegular,
|
||||||
|
RightRegular,
|
||||||
|
UpSplit,
|
||||||
|
DownSplit,
|
||||||
|
LeftSplit,
|
||||||
|
RightSplit,
|
||||||
|
RFn,
|
||||||
|
MediaPlay,
|
||||||
|
MediaStop,
|
||||||
|
MediaNext,
|
||||||
|
MediaPrev,
|
||||||
|
NormalBlank,
|
||||||
|
/// To be ignored by per-key effects
|
||||||
|
NormalSpacer,
|
||||||
|
FuncBlank,
|
||||||
|
/// To be ignored by per-key effects
|
||||||
|
FuncSpacer,
|
||||||
|
ArrowBlank,
|
||||||
|
/// To be ignored by per-key effects
|
||||||
|
ArrowSpacer,
|
||||||
|
ArrowRegularBlank,
|
||||||
|
/// To be ignored by per-key effects
|
||||||
|
ArrowRegularSpacer,
|
||||||
|
ArrowSplitBlank,
|
||||||
|
/// To be ignored by per-key effects
|
||||||
|
ArrowSplitSpacer,
|
||||||
|
/// A gap between regular rows and the rightside buttons
|
||||||
|
RowEndSpacer,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Types of shapes of LED on keyboards. The shape is used for visual representations
|
||||||
|
///
|
||||||
|
/// A post fix of Spacer *must be ignored by per-key effects
|
||||||
|
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize)]
|
||||||
|
pub enum KeyShape {
|
||||||
|
Tilde,
|
||||||
|
#[default]
|
||||||
|
Normal,
|
||||||
|
NormalBlank,
|
||||||
|
NormalSpacer,
|
||||||
|
Func,
|
||||||
|
FuncBlank,
|
||||||
|
FuncSpacer,
|
||||||
|
Space,
|
||||||
|
Space5,
|
||||||
|
LCtrlMed,
|
||||||
|
LShift,
|
||||||
|
/// Used in a group of 3 (LED's)
|
||||||
|
LShift3,
|
||||||
|
RShift,
|
||||||
|
RshiftSmall,
|
||||||
|
/// Used in a group of 3 (LED's)
|
||||||
|
RShift3,
|
||||||
|
Return,
|
||||||
|
Return3,
|
||||||
|
Tab,
|
||||||
|
Caps,
|
||||||
|
Backspace,
|
||||||
|
/// Used in a group of 3 (LED's)
|
||||||
|
Backspace3,
|
||||||
|
Arrow,
|
||||||
|
ArrowBlank,
|
||||||
|
ArrowSpacer,
|
||||||
|
ArrowSplit,
|
||||||
|
ArrowSplitBlank,
|
||||||
|
ArrowSplitSpacer,
|
||||||
|
ArrowRegularBlank,
|
||||||
|
ArrowRegularSpacer,
|
||||||
|
RowEndSpacer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyShape {
|
||||||
|
pub const fn width(&self) -> f32 {
|
||||||
|
match self {
|
||||||
|
Self::Tilde => 0.8,
|
||||||
|
Self::Normal => 1.0,
|
||||||
|
Self::NormalBlank => 1.0,
|
||||||
|
Self::NormalSpacer => 1.0,
|
||||||
|
Self::Func => 1.0,
|
||||||
|
Self::FuncBlank => 1.0,
|
||||||
|
Self::FuncSpacer => 0.6,
|
||||||
|
Self::Space => 5.0,
|
||||||
|
Self::Space5 => 1.0,
|
||||||
|
Self::LCtrlMed => 1.1,
|
||||||
|
Self::LShift => 2.0,
|
||||||
|
Self::LShift3 => 0.67,
|
||||||
|
Self::RShift => 2.8,
|
||||||
|
Self::RshiftSmall => 1.8,
|
||||||
|
Self::RShift3 => 0.93,
|
||||||
|
Self::Return => 2.2,
|
||||||
|
Self::Return3 => 0.7333,
|
||||||
|
Self::Tab => 1.4,
|
||||||
|
Self::Caps => 1.6,
|
||||||
|
Self::Backspace => 2.0,
|
||||||
|
Self::Backspace3 => 0.666,
|
||||||
|
Self::ArrowRegularBlank | Self::ArrowRegularSpacer => 0.7,
|
||||||
|
Self::Arrow => 0.8,
|
||||||
|
Self::ArrowBlank | Self::ArrowSpacer => 1.0,
|
||||||
|
Self::ArrowSplit | Self::ArrowSplitBlank | Self::ArrowSplitSpacer => 1.0,
|
||||||
|
Self::RowEndSpacer => 0.1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A blank is used to space keys out in GUI's and can be used or ignored
|
||||||
|
/// depednign on the per-key effect
|
||||||
|
pub const fn is_blank(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::NormalBlank
|
||||||
|
| Self::FuncBlank
|
||||||
|
| Self::ArrowBlank
|
||||||
|
| Self::ArrowSplitBlank
|
||||||
|
| Self::ArrowRegularBlank => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A spacer is used to space keys out in GUI's, but ignored in per-key effects
|
||||||
|
pub const fn is_spacer(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::FuncSpacer
|
||||||
|
| Self::NormalSpacer
|
||||||
|
| Self::ArrowSpacer
|
||||||
|
| Self::ArrowSplitSpacer
|
||||||
|
| Self::ArrowRegularSpacer => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// All keys with a postfix of some number
|
||||||
|
pub const fn is_group(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::LShift3 | Self::RShift3 => true,
|
||||||
|
Self::Return3 | Self::Space5 | Self::Backspace3 => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mostly intended as a helper for signalling when to draw a
|
||||||
|
/// split/compact arrow cluster
|
||||||
|
pub const fn is_arrow_cluster(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Arrow | Self::ArrowBlank | Self::ArrowSpacer => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn is_arrow_splits(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::ArrowSplit | Self::ArrowSplitBlank | Self::ArrowSplitSpacer => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Key> for KeyShape {
|
||||||
|
fn from(k: Key) -> Self {
|
||||||
|
match k {
|
||||||
|
Key::VolUp
|
||||||
|
| Key::VolDown
|
||||||
|
| Key::MicMute
|
||||||
|
| Key::Rog
|
||||||
|
| Key::Fan
|
||||||
|
| Key::Esc
|
||||||
|
| Key::F1
|
||||||
|
| Key::F2
|
||||||
|
| Key::F3
|
||||||
|
| Key::F4
|
||||||
|
| Key::F5
|
||||||
|
| Key::F6
|
||||||
|
| Key::F7
|
||||||
|
| Key::F8
|
||||||
|
| Key::F9
|
||||||
|
| Key::F10
|
||||||
|
| Key::F11
|
||||||
|
| Key::F12
|
||||||
|
| Key::Del => KeyShape::Func,
|
||||||
|
Key::Tilde => KeyShape::Tilde,
|
||||||
|
|
||||||
|
Key::BkSpc => KeyShape::Backspace,
|
||||||
|
Key::BkSpc3_1 | Key::BkSpc3_2 | Key::BkSpc3_3 => KeyShape::Backspace3,
|
||||||
|
Key::Tab | Key::BackSlash => KeyShape::Tab,
|
||||||
|
Key::Caps => KeyShape::Caps,
|
||||||
|
|
||||||
|
Key::Return => KeyShape::Return,
|
||||||
|
Key::Return3_1 | Key::Return3_2 | Key::Return3_3 => KeyShape::Return3,
|
||||||
|
Key::LCtrlMed => KeyShape::LCtrlMed,
|
||||||
|
Key::LShift => KeyShape::LShift,
|
||||||
|
|
||||||
|
Key::Rshift | Key::RCtrlLarge => KeyShape::RShift,
|
||||||
|
Key::RshiftSmall => KeyShape::RshiftSmall,
|
||||||
|
Key::Rshift3_1 | Key::Rshift3_2 | Key::Rshift3_3 => KeyShape::RShift3,
|
||||||
|
|
||||||
|
Key::Space => KeyShape::Space,
|
||||||
|
Key::Space5_1 | Key::Space5_2 | Key::Space5_3 | Key::Space5_4 | Key::Space5_5 => {
|
||||||
|
KeyShape::Space5
|
||||||
|
}
|
||||||
|
|
||||||
|
Key::NumPadPause | Key::NumPadPrtSc | Key::NumPadHome | Key::NumPadDel => {
|
||||||
|
KeyShape::Func
|
||||||
|
}
|
||||||
|
|
||||||
|
Key::NormalBlank => KeyShape::NormalBlank,
|
||||||
|
Key::NormalSpacer => KeyShape::NormalSpacer,
|
||||||
|
|
||||||
|
Key::FuncBlank => KeyShape::FuncBlank,
|
||||||
|
Key::FuncSpacer => KeyShape::FuncSpacer,
|
||||||
|
|
||||||
|
Key::Up | Key::Down | Key::Left | Key::Right => KeyShape::Arrow,
|
||||||
|
Key::ArrowBlank => KeyShape::ArrowBlank,
|
||||||
|
Key::ArrowSpacer => KeyShape::ArrowSpacer,
|
||||||
|
|
||||||
|
Key::ArrowRegularBlank => KeyShape::ArrowRegularBlank,
|
||||||
|
Key::ArrowRegularSpacer => KeyShape::ArrowRegularSpacer,
|
||||||
|
|
||||||
|
Key::UpSplit | Key::LeftSplit | Key::DownSplit | Key::RightSplit => {
|
||||||
|
KeyShape::ArrowSplit
|
||||||
|
}
|
||||||
|
Key::ArrowSplitBlank => KeyShape::ArrowSplitBlank,
|
||||||
|
Key::ArrowSplitSpacer => KeyShape::ArrowSplitSpacer,
|
||||||
|
|
||||||
|
Key::RowEndSpacer => KeyShape::RowEndSpacer,
|
||||||
|
|
||||||
|
_ => KeyShape::Normal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Key> for KeyShape {
|
||||||
|
fn from(k: &Key) -> Self {
|
||||||
|
(*k).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
use super::{KeyLayout, KeyRow};
|
||||||
|
use crate::keys::Key;
|
||||||
|
|
||||||
|
impl KeyLayout {
|
||||||
|
/// Similar to GX502, but not per-key enabled
|
||||||
|
pub fn g513_layout() -> Self {
|
||||||
|
Self {
|
||||||
|
matches: vec!["G513".into()],
|
||||||
|
locale: "US".to_string(),
|
||||||
|
rows: vec![
|
||||||
|
KeyRow::new(
|
||||||
|
0.8,
|
||||||
|
vec![
|
||||||
|
Key::NormalSpacer,
|
||||||
|
Key::FuncSpacer,
|
||||||
|
Key::VolDown,
|
||||||
|
Key::VolUp,
|
||||||
|
Key::MicMute,
|
||||||
|
Key::Fan,
|
||||||
|
Key::Rog,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
0.8,
|
||||||
|
vec![
|
||||||
|
Key::Esc,
|
||||||
|
Key::FuncSpacer,
|
||||||
|
Key::F1,
|
||||||
|
Key::F2,
|
||||||
|
Key::F3,
|
||||||
|
Key::F4,
|
||||||
|
Key::FuncSpacer, // not sure which key to put here
|
||||||
|
Key::F5,
|
||||||
|
Key::F6,
|
||||||
|
Key::F7,
|
||||||
|
Key::F8,
|
||||||
|
Key::FuncSpacer,
|
||||||
|
Key::F9,
|
||||||
|
Key::F10,
|
||||||
|
Key::F11,
|
||||||
|
Key::F12,
|
||||||
|
Key::RowEndSpacer,
|
||||||
|
Key::Del,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
1.0,
|
||||||
|
vec![
|
||||||
|
Key::Tilde,
|
||||||
|
Key::N1,
|
||||||
|
Key::N2,
|
||||||
|
Key::N3,
|
||||||
|
Key::N4,
|
||||||
|
Key::N5,
|
||||||
|
Key::N6,
|
||||||
|
Key::N7,
|
||||||
|
Key::N8,
|
||||||
|
Key::N9,
|
||||||
|
Key::N0,
|
||||||
|
Key::Hyphen,
|
||||||
|
Key::Equals,
|
||||||
|
Key::BkSpc,
|
||||||
|
Key::RowEndSpacer,
|
||||||
|
Key::Home,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
1.0,
|
||||||
|
vec![
|
||||||
|
Key::Tab,
|
||||||
|
Key::Q,
|
||||||
|
Key::W,
|
||||||
|
Key::E,
|
||||||
|
Key::R,
|
||||||
|
Key::T,
|
||||||
|
Key::Y,
|
||||||
|
Key::U,
|
||||||
|
Key::I,
|
||||||
|
Key::O,
|
||||||
|
Key::P,
|
||||||
|
Key::LBracket,
|
||||||
|
Key::RBracket,
|
||||||
|
Key::BackSlash,
|
||||||
|
Key::RowEndSpacer,
|
||||||
|
Key::PgUp,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
1.0,
|
||||||
|
vec![
|
||||||
|
Key::Caps,
|
||||||
|
Key::A,
|
||||||
|
Key::S,
|
||||||
|
Key::D,
|
||||||
|
Key::F,
|
||||||
|
Key::G,
|
||||||
|
Key::H,
|
||||||
|
Key::J,
|
||||||
|
Key::K,
|
||||||
|
Key::L,
|
||||||
|
Key::SemiColon,
|
||||||
|
Key::Quote,
|
||||||
|
Key::Return,
|
||||||
|
Key::RowEndSpacer,
|
||||||
|
Key::PgDn,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
1.0,
|
||||||
|
vec![
|
||||||
|
Key::LShift,
|
||||||
|
Key::Z,
|
||||||
|
Key::X,
|
||||||
|
Key::C,
|
||||||
|
Key::V,
|
||||||
|
Key::B,
|
||||||
|
Key::N,
|
||||||
|
Key::M,
|
||||||
|
Key::Comma,
|
||||||
|
Key::Period,
|
||||||
|
Key::FwdSlash,
|
||||||
|
Key::Rshift,
|
||||||
|
Key::RowEndSpacer,
|
||||||
|
Key::End,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
1.0,
|
||||||
|
vec![
|
||||||
|
Key::LCtrl,
|
||||||
|
Key::LFn,
|
||||||
|
Key::Meta,
|
||||||
|
Key::LAlt,
|
||||||
|
Key::Space,
|
||||||
|
Key::RAlt,
|
||||||
|
Key::PrtSc,
|
||||||
|
Key::RCtrl,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::Up,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::RowEndSpacer,
|
||||||
|
Key::RFn,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
1.0,
|
||||||
|
vec![
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::Left,
|
||||||
|
Key::Down,
|
||||||
|
Key::Right,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
use super::{KeyLayout, KeyRow};
|
||||||
|
use crate::keys::Key;
|
||||||
|
|
||||||
|
impl KeyLayout {
|
||||||
|
pub fn ga401_layout() -> Self {
|
||||||
|
Self {
|
||||||
|
matches: vec!["GA401".into(), "GA402".into()],
|
||||||
|
locale: "US".to_string(),
|
||||||
|
rows: vec![
|
||||||
|
KeyRow::new(
|
||||||
|
0.8,
|
||||||
|
vec![
|
||||||
|
Key::NormalSpacer,
|
||||||
|
Key::FuncSpacer,
|
||||||
|
Key::VolDown,
|
||||||
|
Key::VolUp,
|
||||||
|
Key::MicMute,
|
||||||
|
Key::Rog,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
0.8,
|
||||||
|
vec![
|
||||||
|
Key::Esc,
|
||||||
|
Key::FuncSpacer,
|
||||||
|
Key::F1,
|
||||||
|
Key::F2,
|
||||||
|
Key::F3,
|
||||||
|
Key::F4,
|
||||||
|
Key::FuncSpacer, // not sure which key to put here
|
||||||
|
Key::F5,
|
||||||
|
Key::F6,
|
||||||
|
Key::F7,
|
||||||
|
Key::F8,
|
||||||
|
Key::FuncSpacer,
|
||||||
|
Key::F9,
|
||||||
|
Key::F10,
|
||||||
|
Key::F11,
|
||||||
|
Key::F12,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
1.0,
|
||||||
|
vec![
|
||||||
|
Key::Tilde,
|
||||||
|
Key::N1,
|
||||||
|
Key::N2,
|
||||||
|
Key::N3,
|
||||||
|
Key::N4,
|
||||||
|
Key::N5,
|
||||||
|
Key::N6,
|
||||||
|
Key::N7,
|
||||||
|
Key::N8,
|
||||||
|
Key::N9,
|
||||||
|
Key::N0,
|
||||||
|
Key::Hyphen,
|
||||||
|
Key::Equals,
|
||||||
|
Key::BkSpc,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
1.0,
|
||||||
|
vec![
|
||||||
|
Key::Tab,
|
||||||
|
Key::Q,
|
||||||
|
Key::W,
|
||||||
|
Key::E,
|
||||||
|
Key::R,
|
||||||
|
Key::T,
|
||||||
|
Key::Y,
|
||||||
|
Key::U,
|
||||||
|
Key::I,
|
||||||
|
Key::O,
|
||||||
|
Key::P,
|
||||||
|
Key::LBracket,
|
||||||
|
Key::RBracket,
|
||||||
|
Key::BackSlash,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
1.0,
|
||||||
|
vec![
|
||||||
|
Key::Caps,
|
||||||
|
Key::A,
|
||||||
|
Key::S,
|
||||||
|
Key::D,
|
||||||
|
Key::F,
|
||||||
|
Key::G,
|
||||||
|
Key::H,
|
||||||
|
Key::J,
|
||||||
|
Key::K,
|
||||||
|
Key::L,
|
||||||
|
Key::SemiColon,
|
||||||
|
Key::Quote,
|
||||||
|
Key::Return,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
1.0,
|
||||||
|
vec![
|
||||||
|
Key::LShift,
|
||||||
|
Key::Z,
|
||||||
|
Key::X,
|
||||||
|
Key::C,
|
||||||
|
Key::V,
|
||||||
|
Key::B,
|
||||||
|
Key::N,
|
||||||
|
Key::M,
|
||||||
|
Key::Comma,
|
||||||
|
Key::Period,
|
||||||
|
Key::FwdSlash,
|
||||||
|
Key::Rshift,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
1.0,
|
||||||
|
vec![
|
||||||
|
Key::LCtrl,
|
||||||
|
Key::LFn,
|
||||||
|
Key::Meta,
|
||||||
|
Key::LAlt,
|
||||||
|
Key::Space,
|
||||||
|
Key::RAlt,
|
||||||
|
Key::PrtSc,
|
||||||
|
Key::RCtrl,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::Up,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
1.2,
|
||||||
|
vec![
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::Left,
|
||||||
|
Key::Down,
|
||||||
|
Key::Right,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
use super::{KeyLayout, KeyRow};
|
||||||
|
use crate::keys::Key;
|
||||||
|
|
||||||
|
impl KeyLayout {
|
||||||
|
pub fn gx502_layout() -> Self {
|
||||||
|
Self {
|
||||||
|
matches: vec!["GX502".into(), "GU502".into()],
|
||||||
|
locale: "US".to_string(),
|
||||||
|
rows: vec![
|
||||||
|
KeyRow::new(
|
||||||
|
0.8,
|
||||||
|
vec![
|
||||||
|
Key::NormalSpacer,
|
||||||
|
Key::FuncSpacer,
|
||||||
|
Key::VolDown,
|
||||||
|
Key::VolUp,
|
||||||
|
Key::MicMute,
|
||||||
|
Key::Rog,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
0.8,
|
||||||
|
vec![
|
||||||
|
Key::Esc,
|
||||||
|
Key::FuncSpacer,
|
||||||
|
Key::F1,
|
||||||
|
Key::F2,
|
||||||
|
Key::F3,
|
||||||
|
Key::F4,
|
||||||
|
Key::FuncSpacer, // not sure which key to put here
|
||||||
|
Key::F5,
|
||||||
|
Key::F6,
|
||||||
|
Key::F7,
|
||||||
|
Key::F8,
|
||||||
|
Key::FuncSpacer,
|
||||||
|
Key::F9,
|
||||||
|
Key::F10,
|
||||||
|
Key::F11,
|
||||||
|
Key::F12,
|
||||||
|
Key::RowEndSpacer,
|
||||||
|
Key::Del,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
1.0,
|
||||||
|
vec![
|
||||||
|
Key::Tilde,
|
||||||
|
Key::N1,
|
||||||
|
Key::N2,
|
||||||
|
Key::N3,
|
||||||
|
Key::N4,
|
||||||
|
Key::N5,
|
||||||
|
Key::N6,
|
||||||
|
Key::N7,
|
||||||
|
Key::N8,
|
||||||
|
Key::N9,
|
||||||
|
Key::N0,
|
||||||
|
Key::Hyphen,
|
||||||
|
Key::Equals,
|
||||||
|
Key::BkSpc3_1,
|
||||||
|
Key::BkSpc3_2,
|
||||||
|
Key::BkSpc3_3,
|
||||||
|
Key::RowEndSpacer,
|
||||||
|
Key::Home,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
1.0,
|
||||||
|
vec![
|
||||||
|
Key::Tab,
|
||||||
|
Key::Q,
|
||||||
|
Key::W,
|
||||||
|
Key::E,
|
||||||
|
Key::R,
|
||||||
|
Key::T,
|
||||||
|
Key::Y,
|
||||||
|
Key::U,
|
||||||
|
Key::I,
|
||||||
|
Key::O,
|
||||||
|
Key::P,
|
||||||
|
Key::LBracket,
|
||||||
|
Key::RBracket,
|
||||||
|
Key::BackSlash,
|
||||||
|
Key::RowEndSpacer,
|
||||||
|
Key::PgUp,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
1.0,
|
||||||
|
vec![
|
||||||
|
Key::Caps,
|
||||||
|
Key::A,
|
||||||
|
Key::S,
|
||||||
|
Key::D,
|
||||||
|
Key::F,
|
||||||
|
Key::G,
|
||||||
|
Key::H,
|
||||||
|
Key::J,
|
||||||
|
Key::K,
|
||||||
|
Key::L,
|
||||||
|
Key::SemiColon,
|
||||||
|
Key::Quote,
|
||||||
|
Key::Return3_1,
|
||||||
|
Key::Return3_2,
|
||||||
|
Key::Return3_3,
|
||||||
|
Key::RowEndSpacer,
|
||||||
|
Key::PgDn,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
1.0,
|
||||||
|
vec![
|
||||||
|
Key::LShift,
|
||||||
|
Key::Z,
|
||||||
|
Key::X,
|
||||||
|
Key::C,
|
||||||
|
Key::V,
|
||||||
|
Key::B,
|
||||||
|
Key::N,
|
||||||
|
Key::M,
|
||||||
|
Key::Comma,
|
||||||
|
Key::Period,
|
||||||
|
Key::FwdSlash,
|
||||||
|
Key::Rshift3_1,
|
||||||
|
Key::Rshift3_2,
|
||||||
|
Key::Rshift3_3,
|
||||||
|
Key::RowEndSpacer,
|
||||||
|
Key::End,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
1.0,
|
||||||
|
vec![
|
||||||
|
Key::LCtrl,
|
||||||
|
Key::LFn,
|
||||||
|
Key::Meta,
|
||||||
|
Key::LAlt,
|
||||||
|
Key::Space5_1,
|
||||||
|
Key::Space5_2,
|
||||||
|
Key::Space5_3,
|
||||||
|
Key::Space5_4,
|
||||||
|
Key::Space5_5,
|
||||||
|
Key::RAlt,
|
||||||
|
Key::PrtSc,
|
||||||
|
Key::RCtrl,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::Up,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::RowEndSpacer,
|
||||||
|
Key::RFn,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
KeyRow::new(
|
||||||
|
1.0,
|
||||||
|
vec![
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
Key::Left,
|
||||||
|
Key::Down,
|
||||||
|
Key::Right,
|
||||||
|
Key::ArrowSpacer,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/// Hardcoded layout. Was used to generate a toml default
|
||||||
|
pub mod g513;
|
||||||
|
/// Hardcoded layout. Was used to generate a toml default
|
||||||
|
pub mod ga401;
|
||||||
|
/// Hardcoded layout. Was used to generate a toml default
|
||||||
|
pub mod gx502;
|
||||||
|
|
||||||
|
use crate::{error::Error, keys::Key};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{fs::OpenOptions, io::Read, path::Path, slice::Iter};
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct KeyLayout {
|
||||||
|
/// A series of board names that this layout can be used for. The board names
|
||||||
|
/// stored with the layout can be globbed, e.g, GA401 will match all of the
|
||||||
|
/// GA401I and GA401Q range variants.
|
||||||
|
///
|
||||||
|
/// `/sys/class/dmi/id/board_name`
|
||||||
|
matches: Vec<String>,
|
||||||
|
locale: String,
|
||||||
|
rows: Vec<KeyRow>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyLayout {
|
||||||
|
pub fn from_file(path: &Path) -> Result<Self, Error> {
|
||||||
|
let mut file = OpenOptions::new().read(true).open(path)?;
|
||||||
|
let mut buf = String::new();
|
||||||
|
let read_len = file.read_to_string(&mut buf)?;
|
||||||
|
if read_len == 0 {
|
||||||
|
return Err(Error::Io(std::io::ErrorKind::InvalidData.into()));
|
||||||
|
} else {
|
||||||
|
return Ok(toml::from_str::<Self>(&buf)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn matches(&self, board_name: &str) -> bool {
|
||||||
|
let board = board_name.to_ascii_uppercase();
|
||||||
|
for tmp in self.matches.iter() {
|
||||||
|
if board.contains(tmp.as_str()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rows(&self) -> Iter<KeyRow> {
|
||||||
|
self.rows.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct KeyRow {
|
||||||
|
height: f32,
|
||||||
|
row: Vec<Key>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyRow {
|
||||||
|
pub fn new(height: f32, row: Vec<Key>) -> Self {
|
||||||
|
Self { height, row }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn row(&self) -> Iter<Key> {
|
||||||
|
self.row.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(&self) -> f32 {
|
||||||
|
self.height
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,11 +8,21 @@ pub use builtin_modes::*;
|
|||||||
|
|
||||||
mod per_key_rgb;
|
mod per_key_rgb;
|
||||||
pub use per_key_rgb::*;
|
pub use per_key_rgb::*;
|
||||||
|
pub mod error;
|
||||||
|
pub mod key_to_str;
|
||||||
|
pub mod keys;
|
||||||
|
pub use key_to_str::*;
|
||||||
|
pub mod layouts;
|
||||||
pub mod usb;
|
pub mod usb;
|
||||||
|
|
||||||
pub mod error;
|
|
||||||
|
|
||||||
pub const LED_MSG_LEN: usize = 17;
|
pub const LED_MSG_LEN: usize = 17;
|
||||||
|
|
||||||
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
|
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
pub const RED: Colour = Colour(0xff, 0x00, 0x00);
|
||||||
|
pub const GREEN: Colour = Colour(0x00, 0xff, 0x00);
|
||||||
|
pub const BLUE: Colour = Colour(0x00, 0x00, 0xff);
|
||||||
|
pub const VIOLET: Colour = Colour(0x9B, 0x26, 0xB6);
|
||||||
|
pub const TEAL: Colour = Colour(0x00, 0x7C, 0x80);
|
||||||
|
pub const YELLOW: Colour = Colour(0xff, 0xef, 0x00);
|
||||||
|
pub const ORANGE: Colour = Colour(0xff, 0xa4, 0x00);
|
||||||
|
pub const GRADIENT: [Colour; 7] = [RED, VIOLET, BLUE, TEAL, GREEN, YELLOW, ORANGE];
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use crate::keys::Key;
|
||||||
|
|
||||||
/// A `KeyColourArray` contains all data to change the full set of keyboard
|
/// A `KeyColourArray` contains all data to change the full set of keyboard
|
||||||
/// key colours individually.
|
/// key colours individually.
|
||||||
///
|
///
|
||||||
@@ -45,16 +47,16 @@ impl KeyColourArray {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set(&mut self, key: Key, r: u8, g: u8, b: u8) {
|
pub fn set(&mut self, key: Key, r: u8, g: u8, b: u8) {
|
||||||
if let Some((rr, gg, bb)) = self.key(key) {
|
if let Some(c) = self.rgb(key) {
|
||||||
*rr = r;
|
c[0] = r;
|
||||||
*gg = g;
|
c[1] = g;
|
||||||
*bb = b;
|
c[2] = b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indexes in to `KeyColourArray` at the correct row and column
|
/// Indexes in to `KeyColourArray` at the correct row and column
|
||||||
/// to set a series of three bytes to the chosen R,G,B values
|
/// to set a series of three bytes to the chosen R,G,B values
|
||||||
pub fn key(&mut self, key: Key) -> Option<(&mut u8, &mut u8, &mut u8)> {
|
pub fn rgb(&mut self, key: Key) -> Option<&mut [u8]> {
|
||||||
// Tuples are indexes in to array
|
// Tuples are indexes in to array
|
||||||
let (row, col) = match key {
|
let (row, col) = match key {
|
||||||
Key::VolDown => (0, 15),
|
Key::VolDown => (0, 15),
|
||||||
@@ -91,9 +93,9 @@ impl KeyColourArray {
|
|||||||
Key::N0 => (3, 21),
|
Key::N0 => (3, 21),
|
||||||
Key::Hyphen => (3, 24),
|
Key::Hyphen => (3, 24),
|
||||||
Key::Equals => (3, 27),
|
Key::Equals => (3, 27),
|
||||||
Key::BkSpc1 => (3, 30),
|
Key::BkSpc3_1 => (3, 30),
|
||||||
Key::BkSpc2 => (3, 33),
|
Key::BkSpc3_2 => (3, 33),
|
||||||
Key::BkSpc3 => (3, 36),
|
Key::BkSpc3_3 => (3, 36),
|
||||||
Key::Home => (3, 39),
|
Key::Home => (3, 39),
|
||||||
Key::Tab => (3, 54),
|
Key::Tab => (3, 54),
|
||||||
//
|
//
|
||||||
@@ -125,11 +127,16 @@ impl KeyColourArray {
|
|||||||
Key::SemiColon => (5, 51),
|
Key::SemiColon => (5, 51),
|
||||||
Key::Quote => (5, 54),
|
Key::Quote => (5, 54),
|
||||||
//
|
//
|
||||||
Key::Ret1 => (6, 12),
|
Key::Return => (6, 9),
|
||||||
Key::Ret2 => (6, 15),
|
Key::Return3_1 => (6, 12),
|
||||||
Key::Ret3 => (6, 18),
|
Key::Return3_2 => (6, 15),
|
||||||
|
Key::Return3_3 => (6, 18),
|
||||||
Key::PgDn => (6, 21),
|
Key::PgDn => (6, 21),
|
||||||
Key::LShift => (6, 36),
|
Key::LShift => (6, 36),
|
||||||
|
// TODO: Find correct locations
|
||||||
|
Key::LShift3_1 => (6, 36),
|
||||||
|
Key::LShift3_2 => (6, 36),
|
||||||
|
Key::LShift3_3 => (6, 36),
|
||||||
Key::Z => (6, 42),
|
Key::Z => (6, 42),
|
||||||
Key::X => (6, 45),
|
Key::X => (6, 45),
|
||||||
Key::C => (6, 48),
|
Key::C => (6, 48),
|
||||||
@@ -141,19 +148,21 @@ impl KeyColourArray {
|
|||||||
Key::Comma => (7, 15),
|
Key::Comma => (7, 15),
|
||||||
Key::Period => (7, 18),
|
Key::Period => (7, 18),
|
||||||
Key::FwdSlash => (7, 21),
|
Key::FwdSlash => (7, 21),
|
||||||
Key::Rshift1 => (7, 27),
|
Key::Rshift => (7, 24),
|
||||||
Key::Rshift2 => (7, 30),
|
Key::Rshift3_1 => (7, 27),
|
||||||
Key::Rshift3 => (7, 33),
|
Key::Rshift3_2 => (7, 30),
|
||||||
|
Key::Rshift3_3 => (7, 33),
|
||||||
Key::End => (7, 36),
|
Key::End => (7, 36),
|
||||||
Key::LCtrl => (7, 51),
|
Key::LCtrl => (7, 51),
|
||||||
Key::LFn => (7, 54),
|
Key::LFn => (7, 54),
|
||||||
//
|
//
|
||||||
Key::Meta => (8, 9),
|
Key::Meta => (8, 9),
|
||||||
Key::LAlt => (8, 12),
|
Key::LAlt => (8, 12),
|
||||||
Key::Space1 => (8, 15),
|
Key::Space5_1 => (8, 15),
|
||||||
Key::Space2 => (8, 18),
|
Key::Space5_2 => (8, 18),
|
||||||
Key::Space3 => (8, 21),
|
Key::Space5_3 => (8, 21),
|
||||||
Key::Space4 => (8, 24),
|
Key::Space5_4 => (8, 24),
|
||||||
|
Key::Space5_5 => (8, 27),
|
||||||
Key::RAlt => (8, 30),
|
Key::RAlt => (8, 30),
|
||||||
Key::PrtSc => (8, 33),
|
Key::PrtSc => (8, 33),
|
||||||
Key::RCtrl => (8, 36),
|
Key::RCtrl => (8, 36),
|
||||||
@@ -164,16 +173,45 @@ impl KeyColourArray {
|
|||||||
//
|
//
|
||||||
Key::Down => (10, 9),
|
Key::Down => (10, 9),
|
||||||
Key::Right => (10, 12),
|
Key::Right => (10, 12),
|
||||||
Key::None => return None,
|
Key::NormalBlank
|
||||||
|
| Key::FuncBlank
|
||||||
|
| Key::NormalSpacer
|
||||||
|
| Key::FuncSpacer
|
||||||
|
| Key::ArrowBlank
|
||||||
|
| Key::ArrowSpacer
|
||||||
|
| Key::UpRegular
|
||||||
|
| Key::DownRegular
|
||||||
|
| Key::LeftRegular
|
||||||
|
| Key::RightRegular
|
||||||
|
| Key::UpSplit
|
||||||
|
| Key::DownSplit
|
||||||
|
| Key::LeftSplit
|
||||||
|
| Key::RightSplit
|
||||||
|
| Key::ArrowRegularBlank
|
||||||
|
| Key::ArrowRegularSpacer
|
||||||
|
| Key::ArrowSplitBlank
|
||||||
|
| Key::ArrowSplitSpacer
|
||||||
|
| Key::RshiftSmall
|
||||||
|
| Key::LCtrlMed
|
||||||
|
| Key::MediaPlay
|
||||||
|
| Key::MediaStop
|
||||||
|
| Key::MediaPrev
|
||||||
|
| Key::MediaNext
|
||||||
|
| Key::Pause
|
||||||
|
| Key::NumLock
|
||||||
|
| Key::Star
|
||||||
|
| Key::NumPadDel
|
||||||
|
| Key::NumPadPlus
|
||||||
|
| Key::NumPadEnter
|
||||||
|
| Key::NumPadPause
|
||||||
|
| Key::NumPadPrtSc
|
||||||
|
| Key::NumPadHome
|
||||||
|
| Key::RCtrlLarge
|
||||||
|
| Key::RowEndSpacer => return None,
|
||||||
|
Key::Fan | Key::Space | Key::BkSpc => return None,
|
||||||
};
|
};
|
||||||
// LOLOLOLOLOLOLOL! Look it's safe okay
|
|
||||||
unsafe {
|
Some(&mut self.0[row][col..2])
|
||||||
Some((
|
|
||||||
&mut *(&mut self.0[row][col] as *mut u8),
|
|
||||||
&mut *(&mut self.0[row][col + 1] as *mut u8),
|
|
||||||
&mut *(&mut self.0[row][col + 2] as *mut u8),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -182,108 +220,6 @@ impl KeyColourArray {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
|
||||||
pub enum Key {
|
|
||||||
VolUp,
|
|
||||||
VolDown,
|
|
||||||
MicMute,
|
|
||||||
Rog,
|
|
||||||
Esc,
|
|
||||||
F1,
|
|
||||||
F2,
|
|
||||||
F3,
|
|
||||||
F4,
|
|
||||||
F5,
|
|
||||||
F6,
|
|
||||||
F7,
|
|
||||||
F8,
|
|
||||||
F9,
|
|
||||||
F10,
|
|
||||||
F11,
|
|
||||||
F12,
|
|
||||||
Del,
|
|
||||||
Tilde,
|
|
||||||
N1,
|
|
||||||
N2,
|
|
||||||
N3,
|
|
||||||
N4,
|
|
||||||
N5,
|
|
||||||
N6,
|
|
||||||
N7,
|
|
||||||
N8,
|
|
||||||
N9,
|
|
||||||
N0,
|
|
||||||
Hyphen,
|
|
||||||
Equals,
|
|
||||||
BkSpc1,
|
|
||||||
BkSpc2,
|
|
||||||
BkSpc3,
|
|
||||||
Home,
|
|
||||||
Tab,
|
|
||||||
Q,
|
|
||||||
W,
|
|
||||||
E,
|
|
||||||
R,
|
|
||||||
T,
|
|
||||||
Y,
|
|
||||||
U,
|
|
||||||
I,
|
|
||||||
O,
|
|
||||||
P,
|
|
||||||
LBracket,
|
|
||||||
RBracket,
|
|
||||||
BackSlash,
|
|
||||||
PgUp,
|
|
||||||
Caps,
|
|
||||||
A,
|
|
||||||
S,
|
|
||||||
D,
|
|
||||||
F,
|
|
||||||
G,
|
|
||||||
H,
|
|
||||||
J,
|
|
||||||
K,
|
|
||||||
L,
|
|
||||||
SemiColon,
|
|
||||||
Quote,
|
|
||||||
Ret1,
|
|
||||||
Ret2,
|
|
||||||
Ret3,
|
|
||||||
PgDn,
|
|
||||||
LShift,
|
|
||||||
Z,
|
|
||||||
X,
|
|
||||||
C,
|
|
||||||
V,
|
|
||||||
B,
|
|
||||||
N,
|
|
||||||
M,
|
|
||||||
Comma,
|
|
||||||
Period,
|
|
||||||
FwdSlash,
|
|
||||||
Rshift1,
|
|
||||||
Rshift2,
|
|
||||||
Rshift3,
|
|
||||||
End,
|
|
||||||
LCtrl,
|
|
||||||
LFn,
|
|
||||||
Meta,
|
|
||||||
LAlt,
|
|
||||||
Space1,
|
|
||||||
Space2,
|
|
||||||
Space3,
|
|
||||||
Space4,
|
|
||||||
RAlt,
|
|
||||||
PrtSc,
|
|
||||||
RCtrl,
|
|
||||||
Up,
|
|
||||||
Down,
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
RFn,
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait KeyLayout {
|
pub trait KeyLayout {
|
||||||
fn get_rows(&self) -> &Vec<[Key; 17]>;
|
fn get_rows(&self) -> &Vec<[Key; 17]>;
|
||||||
}
|
}
|
||||||
@@ -301,37 +237,37 @@ impl Default for GX502Layout {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
GX502Layout(vec![
|
GX502Layout(vec![
|
||||||
[
|
[
|
||||||
Key::None,
|
Key::NormalSpacer,
|
||||||
Key::None,
|
Key::FuncSpacer,
|
||||||
Key::VolDown,
|
Key::VolDown,
|
||||||
Key::VolUp,
|
Key::VolUp,
|
||||||
Key::MicMute,
|
Key::MicMute,
|
||||||
Key::Rog,
|
Key::Rog,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
Key::Esc,
|
Key::Esc,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::F1,
|
Key::F1,
|
||||||
Key::F2,
|
Key::F2,
|
||||||
Key::F3,
|
Key::F3,
|
||||||
Key::F4,
|
Key::F4,
|
||||||
Key::None, // not sure which key to put here
|
Key::NormalBlank, // not sure which key to put here
|
||||||
Key::F5,
|
Key::F5,
|
||||||
Key::F6,
|
Key::F6,
|
||||||
Key::F7,
|
Key::F7,
|
||||||
Key::F8,
|
Key::F8,
|
||||||
Key::F9,
|
Key::NormalBlank,
|
||||||
Key::F9,
|
Key::F9,
|
||||||
Key::F10,
|
Key::F10,
|
||||||
Key::F11,
|
Key::F11,
|
||||||
@@ -352,9 +288,9 @@ impl Default for GX502Layout {
|
|||||||
Key::N0,
|
Key::N0,
|
||||||
Key::Hyphen,
|
Key::Hyphen,
|
||||||
Key::Equals,
|
Key::Equals,
|
||||||
Key::BkSpc1,
|
Key::BkSpc3_1,
|
||||||
Key::BkSpc2,
|
Key::BkSpc3_2,
|
||||||
Key::BkSpc3,
|
Key::BkSpc3_3,
|
||||||
Key::Home,
|
Key::Home,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -390,9 +326,9 @@ impl Default for GX502Layout {
|
|||||||
Key::SemiColon,
|
Key::SemiColon,
|
||||||
Key::Quote,
|
Key::Quote,
|
||||||
Key::Quote,
|
Key::Quote,
|
||||||
Key::Ret1,
|
Key::Return3_1,
|
||||||
Key::Ret2,
|
Key::Return3_2,
|
||||||
Key::Ret3,
|
Key::Return3_3,
|
||||||
Key::PgDn,
|
Key::PgDn,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -409,9 +345,9 @@ impl Default for GX502Layout {
|
|||||||
Key::Period,
|
Key::Period,
|
||||||
Key::FwdSlash,
|
Key::FwdSlash,
|
||||||
Key::FwdSlash,
|
Key::FwdSlash,
|
||||||
Key::Rshift1,
|
Key::Rshift3_1,
|
||||||
Key::Rshift2,
|
Key::Rshift3_2,
|
||||||
Key::Rshift3,
|
Key::Rshift3_3,
|
||||||
Key::End,
|
Key::End,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -419,11 +355,11 @@ impl Default for GX502Layout {
|
|||||||
Key::LFn,
|
Key::LFn,
|
||||||
Key::Meta,
|
Key::Meta,
|
||||||
Key::LAlt,
|
Key::LAlt,
|
||||||
Key::Space1,
|
Key::Space5_1,
|
||||||
Key::Space2,
|
Key::Space5_2,
|
||||||
Key::Space3,
|
Key::Space5_3,
|
||||||
Key::Space4,
|
Key::Space5_4,
|
||||||
Key::Space4,
|
Key::Space5_5,
|
||||||
Key::RAlt,
|
Key::RAlt,
|
||||||
Key::PrtSc,
|
Key::PrtSc,
|
||||||
Key::RCtrl,
|
Key::RCtrl,
|
||||||
@@ -434,23 +370,23 @@ impl Default for GX502Layout {
|
|||||||
Key::RFn,
|
Key::RFn,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
Key::Left,
|
Key::Left,
|
||||||
Key::Down,
|
Key::Down,
|
||||||
Key::Right,
|
Key::Right,
|
||||||
Key::None,
|
Key::NormalBlank,
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::ops::{BitAnd, BitOr};
|
||||||
|
#[cfg(feature = "dbus")]
|
||||||
|
use zvariant::Type;
|
||||||
|
|
||||||
pub const LED_INIT1: [u8; 2] = [0x5d, 0xb9];
|
pub const LED_INIT1: [u8; 2] = [0x5d, 0xb9];
|
||||||
pub const LED_INIT2: &str = "]ASUS Tech.Inc."; // ] == 0x5d
|
pub const LED_INIT2: &str = "]ASUS Tech.Inc."; // ] == 0x5d
|
||||||
pub const LED_INIT3: [u8; 6] = [0x5d, 0x05, 0x20, 0x31, 0, 0x08];
|
pub const LED_INIT3: [u8; 6] = [0x5d, 0x05, 0x20, 0x31, 0, 0x08];
|
||||||
@@ -15,18 +20,339 @@ pub const fn aura_brightness_bytes(brightness: u8) -> [u8; 17] {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const LED_AWAKE_ON_SLEEP_OFF: [u8; 17] = [
|
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||||
0x5d, 0xbd, 0x01, 0xcf, 0x17, 0x0b, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize, Default)]
|
||||||
];
|
pub enum AuraDevice {
|
||||||
|
Tuf,
|
||||||
|
X1854,
|
||||||
|
X1869,
|
||||||
|
X1866,
|
||||||
|
#[default]
|
||||||
|
X19B6,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
pub const LED_AWAKE_ON_SLEEP_ON: [u8; 17] = [
|
impl From<&str> for AuraDevice {
|
||||||
0x5d, 0xbd, 0x01, 0xff, 0x1f, 0x0f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
fn from(s: &str) -> Self {
|
||||||
];
|
match s.to_lowercase().as_str() {
|
||||||
|
"tuf" => AuraDevice::Tuf,
|
||||||
|
"1866" => AuraDevice::X1866,
|
||||||
|
"1869" => AuraDevice::X1869,
|
||||||
|
"1854" => AuraDevice::X1854,
|
||||||
|
"19b6" => AuraDevice::X19B6,
|
||||||
|
"0x1866" => AuraDevice::X1866,
|
||||||
|
"0x1869" => AuraDevice::X1869,
|
||||||
|
"0x1854" => AuraDevice::X1854,
|
||||||
|
"0x19b6" => AuraDevice::X19B6,
|
||||||
|
_ => AuraDevice::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const LED_AWAKE_OFF_SLEEP_OFF: [u8; 17] = [
|
/// This struct is intended as a helper to pass args to generic dbus interface
|
||||||
0x5d, 0xbd, 0x01, 0xc3, 0x13, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||||
];
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct AuraPowerDev {
|
||||||
|
pub tuf: Vec<AuraDevTuf>,
|
||||||
|
pub x1866: Vec<AuraDev1866>,
|
||||||
|
pub x19b6: Vec<AuraDev19b6>,
|
||||||
|
}
|
||||||
|
|
||||||
pub const LED_AWAKE_OFF_SLEEP_ON: [u8; 17] = [
|
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||||
0x5d, 0xbd, 0x01, 0xf3, 0x1b, 0x0d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||||
];
|
#[repr(u32)]
|
||||||
|
pub enum AuraDevTuf {
|
||||||
|
Boot,
|
||||||
|
Awake,
|
||||||
|
Sleep,
|
||||||
|
Keyboard,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuraDevTuf {
|
||||||
|
pub const fn dev_id() -> &'static str {
|
||||||
|
"tuf"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Bits for older 0x1866 keyboard model
|
||||||
|
///
|
||||||
|
/// Keybord and Lightbar require Awake, Boot and Sleep apply to both
|
||||||
|
/// Keybord and Lightbar regardless of if either are enabled (or Awake is enabled)
|
||||||
|
///
|
||||||
|
/// | Byte 1 | Byte 2 | Byte 3 | function | hex |
|
||||||
|
/// |------------|------------|------------|----------|----------|
|
||||||
|
/// | 0000, 0000 | 0000, 0000 | 0000, 0010 | Awake | 00,00,02 |
|
||||||
|
/// | 0000, 1000 | 0000, 0000 | 0000, 0000 | Keyboard | 08,00,00 |
|
||||||
|
/// | 0000, 0100 | 0000, 0101 | 0000, 0000 | Lightbar | 04,05,00 |
|
||||||
|
/// | 1100, 0011 | 0001, 0010 | 0000, 1001 | Boot/Sht | c3,12,09 |
|
||||||
|
/// | 0011, 0000 | 0000, 1000 | 0000, 0100 | Sleep | 30,08,04 |
|
||||||
|
/// | 1111, 1111 | 0001, 1111 | 0000, 1111 | all on | |
|
||||||
|
///
|
||||||
|
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum AuraDev1866 {
|
||||||
|
Awake = 0x000002,
|
||||||
|
Keyboard = 0x080000,
|
||||||
|
Lightbar = 0x040500,
|
||||||
|
Boot = 0xc31209,
|
||||||
|
Sleep = 0x300804,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AuraDev1866> for u32 {
|
||||||
|
fn from(a: AuraDev1866) -> Self {
|
||||||
|
a as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuraDev1866 {
|
||||||
|
pub fn to_bytes(control: &[Self]) -> [u8; 3] {
|
||||||
|
let mut a: u32 = 0;
|
||||||
|
control.iter().for_each(|n| {
|
||||||
|
a |= *n as u32;
|
||||||
|
});
|
||||||
|
[
|
||||||
|
((a & 0xff0000) >> 16) as u8,
|
||||||
|
((a & 0xff00) >> 8) as u8,
|
||||||
|
(a & 0xff) as u8,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn dev_id() -> &'static str {
|
||||||
|
"0x1866"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOr<AuraDev1866> for AuraDev1866 {
|
||||||
|
type Output = u32;
|
||||||
|
|
||||||
|
fn bitor(self, rhs: AuraDev1866) -> Self::Output {
|
||||||
|
return self as u32 | rhs as u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitAnd<AuraDev1866> for AuraDev1866 {
|
||||||
|
type Output = u32;
|
||||||
|
|
||||||
|
fn bitand(self, rhs: AuraDev1866) -> Self::Output {
|
||||||
|
return self as u32 & rhs as u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Bits for 0x19b6 keyboard model
|
||||||
|
///
|
||||||
|
/// byte 4 in the USB packet is for keyboard + logo power states
|
||||||
|
/// default is on, `ff`
|
||||||
|
/// Keyboard and logo use the full range of bits (almost)
|
||||||
|
///
|
||||||
|
/// | n1 | n2 | hex | action | bit |
|
||||||
|
/// |------|------|-----|-----------------------|-------|
|
||||||
|
/// | 0000 | 0000 | 00 | all off | |
|
||||||
|
/// | 0000 | 0001 | 01 | logo boot | bit 1 |
|
||||||
|
/// | 0000 | 0010 | 02 | keyboard boot | bit 2 |
|
||||||
|
/// | 0000 | 0100 | 04 | logo awake | bit 3 |
|
||||||
|
/// | 0000 | 1000 | 08 | keyboard awake | bit 4 |
|
||||||
|
/// | 0001 | 0000 | 10 | logo sleep off | bit 5 |
|
||||||
|
/// | 0010 | 0000 | 20 | keyboard sleep | bit 6 |
|
||||||
|
/// | 0100 | 0000 | 40 | logo shutdown off | bit 7 |
|
||||||
|
/// | 1000 | 0000 | 80 | keyboard shutdown off | bit 8 |
|
||||||
|
///
|
||||||
|
/// byte 5 = lightbar
|
||||||
|
///
|
||||||
|
/// | 1 | 2 | hex | action | bit |
|
||||||
|
/// |------|------|-----|----------------------|-------|
|
||||||
|
/// | 0000 | 0010 | 02 | lightbar off boot | bit 2 |
|
||||||
|
/// | 0000 | 0100 | 04 | lightbar on | bit 3 |
|
||||||
|
/// | 0000 | 1000 | 08 | lightbar off sleep | bit 4 |
|
||||||
|
/// | 0001 | 0000 | 10 | lightbar shtdn off | bit 5 |
|
||||||
|
///
|
||||||
|
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||||
|
#[repr(u16)]
|
||||||
|
pub enum AuraDev19b6 {
|
||||||
|
BootLogo = 1,
|
||||||
|
BootKeyb = 1 << 1,
|
||||||
|
AwakeLogo = 1 << 2,
|
||||||
|
AwakeKeyb = 1 << 3,
|
||||||
|
SleepLogo = 1 << 4,
|
||||||
|
SleepKeyb = 1 << 5,
|
||||||
|
ShutdownLogo = 1 << 6,
|
||||||
|
ShutdownKeyb = 1 << 7,
|
||||||
|
AwakeBar = 1 << 7 + 2,
|
||||||
|
BootBar = 1 << 7 + 3,
|
||||||
|
SleepBar = 1 << 7 + 4,
|
||||||
|
ShutdownBar = 1 << 7 + 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AuraDev19b6> for u16 {
|
||||||
|
fn from(a: AuraDev19b6) -> Self {
|
||||||
|
a as u16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuraDev19b6 {
|
||||||
|
pub fn to_bytes(control: &[Self]) -> [u8; 3] {
|
||||||
|
let mut a: u16 = 0;
|
||||||
|
control.iter().for_each(|n| {
|
||||||
|
a |= *n as u16;
|
||||||
|
});
|
||||||
|
[(a & 0xff) as u8, ((a & 0xff00) >> 8) as u8, 0x00]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn dev_id() -> &'static str {
|
||||||
|
"0x196b"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOr<AuraDev19b6> for AuraDev19b6 {
|
||||||
|
type Output = u16;
|
||||||
|
|
||||||
|
fn bitor(self, rhs: AuraDev19b6) -> Self::Output {
|
||||||
|
return self as u16 | rhs as u16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitAnd<AuraDev19b6> for AuraDev19b6 {
|
||||||
|
type Output = u16;
|
||||||
|
|
||||||
|
fn bitand(self, rhs: AuraDev19b6) -> Self::Output {
|
||||||
|
return self as u16 & rhs as u16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::usb::AuraDev19b6;
|
||||||
|
|
||||||
|
use super::AuraDev1866;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_0x1866_control_bytes() {
|
||||||
|
let bytes = [AuraDev1866::Keyboard, AuraDev1866::Awake];
|
||||||
|
let bytes = AuraDev1866::to_bytes(&bytes);
|
||||||
|
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
|
||||||
|
assert_eq!(bytes, [0x08, 0x00, 0x02]);
|
||||||
|
|
||||||
|
let bytes = [AuraDev1866::Lightbar, AuraDev1866::Awake];
|
||||||
|
let bytes = AuraDev1866::to_bytes(&bytes);
|
||||||
|
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
|
||||||
|
assert_eq!(bytes, [0x04, 0x05, 0x02]);
|
||||||
|
|
||||||
|
let bytes = [AuraDev1866::Sleep];
|
||||||
|
let bytes = AuraDev1866::to_bytes(&bytes);
|
||||||
|
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
|
||||||
|
assert_eq!(bytes, [0x30, 0x08, 0x04]);
|
||||||
|
|
||||||
|
let bytes = [AuraDev1866::Boot];
|
||||||
|
let bytes = AuraDev1866::to_bytes(&bytes);
|
||||||
|
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
|
||||||
|
assert_eq!(bytes, [0xc3, 0x12, 0x09]);
|
||||||
|
|
||||||
|
let bytes = [
|
||||||
|
AuraDev1866::Keyboard,
|
||||||
|
AuraDev1866::Lightbar,
|
||||||
|
AuraDev1866::Awake,
|
||||||
|
AuraDev1866::Sleep,
|
||||||
|
AuraDev1866::Boot,
|
||||||
|
];
|
||||||
|
|
||||||
|
let bytes = AuraDev1866::to_bytes(&bytes);
|
||||||
|
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
|
||||||
|
assert_eq!(bytes, [0xff, 0x1f, 0x000f]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_0x19b6_control_bytes() {
|
||||||
|
// All on
|
||||||
|
let byte1 = [
|
||||||
|
AuraDev19b6::BootLogo,
|
||||||
|
AuraDev19b6::BootKeyb,
|
||||||
|
AuraDev19b6::SleepLogo,
|
||||||
|
AuraDev19b6::SleepKeyb,
|
||||||
|
AuraDev19b6::AwakeLogo,
|
||||||
|
AuraDev19b6::AwakeKeyb,
|
||||||
|
AuraDev19b6::ShutdownLogo,
|
||||||
|
AuraDev19b6::ShutdownKeyb,
|
||||||
|
];
|
||||||
|
let bytes = AuraDev19b6::to_bytes(&byte1);
|
||||||
|
println!("{:08b}", bytes[0]);
|
||||||
|
assert_eq!(bytes[0], 0xff);
|
||||||
|
|
||||||
|
//
|
||||||
|
let byte1 = [
|
||||||
|
// AuraControl::BootLogo,
|
||||||
|
AuraDev19b6::BootKeyb,
|
||||||
|
AuraDev19b6::SleepLogo,
|
||||||
|
AuraDev19b6::SleepKeyb,
|
||||||
|
AuraDev19b6::AwakeLogo,
|
||||||
|
AuraDev19b6::AwakeKeyb,
|
||||||
|
AuraDev19b6::ShutdownLogo,
|
||||||
|
AuraDev19b6::ShutdownKeyb,
|
||||||
|
];
|
||||||
|
let bytes = AuraDev19b6::to_bytes(&byte1);
|
||||||
|
println!("{:08b}", bytes[0]);
|
||||||
|
assert_eq!(bytes[0], 0xfe);
|
||||||
|
|
||||||
|
let byte1 = [
|
||||||
|
AuraDev19b6::BootLogo,
|
||||||
|
// AuraControl::BootKeyb,
|
||||||
|
AuraDev19b6::SleepLogo,
|
||||||
|
AuraDev19b6::SleepKeyb,
|
||||||
|
AuraDev19b6::AwakeLogo,
|
||||||
|
AuraDev19b6::AwakeKeyb,
|
||||||
|
AuraDev19b6::ShutdownLogo,
|
||||||
|
AuraDev19b6::ShutdownKeyb,
|
||||||
|
];
|
||||||
|
let bytes = AuraDev19b6::to_bytes(&byte1);
|
||||||
|
println!("{:08b}", bytes[0]);
|
||||||
|
assert_eq!(bytes[0], 0xfd);
|
||||||
|
|
||||||
|
let byte1 = [
|
||||||
|
AuraDev19b6::BootLogo,
|
||||||
|
AuraDev19b6::BootKeyb,
|
||||||
|
// AuraControl::SleepLogo,
|
||||||
|
AuraDev19b6::SleepKeyb,
|
||||||
|
AuraDev19b6::AwakeLogo,
|
||||||
|
AuraDev19b6::AwakeKeyb,
|
||||||
|
AuraDev19b6::ShutdownLogo,
|
||||||
|
AuraDev19b6::ShutdownKeyb,
|
||||||
|
];
|
||||||
|
let bytes = AuraDev19b6::to_bytes(&byte1);
|
||||||
|
println!("{:08b}", bytes[0]);
|
||||||
|
assert_eq!(bytes[0], 0xef);
|
||||||
|
|
||||||
|
let byte1 = [
|
||||||
|
AuraDev19b6::BootLogo,
|
||||||
|
AuraDev19b6::BootKeyb,
|
||||||
|
AuraDev19b6::SleepLogo,
|
||||||
|
// AuraControl::SleepKeyb,
|
||||||
|
AuraDev19b6::AwakeLogo,
|
||||||
|
AuraDev19b6::AwakeKeyb,
|
||||||
|
AuraDev19b6::ShutdownLogo,
|
||||||
|
AuraDev19b6::ShutdownKeyb,
|
||||||
|
];
|
||||||
|
let bytes = AuraDev19b6::to_bytes(&byte1);
|
||||||
|
println!("{:08b}", bytes[0]);
|
||||||
|
assert_eq!(bytes[0], 0xdf);
|
||||||
|
|
||||||
|
let byte2 = [
|
||||||
|
AuraDev19b6::AwakeBar,
|
||||||
|
AuraDev19b6::BootBar,
|
||||||
|
AuraDev19b6::SleepBar,
|
||||||
|
AuraDev19b6::ShutdownBar,
|
||||||
|
];
|
||||||
|
let bytes = AuraDev19b6::to_bytes(&byte2);
|
||||||
|
println!("{:08b}", bytes[1]);
|
||||||
|
assert_eq!(bytes[1], 0x1e);
|
||||||
|
|
||||||
|
let byte2 = [
|
||||||
|
AuraDev19b6::AwakeBar,
|
||||||
|
AuraDev19b6::BootBar,
|
||||||
|
// AuraControl::SleepBar,
|
||||||
|
AuraDev19b6::ShutdownBar,
|
||||||
|
];
|
||||||
|
let bytes = AuraDev19b6::to_bytes(&byte2);
|
||||||
|
println!("{:08b}", bytes[1]);
|
||||||
|
assert_eq!(bytes[1], 0x16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
[package]
|
||||||
|
name = "rog-control-center"
|
||||||
|
version = "1.0.0"
|
||||||
|
authors = ["Luke D. Jones <luke@ljones.dev>"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
mocking = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
egui = { git = "https://github.com/emilk/egui" }
|
||||||
|
eframe= { git = "https://github.com/emilk/egui" }
|
||||||
|
#eframe= { git = "https://github.com/emilk/egui", default-features = false, features = ["dark-light", "default_fonts", "wgpu"] }
|
||||||
|
|
||||||
|
rog_dbus = { path = "../rog-dbus" }
|
||||||
|
rog_aura = { path = "../rog-aura" }
|
||||||
|
rog_profiles = { path = "../rog-profiles" }
|
||||||
|
rog_supported = { path = "../rog-supported" }
|
||||||
|
# supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git" }
|
||||||
|
|
||||||
|
smol = "^1.2"
|
||||||
|
|
||||||
|
serde = "^1.0"
|
||||||
|
toml = "^0.5"
|
||||||
|
serde_json = "^1.0"
|
||||||
|
serde_derive = "^1.0"
|
||||||
|
zbus = "^2.3"
|
||||||
|
nix = "^0.20.0"
|
||||||
|
tempfile = "3.2.0"
|
||||||
|
dirs = "3.0.1"
|
||||||
|
|
||||||
|
[dependencies.notify-rust]
|
||||||
|
version = "^4.3"
|
||||||
|
default-features = false
|
||||||
|
features = ["z"]
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# App template
|
||||||
|
|
||||||
|
This is a trial app cut down to bare essentials to show how to create an app that can run in the background
|
||||||
|
with some user-config options.
|
||||||
|
|
||||||
|
egui based. Keep in mind that this is very much a bit of a mess due to experimenting.
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
Use `WINIT_UNIX_BACKEND=x11 rog-control-center`. `WINIT_UNIX_BACKEND` is required due to window decorations not updating and the window not really being set as visible/invisible on wayland.
|
||||||
|
|
||||||
|
## Build features
|
||||||
|
|
||||||
|
For testing some features that are typically not available on all laptops:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
cargo run --features mocking
|
||||||
|
```
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
- Add notification watch for certain UI elements to enforce an update (for example when a user changes Aura via a hot key).
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Version=1.0
|
||||||
|
Type=Application
|
||||||
|
|
||||||
|
Name=ROG Control Center
|
||||||
|
Comment=Make your ASUS ROG Laptop go Brrrrr!
|
||||||
|
Categories=Settings
|
||||||
|
|
||||||
|
Icon=rog-control-center
|
||||||
|
Exec=env WINIT_UNIX_BACKEND=x11 rog-control-center
|
||||||
|
Terminal=false
|
||||||
|
After Width: | Height: | Size: 70 KiB |
@@ -0,0 +1,196 @@
|
|||||||
|
use std::{
|
||||||
|
f64::consts::PI,
|
||||||
|
io::Write,
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, AtomicU8, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
|
use egui::{Button, RichText};
|
||||||
|
use rog_supported::SupportedFunctions;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
config::Config, error::Result, get_ipc_file, page_states::PageDataStates, Page,
|
||||||
|
RogDbusClientBlocking, SHOWING_GUI,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct RogApp<'a> {
|
||||||
|
pub page: Page,
|
||||||
|
pub states: PageDataStates,
|
||||||
|
pub supported: SupportedFunctions,
|
||||||
|
/// Should the app begin showing the GUI
|
||||||
|
pub begin_show_gui: Arc<AtomicBool>,
|
||||||
|
/// Is the app GUI closed (and running in bg)
|
||||||
|
pub running_in_bg: bool,
|
||||||
|
// TODO: can probably just open and read whenever
|
||||||
|
pub config: Config,
|
||||||
|
pub asus_dbus: RogDbusClientBlocking<'a>,
|
||||||
|
/// Oscillator in percentage
|
||||||
|
pub oscillator1: Arc<AtomicU8>,
|
||||||
|
pub oscillator2: Arc<AtomicU8>,
|
||||||
|
pub oscillator3: Arc<AtomicU8>,
|
||||||
|
/// Frequency of oscillation
|
||||||
|
pub oscillator_freq: Arc<AtomicU8>,
|
||||||
|
/// A toggle that toggles true/false when the oscillator reaches 0
|
||||||
|
pub oscillator_toggle: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RogApp<'a> {
|
||||||
|
/// Called once before the first frame.
|
||||||
|
pub fn new(
|
||||||
|
start_closed: bool,
|
||||||
|
config: Config,
|
||||||
|
show_gui: Arc<AtomicBool>,
|
||||||
|
states: PageDataStates,
|
||||||
|
_cc: &eframe::CreationContext<'_>,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let (dbus, _) = RogDbusClientBlocking::new()?;
|
||||||
|
let supported = dbus.proxies().supported().supported_functions()?;
|
||||||
|
|
||||||
|
// Set up an oscillator to run on a thread.
|
||||||
|
// Helpful for visual effects like colour pulse.
|
||||||
|
let oscillator1 = Arc::new(AtomicU8::new(0));
|
||||||
|
let oscillator2 = Arc::new(AtomicU8::new(0));
|
||||||
|
let oscillator3 = Arc::new(AtomicU8::new(0));
|
||||||
|
|
||||||
|
let oscillator1_1 = oscillator1.clone();
|
||||||
|
let oscillator1_2 = oscillator2.clone();
|
||||||
|
let oscillator1_3 = oscillator3.clone();
|
||||||
|
|
||||||
|
let oscillator_freq = Arc::new(AtomicU8::new(5));
|
||||||
|
let oscillator_freq1 = oscillator_freq.clone();
|
||||||
|
let oscillator_toggle = Arc::new(AtomicBool::new(false));
|
||||||
|
let oscillator_toggle1 = oscillator_toggle.clone();
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let started = Instant::now();
|
||||||
|
let mut toggled = false;
|
||||||
|
loop {
|
||||||
|
let time = started.elapsed();
|
||||||
|
// 32 = slow, 16 = med, 8 = fast
|
||||||
|
let scale = oscillator_freq1.load(Ordering::SeqCst) as f64;
|
||||||
|
let elapsed1 = (time.as_millis() as f64 + 333.0) / 10000.0;
|
||||||
|
let elapsed2 = (time.as_millis() as f64 + 666.0) / 10000.0;
|
||||||
|
let elapsed3 = (time.as_millis() as f64 + 999.0) / 10000.0;
|
||||||
|
let tmp1 = ((scale * elapsed1 * PI).cos()).abs();
|
||||||
|
let tmp2 = ((scale * 0.85 * elapsed2 * PI).cos()).abs();
|
||||||
|
let tmp3 = ((scale * 0.7 * elapsed3 * PI).cos()).abs();
|
||||||
|
if tmp1 <= 0.1 && !toggled {
|
||||||
|
let s = oscillator_toggle1.load(Ordering::SeqCst);
|
||||||
|
oscillator_toggle1.store(!s, Ordering::SeqCst);
|
||||||
|
toggled = true;
|
||||||
|
} else if tmp1 > 0.9 {
|
||||||
|
toggled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tmp1 = (255.0 * tmp1 * 100.0 / 255.0) as u8;
|
||||||
|
let tmp2 = (255.0 * tmp2 * 100.0 / 255.0) as u8;
|
||||||
|
let tmp3 = (255.0 * tmp3 * 100.0 / 255.0) as u8;
|
||||||
|
|
||||||
|
oscillator1_1.store(tmp1, Ordering::SeqCst);
|
||||||
|
oscillator1_2.store(tmp2, Ordering::SeqCst);
|
||||||
|
oscillator1_3.store(tmp3, Ordering::SeqCst);
|
||||||
|
std::thread::sleep(Duration::from_millis(33));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
supported,
|
||||||
|
states,
|
||||||
|
page: Page::System,
|
||||||
|
begin_show_gui: show_gui,
|
||||||
|
running_in_bg: start_closed,
|
||||||
|
config,
|
||||||
|
asus_dbus: dbus,
|
||||||
|
oscillator1,
|
||||||
|
oscillator2,
|
||||||
|
oscillator3,
|
||||||
|
oscillator_toggle,
|
||||||
|
oscillator_freq,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> eframe::App for RogApp<'a> {
|
||||||
|
fn on_exit_event(&mut self) -> bool {
|
||||||
|
if self.config.run_in_background {
|
||||||
|
self.running_in_bg = true;
|
||||||
|
get_ipc_file().unwrap().write_all(&[0]).unwrap();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called each time the UI needs repainting, which may be many times per second.
|
||||||
|
/// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
|
||||||
|
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||||
|
let Self {
|
||||||
|
begin_show_gui: should_show_gui,
|
||||||
|
supported,
|
||||||
|
asus_dbus: dbus,
|
||||||
|
states,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
states
|
||||||
|
.refresh_if_notfied(supported, dbus)
|
||||||
|
.map(|repaint| {
|
||||||
|
if repaint {
|
||||||
|
ctx.request_repaint();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map_err(|e| self.states.error = Some(e.to_string()))
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
let page = self.page;
|
||||||
|
|
||||||
|
if should_show_gui.load(Ordering::SeqCst) {
|
||||||
|
let mut ipc_file = get_ipc_file().unwrap();
|
||||||
|
ipc_file.write_all(&[SHOWING_GUI]).unwrap();
|
||||||
|
should_show_gui.store(false, Ordering::SeqCst);
|
||||||
|
frame.set_visible(true);
|
||||||
|
self.running_in_bg = false;
|
||||||
|
}
|
||||||
|
if self.running_in_bg {
|
||||||
|
// Request to draw nothing at all
|
||||||
|
ctx.request_repaint_after(Duration::from_millis(500));
|
||||||
|
frame.set_visible(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Do all GUI display after this point
|
||||||
|
|
||||||
|
self.top_bar(ctx, frame);
|
||||||
|
self.side_panel(ctx);
|
||||||
|
|
||||||
|
if let Some(err) = self.states.error.clone() {
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
ui.heading(RichText::new("Error!").size(28.0));
|
||||||
|
|
||||||
|
ui.centered_and_justified(|ui| {
|
||||||
|
ui.label(RichText::new(format!("The error was: {:?}", err)).size(22.0));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
egui::TopBottomPanel::bottom("error_bar")
|
||||||
|
.default_height(26.0)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
|
||||||
|
if ui
|
||||||
|
.add(Button::new(RichText::new("Okay").size(20.0)))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.states.error = None;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if page == Page::System {
|
||||||
|
self.system_page(ctx);
|
||||||
|
} else if page == Page::AuraEffects {
|
||||||
|
self.aura_page(ctx);
|
||||||
|
// TODO: Anime page is not complete
|
||||||
|
// } else if page == Page::AnimeMatrix {
|
||||||
|
// self.anime_page(ctx);
|
||||||
|
} else if page == Page::FanCurves {
|
||||||
|
self.fan_curve_page(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
use std::{
|
||||||
|
fs::{create_dir, OpenOptions},
|
||||||
|
io::{Read, Write},
|
||||||
|
};
|
||||||
|
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
//use log::{error, info, warn};
|
||||||
|
|
||||||
|
use crate::error::Error;
|
||||||
|
|
||||||
|
const CFG_DIR: &str = "rog";
|
||||||
|
const CFG_FILE_NAME: &str = "rog-control-center.cfg";
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct Config {
|
||||||
|
pub run_in_background: bool,
|
||||||
|
pub startup_in_background: bool,
|
||||||
|
pub enable_notifications: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
run_in_background: true,
|
||||||
|
startup_in_background: false,
|
||||||
|
enable_notifications: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn load() -> Result<Config, Error> {
|
||||||
|
let mut path = if let Some(dir) = dirs::config_dir() {
|
||||||
|
dir
|
||||||
|
} else {
|
||||||
|
return Err(Error::XdgVars);
|
||||||
|
};
|
||||||
|
|
||||||
|
path.push(CFG_DIR);
|
||||||
|
if !path.exists() {
|
||||||
|
create_dir(path.clone())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
path.push(CFG_FILE_NAME);
|
||||||
|
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.open(&path)?;
|
||||||
|
|
||||||
|
let mut buf = String::new();
|
||||||
|
|
||||||
|
if let Ok(read_len) = file.read_to_string(&mut buf) {
|
||||||
|
if read_len == 0 {
|
||||||
|
let default = Config::default();
|
||||||
|
let t = toml::to_string_pretty(&default).unwrap();
|
||||||
|
file.write_all(t.as_bytes())?;
|
||||||
|
return Ok(default);
|
||||||
|
} else if let Ok(data) = toml::from_str::<Config>(&buf) {
|
||||||
|
return Ok(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Error::ConfigLoadFail)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save(&self) -> Result<(), Error> {
|
||||||
|
let mut path = if let Some(dir) = dirs::config_dir() {
|
||||||
|
dir
|
||||||
|
} else {
|
||||||
|
return Err(Error::XdgVars);
|
||||||
|
};
|
||||||
|
|
||||||
|
path.push(CFG_DIR);
|
||||||
|
if !path.exists() {
|
||||||
|
create_dir(path.clone())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
path.push(CFG_FILE_NAME);
|
||||||
|
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.truncate(true)
|
||||||
|
.open(&path)?;
|
||||||
|
|
||||||
|
let t = toml::to_string_pretty(&self).unwrap();
|
||||||
|
file.write_all(t.as_bytes())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
Io(std::io::Error),
|
||||||
|
Nix(nix::Error),
|
||||||
|
ConfigLoadFail,
|
||||||
|
ConfigLockFail,
|
||||||
|
XdgVars,
|
||||||
|
Zbus(zbus::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
// This trait requires `fmt` with this exact signature.
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::Io(err) => write!(f, "Failed to open: {}", err),
|
||||||
|
Error::Nix(err) => write!(f, "Error: {}", err),
|
||||||
|
Error::ConfigLoadFail => write!(f, "Failed to load user config"),
|
||||||
|
Error::ConfigLockFail => write!(f, "Failed to lock user config"),
|
||||||
|
Error::XdgVars => write!(f, "XDG environment vars appear unset"),
|
||||||
|
Error::Zbus(err) => write!(f, "Error: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for Error {
|
||||||
|
fn from(err: std::io::Error) -> Self {
|
||||||
|
Error::Io(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<nix::Error> for Error {
|
||||||
|
fn from(err: nix::Error) -> Self {
|
||||||
|
Error::Nix(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<zbus::Error> for Error {
|
||||||
|
fn from(err: zbus::Error) -> Self {
|
||||||
|
Error::Zbus(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
pub mod app;
|
||||||
|
use std::{
|
||||||
|
fs::{remove_dir_all, File, OpenOptions},
|
||||||
|
io::{Read, Write},
|
||||||
|
process::exit,
|
||||||
|
thread::sleep,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use app::RogApp;
|
||||||
|
|
||||||
|
pub mod config;
|
||||||
|
pub mod error;
|
||||||
|
#[cfg(feature = "mocking")]
|
||||||
|
pub mod mocking;
|
||||||
|
pub mod notify;
|
||||||
|
pub mod page_states;
|
||||||
|
pub mod pages;
|
||||||
|
pub mod startup_error;
|
||||||
|
pub mod widgets;
|
||||||
|
|
||||||
|
#[cfg(feature = "mocking")]
|
||||||
|
pub use mocking::RogDbusClientBlocking;
|
||||||
|
#[cfg(not(feature = "mocking"))]
|
||||||
|
pub use rog_dbus::RogDbusClientBlocking;
|
||||||
|
|
||||||
|
use nix::{sys::stat, unistd};
|
||||||
|
use tempfile::TempDir;
|
||||||
|
//use log::{error, info, warn};
|
||||||
|
|
||||||
|
pub const SHOWING_GUI: u8 = 1;
|
||||||
|
pub const SHOW_GUI: u8 = 2;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone, Copy)]
|
||||||
|
pub enum Page {
|
||||||
|
System,
|
||||||
|
AuraEffects,
|
||||||
|
AnimeMatrix,
|
||||||
|
FanCurves,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Either exit the process, or return with a refreshed tmp-dir
|
||||||
|
pub fn on_tmp_dir_exists() -> Result<TempDir, std::io::Error> {
|
||||||
|
let mut buf = [0u8; 4];
|
||||||
|
let path = std::env::temp_dir().join("rog-gui");
|
||||||
|
|
||||||
|
let mut ipc_file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(false)
|
||||||
|
.open(path.join("ipc.pipe"))?;
|
||||||
|
|
||||||
|
// If the app is running this ends up stacked on top of SHOWING_GUI
|
||||||
|
ipc_file.write_all(&[SHOW_GUI])?;
|
||||||
|
// tiny sleep to give the app a chance to respond
|
||||||
|
sleep(Duration::from_millis(10));
|
||||||
|
ipc_file.read(&mut buf).ok();
|
||||||
|
|
||||||
|
// First entry is the actual state
|
||||||
|
if buf[0] == SHOWING_GUI {
|
||||||
|
ipc_file.write_all(&[SHOWING_GUI])?; // Store state again as we drained the fifo
|
||||||
|
exit(0);
|
||||||
|
} else if buf[0] == SHOW_GUI {
|
||||||
|
remove_dir_all(&path)?;
|
||||||
|
return tempfile::Builder::new()
|
||||||
|
.prefix("rog-gui")
|
||||||
|
.rand_bytes(0)
|
||||||
|
.tempdir();
|
||||||
|
}
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_ipc_file() -> Result<File, crate::error::Error> {
|
||||||
|
let tmp_dir = std::env::temp_dir().join("rog-gui");
|
||||||
|
let fifo_path = tmp_dir.join("ipc.pipe");
|
||||||
|
if let Err(e) = unistd::mkfifo(&fifo_path, stat::Mode::S_IRWXU) {
|
||||||
|
if !matches!(e, nix::Error::Sys(nix::errno::Errno::EEXIST)) {
|
||||||
|
return Err(e)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.truncate(true)
|
||||||
|
.open(&fifo_path)?)
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
use rog_aura::layouts::KeyLayout;
|
||||||
|
use rog_control_center::{
|
||||||
|
config::Config, get_ipc_file, notify::start_notifications, on_tmp_dir_exists,
|
||||||
|
page_states::PageDataStates, startup_error::AppErrorShow, RogApp, RogDbusClientBlocking,
|
||||||
|
SHOW_GUI,
|
||||||
|
};
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
fs::{self, OpenOptions},
|
||||||
|
io::Read,
|
||||||
|
path::PathBuf,
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
thread::spawn,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "mocking"))]
|
||||||
|
const DATA_DIR: &str = "/usr/share/rog-gui/";
|
||||||
|
#[cfg(feature = "mocking")]
|
||||||
|
const DATA_DIR: &str = env!("CARGO_MANIFEST_DIR");
|
||||||
|
const BOARD_NAME: &str = "/sys/class/dmi/id/board_name";
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let native_options = eframe::NativeOptions {
|
||||||
|
decorated: false,
|
||||||
|
transparent: false,
|
||||||
|
min_window_size: Some(egui::vec2(840.0, 600.0)),
|
||||||
|
max_window_size: Some(egui::vec2(840.0, 600.0)),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let (dbus, _) = RogDbusClientBlocking::new()
|
||||||
|
.map_err(|e| {
|
||||||
|
eframe::run_native(
|
||||||
|
"ROG Control Center",
|
||||||
|
native_options.clone(),
|
||||||
|
Box::new(move |_| Box::new(AppErrorShow::new(e.to_string()))),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Startup
|
||||||
|
let mut config = Config::load()?;
|
||||||
|
let start_closed = config.startup_in_background;
|
||||||
|
|
||||||
|
if config.startup_in_background {
|
||||||
|
config.run_in_background = true;
|
||||||
|
config.save()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find and load a matching layout for laptop
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.open(PathBuf::from(BOARD_NAME))
|
||||||
|
.map_err(|e| {
|
||||||
|
println!("{BOARD_NAME}, {e}");
|
||||||
|
e
|
||||||
|
})?;
|
||||||
|
let mut board_name = String::new();
|
||||||
|
file.read_to_string(&mut board_name)?;
|
||||||
|
|
||||||
|
let mut layout = KeyLayout::ga401_layout(); // default
|
||||||
|
let mut path = PathBuf::from(DATA_DIR);
|
||||||
|
#[cfg(feature = "mocking")]
|
||||||
|
{
|
||||||
|
board_name = "gl504".to_string();
|
||||||
|
path.pop();
|
||||||
|
path.push("rog-aura");
|
||||||
|
path.push("data");
|
||||||
|
}
|
||||||
|
path.push("layouts");
|
||||||
|
let path = path.as_path();
|
||||||
|
for p in fs::read_dir(path).map_err(|e| {
|
||||||
|
println!("{:?}, {e}", path);
|
||||||
|
e
|
||||||
|
})? {
|
||||||
|
let tmp = KeyLayout::from_file(&p?.path()).unwrap();
|
||||||
|
if tmp.matches(board_name.as_str()) {
|
||||||
|
layout = tmp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cheap method to alert to notifications rather than spinning a thread for each
|
||||||
|
// This is quite different when done in a retained mode app
|
||||||
|
let charge_notified = Arc::new(AtomicBool::new(false));
|
||||||
|
let bios_notified = Arc::new(AtomicBool::new(false));
|
||||||
|
let aura_notified = Arc::new(AtomicBool::new(false));
|
||||||
|
let anime_notified = Arc::new(AtomicBool::new(false));
|
||||||
|
let profiles_notified = Arc::new(AtomicBool::new(false));
|
||||||
|
let fans_notified = Arc::new(AtomicBool::new(false));
|
||||||
|
let notifs_enabled = Arc::new(AtomicBool::new(config.enable_notifications));
|
||||||
|
|
||||||
|
let states = {
|
||||||
|
let supported = dbus
|
||||||
|
.proxies()
|
||||||
|
.supported()
|
||||||
|
.supported_functions()
|
||||||
|
.map_err(|e| {
|
||||||
|
eframe::run_native(
|
||||||
|
"ROG Control Center",
|
||||||
|
native_options.clone(),
|
||||||
|
Box::new(move |_| Box::new(AppErrorShow::new(e.to_string()))),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
PageDataStates::new(
|
||||||
|
layout,
|
||||||
|
notifs_enabled.clone(),
|
||||||
|
charge_notified.clone(),
|
||||||
|
bios_notified.clone(),
|
||||||
|
aura_notified.clone(),
|
||||||
|
anime_notified.clone(),
|
||||||
|
profiles_notified.clone(),
|
||||||
|
fans_notified.clone(),
|
||||||
|
&supported,
|
||||||
|
&dbus,
|
||||||
|
)? // TODO: if error, show alt GUI containing the error message
|
||||||
|
};
|
||||||
|
|
||||||
|
if config.enable_notifications {
|
||||||
|
start_notifications(
|
||||||
|
charge_notified,
|
||||||
|
bios_notified,
|
||||||
|
aura_notified,
|
||||||
|
anime_notified,
|
||||||
|
profiles_notified,
|
||||||
|
fans_notified,
|
||||||
|
notifs_enabled,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tmp-dir must live to the end of program life
|
||||||
|
let _tmp_dir = match tempfile::Builder::new()
|
||||||
|
.prefix("rog-gui")
|
||||||
|
.rand_bytes(0)
|
||||||
|
.tempdir()
|
||||||
|
{
|
||||||
|
Ok(tmp) => tmp,
|
||||||
|
Err(_) => on_tmp_dir_exists().unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let should_show_gui = Arc::new(AtomicBool::new(!start_closed));
|
||||||
|
let should = should_show_gui.clone();
|
||||||
|
spawn(move || {
|
||||||
|
// Loop is blocked here until a single byte is read
|
||||||
|
loop {
|
||||||
|
let mut buf = [0u8; 4];
|
||||||
|
if get_ipc_file().unwrap().read(&mut buf).is_ok() && buf[0] == SHOW_GUI {
|
||||||
|
should_show_gui.store(true, Ordering::SeqCst);
|
||||||
|
// Give the starting app a change to read or we'll race it
|
||||||
|
std::thread::sleep(Duration::from_millis(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
eframe::run_native(
|
||||||
|
"ROG Control Center",
|
||||||
|
native_options,
|
||||||
|
Box::new(move |cc| {
|
||||||
|
Box::new(RogApp::new(start_closed, config, should, states, cc).unwrap())
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use rog_aura::{
|
||||||
|
usb::{AuraDev19b6, AuraDevice, AuraPowerDev},
|
||||||
|
AuraEffect, AuraModeNum, AuraZone,
|
||||||
|
};
|
||||||
|
use rog_profiles::fan_curve_set::{CurveData, FanCurveSet};
|
||||||
|
use rog_supported::{
|
||||||
|
AnimeSupportedFunctions, ChargeSupportedFunctions, LedSupportedFunctions,
|
||||||
|
PlatformProfileFunctions, RogBiosSupportedFunctions, SupportedFunctions,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::error::Result;
|
||||||
|
|
||||||
|
const NOPE: &'static str = "";
|
||||||
|
|
||||||
|
pub struct RogDbusClientBlocking<'a> {
|
||||||
|
_phantom: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Default for RogDbusClientBlocking<'a> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
_phantom: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RogDbusClientBlocking<'a> {
|
||||||
|
pub fn new() -> Result<(Self, bool)> {
|
||||||
|
Ok((Self { _phantom: NOPE }, true))
|
||||||
|
}
|
||||||
|
pub fn proxies(&self) -> Proxies {
|
||||||
|
Proxies
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Proxies;
|
||||||
|
impl Proxies {
|
||||||
|
pub fn rog_bios(&self) -> Bios {
|
||||||
|
Bios
|
||||||
|
}
|
||||||
|
pub fn profile(&self) -> Profile {
|
||||||
|
Profile
|
||||||
|
}
|
||||||
|
pub fn led(&self) -> Led {
|
||||||
|
Led
|
||||||
|
}
|
||||||
|
pub fn anime(&self) -> Anime {
|
||||||
|
Anime
|
||||||
|
}
|
||||||
|
pub fn charge(&self) -> Profile {
|
||||||
|
Profile
|
||||||
|
}
|
||||||
|
pub fn supported(&self) -> Supported {
|
||||||
|
Supported
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Bios;
|
||||||
|
impl Bios {
|
||||||
|
pub fn post_boot_sound(&self) -> Result<i16> {
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
|
pub fn dedicated_graphic_mode(&self) -> Result<i16> {
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
|
pub fn panel_overdrive(&self) -> Result<i16> {
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
|
pub fn set_post_boot_sound(&self, _b: bool) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn set_dedicated_graphic_mode(&self, _b: bool) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn set_panel_overdrive(&self, _b: bool) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Profile;
|
||||||
|
impl Profile {
|
||||||
|
pub fn profiles(&self) -> Result<Vec<rog_profiles::Profile>> {
|
||||||
|
Ok(vec![
|
||||||
|
rog_profiles::Profile::Balanced,
|
||||||
|
rog_profiles::Profile::Performance,
|
||||||
|
rog_profiles::Profile::Quiet,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
pub fn active_profile(&self) -> Result<rog_profiles::Profile> {
|
||||||
|
Ok(rog_profiles::Profile::Performance)
|
||||||
|
}
|
||||||
|
pub fn enabled_fan_profiles(&self) -> Result<Vec<rog_profiles::Profile>> {
|
||||||
|
Ok(vec![
|
||||||
|
rog_profiles::Profile::Performance,
|
||||||
|
rog_profiles::Profile::Balanced,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
pub fn fan_curve_data(&self, _p: rog_profiles::Profile) -> Result<FanCurveSet> {
|
||||||
|
let mut curve = FanCurveSet::default();
|
||||||
|
curve.cpu.pwm = [30, 40, 60, 100, 140, 180, 200, 250];
|
||||||
|
curve.cpu.temp = [20, 30, 40, 50, 70, 80, 90, 100];
|
||||||
|
curve.gpu.pwm = [40, 80, 100, 140, 170, 200, 230, 250];
|
||||||
|
curve.gpu.temp = [20, 30, 40, 50, 70, 80, 90, 100];
|
||||||
|
Ok(curve)
|
||||||
|
}
|
||||||
|
pub fn set_fan_curve(&self, _p: rog_profiles::Profile, _c: CurveData) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn set_fan_curve_enabled(&self, _p: rog_profiles::Profile, _b: bool) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn limit(&self) -> Result<i16> {
|
||||||
|
Ok(66)
|
||||||
|
}
|
||||||
|
pub fn set_limit(&self, _l: u8) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn set_active_profile(&self, _p: rog_profiles::Profile) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Led;
|
||||||
|
impl Led {
|
||||||
|
pub fn led_modes(&self) -> Result<BTreeMap<AuraModeNum, AuraEffect>> {
|
||||||
|
let mut data = BTreeMap::new();
|
||||||
|
data.insert(AuraModeNum::Static, AuraEffect::default());
|
||||||
|
data.insert(AuraModeNum::Star, AuraEffect::default());
|
||||||
|
data.insert(AuraModeNum::Strobe, AuraEffect::default());
|
||||||
|
data.insert(AuraModeNum::Rain, AuraEffect::default());
|
||||||
|
data.insert(AuraModeNum::Rainbow, AuraEffect::default());
|
||||||
|
data.insert(AuraModeNum::Ripple, AuraEffect::default());
|
||||||
|
data.insert(AuraModeNum::Breathe, AuraEffect::default());
|
||||||
|
data.insert(AuraModeNum::Comet, AuraEffect::default());
|
||||||
|
data.insert(AuraModeNum::Flash, AuraEffect::default());
|
||||||
|
data.insert(AuraModeNum::Laser, AuraEffect::default());
|
||||||
|
data.insert(AuraModeNum::Pulse, AuraEffect::default());
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
pub fn led_mode(&self) -> Result<AuraModeNum> {
|
||||||
|
Ok(AuraModeNum::Rainbow)
|
||||||
|
}
|
||||||
|
pub fn led_brightness(&self) -> Result<i16> {
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
|
pub fn leds_enabled(&self) -> Result<AuraPowerDev> {
|
||||||
|
Ok(AuraPowerDev {
|
||||||
|
tuf: vec![],
|
||||||
|
x1866: vec![],
|
||||||
|
x19b6: vec![
|
||||||
|
AuraDev19b6::BootKeyb,
|
||||||
|
AuraDev19b6::AwakeKeyb,
|
||||||
|
AuraDev19b6::SleepLogo,
|
||||||
|
AuraDev19b6::AwakeLogo,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn set_leds_power(&self, _a: AuraPowerDev, _b: bool) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn set_led_mode(&self, _a: &AuraEffect) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Anime;
|
||||||
|
impl Anime {
|
||||||
|
pub fn boot_enabled(&self) -> Result<bool> {
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
pub fn awake_enabled(&self) -> Result<bool> {
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
pub fn set_on_off(&self, _b: bool) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn set_boot_on_off(&self, _b: bool) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Supported;
|
||||||
|
impl Supported {
|
||||||
|
pub fn supported_functions(&self) -> Result<SupportedFunctions> {
|
||||||
|
Ok(SupportedFunctions {
|
||||||
|
anime_ctrl: AnimeSupportedFunctions(true),
|
||||||
|
charge_ctrl: ChargeSupportedFunctions {
|
||||||
|
charge_level_set: true,
|
||||||
|
},
|
||||||
|
platform_profile: PlatformProfileFunctions {
|
||||||
|
platform_profile: true,
|
||||||
|
fan_curves: true,
|
||||||
|
},
|
||||||
|
keyboard_led: LedSupportedFunctions {
|
||||||
|
prod_id: AuraDevice::X19B6,
|
||||||
|
brightness_set: true,
|
||||||
|
stock_led_modes: vec![
|
||||||
|
AuraModeNum::Rain,
|
||||||
|
AuraModeNum::Rainbow,
|
||||||
|
AuraModeNum::Star,
|
||||||
|
AuraModeNum::Static,
|
||||||
|
AuraModeNum::Strobe,
|
||||||
|
],
|
||||||
|
multizone_led_mode: vec![
|
||||||
|
AuraZone::Key1,
|
||||||
|
AuraZone::Key2,
|
||||||
|
AuraZone::Key3,
|
||||||
|
AuraZone::Key4,
|
||||||
|
AuraZone::BarLeft,
|
||||||
|
AuraZone::BarRight,
|
||||||
|
AuraZone::Logo,
|
||||||
|
],
|
||||||
|
per_key_led_mode: true,
|
||||||
|
},
|
||||||
|
rog_bios_ctrl: RogBiosSupportedFunctions {
|
||||||
|
post_sound: true,
|
||||||
|
dedicated_gfx: true,
|
||||||
|
panel_overdrive: true,
|
||||||
|
dgpu_disable: true,
|
||||||
|
egpu_enable: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,256 @@
|
|||||||
|
//TODO: a lot of app state refresh depends on this so there needs
|
||||||
|
// to be an extra AtomicBool for checking if notifications are enabled
|
||||||
|
|
||||||
|
use notify_rust::{Hint, Notification, NotificationHandle};
|
||||||
|
use rog_aura::AuraEffect;
|
||||||
|
use rog_dbus::{
|
||||||
|
zbus_anime::AnimeProxy, zbus_charge::ChargeProxy, zbus_led::LedProxy,
|
||||||
|
zbus_profile::ProfileProxy, zbus_rogbios::RogBiosProxy,
|
||||||
|
};
|
||||||
|
use rog_profiles::Profile;
|
||||||
|
use smol::{future, Executor};
|
||||||
|
use std::{
|
||||||
|
error::Error,
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc, Mutex,
|
||||||
|
},
|
||||||
|
thread::spawn,
|
||||||
|
};
|
||||||
|
use zbus::export::futures_util::StreamExt;
|
||||||
|
|
||||||
|
const NOTIF_HEADER: &str = "ROG Control";
|
||||||
|
|
||||||
|
macro_rules! notify {
|
||||||
|
($notifier:ident, $last_notif:ident, $data:expr) => {
|
||||||
|
if let Some(notif) = $last_notif.take() {
|
||||||
|
notif.close();
|
||||||
|
}
|
||||||
|
if let Ok(x) = $notifier($data) {
|
||||||
|
$last_notif.replace(x);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! base_notification {
|
||||||
|
($body:expr) => {
|
||||||
|
Notification::new()
|
||||||
|
.summary(NOTIF_HEADER)
|
||||||
|
.body($body)
|
||||||
|
.timeout(2000)
|
||||||
|
.show()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
type SharedHandle = Arc<Mutex<Option<NotificationHandle>>>;
|
||||||
|
|
||||||
|
pub fn start_notifications(
|
||||||
|
charge_notified: Arc<AtomicBool>,
|
||||||
|
bios_notified: Arc<AtomicBool>,
|
||||||
|
aura_notified: Arc<AtomicBool>,
|
||||||
|
anime_notified: Arc<AtomicBool>,
|
||||||
|
profiles_notified: Arc<AtomicBool>,
|
||||||
|
_fans_notified: Arc<AtomicBool>,
|
||||||
|
notifs_enabled: Arc<AtomicBool>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let last_notification: SharedHandle = Arc::new(Mutex::new(None));
|
||||||
|
|
||||||
|
let executor = Executor::new();
|
||||||
|
// BIOS notif
|
||||||
|
let last_notif = last_notification.clone();
|
||||||
|
let notifs_enabled1 = notifs_enabled.clone();
|
||||||
|
let bios_notified1 = bios_notified.clone();
|
||||||
|
// TODO: make a macro or generic function or something...
|
||||||
|
executor
|
||||||
|
.spawn(async move {
|
||||||
|
let conn = zbus::Connection::system().await.unwrap();
|
||||||
|
let proxy = RogBiosProxy::new(&conn).await.unwrap();
|
||||||
|
if let Ok(p) = proxy.receive_notify_post_boot_sound().await {
|
||||||
|
p.for_each(|e| {
|
||||||
|
if let Ok(out) = e.args() {
|
||||||
|
if notifs_enabled1.load(Ordering::SeqCst) {
|
||||||
|
if let Ok(ref mut lock) = last_notif.try_lock() {
|
||||||
|
notify!(do_post_sound_notif, lock, &out.sound());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bios_notified1.store(true, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
future::ready(())
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
executor
|
||||||
|
.spawn(async move {
|
||||||
|
let conn = zbus::Connection::system().await.unwrap();
|
||||||
|
let proxy = RogBiosProxy::new(&conn).await.unwrap();
|
||||||
|
if let Ok(p) = proxy.receive_notify_panel_overdrive().await {
|
||||||
|
p.for_each(|_| {
|
||||||
|
bios_notified.store(true, Ordering::SeqCst);
|
||||||
|
future::ready(())
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
// Charge notif
|
||||||
|
let last_notif = last_notification.clone();
|
||||||
|
let notifs_enabled1 = notifs_enabled.clone();
|
||||||
|
executor
|
||||||
|
.spawn(async move {
|
||||||
|
let conn = zbus::Connection::system().await.unwrap();
|
||||||
|
let proxy = ChargeProxy::new(&conn).await.unwrap();
|
||||||
|
if let Ok(p) = proxy.receive_notify_charge().await {
|
||||||
|
p.for_each(|e| {
|
||||||
|
if let Ok(out) = e.args() {
|
||||||
|
if notifs_enabled1.load(Ordering::SeqCst) {
|
||||||
|
if let Ok(ref mut lock) = last_notif.try_lock() {
|
||||||
|
notify!(do_charge_notif, lock, &out.limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
charge_notified.store(true, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
future::ready(())
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
// Profile notif
|
||||||
|
let last_notif = last_notification.clone();
|
||||||
|
let notifs_enabled1 = notifs_enabled.clone();
|
||||||
|
executor
|
||||||
|
.spawn(async move {
|
||||||
|
let conn = zbus::Connection::system().await.unwrap();
|
||||||
|
let proxy = ProfileProxy::new(&conn).await.unwrap();
|
||||||
|
if let Ok(p) = proxy.receive_notify_profile().await {
|
||||||
|
p.for_each(|e| {
|
||||||
|
if let Ok(out) = e.args() {
|
||||||
|
if notifs_enabled1.load(Ordering::SeqCst) {
|
||||||
|
if let Ok(ref mut lock) = last_notif.try_lock() {
|
||||||
|
notify!(do_thermal_notif, lock, &out.profile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
profiles_notified.store(true, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
future::ready(())
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
// LED notif
|
||||||
|
let last_notif = last_notification.clone();
|
||||||
|
let aura_notif = aura_notified.clone();
|
||||||
|
let notifs_enabled1 = notifs_enabled.clone();
|
||||||
|
executor
|
||||||
|
.spawn(async move {
|
||||||
|
let conn = zbus::Connection::system().await.unwrap();
|
||||||
|
let proxy = LedProxy::new(&conn).await.unwrap();
|
||||||
|
if let Ok(p) = proxy.receive_notify_led().await {
|
||||||
|
p.for_each(|e| {
|
||||||
|
if let Ok(out) = e.args() {
|
||||||
|
if notifs_enabled1.load(Ordering::SeqCst) {
|
||||||
|
if let Ok(ref mut lock) = last_notif.try_lock() {
|
||||||
|
notify!(do_led_notif, lock, &out.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aura_notif.store(true, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
future::ready(())
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
let aura_notif = aura_notified.clone();
|
||||||
|
executor
|
||||||
|
.spawn(async move {
|
||||||
|
let conn = zbus::Connection::system().await.unwrap();
|
||||||
|
let proxy = LedProxy::new(&conn).await.unwrap();
|
||||||
|
if let Ok(p) = proxy.receive_notify_led().await {
|
||||||
|
p.for_each(|_| {
|
||||||
|
aura_notif.store(true, Ordering::SeqCst);
|
||||||
|
future::ready(())
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
executor
|
||||||
|
.spawn(async move {
|
||||||
|
let conn = zbus::Connection::system().await.unwrap();
|
||||||
|
let proxy = LedProxy::new(&conn).await.unwrap();
|
||||||
|
if let Ok(p) = proxy.receive_all_signals().await {
|
||||||
|
p.for_each(|_| {
|
||||||
|
aura_notified.store(true, Ordering::SeqCst);
|
||||||
|
future::ready(())
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
executor
|
||||||
|
.spawn(async move {
|
||||||
|
let conn = zbus::Connection::system().await.unwrap();
|
||||||
|
let proxy = AnimeProxy::new(&conn).await.unwrap();
|
||||||
|
if let Ok(p) = proxy.receive_power_states().await {
|
||||||
|
p.for_each(|_| {
|
||||||
|
anime_notified.store(true, Ordering::SeqCst);
|
||||||
|
future::ready(())
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
spawn(move || loop {
|
||||||
|
smol::block_on(executor.tick());
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_thermal_notif(profile: &Profile) -> Result<NotificationHandle, Box<dyn Error>> {
|
||||||
|
let icon = match profile {
|
||||||
|
Profile::Balanced => "asus_notif_yellow",
|
||||||
|
Profile::Performance => "asus_notif_red",
|
||||||
|
Profile::Quiet => "asus_notif_green",
|
||||||
|
};
|
||||||
|
let profile: &str = (*profile).into();
|
||||||
|
let x = Notification::new()
|
||||||
|
.summary("ASUS ROG")
|
||||||
|
.body(&format!(
|
||||||
|
"Thermal profile changed to {}",
|
||||||
|
profile.to_uppercase(),
|
||||||
|
))
|
||||||
|
.hint(Hint::Resident(true))
|
||||||
|
.timeout(2000)
|
||||||
|
.hint(Hint::Category("device".into()))
|
||||||
|
//.hint(Hint::Transient(true))
|
||||||
|
.icon(icon)
|
||||||
|
.show()?;
|
||||||
|
Ok(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_led_notif(ledmode: &AuraEffect) -> Result<NotificationHandle, notify_rust::error::Error> {
|
||||||
|
base_notification!(&format!(
|
||||||
|
"Keyboard LED mode changed to {}",
|
||||||
|
ledmode.mode_name()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_charge_notif(limit: &u8) -> Result<NotificationHandle, notify_rust::error::Error> {
|
||||||
|
base_notification!(&format!("Battery charge limit changed to {}", limit))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_post_sound_notif(on: &bool) -> Result<NotificationHandle, notify_rust::error::Error> {
|
||||||
|
base_notification!(&format!("BIOS Post sound {}", on))
|
||||||
|
}
|
||||||
@@ -0,0 +1,383 @@
|
|||||||
|
use std::{
|
||||||
|
collections::{BTreeMap, HashMap, HashSet},
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use egui::Vec2;
|
||||||
|
use rog_aura::{layouts::KeyLayout, usb::AuraPowerDev, AuraEffect, AuraModeNum};
|
||||||
|
use rog_profiles::{fan_curve_set::FanCurveSet, FanCurvePU, Profile};
|
||||||
|
use rog_supported::SupportedFunctions;
|
||||||
|
|
||||||
|
use crate::{error::Result, RogDbusClientBlocking};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct BiosState {
|
||||||
|
/// To be shared to a thread that checks notifications.
|
||||||
|
/// It's a bit general in that it won't provide *what* was
|
||||||
|
/// updated, so the full state needs refresh
|
||||||
|
pub was_notified: Arc<AtomicBool>,
|
||||||
|
pub post_sound: bool,
|
||||||
|
pub dedicated_gfx: bool,
|
||||||
|
pub panel_overdrive: bool,
|
||||||
|
pub dgpu_disable: bool,
|
||||||
|
pub egpu_enable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BiosState {
|
||||||
|
pub fn new(
|
||||||
|
was_notified: Arc<AtomicBool>,
|
||||||
|
supported: &SupportedFunctions,
|
||||||
|
dbus: &RogDbusClientBlocking,
|
||||||
|
) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
was_notified,
|
||||||
|
post_sound: if supported.rog_bios_ctrl.post_sound {
|
||||||
|
dbus.proxies().rog_bios().post_boot_sound()? != 0
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
},
|
||||||
|
dedicated_gfx: if supported.rog_bios_ctrl.dedicated_gfx {
|
||||||
|
dbus.proxies().rog_bios().dedicated_graphic_mode()? != 0
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
},
|
||||||
|
panel_overdrive: if supported.rog_bios_ctrl.panel_overdrive {
|
||||||
|
dbus.proxies().rog_bios().panel_overdrive()? != 0
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
},
|
||||||
|
// TODO: needs supergfx
|
||||||
|
dgpu_disable: supported.rog_bios_ctrl.dgpu_disable,
|
||||||
|
egpu_enable: supported.rog_bios_ctrl.egpu_enable,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ProfilesState {
|
||||||
|
pub was_notified: Arc<AtomicBool>,
|
||||||
|
pub list: Vec<Profile>,
|
||||||
|
pub current: Profile,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProfilesState {
|
||||||
|
pub fn new(
|
||||||
|
was_notified: Arc<AtomicBool>,
|
||||||
|
supported: &SupportedFunctions,
|
||||||
|
dbus: &RogDbusClientBlocking,
|
||||||
|
) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
was_notified,
|
||||||
|
list: if supported.platform_profile.platform_profile {
|
||||||
|
dbus.proxies().profile().profiles()?
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
},
|
||||||
|
current: if supported.platform_profile.platform_profile {
|
||||||
|
dbus.proxies().profile().active_profile()?
|
||||||
|
} else {
|
||||||
|
Profile::Balanced
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct FanCurvesState {
|
||||||
|
pub was_notified: Arc<AtomicBool>,
|
||||||
|
pub show_curve: Profile,
|
||||||
|
pub show_graph: FanCurvePU,
|
||||||
|
pub enabled: HashSet<Profile>,
|
||||||
|
pub curves: HashMap<Profile, FanCurveSet>,
|
||||||
|
pub drag_delta: Vec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FanCurvesState {
|
||||||
|
pub fn new(
|
||||||
|
was_notified: Arc<AtomicBool>,
|
||||||
|
supported: &SupportedFunctions,
|
||||||
|
dbus: &RogDbusClientBlocking,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let profiles = if supported.platform_profile.platform_profile {
|
||||||
|
dbus.proxies().profile().profiles()?
|
||||||
|
} else {
|
||||||
|
vec![Profile::Balanced, Profile::Quiet, Profile::Performance]
|
||||||
|
};
|
||||||
|
let enabled = if supported.platform_profile.fan_curves {
|
||||||
|
HashSet::from_iter(
|
||||||
|
dbus.proxies()
|
||||||
|
.profile()
|
||||||
|
.enabled_fan_profiles()?
|
||||||
|
.iter()
|
||||||
|
.cloned(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
HashSet::from([Profile::Balanced, Profile::Quiet, Profile::Performance])
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut curves: HashMap<Profile, FanCurveSet> = HashMap::new();
|
||||||
|
profiles.iter().for_each(|p| {
|
||||||
|
if supported.platform_profile.fan_curves {
|
||||||
|
if let Ok(curve) = dbus.proxies().profile().fan_curve_data(*p) {
|
||||||
|
curves.insert(*p, curve);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut curve = FanCurveSet::default();
|
||||||
|
curve.cpu.pwm = [30, 40, 60, 100, 140, 180, 200, 250];
|
||||||
|
curve.cpu.temp = [20, 30, 40, 50, 70, 80, 90, 100];
|
||||||
|
curve.gpu.pwm = [40, 80, 100, 140, 170, 200, 230, 250];
|
||||||
|
curve.gpu.temp = [20, 30, 40, 50, 70, 80, 90, 100];
|
||||||
|
curves.insert(*p, curve);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let show_curve = if supported.platform_profile.fan_curves {
|
||||||
|
dbus.proxies().profile().active_profile()?
|
||||||
|
} else {
|
||||||
|
Profile::Balanced
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
was_notified,
|
||||||
|
show_curve,
|
||||||
|
show_graph: FanCurvePU::CPU,
|
||||||
|
enabled,
|
||||||
|
curves,
|
||||||
|
drag_delta: Vec2::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct AuraState {
|
||||||
|
pub was_notified: Arc<AtomicBool>,
|
||||||
|
pub current_mode: AuraModeNum,
|
||||||
|
pub modes: BTreeMap<AuraModeNum, AuraEffect>,
|
||||||
|
pub enabled: AuraPowerDev,
|
||||||
|
/// Brightness from 0-3
|
||||||
|
pub bright: i16,
|
||||||
|
pub wave_red: [u8; 22],
|
||||||
|
pub wave_green: [u8; 22],
|
||||||
|
pub wave_blue: [u8; 22],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuraState {
|
||||||
|
pub fn new(
|
||||||
|
was_notified: Arc<AtomicBool>,
|
||||||
|
supported: &SupportedFunctions,
|
||||||
|
dbus: &RogDbusClientBlocking,
|
||||||
|
) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
was_notified,
|
||||||
|
current_mode: if !supported.keyboard_led.stock_led_modes.is_empty() {
|
||||||
|
dbus.proxies().led().led_mode().unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
AuraModeNum::Static
|
||||||
|
},
|
||||||
|
|
||||||
|
modes: if !supported.keyboard_led.stock_led_modes.is_empty() {
|
||||||
|
dbus.proxies().led().led_modes().unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
BTreeMap::new()
|
||||||
|
},
|
||||||
|
enabled: dbus.proxies().led().leds_enabled().unwrap_or_default(),
|
||||||
|
bright: if !supported.keyboard_led.brightness_set {
|
||||||
|
dbus.proxies().led().led_brightness().unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
2
|
||||||
|
},
|
||||||
|
wave_red: [0u8; 22],
|
||||||
|
wave_green: [0u8; 22],
|
||||||
|
wave_blue: [0u8; 22],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bump value in to the wave and surf all along.
|
||||||
|
pub fn nudge_wave(&mut self, r: u8, g: u8, b: u8) {
|
||||||
|
for i in (0..self.wave_red.len()).rev() {
|
||||||
|
if i > 0 {
|
||||||
|
self.wave_red[i] = self.wave_red[i - 1];
|
||||||
|
self.wave_green[i] = self.wave_green[i - 1];
|
||||||
|
self.wave_blue[i] = self.wave_blue[i - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.wave_red[0] = r;
|
||||||
|
self.wave_green[0] = g;
|
||||||
|
self.wave_blue[0] = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct AnimeState {
|
||||||
|
pub was_notified: Arc<AtomicBool>,
|
||||||
|
pub bright: u8,
|
||||||
|
pub boot: bool,
|
||||||
|
pub awake: bool,
|
||||||
|
pub sleep: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnimeState {
|
||||||
|
pub fn new(
|
||||||
|
was_notified: Arc<AtomicBool>,
|
||||||
|
supported: &SupportedFunctions,
|
||||||
|
dbus: &RogDbusClientBlocking,
|
||||||
|
) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
was_notified,
|
||||||
|
boot: if supported.anime_ctrl.0 {
|
||||||
|
dbus.proxies().anime().boot_enabled()?
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
},
|
||||||
|
awake: if supported.anime_ctrl.0 {
|
||||||
|
dbus.proxies().anime().awake_enabled()?
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
},
|
||||||
|
// TODO:
|
||||||
|
sleep: false,
|
||||||
|
bright: 200,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PageDataStates {
|
||||||
|
pub keyboard_layout: KeyLayout,
|
||||||
|
pub notifs_enabled: Arc<AtomicBool>,
|
||||||
|
pub was_notified: Arc<AtomicBool>,
|
||||||
|
/// Because much of the app state here is the same as `RogBiosSupportedFunctions`
|
||||||
|
/// we can re-use that structure.
|
||||||
|
pub bios: BiosState,
|
||||||
|
pub aura: AuraState,
|
||||||
|
pub anime: AnimeState,
|
||||||
|
pub profiles: ProfilesState,
|
||||||
|
pub fan_curves: FanCurvesState,
|
||||||
|
pub charge_limit: i16,
|
||||||
|
pub error: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PageDataStates {
|
||||||
|
pub fn new(
|
||||||
|
keyboard_layout: KeyLayout,
|
||||||
|
notifs_enabled: Arc<AtomicBool>,
|
||||||
|
charge_notified: Arc<AtomicBool>,
|
||||||
|
bios_notified: Arc<AtomicBool>,
|
||||||
|
aura_notified: Arc<AtomicBool>,
|
||||||
|
anime_notified: Arc<AtomicBool>,
|
||||||
|
profiles_notified: Arc<AtomicBool>,
|
||||||
|
fans_notified: Arc<AtomicBool>,
|
||||||
|
supported: &SupportedFunctions,
|
||||||
|
dbus: &RogDbusClientBlocking,
|
||||||
|
) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
keyboard_layout,
|
||||||
|
notifs_enabled,
|
||||||
|
was_notified: charge_notified,
|
||||||
|
charge_limit: dbus.proxies().charge().limit()?,
|
||||||
|
bios: BiosState::new(bios_notified, supported, dbus)?,
|
||||||
|
aura: AuraState::new(aura_notified, supported, dbus)?,
|
||||||
|
anime: AnimeState::new(anime_notified, supported, dbus)?,
|
||||||
|
profiles: ProfilesState::new(profiles_notified, supported, dbus)?,
|
||||||
|
fan_curves: FanCurvesState::new(fans_notified, supported, dbus)?,
|
||||||
|
error: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh_if_notfied(
|
||||||
|
&mut self,
|
||||||
|
supported: &SupportedFunctions,
|
||||||
|
dbus: &RogDbusClientBlocking,
|
||||||
|
) -> Result<bool> {
|
||||||
|
let mut notified = false;
|
||||||
|
if self.was_notified.load(Ordering::SeqCst) {
|
||||||
|
self.charge_limit = dbus.proxies().charge().limit()?;
|
||||||
|
self.was_notified.store(false, Ordering::SeqCst);
|
||||||
|
notified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.aura.was_notified.load(Ordering::SeqCst) {
|
||||||
|
self.aura = AuraState::new(self.aura.was_notified.clone(), supported, dbus)?;
|
||||||
|
self.aura.was_notified.store(false, Ordering::SeqCst);
|
||||||
|
notified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.bios.was_notified.load(Ordering::SeqCst) {
|
||||||
|
self.bios = BiosState::new(self.bios.was_notified.clone(), supported, dbus)?;
|
||||||
|
self.bios.was_notified.store(false, Ordering::SeqCst);
|
||||||
|
notified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.profiles.was_notified.load(Ordering::SeqCst) {
|
||||||
|
self.profiles =
|
||||||
|
ProfilesState::new(self.profiles.was_notified.clone(), supported, dbus)?;
|
||||||
|
self.profiles.was_notified.store(false, Ordering::SeqCst);
|
||||||
|
notified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.fan_curves.was_notified.load(Ordering::SeqCst) {
|
||||||
|
self.fan_curves =
|
||||||
|
FanCurvesState::new(self.fan_curves.was_notified.clone(), supported, dbus)?;
|
||||||
|
self.fan_curves.was_notified.store(false, Ordering::SeqCst);
|
||||||
|
notified = true;
|
||||||
|
}
|
||||||
|
Ok(notified)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PageDataStates {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
keyboard_layout: KeyLayout::ga401_layout(),
|
||||||
|
notifs_enabled: Default::default(),
|
||||||
|
was_notified: Default::default(),
|
||||||
|
bios: BiosState {
|
||||||
|
was_notified: Default::default(),
|
||||||
|
post_sound: Default::default(),
|
||||||
|
dedicated_gfx: Default::default(),
|
||||||
|
panel_overdrive: Default::default(),
|
||||||
|
dgpu_disable: Default::default(),
|
||||||
|
egpu_enable: Default::default(),
|
||||||
|
},
|
||||||
|
aura: AuraState {
|
||||||
|
was_notified: Default::default(),
|
||||||
|
current_mode: AuraModeNum::Static,
|
||||||
|
modes: Default::default(),
|
||||||
|
enabled: AuraPowerDev {
|
||||||
|
tuf: vec![],
|
||||||
|
x1866: vec![],
|
||||||
|
x19b6: vec![],
|
||||||
|
},
|
||||||
|
bright: Default::default(),
|
||||||
|
wave_red: Default::default(),
|
||||||
|
wave_green: Default::default(),
|
||||||
|
wave_blue: Default::default(),
|
||||||
|
},
|
||||||
|
anime: AnimeState {
|
||||||
|
was_notified: Default::default(),
|
||||||
|
bright: Default::default(),
|
||||||
|
boot: Default::default(),
|
||||||
|
awake: Default::default(),
|
||||||
|
sleep: Default::default(),
|
||||||
|
},
|
||||||
|
profiles: ProfilesState {
|
||||||
|
was_notified: Default::default(),
|
||||||
|
list: Default::default(),
|
||||||
|
current: Default::default(),
|
||||||
|
},
|
||||||
|
fan_curves: FanCurvesState {
|
||||||
|
was_notified: Default::default(),
|
||||||
|
show_curve: Default::default(),
|
||||||
|
show_graph: Default::default(),
|
||||||
|
enabled: Default::default(),
|
||||||
|
curves: Default::default(),
|
||||||
|
drag_delta: Default::default(),
|
||||||
|
},
|
||||||
|
charge_limit: Default::default(),
|
||||||
|
error: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
use crate::RogApp;
|
||||||
|
|
||||||
|
impl<'a> RogApp<'a> {
|
||||||
|
pub fn anime_page(&mut self, ctx: &egui::Context) {
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
ui.label("In progress");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
use std::{sync::atomic::Ordering, time::Duration};
|
||||||
|
|
||||||
|
use egui::Color32;
|
||||||
|
use rog_aura::AuraModeNum;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
widgets::{aura_modes_group, keyboard},
|
||||||
|
RogApp,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<'a> RogApp<'a> {
|
||||||
|
pub fn aura_page(&mut self, ctx: &egui::Context) {
|
||||||
|
let Self {
|
||||||
|
supported,
|
||||||
|
states,
|
||||||
|
asus_dbus: dbus,
|
||||||
|
oscillator1,
|
||||||
|
oscillator2,
|
||||||
|
oscillator3,
|
||||||
|
oscillator_freq,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
let red = oscillator1.load(Ordering::SeqCst) as u32;
|
||||||
|
let green = oscillator2.load(Ordering::SeqCst) as u32;
|
||||||
|
let blue = oscillator3.load(Ordering::SeqCst) as u32;
|
||||||
|
states.aura.nudge_wave(red as u8, green as u8, blue as u8);
|
||||||
|
// let osc = c.0 * 255 / osc;
|
||||||
|
// dbg!(osc);
|
||||||
|
let c1 = states
|
||||||
|
.aura
|
||||||
|
.modes
|
||||||
|
.get(&states.aura.current_mode)
|
||||||
|
.unwrap()
|
||||||
|
.colour1;
|
||||||
|
|
||||||
|
let c2 = states
|
||||||
|
.aura
|
||||||
|
.modes
|
||||||
|
.get(&states.aura.current_mode)
|
||||||
|
.unwrap()
|
||||||
|
.colour2;
|
||||||
|
|
||||||
|
let mut colour = Color32::from_rgb(c1.0, c1.1, c1.2);
|
||||||
|
if states.aura.current_mode == AuraModeNum::Pulse {
|
||||||
|
colour = Color32::from_rgb(
|
||||||
|
(red * c1.0 as u32 / 100) as u8,
|
||||||
|
(red * c1.1 as u32 / 100) as u8,
|
||||||
|
(red * c1.2 as u32 / 100) as u8,
|
||||||
|
);
|
||||||
|
} else if states.aura.current_mode == AuraModeNum::Breathe {
|
||||||
|
if self.oscillator_toggle.load(Ordering::SeqCst) {
|
||||||
|
colour = Color32::from_rgb(
|
||||||
|
(red * c2.0 as u32 / 100) as u8,
|
||||||
|
(red * c2.1 as u32 / 100) as u8,
|
||||||
|
(red * c2.2 as u32 / 100) as u8,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
colour = Color32::from_rgb(
|
||||||
|
(red * c1.0 as u32 / 100) as u8,
|
||||||
|
(red * c1.1 as u32 / 100) as u8,
|
||||||
|
(red * c1.2 as u32 / 100) as u8,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if states.aura.current_mode == AuraModeNum::Strobe {
|
||||||
|
colour = Color32::from_rgb(
|
||||||
|
(red * 255 / 100) as u8,
|
||||||
|
(green * 255 / 100) as u8,
|
||||||
|
(blue * 255 / 100) as u8,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: animation of colour changes/periods/blending
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
aura_modes_group(supported, states, oscillator_freq, dbus, ui);
|
||||||
|
|
||||||
|
keyboard(ui, &states.keyboard_layout, &mut states.aura, colour);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Only do repaint request if on this page
|
||||||
|
ctx.request_repaint_after(Duration::from_millis(33));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
use crate::{
|
||||||
|
page_states::{FanCurvesState, ProfilesState},
|
||||||
|
widgets::fan_graphs,
|
||||||
|
RogApp, RogDbusClientBlocking,
|
||||||
|
};
|
||||||
|
use egui::Ui;
|
||||||
|
use rog_profiles::Profile;
|
||||||
|
use rog_supported::SupportedFunctions;
|
||||||
|
|
||||||
|
impl<'a> RogApp<'a> {
|
||||||
|
pub fn fan_curve_page(&mut self, ctx: &egui::Context) {
|
||||||
|
let Self {
|
||||||
|
supported,
|
||||||
|
states,
|
||||||
|
asus_dbus: dbus,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
ui.heading("Custom fan curves");
|
||||||
|
ui.label("A fan curve is only active when the related profile is active and the curve is enabled");
|
||||||
|
Self::fan_curve(
|
||||||
|
supported,
|
||||||
|
&mut states.profiles,
|
||||||
|
&mut states.fan_curves,
|
||||||
|
dbus, &mut states.error,
|
||||||
|
ui,
|
||||||
|
);
|
||||||
|
|
||||||
|
fan_graphs(&mut states.profiles, &mut states.fan_curves, dbus, &mut states.error, ui);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fan_curve(
|
||||||
|
supported: &SupportedFunctions,
|
||||||
|
profiles: &mut ProfilesState,
|
||||||
|
curves: &mut FanCurvesState,
|
||||||
|
dbus: &RogDbusClientBlocking,
|
||||||
|
do_error: &mut Option<String>,
|
||||||
|
ui: &mut Ui,
|
||||||
|
) {
|
||||||
|
ui.separator();
|
||||||
|
ui.label("Enabled fan-curves");
|
||||||
|
|
||||||
|
let mut changed = false;
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
let mut item = |p: Profile, curves: &mut FanCurvesState, mut checked: bool| {
|
||||||
|
if ui
|
||||||
|
.add(egui::Checkbox::new(&mut checked, format!("{:?}", p)))
|
||||||
|
.changed()
|
||||||
|
{
|
||||||
|
dbus.proxies()
|
||||||
|
.profile()
|
||||||
|
.set_fan_curve_enabled(p, checked)
|
||||||
|
.map_err(|err| {
|
||||||
|
*do_error = Some(err.to_string());
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
if !checked {
|
||||||
|
curves.enabled.remove(&p);
|
||||||
|
} else {
|
||||||
|
curves.enabled.insert(p);
|
||||||
|
}
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for f in profiles.list.iter() {
|
||||||
|
item(*f, curves, curves.enabled.contains(f));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if changed {
|
||||||
|
let notif = curves.was_notified.clone();
|
||||||
|
match FanCurvesState::new(notif, supported, dbus) {
|
||||||
|
Ok(f) => *curves = f,
|
||||||
|
Err(e) => *do_error = Some(e.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
mod anime_page;
|
||||||
|
mod aura_page;
|
||||||
|
mod fan_curve_page;
|
||||||
|
mod system_page;
|
||||||
|
|
||||||
|
pub use anime_page::*;
|
||||||
|
pub use aura_page::*;
|
||||||
|
pub use fan_curve_page::*;
|
||||||
|
pub use system_page::*;
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
use crate::{
|
||||||
|
widgets::{anime_power_group, aura_power_group, platform_profile, rog_bios_group},
|
||||||
|
RogApp,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<'a> RogApp<'a> {
|
||||||
|
pub fn system_page(&mut self, ctx: &egui::Context) {
|
||||||
|
let Self {
|
||||||
|
supported,
|
||||||
|
states,
|
||||||
|
asus_dbus: dbus,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
ui.heading("Experimental application for asusd");
|
||||||
|
|
||||||
|
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||||
|
ui.spacing_mut().item_spacing = egui::vec2(8.0, 10.0);
|
||||||
|
let rect = ui.available_rect_before_wrap();
|
||||||
|
egui::Grid::new("grid_of_bits")
|
||||||
|
.min_col_width(rect.width() / 2.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.separator();
|
||||||
|
if supported.platform_profile.platform_profile {
|
||||||
|
platform_profile(states, dbus, ui);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.separator();
|
||||||
|
aura_power_group(supported, states, dbus, ui);
|
||||||
|
});
|
||||||
|
ui.end_row();
|
||||||
|
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.separator();
|
||||||
|
rog_bios_group(supported, states, dbus, ui);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.separator();
|
||||||
|
if supported.anime_ctrl.0 {
|
||||||
|
anime_power_group(supported, states, dbus, ui);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ui.end_row();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
use egui::{Button, RichText};
|
||||||
|
|
||||||
|
pub struct AppErrorShow {
|
||||||
|
error: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppErrorShow {
|
||||||
|
pub fn new(error: String) -> Self {
|
||||||
|
Self { error }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl eframe::App for AppErrorShow {
|
||||||
|
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
ui.heading("ROG ERROR");
|
||||||
|
|
||||||
|
ui.centered_and_justified(|ui| {
|
||||||
|
ui.label(RichText::new(format!("The error was: {:?}", self.error)).size(22.0));
|
||||||
|
});
|
||||||
|
|
||||||
|
egui::TopBottomPanel::bottom("error_bar_2")
|
||||||
|
.default_height(26.0)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
|
||||||
|
if ui
|
||||||
|
.add(Button::new(RichText::new("Okay").size(20.0)))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
frame.quit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||