Compare commits
161 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 99dd052d54 | |||
| de9942609e | |||
| b939a9d331 | |||
| 0a565a7a5c | |||
| bfaa478a4a | |||
| f895a5623e | |||
| b7c869bd64 | |||
| 5f677bc3b9 | |||
| ccfadc2fcb | |||
| c6cc304a42 | |||
| 3d41a7978a | |||
| 43fc467d58 | |||
| 6aba60f604 | |||
| a13dedf500 | |||
| cb490f23e9 | |||
| 7dc8a743b9 | |||
| 52f3b5a7bf | |||
| e89e7ca10f | |||
| 2431dd9e93 | |||
| 453d3091c1 | |||
| 8db37491f3 | |||
| cf915b9e00 | |||
| 326ca37847 | |||
| 498e604531 | |||
| 60b7f3be69 | |||
| 6ceb5cf939 | |||
| 0ed97db4c1 | |||
| 8fcd05c2bb | |||
| 6de4590f27 | |||
| b097fd4da9 | |||
| 2a8e05707d | |||
| a54e112978 | |||
| 5785eb981d |
@@ -4,3 +4,4 @@ cargo-config
|
|||||||
.idea
|
.idea
|
||||||
vendor-*
|
vendor-*
|
||||||
vendor_*
|
vendor_*
|
||||||
|
.vscode-ctags
|
||||||
|
|||||||
@@ -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,115 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased ]
|
||||||
|
|
||||||
|
## [4.2.0] - 2022-07-16
|
||||||
|
### Added
|
||||||
|
- Support for GA402 Anime Matrix display (Author: I-Al-Istannen & Luke Jones)
|
||||||
|
- Support for power-config of all LED zones. See `asusctrl led-power --help` (Author: Luke Jones, With much help from: @MNS26)
|
||||||
|
- Full support for multizone LED <logo, keyboard, lightbar> (Author: Luke Jones, With much help from: @MNS26)
|
||||||
|
- Add ability to load extra data from `/etc/asusd/asusd-user-ledmodes.toml` for LED support if file exits
|
||||||
|
- Support for G513IM LED modes
|
||||||
|
- Support for GX703HS LED modes
|
||||||
|
### Changed
|
||||||
|
- Dbus interface for Aura config has been changed, all power control is done with `SetLedsEnabled` and `SetLedsDisabled`
|
||||||
|
- Data for anime-matrix now requires passing the laptop model as enum
|
||||||
|
- Extra unit tests for anime stuff to help verify things
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Support for GA503R LED modes
|
||||||
|
### Changed
|
||||||
|
- Refactor LED and AniMe tasks
|
||||||
|
- Reload keyboard brightness on resume from sleep/hiber
|
||||||
|
|
||||||
|
## [4.1.1] - 2022-06-21
|
||||||
|
### Changed
|
||||||
|
- Fixes to anime matrix system thread cancelation
|
||||||
|
|
||||||
|
## [4.1.0] - 2022-06-20
|
||||||
|
### Changed
|
||||||
|
- Huge refactor to use zbus 2.2 + zvariant 3.0 in system-daemon.
|
||||||
|
- Daemons with tasks now use `smol` for async ops.
|
||||||
|
- Fixes to fan-curve settings from CLI (Author: Armas Span)
|
||||||
|
- Add brightness to anime zbus notification
|
||||||
|
- Adjust how threads in AniMe matrix controller work
|
||||||
|
- Use proper power-state packet for keyboard LED's (Author: Martin Piffault)
|
||||||
|
### Added
|
||||||
|
- Support for GA402R LED modes
|
||||||
|
- Support for GU502LV LED modes
|
||||||
|
- Support for G512 LED modes
|
||||||
|
- Support for G513IC LED modes (Author: dada513)
|
||||||
|
- Support for G513QM LED modes (Author: Martin Piffault)
|
||||||
|
- Add side-LED toggle support (Author: Martin Piffault)
|
||||||
|
- Support reloading keyboard mode on wake (from sleep/hiber)
|
||||||
|
- Support reloading charge-level on wake (from sleep/hiber)
|
||||||
|
- Support running AniMe animation blocks on wake/sleep and boot/shutdown events
|
||||||
|
|
||||||
|
# [4.0.7] - 2021-12-19
|
||||||
|
### Changed
|
||||||
|
- Fix incorrect power-profile validation
|
||||||
|
- Update asusd-ledmodes.toml to support Asus Rog Strix G15 G513QE (@LordVicky)
|
||||||
|
- Update patch notes and links
|
||||||
|
|
||||||
|
# [4.0.6] - 2021-11-01
|
||||||
|
### Changed
|
||||||
|
- Fix CLI for bios toggles
|
||||||
|
### Added
|
||||||
|
- Extra commands for AniMe: pixel-image, gif, pixel-gif
|
||||||
|
|
||||||
|
# [4.0.5] - 2021-10-27
|
||||||
|
### Changed
|
||||||
|
- Convert fan curve percentage to 0-255 expected by kernel driver only if '%' char is used, otherwise the expected range for fan power is 0-255
|
||||||
|
- Use correct error in daemon for invalid charging limit
|
||||||
|
- Enforce charging limit values in range 20-100
|
||||||
|
### Added
|
||||||
|
- LED modes for G513QR
|
||||||
|
|
||||||
|
# [4.0.4] - 2021-10-02
|
||||||
|
### 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
|
||||||
|
### Added
|
||||||
|
- AniMe:
|
||||||
|
+ Support 8bit RGB, RGBA, 16bit Greyscalw, RGB, RGBA
|
||||||
|
+ add `AsusImage` type for slanted-template pixel-perfect images
|
||||||
|
+ `BREAKING:` plain `Image` with time period is changed and old anime configs break as a result (sorry)
|
||||||
|
- LED:
|
||||||
|
+ By popular request LED prev/next cycle is added
|
||||||
|
+ Add led modes for GX551Q
|
||||||
|
### BREAKING CHANGES
|
||||||
|
- Graphics control:
|
||||||
|
+ graphics control is pulled out of asusd and moved to new package; https://gitlab.com/asus-linux/supergfxctl
|
||||||
|
- Proflies:
|
||||||
|
+ profiles now depend on power-profile-daemon plus kernel patches for support of platform_profile
|
||||||
|
- if your system supports fan-curves you will also require upcoming kernel patches for this
|
||||||
|
+ profiles are now moved to a new file
|
||||||
|
+ fan-curves are only partially completed due to this release needing to be done sooner
|
||||||
|
|
||||||
# [3.7.2] - 2021-08-02
|
# [3.7.2] - 2021-08-02
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-types", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles"]
|
members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-supported", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
# thin = 57s, asusd = 9.0M
|
||||||
|
# fat = 72s, asusd = 6.4M
|
||||||
|
lto = "fat"
|
||||||
debug = false
|
debug = false
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
@@ -13,4 +15,4 @@ opt-level = 1
|
|||||||
|
|
||||||
[profile.bench]
|
[profile.bench]
|
||||||
debug = false
|
debug = false
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|||||||
@@ -16,57 +16,15 @@ but can also be used with non-asus laptops with reduced features.
|
|||||||
|
|
||||||
The functionality that `asusd` exposes is:
|
The functionality that `asusd` exposes is:
|
||||||
|
|
||||||
- graphics switching
|
|
||||||
- anime control
|
- anime control
|
||||||
- led keyboard control (aura)
|
- led keyboard control (aura)
|
||||||
- charge limiting
|
- charge limiting
|
||||||
- bios/efivar control
|
- bios/efivar control
|
||||||
- profiles (fan/cpu)
|
- power profile switching
|
||||||
|
- fan curves (if supported, this is auto-detected)
|
||||||
|
|
||||||
each of these will be detailed in sections.
|
each of these will be detailed in sections.
|
||||||
|
|
||||||
### Graphics switching
|
|
||||||
|
|
||||||
`asusd` can switch graphics modes between:
|
|
||||||
- `integrated`, uses the iGPU only and force-disables the dGPU
|
|
||||||
- `compute`, enables Nvidia without Xorg. Useful for ML/Cuda
|
|
||||||
- `hybrid`, enables Nvidia prime-offload mode
|
|
||||||
- `nvidia`, uses the Nvidia gpu only
|
|
||||||
- `vfio`, binds the Nvidia gpu to vfio for VM pass-through
|
|
||||||
|
|
||||||
Switching to/from Hybrid and Nvidia modes requires a logout only (no reboot). Switching between integrated/compute/vfio does not require a logout and is instant.
|
|
||||||
|
|
||||||
#### Required actions in distro
|
|
||||||
|
|
||||||
**Rebootless note:** You must edit `/etc/default/grub` to remove `nvidia-drm.modeset=1`
|
|
||||||
from the line `GRUB_CMDLINE_LINUX=` and then recreate your grub config. In fedora
|
|
||||||
you can do this with `sudo grub2-mkconfig -o /etc/grub2.cfg` - other distro may be
|
|
||||||
similar but with a different config location. It's possible that graphics driver updates
|
|
||||||
may change this.
|
|
||||||
|
|
||||||
This switcher conflicts with other gpu switchers like optimus-manager, suse-prime
|
|
||||||
or ubuntu-prime, system76-power, and bbswitch. If you have issues with `asusd`
|
|
||||||
always defaulting to `integrated` mode on boot then you will need to check for
|
|
||||||
stray configs blocking nvidia modules from loading in:
|
|
||||||
- `/etc/modprobe.d/`
|
|
||||||
- `/usr/lib/modprope.d/`
|
|
||||||
|
|
||||||
#### Config options
|
|
||||||
|
|
||||||
1. `"gfx_mode": "<MODE>",`: MODE can be <Integrated, Hybrid, Compute, Nvidia, vfio>
|
|
||||||
2. `"gfx_last_mode": "Nvidia",`: currently unused
|
|
||||||
3. `"gfx_managed": true,`: enable or disable graphics switching controller
|
|
||||||
4. `"gfx_vfio_enable": false,`: enable vfio switching for Nvidia GPU passthrough
|
|
||||||
5. `"gfx_save_compute_vfio": false,`: wether or not to save the vfio state (so it sticks between boots)
|
|
||||||
|
|
||||||
#### Graphics switching notes
|
|
||||||
|
|
||||||
**G-Sync note:** Some laptops are capable of using the dGPU as the sole GPU in the system which is generally to enable g-sync on the laptop display panel. This is controlled by the bios/efivar control and will be covered in that section.
|
|
||||||
|
|
||||||
**vfio note:** The vfio modules *must not* be compiled into the kernel, they need
|
|
||||||
to be separate modules. If you don't plan to use vfio mode then you can ignore this
|
|
||||||
otherwise you may need a custom built kernel.
|
|
||||||
|
|
||||||
### AniMe control
|
### AniMe control
|
||||||
|
|
||||||
Controller for the fancy AniMe matrix display on the lid of some machines. This controller is a work in progress.
|
Controller for the fancy AniMe matrix display on the lid of some machines. This controller is a work in progress.
|
||||||
@@ -102,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
|
||||||
@@ -139,55 +97,27 @@ These options are not written to the config file as they are stored in efivars.
|
|||||||
|
|
||||||
### Profiles
|
### Profiles
|
||||||
|
|
||||||
Profiles provide a method setting up various basic CPU and fan settings in profile blocks which can then be switched between or cycled through. The CPU controls so far are:
|
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.
|
||||||
|
|
||||||
- Min/Max percentage of CPU frequency (Intel only for now)
|
A common use of asusctl is to bind the `fn+f5` (fan) key to `asusctl profile -n` to cycle through the 3 profiles:
|
||||||
- CPU turbo boost enable or disable
|
1. Balanced
|
||||||
- Fan presets. These are 0: Normal, 1: Boost, 2: Silent.
|
2. Performance
|
||||||
- Fan curves, override fan-preset. AMD only.
|
3. Quiet
|
||||||
|
|
||||||
#### Config options
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```json
|
|
||||||
"toggle_profiles": [
|
|
||||||
"normal",
|
|
||||||
"boost",
|
|
||||||
"silent"
|
|
||||||
],
|
|
||||||
"power_profiles": {
|
|
||||||
"boost": {
|
|
||||||
"min_percentage": 0,
|
|
||||||
"max_percentage": 100,
|
|
||||||
"turbo": true,
|
|
||||||
"fan_preset": 1,
|
|
||||||
"fan_curve": null
|
|
||||||
},
|
|
||||||
"normal": {
|
|
||||||
"min_percentage": 0,
|
|
||||||
"max_percentage": 100,
|
|
||||||
"turbo": true,
|
|
||||||
"fan_preset": 0,
|
|
||||||
"fan_curve": null
|
|
||||||
},
|
|
||||||
"silent": {
|
|
||||||
"min_percentage": 0,
|
|
||||||
"max_percentage": 100,
|
|
||||||
"turbo": true,
|
|
||||||
"fan_preset": 2,
|
|
||||||
"fan_curve": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
1. `"toggle_profiles": [],`: these are the profile names that will be cycled through when using a provided next/prev dbus method.
|
|
||||||
2. `"power_profiles": {}`: all the available profiles.
|
|
||||||
|
|
||||||
#### Fan curves
|
#### Fan curves
|
||||||
|
|
||||||
**fan_curve note:** This is a WIP. Currently it relies on `acpi_call` kernel module which is ancient and hacky, not intended for this purpose. A proper kernel driver is in progress.
|
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 .
|
||||||
|
|
||||||
See [this document](https://github.com/cronosun/atrofac/blob/master/ADVANCED.md#limits) for details on the string format required, e.g, `"fan_curve": "30c:0%,40c:5%,50c:10%,60c:20%,70c:35%,80c:55%,90c:65%,100c:65%"`.
|
The fan curve format can be of varying formats:
|
||||||
|
|
||||||
|
- `30c:0%,40c:5%,50c:10%,60c:20%,70c:35%,80c:55%,90c:65%,100c:65%"`
|
||||||
|
- `30:0,40:5,50:10,60:20,70:35,80:55,90:65,100:65"`
|
||||||
|
- `30 0,40 5,50 10,60 20,70 35,80 55,90 65,100 65"`
|
||||||
|
- `30 0 40 5 50 10 60 20 70 35 80 55 90 65 100 65"`
|
||||||
|
|
||||||
|
the order must always be the same "temperature:percentage", lowest from left to rigth being highest.
|
||||||
|
|
||||||
|
The config file is located at `/etc/asusd/profile.conf` and is self-descriptive. On first run it is populated with the system EC defaults.
|
||||||
|
|
||||||
### Support controller
|
### Support controller
|
||||||
|
|
||||||
@@ -236,6 +166,18 @@ Each object in the array can be one of:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
##### AsusImage
|
||||||
|
|
||||||
|
Virtually the same as `AsusAnimation` but for png files, typically created in the same "slanted" style using a template (`diagonal-template.png`) as the ASUS gifs for pixel perfection.
|
||||||
|
|
||||||
|
```json
|
||||||
|
"AsusImage": {
|
||||||
|
"file": "<FILE_PATH>",
|
||||||
|
"time": <TIME>,
|
||||||
|
"brightness": <FLOAT>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
##### ImageAnimation
|
##### ImageAnimation
|
||||||
|
|
||||||
`ImageAnimation` can play *any* gif of any size.
|
`ImageAnimation` can play *any* gif of any size.
|
||||||
@@ -319,7 +261,7 @@ A cycle is how many gif loops to run:
|
|||||||
"Cycles": 2
|
"Cycles": 2
|
||||||
},
|
},
|
||||||
```
|
```
|
||||||
`Infinite` means that this gif will never end:
|
`Infinite` means that this gif will never end:
|
||||||
```json
|
```json
|
||||||
"time": "Infinite",
|
"time": "Infinite",
|
||||||
```
|
```
|
||||||
@@ -414,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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ BIN_D := asusd
|
|||||||
BIN_U := asusd-user
|
BIN_U := asusd-user
|
||||||
BIN_N := asus-notify
|
BIN_N := asus-notify
|
||||||
LEDCFG := asusd-ledmodes.toml
|
LEDCFG := asusd-ledmodes.toml
|
||||||
X11CFG := 90-nvidia-screen-G05.conf
|
|
||||||
PMRULES := 90-asusd-nvidia-pm.rules
|
|
||||||
|
|
||||||
SRC := Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.rs')
|
SRC := Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.rs')
|
||||||
|
|
||||||
@@ -45,14 +43,15 @@ install:
|
|||||||
$(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)"
|
||||||
$(INSTALL_PROGRAM) "./target/release/$(BIN_N)" "$(DESTDIR)$(bindir)/$(BIN_N)"
|
$(INSTALL_PROGRAM) "./target/release/$(BIN_N)" "$(DESTDIR)$(bindir)/$(BIN_N)"
|
||||||
$(INSTALL_DATA) "./data/$(PMRULES)" "$(DESTDIR)$(libdir)/udev/rules.d/$(PMRULES)"
|
|
||||||
$(INSTALL_DATA) "./data/$(BIN_D).rules" "$(DESTDIR)$(libdir)/udev/rules.d/99-$(BIN_D).rules"
|
$(INSTALL_DATA) "./data/$(BIN_D).rules" "$(DESTDIR)$(libdir)/udev/rules.d/99-$(BIN_D).rules"
|
||||||
$(INSTALL_DATA) "./data/$(LEDCFG)" "$(DESTDIR)/etc/asusd/$(LEDCFG)"
|
$(INSTALL_DATA) "./data/$(LEDCFG)" "$(DESTDIR)/etc/asusd/$(LEDCFG)"
|
||||||
$(INSTALL_DATA) "./data/$(BIN_D).conf" "$(DESTDIR)$(datarootdir)/dbus-1/system.d/$(BIN_D).conf"
|
$(INSTALL_DATA) "./data/$(BIN_D).conf" "$(DESTDIR)$(datarootdir)/dbus-1/system.d/$(BIN_D).conf"
|
||||||
$(INSTALL_DATA) "./data/$(X11CFG)" "$(DESTDIR)$(datarootdir)/X11/xorg.conf.d/$(X11CFG)"
|
|
||||||
$(INSTALL_DATA) "./data/$(BIN_D).service" "$(DESTDIR)$(libdir)/systemd/system/$(BIN_D).service"
|
$(INSTALL_DATA) "./data/$(BIN_D).service" "$(DESTDIR)$(libdir)/systemd/system/$(BIN_D).service"
|
||||||
$(INSTALL_DATA) "./data/$(BIN_N).service" "$(DESTDIR)$(libdir)/systemd/user/$(BIN_N).service"
|
$(INSTALL_DATA) "./data/$(BIN_N).service" "$(DESTDIR)$(libdir)/systemd/user/$(BIN_N).service"
|
||||||
$(INSTALL_DATA) "./data/$(BIN_U).service" "$(DESTDIR)$(libdir)/systemd/user/$(BIN_U).service"
|
$(INSTALL_DATA) "./data/$(BIN_U).service" "$(DESTDIR)$(libdir)/systemd/user/$(BIN_U).service"
|
||||||
|
|
||||||
$(INSTALL_DATA) "./data/icons/asus_notif_yellow.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_yellow.png"
|
$(INSTALL_DATA) "./data/icons/asus_notif_yellow.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_yellow.png"
|
||||||
$(INSTALL_DATA) "./data/icons/asus_notif_green.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_green.png"
|
$(INSTALL_DATA) "./data/icons/asus_notif_green.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_green.png"
|
||||||
$(INSTALL_DATA) "./data/icons/asus_notif_red.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_red.png"
|
$(INSTALL_DATA) "./data/icons/asus_notif_red.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_red.png"
|
||||||
@@ -63,19 +62,18 @@ install:
|
|||||||
$(INSTALL_DATA) "./data/icons/scalable/gpu-nvidia.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-nvidia.svg"
|
$(INSTALL_DATA) "./data/icons/scalable/gpu-nvidia.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-nvidia.svg"
|
||||||
$(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"
|
$(INSTALL_DATA) "./data/_asusctl" "$(DESTDIR)$(zshcpl)/_asusctl"
|
||||||
$(INSTALL_DATA) "./data/completions/asusctl.fish" "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish"
|
$(INSTALL_DATA) "./data/completions/asusctl.fish" "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish"
|
||||||
cd data && find "./anime" -type f -exec install -Dm 755 "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \;
|
cd rog-anime/data && find "./anime" -type f -exec install -Dm 755 "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \;
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
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)"
|
||||||
rm -f "$(DESTDIR)$(libdir)/udev/rules.d/$(PMRULES)"
|
|
||||||
rm -f "$(DESTDIR)$(libdir)/udev/rules.d/99-$(BIN_D).rules"
|
rm -f "$(DESTDIR)$(libdir)/udev/rules.d/99-$(BIN_D).rules"
|
||||||
rm -f "$(DESTDIR)/etc/asusd/$(LEDCFG)"
|
rm -f "$(DESTDIR)/etc/asusd/$(LEDCFG)"
|
||||||
rm -f "$(DESTDIR)$(datarootdir)/dbus-1/system.d/$(BIN_D).conf"
|
rm -f "$(DESTDIR)$(datarootdir)/dbus-1/system.d/$(BIN_D).conf"
|
||||||
rm -f "$(DESTDIR)$(datarootdir)/X11/xorg.conf.d/$(X11CFG)"
|
|
||||||
rm -f "$(DESTDIR)$(libdir)/systemd/system/$(BIN_D).service"
|
rm -f "$(DESTDIR)$(libdir)/systemd/system/$(BIN_D).service"
|
||||||
rm -r "$(DESTDIR)$(libdir)/systemd/user/$(BIN_N).service"
|
rm -r "$(DESTDIR)$(libdir)/systemd/user/$(BIN_N).service"
|
||||||
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_yellow.png"
|
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_yellow.png"
|
||||||
|
|||||||
@@ -5,6 +5,12 @@
|
|||||||
`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 support
|
||||||
|
|
||||||
|
**The minimum supported kernel version is 5.15**
|
||||||
|
|
||||||
|
Fan curve control on laptops with this feature require [this patch](https://lkml.org/lkml/2021/10/23/250) which has been merged for 5.17 upstream.
|
||||||
|
|
||||||
## Goals
|
## Goals
|
||||||
|
|
||||||
1. To provide an interface for rootless control of some system functions most users wish to control such as fan speeds, keyboard LEDs, graphics modes.
|
1. To provide an interface for rootless control of some system functions most users wish to control such as fan speeds, keyboard LEDs, graphics modes.
|
||||||
@@ -19,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)
|
||||||
@@ -48,30 +50,35 @@ will probably suffer another rename once it becomes generic enough to do so.
|
|||||||
- [X] Per-key LED setting
|
- [X] Per-key LED setting
|
||||||
- [X] Fancy LED modes (See examples) (currently being reworked)
|
- [X] Fancy LED modes (See examples) (currently being reworked)
|
||||||
- [X] Saving settings for reload
|
- [X] Saving settings for reload
|
||||||
- [X] Logging - required for journalctl
|
- [X] AniMatrix display on G14 models that include it
|
||||||
- [X] AniMatrix display on G14 models that include it (currently being reworked)
|
|
||||||
- [X] Set battery charge limit (with kernel supporting this)
|
- [X] Set battery charge limit (with kernel supporting this)
|
||||||
- [X] Fan curve control on G14 + G15 thanks to @Yarn1
|
- [X] Fan curve control on G14 + G15. Requires kernel patch (should reach 5.15 kernel)
|
||||||
- [X] Graphics mode switching between iGPU, dGPU, on-demand, and vfio (for VM pass-through)
|
|
||||||
+ [X] Requires only a logout/login
|
|
||||||
- [X] Toggle bios setting for boot/POST sound
|
- [X] Toggle bios setting for boot/POST sound
|
||||||
- [X] Toggle bios setting for "dedicated gfx" mode on supported laptops (g-sync)
|
- [X] Toggle bios setting for "dedicated gfx" mode on supported laptops (g-sync)
|
||||||
|
|
||||||
# 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.
|
=======
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
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.
|
||||||
@@ -84,11 +91,6 @@ $ systemctl daemon-reload && systemctl restart asusd
|
|||||||
|
|
||||||
You may also need to activate the service for debian install. If running Pop!_OS, I suggest disabling `system76-power` gnome-shell extension and systemd service.
|
You may also need to activate the service for debian install. If running Pop!_OS, I suggest disabling `system76-power` gnome-shell extension and systemd service.
|
||||||
|
|
||||||
If you would like to run this daemon on another non-ASUS laptop you can. You'll
|
|
||||||
have all features available except the LED and AniMe control (further controllers
|
|
||||||
can be added on request). You will need to install the alternative service from
|
|
||||||
`data/asusd-alt.service`.
|
|
||||||
|
|
||||||
## Uninstalling
|
## Uninstalling
|
||||||
|
|
||||||
Run `sudo make uninstall` in the source repo, and remove `/etc/asusd/`.
|
Run `sudo make uninstall` in the source repo, and remove `/etc/asusd/`.
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "asus-notify"
|
name = "asus-notify"
|
||||||
version = "3.0.1"
|
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 = "^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_types = { path = "../rog-types" }
|
rog_supported = { path = "../rog-supported" }
|
||||||
rog_profiles = { path = "../rog-profiles" }
|
rog_profiles = { path = "../rog-profiles" }
|
||||||
|
smol = "^1.2"
|
||||||
|
|
||||||
[dependencies.notify-rust]
|
[dependencies.notify-rust]
|
||||||
version = "^4.3"
|
version = "^4.3"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["z"]
|
features = ["z"]
|
||||||
|
|||||||
@@ -1,13 +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::{
|
||||||
use rog_profiles::profiles::{FanLevel, Profile};
|
zbus_charge::ChargeProxy, zbus_led::LedProxy, zbus_profile::ProfileProxy,
|
||||||
use rog_types::gfx_vendors::GfxRequiredUserAction;
|
zbus_rogbios::RogBiosProxy,
|
||||||
use rog_types::gfx_vendors::GfxVendors;
|
};
|
||||||
use std::error::Error;
|
use rog_profiles::Profile;
|
||||||
use std::process;
|
use smol::{future, Executor};
|
||||||
use std::thread::sleep;
|
use std::{
|
||||||
use std::time::Duration;
|
error::Error,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
use zbus::export::futures_util::StreamExt;
|
||||||
|
|
||||||
const NOTIF_HEADER: &str = "ROG Control";
|
const NOTIF_HEADER: &str = "ROG Control";
|
||||||
|
|
||||||
@@ -17,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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -32,75 +35,111 @@ macro_rules! base_notification {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SharedHandle = Arc<Mutex<Option<NotificationHandle>>>;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
println!("asus-notify version {}", env!("CARGO_PKG_VERSION"));
|
println!("asus-notify version {}", env!("CARGO_PKG_VERSION"));
|
||||||
println!(" rog-dbus version {}", rog_dbus::VERSION);
|
println!(" rog-dbus version {}", rog_dbus::VERSION);
|
||||||
|
|
||||||
let (proxies, conn) = DbusProxies::new()?;
|
let last_notification: SharedHandle = Arc::new(Mutex::new(None));
|
||||||
let signals = Signals::new(&proxies)?;
|
|
||||||
|
|
||||||
let mut last_notification: Option<NotificationHandle> = None;
|
let executor = Executor::new();
|
||||||
|
// BIOS notif
|
||||||
|
let x = last_notification.clone();
|
||||||
|
executor
|
||||||
|
.spawn(async move {
|
||||||
|
let conn = zbus::Connection::system().await.unwrap();
|
||||||
|
let proxy = RogBiosProxy::new(&conn).await.unwrap();
|
||||||
|
if let Ok(p) = proxy.receive_notify_post_boot_sound().await {
|
||||||
|
p.for_each(|e| {
|
||||||
|
if let Ok(out) = e.args() {
|
||||||
|
if let Ok(ref mut lock) = x.try_lock() {
|
||||||
|
notify!(do_post_sound_notif, lock, &out.sound());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
future::ready(())
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
// Charge notif
|
||||||
|
let x = last_notification.clone();
|
||||||
|
executor
|
||||||
|
.spawn(async move {
|
||||||
|
let conn = zbus::Connection::system().await.unwrap();
|
||||||
|
let proxy = ChargeProxy::new(&conn).await.unwrap();
|
||||||
|
if let Ok(p) = proxy.receive_notify_charge().await {
|
||||||
|
p.for_each(|e| {
|
||||||
|
if let Ok(out) = e.args() {
|
||||||
|
if let Ok(ref mut lock) = x.try_lock() {
|
||||||
|
notify!(do_charge_notif, lock, &out.limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
future::ready(())
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
// Profile notif
|
||||||
|
let x = last_notification.clone();
|
||||||
|
executor
|
||||||
|
.spawn(async move {
|
||||||
|
let conn = zbus::Connection::system().await.unwrap();
|
||||||
|
let proxy = ProfileProxy::new(&conn).await.unwrap();
|
||||||
|
if let Ok(p) = proxy.receive_notify_profile().await {
|
||||||
|
p.for_each(|e| {
|
||||||
|
if let Ok(out) = e.args() {
|
||||||
|
if let Ok(ref mut lock) = x.try_lock() {
|
||||||
|
notify!(do_thermal_notif, lock, &out.profile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
future::ready(())
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
// LED notif
|
||||||
|
executor
|
||||||
|
.spawn(async move {
|
||||||
|
let conn = zbus::Connection::system().await.unwrap();
|
||||||
|
let proxy = LedProxy::new(&conn).await.unwrap();
|
||||||
|
if let Ok(p) = proxy.receive_notify_led().await {
|
||||||
|
p.for_each(|e| {
|
||||||
|
if let Ok(out) = e.args() {
|
||||||
|
if let Ok(ref mut lock) = last_notification.try_lock() {
|
||||||
|
notify!(do_led_notif, lock, &out.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
future::ready(())
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
let recv = proxies.setup_recv(conn);
|
|
||||||
let mut err_count = 0;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
if let Ok(data) = signals.gfx_vendor.try_recv() {
|
|
||||||
notify!(do_gfx_notif, last_notification, &data);
|
|
||||||
}
|
|
||||||
if let Ok(data) = signals.gfx_action.try_recv() {
|
|
||||||
match data {
|
|
||||||
GfxRequiredUserAction::Logout | GfxRequiredUserAction::Reboot => {
|
|
||||||
do_gfx_action_notif(&data)?;
|
|
||||||
}
|
|
||||||
GfxRequiredUserAction::Integrated => {
|
|
||||||
base_notification!(
|
|
||||||
"You must be in integrated mode first to switch to the requested mode"
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
GfxRequiredUserAction::None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_thermal_notif(profile: &Profile) -> Result<NotificationHandle, Box<dyn Error>> {
|
fn do_thermal_notif(profile: &Profile) -> Result<NotificationHandle, Box<dyn Error>> {
|
||||||
let fan = profile.fan_preset;
|
let icon = match profile {
|
||||||
let turbo = if profile.turbo { "enabled" } else { "disabled" };
|
Profile::Balanced => "asus_notif_yellow",
|
||||||
let icon = match fan {
|
Profile::Performance => "asus_notif_red",
|
||||||
FanLevel::Normal => "asus_notif_yellow",
|
Profile::Quiet => "asus_notif_green",
|
||||||
FanLevel::Boost => "asus_notif_red",
|
|
||||||
FanLevel::Silent => "asus_notif_green",
|
|
||||||
};
|
};
|
||||||
|
let profile: &str = (*profile).into();
|
||||||
let x = Notification::new()
|
let x = Notification::new()
|
||||||
.summary("ASUS ROG")
|
.summary("ASUS ROG")
|
||||||
.body(&format!(
|
.body(&format!(
|
||||||
"Thermal profile changed to {}, turbo {}",
|
"Thermal profile changed to {}",
|
||||||
profile.name.to_uppercase(),
|
profile.to_uppercase(),
|
||||||
turbo
|
|
||||||
))
|
))
|
||||||
.hint(Hint::Resident(true))
|
.hint(Hint::Resident(true))
|
||||||
.timeout(2000)
|
.timeout(2000)
|
||||||
@@ -122,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,24 +1,26 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "asusctl"
|
name = "asusctl"
|
||||||
version = "3.5.1"
|
version = "4.0.7"
|
||||||
authors = ["Luke D Jones <luke@ljones.dev>"]
|
authors = ["Luke D Jones <luke@ljones.dev>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
zbus = "^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" }
|
||||||
rog_profiles = { path = "../rog-profiles" }
|
rog_profiles = { path = "../rog-profiles" }
|
||||||
rog_types = { path = "../rog-types" }
|
rog_supported = { path = "../rog-supported" }
|
||||||
daemon = { path = "../daemon" }
|
daemon = { path = "../daemon" }
|
||||||
rog_fan_curve = { version = "^0.1", features = ["serde"] }
|
|
||||||
gumdrop = "^0.8"
|
gumdrop = "^0.8"
|
||||||
yansi-term = "^0.1"
|
toml = "^0.5.8"
|
||||||
|
|
||||||
|
sysfs-class = "^0.1.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tinybmp = "^0.2.3"
|
tinybmp = "^0.3.3"
|
||||||
glam = "0.14.0"
|
glam = "0.20.5"
|
||||||
rog_dbus = { path = "../rog-dbus" }
|
rog_dbus = { path = "../rog-dbus" }
|
||||||
gif = "^0.11.2"
|
gif = "^0.11.2"
|
||||||
|
|||||||
@@ -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();
|
||||||
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,5 @@
|
|||||||
use rog_anime::{AnimeDataBuffer, AnimeGrid};
|
use rog_anime::{usb::get_anime_type, AnimeDataBuffer, AnimeGrid};
|
||||||
use rog_dbus::RogDbusClient;
|
use rog_dbus::RogDbusClientBlocking;
|
||||||
|
|
||||||
// In usable data:
|
// In usable data:
|
||||||
// Top row start at 1, ends at 32
|
// Top row start at 1, ends at 32
|
||||||
@@ -7,8 +7,9 @@ use rog_dbus::RogDbusClient;
|
|||||||
// 74w x 36h diagonal used by the windows app
|
// 74w x 36h diagonal used by the windows app
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let (client, _) = RogDbusClient::new().unwrap();
|
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||||
let mut matrix = AnimeGrid::new(None);
|
let anime_type = get_anime_type().unwrap();
|
||||||
|
let mut matrix = AnimeGrid::new(anime_type);
|
||||||
let tmp = matrix.get_mut();
|
let tmp = matrix.get_mut();
|
||||||
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
|
|||||||
@@ -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,13 @@
|
|||||||
use std::{env, error::Error, path::Path, process::exit};
|
use std::{env, error::Error, path::Path, process::exit};
|
||||||
|
|
||||||
use rog_anime::{
|
use rog_anime::{
|
||||||
|
usb::get_anime_type,
|
||||||
AnimeDataBuffer, {AnimeImage, Vec2},
|
AnimeDataBuffer, {AnimeImage, Vec2},
|
||||||
};
|
};
|
||||||
use rog_dbus::RogDbusClient;
|
use rog_dbus::RogDbusClientBlocking;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let (client, _) = RogDbusClient::new().unwrap();
|
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||||
|
|
||||||
let args: Vec<String> = env::args().into_iter().collect();
|
let args: Vec<String> = env::args().into_iter().collect();
|
||||||
if args.len() != 7 {
|
if args.len() != 7 {
|
||||||
@@ -15,6 +16,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let anime_type = get_anime_type()?;
|
||||||
let matrix = AnimeImage::from_png(
|
let matrix = AnimeImage::from_png(
|
||||||
Path::new(&args[1]),
|
Path::new(&args[1]),
|
||||||
args[2].parse::<f32>().unwrap(),
|
args[2].parse::<f32>().unwrap(),
|
||||||
@@ -24,6 +26,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
args[5].parse::<f32>().unwrap(),
|
args[5].parse::<f32>().unwrap(),
|
||||||
),
|
),
|
||||||
args[6].parse::<f32>().unwrap(),
|
args[6].parse::<f32>().unwrap(),
|
||||||
|
anime_type,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
client
|
client
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use rog_anime::{
|
use rog_anime::{
|
||||||
|
usb::get_anime_type,
|
||||||
AnimeDataBuffer, {AnimeImage, Vec2},
|
AnimeDataBuffer, {AnimeImage, Vec2},
|
||||||
};
|
};
|
||||||
use rog_dbus::RogDbusClient;
|
use rog_dbus::RogDbusClientBlocking;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let (client, _) = RogDbusClient::new().unwrap();
|
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||||
|
|
||||||
let args: Vec<String> = env::args().into_iter().collect();
|
let args: Vec<String> = env::args().into_iter().collect();
|
||||||
if args.len() != 7 {
|
if args.len() != 7 {
|
||||||
@@ -17,6 +18,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let anime_type = get_anime_type()?;
|
||||||
let mut matrix = AnimeImage::from_png(
|
let mut matrix = AnimeImage::from_png(
|
||||||
Path::new(&args[1]),
|
Path::new(&args[1]),
|
||||||
args[2].parse::<f32>().unwrap(),
|
args[2].parse::<f32>().unwrap(),
|
||||||
@@ -26,6 +28,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
args[5].parse::<f32>().unwrap(),
|
args[5].parse::<f32>().unwrap(),
|
||||||
),
|
),
|
||||||
args[6].parse::<f32>().unwrap(),
|
args[6].parse::<f32>().unwrap(),
|
||||||
|
anime_type,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
|||||||
@@ -1,54 +1,4 @@
|
|||||||
use gumdrop::Options;
|
use gumdrop::Options;
|
||||||
use rog_aura::error::Error;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub enum AnimeStatusValue {
|
|
||||||
On,
|
|
||||||
Off,
|
|
||||||
}
|
|
||||||
impl FromStr for AnimeStatusValue {
|
|
||||||
type Err = Error;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
let s = s.to_lowercase();
|
|
||||||
match s.as_str() {
|
|
||||||
"on" => Ok(AnimeStatusValue::On),
|
|
||||||
"off" => Ok(AnimeStatusValue::Off),
|
|
||||||
_ => {
|
|
||||||
print!("Invalid argument, must be one of: on, off");
|
|
||||||
Err(Error::ParseAnime)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<AnimeStatusValue> for bool {
|
|
||||||
fn from(value: AnimeStatusValue) -> Self {
|
|
||||||
match value {
|
|
||||||
AnimeStatusValue::On => true,
|
|
||||||
AnimeStatusValue::Off => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Options)]
|
|
||||||
pub struct AnimeLeds {
|
|
||||||
#[options(help = "print help message")]
|
|
||||||
help: bool,
|
|
||||||
#[options(
|
|
||||||
no_long,
|
|
||||||
required,
|
|
||||||
short = "b",
|
|
||||||
meta = "",
|
|
||||||
help = "set all leds brightness value"
|
|
||||||
)]
|
|
||||||
led_brightness: u8,
|
|
||||||
}
|
|
||||||
impl AnimeLeds {
|
|
||||||
pub fn led_brightness(&self) -> u8 {
|
|
||||||
self.led_brightness
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Options)]
|
#[derive(Options)]
|
||||||
pub struct AnimeCommand {
|
pub struct AnimeCommand {
|
||||||
@@ -56,21 +6,30 @@ pub struct AnimeCommand {
|
|||||||
pub help: bool,
|
pub help: bool,
|
||||||
#[options(
|
#[options(
|
||||||
meta = "",
|
meta = "",
|
||||||
help = "turn on/off the panel (accept/reject write requests)"
|
help = "enable/disable the panel LEDs (does not erase last image)"
|
||||||
)]
|
)]
|
||||||
pub turn: Option<AnimeStatusValue>,
|
pub enable: Option<bool>,
|
||||||
#[options(meta = "", help = "turn on/off the panel at boot (with Asus effect)")]
|
#[options(
|
||||||
pub boot: Option<AnimeStatusValue>,
|
meta = "",
|
||||||
|
help = "enable/disable system animations (boot/sleep/shutdown)"
|
||||||
|
)]
|
||||||
|
pub boot_enable: Option<bool>,
|
||||||
|
#[options(meta = "", help = "set global AniMe brightness value")]
|
||||||
|
pub brightness: Option<f32>,
|
||||||
#[options(command)]
|
#[options(command)]
|
||||||
pub command: Option<AnimeActions>,
|
pub command: Option<AnimeActions>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Options)]
|
#[derive(Options)]
|
||||||
pub enum AnimeActions {
|
pub enum AnimeActions {
|
||||||
#[options(help = "change all leds brightness")]
|
#[options(help = "display a PNG image")]
|
||||||
Leds(AnimeLeds),
|
|
||||||
#[options(help = "display an 8bit greyscale png")]
|
|
||||||
Image(AnimeImage),
|
Image(AnimeImage),
|
||||||
|
#[options(help = "display a diagonal/pixel-perfect PNG")]
|
||||||
|
PixelImage(AnimeImageDiagonal),
|
||||||
|
#[options(help = "display an animated GIF")]
|
||||||
|
Gif(AnimeGif),
|
||||||
|
#[options(help = "display an animated diagonal/pixel-perfect GIF")]
|
||||||
|
PixelGif(AnimeGifDiagonal),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Options)]
|
#[derive(Options)]
|
||||||
@@ -90,3 +49,53 @@ pub struct AnimeImage {
|
|||||||
#[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")]
|
#[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")]
|
||||||
pub bright: f32,
|
pub bright: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Options)]
|
||||||
|
pub struct AnimeImageDiagonal {
|
||||||
|
#[options(help = "print help message")]
|
||||||
|
pub help: bool,
|
||||||
|
#[options(meta = "", help = "full path to the png to display")]
|
||||||
|
pub path: String,
|
||||||
|
#[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")]
|
||||||
|
pub bright: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Options)]
|
||||||
|
pub struct AnimeGif {
|
||||||
|
#[options(help = "print help message")]
|
||||||
|
pub help: bool,
|
||||||
|
#[options(meta = "", help = "full path to the png to display")]
|
||||||
|
pub path: String,
|
||||||
|
#[options(meta = "", default = "1.0", help = "scale 1.0 == normal")]
|
||||||
|
pub scale: f32,
|
||||||
|
#[options(meta = "", default = "0.0", help = "x position (float)")]
|
||||||
|
pub x_pos: f32,
|
||||||
|
#[options(meta = "", default = "0.0", help = "y position (float)")]
|
||||||
|
pub y_pos: f32,
|
||||||
|
#[options(meta = "", default = "0.0", help = "the angle in radians")]
|
||||||
|
pub angle: f32,
|
||||||
|
#[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")]
|
||||||
|
pub bright: f32,
|
||||||
|
#[options(
|
||||||
|
meta = "",
|
||||||
|
default = "1",
|
||||||
|
help = "how many loops to play - 0 is infinite"
|
||||||
|
)]
|
||||||
|
pub loops: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Options)]
|
||||||
|
pub struct AnimeGifDiagonal {
|
||||||
|
#[options(help = "print help message")]
|
||||||
|
pub help: bool,
|
||||||
|
#[options(meta = "", help = "full path to the png to display")]
|
||||||
|
pub path: String,
|
||||||
|
#[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")]
|
||||||
|
pub bright: f32,
|
||||||
|
#[options(
|
||||||
|
meta = "",
|
||||||
|
default = "1",
|
||||||
|
help = "how many loops to play - 0 is infinite"
|
||||||
|
)]
|
||||||
|
pub loops: u32,
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,53 @@ use gumdrop::Options;
|
|||||||
use rog_aura::{error::Error, AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed};
|
use rog_aura::{error::Error, AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[derive(Options)]
|
||||||
|
pub struct LedPowerCommand {
|
||||||
|
#[options(help = "print help message")]
|
||||||
|
pub help: bool,
|
||||||
|
#[options(command)]
|
||||||
|
pub command: Option<SetAuraEnabled>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Options)]
|
||||||
|
pub enum SetAuraEnabled {
|
||||||
|
#[options(help = "set <keyboard, logo, lightbar> to enabled while the device is booting")]
|
||||||
|
Boot(AuraEnabled),
|
||||||
|
#[options(help = "set <keyboard, logo, lightbar> to animate while the device is suspended")]
|
||||||
|
Sleep(AuraEnabled),
|
||||||
|
#[options(help = "set <keyboard, logo, lightbar> to enabled while device is awake")]
|
||||||
|
Awake(AuraEnabled),
|
||||||
|
#[options(help = "set <keyboard, logo, lightbar> to animate while the device is shutdown")]
|
||||||
|
Shutdown(AuraEnabled),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Options)]
|
||||||
|
pub struct AuraEnabled {
|
||||||
|
#[options(help = "print help message")]
|
||||||
|
pub help: bool,
|
||||||
|
#[options(meta = "", help = "<true/false>")]
|
||||||
|
pub keyboard: Option<bool>,
|
||||||
|
#[options(meta = "", help = "<true/false>")]
|
||||||
|
pub logo: Option<bool>,
|
||||||
|
#[options(meta = "", help = "<true/false>")]
|
||||||
|
pub lightbar: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl FromStr for AuraEnabled {
|
||||||
|
// type Err = Error;
|
||||||
|
|
||||||
|
// fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
// let s = s.to_lowercase();
|
||||||
|
// dbg!(s);
|
||||||
|
// Ok(Self {
|
||||||
|
// help: false,
|
||||||
|
// keyboard: None,
|
||||||
|
// logo: None,
|
||||||
|
// lightbar: None,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
#[derive(Options)]
|
#[derive(Options)]
|
||||||
pub struct LedBrightness {
|
pub struct LedBrightness {
|
||||||
level: Option<u32>,
|
level: Option<u32>,
|
||||||
@@ -50,7 +97,14 @@ pub struct SingleSpeed {
|
|||||||
help: bool,
|
help: bool,
|
||||||
#[options(no_long, meta = "WORD", help = "set the speed: low, med, high")]
|
#[options(no_long, meta = "WORD", help = "set the speed: low, med, high")]
|
||||||
pub speed: Speed,
|
pub speed: Speed,
|
||||||
|
#[options(
|
||||||
|
no_long,
|
||||||
|
meta = "",
|
||||||
|
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
|
||||||
|
)]
|
||||||
|
pub zone: AuraZone,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Options, Default)]
|
#[derive(Debug, Clone, Options, Default)]
|
||||||
pub struct SingleSpeedDirection {
|
pub struct SingleSpeedDirection {
|
||||||
#[options(help = "print help message")]
|
#[options(help = "print help message")]
|
||||||
@@ -59,6 +113,12 @@ pub struct SingleSpeedDirection {
|
|||||||
pub direction: Direction,
|
pub direction: Direction,
|
||||||
#[options(no_long, meta = "", help = "set the speed: low, med, high")]
|
#[options(no_long, meta = "", help = "set the speed: low, med, high")]
|
||||||
pub speed: Speed,
|
pub speed: Speed,
|
||||||
|
#[options(
|
||||||
|
no_long,
|
||||||
|
meta = "",
|
||||||
|
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
|
||||||
|
)]
|
||||||
|
pub zone: AuraZone,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Options)]
|
#[derive(Debug, Clone, Default, Options)]
|
||||||
@@ -67,6 +127,12 @@ pub struct SingleColour {
|
|||||||
help: bool,
|
help: bool,
|
||||||
#[options(no_long, meta = "", help = "set the RGB value e.g, ff00ff")]
|
#[options(no_long, meta = "", help = "set the RGB value e.g, ff00ff")]
|
||||||
pub colour: Colour,
|
pub colour: Colour,
|
||||||
|
#[options(
|
||||||
|
no_long,
|
||||||
|
meta = "",
|
||||||
|
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
|
||||||
|
)]
|
||||||
|
pub zone: AuraZone,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Options)]
|
#[derive(Debug, Clone, Default, Options)]
|
||||||
@@ -77,6 +143,12 @@ pub struct SingleColourSpeed {
|
|||||||
pub colour: Colour,
|
pub colour: Colour,
|
||||||
#[options(no_long, meta = "", help = "set the speed: low, med, high")]
|
#[options(no_long, meta = "", help = "set the speed: low, med, high")]
|
||||||
pub speed: Speed,
|
pub speed: Speed,
|
||||||
|
#[options(
|
||||||
|
no_long,
|
||||||
|
meta = "",
|
||||||
|
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
|
||||||
|
)]
|
||||||
|
pub zone: AuraZone,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Options, Default)]
|
#[derive(Debug, Clone, Options, Default)]
|
||||||
@@ -89,10 +161,16 @@ pub struct TwoColourSpeed {
|
|||||||
pub colour2: Colour,
|
pub colour2: Colour,
|
||||||
#[options(no_long, meta = "", help = "set the speed: low, med, high")]
|
#[options(no_long, meta = "", help = "set the speed: low, med, high")]
|
||||||
pub speed: Speed,
|
pub speed: Speed,
|
||||||
|
#[options(
|
||||||
|
no_long,
|
||||||
|
meta = "",
|
||||||
|
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
|
||||||
|
)]
|
||||||
|
pub zone: AuraZone,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Options)]
|
#[derive(Debug, Clone, Default, Options)]
|
||||||
pub struct MultiColour {
|
pub struct MultiZone {
|
||||||
#[options(help = "print help message")]
|
#[options(help = "print help message")]
|
||||||
help: bool,
|
help: bool,
|
||||||
#[options(short = "a", meta = "", help = "set the RGB value e.g, ff00ff")]
|
#[options(short = "a", meta = "", help = "set the RGB value e.g, ff00ff")]
|
||||||
@@ -152,10 +230,6 @@ pub enum SetAuraBuiltin {
|
|||||||
Comet(SingleColour),
|
Comet(SingleColour),
|
||||||
#[options(help = "set a wide vertical line zooming from left")]
|
#[options(help = "set a wide vertical line zooming from left")]
|
||||||
Flash(SingleColour),
|
Flash(SingleColour),
|
||||||
#[options(help = "4-zone multi-colour")]
|
|
||||||
MultiStatic(MultiColour),
|
|
||||||
#[options(help = "4-zone multi-colour breathing")]
|
|
||||||
MultiBreathe(MultiColourSpeed),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SetAuraBuiltin {
|
impl Default for SetAuraBuiltin {
|
||||||
@@ -168,6 +242,7 @@ impl From<&SingleColour> for AuraEffect {
|
|||||||
fn from(aura: &SingleColour) -> Self {
|
fn from(aura: &SingleColour) -> Self {
|
||||||
Self {
|
Self {
|
||||||
colour1: aura.colour,
|
colour1: aura.colour,
|
||||||
|
zone: aura.zone,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,6 +252,7 @@ impl From<&SingleSpeed> for AuraEffect {
|
|||||||
fn from(aura: &SingleSpeed) -> Self {
|
fn from(aura: &SingleSpeed) -> Self {
|
||||||
Self {
|
Self {
|
||||||
speed: aura.speed,
|
speed: aura.speed,
|
||||||
|
zone: aura.zone,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,6 +263,7 @@ impl From<&SingleColourSpeed> for AuraEffect {
|
|||||||
Self {
|
Self {
|
||||||
colour1: aura.colour,
|
colour1: aura.colour,
|
||||||
speed: aura.speed,
|
speed: aura.speed,
|
||||||
|
zone: aura.zone,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,6 +274,7 @@ impl From<&TwoColourSpeed> for AuraEffect {
|
|||||||
Self {
|
Self {
|
||||||
colour1: aura.colour,
|
colour1: aura.colour,
|
||||||
colour2: aura.colour2,
|
colour2: aura.colour2,
|
||||||
|
zone: aura.zone,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,6 +285,7 @@ impl From<&SingleSpeedDirection> for AuraEffect {
|
|||||||
Self {
|
Self {
|
||||||
speed: aura.speed,
|
speed: aura.speed,
|
||||||
direction: aura.direction,
|
direction: aura.direction,
|
||||||
|
zone: aura.zone,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,55 +354,6 @@ impl From<&SetAuraBuiltin> for AuraEffect {
|
|||||||
data.mode = AuraModeNum::Flash;
|
data.mode = AuraModeNum::Flash;
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
_ => AuraEffect::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&SetAuraBuiltin> for Vec<AuraEffect> {
|
|
||||||
fn from(aura: &SetAuraBuiltin) -> Vec<AuraEffect> {
|
|
||||||
let mut zones = vec![AuraEffect::default(); 4];
|
|
||||||
match aura {
|
|
||||||
SetAuraBuiltin::MultiStatic(data) => {
|
|
||||||
zones[0].mode = AuraModeNum::Static;
|
|
||||||
zones[0].zone = AuraZone::One;
|
|
||||||
zones[0].colour1 = data.colour1;
|
|
||||||
|
|
||||||
zones[1].mode = AuraModeNum::Static;
|
|
||||||
zones[1].zone = AuraZone::Two;
|
|
||||||
zones[1].colour1 = data.colour2;
|
|
||||||
|
|
||||||
zones[2].mode = AuraModeNum::Static;
|
|
||||||
zones[2].zone = AuraZone::Three;
|
|
||||||
zones[2].colour1 = data.colour3;
|
|
||||||
|
|
||||||
zones[3].mode = AuraModeNum::Static;
|
|
||||||
zones[3].zone = AuraZone::Four;
|
|
||||||
zones[3].colour1 = data.colour4;
|
|
||||||
}
|
|
||||||
SetAuraBuiltin::MultiBreathe(data) => {
|
|
||||||
zones[0].mode = AuraModeNum::Breathe;
|
|
||||||
zones[0].zone = AuraZone::One;
|
|
||||||
zones[0].colour1 = data.colour1;
|
|
||||||
zones[0].speed = data.speed;
|
|
||||||
|
|
||||||
zones[1].mode = AuraModeNum::Breathe;
|
|
||||||
zones[1].zone = AuraZone::Two;
|
|
||||||
zones[1].colour1 = data.colour2;
|
|
||||||
zones[1].speed = data.speed;
|
|
||||||
|
|
||||||
zones[2].mode = AuraModeNum::Breathe;
|
|
||||||
zones[2].zone = AuraZone::Three;
|
|
||||||
zones[2].colour1 = data.colour3;
|
|
||||||
zones[2].speed = data.speed;
|
|
||||||
|
|
||||||
zones[3].mode = AuraModeNum::Breathe;
|
|
||||||
zones[3].zone = AuraZone::Four;
|
|
||||||
zones[3].colour1 = data.colour4;
|
|
||||||
zones[3].speed = data.speed;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
zones
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
use crate::{
|
||||||
|
anime_cli::AnimeCommand,
|
||||||
|
aura_cli::{LedBrightness, LedPowerCommand, SetAuraBuiltin},
|
||||||
|
profiles_cli::{FanCurveCommand, ProfileCommand},
|
||||||
|
};
|
||||||
|
use gumdrop::Options;
|
||||||
|
|
||||||
|
#[derive(Default, Options)]
|
||||||
|
pub struct CliStart {
|
||||||
|
#[options(help_flag, help = "print help message")]
|
||||||
|
pub help: bool,
|
||||||
|
#[options(help = "show program version number")]
|
||||||
|
pub version: bool,
|
||||||
|
#[options(help = "show supported functions of this laptop")]
|
||||||
|
pub show_supported: bool,
|
||||||
|
#[options(meta = "", help = "<off, low, med, high>")]
|
||||||
|
pub kbd_bright: Option<LedBrightness>,
|
||||||
|
#[options(help = "Toggle to next keyboard brightness")]
|
||||||
|
pub next_kbd_bright: bool,
|
||||||
|
#[options(help = "Toggle to previous keyboard brightness")]
|
||||||
|
pub prev_kbd_bright: bool,
|
||||||
|
#[options(meta = "", help = "Set your battery charge limit <20-100>")]
|
||||||
|
pub chg_limit: Option<u8>,
|
||||||
|
#[options(command)]
|
||||||
|
pub command: Option<CliCommand>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Options)]
|
||||||
|
pub enum CliCommand {
|
||||||
|
#[options(help = "Set the keyboard lighting from built-in modes")]
|
||||||
|
LedMode(LedModeCommand),
|
||||||
|
#[options(help = "Set the keyboard lighting from built-in modes")]
|
||||||
|
LedPower(LedPowerCommand),
|
||||||
|
#[options(help = "Set or select platform_profile")]
|
||||||
|
Profile(ProfileCommand),
|
||||||
|
#[options(help = "Set, select, or modify fan curves if supported")]
|
||||||
|
FanCurve(FanCurveCommand),
|
||||||
|
#[options(help = "Set the graphics mode (obsoleted by supergfxctl)")]
|
||||||
|
Graphics(GraphicsCommand),
|
||||||
|
#[options(name = "anime", help = "Manage AniMe Matrix")]
|
||||||
|
Anime(AnimeCommand),
|
||||||
|
#[options(help = "Change bios settings")]
|
||||||
|
Bios(BiosCommand),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Options)]
|
||||||
|
pub struct LedModeCommand {
|
||||||
|
#[options(help = "print help message")]
|
||||||
|
pub help: bool,
|
||||||
|
#[options(help = "switch to next aura mode")]
|
||||||
|
pub next_mode: bool,
|
||||||
|
#[options(help = "switch to previous aura mode")]
|
||||||
|
pub prev_mode: bool,
|
||||||
|
#[options(command)]
|
||||||
|
pub command: Option<SetAuraBuiltin>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Options)]
|
||||||
|
pub struct GraphicsCommand {
|
||||||
|
#[options(help = "print help message")]
|
||||||
|
pub help: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Options, Debug)]
|
||||||
|
pub struct BiosCommand {
|
||||||
|
#[options(help = "print help message")]
|
||||||
|
pub help: bool,
|
||||||
|
#[options(
|
||||||
|
meta = "",
|
||||||
|
no_long,
|
||||||
|
help = "set bios POST sound: asusctl -p <true/false>"
|
||||||
|
)]
|
||||||
|
pub post_sound_set: Option<bool>,
|
||||||
|
#[options(no_long, help = "read bios POST sound")]
|
||||||
|
pub post_sound_get: bool,
|
||||||
|
#[options(
|
||||||
|
meta = "",
|
||||||
|
no_long,
|
||||||
|
help = "activate dGPU dedicated/G-Sync: asusctl -d <true/false>, reboot required"
|
||||||
|
)]
|
||||||
|
pub dedicated_gfx_set: Option<bool>,
|
||||||
|
#[options(no_long, help = "get GPU mode")]
|
||||||
|
pub dedicated_gfx_get: bool,
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
use gumdrop::Options;
|
use gumdrop::Options;
|
||||||
use rog_fan_curve::{Curve, Fan};
|
use rog_profiles::{fan_curve_set::CurveData, FanCurvePU, Profile};
|
||||||
use rog_profiles::profiles::FanLevel;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Options)]
|
#[derive(Debug, Clone, Options)]
|
||||||
pub struct ProfileCommand {
|
pub struct ProfileCommand {
|
||||||
@@ -8,46 +7,45 @@ pub struct ProfileCommand {
|
|||||||
pub help: bool,
|
pub help: bool,
|
||||||
#[options(help = "toggle to next profile in list")]
|
#[options(help = "toggle to next profile in list")]
|
||||||
pub next: bool,
|
pub next: bool,
|
||||||
#[options(help = "create the profile if it doesn't exist")]
|
|
||||||
pub create: bool,
|
|
||||||
#[options(meta = "", help = "remove a profile by name")]
|
|
||||||
pub remove: Option<String>,
|
|
||||||
#[options(help = "list available profiles")]
|
#[options(help = "list available profiles")]
|
||||||
pub list: bool,
|
pub list: bool,
|
||||||
#[options(help = "get active profile name")]
|
|
||||||
pub active_name: bool,
|
|
||||||
#[options(help = "get active profile data")]
|
|
||||||
pub active_data: bool,
|
|
||||||
#[options(help = "get all profile data")]
|
|
||||||
pub profiles_data: bool,
|
|
||||||
|
|
||||||
// Options for profile
|
#[options(help = "get profile")]
|
||||||
#[options(meta = "", help = "enable or disable cpu turbo")]
|
pub profile_get: bool,
|
||||||
pub turbo: Option<bool>,
|
#[options(meta = "", help = "set the active profile")]
|
||||||
#[options(meta = "", help = "set min cpu scaling (intel)")]
|
pub profile_set: Option<Profile>,
|
||||||
pub min_percentage: Option<u8>,
|
}
|
||||||
#[options(meta = "", help = "set max cpu scaling (intel)")]
|
|
||||||
pub max_percentage: Option<u8>,
|
#[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 = "<silent, normal, boost>")]
|
|
||||||
pub fan_preset: Option<FanLevel>,
|
|
||||||
#[options(
|
#[options(
|
||||||
meta = "",
|
meta = "",
|
||||||
parse(try_from_str = "parse_fan_curve"),
|
help = "profile to modify fan-curve for. Shows data if no options provided"
|
||||||
help = "set fan curve"
|
|
||||||
)]
|
)]
|
||||||
pub curve: Option<Curve>,
|
pub mod_profile: Option<Profile>,
|
||||||
#[options(free)]
|
#[options(
|
||||||
pub profile: Option<String>,
|
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>,
|
||||||
|
|
||||||
fn parse_fan_curve(data: &str) -> Result<Curve, String> {
|
#[options(
|
||||||
let curve = Curve::from_config_str(data)?;
|
meta = "",
|
||||||
if let Err(err) = curve.check_safety(Fan::Cpu) {
|
help = "data format = 30c:1%,49c:2%,59c:3%,69c:4%,79c:31%,89c:49%,99c:56%,109c:58%.
|
||||||
return Err(format!("Unsafe curve {:?}", err));
|
`--mod-profile` required. If '%' is omitted the fan range is 0-255"
|
||||||
}
|
)]
|
||||||
if let Err(err) = curve.check_safety(Fan::Gpu) {
|
pub data: Option<CurveData>,
|
||||||
return Err(format!("Unsafe curve {:?}", err));
|
|
||||||
}
|
|
||||||
Ok(curve)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -21,10 +21,12 @@ serde_derive = "^1.0"
|
|||||||
|
|
||||||
rog_anime = { path = "../rog-anime" }
|
rog_anime = { path = "../rog-anime" }
|
||||||
rog_dbus = { path = "../rog-dbus" }
|
rog_dbus = { path = "../rog-dbus" }
|
||||||
rog_types = { path = "../rog-types" }
|
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,5 +1,6 @@
|
|||||||
|
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::{
|
||||||
@@ -65,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 {
|
||||||
@@ -90,17 +91,24 @@ 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| {
|
||||||
self.client.proxies().anime().write(output).unwrap()
|
if self.do_early_return.load(Ordering::Acquire) {
|
||||||
})
|
return Ok(true); // Do safe exit
|
||||||
.unwrap();
|
}
|
||||||
|
self.client
|
||||||
|
.proxies()
|
||||||
|
.anime()
|
||||||
|
.write(output)
|
||||||
|
.map_err(|e| AnimeError::Dbus(format!("{}", e)))
|
||||||
|
.map(|_| false)
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
ActionData::Image(image) => {
|
ActionData::Image(image) => {
|
||||||
self.client
|
self.client
|
||||||
.proxies()
|
.proxies()
|
||||||
.anime()
|
.anime()
|
||||||
.write(image.as_ref().clone())
|
.write(image.as_ref().clone())
|
||||||
.unwrap();
|
.ok();
|
||||||
}
|
}
|
||||||
ActionData::Pause(duration) => {
|
ActionData::Pause(duration) => {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
@@ -127,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>,
|
||||||
@@ -137,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 {
|
||||||
@@ -148,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
|
||||||
@@ -261,12 +271,12 @@ impl CtrlAnime<'static> {
|
|||||||
scale: f32,
|
scale: f32,
|
||||||
angle: f32,
|
angle: f32,
|
||||||
xy: (f32, f32),
|
xy: (f32, f32),
|
||||||
time: Option<Timer>,
|
time: Timer,
|
||||||
brightness: f32,
|
brightness: f32,
|
||||||
) -> zbus::fdo::Result<String> {
|
) -> zbus::fdo::Result<String> {
|
||||||
if let Ok(mut config) = self.config.try_lock() {
|
if let Ok(mut config) = self.config.try_lock() {
|
||||||
let file = Path::new(&file);
|
let file = Path::new(&file);
|
||||||
let time = time.map(|time| time.into());
|
let time = time.into();
|
||||||
let action = ActionLoader::Image {
|
let action = ActionLoader::Image {
|
||||||
file: file.into(),
|
file: file.into(),
|
||||||
scale,
|
scale,
|
||||||
@@ -349,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,67 +1,72 @@
|
|||||||
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;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
println!(" user daemon v{}", rog_user::VERSION);
|
println!(" user daemon v{}", rog_user::VERSION);
|
||||||
println!(" rog-anime v{}", rog_anime::VERSION);
|
println!(" rog-anime v{}", rog_anime::VERSION);
|
||||||
println!(" rog-dbus v{}", rog_dbus::VERSION);
|
println!(" rog-dbus v{}", rog_dbus::VERSION);
|
||||||
println!(" rog-types v{}", rog_types::VERSION);
|
println!("rog-supported v{}", rog_supported::VERSION);
|
||||||
|
|
||||||
let (client, _) = RogDbusClient::new().unwrap();
|
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().unwrap();
|
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().unwrap();
|
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)?;
|
||||||
@@ -95,6 +95,15 @@ impl Default for UserAnimeConfig {
|
|||||||
Self {
|
Self {
|
||||||
name: "default".to_string(),
|
name: "default".to_string(),
|
||||||
anime: vec![
|
anime: vec![
|
||||||
|
ActionLoader::AsusImage {
|
||||||
|
file: "/usr/share/asusd/anime/custom/diagonal-template.png".into(),
|
||||||
|
brightness: 1.0,
|
||||||
|
time: AnimTime::Fade(Fade::new(
|
||||||
|
Duration::from_secs(2),
|
||||||
|
None,
|
||||||
|
Duration::from_secs(2),
|
||||||
|
)),
|
||||||
|
},
|
||||||
ActionLoader::AsusAnimation {
|
ActionLoader::AsusAnimation {
|
||||||
file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(),
|
file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(),
|
||||||
brightness: 0.5,
|
brightness: 0.5,
|
||||||
@@ -121,11 +130,11 @@ impl Default for UserAnimeConfig {
|
|||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
angle: 0.0,
|
angle: 0.0,
|
||||||
translation: Vec2::default(),
|
translation: Vec2::default(),
|
||||||
time: Some(AnimTime::Fade(Fade::new(
|
time: AnimTime::Fade(Fade::new(
|
||||||
Duration::from_secs(2),
|
Duration::from_secs(2),
|
||||||
Some(Duration::from_secs(1)),
|
Some(Duration::from_secs(1)),
|
||||||
Duration::from_secs(2),
|
Duration::from_secs(2),
|
||||||
))),
|
)),
|
||||||
brightness: 0.6,
|
brightness: 0.6,
|
||||||
},
|
},
|
||||||
ActionLoader::Pause(Duration::from_secs(1)),
|
ActionLoader::Pause(Duration::from_secs(1)),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "daemon"
|
name = "daemon"
|
||||||
version = "3.7.2"
|
version = "4.2.0"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = ["Luke <luke@ljones.dev>"]
|
authors = ["Luke <luke@ljones.dev>"]
|
||||||
@@ -18,29 +18,31 @@ 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_types = { path = "../rog-types" }
|
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"
|
||||||
serde_derive = "^1.0"
|
serde_derive = "^1.0"
|
||||||
serde_json = "^1.0"
|
serde_json = "^1.0"
|
||||||
toml = "^0.5"
|
toml = "^0.5.8"
|
||||||
|
|
||||||
# Device control
|
# Device control
|
||||||
sysfs-class = "^0.1.2" # used for backlight control and baord ID
|
sysfs-class = "^0.1.2" # used for backlight control and baord ID
|
||||||
rog_fan_curve = { version = "0.1", features = ["serde"] }
|
|
||||||
|
|||||||
@@ -1,134 +1,57 @@
|
|||||||
use log::{error, info, warn};
|
use log::{error, warn};
|
||||||
use rog_profiles::profiles::{FanLevel, Profile};
|
|
||||||
use rog_types::gfx_vendors::GfxVendors;
|
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
use std::path::PathBuf;
|
||||||
use crate::config_old::*;
|
|
||||||
use crate::VERSION;
|
|
||||||
|
|
||||||
pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
|
pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
|
||||||
pub static AURA_CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub gfx_mode: GfxVendors,
|
/// Save charge limit for restoring on boot
|
||||||
/// Only for informational purposes.
|
|
||||||
#[serde(skip)]
|
|
||||||
pub gfx_tmp_mode: Option<GfxVendors>,
|
|
||||||
pub gfx_managed: bool,
|
|
||||||
pub gfx_vfio_enable: bool,
|
|
||||||
pub active_profile: String,
|
|
||||||
pub toggle_profiles: Vec<String>,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub curr_fan_mode: u8,
|
|
||||||
pub bat_charge_limit: u8,
|
pub bat_charge_limit: u8,
|
||||||
pub power_profiles: BTreeMap<String, Profile>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Config {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut pwr = BTreeMap::new();
|
|
||||||
pwr.insert(
|
|
||||||
"normal".into(),
|
|
||||||
Profile::new(
|
|
||||||
"normal".into(),
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
true,
|
|
||||||
FanLevel::Normal,
|
|
||||||
"".to_string(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
pwr.insert(
|
|
||||||
"boost".into(),
|
|
||||||
Profile::new(
|
|
||||||
"boost".into(),
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
true,
|
|
||||||
FanLevel::Boost,
|
|
||||||
"".to_string(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
pwr.insert(
|
|
||||||
"silent".into(),
|
|
||||||
Profile::new(
|
|
||||||
"silent".into(),
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
false,
|
|
||||||
FanLevel::Silent,
|
|
||||||
"".to_string(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Config {
|
|
||||||
gfx_mode: GfxVendors::Hybrid,
|
|
||||||
gfx_tmp_mode: None,
|
|
||||||
gfx_managed: true,
|
|
||||||
gfx_vfio_enable: false,
|
|
||||||
active_profile: "normal".into(),
|
|
||||||
toggle_profiles: vec!["normal".into(), "boost".into(), "silent".into()],
|
|
||||||
curr_fan_mode: 0,
|
|
||||||
bat_charge_limit: 100,
|
|
||||||
power_profiles: pwr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
fn new() -> Self {
|
||||||
|
Config {
|
||||||
|
bat_charge_limit: 100,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// `load` will attempt to read the config, and panic if the dir is missing
|
/// `load` will attempt to read the config, and panic if the dir is missing
|
||||||
pub fn load() -> Self {
|
pub fn load() -> Self {
|
||||||
let mut file = OpenOptions::new()
|
let mut file = OpenOptions::new()
|
||||||
.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;
|
||||||
if let Ok(read_len) = file.read_to_string(&mut buf) {
|
if let Ok(read_len) = file.read_to_string(&mut buf) {
|
||||||
if read_len == 0 {
|
if read_len == 0 {
|
||||||
return Config::create_default(&mut file);
|
config = Self::new();
|
||||||
|
} else if let Ok(data) = serde_json::from_str(&buf) {
|
||||||
|
config = data;
|
||||||
} else {
|
} else {
|
||||||
if let Ok(data) = serde_json::from_str(&buf) {
|
warn!(
|
||||||
return data;
|
"Could not deserialise {}.\nWill rename to {}-old and recreate config",
|
||||||
} else if let Ok(data) = serde_json::from_str::<ConfigV352>(&buf) {
|
CONFIG_PATH, CONFIG_PATH
|
||||||
let config = data.into_current();
|
);
|
||||||
config.write();
|
let cfg_old = CONFIG_PATH.to_string() + "-old";
|
||||||
info!("Updated config version to: {}", VERSION);
|
std::fs::rename(CONFIG_PATH, cfg_old).unwrap_or_else(|err| {
|
||||||
return config;
|
panic!(
|
||||||
} else if let Ok(data) = serde_json::from_str::<ConfigV341>(&buf) {
|
"Could not rename. Please remove {} then restart service: Error {}",
|
||||||
let config = data.into_current();
|
CONFIG_PATH, err
|
||||||
config.write();
|
)
|
||||||
info!("Updated config version to: {}", VERSION);
|
});
|
||||||
return config;
|
config = Self::new();
|
||||||
} else if let Ok(data) = serde_json::from_str::<ConfigV324>(&buf) {
|
|
||||||
let config = data.into_current();
|
|
||||||
config.write();
|
|
||||||
info!("Updated config version to: {}", VERSION);
|
|
||||||
return config;
|
|
||||||
} else if let Ok(data) = serde_json::from_str::<ConfigV317>(&buf) {
|
|
||||||
let config = data.into_current();
|
|
||||||
config.write();
|
|
||||||
info!("Updated config version to: {}", VERSION);
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
warn!("Could not deserialise {}", CONFIG_PATH);
|
|
||||||
panic!("Please remove {} then restart asusd", CONFIG_PATH);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
config = Self::new()
|
||||||
}
|
}
|
||||||
Config::create_default(&mut file)
|
config.write();
|
||||||
}
|
|
||||||
|
|
||||||
fn create_default(file: &mut File) -> Self {
|
|
||||||
let config = Config::default();
|
|
||||||
// Should be okay to unwrap this as is since it is a Default
|
|
||||||
let json = serde_json::to_string_pretty(&config).unwrap();
|
|
||||||
file.write_all(json.as_bytes())
|
|
||||||
.unwrap_or_else(|_| panic!("Could not write {}", CONFIG_PATH));
|
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,12 +65,8 @@ impl Config {
|
|||||||
if l == 0 {
|
if l == 0 {
|
||||||
warn!("File is empty {}", CONFIG_PATH);
|
warn!("File is empty {}", CONFIG_PATH);
|
||||||
} else {
|
} else {
|
||||||
let mut x: Config = serde_json::from_str(&buf)
|
*self = serde_json::from_str(&buf)
|
||||||
.unwrap_or_else(|_| panic!("Could not deserialise {}", CONFIG_PATH));
|
.unwrap_or_else(|_| panic!("Could not deserialise {}", CONFIG_PATH));
|
||||||
// copy over serde skipped values
|
|
||||||
x.gfx_tmp_mode = self.gfx_tmp_mode;
|
|
||||||
x.curr_fan_mode = self.curr_fan_mode;
|
|
||||||
*self = x;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,267 +0,0 @@
|
|||||||
use crate::laptops::LaptopLedData;
|
|
||||||
use log::{error, info, warn};
|
|
||||||
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, LedBrightness};
|
|
||||||
use serde_derive::{Deserialize, Serialize};
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::fs::{File, OpenOptions};
|
|
||||||
use std::io::{Read, Write};
|
|
||||||
|
|
||||||
pub static AURA_CONFIG_PATH: &str = "/etc/asusd/aura.conf";
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct AuraConfigV320 {
|
|
||||||
pub brightness: u32,
|
|
||||||
pub current_mode: AuraModeNum,
|
|
||||||
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
|
|
||||||
pub multizone: Option<AuraMultiZone>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AuraConfigV320 {
|
|
||||||
pub(crate) fn into_current(self) -> AuraConfig {
|
|
||||||
AuraConfig {
|
|
||||||
brightness: <LedBrightness>::from(self.brightness),
|
|
||||||
current_mode: self.current_mode,
|
|
||||||
builtins: self.builtins,
|
|
||||||
multizone: self.multizone,
|
|
||||||
awake_enabled: true,
|
|
||||||
sleep_anim_enabled: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct AuraConfigV352 {
|
|
||||||
pub brightness: LedBrightness,
|
|
||||||
pub current_mode: AuraModeNum,
|
|
||||||
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
|
|
||||||
pub multizone: Option<AuraMultiZone>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AuraConfigV352 {
|
|
||||||
pub(crate) fn into_current(self) -> AuraConfig {
|
|
||||||
AuraConfig {
|
|
||||||
brightness: self.brightness,
|
|
||||||
current_mode: self.current_mode,
|
|
||||||
builtins: self.builtins,
|
|
||||||
multizone: self.multizone,
|
|
||||||
awake_enabled: true,
|
|
||||||
sleep_anim_enabled: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct AuraConfig {
|
|
||||||
pub brightness: LedBrightness,
|
|
||||||
pub current_mode: AuraModeNum,
|
|
||||||
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
|
|
||||||
pub multizone: Option<AuraMultiZone>,
|
|
||||||
pub awake_enabled: bool,
|
|
||||||
pub sleep_anim_enabled: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for AuraConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
AuraConfig {
|
|
||||||
brightness: LedBrightness::Med,
|
|
||||||
current_mode: AuraModeNum::Static,
|
|
||||||
builtins: BTreeMap::new(),
|
|
||||||
multizone: None,
|
|
||||||
awake_enabled: true,
|
|
||||||
sleep_anim_enabled: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AuraConfig {
|
|
||||||
/// `load` will attempt to read the config, and panic if the dir is missing
|
|
||||||
pub fn load(supported_led_modes: &LaptopLedData) -> Self {
|
|
||||||
let mut file = OpenOptions::new()
|
|
||||||
.read(true)
|
|
||||||
.write(true)
|
|
||||||
.create(true)
|
|
||||||
.open(&AURA_CONFIG_PATH)
|
|
||||||
.unwrap_or_else(|_| {
|
|
||||||
panic!(
|
|
||||||
"The file {} or directory /etc/asusd/ is missing",
|
|
||||||
AURA_CONFIG_PATH
|
|
||||||
)
|
|
||||||
}); // okay to cause panic here
|
|
||||||
let mut buf = String::new();
|
|
||||||
if let Ok(read_len) = file.read_to_string(&mut buf) {
|
|
||||||
if read_len == 0 {
|
|
||||||
return AuraConfig::create_default(&mut file, supported_led_modes);
|
|
||||||
} else {
|
|
||||||
if let Ok(data) = serde_json::from_str(&buf) {
|
|
||||||
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);
|
|
||||||
panic!("Please remove {} then restart asusd", AURA_CONFIG_PATH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AuraConfig::create_default(&mut file, supported_led_modes)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_default(file: &mut File, support_data: &LaptopLedData) -> Self {
|
|
||||||
// create a default config here
|
|
||||||
let mut config = AuraConfig::default();
|
|
||||||
|
|
||||||
for n in &support_data.standard {
|
|
||||||
config
|
|
||||||
.builtins
|
|
||||||
.insert(*n, AuraEffect::default_with_mode(*n));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should be okay to unwrap this as is since it is a Default
|
|
||||||
let json = serde_json::to_string(&config).unwrap();
|
|
||||||
file.write_all(json.as_bytes())
|
|
||||||
.unwrap_or_else(|_| panic!("Could not write {}", AURA_CONFIG_PATH));
|
|
||||||
config
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read(&mut self) {
|
|
||||||
let mut file = OpenOptions::new()
|
|
||||||
.read(true)
|
|
||||||
.open(&AURA_CONFIG_PATH)
|
|
||||||
.unwrap_or_else(|err| panic!("Error reading {}: {}", AURA_CONFIG_PATH, err));
|
|
||||||
let mut buf = String::new();
|
|
||||||
if let Ok(l) = file.read_to_string(&mut buf) {
|
|
||||||
if l == 0 {
|
|
||||||
warn!("File is empty {}", AURA_CONFIG_PATH);
|
|
||||||
} else {
|
|
||||||
let x: AuraConfig = serde_json::from_str(&buf)
|
|
||||||
.unwrap_or_else(|_| panic!("Could not deserialise {}", AURA_CONFIG_PATH));
|
|
||||||
*self = x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(&self) {
|
|
||||||
let mut file = File::create(AURA_CONFIG_PATH).expect("Couldn't overwrite config");
|
|
||||||
let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed");
|
|
||||||
file.write_all(json.as_bytes())
|
|
||||||
.unwrap_or_else(|err| error!("Could not write config: {}", err));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Multipurpose, will accept AuraEffect with zones and put in the correct store
|
|
||||||
pub fn set_builtin(&mut self, effect: AuraEffect) {
|
|
||||||
match effect.zone() {
|
|
||||||
AuraZone::None => {
|
|
||||||
self.builtins.insert(*effect.mode(), effect);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
if let Some(multi) = self.multizone.as_mut() {
|
|
||||||
multi.set(effect)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_multizone(&self, aura_type: AuraModeNum) -> Option<&[AuraEffect; 4]> {
|
|
||||||
if let Some(multi) = &self.multizone {
|
|
||||||
if aura_type == AuraModeNum::Static {
|
|
||||||
return Some(multi.static_());
|
|
||||||
} else if aura_type == AuraModeNum::Breathe {
|
|
||||||
return Some(multi.breathe());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct AuraMultiZone {
|
|
||||||
static_: [AuraEffect; 4],
|
|
||||||
breathe: [AuraEffect; 4],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AuraMultiZone {
|
|
||||||
pub fn set(&mut self, effect: AuraEffect) {
|
|
||||||
if effect.mode == AuraModeNum::Static {
|
|
||||||
match effect.zone {
|
|
||||||
AuraZone::None => {}
|
|
||||||
AuraZone::One => self.static_[0] = effect,
|
|
||||||
AuraZone::Two => self.static_[1] = effect,
|
|
||||||
AuraZone::Three => self.static_[2] = effect,
|
|
||||||
AuraZone::Four => self.static_[3] = effect,
|
|
||||||
}
|
|
||||||
} else if effect.mode == AuraModeNum::Breathe {
|
|
||||||
match effect.zone {
|
|
||||||
AuraZone::None => {}
|
|
||||||
AuraZone::One => self.breathe[0] = effect,
|
|
||||||
AuraZone::Two => self.breathe[1] = effect,
|
|
||||||
AuraZone::Three => self.breathe[2] = effect,
|
|
||||||
AuraZone::Four => self.breathe[3] = effect,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn static_(&self) -> &[AuraEffect; 4] {
|
|
||||||
&self.static_
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn breathe(&self) -> &[AuraEffect; 4] {
|
|
||||||
&self.breathe
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for AuraMultiZone {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
static_: [
|
|
||||||
AuraEffect {
|
|
||||||
mode: AuraModeNum::Static,
|
|
||||||
zone: AuraZone::One,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AuraEffect {
|
|
||||||
mode: AuraModeNum::Static,
|
|
||||||
zone: AuraZone::Two,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AuraEffect {
|
|
||||||
mode: AuraModeNum::Static,
|
|
||||||
zone: AuraZone::Three,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AuraEffect {
|
|
||||||
mode: AuraModeNum::Static,
|
|
||||||
zone: AuraZone::Four,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
],
|
|
||||||
breathe: [
|
|
||||||
AuraEffect {
|
|
||||||
mode: AuraModeNum::Breathe,
|
|
||||||
zone: AuraZone::One,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AuraEffect {
|
|
||||||
mode: AuraModeNum::Breathe,
|
|
||||||
zone: AuraZone::Two,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AuraEffect {
|
|
||||||
mode: AuraModeNum::Breathe,
|
|
||||||
zone: AuraZone::Three,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AuraEffect {
|
|
||||||
mode: AuraModeNum::Breathe,
|
|
||||||
zone: AuraZone::Four,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
use rog_fan_curve::Curve;
|
|
||||||
use rog_profiles::profiles::Profile;
|
|
||||||
use rog_types::gfx_vendors::GfxVendors;
|
|
||||||
use serde_derive::{Deserialize, Serialize};
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use crate::config::Config;
|
|
||||||
|
|
||||||
/// for parsing old v3.1.7 config
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub(crate) struct ConfigV317 {
|
|
||||||
pub gfx_mode: GfxVendors,
|
|
||||||
pub gfx_managed: bool,
|
|
||||||
pub active_profile: String,
|
|
||||||
pub toggle_profiles: Vec<String>,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub curr_fan_mode: u8,
|
|
||||||
pub bat_charge_limit: u8,
|
|
||||||
pub kbd_led_brightness: u8,
|
|
||||||
pub kbd_backlight_mode: u8,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub kbd_backlight_modes: Option<bool>,
|
|
||||||
pub power_profiles: BTreeMap<String, ProfileV317>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConfigV317 {
|
|
||||||
pub(crate) fn into_current(self) -> Config {
|
|
||||||
Config {
|
|
||||||
gfx_mode: self.gfx_mode,
|
|
||||||
gfx_tmp_mode: None,
|
|
||||||
gfx_managed: self.gfx_managed,
|
|
||||||
gfx_vfio_enable: false,
|
|
||||||
active_profile: self.active_profile,
|
|
||||||
toggle_profiles: self.toggle_profiles,
|
|
||||||
curr_fan_mode: self.curr_fan_mode,
|
|
||||||
bat_charge_limit: self.bat_charge_limit,
|
|
||||||
power_profiles: ProfileV317::transform_map(self.power_profiles),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct ConfigV324 {
|
|
||||||
pub gfx_mode: GfxVendors,
|
|
||||||
pub gfx_managed: bool,
|
|
||||||
pub active_profile: String,
|
|
||||||
pub toggle_profiles: Vec<String>,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub curr_fan_mode: u8,
|
|
||||||
pub bat_charge_limit: u8,
|
|
||||||
pub power_profiles: BTreeMap<String, ProfileV317>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConfigV324 {
|
|
||||||
pub(crate) fn into_current(self) -> Config {
|
|
||||||
Config {
|
|
||||||
gfx_mode: GfxVendors::Hybrid,
|
|
||||||
gfx_tmp_mode: None,
|
|
||||||
gfx_managed: self.gfx_managed,
|
|
||||||
gfx_vfio_enable: false,
|
|
||||||
active_profile: self.active_profile,
|
|
||||||
toggle_profiles: self.toggle_profiles,
|
|
||||||
curr_fan_mode: self.curr_fan_mode,
|
|
||||||
bat_charge_limit: self.bat_charge_limit,
|
|
||||||
power_profiles: ProfileV317::transform_map(self.power_profiles),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct ConfigV341 {
|
|
||||||
pub gfx_mode: GfxVendors,
|
|
||||||
pub gfx_managed: bool,
|
|
||||||
pub gfx_vfio_enable: bool,
|
|
||||||
pub active_profile: String,
|
|
||||||
pub toggle_profiles: Vec<String>,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub curr_fan_mode: u8,
|
|
||||||
pub bat_charge_limit: u8,
|
|
||||||
pub power_profiles: BTreeMap<String, ProfileV317>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConfigV341 {
|
|
||||||
pub(crate) fn into_current(self) -> Config {
|
|
||||||
Config {
|
|
||||||
gfx_mode: GfxVendors::Hybrid,
|
|
||||||
gfx_tmp_mode: None,
|
|
||||||
gfx_managed: self.gfx_managed,
|
|
||||||
gfx_vfio_enable: false,
|
|
||||||
active_profile: self.active_profile,
|
|
||||||
toggle_profiles: self.toggle_profiles,
|
|
||||||
curr_fan_mode: self.curr_fan_mode,
|
|
||||||
bat_charge_limit: self.bat_charge_limit,
|
|
||||||
power_profiles: ProfileV317::transform_map(self.power_profiles),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct ConfigV352 {
|
|
||||||
pub gfx_mode: GfxVendors,
|
|
||||||
pub gfx_last_mode: GfxVendors,
|
|
||||||
pub gfx_managed: bool,
|
|
||||||
pub gfx_vfio_enable: bool,
|
|
||||||
pub gfx_save_compute_vfio: bool,
|
|
||||||
pub active_profile: String,
|
|
||||||
pub toggle_profiles: Vec<String>,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub curr_fan_mode: u8,
|
|
||||||
pub bat_charge_limit: u8,
|
|
||||||
pub power_profiles: BTreeMap<String, ProfileV317>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConfigV352 {
|
|
||||||
pub(crate) fn into_current(self) -> Config {
|
|
||||||
Config {
|
|
||||||
gfx_mode: GfxVendors::Hybrid,
|
|
||||||
gfx_tmp_mode: None,
|
|
||||||
gfx_managed: self.gfx_managed,
|
|
||||||
gfx_vfio_enable: false,
|
|
||||||
active_profile: self.active_profile,
|
|
||||||
toggle_profiles: self.toggle_profiles,
|
|
||||||
curr_fan_mode: self.curr_fan_mode,
|
|
||||||
bat_charge_limit: self.bat_charge_limit,
|
|
||||||
power_profiles: ProfileV317::transform_map(self.power_profiles),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct ProfileV317 {
|
|
||||||
pub min_percentage: u8,
|
|
||||||
pub max_percentage: u8,
|
|
||||||
pub turbo: bool,
|
|
||||||
pub fan_preset: u8,
|
|
||||||
pub fan_curve: Option<Curve>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProfileV317 {
|
|
||||||
fn into_current(self, name: String) -> Profile {
|
|
||||||
Profile {
|
|
||||||
name,
|
|
||||||
min_percentage: self.min_percentage,
|
|
||||||
max_percentage: self.max_percentage,
|
|
||||||
turbo: self.turbo,
|
|
||||||
fan_preset: self.fan_preset.into(),
|
|
||||||
fan_curve: self
|
|
||||||
.fan_curve
|
|
||||||
.map_or_else(|| "".to_string(), |c| c.as_config_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transform_map(map: BTreeMap<String, ProfileV317>) -> BTreeMap<String, Profile> {
|
|
||||||
let mut new_map = BTreeMap::new();
|
|
||||||
map.iter().for_each(|(k, v)| {
|
|
||||||
new_map.insert(k.to_string(), v.clone().into_current(k.to_string()));
|
|
||||||
});
|
|
||||||
new_map
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,456 +0,0 @@
|
|||||||
use log::{error, info, warn};
|
|
||||||
use logind_zbus::ManagerProxy;
|
|
||||||
use rog_anime::{
|
|
||||||
usb::{
|
|
||||||
pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on, pkts_for_init, PROD_ID,
|
|
||||||
VENDOR_ID,
|
|
||||||
},
|
|
||||||
ActionData, AnimeDataBuffer, AnimePacketType, AnimePowerStates, ANIME_DATA_LEN,
|
|
||||||
};
|
|
||||||
use rog_types::supported::AnimeSupportedFunctions;
|
|
||||||
use rusb::{Device, DeviceHandle};
|
|
||||||
use std::{
|
|
||||||
error::Error,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
thread::sleep,
|
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
sync::atomic::{AtomicBool, Ordering},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
use zbus::{dbus_interface, Connection};
|
|
||||||
use zvariant::ObjectPath;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
config_anime::{AnimeConfig, AnimeConfigCached},
|
|
||||||
error::RogError,
|
|
||||||
GetSupported,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl GetSupported for CtrlAnime {
|
|
||||||
type A = AnimeSupportedFunctions;
|
|
||||||
|
|
||||||
fn get_supported() -> Self::A {
|
|
||||||
AnimeSupportedFunctions(CtrlAnime::get_device(VENDOR_ID, PROD_ID).is_ok())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CtrlAnime {
|
|
||||||
handle: DeviceHandle<rusb::GlobalContext>,
|
|
||||||
cache: AnimeConfigCached,
|
|
||||||
config: AnimeConfig,
|
|
||||||
// set to force thread to exit
|
|
||||||
thread_exit: Arc<AtomicBool>,
|
|
||||||
// Set to false when the thread exits
|
|
||||||
thread_running: Arc<AtomicBool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CtrlAnime {
|
|
||||||
#[inline]
|
|
||||||
pub fn new(config: AnimeConfig) -> Result<CtrlAnime, Box<dyn Error>> {
|
|
||||||
// We don't expect this ID to ever change
|
|
||||||
let device = CtrlAnime::get_device(0x0b05, 0x193b)?;
|
|
||||||
|
|
||||||
let mut device = device.open()?;
|
|
||||||
device.reset()?;
|
|
||||||
|
|
||||||
device.set_auto_detach_kernel_driver(true).map_err(|err| {
|
|
||||||
error!("Auto-detach kernel driver failed: {}", err);
|
|
||||||
err
|
|
||||||
})?;
|
|
||||||
|
|
||||||
device.claim_interface(0).map_err(|err| {
|
|
||||||
error!("Could not claim device interface: {}", err);
|
|
||||||
err
|
|
||||||
})?;
|
|
||||||
|
|
||||||
info!("Device has an AniMe Matrix display");
|
|
||||||
let mut cache = AnimeConfigCached::default();
|
|
||||||
cache.init_from_config(&config)?;
|
|
||||||
|
|
||||||
let ctrl = CtrlAnime {
|
|
||||||
handle: device,
|
|
||||||
cache,
|
|
||||||
config,
|
|
||||||
thread_exit: Arc::new(AtomicBool::new(false)),
|
|
||||||
thread_running: Arc::new(AtomicBool::new(false)),
|
|
||||||
};
|
|
||||||
ctrl.do_initialization();
|
|
||||||
|
|
||||||
Ok(ctrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_device(vendor: u16, product: u16) -> Result<Device<rusb::GlobalContext>, rusb::Error> {
|
|
||||||
for device in rusb::devices()?.iter() {
|
|
||||||
let device_desc = device.device_descriptor()?;
|
|
||||||
if device_desc.vendor_id() == vendor && device_desc.product_id() == product {
|
|
||||||
return Ok(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(rusb::Error::NoDevice)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start an action thread. This is classed as a singleton and there should be only
|
|
||||||
/// one running - so the thread uses atomics to signal run/exit.
|
|
||||||
///
|
|
||||||
/// Because this also writes to the usb device, other write tries (display only) *must*
|
|
||||||
/// get the mutex lock and set the thread_exit atomic.
|
|
||||||
fn run_thread(inner: Arc<Mutex<CtrlAnime>>, actions: Vec<ActionData>, mut once: bool) {
|
|
||||||
if actions.is_empty() {
|
|
||||||
warn!("AniMe system actions was empty");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Loop rules:
|
|
||||||
// - Lock the mutex **only when required**. That is, the lock must be held for the shortest duration possible.
|
|
||||||
// - An AtomicBool used for thread exit should be checked in every loop, including nested
|
|
||||||
|
|
||||||
// The only reason for this outer thread is to prevent blocking while waiting for the
|
|
||||||
// next spawned thread to exit
|
|
||||||
std::thread::Builder::new()
|
|
||||||
.name("AniMe system thread start".into())
|
|
||||||
.spawn(move || {
|
|
||||||
info!("AniMe system thread started");
|
|
||||||
// Getting copies of these Atomics is done *in* the thread to ensure
|
|
||||||
// we don't block other threads/main
|
|
||||||
let thread_exit;
|
|
||||||
let thread_running;
|
|
||||||
// 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.
|
|
||||||
loop {
|
|
||||||
if let Ok(lock) = inner.try_lock() {
|
|
||||||
thread_exit = lock.thread_exit.clone();
|
|
||||||
thread_running = lock.thread_running.clone();
|
|
||||||
// Make any running loop exit first
|
|
||||||
thread_exit.store(true, Ordering::SeqCst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
// wait for other threads to set not running so we know they exited
|
|
||||||
if !thread_running.load(Ordering::SeqCst) {
|
|
||||||
thread_exit.store(false, Ordering::SeqCst);
|
|
||||||
info!("AniMe forced a thread to exit");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
'main: loop {
|
|
||||||
if thread_exit.load(Ordering::SeqCst) {
|
|
||||||
break 'main;
|
|
||||||
}
|
|
||||||
for action in actions.iter() {
|
|
||||||
match action {
|
|
||||||
ActionData::Animation(frames) => {
|
|
||||||
rog_anime::run_animation(frames, thread_exit.clone(), &|frame| {
|
|
||||||
if let Ok(lock) = inner.try_lock() {
|
|
||||||
lock.write_data_buffer(frame);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if thread_exit.load(Ordering::SeqCst) {
|
|
||||||
break 'main;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ActionData::Image(image) => {
|
|
||||||
once = false;
|
|
||||||
if let Ok(lock) = inner.try_lock() {
|
|
||||||
lock.write_data_buffer(image.as_ref().clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ActionData::Pause(duration) => sleep(*duration),
|
|
||||||
ActionData::AudioEq => {}
|
|
||||||
ActionData::SystemInfo => {}
|
|
||||||
ActionData::TimeDate => {}
|
|
||||||
ActionData::Matrix => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if once || actions.is_empty() {
|
|
||||||
break 'main;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Clear the display on exit
|
|
||||||
if let Ok(lock) = inner.try_lock() {
|
|
||||||
let data = AnimeDataBuffer::from_vec([0u8; ANIME_DATA_LEN].to_vec());
|
|
||||||
lock.write_data_buffer(data);
|
|
||||||
}
|
|
||||||
// Loop ended, set the atmonics
|
|
||||||
thread_exit.store(false, Ordering::SeqCst);
|
|
||||||
thread_running.store(false, Ordering::SeqCst);
|
|
||||||
info!("AniMe system thread exited");
|
|
||||||
})
|
|
||||||
.map(|err| info!("AniMe system thread: {:?}", err))
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_bytes(&self, message: &[u8]) {
|
|
||||||
match self.handle.write_control(
|
|
||||||
0x21, // request_type
|
|
||||||
0x09, // request
|
|
||||||
0x35e, // value
|
|
||||||
0x00, // index
|
|
||||||
message,
|
|
||||||
Duration::from_millis(200),
|
|
||||||
) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(err) => match err {
|
|
||||||
rusb::Error::Timeout => {}
|
|
||||||
_ => error!("Failed to write to led interrupt: {}", err),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write only a data packet. This will modify the leds brightness using the
|
|
||||||
/// global brightness set in config.
|
|
||||||
fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) {
|
|
||||||
for led in buffer.get_mut()[7..].iter_mut() {
|
|
||||||
let mut bright = *led as f32 * self.config.brightness;
|
|
||||||
if bright > 254.0 {
|
|
||||||
bright = 254.0;
|
|
||||||
}
|
|
||||||
*led = bright as u8;
|
|
||||||
}
|
|
||||||
let data = AnimePacketType::from(buffer);
|
|
||||||
for row in data.iter() {
|
|
||||||
self.write_bytes(row);
|
|
||||||
}
|
|
||||||
self.write_bytes(&pkt_for_flush());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn do_initialization(&self) {
|
|
||||||
let pkts = pkts_for_init();
|
|
||||||
self.write_bytes(&pkts[0]);
|
|
||||||
self.write_bytes(&pkts[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CtrlAnimeTask<'a> {
|
|
||||||
inner: Arc<Mutex<CtrlAnime>>,
|
|
||||||
_c: Connection,
|
|
||||||
manager: ManagerProxy<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> CtrlAnimeTask<'a> {
|
|
||||||
pub fn new(inner: Arc<Mutex<CtrlAnime>>) -> Self {
|
|
||||||
let connection = Connection::new_system().unwrap();
|
|
||||||
|
|
||||||
let manager = ManagerProxy::new(&connection).unwrap();
|
|
||||||
|
|
||||||
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> {
|
|
||||||
fn do_task(&self) -> Result<(), RogError> {
|
|
||||||
if let Ok(mut lock) = self.inner.try_lock() {
|
|
||||||
// Refresh the config and cache incase the user has edited it
|
|
||||||
let config = AnimeConfig::load();
|
|
||||||
lock.cache
|
|
||||||
.init_from_config(&config)
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!("CtrlAnimeTask: do_task {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for signals on each task iteration, this will run the callbacks
|
|
||||||
// if any signal is recieved
|
|
||||||
self.manager.next_signal()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CtrlAnimeReloader(pub Arc<Mutex<CtrlAnime>>);
|
|
||||||
|
|
||||||
impl crate::Reloadable for CtrlAnimeReloader {
|
|
||||||
fn reload(&mut self) -> Result<(), RogError> {
|
|
||||||
if let Ok(lock) = self.0.try_lock() {
|
|
||||||
lock.write_bytes(&pkt_for_set_on(lock.config.awake_enabled));
|
|
||||||
lock.write_bytes(&pkt_for_apply());
|
|
||||||
lock.write_bytes(&pkt_for_set_boot(lock.config.boot_anim_enabled));
|
|
||||||
lock.write_bytes(&pkt_for_apply());
|
|
||||||
|
|
||||||
let action = lock.cache.boot.clone();
|
|
||||||
CtrlAnime::run_thread(self.0.clone(), action, true);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>);
|
|
||||||
|
|
||||||
/// The struct with the main dbus methods requires this trait
|
|
||||||
impl crate::ZbusAdd for CtrlAnimeZbus {
|
|
||||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
|
||||||
server
|
|
||||||
.at(
|
|
||||||
&ObjectPath::from_str_unchecked("/org/asuslinux/Anime"),
|
|
||||||
self,
|
|
||||||
)
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!("CtrlAnimeDisplay: add_to_server {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// None of these calls can be guarnateed to succeed unless we loop until okay
|
|
||||||
// If the try_lock *does* succeed then any other thread trying to lock will not grab it
|
|
||||||
// until we finish.
|
|
||||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
|
||||||
impl CtrlAnimeZbus {
|
|
||||||
/// Writes a data stream of length. Will force system thread to exit until it is restarted
|
|
||||||
fn write(&self, input: AnimeDataBuffer) {
|
|
||||||
'outer: loop {
|
|
||||||
if let Ok(lock) = self.0.try_lock() {
|
|
||||||
lock.thread_exit.store(true, Ordering::SeqCst);
|
|
||||||
lock.write_data_buffer(input);
|
|
||||||
break 'outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the global AniMe brightness
|
|
||||||
fn set_brightness(&self, bright: f32) {
|
|
||||||
'outer: loop {
|
|
||||||
if let Ok(mut lock) = self.0.try_lock() {
|
|
||||||
let mut bright = bright;
|
|
||||||
if bright < 0.0 {
|
|
||||||
bright = 0.0
|
|
||||||
} else if bright > 254.0 {
|
|
||||||
bright = 254.0;
|
|
||||||
}
|
|
||||||
lock.config.brightness = bright;
|
|
||||||
lock.config.write();
|
|
||||||
break 'outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set whether the AniMe is displaying images/data
|
|
||||||
fn set_on_off(&self, status: bool) {
|
|
||||||
'outer: loop {
|
|
||||||
if let Ok(mut lock) = self.0.try_lock() {
|
|
||||||
lock.write_bytes(&pkt_for_set_on(status));
|
|
||||||
lock.config.awake_enabled = status;
|
|
||||||
lock.config.write();
|
|
||||||
|
|
||||||
let states = AnimePowerStates {
|
|
||||||
enabled: lock.config.awake_enabled,
|
|
||||||
boot_anim_enabled: lock.config.boot_anim_enabled,
|
|
||||||
};
|
|
||||||
self.notify_power_states(&states)
|
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
|
||||||
break 'outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set whether the AniMe will show boot, suspend, or off animations
|
|
||||||
fn set_boot_on_off(&self, on: bool) {
|
|
||||||
'outer: loop {
|
|
||||||
if let Ok(mut lock) = self.0.try_lock() {
|
|
||||||
lock.write_bytes(&pkt_for_set_boot(on));
|
|
||||||
lock.write_bytes(&pkt_for_apply());
|
|
||||||
lock.config.boot_anim_enabled = on;
|
|
||||||
lock.config.write();
|
|
||||||
|
|
||||||
let states = AnimePowerStates {
|
|
||||||
enabled: lock.config.awake_enabled,
|
|
||||||
boot_anim_enabled: lock.config.boot_anim_enabled,
|
|
||||||
};
|
|
||||||
self.notify_power_states(&states)
|
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
|
||||||
break 'outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The main loop is the base system set action if the user isn't running
|
|
||||||
/// the user daemon
|
|
||||||
fn run_main_loop(&self, start: bool) {
|
|
||||||
if start {
|
|
||||||
'outer: loop {
|
|
||||||
if let Ok(lock) = self.0.try_lock() {
|
|
||||||
lock.thread_exit.store(true, Ordering::SeqCst);
|
|
||||||
CtrlAnime::run_thread(self.0.clone(), lock.cache.system.clone(), false);
|
|
||||||
break 'outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get status of if the AniMe LEDs are on
|
|
||||||
#[dbus_interface(property)]
|
|
||||||
fn awake_enabled(&self) -> bool {
|
|
||||||
if let Ok(ctrl) = self.0.try_lock() {
|
|
||||||
return ctrl.config.awake_enabled;
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the status of if factory system-status animations are enabled
|
|
||||||
#[dbus_interface(property)]
|
|
||||||
fn boot_enabled(&self) -> bool {
|
|
||||||
if let Ok(ctrl) = self.0.try_lock() {
|
|
||||||
return ctrl.config.boot_anim_enabled;
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Notify listeners of the status of AniMe LED power and factory system-status animations
|
|
||||||
#[dbus_interface(signal)]
|
|
||||||
fn notify_power_states(&self, data: &AnimePowerStates) -> zbus::Result<()>;
|
|
||||||
}
|
|
||||||
@@ -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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,375 @@
|
|||||||
|
pub mod config;
|
||||||
|
pub mod zbus;
|
||||||
|
|
||||||
|
use ::zbus::Connection;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use log::{error, info, warn};
|
||||||
|
use logind_zbus::manager::ManagerProxy;
|
||||||
|
use rog_anime::{
|
||||||
|
error::AnimeError,
|
||||||
|
usb::{
|
||||||
|
find_node, get_anime_type, pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on,
|
||||||
|
pkts_for_init, PROD_ID, VENDOR_ID,
|
||||||
|
},
|
||||||
|
ActionData, AnimeDataBuffer, AnimePacketType, AnimeType,
|
||||||
|
};
|
||||||
|
use rog_supported::AnimeSupportedFunctions;
|
||||||
|
use rusb::{Device, DeviceHandle};
|
||||||
|
use smol::{stream::StreamExt, Executor};
|
||||||
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
|
error::Error,
|
||||||
|
sync::{Arc, Mutex, MutexGuard},
|
||||||
|
thread::sleep,
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{error::RogError, GetSupported};
|
||||||
|
|
||||||
|
use self::config::{AnimeConfig, AnimeConfigCached};
|
||||||
|
|
||||||
|
impl GetSupported for CtrlAnime {
|
||||||
|
type A = AnimeSupportedFunctions;
|
||||||
|
|
||||||
|
fn get_supported() -> Self::A {
|
||||||
|
AnimeSupportedFunctions(CtrlAnime::get_device(VENDOR_ID, PROD_ID).is_ok())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CtrlAnime {
|
||||||
|
_node: String,
|
||||||
|
anime_type: AnimeType,
|
||||||
|
handle: RefCell<DeviceHandle<rusb::GlobalContext>>,
|
||||||
|
cache: AnimeConfigCached,
|
||||||
|
config: AnimeConfig,
|
||||||
|
// set to force thread to exit
|
||||||
|
thread_exit: Arc<AtomicBool>,
|
||||||
|
// Set to false when the thread exits
|
||||||
|
thread_running: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CtrlAnime {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(config: AnimeConfig) -> Result<CtrlAnime, Box<dyn Error>> {
|
||||||
|
let node = find_node("193b")?;
|
||||||
|
let anime_type = get_anime_type()?;
|
||||||
|
let device = Self::get_dev_handle()?;
|
||||||
|
|
||||||
|
info!("Device has an AniMe Matrix display");
|
||||||
|
let mut cache = AnimeConfigCached::default();
|
||||||
|
cache.init_from_config(&config, anime_type)?;
|
||||||
|
|
||||||
|
let ctrl = CtrlAnime {
|
||||||
|
_node: node,
|
||||||
|
anime_type,
|
||||||
|
handle: RefCell::new(device),
|
||||||
|
cache,
|
||||||
|
config,
|
||||||
|
thread_exit: Arc::new(AtomicBool::new(false)),
|
||||||
|
thread_running: Arc::new(AtomicBool::new(false)),
|
||||||
|
};
|
||||||
|
ctrl.do_initialization();
|
||||||
|
|
||||||
|
Ok(ctrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_dev_handle() -> Result<DeviceHandle<rusb::GlobalContext>, Box<dyn Error>> {
|
||||||
|
// We don't expect this ID to ever change
|
||||||
|
let device = CtrlAnime::get_device(0x0b05, 0x193b)?;
|
||||||
|
|
||||||
|
let mut device = device.open()?;
|
||||||
|
device.reset()?;
|
||||||
|
|
||||||
|
device.set_auto_detach_kernel_driver(true).map_err(|err| {
|
||||||
|
error!("Auto-detach kernel driver failed: {}", err);
|
||||||
|
err
|
||||||
|
})?;
|
||||||
|
|
||||||
|
device.claim_interface(0).map_err(|err| {
|
||||||
|
error!("Could not claim device interface: {}", err);
|
||||||
|
err
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(device)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_device(vendor: u16, product: u16) -> Result<Device<rusb::GlobalContext>, rusb::Error> {
|
||||||
|
for device in rusb::devices()?.iter() {
|
||||||
|
let device_desc = device.device_descriptor()?;
|
||||||
|
if device_desc.vendor_id() == vendor && device_desc.product_id() == product {
|
||||||
|
return Ok(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(rusb::Error::NoDevice)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an action thread. This is classed as a singleton and there should be only
|
||||||
|
/// one running - so the thread uses atomics to signal run/exit.
|
||||||
|
///
|
||||||
|
/// Because this also writes to the usb device, other write tries (display only) *must*
|
||||||
|
/// get the mutex lock and set the thread_exit atomic.
|
||||||
|
fn run_thread(inner: Arc<Mutex<CtrlAnime>>, actions: Vec<ActionData>, mut once: bool) {
|
||||||
|
if actions.is_empty() {
|
||||||
|
warn!("AniMe system actions was empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop rules:
|
||||||
|
// - Lock the mutex **only when required**. That is, the lock must be held for the shortest duration possible.
|
||||||
|
// - An AtomicBool used for thread exit should be checked in every loop, including nested
|
||||||
|
|
||||||
|
// The only reason for this outer thread is to prevent blocking while waiting for the
|
||||||
|
// next spawned thread to exit
|
||||||
|
std::thread::Builder::new()
|
||||||
|
.name("AniMe system thread start".into())
|
||||||
|
.spawn(move || {
|
||||||
|
info!("AniMe new system thread started");
|
||||||
|
// Getting copies of these Atomics is done *in* the thread to ensure
|
||||||
|
// we don't block other threads/main
|
||||||
|
let thread_exit;
|
||||||
|
let thread_running;
|
||||||
|
let anime_type;
|
||||||
|
loop {
|
||||||
|
if let Ok(lock) = inner.try_lock() {
|
||||||
|
thread_exit = lock.thread_exit.clone();
|
||||||
|
thread_running = lock.thread_running.clone();
|
||||||
|
anime_type = lock.anime_type;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// First two loops are to ensure we *do* aquire a lock on the mutex
|
||||||
|
// The reason the loop is required is because the USB writes can block
|
||||||
|
// for up to 10ms. We can't fail to get the atomics.
|
||||||
|
while thread_running.load(Ordering::SeqCst) {
|
||||||
|
// Make any running loop exit first
|
||||||
|
thread_exit.store(true, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("AniMe no previous system thread running (now)");
|
||||||
|
thread_exit.store(false, Ordering::SeqCst);
|
||||||
|
|
||||||
|
'main: loop {
|
||||||
|
thread_running.store(true, Ordering::SeqCst);
|
||||||
|
for action in actions.iter() {
|
||||||
|
if thread_exit.load(Ordering::SeqCst) {
|
||||||
|
break 'main;
|
||||||
|
}
|
||||||
|
match action {
|
||||||
|
ActionData::Animation(frames) => {
|
||||||
|
if let Err(err) = rog_anime::run_animation(frames, &|frame| {
|
||||||
|
if thread_exit.load(Ordering::Acquire) {
|
||||||
|
info!("rog-anime: frame-loop was asked to exit");
|
||||||
|
return Ok(true); // Do safe exit
|
||||||
|
}
|
||||||
|
inner
|
||||||
|
.try_lock()
|
||||||
|
.map(|lock| {
|
||||||
|
lock.write_data_buffer(frame);
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
ActionData::Image(image) => {
|
||||||
|
once = false;
|
||||||
|
if let Ok(lock) = inner.try_lock() {
|
||||||
|
lock.write_data_buffer(image.as_ref().clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActionData::Pause(duration) => sleep(*duration),
|
||||||
|
ActionData::AudioEq => {}
|
||||||
|
ActionData::SystemInfo => {}
|
||||||
|
ActionData::TimeDate => {}
|
||||||
|
ActionData::Matrix => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if thread_exit.load(Ordering::SeqCst) {
|
||||||
|
break 'main;
|
||||||
|
}
|
||||||
|
if once || actions.is_empty() {
|
||||||
|
break 'main;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Clear the display on exit
|
||||||
|
if let Ok(lock) = inner.try_lock() {
|
||||||
|
let data =
|
||||||
|
AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()]);
|
||||||
|
lock.write_data_buffer(data);
|
||||||
|
}
|
||||||
|
// Loop ended, set the atmonics
|
||||||
|
thread_running.store(false, Ordering::SeqCst);
|
||||||
|
info!("AniMe system thread exited");
|
||||||
|
})
|
||||||
|
.map(|err| info!("AniMe system thread: {:?}", err))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_bytes(&self, message: &[u8]) {
|
||||||
|
// if let Ok(mut file) = OpenOptions::new().write(true).open(&self.node) {
|
||||||
|
// println!("write: {:02x?}", &message);
|
||||||
|
// return file
|
||||||
|
// .write_all(message).unwrap();
|
||||||
|
// }
|
||||||
|
let mut error = false;
|
||||||
|
|
||||||
|
match self.handle.borrow().write_control(
|
||||||
|
0x21, // request_type
|
||||||
|
0x09, // request
|
||||||
|
0x35e, // value
|
||||||
|
0x00, // index
|
||||||
|
message,
|
||||||
|
Duration::from_millis(200),
|
||||||
|
) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => match err {
|
||||||
|
rusb::Error::Timeout => {}
|
||||||
|
_ => {
|
||||||
|
error = true;
|
||||||
|
error!("Failed to write to led interrupt: {}", err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if error {
|
||||||
|
warn!("Will attempt to get AniMe device handle again");
|
||||||
|
match Self::get_dev_handle() {
|
||||||
|
Ok(dev) => {
|
||||||
|
self.handle.replace(dev);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("Failed to get AniMe device: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write only a data packet. This will modify the leds brightness using the
|
||||||
|
/// global brightness set in config.
|
||||||
|
fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) {
|
||||||
|
for led in buffer.data_mut()[7..].iter_mut() {
|
||||||
|
let mut bright = *led as f32 * self.config.brightness;
|
||||||
|
if bright > 254.0 {
|
||||||
|
bright = 254.0;
|
||||||
|
}
|
||||||
|
*led = bright as u8;
|
||||||
|
}
|
||||||
|
let data = AnimePacketType::from(buffer);
|
||||||
|
for row in data.iter() {
|
||||||
|
self.write_bytes(row);
|
||||||
|
}
|
||||||
|
self.write_bytes(&pkt_for_flush());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_initialization(&self) {
|
||||||
|
let pkts = pkts_for_init();
|
||||||
|
self.write_bytes(&pkts[0]);
|
||||||
|
self.write_bytes(&pkts[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CtrlAnimeTask {
|
||||||
|
inner: Arc<Mutex<CtrlAnime>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CtrlAnimeTask {
|
||||||
|
pub async fn new(inner: Arc<Mutex<CtrlAnime>>) -> CtrlAnimeTask {
|
||||||
|
Self { inner }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl crate::CtrlTask for CtrlAnimeTask {
|
||||||
|
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
|
||||||
|
let connection = Connection::system()
|
||||||
|
.await
|
||||||
|
.expect("CtrlAnimeTask could not create dbus connection");
|
||||||
|
|
||||||
|
let manager = ManagerProxy::new(&connection)
|
||||||
|
.await
|
||||||
|
.expect("CtrlAnimeTask could not create ManagerProxy");
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CtrlAnimeReloader(pub Arc<Mutex<CtrlAnime>>);
|
||||||
|
|
||||||
|
impl crate::Reloadable for CtrlAnimeReloader {
|
||||||
|
fn reload(&mut self) -> Result<(), RogError> {
|
||||||
|
if let Ok(lock) = self.0.try_lock() {
|
||||||
|
lock.write_bytes(&pkt_for_set_on(lock.config.awake_enabled));
|
||||||
|
lock.write_bytes(&pkt_for_apply());
|
||||||
|
lock.write_bytes(&pkt_for_set_boot(lock.config.boot_anim_enabled));
|
||||||
|
lock.write_bytes(&pkt_for_apply());
|
||||||
|
|
||||||
|
let action = lock.cache.boot.clone();
|
||||||
|
CtrlAnime::run_thread(self.0.clone(), action, true);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use rog_anime::{
|
||||||
|
usb::{pkt_for_apply, pkt_for_set_boot, pkt_for_set_on},
|
||||||
|
AnimeDataBuffer, AnimePowerStates,
|
||||||
|
};
|
||||||
|
use zbus::{dbus_interface, Connection, SignalContext};
|
||||||
|
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
use super::CtrlAnime;
|
||||||
|
|
||||||
|
pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>);
|
||||||
|
|
||||||
|
/// The struct with the main dbus methods requires this trait
|
||||||
|
#[async_trait]
|
||||||
|
impl crate::ZbusAdd for CtrlAnimeZbus {
|
||||||
|
async fn add_to_server(self, server: &mut Connection) {
|
||||||
|
Self::add_to_server_helper(self, "/org/asuslinux/Anime", server).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// None of these calls can be guarnateed to succeed unless we loop until okay
|
||||||
|
// If the try_lock *does* succeed then any other thread trying to lock will not grab it
|
||||||
|
// until we finish.
|
||||||
|
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||||
|
impl CtrlAnimeZbus {
|
||||||
|
/// Writes a data stream of length. Will force system thread to exit until it is restarted
|
||||||
|
fn write(&self, input: AnimeDataBuffer) {
|
||||||
|
'outer: loop {
|
||||||
|
if let Ok(lock) = self.0.try_lock() {
|
||||||
|
lock.thread_exit.store(true, Ordering::SeqCst);
|
||||||
|
lock.write_data_buffer(input);
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the global AniMe brightness
|
||||||
|
fn set_brightness(&self, bright: f32) {
|
||||||
|
'outer: loop {
|
||||||
|
if let Ok(mut lock) = self.0.try_lock() {
|
||||||
|
let mut bright = bright;
|
||||||
|
if bright < 0.0 {
|
||||||
|
bright = 0.0
|
||||||
|
} else if bright > 1.0 {
|
||||||
|
bright = 1.0;
|
||||||
|
}
|
||||||
|
lock.config.brightness = bright;
|
||||||
|
lock.config.write();
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set whether the AniMe is displaying images/data
|
||||||
|
async fn set_on_off(&self, #[zbus(signal_context)] ctxt: SignalContext<'_>, status: bool) {
|
||||||
|
let states;
|
||||||
|
'outer: loop {
|
||||||
|
if let Ok(mut lock) = self.0.try_lock() {
|
||||||
|
lock.write_bytes(&pkt_for_set_on(status));
|
||||||
|
lock.config.awake_enabled = status;
|
||||||
|
lock.config.write();
|
||||||
|
|
||||||
|
states = Some(AnimePowerStates {
|
||||||
|
brightness: lock.config.brightness.floor() as u8,
|
||||||
|
enabled: lock.config.awake_enabled,
|
||||||
|
boot_anim_enabled: lock.config.boot_anim_enabled,
|
||||||
|
});
|
||||||
|
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
|
||||||
|
async fn set_boot_on_off(&self, #[zbus(signal_context)] ctxt: SignalContext<'_>, on: bool) {
|
||||||
|
let states;
|
||||||
|
'outer: loop {
|
||||||
|
if let Ok(mut lock) = self.0.try_lock() {
|
||||||
|
lock.write_bytes(&pkt_for_set_boot(on));
|
||||||
|
lock.write_bytes(&pkt_for_apply());
|
||||||
|
lock.config.boot_anim_enabled = on;
|
||||||
|
lock.config.write();
|
||||||
|
|
||||||
|
states = Some(AnimePowerStates {
|
||||||
|
brightness: lock.config.brightness.floor() as u8,
|
||||||
|
enabled: lock.config.awake_enabled,
|
||||||
|
boot_anim_enabled: lock.config.boot_anim_enabled,
|
||||||
|
});
|
||||||
|
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 user daemon
|
||||||
|
fn run_main_loop(&self, start: bool) {
|
||||||
|
if start {
|
||||||
|
'outer: loop {
|
||||||
|
if let Ok(lock) = self.0.try_lock() {
|
||||||
|
lock.thread_exit.store(true, Ordering::SeqCst);
|
||||||
|
CtrlAnime::run_thread(self.0.clone(), lock.cache.system.clone(), false);
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get status of if the AniMe LEDs are on/displaying while system is awake
|
||||||
|
#[dbus_interface(property)]
|
||||||
|
fn awake_enabled(&self) -> bool {
|
||||||
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
|
return ctrl.config.awake_enabled;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the status of if factory system-status animations are enabled
|
||||||
|
#[dbus_interface(property)]
|
||||||
|
fn boot_enabled(&self) -> bool {
|
||||||
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
|
return ctrl.config.boot_anim_enabled;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Notify listeners of the status of AniMe LED power and factory system-status animations
|
||||||
|
#[dbus_interface(signal)]
|
||||||
|
async fn notify_power_states(
|
||||||
|
ctxt: &SignalContext<'_>,
|
||||||
|
data: AnimePowerStates,
|
||||||
|
) -> zbus::Result<()>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
use crate::laptops::LaptopLedData;
|
||||||
|
use log::{error, warn};
|
||||||
|
use rog_aura::usb::AuraControl;
|
||||||
|
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, LedBrightness};
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use std::collections::{BTreeMap, HashSet};
|
||||||
|
use std::fs::{File, OpenOptions};
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
|
pub static AURA_CONFIG_PATH: &str = "/etc/asusd/aura.conf";
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct AuraConfig {
|
||||||
|
pub brightness: LedBrightness,
|
||||||
|
pub current_mode: AuraModeNum,
|
||||||
|
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
|
||||||
|
pub multizone: Option<BTreeMap<AuraModeNum, Vec<AuraEffect>>>,
|
||||||
|
pub enabled: HashSet<AuraControl>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AuraConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
AuraConfig {
|
||||||
|
brightness: LedBrightness::Med,
|
||||||
|
current_mode: AuraModeNum::Static,
|
||||||
|
builtins: BTreeMap::new(),
|
||||||
|
multizone: None,
|
||||||
|
enabled: HashSet::from([
|
||||||
|
AuraControl::BootLogo,
|
||||||
|
AuraControl::BootKeyb,
|
||||||
|
AuraControl::SleepLogo,
|
||||||
|
AuraControl::SleepKeyb,
|
||||||
|
AuraControl::AwakeLogo,
|
||||||
|
AuraControl::AwakeKeyb,
|
||||||
|
AuraControl::ShutdownLogo,
|
||||||
|
AuraControl::ShutdownKeyb,
|
||||||
|
AuraControl::AwakeBar,
|
||||||
|
AuraControl::BootBar,
|
||||||
|
AuraControl::SleepBar,
|
||||||
|
AuraControl::ShutdownBar,
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuraConfig {
|
||||||
|
/// `load` will attempt to read the config, and panic if the dir is missing
|
||||||
|
pub fn load(supported_led_modes: &LaptopLedData) -> Self {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.open(&AURA_CONFIG_PATH)
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
panic!(
|
||||||
|
"The file {} or directory /etc/asusd/ is missing",
|
||||||
|
AURA_CONFIG_PATH
|
||||||
|
)
|
||||||
|
}); // okay to cause panic here
|
||||||
|
let mut buf = String::new();
|
||||||
|
if let Ok(read_len) = file.read_to_string(&mut buf) {
|
||||||
|
if read_len == 0 {
|
||||||
|
return AuraConfig::create_default(&mut file, supported_led_modes);
|
||||||
|
} else {
|
||||||
|
if let Ok(data) = serde_json::from_str(&buf) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
warn!(
|
||||||
|
"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)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_default(file: &mut File, support_data: &LaptopLedData) -> Self {
|
||||||
|
// create a default config here
|
||||||
|
let mut config = AuraConfig::default();
|
||||||
|
|
||||||
|
for n in &support_data.standard {
|
||||||
|
config
|
||||||
|
.builtins
|
||||||
|
.insert(*n, AuraEffect::default_with_mode(*n));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be okay to unwrap this as is since it is a Default
|
||||||
|
let json = serde_json::to_string(&config).unwrap();
|
||||||
|
file.write_all(json.as_bytes())
|
||||||
|
.unwrap_or_else(|_| panic!("Could not write {}", AURA_CONFIG_PATH));
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self) {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.open(&AURA_CONFIG_PATH)
|
||||||
|
.unwrap_or_else(|err| panic!("Error reading {}: {}", AURA_CONFIG_PATH, err));
|
||||||
|
let mut buf = String::new();
|
||||||
|
if let Ok(l) = file.read_to_string(&mut buf) {
|
||||||
|
if l == 0 {
|
||||||
|
warn!("File is empty {}", AURA_CONFIG_PATH);
|
||||||
|
} else {
|
||||||
|
let x: AuraConfig = serde_json::from_str(&buf)
|
||||||
|
.unwrap_or_else(|_| panic!("Could not deserialise {}", AURA_CONFIG_PATH));
|
||||||
|
*self = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&self) {
|
||||||
|
let mut file = File::create(AURA_CONFIG_PATH).expect("Couldn't overwrite config");
|
||||||
|
let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed");
|
||||||
|
file.write_all(json.as_bytes())
|
||||||
|
.unwrap_or_else(|err| error!("Could not write config: {}", err));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multipurpose, will accept AuraEffect with zones and put in the correct store
|
||||||
|
pub fn set_builtin(&mut self, effect: AuraEffect) {
|
||||||
|
match effect.zone() {
|
||||||
|
AuraZone::None => {
|
||||||
|
self.builtins.insert(*effect.mode(), effect);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if let Some(multi) = self.multizone.as_mut() {
|
||||||
|
if let Some(fx) = multi.get_mut(effect.mode()) {
|
||||||
|
for fx in fx.iter_mut() {
|
||||||
|
if fx.mode == effect.mode {
|
||||||
|
*fx = effect;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut tmp = BTreeMap::new();
|
||||||
|
tmp.insert(*effect.mode(), vec![effect]);
|
||||||
|
self.multizone = Some(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_multizone(&self, aura_type: AuraModeNum) -> Option<&[AuraEffect]> {
|
||||||
|
if let Some(multi) = &self.multizone {
|
||||||
|
return multi.get(&aura_type).map(|v| v.as_slice());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,39 +2,40 @@
|
|||||||
static KBD_BRIGHT_PATH: &str = "/sys/class/leds/asus::kbd_backlight/brightness";
|
static KBD_BRIGHT_PATH: &str = "/sys/class/leds/asus::kbd_backlight/brightness";
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config_aura::AuraConfig,
|
|
||||||
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::usb::AuraControl;
|
||||||
use rog_aura::{
|
use rog_aura::{
|
||||||
usb::{
|
usb::{LED_APPLY, LED_SET},
|
||||||
LED_APPLY, LED_AWAKE_OFF_SLEEP_OFF, LED_AWAKE_OFF_SLEEP_ON, LED_AWAKE_ON_SLEEP_OFF,
|
|
||||||
LED_AWAKE_ON_SLEEP_ON, LED_SET,
|
|
||||||
},
|
|
||||||
AuraEffect, LedBrightness, LED_MSG_LEN,
|
AuraEffect, LedBrightness, LED_MSG_LEN,
|
||||||
};
|
};
|
||||||
use rog_types::supported::LedSupportedFunctions;
|
use rog_supported::LedSupportedFunctions;
|
||||||
|
use smol::{stream::StreamExt, Executor};
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::{fs::OpenOptions, thread::spawn};
|
use std::{fs::OpenOptions, sync::MutexGuard};
|
||||||
use zbus::Connection;
|
use zbus::Connection;
|
||||||
|
|
||||||
use crate::GetSupported;
|
use crate::GetSupported;
|
||||||
|
|
||||||
|
use super::config::AuraConfig;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
LedSupportedFunctions {
|
LedSupportedFunctions {
|
||||||
brightness_set: CtrlKbdLed::get_kbd_bright_path().is_some(),
|
brightness_set: CtrlKbdLed::get_kbd_bright_path().is_some(),
|
||||||
@@ -53,48 +54,13 @@ pub struct CtrlKbdLed {
|
|||||||
pub config: AuraConfig,
|
pub config: AuraConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CtrlKbdLedTask<'a> {
|
pub struct CtrlKbdLedTask {
|
||||||
inner: Arc<Mutex<CtrlKbdLed>>,
|
inner: Arc<Mutex<CtrlKbdLed>>,
|
||||||
_c: Connection,
|
|
||||||
manager: ManagerProxy<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CtrlKbdLedTask<'a> {
|
impl CtrlKbdLedTask {
|
||||||
pub fn new(inner: Arc<Mutex<CtrlKbdLed>>) -> Self {
|
pub fn new(inner: Arc<Mutex<CtrlKbdLed>>) -> Self {
|
||||||
let connection = Connection::new_system().unwrap();
|
Self { inner }
|
||||||
|
|
||||||
let manager = ManagerProxy::new(&connection).unwrap();
|
|
||||||
|
|
||||||
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> {
|
||||||
@@ -122,12 +88,81 @@ impl<'a> CtrlKbdLedTask<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CtrlTask for CtrlKbdLedTask<'a> {
|
#[async_trait]
|
||||||
fn do_task(&self) -> Result<(), RogError> {
|
impl CtrlTask for CtrlKbdLedTask {
|
||||||
self.manager.next_signal()?;
|
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
|
||||||
if let Ok(ref mut lock) = self.inner.try_lock() {
|
let connection = Connection::system()
|
||||||
return Self::update_config(lock);
|
.await
|
||||||
}
|
.expect("CtrlKbdLedTask could not create dbus connection");
|
||||||
|
|
||||||
|
let manager = ManagerProxy::new(&connection)
|
||||||
|
.await
|
||||||
|
.expect("CtrlKbdLedTask could not create ManagerProxy");
|
||||||
|
|
||||||
|
let load_save = |start: bool, mut lock: MutexGuard<CtrlKbdLed>| {
|
||||||
|
// If waking up
|
||||||
|
if !start {
|
||||||
|
info!("CtrlKbdLedTask reloading brightness and modes");
|
||||||
|
lock.set_brightness(lock.config.brightness)
|
||||||
|
.map_err(|e| error!("CtrlKbdLedTask: {e}"))
|
||||||
|
.ok();
|
||||||
|
if let Some(mode) = lock.config.builtins.get(&lock.config.current_mode) {
|
||||||
|
lock.write_mode(mode)
|
||||||
|
.map_err(|e| error!("CtrlKbdLedTask: {e}"))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
} else if start {
|
||||||
|
info!("CtrlKbdLedTask saving last brightness");
|
||||||
|
Self::update_config(&mut lock)
|
||||||
|
.map_err(|e| error!("CtrlKbdLedTask: {e}"))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let inner = self.inner.clone();
|
||||||
|
executor
|
||||||
|
.spawn(async move {
|
||||||
|
if let Ok(notif) = manager.receive_prepare_for_sleep().await {
|
||||||
|
notif
|
||||||
|
.for_each(|event| {
|
||||||
|
if let Ok(args) = event.args() {
|
||||||
|
loop {
|
||||||
|
// Loop so that we do aquire the lock but also don't block other
|
||||||
|
// threads (prevents potential deadlocks)
|
||||||
|
if let Ok(lock) = inner.clone().try_lock() {
|
||||||
|
load_save(args.start, lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
if let Ok(notif) = manager.receive_prepare_for_shutdown().await {
|
||||||
|
notif
|
||||||
|
.for_each(|event| {
|
||||||
|
if let Ok(args) = event.args() {
|
||||||
|
loop {
|
||||||
|
if let Ok(lock) = inner.clone().try_lock() {
|
||||||
|
load_save(args.start, lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
// let inner = self.inner.clone();
|
||||||
|
// self.repeating_task(500, executor, move || loop {
|
||||||
|
// if let Ok(ref mut lock) = inner.try_lock() {
|
||||||
|
// Self::update_config(lock).unwrap();
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// .await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,8 +177,8 @@ impl crate::Reloadable for CtrlKbdLedReloader {
|
|||||||
ctrl.do_command(mode).ok();
|
ctrl.do_command(mode).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
ctrl.set_states_enabled(ctrl.config.awake_enabled, ctrl.config.sleep_anim_enabled)
|
ctrl.set_power_states(&ctrl.config)
|
||||||
.map_err(|err| warn!("{}", err))
|
.map_err(|err| warn!("{err}"))
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -167,9 +202,10 @@ impl CtrlKbdLed {
|
|||||||
match Self::find_led_node(prod) {
|
match Self::find_led_node(prod) {
|
||||||
Ok(node) => {
|
Ok(node) => {
|
||||||
led_node = Some(node);
|
led_node = Some(node);
|
||||||
|
info!("Looked for keyboard controller 0x{prod}: Found");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(err) => warn!("led_node: {}", err),
|
Err(err) => info!("Looked for keyboard controller 0x{prod}: {err}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,20 +274,39 @@ impl CtrlKbdLed {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set if awake/on LED active, and/or sleep animation active
|
pub fn next_brightness(&mut self) -> Result<(), RogError> {
|
||||||
pub(super) fn set_states_enabled(&self, awake: bool, sleep: bool) -> Result<(), RogError> {
|
let mut bright = (self.config.brightness as u32) + 1;
|
||||||
let bytes = if awake && sleep {
|
if bright > 3 {
|
||||||
LED_AWAKE_ON_SLEEP_ON
|
bright = 0;
|
||||||
} else if awake && !sleep {
|
}
|
||||||
LED_AWAKE_ON_SLEEP_OFF
|
self.config.brightness = <LedBrightness>::from(bright);
|
||||||
} else if !awake && sleep {
|
self.config.write();
|
||||||
LED_AWAKE_OFF_SLEEP_ON
|
self.set_brightness(self.config.brightness)
|
||||||
} else if !awake && !sleep {
|
}
|
||||||
LED_AWAKE_OFF_SLEEP_OFF
|
|
||||||
|
pub fn prev_brightness(&mut self) -> Result<(), RogError> {
|
||||||
|
let mut bright = self.config.brightness as u32;
|
||||||
|
if bright == 0 {
|
||||||
|
bright = 3;
|
||||||
} else {
|
} else {
|
||||||
LED_AWAKE_ON_SLEEP_ON
|
bright -= 1;
|
||||||
};
|
}
|
||||||
self.write_bytes(&bytes)?;
|
self.config.brightness = <LedBrightness>::from(bright);
|
||||||
|
self.config.write();
|
||||||
|
self.set_brightness(self.config.brightness)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set combination state for boot animation/sleep animation/all leds/keys leds/side leds LED active
|
||||||
|
pub(super) fn set_power_states(&self, config: &AuraConfig) -> Result<(), RogError> {
|
||||||
|
let set: Vec<AuraControl> = config.enabled.iter().map(|v| *v).collect();
|
||||||
|
let bytes = AuraControl::to_bytes(&set);
|
||||||
|
|
||||||
|
// Quite ugly, must be a more idiomatic way to do
|
||||||
|
let message = [
|
||||||
|
0x5d, 0xbd, 0x01, bytes[0], bytes[1], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
];
|
||||||
|
|
||||||
|
self.write_bytes(&message)?;
|
||||||
self.write_bytes(&LED_SET)?;
|
self.write_bytes(&LED_SET)?;
|
||||||
// Changes won't persist unless apply is set
|
// Changes won't persist unless apply is set
|
||||||
self.write_bytes(&LED_APPLY)?;
|
self.write_bytes(&LED_APPLY)?;
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
|
pub mod config;
|
||||||
pub mod controller;
|
pub mod controller;
|
||||||
pub mod zbus;
|
pub mod zbus;
|
||||||
@@ -0,0 +1,225 @@
|
|||||||
|
use async_trait::async_trait;
|
||||||
|
use log::warn;
|
||||||
|
use rog_aura::{usb::AuraControl, AuraEffect, LedBrightness};
|
||||||
|
use zbus::{dbus_interface, Connection, SignalContext};
|
||||||
|
|
||||||
|
use super::controller::CtrlKbdLedZbus;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl crate::ZbusAdd for CtrlKbdLedZbus {
|
||||||
|
async fn add_to_server(self, server: &mut Connection) {
|
||||||
|
Self::add_to_server_helper(self, "/org/asuslinux/Led", server).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The main interface for changing, reading, or notfying signals
|
||||||
|
///
|
||||||
|
/// LED commands are split between Brightness, Modes, Per-Key
|
||||||
|
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||||
|
impl CtrlKbdLedZbus {
|
||||||
|
/// Set the keyboard brightness level (0-3)
|
||||||
|
async fn set_brightness(&mut self, brightness: LedBrightness) {
|
||||||
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
|
ctrl.set_brightness(brightness)
|
||||||
|
.map_err(|err| warn!("{}", err))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a variety of states, input is array of enum.
|
||||||
|
///
|
||||||
|
/// enum AuraControl {
|
||||||
|
/// BootLogo,
|
||||||
|
/// BootKeyb,
|
||||||
|
/// AwakeLogo,
|
||||||
|
/// AwakeKeyb,
|
||||||
|
/// SleepLogo,
|
||||||
|
/// SleepKeyb,
|
||||||
|
/// ShutdownLogo,
|
||||||
|
/// ShutdownKeyb,
|
||||||
|
/// AwakeBar,
|
||||||
|
/// BootBar,
|
||||||
|
/// SleepBar,
|
||||||
|
/// ShutdownBar,
|
||||||
|
/// }
|
||||||
|
async fn set_leds_enabled(
|
||||||
|
&mut self,
|
||||||
|
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||||
|
enabled: Vec<AuraControl>,
|
||||||
|
) {
|
||||||
|
let mut states = None;
|
||||||
|
if let Ok(mut ctrl) = self.0.try_lock() {
|
||||||
|
for s in enabled {
|
||||||
|
ctrl.config.enabled.insert(s);
|
||||||
|
}
|
||||||
|
ctrl.config.write();
|
||||||
|
|
||||||
|
ctrl.set_power_states(&ctrl.config)
|
||||||
|
.map_err(|err| warn!("{}", err))
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
let set: Vec<AuraControl> = ctrl.config.enabled.iter().map(|v| *v).collect();
|
||||||
|
states = Some(set);
|
||||||
|
}
|
||||||
|
// Need to pull state out like this due to MutexGuard
|
||||||
|
if let Some(states) = states {
|
||||||
|
Self::notify_power_states(&ctxt, &states)
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_leds_disabled(
|
||||||
|
&mut self,
|
||||||
|
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||||
|
disabled: Vec<AuraControl>,
|
||||||
|
) {
|
||||||
|
let mut states = None;
|
||||||
|
if let Ok(mut ctrl) = self.0.try_lock() {
|
||||||
|
for s in disabled {
|
||||||
|
ctrl.config.enabled.remove(&s);
|
||||||
|
}
|
||||||
|
ctrl.config.write();
|
||||||
|
|
||||||
|
ctrl.set_power_states(&ctrl.config)
|
||||||
|
.map_err(|err| warn!("{}", err))
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
let set: Vec<AuraControl> = ctrl.config.enabled.iter().map(|v| *v).collect();
|
||||||
|
states = Some(set);
|
||||||
|
}
|
||||||
|
// Need to pull state out like this due to MutexGuard
|
||||||
|
if let Some(states) = states {
|
||||||
|
Self::notify_power_states(&ctxt, &states)
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_led_mode(
|
||||||
|
&mut self,
|
||||||
|
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||||
|
effect: AuraEffect,
|
||||||
|
) {
|
||||||
|
let mut led = None;
|
||||||
|
if let Ok(mut ctrl) = self.0.try_lock() {
|
||||||
|
match ctrl.do_command(effect) {
|
||||||
|
Ok(_) => {
|
||||||
|
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
|
||||||
|
led = Some(mode.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn!("{}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(led) = led {
|
||||||
|
Self::notify_led(&ctxt, led)
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn next_led_mode(&self, #[zbus(signal_context)] ctxt: SignalContext<'_>) {
|
||||||
|
let mut led = None;
|
||||||
|
if let Ok(mut ctrl) = self.0.lock() {
|
||||||
|
ctrl.toggle_mode(false)
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
|
||||||
|
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
|
||||||
|
led = Some(mode.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(led) = led {
|
||||||
|
Self::notify_led(&ctxt, led)
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn prev_led_mode(&self, #[zbus(signal_context)] ctxt: SignalContext<'_>) {
|
||||||
|
let mut led = None;
|
||||||
|
if let Ok(mut ctrl) = self.0.lock() {
|
||||||
|
ctrl.toggle_mode(true)
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
|
||||||
|
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
|
||||||
|
led = Some(mode.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(led) = led {
|
||||||
|
Self::notify_led(&ctxt, led)
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn next_led_brightness(&self) {
|
||||||
|
if let Ok(mut ctrl) = self.0.try_lock() {
|
||||||
|
ctrl.next_brightness()
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn prev_led_brightness(&self) {
|
||||||
|
if let Ok(mut ctrl) = self.0.try_lock() {
|
||||||
|
ctrl.prev_brightness()
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[dbus_interface(property)]
|
||||||
|
async fn leds_enabled(&self) -> Vec<u8> {
|
||||||
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
|
let set: Vec<AuraControl> = ctrl.config.enabled.iter().map(|v| *v).collect();
|
||||||
|
return AuraControl::to_bytes(&set).to_vec();
|
||||||
|
}
|
||||||
|
vec![0, 0]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the current mode data
|
||||||
|
#[dbus_interface(property)]
|
||||||
|
async fn led_mode(&self) -> String {
|
||||||
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
|
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
|
||||||
|
if let Ok(json) = serde_json::to_string(&mode) {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
warn!("SetKeyBacklight could not deserialise");
|
||||||
|
"SetKeyBacklight could not deserialise".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a list of available modes
|
||||||
|
#[dbus_interface(property)]
|
||||||
|
async fn led_modes(&self) -> String {
|
||||||
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
|
if let Ok(json) = serde_json::to_string(&ctrl.config.builtins) {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
warn!("SetKeyBacklight could not deserialise");
|
||||||
|
"SetKeyBacklight could not serialise".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the current LED brightness
|
||||||
|
#[dbus_interface(property)]
|
||||||
|
async fn led_brightness(&self) -> i8 {
|
||||||
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
|
return ctrl.get_brightness().map(|n| n as i8).unwrap_or(-1);
|
||||||
|
}
|
||||||
|
warn!("SetKeyBacklight could not serialise");
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[dbus_interface(signal)]
|
||||||
|
async fn notify_led(signal_ctxt: &SignalContext<'_>, data: AuraEffect) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
#[dbus_interface(signal)]
|
||||||
|
async fn notify_power_states(
|
||||||
|
signal_ctxt: &SignalContext<'_>,
|
||||||
|
data: &[AuraControl],
|
||||||
|
) -> zbus::Result<()>;
|
||||||
|
}
|
||||||
@@ -1,16 +1,23 @@
|
|||||||
|
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 rog_types::supported::ChargeSupportedFunctions;
|
use logind_zbus::manager::ManagerProxy;
|
||||||
|
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_PATH: &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_PATH2: &str = "/sys/class/power_supply/BAT2/charge_control_end_threshold";
|
||||||
|
|
||||||
impl GetSupported for CtrlCharge {
|
impl GetSupported for CtrlCharge {
|
||||||
type A = ChargeSupportedFunctions;
|
type A = ChargeSupportedFunctions;
|
||||||
@@ -28,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;
|
||||||
}
|
}
|
||||||
@@ -53,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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,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(())
|
||||||
}
|
}
|
||||||
@@ -88,8 +90,12 @@ impl CtrlCharge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_battery_path() -> Result<&'static str, RogError> {
|
fn get_battery_path() -> Result<&'static str, RogError> {
|
||||||
if Path::new(BAT_CHARGE_PATH).exists() {
|
if Path::new(BAT_CHARGE_PATH0).exists() {
|
||||||
Ok(BAT_CHARGE_PATH)
|
Ok(BAT_CHARGE_PATH0)
|
||||||
|
} else if Path::new(BAT_CHARGE_PATH1).exists() {
|
||||||
|
Ok(BAT_CHARGE_PATH1)
|
||||||
|
} else if Path::new(BAT_CHARGE_PATH2).exists() {
|
||||||
|
Ok(BAT_CHARGE_PATH2)
|
||||||
} else {
|
} else {
|
||||||
Err(RogError::MissingFunction(
|
Err(RogError::MissingFunction(
|
||||||
"Charge control not available, you may require a v5.8.10 series kernel or newer"
|
"Charge control not available, you may require a v5.8.10 series kernel or newer"
|
||||||
@@ -98,20 +104,19 @@ 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 mut file = OpenOptions::new()
|
let mut file = OpenOptions::new()
|
||||||
.write(true)
|
.write(true)
|
||||||
.open(BAT_CHARGE_PATH)
|
.open(path)
|
||||||
.map_err(|err| RogError::Path(BAT_CHARGE_PATH.into(), err))?;
|
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||||
file.write_all(limit.to_string().as_bytes())
|
file.write_all(limit.to_string().as_bytes())
|
||||||
.map_err(|err| RogError::Write(BAT_CHARGE_PATH.into(), err))?;
|
.map_err(|err| RogError::Write(path.into(), err))?;
|
||||||
info!("Battery charge limit: {}", limit);
|
info!("Battery charge limit: {}", limit);
|
||||||
|
|
||||||
config.read();
|
config.read();
|
||||||
@@ -121,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,702 +0,0 @@
|
|||||||
use ::zbus::Connection;
|
|
||||||
use ctrl_gfx::error::GfxError;
|
|
||||||
use ctrl_gfx::*;
|
|
||||||
use ctrl_rog_bios::CtrlRogBios;
|
|
||||||
use log::{error, info, warn};
|
|
||||||
use logind_zbus::{
|
|
||||||
types::{SessionClass, SessionInfo, SessionState, SessionType},
|
|
||||||
ManagerProxy, SessionProxy,
|
|
||||||
};
|
|
||||||
use rog_types::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors};
|
|
||||||
use std::{io::Write, ops::Add, path::Path, time::Instant};
|
|
||||||
use std::{process::Command, thread::sleep, time::Duration};
|
|
||||||
use std::{str::FromStr, sync::mpsc};
|
|
||||||
use std::{sync::Arc, sync::Mutex};
|
|
||||||
use sysfs_class::RuntimePM;
|
|
||||||
use sysfs_class::{PciDevice, SysClass};
|
|
||||||
use system::{GraphicsDevice, PciBus};
|
|
||||||
|
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
const THREAD_TIMEOUT_MSG: &str = "GFX: thread time exceeded 3 minutes, exiting";
|
|
||||||
|
|
||||||
pub struct CtrlGraphics {
|
|
||||||
bus: PciBus,
|
|
||||||
_amd: Vec<GraphicsDevice>,
|
|
||||||
_intel: Vec<GraphicsDevice>,
|
|
||||||
nvidia: Vec<GraphicsDevice>,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
other: Vec<GraphicsDevice>,
|
|
||||||
config: Arc<Mutex<Config>>,
|
|
||||||
thread_kill: Arc<Mutex<Option<mpsc::Sender<bool>>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Reloadable for CtrlGraphics {
|
|
||||||
fn reload(&mut self) -> Result<(), RogError> {
|
|
||||||
self.auto_power()?;
|
|
||||||
info!("GFX: Reloaded gfx mode: {:?}", self.get_gfx_mode()?);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CtrlGraphics {
|
|
||||||
pub fn new(config: Arc<Mutex<Config>>) -> std::io::Result<CtrlGraphics> {
|
|
||||||
let bus = PciBus::new()?;
|
|
||||||
info!("GFX: Rescanning PCI bus");
|
|
||||||
bus.rescan()?;
|
|
||||||
let devs = PciDevice::all()?;
|
|
||||||
|
|
||||||
let functions = |parent: &PciDevice| -> Vec<PciDevice> {
|
|
||||||
let mut functions = Vec::new();
|
|
||||||
if let Some(parent_slot) = parent.id().split('.').next() {
|
|
||||||
for func in devs.iter() {
|
|
||||||
if let Some(func_slot) = func.id().split('.').next() {
|
|
||||||
if func_slot == parent_slot {
|
|
||||||
info!("GFX: {}: Function for {}", func.id(), parent.id());
|
|
||||||
functions.push(func.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
functions
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut amd = Vec::new();
|
|
||||||
let mut intel = Vec::new();
|
|
||||||
let mut nvidia = Vec::new();
|
|
||||||
let mut other = Vec::new();
|
|
||||||
for dev in devs.iter() {
|
|
||||||
let c = dev.class().map_err(|err| {
|
|
||||||
error!(
|
|
||||||
"GFX: device error: {}, {}",
|
|
||||||
dev.path().to_string_lossy(),
|
|
||||||
err
|
|
||||||
);
|
|
||||||
err
|
|
||||||
})?;
|
|
||||||
if 0x03 == (c >> 16) & 0xFF {
|
|
||||||
match dev.vendor()? {
|
|
||||||
0x1002 => {
|
|
||||||
info!("GFX: {}: AMD graphics", dev.id());
|
|
||||||
amd.push(GraphicsDevice::new(dev.id().to_owned(), functions(dev)));
|
|
||||||
}
|
|
||||||
0x10DE => {
|
|
||||||
info!("GFX: {}: NVIDIA graphics", dev.id());
|
|
||||||
dev.set_runtime_pm(sysfs_class::RuntimePowerManagement::On)?;
|
|
||||||
nvidia.push(GraphicsDevice::new(dev.id().to_owned(), functions(dev)));
|
|
||||||
}
|
|
||||||
0x8086 => {
|
|
||||||
info!("GFX: {}: Intel graphics", dev.id());
|
|
||||||
intel.push(GraphicsDevice::new(dev.id().to_owned(), functions(dev)));
|
|
||||||
}
|
|
||||||
vendor => {
|
|
||||||
info!("GFX: {}: Other({:X}) graphics", dev.id(), vendor);
|
|
||||||
other.push(GraphicsDevice::new(dev.id().to_owned(), functions(dev)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(CtrlGraphics {
|
|
||||||
bus,
|
|
||||||
_amd: amd,
|
|
||||||
_intel: intel,
|
|
||||||
nvidia,
|
|
||||||
other,
|
|
||||||
config,
|
|
||||||
thread_kill: Arc::new(Mutex::new(None)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bus(&self) -> PciBus {
|
|
||||||
self.bus.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn devices(&self) -> Vec<GraphicsDevice> {
|
|
||||||
self.nvidia.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Save the selected `Vendor` mode to config
|
|
||||||
fn save_gfx_mode(vendor: GfxVendors, config: Arc<Mutex<Config>>) {
|
|
||||||
if let Ok(mut config) = config.lock() {
|
|
||||||
config.gfx_mode = vendor;
|
|
||||||
config.write();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Associated method to get which vendor mode is set
|
|
||||||
pub(super) fn get_gfx_mode(&self) -> Result<GfxVendors, RogError> {
|
|
||||||
if let Ok(config) = self.config.lock() {
|
|
||||||
if let Some(mode) = config.gfx_tmp_mode {
|
|
||||||
return Ok(mode);
|
|
||||||
}
|
|
||||||
return Ok(config.gfx_mode);
|
|
||||||
}
|
|
||||||
// TODO: Error here
|
|
||||||
Ok(GfxVendors::Hybrid)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn get_runtime_status() -> Result<GfxPower, RogError> {
|
|
||||||
let path = Path::new("/sys/bus/pci/devices/0000:01:00.0/power/runtime_status");
|
|
||||||
if path.exists() {
|
|
||||||
let buf = std::fs::read_to_string(path).map_err(|err| {
|
|
||||||
RogError::Read(
|
|
||||||
"/sys/bus/pci/devices/0000:01:00.0/power/runtime_status".to_string(),
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
Ok(GfxPower::from_str(&buf)?)
|
|
||||||
} else {
|
|
||||||
Ok(GfxPower::Off)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Some systems have a fallback service to load nouveau if nvidia fails
|
|
||||||
fn toggle_fallback_service(vendor: GfxVendors) -> Result<(), RogError> {
|
|
||||||
let action = if vendor == GfxVendors::Nvidia {
|
|
||||||
info!("GFX: Enabling nvidia-fallback.service");
|
|
||||||
"enable"
|
|
||||||
} else {
|
|
||||||
info!("GFX: Disabling nvidia-fallback.service");
|
|
||||||
"disable"
|
|
||||||
};
|
|
||||||
|
|
||||||
let status = Command::new("systemctl")
|
|
||||||
.arg(action)
|
|
||||||
.arg("nvidia-fallback.service")
|
|
||||||
.status()
|
|
||||||
.map_err(|err| RogError::Command("systemctl".into(), err))?;
|
|
||||||
|
|
||||||
if !status.success() {
|
|
||||||
// Error is ignored in case this service is removed
|
|
||||||
warn!(
|
|
||||||
"systemctl: {} (ignore warning if service does not exist!)",
|
|
||||||
status
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write the appropriate xorg config for the chosen mode
|
|
||||||
fn write_xorg_conf(vendor: GfxVendors) -> Result<(), RogError> {
|
|
||||||
let text = if vendor == GfxVendors::Nvidia {
|
|
||||||
[PRIMARY_GPU_BEGIN, PRIMARY_GPU_NVIDIA, PRIMARY_GPU_END].concat()
|
|
||||||
} else {
|
|
||||||
[PRIMARY_GPU_BEGIN, PRIMARY_GPU_END].concat()
|
|
||||||
};
|
|
||||||
|
|
||||||
if !Path::new(XORG_PATH).exists() {
|
|
||||||
std::fs::create_dir(XORG_PATH).map_err(|err| RogError::Write(XORG_PATH.into(), err))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let file = XORG_PATH.to_string().add(XORG_FILE);
|
|
||||||
info!("GFX: Writing {}", file);
|
|
||||||
let mut file = std::fs::OpenOptions::new()
|
|
||||||
.create(true)
|
|
||||||
.truncate(true)
|
|
||||||
.write(true)
|
|
||||||
.open(&file)
|
|
||||||
.map_err(|err| RogError::Write(file, err))?;
|
|
||||||
|
|
||||||
file.write_all(&text)
|
|
||||||
.and_then(|_| file.sync_all())
|
|
||||||
.map_err(|err| RogError::Write(MODPROBE_PATH.into(), err))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates the full modprobe.conf required for vfio pass-through
|
|
||||||
fn get_vfio_conf(devices: &[GraphicsDevice]) -> Vec<u8> {
|
|
||||||
let mut vifo = MODPROBE_VFIO.to_vec();
|
|
||||||
for (d_count, dev) in devices.iter().enumerate() {
|
|
||||||
for (f_count, func) in dev.functions().iter().enumerate() {
|
|
||||||
let vendor = func.vendor().unwrap();
|
|
||||||
let device = func.device().unwrap();
|
|
||||||
unsafe {
|
|
||||||
vifo.append(format!("{:x}", vendor).as_mut_vec());
|
|
||||||
}
|
|
||||||
vifo.append(&mut vec![b':']);
|
|
||||||
unsafe {
|
|
||||||
vifo.append(format!("{:x}", device).as_mut_vec());
|
|
||||||
}
|
|
||||||
if f_count < dev.functions().len() - 1 {
|
|
||||||
vifo.append(&mut vec![b',']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if d_count < dev.functions().len() - 1 {
|
|
||||||
vifo.append(&mut vec![b',']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut conf = MODPROBE_INTEGRATED.to_vec();
|
|
||||||
conf.append(&mut vifo);
|
|
||||||
conf
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_modprobe_conf(vendor: GfxVendors, devices: &[GraphicsDevice]) -> Result<(), RogError> {
|
|
||||||
info!("GFX: Writing {}", MODPROBE_PATH);
|
|
||||||
let content = match vendor {
|
|
||||||
GfxVendors::Nvidia | GfxVendors::Hybrid => {
|
|
||||||
let mut base = MODPROBE_BASE.to_vec();
|
|
||||||
base.append(&mut MODPROBE_DRM_MODESET.to_vec());
|
|
||||||
base
|
|
||||||
}
|
|
||||||
GfxVendors::Vfio => Self::get_vfio_conf(devices),
|
|
||||||
GfxVendors::Integrated => MODPROBE_INTEGRATED.to_vec(),
|
|
||||||
GfxVendors::Compute => MODPROBE_BASE.to_vec(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut file = std::fs::OpenOptions::new()
|
|
||||||
.create(true)
|
|
||||||
.truncate(true)
|
|
||||||
.write(true)
|
|
||||||
.open(MODPROBE_PATH)
|
|
||||||
.map_err(|err| RogError::Path(MODPROBE_PATH.into(), err))?;
|
|
||||||
|
|
||||||
file.write_all(&content)
|
|
||||||
.and_then(|_| file.sync_all())
|
|
||||||
.map_err(|err| RogError::Write(MODPROBE_PATH.into(), err))?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unbind_remove_nvidia(devices: &[GraphicsDevice]) -> Result<(), RogError> {
|
|
||||||
// Unbind NVIDIA graphics devices and their functions
|
|
||||||
let unbinds = devices.iter().map(|dev| dev.unbind());
|
|
||||||
// Remove NVIDIA graphics devices and their functions
|
|
||||||
let removes = devices.iter().map(|dev| dev.remove());
|
|
||||||
unbinds.chain(removes).collect::<Result<_, _>>()
|
|
||||||
.map_err(|err| RogError::Command("device unbind error".into(), err))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unbind_only(devices: &[GraphicsDevice]) -> Result<(), RogError> {
|
|
||||||
let unbinds = devices.iter().map(|dev| dev.unbind());
|
|
||||||
unbinds.collect::<Result<_, _>>()
|
|
||||||
.map_err(|err| RogError::Command("device unbind error".into(), err))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add or remove driver modules
|
|
||||||
fn do_driver_action(driver: &str, action: &str) -> Result<(), GfxError> {
|
|
||||||
let mut cmd = Command::new(action);
|
|
||||||
cmd.arg(driver);
|
|
||||||
|
|
||||||
let mut count = 0;
|
|
||||||
const MAX_TRIES: i32 = 6;
|
|
||||||
loop {
|
|
||||||
if count > MAX_TRIES {
|
|
||||||
let msg = format!("{} {} failed for unknown reason", action, driver);
|
|
||||||
error!("GFX: {}", msg);
|
|
||||||
return Ok(()); //Err(RogError::Modprobe(msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
let output = cmd
|
|
||||||
.output()
|
|
||||||
.map_err(|err| GfxError::Command(format!("{:?}", cmd), err))?;
|
|
||||||
if !output.status.success() {
|
|
||||||
if output
|
|
||||||
.stderr
|
|
||||||
.ends_with("is not currently loaded\n".as_bytes())
|
|
||||||
{
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if output.stderr.ends_with("is builtin.\n".as_bytes()) {
|
|
||||||
return Err(GfxError::VfioBuiltin);
|
|
||||||
}
|
|
||||||
if output.stderr.ends_with("Permission denied\n".as_bytes()) {
|
|
||||||
warn!(
|
|
||||||
"{} {} failed: {:?}",
|
|
||||||
action,
|
|
||||||
driver,
|
|
||||||
String::from_utf8_lossy(&output.stderr)
|
|
||||||
);
|
|
||||||
warn!("GFX: It may be safe to ignore the above error, run `lsmod |grep {}` to confirm modules loaded", driver);
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if String::from_utf8_lossy(&output.stderr)
|
|
||||||
.contains(&format!("Module {} not found", driver))
|
|
||||||
{
|
|
||||||
return Err(GfxError::MissingModule(driver.into()));
|
|
||||||
}
|
|
||||||
if count >= MAX_TRIES {
|
|
||||||
let msg = format!(
|
|
||||||
"{} {} failed: {:?}",
|
|
||||||
action,
|
|
||||||
driver,
|
|
||||||
String::from_utf8_lossy(&output.stderr)
|
|
||||||
);
|
|
||||||
return Err(GfxError::Modprobe(msg));
|
|
||||||
}
|
|
||||||
} else if output.status.success() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
count += 1;
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn do_display_manager_action(action: &str) -> Result<(), RogError> {
|
|
||||||
let mut cmd = Command::new("systemctl");
|
|
||||||
cmd.arg(action);
|
|
||||||
cmd.arg(DISPLAY_MANAGER);
|
|
||||||
|
|
||||||
let status = cmd
|
|
||||||
.status()
|
|
||||||
.map_err(|err| RogError::Command(format!("{:?}", cmd), err))?;
|
|
||||||
if !status.success() {
|
|
||||||
let msg = format!(
|
|
||||||
"systemctl {} {} failed: {:?}",
|
|
||||||
action, DISPLAY_MANAGER, status
|
|
||||||
);
|
|
||||||
return Err(GfxError::DisplayManagerAction(msg, status).into());
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wait_display_manager_state(state: &str) -> Result<(), RogError> {
|
|
||||||
let mut cmd = Command::new("systemctl");
|
|
||||||
cmd.arg("is-active");
|
|
||||||
cmd.arg(DISPLAY_MANAGER);
|
|
||||||
|
|
||||||
let mut count = 0;
|
|
||||||
|
|
||||||
while count <= (4 * 3) {
|
|
||||||
// 3 seconds max
|
|
||||||
let output = cmd
|
|
||||||
.output()
|
|
||||||
.map_err(|err| RogError::Command(format!("{:?}", cmd), err))?;
|
|
||||||
if output.stdout.starts_with(state.as_bytes()) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(250));
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
Err(GfxError::DisplayManagerTimeout(state.into()).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Determine if we need to logout/thread. Integrated<->Vfio mode does not
|
|
||||||
/// require logout.
|
|
||||||
fn is_logout_required(&self, vendor: GfxVendors) -> GfxRequiredUserAction {
|
|
||||||
if let Ok(config) = self.config.lock() {
|
|
||||||
let current = config.gfx_mode;
|
|
||||||
// Modes that can switch without logout
|
|
||||||
if matches!(
|
|
||||||
current,
|
|
||||||
GfxVendors::Integrated | GfxVendors::Vfio | GfxVendors::Compute
|
|
||||||
) && matches!(
|
|
||||||
vendor,
|
|
||||||
GfxVendors::Integrated | GfxVendors::Vfio | GfxVendors::Compute
|
|
||||||
) {
|
|
||||||
return GfxRequiredUserAction::None;
|
|
||||||
}
|
|
||||||
// Modes that require a switch to integrated first
|
|
||||||
if matches!(current, GfxVendors::Nvidia | GfxVendors::Hybrid)
|
|
||||||
&& matches!(vendor, GfxVendors::Compute | GfxVendors::Vfio)
|
|
||||||
{
|
|
||||||
return GfxRequiredUserAction::Integrated;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GfxRequiredUserAction::Logout
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Do a full setup flow for the chosen mode:
|
|
||||||
///
|
|
||||||
/// Tasks:
|
|
||||||
/// - rescan for devices
|
|
||||||
/// - write xorg config
|
|
||||||
/// - write modprobe config
|
|
||||||
/// + add drivers
|
|
||||||
/// + or remove drivers and devices
|
|
||||||
///
|
|
||||||
/// The daemon needs direct access to this function when it detects that the
|
|
||||||
/// bios has G-Sync switch is enabled
|
|
||||||
pub fn do_mode_setup_tasks(
|
|
||||||
vendor: GfxVendors,
|
|
||||||
vfio_enable: bool,
|
|
||||||
devices: &[GraphicsDevice],
|
|
||||||
bus: &PciBus,
|
|
||||||
) -> Result<(), RogError> {
|
|
||||||
// Rescan before doing remove or add drivers
|
|
||||||
bus.rescan()?;
|
|
||||||
// Make sure the power management is set to auto for nvidia devices
|
|
||||||
let devs = PciDevice::all()?;
|
|
||||||
for dev in devs.iter() {
|
|
||||||
let c = dev.class().map_err(|err| {
|
|
||||||
error!(
|
|
||||||
"GFX: device error: {}, {}",
|
|
||||||
dev.path().to_string_lossy(),
|
|
||||||
err
|
|
||||||
);
|
|
||||||
err
|
|
||||||
})?;
|
|
||||||
if 0x03 == (c >> 16) & 0xFF && dev.vendor()? == 0x10DE {
|
|
||||||
info!("GFX: {}: NVIDIA graphics, setting PM to auto", dev.id());
|
|
||||||
dev.set_runtime_pm(sysfs_class::RuntimePowerManagement::On)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Only these modes should have xorg config
|
|
||||||
if matches!(
|
|
||||||
vendor,
|
|
||||||
GfxVendors::Nvidia | GfxVendors::Hybrid | GfxVendors::Integrated
|
|
||||||
) {
|
|
||||||
Self::write_xorg_conf(vendor)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write different modprobe to enable boot control to work
|
|
||||||
Self::write_modprobe_conf(vendor, devices)?;
|
|
||||||
|
|
||||||
match vendor {
|
|
||||||
GfxVendors::Nvidia | GfxVendors::Hybrid | GfxVendors::Compute => {
|
|
||||||
if vfio_enable {
|
|
||||||
for driver in VFIO_DRIVERS.iter() {
|
|
||||||
Self::do_driver_action(driver, "rmmod")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for driver in NVIDIA_DRIVERS.iter() {
|
|
||||||
Self::do_driver_action(driver, "modprobe")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GfxVendors::Vfio => {
|
|
||||||
if vfio_enable {
|
|
||||||
Self::do_driver_action("nouveau", "rmmod")?;
|
|
||||||
for driver in NVIDIA_DRIVERS.iter() {
|
|
||||||
Self::do_driver_action(driver, "rmmod")?;
|
|
||||||
}
|
|
||||||
Self::unbind_only(devices)?;
|
|
||||||
Self::do_driver_action("vfio-pci", "modprobe")?;
|
|
||||||
} else {
|
|
||||||
return Err(GfxError::VfioDisabled.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GfxVendors::Integrated => {
|
|
||||||
Self::do_driver_action("nouveau", "rmmod")?;
|
|
||||||
if vfio_enable {
|
|
||||||
for driver in VFIO_DRIVERS.iter() {
|
|
||||||
Self::do_driver_action(driver, "rmmod")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for driver in NVIDIA_DRIVERS.iter() {
|
|
||||||
Self::do_driver_action(driver, "rmmod")?;
|
|
||||||
}
|
|
||||||
Self::unbind_remove_nvidia(devices)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if the user has any graphical uiser sessions that are active or online
|
|
||||||
fn graphical_user_sessions_exist(
|
|
||||||
connection: &Connection,
|
|
||||||
sessions: &[SessionInfo],
|
|
||||||
) -> Result<bool, RogError> {
|
|
||||||
for session in sessions {
|
|
||||||
let session_proxy = SessionProxy::new(connection, session)?;
|
|
||||||
if session_proxy.get_class()? == SessionClass::User {
|
|
||||||
match session_proxy.get_type()? {
|
|
||||||
SessionType::X11 | SessionType::Wayland | SessionType::MIR => {
|
|
||||||
match session_proxy.get_state()? {
|
|
||||||
SessionState::Online | SessionState::Active => return Ok(true),
|
|
||||||
SessionState::Closing | SessionState::Invalid => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Spools until all user sessions are ended then switches to requested mode
|
|
||||||
fn create_mode_change_thread(
|
|
||||||
vendor: GfxVendors,
|
|
||||||
devices: Vec<GraphicsDevice>,
|
|
||||||
bus: PciBus,
|
|
||||||
thread_stop: mpsc::Receiver<bool>,
|
|
||||||
config: Arc<Mutex<Config>>,
|
|
||||||
) -> Result<String, RogError> {
|
|
||||||
info!("GFX: display-manager thread started");
|
|
||||||
|
|
||||||
const SLEEP_PERIOD: Duration = Duration::from_millis(100);
|
|
||||||
let start_time = Instant::now();
|
|
||||||
|
|
||||||
let connection = Connection::new_system()?;
|
|
||||||
let manager = ManagerProxy::new(&connection)?;
|
|
||||||
let mut sessions = manager.list_sessions()?;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let tmp = manager.list_sessions()?;
|
|
||||||
if !tmp.iter().eq(&sessions) {
|
|
||||||
info!("GFX thread: Sessions list changed");
|
|
||||||
sessions = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !Self::graphical_user_sessions_exist(&connection, &sessions)? {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(stop) = thread_stop.try_recv() {
|
|
||||||
if stop {
|
|
||||||
return Ok("Graphics mode change was cancelled".into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// exit if 3 minutes pass
|
|
||||||
if Instant::now().duration_since(start_time).as_secs() > 180 {
|
|
||||||
warn!("{}", THREAD_TIMEOUT_MSG);
|
|
||||||
return Ok(THREAD_TIMEOUT_MSG.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't spin at max speed
|
|
||||||
sleep(SLEEP_PERIOD);
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("GFX thread: all graphical user sessions ended, continuing");
|
|
||||||
Self::do_display_manager_action("stop")?;
|
|
||||||
Self::wait_display_manager_state("inactive")?;
|
|
||||||
|
|
||||||
let mut mode_to_save = vendor;
|
|
||||||
// Need to change to integrated before we can change to vfio or compute
|
|
||||||
if let Ok(mut config) = config.try_lock() {
|
|
||||||
// Since we have a lock, reset tmp to none. This thread should only ever run
|
|
||||||
// for Integrated, Hybrid, or Nvidia. Tmp is also only for informational
|
|
||||||
config.gfx_tmp_mode = None;
|
|
||||||
//
|
|
||||||
let vfio_enable = config.gfx_vfio_enable;
|
|
||||||
|
|
||||||
// Failsafe. In the event this loop is run with a switch from nvidia in use
|
|
||||||
// to vfio or compute do a forced switch to integrated instead to prevent issues
|
|
||||||
if matches!(vendor, GfxVendors::Compute | GfxVendors::Vfio)
|
|
||||||
&& matches!(config.gfx_mode, GfxVendors::Nvidia | GfxVendors::Hybrid)
|
|
||||||
{
|
|
||||||
Self::do_mode_setup_tasks(GfxVendors::Integrated, vfio_enable, &devices, &bus)?;
|
|
||||||
Self::do_display_manager_action("restart")?;
|
|
||||||
mode_to_save = GfxVendors::Integrated;
|
|
||||||
} else {
|
|
||||||
Self::do_mode_setup_tasks(vendor, vfio_enable, &devices, &bus)?;
|
|
||||||
Self::do_display_manager_action("restart")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save selected mode in case of reboot
|
|
||||||
Self::save_gfx_mode(mode_to_save, config);
|
|
||||||
info!("GFX thread: display-manager started");
|
|
||||||
|
|
||||||
let v: &str = vendor.into();
|
|
||||||
info!("GFX thread: Graphics mode changed to {} successfully", v);
|
|
||||||
Ok(format!("Graphics mode changed to {} successfully", v))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Before starting a new thread the old one *must* be cancelled
|
|
||||||
fn cancel_mode_change_thread(&self) {
|
|
||||||
if let Ok(lock) = self.thread_kill.lock() {
|
|
||||||
if let Some(tx) = lock.as_ref() {
|
|
||||||
// Cancel the running thread
|
|
||||||
info!("GFX: Cancelling previous thread");
|
|
||||||
tx.send(true)
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!("GFX thread: {}", err);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The thread is used only in cases where a logout is required
|
|
||||||
fn setup_mode_change_thread(&mut self, vendor: GfxVendors) {
|
|
||||||
let config = self.config.clone();
|
|
||||||
let devices = self.nvidia.clone();
|
|
||||||
let bus = self.bus.clone();
|
|
||||||
let (tx, rx) = mpsc::channel();
|
|
||||||
if let Ok(mut lock) = self.thread_kill.lock() {
|
|
||||||
*lock = Some(tx);
|
|
||||||
}
|
|
||||||
let thread_kill = self.thread_kill.clone();
|
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
Self::create_mode_change_thread(vendor, devices, bus, rx, config)
|
|
||||||
.map_err(|err| {
|
|
||||||
error!("GFX: {}", err);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
// clear the tx/rx when done
|
|
||||||
if let Ok(mut lock) = thread_kill.try_lock() {
|
|
||||||
*lock = None;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initiates a mode change by starting a thread that will wait until all
|
|
||||||
/// graphical sessions are exited before performing the tasks required
|
|
||||||
/// to switch modes.
|
|
||||||
///
|
|
||||||
/// For manually calling (not on boot/startup) via dbus
|
|
||||||
pub fn set_gfx_mode(&mut self, vendor: GfxVendors) -> Result<GfxRequiredUserAction, RogError> {
|
|
||||||
if let Ok(gsync) = CtrlRogBios::get_gfx_mode() {
|
|
||||||
if gsync == 1 {
|
|
||||||
return Err(GfxError::GsyncModeActive.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let vfio_enable = if let Ok(config) = self.config.try_lock() {
|
|
||||||
config.gfx_vfio_enable
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
if !vfio_enable && matches!(vendor, GfxVendors::Vfio) {
|
|
||||||
return Err(GfxError::VfioDisabled.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must always cancel any thread running
|
|
||||||
self.cancel_mode_change_thread();
|
|
||||||
// determine which method we need here
|
|
||||||
let action_required = self.is_logout_required(vendor);
|
|
||||||
|
|
||||||
match action_required {
|
|
||||||
GfxRequiredUserAction::Logout => {
|
|
||||||
info!("GFX: mode change requires a logout to complete");
|
|
||||||
self.setup_mode_change_thread(vendor);
|
|
||||||
}
|
|
||||||
GfxRequiredUserAction::Reboot => {
|
|
||||||
info!("GFX: mode change requires reboot");
|
|
||||||
let devices = self.nvidia.clone();
|
|
||||||
let bus = self.bus.clone();
|
|
||||||
Self::do_mode_setup_tasks(vendor, vfio_enable, &devices, &bus)?;
|
|
||||||
info!("GFX: Graphics mode changed to {}", <&str>::from(vendor));
|
|
||||||
}
|
|
||||||
GfxRequiredUserAction::Integrated => {
|
|
||||||
info!("GFX: mode change requires user to be in Integrated mode first");
|
|
||||||
}
|
|
||||||
GfxRequiredUserAction::None => {
|
|
||||||
info!("GFX: mode change does not require logout");
|
|
||||||
let devices = self.nvidia.clone();
|
|
||||||
let bus = self.bus.clone();
|
|
||||||
Self::do_mode_setup_tasks(vendor, vfio_enable, &devices, &bus)?;
|
|
||||||
info!("GFX: Graphics mode changed to {}", <&str>::from(vendor));
|
|
||||||
if let Ok(mut config) = self.config.try_lock() {
|
|
||||||
config.gfx_tmp_mode = None;
|
|
||||||
if matches!(vendor, GfxVendors::Vfio | GfxVendors::Compute) {
|
|
||||||
config.gfx_tmp_mode = Some(vendor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(action_required)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Used only on boot to set correct mode
|
|
||||||
fn auto_power(&mut self) -> Result<(), RogError> {
|
|
||||||
let vendor = self.get_gfx_mode()?;
|
|
||||||
let devices = self.nvidia.clone();
|
|
||||||
let bus = self.bus.clone();
|
|
||||||
|
|
||||||
let vfio_enable = if let Ok(config) = self.config.try_lock() {
|
|
||||||
config.gfx_vfio_enable
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
Self::do_mode_setup_tasks(vendor, vfio_enable, &devices, &bus)?;
|
|
||||||
Self::toggle_fallback_service(vendor)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
use std::fmt;
|
|
||||||
use std::{error, process::ExitStatus};
|
|
||||||
|
|
||||||
use crate::error::RogError;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum GfxError {
|
|
||||||
ParseVendor,
|
|
||||||
ParsePower,
|
|
||||||
Bus(String, std::io::Error),
|
|
||||||
DisplayManagerAction(String, ExitStatus),
|
|
||||||
DisplayManagerTimeout(String),
|
|
||||||
GsyncModeActive,
|
|
||||||
VfioBuiltin,
|
|
||||||
VfioDisabled,
|
|
||||||
MissingModule(String),
|
|
||||||
Modprobe(String),
|
|
||||||
Command(String, std::io::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for GfxError {
|
|
||||||
// This trait requires `fmt` with this exact signature.
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
GfxError::ParseVendor => write!(f, "Could not parse vendor name"),
|
|
||||||
GfxError::ParsePower => write!(f, "Could not parse dGPU power status"),
|
|
||||||
GfxError::Bus(func, error) => write!(f, "Bus error: {}: {}", func, error),
|
|
||||||
GfxError::DisplayManagerAction(action, status) => {
|
|
||||||
write!(f, "Display-manager action {} failed: {}", action, status)
|
|
||||||
}
|
|
||||||
GfxError::DisplayManagerTimeout(state) => {
|
|
||||||
write!(f, "Timed out waiting for display-manager {} state", state)
|
|
||||||
}
|
|
||||||
GfxError::GsyncModeActive => write!(
|
|
||||||
f,
|
|
||||||
"Can not switch gfx modes when dedicated/G-Sync mode is active"
|
|
||||||
),
|
|
||||||
GfxError::VfioBuiltin => write!(
|
|
||||||
f,
|
|
||||||
"Can not switch to vfio mode if the modules are built in to kernel"
|
|
||||||
),
|
|
||||||
GfxError::VfioDisabled => {
|
|
||||||
write!(f, "Can not switch to vfio mode if disabled in config file")
|
|
||||||
}
|
|
||||||
GfxError::MissingModule(m) => write!(f, "The module {} is missing", m),
|
|
||||||
GfxError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail),
|
|
||||||
GfxError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl error::Error for GfxError {}
|
|
||||||
|
|
||||||
impl From<GfxError> for RogError {
|
|
||||||
fn from(err: GfxError) -> Self {
|
|
||||||
RogError::GfxSwitching(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
pub mod error;
|
|
||||||
|
|
||||||
pub mod controller;
|
|
||||||
|
|
||||||
pub mod system;
|
|
||||||
|
|
||||||
pub mod zbus_gfx;
|
|
||||||
|
|
||||||
const NVIDIA_DRIVERS: [&str; 4] = ["nvidia_drm", "nvidia_modeset", "nvidia_uvm", "nvidia"];
|
|
||||||
|
|
||||||
const VFIO_DRIVERS: [&str; 5] = [
|
|
||||||
"vfio-pci",
|
|
||||||
"vfio_iommu_type1",
|
|
||||||
"vfio_virqfd",
|
|
||||||
"vfio_mdev",
|
|
||||||
"vfio",
|
|
||||||
];
|
|
||||||
|
|
||||||
const DISPLAY_MANAGER: &str = "display-manager.service";
|
|
||||||
|
|
||||||
const MODPROBE_PATH: &str = "/etc/modprobe.d/asusd.conf";
|
|
||||||
|
|
||||||
static MODPROBE_BASE: &[u8] = br#"# Automatically generated by asusd
|
|
||||||
# If you have issues with i2c_nvidia_gpu, copy the 2 lines below to a
|
|
||||||
# new blacklist file and uncomment
|
|
||||||
#blacklist i2c_nvidia_gpu
|
|
||||||
#alias i2c_nvidia_gpu off
|
|
||||||
blacklist nouveau
|
|
||||||
alias nouveau off
|
|
||||||
options nvidia NVreg_DynamicPowerManagement=0x02
|
|
||||||
"#;
|
|
||||||
|
|
||||||
static MODPROBE_DRM_MODESET: &[u8] = br#"
|
|
||||||
options nvidia-drm modeset=1
|
|
||||||
"#;
|
|
||||||
|
|
||||||
static MODPROBE_INTEGRATED: &[u8] = br#"# Automatically generated by asusd
|
|
||||||
blacklist i2c_nvidia_gpu
|
|
||||||
blacklist nvidia
|
|
||||||
blacklist nvidia-drm
|
|
||||||
blacklist nvidia-modeset
|
|
||||||
blacklist nouveau
|
|
||||||
alias nouveau off
|
|
||||||
"#;
|
|
||||||
|
|
||||||
static MODPROBE_VFIO: &[u8] = br#"options vfio-pci ids="#;
|
|
||||||
|
|
||||||
const XORG_FILE: &str = "90-nvidia-primary.conf";
|
|
||||||
const XORG_PATH: &str = "/etc/X11/xorg.conf.d/";
|
|
||||||
|
|
||||||
static PRIMARY_GPU_BEGIN: &[u8] = br#"# Automatically generated by asusd
|
|
||||||
Section "OutputClass"
|
|
||||||
Identifier "nvidia"
|
|
||||||
MatchDriver "nvidia-drm"
|
|
||||||
Driver "nvidia"
|
|
||||||
Option "AllowEmptyInitialConfiguration" "true""#;
|
|
||||||
|
|
||||||
static PRIMARY_GPU_NVIDIA: &[u8] = br#"
|
|
||||||
Option "PrimaryGPU" "true""#;
|
|
||||||
|
|
||||||
static PRIMARY_GPU_END: &[u8] = br#"
|
|
||||||
EndSection"#;
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
use log::{error, info, warn};
|
|
||||||
use std::fs::read_to_string;
|
|
||||||
use std::{fs::write, io, path::PathBuf};
|
|
||||||
use sysfs_class::{PciDevice, SysClass};
|
|
||||||
|
|
||||||
pub struct Module {
|
|
||||||
pub name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Module {
|
|
||||||
fn parse(line: &str) -> io::Result<Module> {
|
|
||||||
let mut parts = line.split(' ');
|
|
||||||
|
|
||||||
let name = parts
|
|
||||||
.next()
|
|
||||||
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "module name not found"))?;
|
|
||||||
|
|
||||||
Ok(Module {
|
|
||||||
name: name.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn all() -> io::Result<Vec<Module>> {
|
|
||||||
let mut modules = Vec::new();
|
|
||||||
|
|
||||||
let data = read_to_string("/proc/modules")?;
|
|
||||||
for line in data.lines() {
|
|
||||||
let module = Module::parse(line)?;
|
|
||||||
modules.push(module);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(modules)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct PciBus {
|
|
||||||
path: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PciBus {
|
|
||||||
pub fn new() -> io::Result<PciBus> {
|
|
||||||
let path = PathBuf::from("/sys/bus/pci");
|
|
||||||
if path.is_dir() {
|
|
||||||
Ok(PciBus { path })
|
|
||||||
} else {
|
|
||||||
Err(io::Error::new(
|
|
||||||
io::ErrorKind::NotFound,
|
|
||||||
"pci directory not found",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Will rescan the device tree, which adds all removed devices back
|
|
||||||
pub fn rescan(&self) -> io::Result<()> {
|
|
||||||
write(self.path.join("rescan"), "1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct GraphicsDevice {
|
|
||||||
_id: String,
|
|
||||||
functions: Vec<PciDevice>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GraphicsDevice {
|
|
||||||
pub fn new(id: String, functions: Vec<PciDevice>) -> GraphicsDevice {
|
|
||||||
GraphicsDevice { _id: id, functions }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exists(&self) -> bool {
|
|
||||||
self.functions.iter().any(|func| func.path().exists())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn functions(&self) -> &[PciDevice] {
|
|
||||||
&self.functions
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unbind(&self) -> Result<(), std::io::Error> {
|
|
||||||
for func in self.functions.iter() {
|
|
||||||
if func.path().exists() {
|
|
||||||
match func.driver() {
|
|
||||||
Ok(driver) => {
|
|
||||||
info!("{}: Unbinding {}", driver.id(), func.id());
|
|
||||||
unsafe {
|
|
||||||
driver.unbind(func).map_err(|err| {
|
|
||||||
error!("gfx unbind: {}", err);
|
|
||||||
err
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => match err.kind() {
|
|
||||||
io::ErrorKind::NotFound => (),
|
|
||||||
_ => {
|
|
||||||
error!("gfx driver: {:?}, {}", func.path(), err);
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rebind(&self) -> Result<(), std::io::Error> {
|
|
||||||
for func in self.functions.iter() {
|
|
||||||
if func.path().exists() {
|
|
||||||
match func.driver() {
|
|
||||||
Ok(driver) => {
|
|
||||||
info!("{}: Binding {}", driver.id(), func.id());
|
|
||||||
unsafe {
|
|
||||||
driver.bind(func).map_err(|err| {
|
|
||||||
error!("gfx bind: {}", err);
|
|
||||||
err
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => match err.kind() {
|
|
||||||
io::ErrorKind::NotFound => (),
|
|
||||||
_ => {
|
|
||||||
error!("gfx driver: {:?}, {}", func.path(), err);
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove(&self) -> Result<(), std::io::Error> {
|
|
||||||
for func in self.functions.iter() {
|
|
||||||
if func.path().exists() {
|
|
||||||
match func.driver() {
|
|
||||||
Ok(driver) => {
|
|
||||||
error!("{}: in use by {}", func.id(), driver.id());
|
|
||||||
}
|
|
||||||
Err(why) => match why.kind() {
|
|
||||||
std::io::ErrorKind::NotFound => {
|
|
||||||
info!("{}: Removing", func.id());
|
|
||||||
unsafe {
|
|
||||||
// ignore errors and carry on
|
|
||||||
if let Err(err) = func.remove() {
|
|
||||||
error!("gfx remove: {}", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
error!("Remove device failed");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
warn!("{}: Already removed", func.id());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
info!("Removed all gfx devices");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
use ::zbus::dbus_interface;
|
|
||||||
use log::{error, info, warn};
|
|
||||||
use rog_types::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors};
|
|
||||||
use zvariant::ObjectPath;
|
|
||||||
|
|
||||||
use crate::ZbusAdd;
|
|
||||||
|
|
||||||
use super::controller::CtrlGraphics;
|
|
||||||
|
|
||||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
|
||||||
impl CtrlGraphics {
|
|
||||||
fn vendor(&self) -> zbus::fdo::Result<GfxVendors> {
|
|
||||||
self.get_gfx_mode().map_err(|err| {
|
|
||||||
error!("GFX: {}", err);
|
|
||||||
zbus::fdo::Error::Failed(format!("GFX fail: {}", err))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn power(&self) -> zbus::fdo::Result<GfxPower> {
|
|
||||||
Self::get_runtime_status().map_err(|err| {
|
|
||||||
error!("GFX: {}", err);
|
|
||||||
zbus::fdo::Error::Failed(format!("GFX fail: {}", err))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_vendor(&mut self, vendor: GfxVendors) -> zbus::fdo::Result<GfxRequiredUserAction> {
|
|
||||||
info!("GFX: Switching gfx mode to {}", <&str>::from(vendor));
|
|
||||||
let msg = self.set_gfx_mode(vendor).map_err(|err| {
|
|
||||||
error!("GFX: {}", err);
|
|
||||||
zbus::fdo::Error::Failed(format!("GFX fail: {}", err))
|
|
||||||
})?;
|
|
||||||
self.notify_gfx(&vendor)
|
|
||||||
.unwrap_or_else(|err| warn!("GFX: {}", err));
|
|
||||||
self.notify_action(&msg)
|
|
||||||
.unwrap_or_else(|err| warn!("GFX: {}", err));
|
|
||||||
Ok(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[dbus_interface(signal)]
|
|
||||||
fn notify_gfx(&self, vendor: &GfxVendors) -> zbus::Result<()> {}
|
|
||||||
|
|
||||||
#[dbus_interface(signal)]
|
|
||||||
fn notify_action(&self, action: &GfxRequiredUserAction) -> zbus::Result<()> {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ZbusAdd for CtrlGraphics {
|
|
||||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
|
||||||
server
|
|
||||||
.at(&ObjectPath::from_str_unchecked("/org/asuslinux/Gfx"), self)
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!("GFX: CtrlGraphics: add_to_server {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
use log::{error, warn};
|
|
||||||
use rog_aura::{AuraEffect, LedBrightness, LedPowerStates};
|
|
||||||
use zbus::dbus_interface;
|
|
||||||
use zvariant::ObjectPath;
|
|
||||||
|
|
||||||
use super::controller::CtrlKbdLedZbus;
|
|
||||||
|
|
||||||
impl crate::ZbusAdd for CtrlKbdLedZbus {
|
|
||||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
|
||||||
server
|
|
||||||
.at(&ObjectPath::from_str_unchecked("/org/asuslinux/Led"), self)
|
|
||||||
.map_err(|err| {
|
|
||||||
error!("DbusKbdLed: add_to_server {}", err);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The main interface for changing, reading, or notfying signals
|
|
||||||
///
|
|
||||||
/// LED commands are split between Brightness, Modes, Per-Key
|
|
||||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
|
||||||
impl CtrlKbdLedZbus {
|
|
||||||
/// Set the keyboard brightness level (0-3)
|
|
||||||
fn set_brightness(&mut self, brightness: LedBrightness) {
|
|
||||||
if let Ok(ctrl) = self.0.try_lock() {
|
|
||||||
ctrl.set_brightness(brightness)
|
|
||||||
.map_err(|err| warn!("{}", err))
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the keyboard LED to enabled while the device is awake
|
|
||||||
fn set_awake_enabled(&mut self, enabled: bool) {
|
|
||||||
if let Ok(mut ctrl) = self.0.try_lock() {
|
|
||||||
ctrl.set_states_enabled(enabled, ctrl.config.sleep_anim_enabled)
|
|
||||||
.map_err(|err| warn!("{}", err))
|
|
||||||
.ok();
|
|
||||||
ctrl.config.awake_enabled = enabled;
|
|
||||||
ctrl.config.write();
|
|
||||||
|
|
||||||
let states = LedPowerStates {
|
|
||||||
enabled: ctrl.config.awake_enabled,
|
|
||||||
sleep_anim_enabled: ctrl.config.sleep_anim_enabled,
|
|
||||||
};
|
|
||||||
self.notify_power_states(&states)
|
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the keyboard LED suspend animation to enabled while the device is suspended
|
|
||||||
fn set_sleep_enabled(&mut self, enabled: bool) {
|
|
||||||
if let Ok(mut ctrl) = self.0.try_lock() {
|
|
||||||
ctrl.set_states_enabled(ctrl.config.awake_enabled, enabled)
|
|
||||||
.map_err(|err| warn!("{}", err))
|
|
||||||
.ok();
|
|
||||||
ctrl.config.sleep_anim_enabled = enabled;
|
|
||||||
ctrl.config.write();
|
|
||||||
let states = LedPowerStates {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_led_mode(&self) {
|
|
||||||
if let Ok(mut ctrl) = self.0.try_lock() {
|
|
||||||
ctrl.toggle_mode(false)
|
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
|
||||||
|
|
||||||
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
|
|
||||||
self.notify_led(mode.clone())
|
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prev_led_mode(&self) {
|
|
||||||
if let Ok(mut ctrl) = self.0.try_lock() {
|
|
||||||
ctrl.toggle_mode(true)
|
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
|
||||||
|
|
||||||
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
|
|
||||||
self.notify_led(mode.clone())
|
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[dbus_interface(property)]
|
|
||||||
fn awake_enabled(&self) -> bool {
|
|
||||||
if let Ok(ctrl) = self.0.try_lock() {
|
|
||||||
return ctrl.config.awake_enabled;
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
#[dbus_interface(property)]
|
|
||||||
fn sleep_enabled(&self) -> bool {
|
|
||||||
if let Ok(ctrl) = self.0.try_lock() {
|
|
||||||
return ctrl.config.sleep_anim_enabled;
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the current mode data
|
|
||||||
#[dbus_interface(property)]
|
|
||||||
fn led_mode(&self) -> String {
|
|
||||||
if let Ok(ctrl) = self.0.try_lock() {
|
|
||||||
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
|
|
||||||
if let Ok(json) = serde_json::to_string(&mode) {
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
warn!("SetKeyBacklight could not deserialise");
|
|
||||||
"SetKeyBacklight could not deserialise".to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return a list of available modes
|
|
||||||
#[dbus_interface(property)]
|
|
||||||
fn led_modes(&self) -> String {
|
|
||||||
if let Ok(ctrl) = self.0.try_lock() {
|
|
||||||
if let Ok(json) = serde_json::to_string(&ctrl.config.builtins) {
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
warn!("SetKeyBacklight could not deserialise");
|
|
||||||
"SetKeyBacklight could not serialise".to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the current LED brightness
|
|
||||||
#[dbus_interface(property)]
|
|
||||||
fn led_brightness(&self) -> i8 {
|
|
||||||
if let Ok(ctrl) = self.0.try_lock() {
|
|
||||||
return ctrl.get_brightness().map(|n| n as i8).unwrap_or(-1);
|
|
||||||
}
|
|
||||||
warn!("SetKeyBacklight could not serialise");
|
|
||||||
-1
|
|
||||||
}
|
|
||||||
|
|
||||||
#[dbus_interface(signal)]
|
|
||||||
fn notify_led(&self, data: AuraEffect) -> zbus::Result<()>;
|
|
||||||
|
|
||||||
#[dbus_interface(signal)]
|
|
||||||
fn notify_power_states(&self, data: &LedPowerStates) -> zbus::Result<()>;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
use log::{error, warn};
|
||||||
|
use rog_profiles::{FanCurveProfiles, Profile};
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use std::fs::{File, OpenOptions};
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
|
pub struct ProfileConfig {
|
||||||
|
#[serde(skip)]
|
||||||
|
config_path: String,
|
||||||
|
/// For restore on boot
|
||||||
|
pub active_profile: Profile,
|
||||||
|
/// States to restore
|
||||||
|
pub fan_curves: Option<FanCurveProfiles>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProfileConfig {
|
||||||
|
fn new(config_path: String) -> Self {
|
||||||
|
Self {
|
||||||
|
config_path,
|
||||||
|
active_profile: Profile::Balanced,
|
||||||
|
fan_curves: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_defaults_and_save(&mut self) {
|
||||||
|
self.active_profile = Profile::get_active_profile().unwrap_or(Profile::Balanced);
|
||||||
|
if let Ok(res) = FanCurveProfiles::is_supported() {
|
||||||
|
if res {
|
||||||
|
let curves = FanCurveProfiles::default();
|
||||||
|
self.fan_curves = Some(curves);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.write();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(config_path: String) -> Self {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.open(&config_path)
|
||||||
|
.unwrap_or_else(|_| panic!("The directory /etc/asusd/ is missing")); // okay to cause panic here
|
||||||
|
let mut buf = String::new();
|
||||||
|
let mut config;
|
||||||
|
if let Ok(read_len) = file.read_to_string(&mut buf) {
|
||||||
|
if read_len == 0 {
|
||||||
|
config = Self::new(config_path);
|
||||||
|
config.set_defaults_and_save();
|
||||||
|
} else if let Ok(data) = toml::from_str(&buf) {
|
||||||
|
config = data;
|
||||||
|
config.config_path = config_path;
|
||||||
|
} else {
|
||||||
|
warn!(
|
||||||
|
"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);
|
||||||
|
config.set_defaults_and_save();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
config = Self::new(config_path);
|
||||||
|
config.set_defaults_and_save();
|
||||||
|
}
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self) {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.open(&self.config_path)
|
||||||
|
.unwrap_or_else(|err| panic!("Error reading {}: {}", self.config_path, err));
|
||||||
|
|
||||||
|
let mut buf = String::new();
|
||||||
|
if let Ok(l) = file.read_to_string(&mut buf) {
|
||||||
|
if l == 0 {
|
||||||
|
warn!("File is empty {}", self.config_path);
|
||||||
|
} else {
|
||||||
|
let mut data: ProfileConfig = toml::from_str(&buf)
|
||||||
|
.unwrap_or_else(|_| panic!("Could not deserialise {}", self.config_path));
|
||||||
|
// copy over serde skipped values
|
||||||
|
data.config_path = self.config_path.clone();
|
||||||
|
*self = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&self) {
|
||||||
|
let mut file = File::create(&self.config_path).expect("Couldn't overwrite config");
|
||||||
|
let data = toml::to_string(self).expect("Parse config to toml failed");
|
||||||
|
file.write_all(data.as_bytes())
|
||||||
|
.unwrap_or_else(|err| error!("Could not write config: {}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,118 +1,157 @@
|
|||||||
use crate::error::RogError;
|
use std::sync::{Arc, Mutex};
|
||||||
use crate::{config::Config, GetSupported};
|
|
||||||
use log::{info, warn};
|
|
||||||
use rog_profiles::profiles::Profile;
|
|
||||||
use rog_types::supported::FanCpuSupportedFunctions;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
pub struct CtrlFanAndCpu {
|
use crate::error::RogError;
|
||||||
pub config: Arc<Mutex<Config>>,
|
use crate::{CtrlTask, GetSupported};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use log::{info, warn};
|
||||||
|
use rog_profiles::error::ProfileError;
|
||||||
|
use rog_profiles::{FanCurveProfiles, Profile};
|
||||||
|
use rog_supported::PlatformProfileFunctions;
|
||||||
|
use smol::Executor;
|
||||||
|
|
||||||
|
use super::config::ProfileConfig;
|
||||||
|
|
||||||
|
pub struct CtrlPlatformProfile {
|
||||||
|
pub config: ProfileConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GetSupported for CtrlFanAndCpu {
|
impl GetSupported for CtrlPlatformProfile {
|
||||||
type A = FanCpuSupportedFunctions;
|
type A = PlatformProfileFunctions;
|
||||||
|
|
||||||
fn get_supported() -> Self::A {
|
fn get_supported() -> Self::A {
|
||||||
FanCpuSupportedFunctions {
|
if !Profile::is_platform_profile_supported() {
|
||||||
stock_fan_modes: Profile::get_fan_path().is_ok(),
|
warn!(
|
||||||
min_max_freq: Profile::get_intel_supported(),
|
r#"
|
||||||
fan_curve_set: rog_fan_curve::Board::from_board_name().is_some(),
|
platform_profile kernel interface not found, your laptop does not support this, or the interface is missing.
|
||||||
|
To enable profile support you require a kernel version 5.15.2 minimum.
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = FanCurveProfiles::is_supported();
|
||||||
|
let mut fan_curve_supported = res.is_err();
|
||||||
|
if let Ok(r) = res {
|
||||||
|
fan_curve_supported = r;
|
||||||
|
};
|
||||||
|
|
||||||
|
if !fan_curve_supported {
|
||||||
|
info!(
|
||||||
|
r#"
|
||||||
|
fan curves kernel interface not found, your laptop does not support this, or the interface is missing.
|
||||||
|
To enable fan-curve support you require a kernel with the following patch applied:
|
||||||
|
https://lkml.org/lkml/2021/10/23/250
|
||||||
|
This patch has been accepted upstream for 5.17 kernel release.
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
PlatformProfileFunctions {
|
||||||
|
platform_profile: Profile::is_platform_profile_supported(),
|
||||||
|
fan_curves: fan_curve_supported,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::Reloadable for CtrlFanAndCpu {
|
impl crate::Reloadable for CtrlPlatformProfile {
|
||||||
/// Fetcht he 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 Ok(mut cfg) = self.config.clone().try_lock() {
|
if let Some(curves) = &mut self.config.fan_curves {
|
||||||
let active = cfg.active_profile.clone();
|
if let Ok(mut device) = FanCurveProfiles::get_device() {
|
||||||
if let Some(existing) = cfg.power_profiles.get_mut(&active) {
|
// There is a possibility that the curve was default zeroed, so this call initialises
|
||||||
existing.set_system_all()?;
|
// 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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CtrlFanAndCpu {
|
impl CtrlPlatformProfile {
|
||||||
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
|
pub fn new(config: ProfileConfig) -> Result<Self, RogError> {
|
||||||
Profile::get_fan_path()?;
|
if Profile::is_platform_profile_supported() {
|
||||||
info!("Device has fan control available");
|
info!("Device has profile control available");
|
||||||
Ok(CtrlFanAndCpu { config })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Toggle to next profile in list
|
if FanCurveProfiles::get_device().is_ok() {
|
||||||
pub(super) fn do_next_profile(&mut self) -> Result<(), RogError> {
|
info!("Device has fan curves available");
|
||||||
if let Ok(mut config) = self.config.clone().try_lock() {
|
|
||||||
// Read first just incase the user has modified the config before calling this
|
|
||||||
config.read();
|
|
||||||
|
|
||||||
let mut toggle_index = config
|
|
||||||
.toggle_profiles
|
|
||||||
.binary_search(&config.active_profile)
|
|
||||||
.unwrap_or(0)
|
|
||||||
+ 1;
|
|
||||||
if toggle_index >= config.toggle_profiles.len() {
|
|
||||||
toggle_index = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let profile = config.toggle_profiles[toggle_index].clone();
|
return Ok(CtrlPlatformProfile { config });
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(existing) = config.power_profiles.get(&profile) {
|
Err(ProfileError::NotSupported.into())
|
||||||
existing.set_system_all()?;
|
}
|
||||||
config.active_profile = existing.name.clone();
|
|
||||||
config.write();
|
pub fn save_config(&self) {
|
||||||
info!("Profile was changed to: {}", &profile);
|
self.config.write();
|
||||||
} else {
|
}
|
||||||
warn!(
|
|
||||||
"toggle_profile {} does not exist in power_profiles",
|
/// Toggle to next profile in list. This will first read the config, switch, then write out
|
||||||
&profile
|
pub(super) fn set_next_profile(&mut self) -> Result<(), RogError> {
|
||||||
);
|
// Read first just incase the user has modified the config before calling this
|
||||||
return Err(RogError::MissingProfile(profile.to_string()));
|
match self.config.active_profile {
|
||||||
|
Profile::Balanced => {
|
||||||
|
Profile::set_profile(Profile::Performance)?;
|
||||||
|
self.config.active_profile = Profile::Performance;
|
||||||
|
}
|
||||||
|
Profile::Performance => {
|
||||||
|
Profile::set_profile(Profile::Quiet)?;
|
||||||
|
self.config.active_profile = Profile::Quiet;
|
||||||
|
}
|
||||||
|
Profile::Quiet => {
|
||||||
|
Profile::set_profile(Profile::Balanced)?;
|
||||||
|
self.config.active_profile = Profile::Balanced;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.write_profile_curve_to_platform()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn set_active(&mut self, profile: &str) -> Result<(), RogError> {
|
pub(super) fn set_active_curve_to_defaults(&mut self) -> Result<(), RogError> {
|
||||||
if let Ok(mut config) = self.config.clone().try_lock() {
|
if let Some(curves) = self.config.fan_curves.as_mut() {
|
||||||
// Read first just incase the user has modified the config before calling this
|
if let Ok(mut device) = FanCurveProfiles::get_device() {
|
||||||
config.read();
|
curves.set_active_curve_to_defaults(self.config.active_profile, &mut device)?;
|
||||||
if let Some(existing) = config.power_profiles.get(profile) {
|
|
||||||
existing.set_system_all()?;
|
|
||||||
config.active_profile = existing.name.clone();
|
|
||||||
config.write();
|
|
||||||
info!("Profile was changed to: {}", profile);
|
|
||||||
} else {
|
|
||||||
warn!(
|
|
||||||
"toggle_profile {} does not exist in power_profiles",
|
|
||||||
profile
|
|
||||||
);
|
|
||||||
return Err(RogError::MissingProfile(profile.to_string()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/// Create a new profile if the requested name doesn't exist, or modify existing
|
|
||||||
pub(super) fn new_or_modify(&mut self, profile: &Profile) -> Result<(), RogError> {
|
pub struct CtrlProfileTask {
|
||||||
if let Ok(mut config) = self.config.clone().try_lock() {
|
ctrl: Arc<Mutex<CtrlPlatformProfile>>,
|
||||||
config.read();
|
}
|
||||||
|
|
||||||
if let Some(existing) = config.power_profiles.get_mut(&profile.name) {
|
impl CtrlProfileTask {
|
||||||
*existing = profile.clone();
|
pub fn new(ctrl: Arc<Mutex<CtrlPlatformProfile>>) -> Self {
|
||||||
existing.set_system_all()?;
|
Self { ctrl }
|
||||||
} else {
|
}
|
||||||
config
|
}
|
||||||
.power_profiles
|
|
||||||
.insert(profile.name.clone(), profile.clone());
|
#[async_trait]
|
||||||
profile.set_system_all()?;
|
impl CtrlTask for CtrlProfileTask {
|
||||||
}
|
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
|
||||||
|
let ctrl = self.ctrl.clone();
|
||||||
config.active_profile = profile.name.clone();
|
self.repeating_task(666, executor, move || {
|
||||||
config.write();
|
if let Ok(ref mut lock) = ctrl.try_lock() {
|
||||||
}
|
let new_profile = Profile::get_active_profile().unwrap();
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
pub mod zbus;
|
pub mod config;
|
||||||
|
|
||||||
pub mod controller;
|
pub mod controller;
|
||||||
|
pub mod zbus;
|
||||||
|
|||||||
@@ -1,163 +1,187 @@
|
|||||||
|
use async_trait::async_trait;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use rog_profiles::profiles::Profile;
|
use rog_profiles::fan_curve_set::CurveData;
|
||||||
|
use rog_profiles::fan_curve_set::FanCurveSet;
|
||||||
|
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::CtrlFanAndCpu;
|
use super::controller::CtrlPlatformProfile;
|
||||||
|
|
||||||
pub struct FanAndCpuZbus {
|
static UNSUPPORTED_MSG: &str =
|
||||||
inner: Arc<Mutex<CtrlFanAndCpu>>,
|
"Fan curves are not supported on this laptop or you require a patched kernel";
|
||||||
|
|
||||||
|
pub struct ProfileZbus {
|
||||||
|
inner: Arc<Mutex<CtrlPlatformProfile>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FanAndCpuZbus {
|
impl ProfileZbus {
|
||||||
pub fn new(inner: Arc<Mutex<CtrlFanAndCpu>>) -> Self {
|
pub fn new(inner: Arc<Mutex<CtrlPlatformProfile>>) -> Self {
|
||||||
Self { inner }
|
Self { inner }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||||
impl FanAndCpuZbus {
|
impl ProfileZbus {
|
||||||
/// Create new profile and make active
|
/// Fetch profile names
|
||||||
fn set_profile(&self, profile: String) {
|
|
||||||
if let Ok(mut ctrl) = self.inner.try_lock() {
|
|
||||||
ctrl.set_active(&profile)
|
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
|
||||||
}
|
|
||||||
self.do_notification();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// New or modify profile details and make active, will create if it does not exist
|
|
||||||
fn new_or_modify(&self, profile: Profile) {
|
|
||||||
if let Ok(mut ctrl) = self.inner.try_lock() {
|
|
||||||
ctrl.new_or_modify(&profile)
|
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
|
||||||
}
|
|
||||||
self.do_notification();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetch the active profile name
|
|
||||||
fn next_profile(&mut self) {
|
|
||||||
if let Ok(mut ctrl) = self.inner.try_lock() {
|
|
||||||
ctrl.do_next_profile()
|
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
|
||||||
}
|
|
||||||
self.do_notification();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetch the active profile name
|
|
||||||
fn active_name(&mut self) -> zbus::fdo::Result<String> {
|
|
||||||
if let Ok(ctrl) = self.inner.try_lock() {
|
|
||||||
if let Ok(mut cfg) = ctrl.config.try_lock() {
|
|
||||||
cfg.read();
|
|
||||||
return Ok(cfg.active_profile.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(Error::Failed(
|
|
||||||
"Failed to get active profile name".to_string(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Profile can't implement Type because of Curve
|
|
||||||
/// Fetch the active profile details
|
|
||||||
fn active_data(&mut self) -> zbus::fdo::Result<Profile> {
|
|
||||||
if let Ok(ctrl) = self.inner.try_lock() {
|
|
||||||
if let Ok(mut cfg) = ctrl.config.try_lock() {
|
|
||||||
cfg.read();
|
|
||||||
if let Some(profile) = cfg.power_profiles.get(&cfg.active_profile) {
|
|
||||||
return Ok(profile.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(Error::Failed(
|
|
||||||
"Failed to get active profile details".to_string(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetch all profile data
|
|
||||||
fn profiles(&mut self) -> zbus::fdo::Result<Vec<Profile>> {
|
fn profiles(&mut self) -> zbus::fdo::Result<Vec<Profile>> {
|
||||||
if let Ok(ctrl) = self.inner.try_lock() {
|
if let Ok(profiles) = Profile::get_profile_names() {
|
||||||
if let Ok(mut cfg) = ctrl.config.try_lock() {
|
return Ok(profiles);
|
||||||
cfg.read();
|
|
||||||
return Ok(cfg.power_profiles.values().cloned().collect());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(Error::Failed(
|
Err(Error::Failed(
|
||||||
"Failed to get all profile details".to_string(),
|
"Failed to get all profile details".to_string(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn profile_names(&self) -> zbus::fdo::Result<Vec<String>> {
|
/// Toggle to next platform_profile. Names provided by `Profiles`.
|
||||||
if let Ok(ctrl) = self.inner.try_lock() {
|
/// If fan-curves are supported will also activate a fan curve for profile.
|
||||||
if let Ok(mut cfg) = ctrl.config.try_lock() {
|
async fn next_profile(&mut self, #[zbus(signal_context)] ctxt: SignalContext<'_>) {
|
||||||
cfg.read();
|
let mut profile = None;
|
||||||
let profile_names = cfg.power_profiles.keys().cloned().collect::<Vec<String>>();
|
if let Ok(mut ctrl) = self.inner.try_lock() {
|
||||||
return Ok(profile_names);
|
ctrl.set_next_profile()
|
||||||
}
|
.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();
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(Error::Failed("Failed to get all profile names".to_string()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(&self, profile: &str) -> zbus::fdo::Result<()> {
|
/// Fetch the active profile name
|
||||||
if let Ok(ctrl) = self.inner.try_lock() {
|
fn active_profile(&mut self) -> zbus::fdo::Result<Profile> {
|
||||||
if let Ok(mut cfg) = ctrl.config.try_lock() {
|
if let Ok(mut ctrl) = self.inner.try_lock() {
|
||||||
cfg.read();
|
ctrl.config.read();
|
||||||
|
return Ok(ctrl.config.active_profile);
|
||||||
if !cfg.power_profiles.contains_key(profile) {
|
|
||||||
return Err(Error::Failed("Invalid profile specified".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.power_profiles.keys().len() == 1 {
|
|
||||||
return Err(Error::Failed("Cannot delete the last profile".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.active_profile == *profile {
|
|
||||||
return Err(Error::Failed(
|
|
||||||
"Cannot delete the active profile".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.power_profiles.remove(profile);
|
|
||||||
cfg.write();
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Err(Error::Failed(
|
||||||
|
"Failed to get active profile name".to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
Err(Error::Failed("Failed to lock configuration".to_string()))
|
/// Set this platform_profile name as active
|
||||||
|
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() {
|
||||||
|
// Read first just incase the user has modified the config before calling this
|
||||||
|
ctrl.config.read();
|
||||||
|
Profile::set_profile(profile)
|
||||||
|
.map_err(|e| warn!("set_profile, {}", e))
|
||||||
|
.ok();
|
||||||
|
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();
|
||||||
|
tmp = Some(ctrl.config.active_profile);
|
||||||
|
}
|
||||||
|
if let Some(profile) = tmp {
|
||||||
|
Self::notify_profile(&ctxt, profile).await.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a list of profiles that have fan-curves enabled.
|
||||||
|
fn enabled_fan_profiles(&mut self) -> zbus::fdo::Result<Vec<Profile>> {
|
||||||
|
if let Ok(mut ctrl) = self.inner.try_lock() {
|
||||||
|
ctrl.config.read();
|
||||||
|
if let Some(curves) = &ctrl.config.fan_curves {
|
||||||
|
return Ok(curves.get_enabled_curve_profiles().to_vec());
|
||||||
|
}
|
||||||
|
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
|
||||||
|
}
|
||||||
|
Err(Error::Failed(
|
||||||
|
"Failed to get enabled fan curve names".to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a profile fan curve enabled status. Will also activate a fan curve if in the
|
||||||
|
/// 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() {
|
||||||
|
ctrl.config.read();
|
||||||
|
return if let Some(curves) = &mut ctrl.config.fan_curves {
|
||||||
|
curves.set_profile_curve_enabled(profile, enabled);
|
||||||
|
|
||||||
|
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(
|
||||||
|
"Failed to get enabled fan curve names".to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the fan-curve data for the currently active Profile
|
||||||
|
fn fan_curve_data(&mut self, profile: Profile) -> zbus::fdo::Result<FanCurveSet> {
|
||||||
|
if let Ok(mut ctrl) = self.inner.try_lock() {
|
||||||
|
ctrl.config.read();
|
||||||
|
if let Some(curves) = &ctrl.config.fan_curves {
|
||||||
|
let curve = curves.get_fan_curves_for(profile);
|
||||||
|
return Ok(curve.clone());
|
||||||
|
}
|
||||||
|
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
|
||||||
|
}
|
||||||
|
Err(Error::Failed("Failed to get fan curve data".to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the fan curve for the specified profile.
|
||||||
|
/// 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() {
|
||||||
|
ctrl.config.read();
|
||||||
|
if let Some(curves) = &mut ctrl.config.fan_curves {
|
||||||
|
curves
|
||||||
|
.save_fan_curve(curve, profile)
|
||||||
|
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
|
||||||
|
} else {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 FanAndCpuZbus {
|
|
||||||
fn do_notification(&self) {
|
|
||||||
if let Ok(ctrl) = self.inner.try_lock() {
|
|
||||||
if let Ok(cfg) = ctrl.config.clone().try_lock() {
|
|
||||||
if let Some(profile) = cfg.power_profiles.get(&cfg.active_profile) {
|
|
||||||
self.notify_profile(profile)
|
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::ZbusAdd for FanAndCpuZbus {
|
#[async_trait]
|
||||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
impl crate::ZbusAdd for ProfileZbus {
|
||||||
server
|
async fn add_to_server(self, server: &mut Connection) {
|
||||||
.at(
|
Self::add_to_server_helper(self, "/org/asuslinux/Profile", server).await;
|
||||||
&ObjectPath::from_str_unchecked("/org/asuslinux/Profile"),
|
|
||||||
self,
|
|
||||||
)
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!("DbusFanAndCpu: add_to_server {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
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_types::supported::RogBiosSupportedFunctions;
|
use rog_supported::RogBiosSupportedFunctions;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::BufRead;
|
use std::io::BufRead;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
@@ -8,8 +9,8 @@ use std::path::Path;
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use zbus::dbus_interface;
|
use zbus::Connection;
|
||||||
use zvariant::ObjectPath;
|
use zbus::{dbus_interface, SignalContext};
|
||||||
|
|
||||||
const INITRAMFS_PATH: &str = "/usr/sbin/update-initramfs";
|
const INITRAMFS_PATH: &str = "/usr/sbin/update-initramfs";
|
||||||
const DRACUT_PATH: &str = "/usr/bin/dracut";
|
const DRACUT_PATH: &str = "/usr/bin/dracut";
|
||||||
@@ -36,22 +37,23 @@ impl GetSupported for CtrlRogBios {
|
|||||||
|
|
||||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||||
impl CtrlRogBios {
|
impl CtrlRogBios {
|
||||||
pub fn set_dedicated_graphic_mode(&mut self, dedicated: bool) {
|
async fn set_dedicated_graphic_mode(
|
||||||
|
&mut self,
|
||||||
|
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||||
|
dedicated: bool,
|
||||||
|
) {
|
||||||
self.set_gfx_mode(dedicated)
|
self.set_gfx_mode(dedicated)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
warn!("CtrlRogBios: set_asus_switch_graphic_mode {}", err);
|
warn!("CtrlRogBios: set_asus_switch_graphic_mode {}", err);
|
||||||
err
|
err
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
self.notify_dedicated_graphic_mode(dedicated)
|
Self::notify_dedicated_graphic_mode(&ctxt, dedicated)
|
||||||
.map_err(|err| {
|
.await
|
||||||
warn!("CtrlRogBios: notify_asus_switch_graphic_mode {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dedicated_graphic_mode(&self) -> i8 {
|
fn dedicated_graphic_mode(&self) -> i8 {
|
||||||
Self::get_gfx_mode()
|
Self::get_gfx_mode()
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
warn!("CtrlRogBios: get_gfx_mode {}", err);
|
warn!("CtrlRogBios: get_gfx_mode {}", err);
|
||||||
@@ -61,24 +63,27 @@ impl CtrlRogBios {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[dbus_interface(signal)]
|
#[dbus_interface(signal)]
|
||||||
pub fn notify_dedicated_graphic_mode(&self, dedicated: bool) -> zbus::Result<()> {}
|
async fn notify_dedicated_graphic_mode(
|
||||||
|
signal_ctxt: &SignalContext<'_>,
|
||||||
|
dedicated: bool,
|
||||||
|
) -> zbus::Result<()> {
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_post_boot_sound(&mut self, on: bool) {
|
async fn set_post_boot_sound(
|
||||||
|
&mut self,
|
||||||
|
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||||
|
on: bool,
|
||||||
|
) {
|
||||||
Self::set_boot_sound(on)
|
Self::set_boot_sound(on)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
warn!("CtrlRogBios: set_post_boot_sound {}", err);
|
warn!("CtrlRogBios: set_post_boot_sound {}", err);
|
||||||
err
|
err
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
self.notify_post_boot_sound(on)
|
Self::notify_post_boot_sound(&ctxt, on).await.ok();
|
||||||
.map_err(|err| {
|
|
||||||
warn!("CtrlRogBios: notify_post_boot_sound {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn post_boot_sound(&self) -> i8 {
|
fn post_boot_sound(&self) -> i8 {
|
||||||
Self::get_boot_sound()
|
Self::get_boot_sound()
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
warn!("CtrlRogBios: get_boot_sound {}", err);
|
warn!("CtrlRogBios: get_boot_sound {}", err);
|
||||||
@@ -88,21 +93,13 @@ impl CtrlRogBios {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[dbus_interface(signal)]
|
#[dbus_interface(signal)]
|
||||||
pub fn notify_post_boot_sound(&self, dedicated: bool) -> zbus::Result<()> {}
|
async fn notify_post_boot_sound(ctxt: &SignalContext<'_>, on: bool) -> zbus::Result<()> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl crate::ZbusAdd for CtrlRogBios {
|
impl crate::ZbusAdd for CtrlRogBios {
|
||||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
async fn add_to_server(self, server: &mut Connection) {
|
||||||
server
|
Self::add_to_server_helper(self, "/org/asuslinux/RogBios", server).await;
|
||||||
.at(
|
|
||||||
&ObjectPath::from_str_unchecked("/org/asuslinux/RogBios"),
|
|
||||||
self,
|
|
||||||
)
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!("CtrlRogBios: add_to_server {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +165,7 @@ impl CtrlRogBios {
|
|||||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||||
|
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
file.read_to_end(&mut data).unwrap();
|
file.read_to_end(&mut data)?;
|
||||||
|
|
||||||
let idx = data.len() - 1;
|
let idx = data.len() - 1;
|
||||||
if dedicated {
|
if dedicated {
|
||||||
@@ -269,7 +266,7 @@ impl CtrlRogBios {
|
|||||||
RogError::Write(module_include.to_string_lossy().to_string(), err)
|
RogError::Write(module_include.to_string_lossy().to_string(), err)
|
||||||
})?;
|
})?;
|
||||||
// add nvidia modules to module_include
|
// add nvidia modules to module_include
|
||||||
file.write_all(modules.concat().as_bytes()).unwrap();
|
file.write_all(modules.concat().as_bytes())?;
|
||||||
} else {
|
} else {
|
||||||
let file = std::fs::OpenOptions::new()
|
let file = std::fs::OpenOptions::new()
|
||||||
.read(true)
|
.read(true)
|
||||||
@@ -292,7 +289,7 @@ impl CtrlRogBios {
|
|||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
RogError::Write(module_include.to_string_lossy().to_string(), err)
|
RogError::Write(module_include.to_string_lossy().to_string(), err)
|
||||||
})?;
|
})?;
|
||||||
std::io::BufWriter::new(file).write_all(&buf).unwrap();
|
std::io::BufWriter::new(file).write_all(&buf)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
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_charge::CtrlCharge, ctrl_leds::controller::CtrlKbdLed,
|
ctrl_anime::CtrlAnime, ctrl_aura::controller::CtrlKbdLed, ctrl_charge::CtrlCharge,
|
||||||
ctrl_profiles::controller::CtrlFanAndCpu, ctrl_rog_bios::CtrlRogBios, GetSupported,
|
ctrl_profiles::controller::CtrlPlatformProfile, ctrl_rog_bios::CtrlRogBios, GetSupported,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rog_types::supported::{
|
use rog_supported::{
|
||||||
AnimeSupportedFunctions, ChargeSupportedFunctions, FanCpuSupportedFunctions,
|
AnimeSupportedFunctions, ChargeSupportedFunctions, LedSupportedFunctions,
|
||||||
LedSupportedFunctions, RogBiosSupportedFunctions,
|
PlatformProfileFunctions, RogBiosSupportedFunctions,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Type)]
|
#[derive(Serialize, Deserialize, Type)]
|
||||||
pub struct SupportedFunctions {
|
pub struct SupportedFunctions {
|
||||||
pub anime_ctrl: AnimeSupportedFunctions,
|
pub anime_ctrl: AnimeSupportedFunctions,
|
||||||
pub charge_ctrl: ChargeSupportedFunctions,
|
pub charge_ctrl: ChargeSupportedFunctions,
|
||||||
pub fan_cpu_ctrl: FanCpuSupportedFunctions,
|
pub platform_profile: PlatformProfileFunctions,
|
||||||
pub keyboard_led: LedSupportedFunctions,
|
pub keyboard_led: LedSupportedFunctions,
|
||||||
pub rog_bios_ctrl: RogBiosSupportedFunctions,
|
pub rog_bios_ctrl: RogBiosSupportedFunctions,
|
||||||
}
|
}
|
||||||
@@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +45,7 @@ impl GetSupported for SupportedFunctions {
|
|||||||
anime_ctrl: CtrlAnime::get_supported(),
|
anime_ctrl: CtrlAnime::get_supported(),
|
||||||
keyboard_led: CtrlKbdLed::get_supported(),
|
keyboard_led: CtrlKbdLed::get_supported(),
|
||||||
charge_ctrl: CtrlCharge::get_supported(),
|
charge_ctrl: CtrlCharge::get_supported(),
|
||||||
fan_cpu_ctrl: CtrlFanAndCpu::get_supported(),
|
platform_profile: CtrlPlatformProfile::get_supported(),
|
||||||
rog_bios_ctrl: CtrlRogBios::get_supported(),
|
rog_bios_ctrl: CtrlRogBios::get_supported(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,36 @@
|
|||||||
use daemon::ctrl_leds::controller::{
|
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::zbus::CtrlAnimeZbus;
|
||||||
|
use daemon::ctrl_anime::*;
|
||||||
|
use daemon::ctrl_aura::config::AuraConfig;
|
||||||
|
use daemon::ctrl_aura::controller::{
|
||||||
CtrlKbdLed, CtrlKbdLedReloader, CtrlKbdLedTask, CtrlKbdLedZbus,
|
CtrlKbdLed, CtrlKbdLedReloader, CtrlKbdLedTask, CtrlKbdLedZbus,
|
||||||
};
|
};
|
||||||
|
use daemon::ctrl_charge::CtrlCharge;
|
||||||
|
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,
|
||||||
};
|
};
|
||||||
use daemon::{config_anime::AnimeConfig, config_aura::AuraConfig, ctrl_charge::CtrlCharge};
|
|
||||||
use daemon::{ctrl_anime::*, ctrl_gfx::controller::CtrlGraphics};
|
|
||||||
use daemon::{
|
use daemon::{
|
||||||
ctrl_profiles::{controller::CtrlFanAndCpu, zbus::FanAndCpuZbus},
|
ctrl_profiles::{controller::CtrlPlatformProfile, zbus::ProfileZbus},
|
||||||
laptops::LaptopLedData,
|
laptops::LaptopLedData,
|
||||||
};
|
};
|
||||||
|
|
||||||
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_types::gfx_vendors::GfxVendors;
|
use rog_profiles::Profile;
|
||||||
use std::error::Error;
|
|
||||||
use std::io::Write;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::Mutex;
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
use daemon::ctrl_rog_bios::CtrlRogBios;
|
static PROFILE_CONFIG_PATH: &str = "/etc/asusd/profile.conf";
|
||||||
use std::convert::Into;
|
|
||||||
use zbus::fdo;
|
|
||||||
use zbus::Connection;
|
|
||||||
use zvariant::ObjectPath;
|
|
||||||
|
|
||||||
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut logger = env_logger::Builder::new();
|
let mut logger = env_logger::Builder::new();
|
||||||
@@ -43,41 +47,39 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
if !is_service {
|
if !is_service {
|
||||||
println!("asusd schould be only run from the right systemd service");
|
println!("asusd schould be only run from the right systemd service");
|
||||||
println!("do not run in your terminal, if you need an logs please use journalctl -b -u asusd");
|
println!(
|
||||||
|
"do not run in your terminal, if you need an logs please use journalctl -b -u asusd"
|
||||||
|
);
|
||||||
println!("asusd will now exit");
|
println!("asusd will now exit");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
info!(" daemon v{}", daemon::VERSION);
|
info!(" daemon v{}", daemon::VERSION);
|
||||||
info!(" rog-anime v{}", rog_anime::VERSION);
|
info!(" rog-anime v{}", rog_anime::VERSION);
|
||||||
info!(" rog-aura v{}", rog_aura::VERSION);
|
info!(" rog-aura v{}", rog_aura::VERSION);
|
||||||
info!(" rog-dbus v{}", rog_dbus::VERSION);
|
info!(" rog-dbus v{}", rog_dbus::VERSION);
|
||||||
info!("rog-profiles v{}", rog_profiles::VERSION);
|
info!(" rog-profiles v{}", rog_profiles::VERSION);
|
||||||
info!(" rog-types v{}", rog_types::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).unwrap());
|
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?;
|
||||||
fdo::DBusProxy::new(&connection)?
|
|
||||||
.request_name(DBUS_NAME, fdo::RequestNameFlags::ReplaceExisting.into())?;
|
|
||||||
let mut object_server = zbus::ObjectServer::new(&connection);
|
|
||||||
|
|
||||||
let config = Config::load();
|
let config = Config::load();
|
||||||
let enable_gfx_switching = config.gfx_managed;
|
|
||||||
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) => {
|
||||||
@@ -85,7 +87,7 @@ 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);
|
||||||
@@ -98,23 +100,36 @@ 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;
|
||||||
|
|
||||||
|
let task = CtrlCharge::new(config)?;
|
||||||
|
task.create_tasks(executor).await.ok();
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("charge_control: {}", err);
|
error!("charge_control: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match CtrlFanAndCpu::new(config.clone()) {
|
if Profile::is_platform_profile_supported() {
|
||||||
Ok(mut ctrl) => {
|
let profile_config = ProfileConfig::load(PROFILE_CONFIG_PATH.into());
|
||||||
ctrl.reload()
|
match CtrlPlatformProfile::new(profile_config) {
|
||||||
.unwrap_or_else(|err| warn!("Profile control: {}", err));
|
Ok(mut ctrl) => {
|
||||||
let tmp = Arc::new(Mutex::new(ctrl));
|
ctrl.reload()
|
||||||
FanAndCpuZbus::new(tmp).add_to_server(&mut object_server);
|
.unwrap_or_else(|err| warn!("Profile control: {}", err));
|
||||||
}
|
|
||||||
Err(err) => {
|
let tmp = Arc::new(Mutex::new(ctrl));
|
||||||
error!("Profile control: {}", err);
|
let task = CtrlProfileTask::new(tmp.clone());
|
||||||
|
task.create_tasks(executor).await.ok();
|
||||||
|
|
||||||
|
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()) {
|
||||||
@@ -127,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);
|
||||||
@@ -147,95 +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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Graphics switching requires some checks on boot specifically for g-sync capable laptops
|
// Request dbus name after finishing initalizing all functions
|
||||||
if enable_gfx_switching {
|
connection.request_name(DBUS_NAME).await?;
|
||||||
match CtrlGraphics::new(config.clone()) {
|
|
||||||
Ok(mut ctrl) => {
|
|
||||||
// Need to check if a laptop has the dedicated gfx switch
|
|
||||||
if CtrlRogBios::has_dedicated_gfx_toggle() {
|
|
||||||
if let Ok(ded) = CtrlRogBios::get_gfx_mode() {
|
|
||||||
if let Ok(config) = config.lock() {
|
|
||||||
if ded == 1 {
|
|
||||||
warn!("Dedicated GFX toggle is on but driver mode is not nvidia \nSetting to nvidia driver mode");
|
|
||||||
let devices = ctrl.devices();
|
|
||||||
let bus = ctrl.bus();
|
|
||||||
CtrlGraphics::do_mode_setup_tasks(
|
|
||||||
GfxVendors::Nvidia,
|
|
||||||
false,
|
|
||||||
&devices,
|
|
||||||
&bus,
|
|
||||||
)?;
|
|
||||||
} else if ded == 0 {
|
|
||||||
info!("Dedicated GFX toggle is off");
|
|
||||||
let devices = ctrl.devices();
|
|
||||||
let bus = ctrl.bus();
|
|
||||||
CtrlGraphics::do_mode_setup_tasks(
|
|
||||||
config.gfx_mode,
|
|
||||||
false,
|
|
||||||
&devices,
|
|
||||||
&bus,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctrl.reload()
|
|
||||||
.unwrap_or_else(|err| error!("Gfx controller: {}", err));
|
|
||||||
ctrl.add_to_server(&mut object_server);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
error!("Gfx 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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Run zbus server
|
|
||||||
object_server
|
|
||||||
.with(
|
|
||||||
&ObjectPath::from_str_unchecked("/org/asuslinux/Charge"),
|
|
||||||
|obj: &CtrlCharge| {
|
|
||||||
let x = obj.limit();
|
|
||||||
obj.notify_charge(x as u8)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!("object_server notify_charge error: {}", err);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
// 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,11 +1,7 @@
|
|||||||
use rog_fan_curve::CurveError;
|
|
||||||
use rog_profiles::error::ProfileError;
|
use rog_profiles::error::ProfileError;
|
||||||
use rog_types::error::GraphicsError;
|
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use crate::ctrl_gfx::error::GfxError;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum RogError {
|
pub enum RogError {
|
||||||
ParseVendor,
|
ParseVendor,
|
||||||
@@ -17,18 +13,16 @@ pub enum RogError {
|
|||||||
Write(String, std::io::Error),
|
Write(String, std::io::Error),
|
||||||
NotSupported,
|
NotSupported,
|
||||||
NotFound(String),
|
NotFound(String),
|
||||||
FanCurve(CurveError),
|
|
||||||
DoTask(String),
|
DoTask(String),
|
||||||
MissingFunction(String),
|
MissingFunction(String),
|
||||||
MissingLedBrightNode(String, std::io::Error),
|
MissingLedBrightNode(String, std::io::Error),
|
||||||
ReloadFail(String),
|
ReloadFail(String),
|
||||||
GfxSwitching(GfxError),
|
|
||||||
Profiles(ProfileError),
|
Profiles(ProfileError),
|
||||||
Initramfs(String),
|
Initramfs(String),
|
||||||
Modprobe(String),
|
Modprobe(String),
|
||||||
Command(String, std::io::Error),
|
|
||||||
Io(std::io::Error),
|
Io(std::io::Error),
|
||||||
Zbus(zbus::Error),
|
Zbus(zbus::Error),
|
||||||
|
ChargeLimit(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for RogError {
|
impl fmt::Display for RogError {
|
||||||
@@ -44,39 +38,22 @@ impl fmt::Display for RogError {
|
|||||||
RogError::Write(path, error) => write!(f, "Write {}: {}", path, error),
|
RogError::Write(path, error) => write!(f, "Write {}: {}", path, error),
|
||||||
RogError::NotSupported => write!(f, "Not supported"),
|
RogError::NotSupported => write!(f, "Not supported"),
|
||||||
RogError::NotFound(deets) => write!(f, "Not found: {}", deets),
|
RogError::NotFound(deets) => write!(f, "Not found: {}", deets),
|
||||||
RogError::FanCurve(err) => write!(f, "Custom fan-curve error: {}", err),
|
|
||||||
RogError::DoTask(deets) => write!(f, "Task error: {}", deets),
|
RogError::DoTask(deets) => write!(f, "Task error: {}", deets),
|
||||||
RogError::MissingFunction(deets) => write!(f, "Missing functionality: {}", deets),
|
RogError::MissingFunction(deets) => write!(f, "Missing functionality: {}", deets),
|
||||||
RogError::MissingLedBrightNode(path, error) => write!(f, "Led node at {} is missing, please check you have the required patch or dkms module installed: {}", path, error),
|
RogError::MissingLedBrightNode(path, error) => write!(f, "Led node at {} is missing, please check you have the required patch or dkms module installed: {}", path, error),
|
||||||
RogError::ReloadFail(deets) => write!(f, "Task error: {}", deets),
|
RogError::ReloadFail(deets) => write!(f, "Task error: {}", deets),
|
||||||
RogError::GfxSwitching(deets) => write!(f, "Graphics switching error: {}", deets),
|
|
||||||
RogError::Profiles(deets) => write!(f, "Profile error: {}", deets),
|
RogError::Profiles(deets) => write!(f, "Profile error: {}", deets),
|
||||||
RogError::Initramfs(detail) => write!(f, "Initiramfs error: {}", detail),
|
RogError::Initramfs(detail) => write!(f, "Initiramfs error: {}", detail),
|
||||||
RogError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail),
|
RogError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail),
|
||||||
RogError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error),
|
|
||||||
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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for RogError {}
|
impl std::error::Error for RogError {}
|
||||||
|
|
||||||
impl From<CurveError> for RogError {
|
|
||||||
fn from(err: CurveError) -> Self {
|
|
||||||
RogError::FanCurve(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<GraphicsError> for RogError {
|
|
||||||
fn from(err: GraphicsError) -> Self {
|
|
||||||
match err {
|
|
||||||
GraphicsError::ParseVendor => RogError::GfxSwitching(GfxError::ParseVendor),
|
|
||||||
GraphicsError::ParsePower => RogError::GfxSwitching(GfxError::ParsePower),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ProfileError> for RogError {
|
impl From<ProfileError> for RogError {
|
||||||
fn from(err: ProfileError) -> Self {
|
fn from(err: ProfileError) -> Self {
|
||||||
RogError::Profiles(err)
|
RogError::Profiles(err)
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
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() {
|
||||||
let dmi = sysfs_class::DmiId::default();
|
let dmi = sysfs_class::DmiId::default();
|
||||||
let board_name = dmi.board_name().expect("Could not get board_name");
|
let board_name = dmi.board_name().expect("Could not get board_name");
|
||||||
let prod_name = dmi.product_name().expect("Could not get product_name");
|
|
||||||
let prod_family = dmi.product_family().expect("Could not get product_family");
|
let prod_family = dmi.product_family().expect("Could not get product_family");
|
||||||
|
|
||||||
info!("Product name: {}", prod_name.trim());
|
|
||||||
info!("Product family: {}", prod_family.trim());
|
info!("Product family: {}", prod_family.trim());
|
||||||
info!("Board name: {}", board_name.trim());
|
info!("Board name: {}", board_name.trim());
|
||||||
}
|
}
|
||||||
@@ -34,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,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
#![deny(unused_must_use)]
|
#![deny(unused_must_use)]
|
||||||
/// Configuration loading, saving
|
/// Configuration loading, saving
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod config_anime;
|
|
||||||
pub mod config_aura;
|
|
||||||
pub(crate) mod config_old;
|
|
||||||
/// Control of AniMe matrix display
|
/// Control of AniMe matrix display
|
||||||
pub mod ctrl_anime;
|
pub mod ctrl_anime;
|
||||||
|
/// Keyboard LED brightness control, RGB, and LED display modes
|
||||||
|
pub mod ctrl_aura;
|
||||||
/// Control of battery charge level
|
/// Control of battery charge level
|
||||||
pub mod ctrl_charge;
|
pub mod ctrl_charge;
|
||||||
/// GPU switching and power
|
|
||||||
pub mod ctrl_gfx;
|
|
||||||
/// Keyboard LED brightness control, RGB, and LED display modes
|
|
||||||
pub mod ctrl_leds;
|
|
||||||
/// Control CPU min/max freq and turbo, fan mode, fan curves
|
/// Control CPU min/max freq and turbo, fan mode, fan curves
|
||||||
///
|
///
|
||||||
/// Intel machines can control:
|
/// Intel machines can control:
|
||||||
@@ -32,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");
|
||||||
|
|
||||||
@@ -44,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,7 +0,0 @@
|
|||||||
# Enable runtime PM for NVIDIA VGA/3D controller devices on driver bind
|
|
||||||
ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="auto"
|
|
||||||
ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="auto"
|
|
||||||
|
|
||||||
# Disable runtime PM for NVIDIA VGA/3D controller devices on driver unbind
|
|
||||||
ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="on"
|
|
||||||
ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="on"
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
Section "ServerLayout"
|
|
||||||
Identifier "layout"
|
|
||||||
Option "AllowNVIDIAGPUScreens"
|
|
||||||
EndSection
|
|
||||||
@@ -2,117 +2,168 @@
|
|||||||
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"]
|
||||||
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", "G513QY"]
|
board_names = ["G513QE", "GX531", "G512LV", "G712LV", "G712LW", "G513IH", "G513QY", "G713QM", "G512"]
|
||||||
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
||||||
multizone = true
|
multizone = ["Key1", "Key2", "Key3", "Key4"]
|
||||||
per_key = false
|
per_key = false
|
||||||
|
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "ROG Strix"
|
prod_family = "ROG Strix"
|
||||||
board_names = ["G512LI", "G712LI", "G531GD"]
|
board_names = ["G512LI", "G712LI", "G531GD"]
|
||||||
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
||||||
multizone = false
|
multizone = []
|
||||||
per_key = false
|
per_key = false
|
||||||
|
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "ROG Strix"
|
prod_family = "ROG Strix"
|
||||||
board_names = ["G513IH"]
|
board_names = ["G513IM"]
|
||||||
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Pulse"]
|
standard = ["Flash", "Static", "Breathe", "Strobe", "Rainbow"]
|
||||||
multizone = true
|
multizone = []
|
||||||
per_key = false
|
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]]
|
||||||
|
prod_family = "ROG Zephyrus Duo 15 SE"
|
||||||
|
board_names = ["GX551Q"]
|
||||||
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
||||||
|
multizone = []
|
||||||
|
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
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Description=ASUS Notebook Control
|
Description=ASUS Notebook Control
|
||||||
StartLimitInterval=200
|
StartLimitInterval=200
|
||||||
StartLimitBurst=2
|
StartLimitBurst=2
|
||||||
Before=display-manager.service
|
Before=multi-user.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Environment=IS_SERVICE=1
|
Environment=IS_SERVICE=1
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
{
|
|
||||||
"anime": [
|
|
||||||
{
|
|
||||||
"AsusAnimation": {
|
|
||||||
"file": "/usr/share/asusd/anime/asus/rog/Sunset.gif",
|
|
||||||
"time": {
|
|
||||||
"Cycles": 1
|
|
||||||
},
|
|
||||||
"brightness": 0.5
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImageAnimation": {
|
|
||||||
"file": "/usr/share/asusd/anime/custom/sonic-run.gif",
|
|
||||||
"scale": 0.9,
|
|
||||||
"angle": 0.65,
|
|
||||||
"translation": [
|
|
||||||
0.0,
|
|
||||||
0.0
|
|
||||||
],
|
|
||||||
"time": {
|
|
||||||
"Time": {
|
|
||||||
"secs": 5,
|
|
||||||
"nanos": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"brightness": 0.5
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Image": {
|
|
||||||
"file": "/usr/share/asusd/anime/custom/rust.png",
|
|
||||||
"scale": 1.0,
|
|
||||||
"angle": 0.0,
|
|
||||||
"translation": [
|
|
||||||
0.0,
|
|
||||||
0.0
|
|
||||||
],
|
|
||||||
"brightness": 0.6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Pause": {
|
|
||||||
"secs": 6,
|
|
||||||
"nanos": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImageAnimation": {
|
|
||||||
"file": "/usr/share/asusd/anime/custom/sonic-wait.gif",
|
|
||||||
"scale": 0.9,
|
|
||||||
"angle": 0.0,
|
|
||||||
"translation": [
|
|
||||||
3.0,
|
|
||||||
2.0
|
|
||||||
],
|
|
||||||
"time": {
|
|
||||||
"Cycles": 2
|
|
||||||
},
|
|
||||||
"brightness": 0.5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -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.0.5"
|
version = "1.3.4"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = ["Luke <luke@ljones.dev>"]
|
authors = ["Luke <luke@ljones.dev>"]
|
||||||
@@ -13,18 +13,22 @@ edition = "2018"
|
|||||||
exclude = ["data"]
|
exclude = ["data"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["dbus"]
|
default = ["dbus", "detect"]
|
||||||
dbus = ["zvariant", "zvariant_derive"]
|
dbus = ["zvariant"]
|
||||||
|
detect = ["udev", "sysfs-class"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
png_pong = "^0.8.0"
|
png_pong = "^0.8.0"
|
||||||
pix = "0.13"
|
pix = "0.13"
|
||||||
gif = "^0.11.2"
|
gif = "^0.11.2"
|
||||||
|
log = "*"
|
||||||
|
|
||||||
serde = "^1.0"
|
serde = "^1.0"
|
||||||
serde_derive = "^1.0"
|
serde_derive = "^1.0"
|
||||||
|
|
||||||
glam = { version = "0.14.0", features = ["serde"] }
|
glam = { version = "0.20.5", features = ["serde"] }
|
||||||
|
|
||||||
zvariant = { version = "^2.6", optional = true }
|
zvariant = { version = "^3.0", optional = true }
|
||||||
zvariant_derive = { version = "^2.6", optional = true }
|
|
||||||
|
udev = { version = "^0.6", optional = true }
|
||||||
|
sysfs-class = { version = "^0.1", optional = true }
|
||||||
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 153 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 234 KiB After Width: | Height: | Size: 234 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |