Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 49234af08b | |||
| 8f5717def8 | |||
| 9e55c0b2ca | |||
| dd6ee91364 | |||
| efbb838ca1 | |||
| daf7d39d41 | |||
| 28b1194924 | |||
| 73d95ca187 | |||
| 00b7c6482f | |||
| 29c26e8c89 | |||
| fb124dd228 | |||
| 0599be02dc | |||
| 81a88263a9 | |||
| cea1fd2540 | |||
| f2ced3bc7c | |||
| dc2a05894b | |||
| 9ee962ad09 | |||
| eb7ef0af4f | |||
| dc51120c27 | |||
| fc4c2c4346 | |||
| 3f6be037c1 | |||
| 0a80c97f02 | |||
| 88abafc728 | |||
| ac880a0363 | |||
| baebd51d99 | |||
| 226620eb53 | |||
| 1b34079d14 | |||
| 8deeffcdad | |||
| b7e45d7305 | |||
| d9077db234 | |||
| 439b006342 | |||
| ffa74d52e5 | |||
| 6ccdd703e6 | |||
| bb910344b8 | |||
| b9c4ff9ca7 | |||
| 62a18d4e57 | |||
| f520e381a9 | |||
| 1dd543ddf3 | |||
| a7ef63bd8a | |||
| db43c0f2a4 | |||
| f0e5bb4ad1 |
@@ -4,3 +4,4 @@ cargo-config
|
|||||||
.idea
|
.idea
|
||||||
vendor-*
|
vendor-*
|
||||||
vendor_*
|
vendor_*
|
||||||
|
.vscode-ctags
|
||||||
|
|||||||
@@ -6,6 +6,89 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
# [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
|
||||||
|
### Added
|
||||||
|
- Enable multizone support on Strix 513IH
|
||||||
|
- Add G513QY ledmodes
|
||||||
|
### Changed
|
||||||
|
- Fix missing CLI command help for some supported options
|
||||||
|
- Fix incorrectly selecting profile by name, where the active profile was being copied to the selected profile
|
||||||
|
- Add `asusd` version back to `asusctl -v` report
|
||||||
|
- Fix various clippy warnings
|
||||||
|
|
||||||
|
# [3.7.1] - 2021-06-11
|
||||||
|
### Changed
|
||||||
|
- Refine graphics mode switching:
|
||||||
|
+ Disallow switching to compute or vfio mode unless existing mode is "Integrated"
|
||||||
|
|
||||||
|
# [3.7.0] - 2021-06-06
|
||||||
|
### Changed
|
||||||
|
- Set PM to auto for Nvidia always
|
||||||
|
- Extra info output for gfx dev scan
|
||||||
|
- Extra info in log for G-Sync to help prevent user confusion around gfx switching
|
||||||
|
- Add GA503Q led modes
|
||||||
|
- Added ability to fade in/out gifs and images for anime. This does break anime configs. See manual for details.
|
||||||
|
- Added task to CtrlLed to set the keyboard LED brightness on wake from suspend
|
||||||
|
+ requires a kernel patch which will be upstreamed and in fedora rog kernel
|
||||||
|
- Make gfx change from nvidia to vfio/compute also force-change to integrated _then_
|
||||||
|
to requested mode
|
||||||
|
- Fix invalid gfx status when switching from some modes
|
||||||
|
- Fix copy over of serde skipped config values on config reload
|
||||||
|
|
||||||
# [3.6.1] - 2021-05-25
|
# [3.6.1] - 2021-05-25
|
||||||
### Changed
|
### Changed
|
||||||
- Bugfix: write correct fan modes for profiles
|
- Bugfix: write correct fan modes for profiles
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[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
|
lto = true
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -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.9.0 minimum. It also requires the kernel patch for platform_profile support to be applied form [here](https://lkml.org/lkml/2021/8/18/1022) - this patch is included in the "rog" kernels we build for fedora and arch, and will hit kernel 5.15 upstream.
|
||||||
|
|
||||||
- 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/8/29/50) which is still in review as of 29/09/21. As with Profiles, this is included in the kernels we build, and will hit 5.15 kernel upstream.
|
||||||
|
|
||||||
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.
|
||||||
@@ -269,6 +211,7 @@ Each object in the array can be one of:
|
|||||||
<FLOAT>,
|
<FLOAT>,
|
||||||
<FLOAT>
|
<FLOAT>
|
||||||
],
|
],
|
||||||
|
"time": <TIME>,
|
||||||
"brightness": <FLOAT>
|
"brightness": <FLOAT>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -322,6 +265,27 @@ A cycle is how many gif loops to run:
|
|||||||
```json
|
```json
|
||||||
"time": "Infinite",
|
"time": "Infinite",
|
||||||
```
|
```
|
||||||
|
`Fade` allows an image or gif to fade in and out, and remain at max brightness to n time:
|
||||||
|
```json
|
||||||
|
"time": {
|
||||||
|
"Fade": {
|
||||||
|
"fade_in": {
|
||||||
|
"secs": 2,
|
||||||
|
"nanos": 0
|
||||||
|
},
|
||||||
|
"show_for": {
|
||||||
|
"secs": 1,
|
||||||
|
"nanos": 0
|
||||||
|
},
|
||||||
|
"fade_out": {
|
||||||
|
"secs": 2,
|
||||||
|
"nanos": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
```
|
||||||
|
`show_for` can be `null`, if it is `null` then the `show_for` becomes `gif_time_length - fade_in - fade_out`.
|
||||||
|
This is period for which the gif or image will be max brightness (as set).
|
||||||
|
|
||||||
**<INT>**
|
**<INT>**
|
||||||
|
|
||||||
|
|||||||
@@ -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,35 +43,48 @@ 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"
|
||||||
|
|
||||||
|
$(INSTALL_DATA) "./data/icons/scalable/gpu-compute.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-compute.svg"
|
||||||
|
$(INSTALL_DATA) "./data/icons/scalable/gpu-hybrid.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-hybrid.svg"
|
||||||
|
$(INSTALL_DATA) "./data/icons/scalable/gpu-integrated.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-integrated.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/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"
|
||||||
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_green.png"
|
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_green.png"
|
||||||
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_red.png"
|
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_red.png"
|
||||||
|
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-compute.svg"
|
||||||
|
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-hybrid.svg"
|
||||||
|
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-integrated.svg"
|
||||||
|
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-nvidia.svg"
|
||||||
|
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-vfio.svg"
|
||||||
|
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/notification-reboot.svg"
|
||||||
rm -f "$(DESTDIR)$(zshcpl)/_asusctl"
|
rm -f "$(DESTDIR)$(zshcpl)/_asusctl"
|
||||||
rm -f "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish"
|
rm -f "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish"
|
||||||
rm -rf "$(DESTDIR)$(datarootdir)/asusd"
|
rm -rf "$(DESTDIR)$(datarootdir)/asusd"
|
||||||
|
|||||||
@@ -5,6 +5,11 @@
|
|||||||
`asusd` is a utility for Linux to control many aspects of various ASUS laptops
|
`asusd` is a utility for Linux to control many aspects of various ASUS laptops
|
||||||
but can also be used with non-asus laptops with reduced features.
|
but can also be used with non-asus laptops with reduced features.
|
||||||
|
|
||||||
|
## Kernel patches required
|
||||||
|
|
||||||
|
1. https://lkml.org/lkml/2021/8/20/232
|
||||||
|
2. https://lkml.org/lkml/2021/8/18/1022
|
||||||
|
|
||||||
## 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.
|
||||||
@@ -25,7 +30,7 @@ Various patches are required for keyboard support. See [this post](https://asus-
|
|||||||
|
|
||||||
## Discord
|
## Discord
|
||||||
|
|
||||||
[Discord server link](https://discord.gg/ngbdKabAnP)
|
[Discord server link](https://discord.gg/4ZKGd7Un5t)
|
||||||
|
|
||||||
## SUPPORTED LAPTOPS
|
## SUPPORTED LAPTOPS
|
||||||
|
|
||||||
@@ -48,12 +53,9 @@ 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)
|
||||||
|
|
||||||
@@ -61,7 +63,7 @@ will probably suffer another rename once it becomes generic enough to do so.
|
|||||||
|
|
||||||
Requirements are rust >= 1.47 installed from rustup.io if the distro provided version is too old, and `make`.
|
Requirements are rust >= 1.47 installed from rustup.io if the distro provided version is too old, and `make`.
|
||||||
|
|
||||||
**Ubuntu*:** `apt install libclang-dev libudev-dev`
|
**Ubuntu:** `apt install libclang-dev libudev-dev`
|
||||||
|
|
||||||
**fedora:** `dnf install clang-devel systemd-devel`
|
**fedora:** `dnf install clang-devel systemd-devel`
|
||||||
|
|
||||||
@@ -69,6 +71,14 @@ Requirements are rust >= 1.47 installed from rustup.io if the distro provided ve
|
|||||||
|
|
||||||
Download repositories are available [here](https://download.opensuse.org/repositories/home:/luke_nukem:/asus/) for the latest versions of Fedora, Ubuntu, and openSUSE.
|
Download repositories are available [here](https://download.opensuse.org/repositories/home:/luke_nukem:/asus/) for the latest versions of Fedora, Ubuntu, and openSUSE.
|
||||||
|
|
||||||
|
### Ubuntu
|
||||||
|
|
||||||
|
sudo su -c "echo 'deb https://download.opensuse.org/repositories/home:/luke_nukem:/asus/xUbuntu_21.04/ /' > /etc/apt/sources.list.d/luke_nukem.list"
|
||||||
|
curl -fsSL https://download.opensuse.org/repositories/home:/luke_nukem:/asus/xUbuntu_21.04/Release.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/home_luke_nukem.gpg > /dev/null
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install asusctl dkms-hid-asus-rog
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Run `make` then `sudo make install` then reboot.
|
Run `make` then `sudo make install` then reboot.
|
||||||
@@ -84,11 +94,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,18 +1,19 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "asus-notify"
|
name = "asus-notify"
|
||||||
version = "3.0.0"
|
version = "3.0.2"
|
||||||
authors = ["Luke D Jones <luke@ljones.dev>"]
|
authors = ["Luke D Jones <luke@ljones.dev>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
zbus = "^1.9"
|
||||||
# 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_supported = { path = "../rog-supported" }
|
||||||
rog_profiles = { path = "../rog-profiles" }
|
rog_profiles = { path = "../rog-profiles" }
|
||||||
rog_types = { path = "../rog-types" }
|
|
||||||
daemon = { path = "../daemon" }
|
|
||||||
|
|
||||||
[dependencies.notify-rust]
|
[dependencies.notify-rust]
|
||||||
version = "^4.3"
|
version = "^4.3"
|
||||||
|
|||||||
@@ -1,25 +1,46 @@
|
|||||||
use notify_rust::{Hint, Notification, NotificationHandle};
|
use notify_rust::{Hint, Notification, NotificationHandle};
|
||||||
|
use rog_aura::AuraEffect;
|
||||||
use rog_dbus::{DbusProxies, Signals};
|
use rog_dbus::{DbusProxies, Signals};
|
||||||
use rog_profiles::profiles::{FanLevel, Profile};
|
use rog_profiles::Profile;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
const NOTIF_HEADER: &str = "ROG Control";
|
||||||
|
|
||||||
|
macro_rules! notify {
|
||||||
|
($notifier:ident, $last_notif:ident, $data:expr) => {
|
||||||
|
if let Some(notif) = $last_notif.take() {
|
||||||
|
notif.close();
|
||||||
|
}
|
||||||
|
if let Ok(x) = $notifier($data) {
|
||||||
|
$last_notif = Some(x);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! base_notification {
|
||||||
|
($body:expr) => {
|
||||||
|
Notification::new()
|
||||||
|
.summary(NOTIF_HEADER)
|
||||||
|
.body($body)
|
||||||
|
.timeout(2000)
|
||||||
|
.show()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
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!(" daemon version {}", daemon::VERSION);
|
|
||||||
println!(" rog-dbus version {}", rog_dbus::VERSION);
|
println!(" rog-dbus version {}", rog_dbus::VERSION);
|
||||||
|
|
||||||
let (proxies, conn) = DbusProxies::new()?;
|
let (proxies, conn) = DbusProxies::new()?;
|
||||||
let signals = Signals::new(&proxies)?;
|
let signals = Signals::new(&proxies)?;
|
||||||
|
|
||||||
let mut last_profile_notif: Option<NotificationHandle> = None;
|
let mut last_notification: Option<NotificationHandle> = None;
|
||||||
let mut last_led_notif: Option<NotificationHandle> = None;
|
|
||||||
let mut last_gfx_notif: Option<NotificationHandle> = None;
|
|
||||||
let mut last_chrg_notif: Option<NotificationHandle> = None;
|
|
||||||
|
|
||||||
let recv = proxies.setup_recv(conn);
|
let recv = proxies.setup_recv(conn);
|
||||||
let mut err_count = 0;
|
let mut err_count = 0;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
sleep(Duration::from_millis(100));
|
sleep(Duration::from_millis(100));
|
||||||
if let Err(err) = recv.next_signal() {
|
if let Err(err) = recv.next_signal() {
|
||||||
@@ -36,60 +57,30 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
}
|
}
|
||||||
err_count = 0;
|
err_count = 0;
|
||||||
|
|
||||||
if let Ok(vendor) = signals.gfx_vendor.try_recv() {
|
if let Ok(data) = signals.led_mode.try_recv() {
|
||||||
if let Some(notif) = last_gfx_notif.take() {
|
notify!(do_led_notif, last_notification, &data);
|
||||||
notif.close();
|
|
||||||
}
|
|
||||||
let x = do_notif(&format!(
|
|
||||||
"Graphics mode changed to {}",
|
|
||||||
<&str>::from(vendor)
|
|
||||||
))?;
|
|
||||||
last_gfx_notif = Some(x);
|
|
||||||
}
|
}
|
||||||
|
if let Ok(data) = signals.profile.try_recv() {
|
||||||
if let Ok(limit) = signals.charge.try_recv() {
|
notify!(do_thermal_notif, last_notification, &data);
|
||||||
if let Some(notif) = last_chrg_notif.take() {
|
|
||||||
notif.close();
|
|
||||||
}
|
|
||||||
let x = do_notif(&format!("Battery charge limit changed to {}", limit))?;
|
|
||||||
last_chrg_notif = Some(x);
|
|
||||||
}
|
}
|
||||||
|
if let Ok(data) = signals.charge.try_recv() {
|
||||||
if let Ok(profile) = signals.profile.try_recv() {
|
notify!(do_charge_notif, last_notification, &data);
|
||||||
if let Some(notif) = last_profile_notif.take() {
|
|
||||||
notif.close();
|
|
||||||
}
|
|
||||||
let x = do_thermal_notif(&profile)?;
|
|
||||||
last_profile_notif = Some(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(ledmode) = signals.led_mode.try_recv() {
|
|
||||||
if let Some(notif) = last_led_notif.take() {
|
|
||||||
notif.close();
|
|
||||||
}
|
|
||||||
let x = do_notif(&format!(
|
|
||||||
"Keyboard LED mode changed to {}",
|
|
||||||
ledmode.mode_name()
|
|
||||||
))?;
|
|
||||||
last_led_notif = Some(x);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
@@ -100,11 +91,14 @@ fn do_thermal_notif(profile: &Profile) -> Result<NotificationHandle, Box<dyn Err
|
|||||||
Ok(x)
|
Ok(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_notif(body: &str) -> Result<NotificationHandle, Box<dyn Error>> {
|
fn do_led_notif(ledmode: &AuraEffect) -> Result<NotificationHandle, notify_rust::error::Error> {
|
||||||
let x = Notification::new()
|
base_notification!(&format!(
|
||||||
.summary("ASUS ROG")
|
"Keyboard LED mode changed to {}",
|
||||||
.body(body)
|
ledmode.mode_name()
|
||||||
.timeout(2000)
|
))
|
||||||
.show()?;
|
|
||||||
Ok(x)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn do_charge_notif(limit: &u8) -> Result<NotificationHandle, notify_rust::error::Error> {
|
||||||
|
base_notification!(&format!("Battery charge limit changed to {}", limit))
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "asusctl"
|
name = "asusctl"
|
||||||
version = "3.5.0"
|
version = "4.0.5"
|
||||||
authors = ["Luke D Jones <luke@ljones.dev>"]
|
authors = ["Luke D Jones <luke@ljones.dev>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
zbus = "^1.9.1"
|
||||||
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" }
|
||||||
rog_fan_curve = { version = "^0.1", features = ["serde"] }
|
daemon = { path = "../daemon" }
|
||||||
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.2.3"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::{env, path::Path, thread::sleep};
|
use std::{env, path::Path, thread::sleep};
|
||||||
|
|
||||||
use rog_anime::{ActionData, AnimeAction, Sequences};
|
use rog_anime::{ActionData, ActionLoader, Sequences};
|
||||||
use rog_dbus::RogDbusClient;
|
use rog_dbus::RogDbusClient;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -17,7 +17,7 @@ fn main() {
|
|||||||
let mut seq = Sequences::new();
|
let mut seq = Sequences::new();
|
||||||
seq.insert(
|
seq.insert(
|
||||||
0,
|
0,
|
||||||
&AnimeAction::AsusAnimation {
|
&ActionLoader::AsusAnimation {
|
||||||
file: path.into(),
|
file: path.into(),
|
||||||
time: rog_anime::AnimTime::Infinite,
|
time: rog_anime::AnimTime::Infinite,
|
||||||
brightness,
|
brightness,
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ pub struct AnimeCommand {
|
|||||||
pub enum AnimeActions {
|
pub enum AnimeActions {
|
||||||
#[options(help = "change all leds brightness")]
|
#[options(help = "change all leds brightness")]
|
||||||
Leds(AnimeLeds),
|
Leds(AnimeLeds),
|
||||||
#[options(help = "display an 8bit greyscale png")]
|
#[options(help = "display an image png")]
|
||||||
Image(AnimeImage),
|
Image(AnimeImage),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,6 +124,8 @@ pub struct MultiColourSpeed {
|
|||||||
/// Byte value for setting the built-in mode.
|
/// Byte value for setting the built-in mode.
|
||||||
///
|
///
|
||||||
/// Enum corresponds to the required integer value
|
/// Enum corresponds to the required integer value
|
||||||
|
///
|
||||||
|
// NOTE: The option names here must match those in rog-aura crate
|
||||||
#[derive(Options)]
|
#[derive(Options)]
|
||||||
pub enum SetAuraBuiltin {
|
pub enum SetAuraBuiltin {
|
||||||
#[options(help = "set a single static colour")]
|
#[options(help = "set a single static colour")]
|
||||||
@@ -135,7 +137,7 @@ pub enum SetAuraBuiltin {
|
|||||||
#[options(help = "rainbow cycling in one of four directions")]
|
#[options(help = "rainbow cycling in one of four directions")]
|
||||||
Rainbow(SingleSpeedDirection),
|
Rainbow(SingleSpeedDirection),
|
||||||
#[options(help = "rain pattern mimicking raindrops")]
|
#[options(help = "rain pattern mimicking raindrops")]
|
||||||
Star(TwoColourSpeed),
|
Stars(TwoColourSpeed),
|
||||||
#[options(help = "rain pattern of three preset colours")]
|
#[options(help = "rain pattern of three preset colours")]
|
||||||
Rain(SingleSpeed),
|
Rain(SingleSpeed),
|
||||||
#[options(help = "pressed keys are highlighted to fade")]
|
#[options(help = "pressed keys are highlighted to fade")]
|
||||||
@@ -233,7 +235,7 @@ impl From<&SetAuraBuiltin> for AuraEffect {
|
|||||||
data.mode = AuraModeNum::Rainbow;
|
data.mode = AuraModeNum::Rainbow;
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
SetAuraBuiltin::Star(x) => {
|
SetAuraBuiltin::Stars(x) => {
|
||||||
let mut data: AuraEffect = x.into();
|
let mut data: AuraEffect = x.into();
|
||||||
data.mode = AuraModeNum::Star;
|
data.mode = AuraModeNum::Star;
|
||||||
data
|
data
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
use crate::{
|
||||||
|
anime_cli::AnimeCommand,
|
||||||
|
aura_cli::{LedBrightness, 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 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(
|
||||||
|
meta = "",
|
||||||
|
help = "set the keyboard LED to enabled while the device is awake"
|
||||||
|
)]
|
||||||
|
pub awake_enable: Option<bool>,
|
||||||
|
#[options(
|
||||||
|
meta = "",
|
||||||
|
help = "set the keyboard LED suspend animation to enabled while the device is suspended"
|
||||||
|
)]
|
||||||
|
pub sleep_enable: Option<bool>,
|
||||||
|
#[options(command)]
|
||||||
|
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,112 +1,26 @@
|
|||||||
mod anime_cli;
|
mod anime_cli;
|
||||||
mod aura_cli;
|
mod aura_cli;
|
||||||
|
mod cli_opts;
|
||||||
mod profiles_cli;
|
mod profiles_cli;
|
||||||
|
|
||||||
use crate::aura_cli::{LedBrightness, SetAuraBuiltin};
|
use crate::aura_cli::{LedBrightness, SetAuraBuiltin};
|
||||||
|
use crate::cli_opts::*;
|
||||||
use anime_cli::{AnimeActions, AnimeCommand};
|
use anime_cli::{AnimeActions, AnimeCommand};
|
||||||
use gumdrop::{Opt, Options};
|
use gumdrop::{Opt, Options};
|
||||||
use profiles_cli::ProfileCommand;
|
use profiles_cli::{FanCurveCommand, ProfileCommand};
|
||||||
use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2, ANIME_DATA_LEN};
|
use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2, ANIME_DATA_LEN};
|
||||||
use rog_aura::{self, AuraEffect};
|
use rog_aura::{self, AuraEffect};
|
||||||
use rog_dbus::RogDbusClient;
|
use rog_dbus::RogDbusClient;
|
||||||
use rog_profiles::profiles::Profile;
|
use rog_profiles::error::ProfileError;
|
||||||
use rog_types::{
|
use rog_supported::SupportedFunctions;
|
||||||
gfx_vendors::GfxVendors,
|
use rog_supported::{
|
||||||
supported::{
|
AnimeSupportedFunctions, LedSupportedFunctions, PlatformProfileFunctions,
|
||||||
FanCpuSupportedFunctions, LedSupportedFunctions, RogBiosSupportedFunctions,
|
RogBiosSupportedFunctions,
|
||||||
SupportedFunctions,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
use std::process::Command;
|
||||||
use std::{env::args, path::Path};
|
use std::{env::args, path::Path};
|
||||||
use yansi_term::Colour::Green;
|
|
||||||
use yansi_term::Colour::Red;
|
|
||||||
|
|
||||||
#[derive(Default, Options)]
|
const CONFIG_ADVICE: &str = "A config file need to be removed so a new one can be generated";
|
||||||
struct CliStart {
|
|
||||||
#[options(help_flag, help = "print help message")]
|
|
||||||
help: bool,
|
|
||||||
#[options(help = "show program version number")]
|
|
||||||
version: bool,
|
|
||||||
#[options(help = "show supported functions of this laptop")]
|
|
||||||
show_supported: bool,
|
|
||||||
#[options(meta = "", help = "<off, low, med, high>")]
|
|
||||||
kbd_bright: Option<LedBrightness>,
|
|
||||||
#[options(meta = "", help = "<20-100>")]
|
|
||||||
chg_limit: Option<u8>,
|
|
||||||
#[options(command)]
|
|
||||||
command: Option<CliCommand>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Options)]
|
|
||||||
enum CliCommand {
|
|
||||||
#[options(help = "Set the keyboard lighting from built-in modes")]
|
|
||||||
LedMode(LedModeCommand),
|
|
||||||
#[options(help = "Create and configure profiles")]
|
|
||||||
Profile(ProfileCommand),
|
|
||||||
#[options(help = "Set the graphics mode")]
|
|
||||||
Graphics(GraphicsCommand),
|
|
||||||
#[options(name = "anime", help = "Manage AniMe Matrix")]
|
|
||||||
Anime(AnimeCommand),
|
|
||||||
#[options(help = "Change bios settings")]
|
|
||||||
Bios(BiosCommand),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Options)]
|
|
||||||
struct LedModeCommand {
|
|
||||||
#[options(help = "print help message")]
|
|
||||||
help: bool,
|
|
||||||
#[options(help = "switch to next aura mode")]
|
|
||||||
next_mode: bool,
|
|
||||||
#[options(help = "switch to previous aura mode")]
|
|
||||||
prev_mode: bool,
|
|
||||||
#[options(
|
|
||||||
meta = "",
|
|
||||||
help = "set the keyboard LED to enabled while the device is awake"
|
|
||||||
)]
|
|
||||||
awake_enable: Option<bool>,
|
|
||||||
#[options(
|
|
||||||
meta = "",
|
|
||||||
help = "set the keyboard LED suspend animation to enabled while the device is suspended"
|
|
||||||
)]
|
|
||||||
sleep_enable: Option<bool>,
|
|
||||||
#[options(command)]
|
|
||||||
command: Option<SetAuraBuiltin>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Options)]
|
|
||||||
struct GraphicsCommand {
|
|
||||||
#[options(help = "print help message")]
|
|
||||||
help: bool,
|
|
||||||
#[options(
|
|
||||||
meta = "",
|
|
||||||
help = "Set graphics mode: <nvidia, hybrid, compute, integrated>"
|
|
||||||
)]
|
|
||||||
mode: Option<GfxVendors>,
|
|
||||||
#[options(help = "Get the current mode")]
|
|
||||||
get: bool,
|
|
||||||
#[options(help = "Get the current power status")]
|
|
||||||
pow: bool,
|
|
||||||
#[options(help = "Do not ask for confirmation")]
|
|
||||||
force: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Options, Debug)]
|
|
||||||
struct BiosCommand {
|
|
||||||
#[options(help = "print help message")]
|
|
||||||
help: bool,
|
|
||||||
#[options(meta = "", no_long, help = "set bios POST sound <true/false>")]
|
|
||||||
post_sound_set: Option<bool>,
|
|
||||||
#[options(no_long, help = "read bios POST sound")]
|
|
||||||
post_sound_get: bool,
|
|
||||||
#[options(
|
|
||||||
meta = "",
|
|
||||||
no_long,
|
|
||||||
help = "activate dGPU dedicated/G-Sync <true/false>"
|
|
||||||
)]
|
|
||||||
dedicated_gfx_set: Option<bool>,
|
|
||||||
#[options(no_long, help = "get GPU mode")]
|
|
||||||
dedicated_gfx_get: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let args: Vec<String> = args().skip(1).collect();
|
let args: Vec<String> = args().skip(1).collect();
|
||||||
@@ -129,86 +43,127 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (dbus, _) = RogDbusClient::new()?;
|
let (dbus, _) = RogDbusClient::new()
|
||||||
|
.map_err(|e| {
|
||||||
|
print_error_help(Box::new(e), None);
|
||||||
|
std::process::exit(3);
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let supported = dbus.proxies().supported().get_supported_functions()?;
|
let supported = dbus
|
||||||
|
.proxies()
|
||||||
if parsed.help {
|
.supported()
|
||||||
print_supported_help(&supported, &parsed);
|
.get_supported_functions()
|
||||||
println!("\nSee https://asus-linux.org/faq/ for additional help");
|
.map_err(|e| {
|
||||||
std::process::exit(1);
|
print_error_help(Box::new(e), None);
|
||||||
}
|
std::process::exit(4);
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
if parsed.version {
|
if parsed.version {
|
||||||
println!(" asusctl v{}", env!("CARGO_PKG_VERSION"));
|
print_versions();
|
||||||
println!(" rog-dbus v{}", rog_dbus::VERSION);
|
println!();
|
||||||
println!("rog-types v{}", rog_types::VERSION);
|
print_laptop_info();
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
match parsed.command {
|
if let Err(err) = do_parsed(&parsed, &supported, &dbus) {
|
||||||
Some(CliCommand::LedMode(mode)) => handle_led_mode(&dbus, &supported.keyboard_led, &mode)?,
|
print_error_help(err, Some(&supported));
|
||||||
Some(CliCommand::Profile(cmd)) => handle_profile(&dbus, &supported.fan_cpu_ctrl, &cmd)?,
|
}
|
||||||
Some(CliCommand::Graphics(cmd)) => do_gfx(&dbus, &supported.rog_bios_ctrl, cmd)?,
|
|
||||||
Some(CliCommand::Anime(cmd)) => {
|
|
||||||
if (cmd.command.is_none() && cmd.boot.is_none() && cmd.turn.is_none()) || cmd.help {
|
|
||||||
println!("Missing arg or command\n\n{}", cmd.self_usage());
|
|
||||||
if let Some(lst) = cmd.self_command_list() {
|
|
||||||
println!("\n{}", lst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(anime_turn) = cmd.turn {
|
|
||||||
dbus.proxies().anime().toggle_on(anime_turn.into())?
|
|
||||||
}
|
|
||||||
if let Some(anime_boot) = cmd.boot {
|
|
||||||
dbus.proxies().anime().toggle_boot_on(anime_boot.into())?
|
|
||||||
}
|
|
||||||
if let Some(action) = cmd.command {
|
|
||||||
match action {
|
|
||||||
AnimeActions::Leds(anime_leds) => {
|
|
||||||
let data = AnimeDataBuffer::from_vec(
|
|
||||||
[anime_leds.led_brightness(); ANIME_DATA_LEN].to_vec(),
|
|
||||||
);
|
|
||||||
dbus.proxies().anime().write(data)?;
|
|
||||||
}
|
|
||||||
AnimeActions::Image(image) => {
|
|
||||||
if image.help_requested() {
|
|
||||||
println!("Missing arg or command\n\n{}", image.self_usage());
|
|
||||||
if let Some(lst) = image.self_command_list() {
|
|
||||||
println!("\n{}", lst);
|
|
||||||
}
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let matrix = AnimeImage::from_png(
|
Ok(())
|
||||||
Path::new(&image.path),
|
}
|
||||||
image.scale,
|
|
||||||
image.angle,
|
|
||||||
Vec2::new(image.x_pos, image.y_pos),
|
|
||||||
image.bright,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
dbus.proxies()
|
fn print_error_help(err: Box<dyn std::error::Error>, supported: Option<&SupportedFunctions>) {
|
||||||
.anime()
|
if do_diagnose("asusd") {
|
||||||
.write(<AnimeDataBuffer>::from(&matrix))
|
println!("\nError: {}\n", err);
|
||||||
.unwrap();
|
print_versions();
|
||||||
}
|
println!();
|
||||||
}
|
print_laptop_info();
|
||||||
}
|
if let Some(supported) = supported {
|
||||||
|
println!();
|
||||||
|
println!("Supported laptop functions:\n\n{}", supported);
|
||||||
}
|
}
|
||||||
Some(CliCommand::Bios(cmd)) => handle_bios_option(&dbus, &supported.rog_bios_ctrl, &cmd)?,
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_versions() {
|
||||||
|
println!("App and daemon versions:");
|
||||||
|
println!(" asusctl v{}", env!("CARGO_PKG_VERSION"));
|
||||||
|
println!(" asusd v{}", daemon::VERSION);
|
||||||
|
println!("\nComponent crate versions:");
|
||||||
|
println!(" rog-anime v{}", rog_anime::VERSION);
|
||||||
|
println!(" rog-aura v{}", rog_aura::VERSION);
|
||||||
|
println!(" rog-dbus v{}", rog_dbus::VERSION);
|
||||||
|
println!(" rog-profiles v{}", rog_profiles::VERSION);
|
||||||
|
println!("rog-supported v{}", rog_supported::VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_laptop_info() {
|
||||||
|
let dmi = sysfs_class::DmiId::default();
|
||||||
|
let board_name = dmi.board_name().expect("Could not get board_name");
|
||||||
|
let prod_family = dmi.product_family().expect("Could not get product_family");
|
||||||
|
|
||||||
|
println!("Product family: {}", prod_family.trim());
|
||||||
|
println!("Board name: {}", board_name.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_diagnose(name: &str) -> bool {
|
||||||
|
if name != "asusd" && !check_systemd_unit_enabled(name) {
|
||||||
|
println!(
|
||||||
|
"\n\x1b[0;31m{} is not enabled, enable it with `systemctl enable {}\x1b[0m",
|
||||||
|
name, name
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} else if !check_systemd_unit_active(name) {
|
||||||
|
println!(
|
||||||
|
"\n\x1b[0;31m{} is not running, start it with `systemctl start {}\x1b[0m",
|
||||||
|
name, name
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
println!("\nSome error happened (sorry)");
|
||||||
|
println!(
|
||||||
|
"Please use `systemctl status {}` and `journalctl -b -u {}` for more information",
|
||||||
|
name, name
|
||||||
|
);
|
||||||
|
println!("{}", CONFIG_ADVICE);
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_parsed(
|
||||||
|
parsed: &CliStart,
|
||||||
|
supported: &SupportedFunctions,
|
||||||
|
dbus: &RogDbusClient,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
match &parsed.command {
|
||||||
|
Some(CliCommand::LedMode(mode)) => handle_led_mode(dbus, &supported.keyboard_led, mode)?,
|
||||||
|
Some(CliCommand::Profile(cmd)) => handle_profile(dbus, &supported.platform_profile, cmd)?,
|
||||||
|
Some(CliCommand::FanCurve(cmd)) => {
|
||||||
|
handle_fan_curve(dbus, &supported.platform_profile, cmd)?
|
||||||
|
}
|
||||||
|
Some(CliCommand::Graphics(_)) => do_gfx()?,
|
||||||
|
Some(CliCommand::Anime(cmd)) => handle_anime(dbus, &supported.anime_ctrl, cmd)?,
|
||||||
|
Some(CliCommand::Bios(cmd)) => handle_bios_option(dbus, &supported.rog_bios_ctrl, cmd)?,
|
||||||
None => {
|
None => {
|
||||||
if (!parsed.show_supported && parsed.kbd_bright.is_none() && parsed.chg_limit.is_none())
|
if (!parsed.show_supported
|
||||||
|
&& parsed.kbd_bright.is_none()
|
||||||
|
&& parsed.chg_limit.is_none()
|
||||||
|
&& !parsed.next_kbd_bright
|
||||||
|
&& !parsed.prev_kbd_bright)
|
||||||
|| parsed.help
|
|| parsed.help
|
||||||
{
|
{
|
||||||
println!("{}", CliStart::usage());
|
println!("{}", CliStart::usage());
|
||||||
println!();
|
println!();
|
||||||
println!("{}", CliStart::command_list().unwrap());
|
if let Some(cmdlist) = CliStart::command_list() {
|
||||||
|
println!("{}", cmdlist);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(brightness) = parsed.kbd_bright {
|
if let Some(brightness) = &parsed.kbd_bright {
|
||||||
match brightness.level() {
|
match brightness.level() {
|
||||||
None => {
|
None => {
|
||||||
let level = dbus.proxies().led().get_led_brightness()?;
|
let level = dbus.proxies().led().get_led_brightness()?;
|
||||||
@@ -221,9 +176,16 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if parsed.next_kbd_bright {
|
||||||
|
dbus.proxies().led().next_led_brightness()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if parsed.prev_kbd_bright {
|
||||||
|
dbus.proxies().led().prev_led_brightness()?;
|
||||||
|
}
|
||||||
|
|
||||||
if parsed.show_supported {
|
if parsed.show_supported {
|
||||||
let dat = dbus.proxies().supported().get_supported_functions()?;
|
println!("Supported laptop functions:\n\n{}", supported);
|
||||||
println!("Supported laptop functions:\n{:?}", dat);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(chg_limit) = parsed.chg_limit {
|
if let Some(chg_limit) = parsed.chg_limit {
|
||||||
@@ -233,98 +195,60 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_supported_help(supported: &SupportedFunctions, parsed: &CliStart) {
|
fn do_gfx() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
// As help option don't work with `parse_args_default`
|
println!("Please use supergfxctl for graphics switching. supergfxctl is the result of making asusctl graphics switching generic so all laptops can use it");
|
||||||
// we will call `parse_args_default_or_exit` instead
|
println!("This command will be removed in future");
|
||||||
let usage: Vec<String> = parsed.self_usage().lines().map(|s| s.to_string()).collect();
|
Ok(())
|
||||||
for line in usage.iter().filter(|line| {
|
|
||||||
if line.contains("--fan-mode") && !supported.fan_cpu_ctrl.stock_fan_modes {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if line.contains("--chg-limit") && !supported.charge_ctrl.charge_level_set {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}) {
|
|
||||||
println!("{}", line);
|
|
||||||
}
|
|
||||||
|
|
||||||
// command strings are in order of the struct
|
|
||||||
let commands: Vec<String> = CliCommand::usage().lines().map(|s| s.to_string()).collect();
|
|
||||||
println!("\nCommands available");
|
|
||||||
for line in commands.iter().filter(|line| {
|
|
||||||
if line.contains("profile")
|
|
||||||
&& !supported.fan_cpu_ctrl.stock_fan_modes
|
|
||||||
&& !supported.fan_cpu_ctrl.fan_curve_set
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if line.contains("led-mode") && !supported.keyboard_led.stock_led_modes.is_empty() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if line.contains("bios")
|
|
||||||
&& (!supported.rog_bios_ctrl.dedicated_gfx_toggle
|
|
||||||
|| !supported.rog_bios_ctrl.post_sound_toggle)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if line.contains("anime") && !supported.anime_ctrl.0 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}) {
|
|
||||||
println!("{}", line);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !supported.fan_cpu_ctrl.stock_fan_modes {
|
|
||||||
println!("Note: Fan mode control is not supported by this laptop");
|
|
||||||
}
|
|
||||||
if !supported.charge_ctrl.charge_level_set {
|
|
||||||
println!("Note: Charge control is not supported by this laptop");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_gfx(
|
fn handle_anime(
|
||||||
dbus: &RogDbusClient,
|
dbus: &RogDbusClient,
|
||||||
supported: &RogBiosSupportedFunctions,
|
_supported: &AnimeSupportedFunctions,
|
||||||
command: GraphicsCommand,
|
cmd: &AnimeCommand,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
if command.mode.is_none() && !command.get && !command.pow && !command.force || command.help {
|
if (cmd.command.is_none() && cmd.boot.is_none() && cmd.turn.is_none()) || cmd.help {
|
||||||
println!("{}", command.self_usage());
|
println!("Missing arg or command\n\n{}", cmd.self_usage());
|
||||||
}
|
if let Some(lst) = cmd.self_command_list() {
|
||||||
|
println!("\n{}", lst);
|
||||||
if let Some(mode) = command.mode {
|
|
||||||
if supported.dedicated_gfx_toggle && dbus.proxies().rog_bios().get_dedicated_gfx()? == 1 {
|
|
||||||
println!("You can not change modes until you turn dedicated/G-Sync off and reboot");
|
|
||||||
std::process::exit(-1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("If anything fails check `journalctl -b -u asusd`\n");
|
|
||||||
|
|
||||||
dbus.proxies().gfx().gfx_write_mode(&mode).map_err(|err|{
|
|
||||||
println!("Graphics mode change error. You may be in an invalid state.");
|
|
||||||
println!("Check mode with `asusctl graphics -g` and switch to opposite\nmode to correct it, e.g: if integrated, switch to hybrid, or if nvidia, switch to integrated.\n");
|
|
||||||
err
|
|
||||||
})?;
|
|
||||||
let res = dbus.gfx_wait_changed()?;
|
|
||||||
println!(
|
|
||||||
"Graphics mode changed to {}. User action required is: {}",
|
|
||||||
<&str>::from(mode),
|
|
||||||
<&str>::from(&res)
|
|
||||||
);
|
|
||||||
std::process::exit(0)
|
|
||||||
}
|
}
|
||||||
if command.get {
|
if let Some(anime_turn) = cmd.turn {
|
||||||
let res = dbus.proxies().gfx().gfx_get_mode()?;
|
dbus.proxies().anime().set_led_power(anime_turn.into())?
|
||||||
println!("Current graphics mode: {}", <&str>::from(res));
|
|
||||||
}
|
}
|
||||||
if command.pow {
|
if let Some(anime_boot) = cmd.boot {
|
||||||
let res = dbus.proxies().gfx().gfx_get_pwr()?;
|
dbus.proxies()
|
||||||
match res {
|
.anime()
|
||||||
rog_types::gfx_vendors::GfxPower::Active => {
|
.set_system_animations(anime_boot.into())?
|
||||||
println!("Current power status: {}", Red.paint(<&str>::from(&res)))
|
}
|
||||||
|
if let Some(action) = cmd.command.as_ref() {
|
||||||
|
match action {
|
||||||
|
AnimeActions::Leds(anime_leds) => {
|
||||||
|
let data = AnimeDataBuffer::from_vec(
|
||||||
|
[anime_leds.led_brightness(); ANIME_DATA_LEN].to_vec(),
|
||||||
|
);
|
||||||
|
dbus.proxies().anime().write(data)?;
|
||||||
|
}
|
||||||
|
AnimeActions::Image(image) => {
|
||||||
|
if image.help_requested() || image.path.is_empty() {
|
||||||
|
println!("Missing arg or command\n\n{}", image.self_usage());
|
||||||
|
if let Some(lst) = image.self_command_list() {
|
||||||
|
println!("\n{}", lst);
|
||||||
|
}
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let matrix = AnimeImage::from_png(
|
||||||
|
Path::new(&image.path),
|
||||||
|
image.scale,
|
||||||
|
image.angle,
|
||||||
|
Vec2::new(image.x_pos, image.y_pos),
|
||||||
|
image.bright,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
dbus.proxies()
|
||||||
|
.anime()
|
||||||
|
.write(<AnimeDataBuffer>::from(&matrix))?;
|
||||||
}
|
}
|
||||||
_ => println!("Current power status: {}", Green.paint(<&str>::from(&res))),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -347,23 +271,21 @@ fn handle_led_mode(
|
|||||||
println!("{}\n", mode.self_usage());
|
println!("{}\n", mode.self_usage());
|
||||||
println!("Commands available");
|
println!("Commands available");
|
||||||
|
|
||||||
let commands: Vec<String> = LedModeCommand::command_list()
|
if let Some(cmdlist) = LedModeCommand::command_list() {
|
||||||
.unwrap()
|
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_string()).collect();
|
||||||
.lines()
|
for command in commands.iter().filter(|command| {
|
||||||
.map(|s| s.to_string())
|
for mode in &supported.stock_led_modes {
|
||||||
.collect();
|
if command.contains(&<&str>::from(mode).to_lowercase()) {
|
||||||
for command in commands.iter().filter(|command| {
|
return true;
|
||||||
for mode in &supported.stock_led_modes {
|
}
|
||||||
if command.contains(<&str>::from(mode)) {
|
}
|
||||||
|
if supported.multizone_led_mode {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
false
|
||||||
|
}) {
|
||||||
|
println!("{}", command);
|
||||||
}
|
}
|
||||||
if supported.multizone_led_mode {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}) {
|
|
||||||
println!("{}", command);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("\nHelp can also be requested on modes, e.g: static --help");
|
println!("\nHelp can also be requested on modes, e.g: static --help");
|
||||||
@@ -410,109 +332,105 @@ fn handle_led_mode(
|
|||||||
|
|
||||||
fn handle_profile(
|
fn handle_profile(
|
||||||
dbus: &RogDbusClient,
|
dbus: &RogDbusClient,
|
||||||
supported: &FanCpuSupportedFunctions,
|
supported: &PlatformProfileFunctions,
|
||||||
cmd: &ProfileCommand,
|
cmd: &ProfileCommand,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
if !cmd.next
|
if !supported.fan_curves {
|
||||||
&& !cmd.create // TODO
|
println!("Profiles not supported by either this kernel or by the laptop.");
|
||||||
&& !cmd.list
|
return Err(ProfileError::NotSupported.into());
|
||||||
&& cmd.profile.is_none()
|
}
|
||||||
&& !cmd.active_name
|
|
||||||
&& !cmd.active_data
|
println!("Warning: Profiles now depend on power-profiles-daemon v0.9+ which may not be in your install");
|
||||||
&& !cmd.profiles_data
|
println!(" If you have unexpected behaviour or have only two profiles in your desktop control");
|
||||||
&& cmd.remove.is_none()
|
println!(" you need to manually install from https://gitlab.freedesktop.org/hadess/power-profiles-daemon");
|
||||||
&& cmd.curve.is_none() // TODO
|
println!(" Fedora and Arch distros will get the update soon...\n");
|
||||||
&& cmd.fan_preset.is_none() // TODO
|
if !cmd.next && !cmd.list && cmd.profile_set.is_none() && !cmd.profile_get {
|
||||||
&& cmd.turbo.is_none() // TODO
|
|
||||||
&& cmd.max_percentage.is_none() // TODO
|
|
||||||
&& cmd.min_percentage.is_none()
|
|
||||||
// TODO
|
|
||||||
{
|
|
||||||
if !cmd.help {
|
if !cmd.help {
|
||||||
println!("Missing arg or command\n");
|
println!("Missing arg or command\n");
|
||||||
}
|
}
|
||||||
let usage: Vec<String> = ProfileCommand::usage()
|
println!("{}", ProfileCommand::usage());
|
||||||
.lines()
|
|
||||||
.map(|s| s.to_string())
|
|
||||||
.collect();
|
|
||||||
for line in usage
|
|
||||||
.iter()
|
|
||||||
.filter(|line| !line.contains("--curve") || supported.fan_curve_set)
|
|
||||||
{
|
|
||||||
println!("{}", line);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(lst) = cmd.self_command_list() {
|
if let Some(lst) = cmd.self_command_list() {
|
||||||
println!("\n{}", lst);
|
println!("\n{}", lst);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Note: turbo, frequency, fan preset and fan curve options will apply to");
|
|
||||||
println!(" to the currently active profile unless a profile name is specified");
|
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.next {
|
if cmd.next {
|
||||||
dbus.proxies().profile().next_fan()?;
|
dbus.proxies().profile().next_profile()?;
|
||||||
}
|
} else if let Some(profile) = cmd.profile_set {
|
||||||
if let Some(profile) = &cmd.remove {
|
dbus.proxies().profile().set_active_profile(profile)?;
|
||||||
dbus.proxies().profile().remove(profile)?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.list {
|
if cmd.list {
|
||||||
let profile_names = dbus.proxies().profile().profile_names()?;
|
let res = dbus.proxies().profile().profiles()?;
|
||||||
println!("Available profiles are {:?}", profile_names);
|
res.iter().for_each(|p| println!("{:?}", p));
|
||||||
}
|
}
|
||||||
if cmd.active_name {
|
|
||||||
println!(
|
if cmd.profile_get {
|
||||||
"Active profile: {:?}",
|
let res = dbus.proxies().profile().active_profile()?;
|
||||||
dbus.proxies().profile().active_name()?
|
println!("Active profile is {:?}", res);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if cmd.active_data {
|
|
||||||
println!("Active profile:");
|
Ok(())
|
||||||
println!("{:?}", dbus.proxies().profile().active_data()?);
|
}
|
||||||
|
|
||||||
|
fn handle_fan_curve(
|
||||||
|
dbus: &RogDbusClient,
|
||||||
|
supported: &PlatformProfileFunctions,
|
||||||
|
cmd: &FanCurveCommand,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
if !supported.fan_curves {
|
||||||
|
println!("Fan-curves not supported by either this kernel or by the laptop.");
|
||||||
|
return Err(ProfileError::NotSupported.into());
|
||||||
}
|
}
|
||||||
if cmd.profiles_data {
|
|
||||||
println!("Profiles:");
|
if !cmd.get_enabled && !cmd.default && cmd.mod_profile.is_none() {
|
||||||
for s in dbus.proxies().profile().all_profile_data()? {
|
if !cmd.help {
|
||||||
println!("{:?}", s);
|
println!("Missing arg or command\n");
|
||||||
}
|
}
|
||||||
|
println!("{}", FanCurveCommand::usage());
|
||||||
|
|
||||||
|
if let Some(lst) = cmd.self_command_list() {
|
||||||
|
println!("\n{}", lst);
|
||||||
|
}
|
||||||
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut set_profile = false;
|
if (cmd.enabled.is_some() || cmd.fan.is_some() || cmd.data.is_some())
|
||||||
let mut profile;
|
&& cmd.mod_profile.is_none()
|
||||||
if cmd.create {
|
{
|
||||||
profile = Profile::default();
|
println!("--enabled, --fan, and --data options require --mod-profile");
|
||||||
set_profile = true;
|
std::process::exit(666);
|
||||||
} else {
|
|
||||||
profile = dbus.proxies().profile().active_data()?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(turbo) = cmd.turbo {
|
if cmd.get_enabled {
|
||||||
set_profile = true;
|
let res = dbus.proxies().profile().enabled_fan_profiles()?;
|
||||||
profile.turbo = turbo;
|
println!("{:?}", res);
|
||||||
}
|
}
|
||||||
if let Some(min) = cmd.min_percentage {
|
|
||||||
set_profile = true;
|
if cmd.default {
|
||||||
profile.min_percentage = min;
|
dbus.proxies().profile().set_active_curve_to_defaults()?;
|
||||||
}
|
}
|
||||||
if let Some(max) = cmd.max_percentage {
|
|
||||||
set_profile = true;
|
if let Some(profile) = cmd.mod_profile {
|
||||||
profile.max_percentage = max;
|
if cmd.enabled.is_none() && cmd.data.is_none() {
|
||||||
}
|
let data = dbus.proxies().profile().fan_curve_data(profile)?;
|
||||||
if let Some(preset) = cmd.fan_preset {
|
let data = toml::to_string(&data)?;
|
||||||
set_profile = true;
|
println!("\nFan curves for {:?}\n\n{}", profile, data);
|
||||||
profile.fan_preset = preset;
|
}
|
||||||
}
|
|
||||||
if let Some(ref curve) = cmd.curve {
|
if let Some(enabled) = cmd.enabled {
|
||||||
set_profile = true;
|
dbus.proxies()
|
||||||
profile.fan_curve = curve.as_config_string();
|
.profile()
|
||||||
}
|
.set_fan_curve_enabled(profile, enabled)?;
|
||||||
if let Some(ref name) = cmd.profile {
|
}
|
||||||
set_profile = true;
|
|
||||||
profile.name = name.clone();
|
if let Some(mut curve) = cmd.data.clone() {
|
||||||
}
|
let fan = cmd.fan.unwrap_or_default();
|
||||||
if set_profile {
|
curve.set_fan(fan);
|
||||||
dbus.proxies().profile().new_or_modify(&profile)?;
|
dbus.proxies().profile().set_fan_curve(curve, profile)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -571,3 +489,27 @@ fn handle_bios_option(
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_systemd_unit_active(name: &str) -> bool {
|
||||||
|
if let Ok(out) = Command::new("systemctl")
|
||||||
|
.arg("is-active")
|
||||||
|
.arg(name)
|
||||||
|
.output()
|
||||||
|
{
|
||||||
|
let buf = String::from_utf8_lossy(&out.stdout);
|
||||||
|
return !buf.contains("inactive") && !buf.contains("failed");
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_systemd_unit_enabled(name: &str) -> bool {
|
||||||
|
if let Ok(out) = Command::new("systemctl")
|
||||||
|
.arg("is-enabled")
|
||||||
|
.arg(name)
|
||||||
|
.output()
|
||||||
|
{
|
||||||
|
let buf = String::from_utf8_lossy(&out.stdout);
|
||||||
|
return buf.contains("enabled");
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,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.1.1"
|
version = "1.2.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,7 +21,7 @@ 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 = "3.0.1"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use rog_anime::{ActionData, AnimTime, AnimeAction, Sequences, Vec2};
|
use rog_anime::error::AnimeError;
|
||||||
|
use rog_anime::{ActionData, ActionLoader, AnimTime, Fade, Sequences, Vec2};
|
||||||
use rog_dbus::RogDbusClient;
|
use rog_dbus::RogDbusClient;
|
||||||
//use crate::dbus::DbusEvents;
|
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{
|
use std::{
|
||||||
@@ -17,6 +17,44 @@ use zvariant_derive::Type;
|
|||||||
|
|
||||||
use crate::{error::Error, user_config::UserAnimeConfig};
|
use crate::{error::Error, user_config::UserAnimeConfig};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, Type)]
|
||||||
|
pub struct Timer {
|
||||||
|
type_of: TimeType,
|
||||||
|
/// If time type is Timer then this is milliseonds, otherwise it is animation loop count
|
||||||
|
count: u64,
|
||||||
|
/// Used only for `TimeType::Timer`, milliseonds to fade the image in for
|
||||||
|
fade_in: Option<u64>,
|
||||||
|
/// Used only for `TimeType::Timer`, milliseonds to fade the image out for
|
||||||
|
fade_out: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Timer> for AnimTime {
|
||||||
|
fn from(time: Timer) -> Self {
|
||||||
|
match time.type_of {
|
||||||
|
TimeType::Timer => {
|
||||||
|
if time.fade_in.is_some() || time.fade_out.is_some() {
|
||||||
|
let fade_in = time
|
||||||
|
.fade_in
|
||||||
|
.map_or(Duration::from_secs(0), Duration::from_millis);
|
||||||
|
let fade_out = time
|
||||||
|
.fade_out
|
||||||
|
.map_or(Duration::from_secs(0), Duration::from_millis);
|
||||||
|
let show_for = if time.count != 0 {
|
||||||
|
Some(Duration::from_millis(time.count))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
AnimTime::Fade(Fade::new(fade_in, show_for, fade_out))
|
||||||
|
} else {
|
||||||
|
AnimTime::Time(Duration::from_millis(time.count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TimeType::Count => AnimTime::Count(time.count as u32),
|
||||||
|
TimeType::Infinite => AnimTime::Infinite,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, Type)]
|
#[derive(Debug, Clone, Deserialize, Serialize, Type)]
|
||||||
pub enum TimeType {
|
pub enum TimeType {
|
||||||
Timer,
|
Timer,
|
||||||
@@ -29,14 +67,14 @@ pub enum TimeType {
|
|||||||
pub struct CtrlAnimeInner<'a> {
|
pub struct CtrlAnimeInner<'a> {
|
||||||
sequences: Sequences,
|
sequences: Sequences,
|
||||||
client: RogDbusClient<'a>,
|
client: RogDbusClient<'a>,
|
||||||
do_early_return: &'a 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: RogDbusClient<'static>,
|
||||||
do_early_return: &'static AtomicBool,
|
do_early_return: Arc<AtomicBool>,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
sequences,
|
sequences,
|
||||||
@@ -45,7 +83,7 @@ impl<'a> CtrlAnimeInner<'static> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
/// To be called on each main loop iteration to pump out commands to the anime
|
/// To be called on each main loop iteration to pump out commands to the anime
|
||||||
pub fn run(&self) -> Result<(), Error> {
|
pub fn run(&'a self) -> Result<(), Error> {
|
||||||
if self.do_early_return.load(Ordering::SeqCst) {
|
if self.do_early_return.load(Ordering::SeqCst) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -53,39 +91,20 @@ 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) => {
|
||||||
let mut count = 0;
|
rog_anime::run_animation(frames, self.do_early_return.clone(), &|output| {
|
||||||
let start = Instant::now();
|
self.client
|
||||||
'animation: loop {
|
.proxies()
|
||||||
for frame in frames.frames() {
|
.anime()
|
||||||
if self.do_early_return.load(Ordering::SeqCst) {
|
.write(output)
|
||||||
return Ok(());
|
.map_err(|e| AnimeError::Dbus(format!("{}", e)))
|
||||||
}
|
})?;
|
||||||
self.client
|
|
||||||
.proxies()
|
|
||||||
.anime()
|
|
||||||
.write(frame.frame().clone())
|
|
||||||
.unwrap();
|
|
||||||
if let AnimTime::Time(time) = frames.duration() {
|
|
||||||
if Instant::now().duration_since(start) > time {
|
|
||||||
break 'animation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sleep(frame.delay());
|
|
||||||
}
|
|
||||||
if let AnimTime::Cycles(times) = frames.duration() {
|
|
||||||
count += 1;
|
|
||||||
if count >= times {
|
|
||||||
break 'animation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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();
|
||||||
@@ -115,7 +134,7 @@ pub struct CtrlAnime<'a> {
|
|||||||
client: RogDbusClient<'a>,
|
client: RogDbusClient<'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: &'a AtomicBool,
|
inner_early_return: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CtrlAnime<'static> {
|
impl<'a> CtrlAnime<'static> {
|
||||||
@@ -123,7 +142,7 @@ impl<'a> CtrlAnime<'static> {
|
|||||||
config: Arc<Mutex<UserAnimeConfig>>,
|
config: Arc<Mutex<UserAnimeConfig>>,
|
||||||
inner: Arc<Mutex<CtrlAnimeInner<'static>>>,
|
inner: Arc<Mutex<CtrlAnimeInner<'static>>>,
|
||||||
client: RogDbusClient<'static>,
|
client: RogDbusClient<'static>,
|
||||||
inner_early_return: &'static AtomicBool,
|
inner_early_return: Arc<AtomicBool>,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
Ok(CtrlAnime {
|
Ok(CtrlAnime {
|
||||||
config,
|
config,
|
||||||
@@ -159,18 +178,13 @@ impl CtrlAnime<'static> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
index: u32,
|
index: u32,
|
||||||
file: String,
|
file: String,
|
||||||
time: TimeType,
|
time: Timer,
|
||||||
count: u32,
|
|
||||||
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 time: AnimTime = match time {
|
let time: AnimTime = time.into();
|
||||||
TimeType::Timer => AnimTime::Time(Duration::from_millis(count as u64)),
|
|
||||||
TimeType::Count => AnimTime::Cycles(count),
|
|
||||||
TimeType::Infinite => AnimTime::Infinite,
|
|
||||||
};
|
|
||||||
let file = Path::new(&file);
|
let file = Path::new(&file);
|
||||||
let action = AnimeAction::AsusAnimation {
|
let action = ActionLoader::AsusAnimation {
|
||||||
file: file.into(),
|
file: file.into(),
|
||||||
brightness,
|
brightness,
|
||||||
time,
|
time,
|
||||||
@@ -205,19 +219,14 @@ impl CtrlAnime<'static> {
|
|||||||
scale: f32,
|
scale: f32,
|
||||||
angle: f32,
|
angle: f32,
|
||||||
xy: (f32, f32),
|
xy: (f32, f32),
|
||||||
time: TimeType,
|
time: Timer,
|
||||||
count: u32,
|
|
||||||
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 time: AnimTime = match time {
|
let time: AnimTime = time.into();
|
||||||
TimeType::Timer => AnimTime::Time(Duration::from_millis(count as u64)),
|
|
||||||
TimeType::Count => AnimTime::Cycles(count),
|
|
||||||
TimeType::Infinite => AnimTime::Infinite,
|
|
||||||
};
|
|
||||||
let file = Path::new(&file);
|
let file = Path::new(&file);
|
||||||
let translation = Vec2::new(xy.0, xy.1);
|
let translation = Vec2::new(xy.0, xy.1);
|
||||||
let action = AnimeAction::ImageAnimation {
|
let action = ActionLoader::ImageAnimation {
|
||||||
file: file.into(),
|
file: file.into(),
|
||||||
scale,
|
scale,
|
||||||
angle,
|
angle,
|
||||||
@@ -248,6 +257,7 @@ impl CtrlAnime<'static> {
|
|||||||
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
|
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn insert_image(
|
pub fn insert_image(
|
||||||
&mut self,
|
&mut self,
|
||||||
index: u32,
|
index: u32,
|
||||||
@@ -255,16 +265,19 @@ impl CtrlAnime<'static> {
|
|||||||
scale: f32,
|
scale: f32,
|
||||||
angle: f32,
|
angle: f32,
|
||||||
xy: (f32, f32),
|
xy: (f32, f32),
|
||||||
|
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 action = AnimeAction::Image {
|
let time = time.into();
|
||||||
|
let action = ActionLoader::Image {
|
||||||
file: file.into(),
|
file: file.into(),
|
||||||
scale,
|
scale,
|
||||||
angle,
|
angle,
|
||||||
translation: Vec2::new(xy.0, xy.1),
|
translation: Vec2::new(xy.0, xy.1),
|
||||||
brightness,
|
brightness,
|
||||||
|
time,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Must make the inner run loop return early
|
// Must make the inner run loop return early
|
||||||
@@ -291,7 +304,7 @@ impl CtrlAnime<'static> {
|
|||||||
|
|
||||||
pub fn insert_pause(&mut self, index: u32, millis: u64) -> zbus::fdo::Result<String> {
|
pub fn insert_pause(&mut self, index: u32, millis: u64) -> zbus::fdo::Result<String> {
|
||||||
if let Ok(mut config) = self.config.try_lock() {
|
if let Ok(mut config) = self.config.try_lock() {
|
||||||
let action = AnimeAction::Pause(Duration::from_millis(millis));
|
let action = ActionLoader::Pause(Duration::from_millis(millis));
|
||||||
// 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);
|
||||||
|
|
||||||
@@ -340,13 +353,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().toggle_on(on)?;
|
self.client.proxies().anime().set_led_power(on)?;
|
||||||
// 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().toggle_on(on)?;
|
self.client.proxies().anime().set_led_power(on)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,16 +11,13 @@ use zbus::{fdo, Connection};
|
|||||||
|
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
|
|
||||||
/// The anime loop needs an atomic to make it exit early if required
|
|
||||||
static ANIME_INNER_EARLY_RETURN: AtomicBool = AtomicBool::new(false);
|
|
||||||
|
|
||||||
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, _) = RogDbusClient::new()?;
|
||||||
let supported = client.proxies().supported().get_supported_functions()?;
|
let supported = client.proxies().supported().get_supported_functions()?;
|
||||||
|
|
||||||
let mut config = UserConfig::new();
|
let mut config = UserConfig::new();
|
||||||
@@ -39,27 +36,23 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
// 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));
|
||||||
// Inner behind mutex required for thread safety
|
// Inner behind mutex required for thread safety
|
||||||
let inner = Arc::new(Mutex::new(CtrlAnimeInner::new(
|
let inner = Arc::new(Mutex::new(CtrlAnimeInner::new(
|
||||||
anime,
|
anime,
|
||||||
client,
|
client,
|
||||||
&ANIME_INNER_EARLY_RETURN,
|
early_return.clone(),
|
||||||
)?));
|
)?));
|
||||||
// Need new client object for dbus control part
|
// Need new client object for dbus control part
|
||||||
let (client, _) = RogDbusClient::new().unwrap();
|
let (client, _) = RogDbusClient::new()?;
|
||||||
let anime_control = CtrlAnime::new(
|
let anime_control = CtrlAnime::new(anime_config, inner.clone(), client, early_return)?;
|
||||||
anime_config,
|
|
||||||
inner.clone(),
|
|
||||||
client,
|
|
||||||
&ANIME_INNER_EARLY_RETURN,
|
|
||||||
)?;
|
|
||||||
anime_control.add_to_server(&mut server);
|
anime_control.add_to_server(&mut server);
|
||||||
// Thread using inner
|
// Thread using inner
|
||||||
let _anime_thread = thread::Builder::new()
|
let _anime_thread = thread::Builder::new()
|
||||||
.name("Anime User".into())
|
.name("Anime User".into())
|
||||||
.spawn(move || loop {
|
.spawn(move || loop {
|
||||||
if let Ok(inner) = inner.try_lock() {
|
if let Ok(inner) = inner.try_lock() {
|
||||||
inner.run().unwrap();
|
inner.run().ok();
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rog_anime::{AnimTime, AnimeAction, Sequences, Vec2};
|
use rog_anime::{ActionLoader, AnimTime, Fade, Sequences, Vec2};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
@@ -12,7 +12,7 @@ use crate::error::Error;
|
|||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct UserAnimeConfig {
|
pub struct UserAnimeConfig {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub anime: Vec<AnimeAction>,
|
pub anime: Vec<ActionLoader>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserAnimeConfig {
|
impl UserAnimeConfig {
|
||||||
@@ -95,34 +95,56 @@ impl Default for UserAnimeConfig {
|
|||||||
Self {
|
Self {
|
||||||
name: "default".to_string(),
|
name: "default".to_string(),
|
||||||
anime: vec![
|
anime: vec![
|
||||||
AnimeAction::AsusAnimation {
|
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 {
|
||||||
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,
|
||||||
time: AnimTime::Cycles(1),
|
time: AnimTime::Fade(Fade::new(
|
||||||
|
Duration::from_secs(6),
|
||||||
|
None,
|
||||||
|
Duration::from_secs(3),
|
||||||
|
)),
|
||||||
},
|
},
|
||||||
AnimeAction::ImageAnimation {
|
ActionLoader::ImageAnimation {
|
||||||
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
|
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
|
||||||
scale: 0.9,
|
scale: 0.9,
|
||||||
angle: 0.65,
|
angle: 0.65,
|
||||||
translation: Vec2::default(),
|
translation: Vec2::default(),
|
||||||
brightness: 0.5,
|
brightness: 0.5,
|
||||||
time: AnimTime::Time(Duration::from_secs(5)),
|
time: AnimTime::Fade(Fade::new(
|
||||||
|
Duration::from_secs(2),
|
||||||
|
Some(Duration::from_secs(2)),
|
||||||
|
Duration::from_secs(2),
|
||||||
|
)),
|
||||||
},
|
},
|
||||||
AnimeAction::Image {
|
ActionLoader::Image {
|
||||||
file: "/usr/share/asusd/anime/custom/rust.png".into(),
|
file: "/usr/share/asusd/anime/custom/rust.png".into(),
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
angle: 0.0,
|
angle: 0.0,
|
||||||
translation: Vec2::default(),
|
translation: Vec2::default(),
|
||||||
|
time: AnimTime::Fade(Fade::new(
|
||||||
|
Duration::from_secs(2),
|
||||||
|
Some(Duration::from_secs(1)),
|
||||||
|
Duration::from_secs(2),
|
||||||
|
)),
|
||||||
brightness: 0.6,
|
brightness: 0.6,
|
||||||
},
|
},
|
||||||
AnimeAction::Pause(Duration::from_secs(6)),
|
ActionLoader::Pause(Duration::from_secs(1)),
|
||||||
AnimeAction::ImageAnimation {
|
ActionLoader::ImageAnimation {
|
||||||
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
|
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
|
||||||
scale: 0.9,
|
scale: 0.9,
|
||||||
angle: 0.0,
|
angle: 0.0,
|
||||||
translation: Vec2::new(3.0, 2.0),
|
translation: Vec2::new(3.0, 2.0),
|
||||||
brightness: 0.5,
|
brightness: 0.5,
|
||||||
time: AnimTime::Cycles(2),
|
time: AnimTime::Count(2),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "daemon"
|
name = "daemon"
|
||||||
version = "3.6.2"
|
version = "4.0.5"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = ["Luke <luke@ljones.dev>"]
|
authors = ["Luke <luke@ljones.dev>"]
|
||||||
@@ -20,7 +20,7 @@ path = "src/daemon.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
rog_anime = { path = "../rog-anime" }
|
rog_anime = { path = "../rog-anime" }
|
||||||
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" }
|
||||||
rog_dbus = { path = "../rog-dbus" }
|
rog_dbus = { path = "../rog-dbus" }
|
||||||
rusb = "^0.8"
|
rusb = "^0.8"
|
||||||
@@ -39,8 +39,7 @@ logind-zbus = "^0.7.1"
|
|||||||
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,85 +1,23 @@
|
|||||||
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 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,
|
|
||||||
true,
|
|
||||||
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()
|
||||||
@@ -89,46 +27,30 @@ impl Config {
|
|||||||
.open(&CONFIG_PATH)
|
.open(&CONFIG_PATH)
|
||||||
.unwrap_or_else(|_| panic!("The directory /etc/asusd/ is missing")); // okay to cause panic here
|
.unwrap_or_else(|_| panic!("The directory /etc/asusd/ is missing")); // 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,24 +64,12 @@ impl Config {
|
|||||||
if l == 0 {
|
if l == 0 {
|
||||||
warn!("File is empty {}", CONFIG_PATH);
|
warn!("File is empty {}", CONFIG_PATH);
|
||||||
} else {
|
} else {
|
||||||
let 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));
|
||||||
*self = x;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_new() -> Result<Config, Box<dyn std::error::Error>> {
|
|
||||||
let mut file = OpenOptions::new()
|
|
||||||
.read(true)
|
|
||||||
.open(&CONFIG_PATH)
|
|
||||||
.unwrap_or_else(|err| panic!("Error reading {}: {}", CONFIG_PATH, err));
|
|
||||||
let mut buf = String::new();
|
|
||||||
file.read_to_string(&mut buf)?;
|
|
||||||
let x: Config = serde_json::from_str(&buf)?;
|
|
||||||
Ok(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(&self) {
|
pub fn write(&self) {
|
||||||
let mut file = File::create(CONFIG_PATH).expect("Couldn't overwrite config");
|
let mut file = File::create(CONFIG_PATH).expect("Couldn't overwrite config");
|
||||||
let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed");
|
let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed");
|
||||||
|
|||||||
@@ -1,160 +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
|
|
||||||
#[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: 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 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,6 +1,7 @@
|
|||||||
use crate::VERSION;
|
use crate::VERSION;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use rog_anime::{error::AnimeError, ActionData, AnimTime, AnimeAction, Vec2};
|
use rog_anime::Fade;
|
||||||
|
use rog_anime::{error::AnimeError, ActionData, ActionLoader, AnimTime, Vec2};
|
||||||
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};
|
||||||
@@ -11,10 +12,10 @@ pub static ANIME_CACHE_PATH: &str = "/etc/asusd/anime-cache.conf";
|
|||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct AnimeConfigV341 {
|
pub struct AnimeConfigV341 {
|
||||||
pub system: Option<AnimeAction>,
|
pub system: Option<ActionLoader>,
|
||||||
pub boot: Option<AnimeAction>,
|
pub boot: Option<ActionLoader>,
|
||||||
pub suspend: Option<AnimeAction>,
|
pub suspend: Option<ActionLoader>,
|
||||||
pub shutdown: Option<AnimeAction>,
|
pub shutdown: Option<ActionLoader>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnimeConfigV341 {
|
impl AnimeConfigV341 {
|
||||||
@@ -49,10 +50,10 @@ impl AnimeConfigV341 {
|
|||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct AnimeConfigV352 {
|
pub struct AnimeConfigV352 {
|
||||||
pub system: Vec<AnimeAction>,
|
pub system: Vec<ActionLoader>,
|
||||||
pub boot: Vec<AnimeAction>,
|
pub boot: Vec<ActionLoader>,
|
||||||
pub wake: Vec<AnimeAction>,
|
pub wake: Vec<ActionLoader>,
|
||||||
pub shutdown: Vec<AnimeAction>,
|
pub shutdown: Vec<ActionLoader>,
|
||||||
pub brightness: f32,
|
pub brightness: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,10 +111,10 @@ impl AnimeConfigCached {
|
|||||||
/// Config for base system actions for the anime display
|
/// Config for base system actions for the anime display
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct AnimeConfig {
|
pub struct AnimeConfig {
|
||||||
pub system: Vec<AnimeAction>,
|
pub system: Vec<ActionLoader>,
|
||||||
pub boot: Vec<AnimeAction>,
|
pub boot: Vec<ActionLoader>,
|
||||||
pub wake: Vec<AnimeAction>,
|
pub wake: Vec<ActionLoader>,
|
||||||
pub shutdown: Vec<AnimeAction>,
|
pub shutdown: Vec<ActionLoader>,
|
||||||
pub brightness: f32,
|
pub brightness: f32,
|
||||||
pub awake_enabled: bool,
|
pub awake_enabled: bool,
|
||||||
pub boot_anim_enabled: bool,
|
pub boot_anim_enabled: bool,
|
||||||
@@ -165,8 +166,17 @@ impl AnimeConfig {
|
|||||||
info!("Updated config version to: {}", VERSION);
|
info!("Updated config version to: {}", VERSION);
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
warn!("Could not deserialise {}", ANIME_CONFIG_PATH);
|
warn!(
|
||||||
panic!("Please remove {} then restart asusd", ANIME_CONFIG_PATH);
|
"Could not deserialise {}.\nWill rename to {}-old and recreate config",
|
||||||
|
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)
|
||||||
@@ -176,23 +186,31 @@ impl AnimeConfig {
|
|||||||
// create a default config here
|
// create a default config here
|
||||||
let config = AnimeConfig {
|
let config = AnimeConfig {
|
||||||
system: vec![],
|
system: vec![],
|
||||||
boot: vec![AnimeAction::ImageAnimation {
|
boot: vec![ActionLoader::ImageAnimation {
|
||||||
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
|
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
|
||||||
scale: 0.9,
|
scale: 0.9,
|
||||||
angle: 0.65,
|
angle: 0.65,
|
||||||
translation: Vec2::default(),
|
translation: Vec2::default(),
|
||||||
brightness: 1.0,
|
brightness: 1.0,
|
||||||
time: AnimTime::Time(Duration::from_secs(5)),
|
time: AnimTime::Fade(Fade::new(
|
||||||
|
Duration::from_secs(2),
|
||||||
|
Some(Duration::from_secs(2)),
|
||||||
|
Duration::from_secs(2),
|
||||||
|
)),
|
||||||
}],
|
}],
|
||||||
wake: vec![AnimeAction::ImageAnimation {
|
wake: vec![ActionLoader::ImageAnimation {
|
||||||
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
|
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
|
||||||
scale: 0.9,
|
scale: 0.9,
|
||||||
angle: 0.65,
|
angle: 0.65,
|
||||||
translation: Vec2::default(),
|
translation: Vec2::default(),
|
||||||
brightness: 1.0,
|
brightness: 1.0,
|
||||||
time: AnimTime::Time(Duration::from_secs(5)),
|
time: AnimTime::Fade(Fade::new(
|
||||||
|
Duration::from_secs(2),
|
||||||
|
Some(Duration::from_secs(2)),
|
||||||
|
Duration::from_secs(2),
|
||||||
|
)),
|
||||||
}],
|
}],
|
||||||
shutdown: vec![AnimeAction::ImageAnimation {
|
shutdown: vec![ActionLoader::ImageAnimation {
|
||||||
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
|
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
|
||||||
scale: 0.9,
|
scale: 0.9,
|
||||||
angle: 0.0,
|
angle: 0.0,
|
||||||
@@ -1,32 +1,33 @@
|
|||||||
|
pub mod config;
|
||||||
|
pub mod zbus;
|
||||||
|
|
||||||
|
use ::zbus::Connection;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use logind_zbus::ManagerProxy;
|
use logind_zbus::ManagerProxy;
|
||||||
use rog_anime::{
|
use rog_anime::{
|
||||||
|
error::AnimeError,
|
||||||
usb::{
|
usb::{
|
||||||
pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on, pkts_for_init, PROD_ID,
|
pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on, pkts_for_init, PROD_ID,
|
||||||
VENDOR_ID,
|
VENDOR_ID,
|
||||||
},
|
},
|
||||||
ActionData, AnimTime, AnimeDataBuffer, AnimePacketType, AnimePowerStates, ANIME_DATA_LEN,
|
ActionData, AnimeDataBuffer, AnimePacketType, ANIME_DATA_LEN,
|
||||||
};
|
};
|
||||||
use rog_types::supported::AnimeSupportedFunctions;
|
use rog_supported::AnimeSupportedFunctions;
|
||||||
use rusb::{Device, DeviceHandle};
|
use rusb::{Device, DeviceHandle};
|
||||||
use std::{
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
error::Error,
|
error::Error,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
thread::sleep,
|
thread::sleep,
|
||||||
time::Instant,
|
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
sync::atomic::{AtomicBool, Ordering},
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use zbus::{dbus_interface, Connection};
|
|
||||||
use zvariant::ObjectPath;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{error::RogError, GetSupported};
|
||||||
config_anime::{AnimeConfig, AnimeConfigCached},
|
|
||||||
error::RogError,
|
use self::config::{AnimeConfig, AnimeConfigCached};
|
||||||
GetSupported,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl GetSupported for CtrlAnime {
|
impl GetSupported for CtrlAnime {
|
||||||
type A = AnimeSupportedFunctions;
|
type A = AnimeSupportedFunctions;
|
||||||
@@ -37,7 +38,8 @@ impl GetSupported for CtrlAnime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct CtrlAnime {
|
pub struct CtrlAnime {
|
||||||
handle: DeviceHandle<rusb::GlobalContext>,
|
_node: String,
|
||||||
|
handle: RefCell<DeviceHandle<rusb::GlobalContext>>,
|
||||||
cache: AnimeConfigCached,
|
cache: AnimeConfigCached,
|
||||||
config: AnimeConfig,
|
config: AnimeConfig,
|
||||||
// set to force thread to exit
|
// set to force thread to exit
|
||||||
@@ -49,6 +51,55 @@ pub struct CtrlAnime {
|
|||||||
impl CtrlAnime {
|
impl CtrlAnime {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(config: AnimeConfig) -> Result<CtrlAnime, Box<dyn Error>> {
|
pub fn new(config: AnimeConfig) -> Result<CtrlAnime, Box<dyn Error>> {
|
||||||
|
let node = Self::find_node("193b")?;
|
||||||
|
let device = Self::get_dev_handle()?;
|
||||||
|
|
||||||
|
info!("Device has an AniMe Matrix display");
|
||||||
|
let mut cache = AnimeConfigCached::default();
|
||||||
|
cache.init_from_config(&config)?;
|
||||||
|
|
||||||
|
let ctrl = CtrlAnime {
|
||||||
|
_node: node,
|
||||||
|
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 find_node(id_product: &str) -> Result<String, RogError> {
|
||||||
|
let mut enumerator = udev::Enumerator::new().map_err(|err| {
|
||||||
|
warn!("{}", err);
|
||||||
|
RogError::Udev("enumerator failed".into(), err)
|
||||||
|
})?;
|
||||||
|
enumerator.match_subsystem("usb").map_err(|err| {
|
||||||
|
warn!("{}", err);
|
||||||
|
RogError::Udev("match_subsystem failed".into(), err)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
for device in enumerator.scan_devices().map_err(|err| {
|
||||||
|
warn!("{}", err);
|
||||||
|
RogError::Udev("scan_devices failed".into(), err)
|
||||||
|
})? {
|
||||||
|
if let Some(attr) = device.attribute_value("idProduct") {
|
||||||
|
if attr == id_product {
|
||||||
|
if let Some(dev_node) = device.devnode() {
|
||||||
|
info!("Using device at: {:?} for AniMe control", dev_node);
|
||||||
|
return Ok(dev_node.to_string_lossy().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(RogError::MissingFunction(
|
||||||
|
"ASUS AniMe device node not found".into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_dev_handle() -> Result<DeviceHandle<rusb::GlobalContext>, Box<dyn Error>> {
|
||||||
// We don't expect this ID to ever change
|
// We don't expect this ID to ever change
|
||||||
let device = CtrlAnime::get_device(0x0b05, 0x193b)?;
|
let device = CtrlAnime::get_device(0x0b05, 0x193b)?;
|
||||||
|
|
||||||
@@ -65,20 +116,7 @@ impl CtrlAnime {
|
|||||||
err
|
err
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
info!("Device has an AniMe Matrix display");
|
Ok(device)
|
||||||
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> {
|
fn get_device(vendor: u16, product: u16) -> Result<Device<rusb::GlobalContext>, rusb::Error> {
|
||||||
@@ -144,31 +182,25 @@ impl CtrlAnime {
|
|||||||
for action in actions.iter() {
|
for action in actions.iter() {
|
||||||
match action {
|
match action {
|
||||||
ActionData::Animation(frames) => {
|
ActionData::Animation(frames) => {
|
||||||
let mut count = 0;
|
if let Err(err) = rog_anime::run_animation(
|
||||||
let start = Instant::now();
|
frames,
|
||||||
'animation: loop {
|
thread_exit.clone(),
|
||||||
for frame in frames.frames() {
|
&|frame| {
|
||||||
if let Ok(lock) = inner.try_lock() {
|
inner
|
||||||
lock.write_data_buffer(frame.frame().clone());
|
.try_lock()
|
||||||
}
|
.map(|lock| lock.write_data_buffer(frame))
|
||||||
if let AnimTime::Time(time) = frames.duration() {
|
.map_err(|err| {
|
||||||
if Instant::now().duration_since(start) > time {
|
warn!("rog_anime::run_animation: {}", err);
|
||||||
break 'animation;
|
AnimeError::NoFrames
|
||||||
}
|
})
|
||||||
}
|
},
|
||||||
sleep(frame.delay());
|
) {
|
||||||
// Need to check for early exit condition here or it might run
|
warn!("rog_anime::run_animation: {}", err);
|
||||||
// until end of gif or time
|
break 'main;
|
||||||
if thread_exit.load(Ordering::SeqCst) {
|
};
|
||||||
break 'main;
|
|
||||||
}
|
if thread_exit.load(Ordering::SeqCst) {
|
||||||
}
|
break 'main;
|
||||||
if let AnimTime::Cycles(times) = frames.duration() {
|
|
||||||
count += 1;
|
|
||||||
if count >= times {
|
|
||||||
break 'animation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ActionData::Image(image) => {
|
ActionData::Image(image) => {
|
||||||
@@ -203,7 +235,14 @@ impl CtrlAnime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write_bytes(&self, message: &[u8]) {
|
fn write_bytes(&self, message: &[u8]) {
|
||||||
match self.handle.write_control(
|
// 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
|
0x21, // request_type
|
||||||
0x09, // request
|
0x09, // request
|
||||||
0x35e, // value
|
0x35e, // value
|
||||||
@@ -214,9 +253,24 @@ impl CtrlAnime {
|
|||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(err) => match err {
|
Err(err) => match err {
|
||||||
rusb::Error::Timeout => {}
|
rusb::Error::Timeout => {}
|
||||||
_ => error!("Failed to write to led interrupt: {}", err),
|
_ => {
|
||||||
|
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
|
/// Write only a data packet. This will modify the leds brightness using the
|
||||||
@@ -251,9 +305,11 @@ pub struct CtrlAnimeTask<'a> {
|
|||||||
|
|
||||||
impl<'a> CtrlAnimeTask<'a> {
|
impl<'a> CtrlAnimeTask<'a> {
|
||||||
pub fn new(inner: Arc<Mutex<CtrlAnime>>) -> Self {
|
pub fn new(inner: Arc<Mutex<CtrlAnime>>) -> Self {
|
||||||
let connection = Connection::new_system().unwrap();
|
let connection =
|
||||||
|
Connection::new_system().expect("CtrlAnimeTask could not create dbus connection");
|
||||||
|
|
||||||
let manager = ManagerProxy::new(&connection).unwrap();
|
let manager =
|
||||||
|
ManagerProxy::new(&connection).expect("CtrlAnimeTask could not create ManagerProxy");
|
||||||
|
|
||||||
let c1 = inner.clone();
|
let c1 = inner.clone();
|
||||||
// Run this action when the system starts shutting down
|
// Run this action when the system starts shutting down
|
||||||
@@ -344,124 +400,3 @@ impl crate::Reloadable for CtrlAnimeReloader {
|
|||||||
Ok(())
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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 boot_enabled(&self) -> bool {
|
|
||||||
if let Ok(ctrl) = self.0.try_lock() {
|
|
||||||
return ctrl.config.boot_anim_enabled;
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
#[dbus_interface(signal)]
|
|
||||||
fn notify_power_states(&self, data: &AnimePowerStates) -> zbus::Result<()>;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use log::warn;
|
||||||
|
use rog_anime::{
|
||||||
|
usb::{pkt_for_apply, pkt_for_set_boot, pkt_for_set_on},
|
||||||
|
AnimeDataBuffer, AnimePowerStates,
|
||||||
|
};
|
||||||
|
use zbus::dbus_interface;
|
||||||
|
use zvariant::ObjectPath;
|
||||||
|
|
||||||
|
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
|
||||||
|
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<()>;
|
||||||
|
}
|
||||||
@@ -90,7 +90,7 @@ impl AuraConfig {
|
|||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
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 AuraConfig::create_default(&mut file, &supported_led_modes);
|
return AuraConfig::create_default(&mut file, supported_led_modes);
|
||||||
} else {
|
} else {
|
||||||
if let Ok(data) = serde_json::from_str(&buf) {
|
if let Ok(data) = serde_json::from_str(&buf) {
|
||||||
return data;
|
return data;
|
||||||
@@ -105,11 +105,20 @@ impl AuraConfig {
|
|||||||
info!("Updated AuraConfig version");
|
info!("Updated AuraConfig version");
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
warn!("Could not deserialise {}", AURA_CONFIG_PATH);
|
warn!(
|
||||||
panic!("Please remove {} then restart asusd", AURA_CONFIG_PATH);
|
"Could not deserialise {}.\nWill rename to {}-old and recreate config",
|
||||||
|
AURA_CONFIG_PATH, AURA_CONFIG_PATH
|
||||||
|
);
|
||||||
|
let cfg_old = AURA_CONFIG_PATH.to_string() + "-old";
|
||||||
|
std::fs::rename(AURA_CONFIG_PATH, cfg_old).unwrap_or_else(|err| {
|
||||||
|
panic!(
|
||||||
|
"Could not rename. Please remove {} then restart service: Error {}",
|
||||||
|
AURA_CONFIG_PATH, err
|
||||||
|
)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AuraConfig::create_default(&mut file, &supported_led_modes)
|
AuraConfig::create_default(&mut file, supported_led_modes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_default(file: &mut File, support_data: &LaptopLedData) -> Self {
|
fn create_default(file: &mut File, support_data: &LaptopLedData) -> Self {
|
||||||
@@ -153,7 +162,7 @@ impl AuraConfig {
|
|||||||
.unwrap_or_else(|err| error!("Could not write config: {}", err));
|
.unwrap_or_else(|err| error!("Could not write config: {}", err));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Multipurpose, will accecpt AuraEffect with zones and put in the correct store
|
/// Multipurpose, will accept AuraEffect with zones and put in the correct store
|
||||||
pub fn set_builtin(&mut self, effect: AuraEffect) {
|
pub fn set_builtin(&mut self, effect: AuraEffect) {
|
||||||
match effect.zone() {
|
match effect.zone() {
|
||||||
AuraZone::None => {
|
AuraZone::None => {
|
||||||
@@ -2,11 +2,12 @@
|
|||||||
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,
|
||||||
};
|
};
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
use logind_zbus::ManagerProxy;
|
||||||
use rog_aura::{
|
use rog_aura::{
|
||||||
usb::{
|
usb::{
|
||||||
LED_APPLY, LED_AWAKE_OFF_SLEEP_OFF, LED_AWAKE_OFF_SLEEP_ON, LED_AWAKE_ON_SLEEP_OFF,
|
LED_APPLY, LED_AWAKE_OFF_SLEEP_OFF, LED_AWAKE_OFF_SLEEP_ON, LED_AWAKE_ON_SLEEP_OFF,
|
||||||
@@ -14,15 +15,18 @@ use rog_aura::{
|
|||||||
},
|
},
|
||||||
AuraEffect, LedBrightness, LED_MSG_LEN,
|
AuraEffect, LedBrightness, LED_MSG_LEN,
|
||||||
};
|
};
|
||||||
use rog_types::supported::LedSupportedFunctions;
|
use rog_supported::LedSupportedFunctions;
|
||||||
use std::fs::OpenOptions;
|
|
||||||
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 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;
|
||||||
|
|
||||||
@@ -50,32 +54,82 @@ pub struct CtrlKbdLed {
|
|||||||
pub config: AuraConfig,
|
pub config: AuraConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CtrlKbdLedTask(pub Arc<Mutex<CtrlKbdLed>>);
|
pub struct CtrlKbdLedTask<'a> {
|
||||||
|
inner: Arc<Mutex<CtrlKbdLed>>,
|
||||||
|
_c: Connection,
|
||||||
|
manager: ManagerProxy<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
impl crate::CtrlTask for CtrlKbdLedTask {
|
impl<'a> CtrlKbdLedTask<'a> {
|
||||||
fn do_task(&self) -> Result<(), RogError> {
|
pub fn new(inner: Arc<Mutex<CtrlKbdLed>>) -> Self {
|
||||||
if let Ok(mut lock) = self.0.try_lock() {
|
let connection =
|
||||||
let mut file = OpenOptions::new()
|
Connection::new_system().expect("CtrlKbdLedTask could not create dbus connection");
|
||||||
.read(true)
|
|
||||||
.open(&lock.bright_node)
|
let manager =
|
||||||
.map_err(|err| match err.kind() {
|
ManagerProxy::new(&connection).expect("CtrlKbdLedTask could not create ManagerProxy");
|
||||||
std::io::ErrorKind::NotFound => {
|
|
||||||
RogError::MissingLedBrightNode((&lock.bright_node).into(), err)
|
let c1 = inner.clone();
|
||||||
}
|
// Run this action when the system wakes up from sleep
|
||||||
_ => RogError::Path((&lock.bright_node).into(), err),
|
manager
|
||||||
})?;
|
.connect_prepare_for_sleep(move |sleep| {
|
||||||
let mut buf = [0u8; 1];
|
if !sleep {
|
||||||
file.read_exact(&mut buf)
|
let c1 = c1.clone();
|
||||||
.map_err(|err| RogError::Read("buffer".into(), err))?;
|
spawn(move || {
|
||||||
if let Some(num) = char::from(buf[0]).to_digit(10) {
|
// wait a fraction for things to wake up properly
|
||||||
if lock.config.brightness != num.into() {
|
//std::thread::sleep(Duration::from_millis(100));
|
||||||
lock.config.read();
|
loop {
|
||||||
lock.config.brightness = num.into();
|
if let Ok(ref mut lock) = c1.try_lock() {
|
||||||
lock.config.write();
|
lock.set_brightness(lock.config.brightness).ok();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return Ok(());
|
Ok(())
|
||||||
|
})
|
||||||
|
.map_err(|err| {
|
||||||
|
warn!("CtrlAnimeTask: new() {}", err);
|
||||||
|
err
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
inner,
|
||||||
|
_c: connection,
|
||||||
|
manager,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.open(&lock.bright_node)
|
||||||
|
.map_err(|err| match err.kind() {
|
||||||
|
std::io::ErrorKind::NotFound => {
|
||||||
|
RogError::MissingLedBrightNode((&lock.bright_node).into(), err)
|
||||||
|
}
|
||||||
|
_ => RogError::Path((&lock.bright_node).into(), err),
|
||||||
|
})?;
|
||||||
|
let mut buf = [0u8; 1];
|
||||||
|
file.read_exact(&mut buf)
|
||||||
|
.map_err(|err| RogError::Read("buffer".into(), err))?;
|
||||||
|
if let Some(num) = char::from(buf[0]).to_digit(10) {
|
||||||
|
if lock.config.brightness != num.into() {
|
||||||
|
lock.config.read();
|
||||||
|
lock.config.brightness = num.into();
|
||||||
|
lock.config.write();
|
||||||
}
|
}
|
||||||
return Err(RogError::ParseLed);
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(RogError::ParseLed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CtrlTask for CtrlKbdLedTask<'a> {
|
||||||
|
fn do_task(&self) -> Result<(), RogError> {
|
||||||
|
self.manager.next_signal()?;
|
||||||
|
if let Ok(ref mut lock) = self.inner.try_lock() {
|
||||||
|
return Self::update_config(lock);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -187,6 +241,28 @@ impl CtrlKbdLed {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn next_brightness(&mut self) -> Result<(), RogError> {
|
||||||
|
let mut bright = (self.config.brightness as u32) + 1;
|
||||||
|
if bright > 3 {
|
||||||
|
bright = 0;
|
||||||
|
}
|
||||||
|
self.config.brightness = <LedBrightness>::from(bright);
|
||||||
|
self.config.write();
|
||||||
|
self.set_brightness(self.config.brightness)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prev_brightness(&mut self) -> Result<(), RogError> {
|
||||||
|
let mut bright = self.config.brightness as u32;
|
||||||
|
if bright == 0 {
|
||||||
|
bright = 3;
|
||||||
|
} else {
|
||||||
|
bright -= 1;
|
||||||
|
}
|
||||||
|
self.config.brightness = <LedBrightness>::from(bright);
|
||||||
|
self.config.write();
|
||||||
|
self.set_brightness(self.config.brightness)
|
||||||
|
}
|
||||||
|
|
||||||
/// Set if awake/on LED active, and/or sleep animation active
|
/// Set if awake/on LED active, and/or sleep animation active
|
||||||
pub(super) fn set_states_enabled(&self, awake: bool, sleep: bool) -> Result<(), RogError> {
|
pub(super) fn set_states_enabled(&self, awake: bool, sleep: bool) -> Result<(), RogError> {
|
||||||
let bytes = if awake && sleep {
|
let bytes = if awake && sleep {
|
||||||
@@ -319,7 +395,7 @@ impl CtrlKbdLed {
|
|||||||
|
|
||||||
self.config.read();
|
self.config.read();
|
||||||
if let Some(data) = self.config.builtins.get(&next) {
|
if let Some(data) = self.config.builtins.get(&next) {
|
||||||
self.write_mode(&data)?;
|
self.write_mode(data)?;
|
||||||
self.config.current_mode = next;
|
self.config.current_mode = next;
|
||||||
}
|
}
|
||||||
self.config.write();
|
self.config.write();
|
||||||
@@ -330,7 +406,7 @@ impl CtrlKbdLed {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_mode(&self, mode: &AuraEffect) -> Result<(), RogError> {
|
fn write_mode(&self, mode: &AuraEffect) -> Result<(), RogError> {
|
||||||
if !self.supported_modes.standard.contains(&mode.mode()) {
|
if !self.supported_modes.standard.contains(mode.mode()) {
|
||||||
return Err(RogError::NotSupported);
|
return Err(RogError::NotSupported);
|
||||||
}
|
}
|
||||||
let bytes: [u8; LED_MSG_LEN] = mode.into();
|
let bytes: [u8; LED_MSG_LEN] = mode.into();
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
|
pub mod config;
|
||||||
pub mod controller;
|
pub mod controller;
|
||||||
pub mod zbus;
|
pub mod zbus;
|
||||||
@@ -105,6 +105,20 @@ impl CtrlKbdLedZbus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn next_led_brightness(&self) {
|
||||||
|
if let Ok(mut ctrl) = self.0.try_lock() {
|
||||||
|
ctrl.next_brightness()
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)]
|
#[dbus_interface(property)]
|
||||||
fn awake_enabled(&self) -> bool {
|
fn awake_enabled(&self) -> bool {
|
||||||
if let Ok(ctrl) = self.0.try_lock() {
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::{config::Config, error::RogError, GetSupported};
|
use crate::{config::Config, error::RogError, GetSupported};
|
||||||
//use crate::dbus::DbusEvents;
|
//use crate::dbus::DbusEvents;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use rog_types::supported::ChargeSupportedFunctions;
|
use rog_supported::ChargeSupportedFunctions;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@@ -10,7 +10,9 @@ use std::sync::Mutex;
|
|||||||
use zbus::dbus_interface;
|
use zbus::dbus_interface;
|
||||||
use zvariant::ObjectPath;
|
use zvariant::ObjectPath;
|
||||||
|
|
||||||
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,7 +30,10 @@ 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) {
|
pub fn set_limit(&mut self, limit: u8) -> Result<(), RogError> {
|
||||||
|
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| {
|
.map_err(|err| {
|
||||||
@@ -43,6 +48,7 @@ impl CtrlCharge {
|
|||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn limit(&self) -> i8 {
|
pub fn limit(&self) -> i8 {
|
||||||
@@ -88,8 +94,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"
|
||||||
@@ -100,18 +110,17 @@ impl CtrlCharge {
|
|||||||
|
|
||||||
pub(super) fn set(&self, limit: u8, config: &mut Config) -> Result<(), RogError> {
|
pub(super) fn set(&self, 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();
|
||||||
|
|||||||
@@ -1,642 +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::{iter::FromIterator, thread::JoinHandle};
|
|
||||||
use std::{process::Command, thread::sleep, time::Duration};
|
|
||||||
use std::{str::FromStr, sync::mpsc};
|
|
||||||
use std::{sync::Arc, sync::Mutex};
|
|
||||||
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()?;
|
|
||||||
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());
|
|
||||||
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 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());
|
|
||||||
Result::from_iter(unbinds.chain(removes))
|
|
||||||
.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());
|
|
||||||
Result::from_iter(unbinds)
|
|
||||||
.map_err(|err| RogError::Command("device unbind error".into(), err))
|
|
||||||
}
|
|
||||||
|
|
||||||
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 <= 5 {
|
|
||||||
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(500));
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
Err(GfxError::DisplayManagerTimeout(state.into()).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Determine if we need to logout/thread. Integrated<->Vfio mode does not
|
|
||||||
/// require logout.
|
|
||||||
fn logout_required(&self, vendor: GfxVendors) -> GfxRequiredUserAction {
|
|
||||||
if let Ok(config) = self.config.lock() {
|
|
||||||
let current = config.gfx_mode;
|
|
||||||
if matches!(
|
|
||||||
current,
|
|
||||||
GfxVendors::Integrated | GfxVendors::Vfio | GfxVendors::Compute
|
|
||||||
) && matches!(
|
|
||||||
vendor,
|
|
||||||
GfxVendors::Integrated | GfxVendors::Vfio | GfxVendors::Compute
|
|
||||||
) {
|
|
||||||
return GfxRequiredUserAction::None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GfxRequiredUserAction::Logout
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write the config changes and add/remove drivers and devices depending
|
|
||||||
/// on selected mode:
|
|
||||||
///
|
|
||||||
/// Tasks:
|
|
||||||
/// - write xorg config
|
|
||||||
/// - write modprobe config
|
|
||||||
/// - rescan for devices
|
|
||||||
/// + add drivers
|
|
||||||
/// + or remove drivers and devices
|
|
||||||
///
|
|
||||||
/// The daemon needs direct access to this function when it detects that the
|
|
||||||
pub fn do_vendor_tasks(
|
|
||||||
vendor: GfxVendors,
|
|
||||||
vfio_enable: bool,
|
|
||||||
devices: &[GraphicsDevice],
|
|
||||||
bus: &PciBus,
|
|
||||||
) -> Result<(), RogError> {
|
|
||||||
// Rescan before doing remove or add drivers
|
|
||||||
bus.rescan()?;
|
|
||||||
//
|
|
||||||
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 fire_starter(
|
|
||||||
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 vfio_enable = 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;
|
|
||||||
//
|
|
||||||
config.gfx_vfio_enable
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
Self::do_vendor_tasks(vendor, vfio_enable, &devices, &bus)?;
|
|
||||||
Self::do_display_manager_action("restart")?;
|
|
||||||
// Save selected mode in case of reboot
|
|
||||||
Self::save_gfx_mode(vendor, 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_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_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 killer = self.thread_kill.clone();
|
|
||||||
|
|
||||||
let _join: JoinHandle<()> = std::thread::spawn(move || {
|
|
||||||
Self::fire_starter(vendor, devices, bus, rx, config)
|
|
||||||
.map_err(|err| {
|
|
||||||
error!("GFX: {}", err);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
// clear the tx/rx when done
|
|
||||||
if let Ok(mut lock) = killer.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_config(
|
|
||||||
&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_thread();
|
|
||||||
// determine which method we need here
|
|
||||||
let action_required = self.logout_required(vendor);
|
|
||||||
if matches!(action_required, GfxRequiredUserAction::Logout) {
|
|
||||||
// Yeah need the thread to check if all users are logged out
|
|
||||||
info!("GFX: mode change requires a logout to complete");
|
|
||||||
self.setup_thread(vendor);
|
|
||||||
} else {
|
|
||||||
// Okay cool, we can switch on/off vfio
|
|
||||||
info!("GFX: mode change does not require logout");
|
|
||||||
let devices = self.nvidia.clone();
|
|
||||||
let bus = self.bus.clone();
|
|
||||||
Self::do_vendor_tasks(vendor, vfio_enable, &devices, &bus)?;
|
|
||||||
info!("GFX: Graphics mode changed to {}", <&str>::from(vendor));
|
|
||||||
if matches!(vendor, GfxVendors::Vfio | GfxVendors::Compute) {
|
|
||||||
if let Ok(mut config) = self.config.try_lock() {
|
|
||||||
config.gfx_tmp_mode = Some(vendor);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: undo if failed? Save last mode, catch errors...
|
|
||||||
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_vendor_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_config(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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,102 +1,151 @@
|
|||||||
use crate::error::RogError;
|
use std::sync::{Arc, Mutex};
|
||||||
use crate::{config::Config, GetSupported};
|
|
||||||
use log::info;
|
|
||||||
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 log::{info, warn};
|
||||||
|
use rog_profiles::error::ProfileError;
|
||||||
|
use rog_profiles::{FanCurveProfiles, Profile};
|
||||||
|
use rog_supported::PlatformProfileFunctions;
|
||||||
|
|
||||||
|
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 with the following patch applied:
|
||||||
|
https://lkml.org/lkml/2021/8/18/1022
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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/8/20/232
|
||||||
|
Please note that as of 24/08/2021 this is not final.
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
PlatformProfileFunctions {
|
||||||
|
platform_profile: Profile::is_platform_profile_supported(),
|
||||||
|
fan_curves: fan_curve_supported,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::Reloadable for CtrlFanAndCpu {
|
impl crate::Reloadable for CtrlPlatformProfile {
|
||||||
|
/// 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
|
||||||
cfg.write();
|
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 thermal throttle control");
|
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() {
|
|
||||||
config.read();
|
|
||||||
|
|
||||||
let mut i = config
|
|
||||||
.toggle_profiles
|
|
||||||
.binary_search(&config.active_profile)
|
|
||||||
.unwrap_or(0)
|
|
||||||
+ 1;
|
|
||||||
if i >= config.toggle_profiles.len() {
|
|
||||||
i = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let profile = config.toggle_profiles[i].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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Toggle to next profile in list. This will first read the config, switch, then write out
|
||||||
|
pub(super) fn set_next_profile(&mut self) -> Result<(), RogError> {
|
||||||
|
// Read first just incase the user has modified the config before calling this
|
||||||
|
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() {
|
||||||
config.read();
|
if let Ok(mut device) = FanCurveProfiles::get_device() {
|
||||||
if let Some(existing) = config.power_profiles.get(profile) {
|
curves.set_active_curve_to_defaults(self.config.active_profile, &mut device)?;
|
||||||
existing.set_system_all()?;
|
}
|
||||||
config.active_profile = existing.name.clone();
|
}
|
||||||
config.write();
|
Ok(())
|
||||||
info!("Profile was changed to: {}", profile);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CtrlProfileTask {
|
||||||
|
ctrl: Arc<Mutex<CtrlPlatformProfile>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CtrlProfileTask {
|
||||||
|
pub fn new(ctrl: Arc<Mutex<CtrlPlatformProfile>>) -> Self {
|
||||||
|
Self { ctrl }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CtrlTask for CtrlProfileTask {
|
||||||
|
fn do_task(&self) -> Result<(), RogError> {
|
||||||
|
if let Ok(ref mut lock) = self.ctrl.try_lock() {
|
||||||
|
let new_profile = Profile::get_active_profile().unwrap();
|
||||||
|
if new_profile != lock.config.active_profile {
|
||||||
|
lock.config.active_profile = new_profile;
|
||||||
|
lock.write_profile_curve_to_platform()?;
|
||||||
|
lock.save_config();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn new_or_modify(&mut self, profile: &Profile) -> Result<(), RogError> {
|
|
||||||
if let Ok(mut config) = self.config.clone().try_lock() {
|
|
||||||
config.read();
|
|
||||||
|
|
||||||
if let Some(existing) = config.power_profiles.get_mut(&profile.name) {
|
|
||||||
*existing = profile.clone();
|
|
||||||
existing.set_system_all()?;
|
|
||||||
} else {
|
|
||||||
config.power_profiles
|
|
||||||
.insert(profile.name.clone(), profile.clone());
|
|
||||||
profile.set_system_all()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
config.active_profile = profile.name.clone();
|
|
||||||
config.write();
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
pub mod zbus;
|
pub mod config;
|
||||||
|
|
||||||
pub mod controller;
|
pub mod controller;
|
||||||
|
pub mod zbus;
|
||||||
|
|||||||
@@ -1,153 +1,179 @@
|
|||||||
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 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 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() {
|
fn next_profile(&mut self) {
|
||||||
cfg.read();
|
if let Ok(mut ctrl) = self.inner.try_lock() {
|
||||||
let profile_names = cfg.power_profiles.keys().cloned().collect::<Vec<String>>();
|
ctrl.set_next_profile()
|
||||||
return Ok(profile_names);
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
}
|
ctrl.save_config();
|
||||||
}
|
}
|
||||||
|
self.do_notification();
|
||||||
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
|
||||||
|
fn set_active_profile(&self, profile: Profile) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
self.do_notification();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<()> {}
|
fn notify_profile(&self, profile: &Profile) -> zbus::Result<()> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FanAndCpuZbus {
|
impl ProfileZbus {
|
||||||
fn do_notification(&self) {
|
fn do_notification(&self) {
|
||||||
if let Ok(ctrl) = self.inner.try_lock() {
|
if let Ok(ctrl) = self.inner.try_lock() {
|
||||||
if let Ok(cfg) = ctrl.config.clone().try_lock() {
|
self.notify_profile(&ctrl.config.active_profile)
|
||||||
if let Some(profile) = cfg.power_profiles.get(&cfg.active_profile) {
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
self.notify_profile(&profile)
|
|
||||||
.unwrap_or_else(|err| warn!("{}", err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::ZbusAdd for FanAndCpuZbus {
|
impl crate::ZbusAdd for ProfileZbus {
|
||||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||||
server
|
server
|
||||||
.at(
|
.at(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::{config::Config, error::RogError, GetSupported};
|
use crate::{config::Config, error::RogError, GetSupported};
|
||||||
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};
|
||||||
@@ -28,8 +28,8 @@ impl GetSupported for CtrlRogBios {
|
|||||||
|
|
||||||
fn get_supported() -> Self::A {
|
fn get_supported() -> Self::A {
|
||||||
RogBiosSupportedFunctions {
|
RogBiosSupportedFunctions {
|
||||||
post_sound_toggle: CtrlRogBios::check_path_exists(ASUS_POST_LOGO_SOUND).is_ok(),
|
post_sound_toggle: Path::new(ASUS_POST_LOGO_SOUND).exists(),
|
||||||
dedicated_gfx_toggle: CtrlRogBios::check_path_exists(ASUS_SWITCH_GRAPHIC_MODE).is_ok(),
|
dedicated_gfx_toggle: Path::new(ASUS_SWITCH_GRAPHIC_MODE).exists(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,8 +63,6 @@ impl CtrlRogBios {
|
|||||||
#[dbus_interface(signal)]
|
#[dbus_interface(signal)]
|
||||||
pub fn notify_dedicated_graphic_mode(&self, dedicated: bool) -> zbus::Result<()> {}
|
pub fn notify_dedicated_graphic_mode(&self, dedicated: bool) -> zbus::Result<()> {}
|
||||||
|
|
||||||
// // // // // // // // // //
|
|
||||||
|
|
||||||
pub fn set_post_boot_sound(&mut self, on: bool) {
|
pub fn set_post_boot_sound(&mut self, on: bool) {
|
||||||
Self::set_boot_sound(on)
|
Self::set_boot_sound(on)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
@@ -116,22 +114,17 @@ impl crate::Reloadable for CtrlRogBios {
|
|||||||
|
|
||||||
impl CtrlRogBios {
|
impl CtrlRogBios {
|
||||||
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
|
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
|
||||||
match CtrlRogBios::check_path_exists(ASUS_SWITCH_GRAPHIC_MODE) {
|
if Path::new(ASUS_SWITCH_GRAPHIC_MODE).exists() {
|
||||||
Ok(_) => {
|
CtrlRogBios::set_path_mutable(ASUS_SWITCH_GRAPHIC_MODE)?;
|
||||||
CtrlRogBios::set_path_mutable(ASUS_SWITCH_GRAPHIC_MODE)?;
|
} else {
|
||||||
}
|
info!("G-Sync Switchable Graphics not detected");
|
||||||
Err(err) => {
|
info!("If your laptop is not a G-Sync enabled laptop then you can ignore this. Standard graphics switching will still work.");
|
||||||
info!("ROG Switchable Graphics (bios) not detected: {}", err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match CtrlRogBios::check_path_exists(ASUS_POST_LOGO_SOUND) {
|
if Path::new(ASUS_POST_LOGO_SOUND).exists() {
|
||||||
Ok(_) => {
|
CtrlRogBios::set_path_mutable(ASUS_POST_LOGO_SOUND)?;
|
||||||
CtrlRogBios::set_path_mutable(ASUS_POST_LOGO_SOUND)?;
|
} else {
|
||||||
}
|
info!("Switch for POST boot sound not detected");
|
||||||
Err(err) => {
|
|
||||||
info!("ROG boot sound toggle (bios) not detected: {}", err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(CtrlRogBios { _config: config })
|
Ok(CtrlRogBios { _config: config })
|
||||||
@@ -147,19 +140,8 @@ impl CtrlRogBios {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_path_exists(path: &str) -> Result<(), RogError> {
|
|
||||||
if Path::new(path).exists() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(RogError::MissingFunction(path.into()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn has_dedicated_gfx_toggle() -> bool {
|
pub fn has_dedicated_gfx_toggle() -> bool {
|
||||||
if CtrlRogBios::check_path_exists(ASUS_SWITCH_GRAPHIC_MODE).is_ok() {
|
Path::new(ASUS_SWITCH_GRAPHIC_MODE).exists()
|
||||||
return true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_gfx_mode() -> Result<i8, RogError> {
|
pub fn get_gfx_mode() -> Result<i8, RogError> {
|
||||||
@@ -186,7 +168,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 {
|
||||||
@@ -200,17 +182,6 @@ impl CtrlRogBios {
|
|||||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||||
|
|
||||||
self.update_initramfs(dedicated)?;
|
self.update_initramfs(dedicated)?;
|
||||||
|
|
||||||
// if let Ok(ded) = CtrlRogBios::get_gfx_mode() {
|
|
||||||
// if let Ok(vendor) = CtrlGraphics::get_vendor() {
|
|
||||||
// if ded == 1 && vendor != "nvidia" {
|
|
||||||
// warn!("Dedicated GFX toggle is on but driver mode is not nvidia \nSetting to nvidia driver mode");
|
|
||||||
// CtrlGraphics::set_gfx_config(&GfxVendors::Nvidia)
|
|
||||||
// .unwrap_or_else(|err| warn!("Gfx controller: {}", err));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +269,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)
|
||||||
@@ -321,7 +292,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)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,20 +5,20 @@ use zvariant::ObjectPath;
|
|||||||
use zvariant_derive::Type;
|
use zvariant_derive::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,
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,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,31 +1,39 @@
|
|||||||
use daemon::ctrl_leds::controller::{
|
|
||||||
CtrlKbdLed, CtrlKbdLedReloader, CtrlKbdLedTask, CtrlKbdLedZbus,
|
|
||||||
};
|
|
||||||
use daemon::{
|
|
||||||
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::{
|
|
||||||
ctrl_profiles::{controller::CtrlFanAndCpu, zbus::FanAndCpuZbus},
|
|
||||||
laptops::LaptopLedData,
|
|
||||||
};
|
|
||||||
|
|
||||||
use daemon::{CtrlTask, Reloadable, ZbusAdd};
|
|
||||||
use log::LevelFilter;
|
|
||||||
use log::{error, info, warn};
|
|
||||||
use rog_dbus::DBUS_NAME;
|
|
||||||
use rog_types::gfx_vendors::GfxVendors;
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
use std::thread::sleep;
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::{env, thread};
|
||||||
|
|
||||||
|
use ::zbus::{fdo, Connection, ObjectServer};
|
||||||
|
use log::LevelFilter;
|
||||||
|
use log::{error, info, warn};
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
use daemon::ctrl_charge::CtrlCharge;
|
||||||
|
use daemon::ctrl_profiles::config::ProfileConfig;
|
||||||
|
use daemon::ctrl_profiles::controller::CtrlProfileTask;
|
||||||
use daemon::ctrl_rog_bios::CtrlRogBios;
|
use daemon::ctrl_rog_bios::CtrlRogBios;
|
||||||
use std::convert::Into;
|
use daemon::error::RogError;
|
||||||
use zbus::fdo;
|
use daemon::{
|
||||||
use zbus::Connection;
|
config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported,
|
||||||
use zvariant::ObjectPath;
|
};
|
||||||
|
use daemon::{
|
||||||
|
ctrl_profiles::{controller::CtrlPlatformProfile, zbus::ProfileZbus},
|
||||||
|
laptops::LaptopLedData,
|
||||||
|
};
|
||||||
|
use daemon::{CtrlTask, Reloadable, ZbusAdd};
|
||||||
|
use rog_dbus::DBUS_NAME;
|
||||||
|
use rog_profiles::Profile;
|
||||||
|
|
||||||
|
static PROFILE_CONFIG_PATH: &str = "/etc/asusd/profile.conf";
|
||||||
|
|
||||||
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();
|
||||||
@@ -35,12 +43,26 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
.filter(None, LevelFilter::Info)
|
.filter(None, LevelFilter::Info)
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
info!(" daemon v{}", daemon::VERSION);
|
let is_service = match env::var_os("IS_SERVICE") {
|
||||||
info!(" rog-anime v{}", rog_anime::VERSION);
|
Some(val) => val == "1",
|
||||||
info!(" rog-aura v{}", rog_aura::VERSION);
|
None => false,
|
||||||
info!(" rog-dbus v{}", rog_dbus::VERSION);
|
};
|
||||||
info!("rog-profiles v{}", rog_profiles::VERSION);
|
|
||||||
info!(" rog-types v{}", rog_types::VERSION);
|
if !is_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!("asusd will now exit");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
info!(" daemon v{}", daemon::VERSION);
|
||||||
|
info!(" rog-anime v{}", rog_anime::VERSION);
|
||||||
|
info!(" rog-aura v{}", rog_aura::VERSION);
|
||||||
|
info!(" rog-dbus v{}", rog_dbus::VERSION);
|
||||||
|
info!(" rog-profiles v{}", rog_profiles::VERSION);
|
||||||
|
info!("rog-supported v{}", rog_supported::VERSION);
|
||||||
|
|
||||||
start_daemon()?;
|
start_daemon()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -50,18 +72,14 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
fn start_daemon() -> Result<(), Box<dyn Error>> {
|
fn start_daemon() -> 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 connection = Connection::new_system()?;
|
||||||
fdo::DBusProxy::new(&connection)?
|
let fdo_connection = fdo::DBusProxy::new(&connection)?;
|
||||||
.request_name(DBUS_NAME, fdo::RequestNameFlags::ReplaceExisting.into())?;
|
let mut object_server = ObjectServer::new(&connection);
|
||||||
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 object_server);
|
||||||
@@ -79,7 +97,7 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match CtrlCharge::new(config.clone()) {
|
match CtrlCharge::new(config) {
|
||||||
Ok(mut ctrl) => {
|
Ok(mut ctrl) => {
|
||||||
// Do a reload of any settings
|
// Do a reload of any settings
|
||||||
ctrl.reload()
|
ctrl.reload()
|
||||||
@@ -92,16 +110,32 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
ProfileZbus::new(tmp.clone()).add_to_server(&mut object_server);
|
||||||
|
|
||||||
|
let task = CtrlProfileTask::new(tmp);
|
||||||
|
thread::Builder::new().name("profile tasks".into()).spawn(
|
||||||
|
move || -> Result<(), RogError> {
|
||||||
|
loop {
|
||||||
|
task.do_task()?;
|
||||||
|
sleep(Duration::from_millis(100));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
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()) {
|
||||||
@@ -116,7 +150,14 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
let zbus = CtrlAnimeZbus(inner.clone());
|
let zbus = CtrlAnimeZbus(inner.clone());
|
||||||
zbus.add_to_server(&mut object_server);
|
zbus.add_to_server(&mut object_server);
|
||||||
|
|
||||||
tasks.push(Box::new(CtrlAnimeTask::new(inner)));
|
let task = CtrlAnimeTask::new(inner);
|
||||||
|
thread::Builder::new().name("anime tasks".into()).spawn(
|
||||||
|
move || -> Result<(), RogError> {
|
||||||
|
loop {
|
||||||
|
task.do_task()?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("AniMe control: {}", err);
|
error!("AniMe control: {}", err);
|
||||||
@@ -135,92 +176,26 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
.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 object_server);
|
||||||
let task = CtrlKbdLedTask(inner);
|
|
||||||
tasks.push(Box::new(task));
|
let task = CtrlKbdLedTask::new(inner);
|
||||||
|
thread::Builder::new().name("keyboard tasks".into()).spawn(
|
||||||
|
move || -> Result<(), RogError> {
|
||||||
|
loop {
|
||||||
|
task.do_task()?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
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 {
|
fdo_connection.request_name(DBUS_NAME, fdo::RequestNameFlags::ReplaceExisting.into())?;
|
||||||
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_vendor_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_vendor_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 to check errors and iterate zbus server
|
||||||
loop {
|
loop {
|
||||||
if let Err(err) = &handle {
|
|
||||||
error!("{}", err);
|
|
||||||
}
|
|
||||||
if let Err(err) = object_server.try_handle_next() {
|
if let Err(err) = object_server.try_handle_next() {
|
||||||
error!("{}", err);
|
error!("{}", err);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
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 {
|
||||||
ParseFanLevel,
|
|
||||||
ParseVendor,
|
ParseVendor,
|
||||||
ParseLed,
|
ParseLed,
|
||||||
MissingProfile(String),
|
MissingProfile(String),
|
||||||
@@ -18,25 +13,22 @@ 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 {
|
||||||
// This trait requires `fmt` with this exact signature.
|
// This trait requires `fmt` with this exact signature.
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
RogError::ParseFanLevel => write!(f, "Parse profile error"),
|
|
||||||
RogError::ParseVendor => write!(f, "Parse gfx vendor error"),
|
RogError::ParseVendor => write!(f, "Parse gfx vendor error"),
|
||||||
RogError::ParseLed => write!(f, "Parse LED error"),
|
RogError::ParseLed => write!(f, "Parse LED error"),
|
||||||
RogError::MissingProfile(profile) => write!(f, "Profile does not exist {}", profile),
|
RogError::MissingProfile(profile) => write!(f, "Profile does not exist {}", profile),
|
||||||
@@ -46,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)
|
||||||
|
|||||||
@@ -10,10 +10,8 @@ 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());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,7 +27,7 @@ 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 crate::error::RogError;
|
use crate::error::RogError;
|
||||||
use config::Config;
|
use config::Config;
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -3,6 +3,7 @@ Description=ASUS Notebook Control
|
|||||||
After=basic.target syslog.target
|
After=basic.target syslog.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
|
Environment=IS_SERVICE=1
|
||||||
ExecStart=/usr/bin/asusd
|
ExecStart=/usr/bin/asusd
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
Type=dbus
|
Type=dbus
|
||||||
|
|||||||
@@ -12,6 +12,13 @@ standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight
|
|||||||
multizone = false
|
multizone = false
|
||||||
per_key = true
|
per_key = true
|
||||||
|
|
||||||
|
[[led_data]]
|
||||||
|
prod_family = "Zephyrus M"
|
||||||
|
board_names = ["GM501GS"]
|
||||||
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
||||||
|
multizone = true
|
||||||
|
per_key = false
|
||||||
|
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "ROG Zephyrus M15"
|
prod_family = "ROG Zephyrus M15"
|
||||||
board_names = ["GU502LW"]
|
board_names = ["GU502LW"]
|
||||||
@@ -35,14 +42,14 @@ per_key = false
|
|||||||
|
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "ROG Strix"
|
prod_family = "ROG Strix"
|
||||||
board_names = ["G531GW", "G733QS", "G733QR"]
|
board_names = ["G531GW", "G533QR", "G533QS", "G733QS", "G733QR", "G513QR", "G713QR"]
|
||||||
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 = false
|
||||||
per_key = true
|
per_key = true
|
||||||
|
|
||||||
[[led_data]]
|
[[led_data]]
|
||||||
prod_family = "ROG Strix"
|
prod_family = "ROG Strix"
|
||||||
board_names = ["GX531", "G512LV", "G712LV", "G712LW"]
|
board_names = ["GX531", "G512LV", "G712LV", "G712LW", "G513IH", "G513QY", "G713QM"]
|
||||||
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
||||||
multizone = true
|
multizone = true
|
||||||
per_key = false
|
per_key = false
|
||||||
@@ -89,9 +96,38 @@ standard = ["Static", "Breathe", "Pulse"]
|
|||||||
multizone = false
|
multizone = false
|
||||||
per_key = false
|
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 = false
|
||||||
|
per_key = false
|
||||||
|
|
||||||
|
[[led_data]]
|
||||||
|
prod_family = "ROG Zephyrus G15"
|
||||||
|
board_names = ["GA503Q"]
|
||||||
|
standard = ["Static", "Breathe", "Pulse", "Rainbow", "Strobe"]
|
||||||
|
multizone = 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 = false
|
||||||
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 = false
|
||||||
|
per_key = true
|
||||||
|
|
||||||
|
[[led_data]]
|
||||||
|
prod_family = "ROG Flow X13"
|
||||||
|
board_names = ["GV301QH", "GV301QE"]
|
||||||
|
standard = ["Static", "Breathe", "Pulse"]
|
||||||
|
multizone = false
|
||||||
|
per_key = false
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
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
|
||||||
|
ExecStartPre=/bin/sleep 2
|
||||||
ExecStart=/usr/bin/asusd
|
ExecStart=/usr/bin/asusd
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
Restart=always
|
Restart=always
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Gravit.io -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
style="isolation:isolate"
|
||||||
|
viewBox="0 0 82.733002 82.733002"
|
||||||
|
width="82.733002pt"
|
||||||
|
height="82.733002pt"
|
||||||
|
version="1.1"
|
||||||
|
id="svg19"
|
||||||
|
sodipodi:docname="gpu-compute.svg"
|
||||||
|
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview21"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:document-units="pt"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="4.96875"
|
||||||
|
inkscape:cx="15.597484"
|
||||||
|
inkscape:cy="54.742138"
|
||||||
|
inkscape:window-width="2048"
|
||||||
|
inkscape:window-height="1083"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg19" />
|
||||||
|
<defs
|
||||||
|
id="defs5">
|
||||||
|
<clipPath
|
||||||
|
id="_clipPath_ZEBBDq6ZtbLgkRXocTHUWTcRbcURZIva">
|
||||||
|
<rect
|
||||||
|
width="128"
|
||||||
|
height="128"
|
||||||
|
id="rect2"
|
||||||
|
x="0"
|
||||||
|
y="0" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
clip-path="url(#_clipPath_ZEBBDq6ZtbLgkRXocTHUWTcRbcURZIva)"
|
||||||
|
id="g17"
|
||||||
|
transform="translate(-22.624,-23)">
|
||||||
|
<rect
|
||||||
|
width="128"
|
||||||
|
height="128"
|
||||||
|
style="fill:#000000"
|
||||||
|
fill-opacity="0"
|
||||||
|
id="rect7"
|
||||||
|
x="0"
|
||||||
|
y="0" />
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
<path
|
||||||
|
id="path9"
|
||||||
|
style="opacity:1;mix-blend-mode:normal;fill:#ed1c24;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 13.789062,13.789062 v 6.894532 H 0 v 6.894531 H 13.789062 V 41.367188 H 0 v 6.894531 H 13.789062 V 62.048828 H 0 v 6.894531 H 13.789062 V 82.732422 H 0 v 6.894531 h 13.789062 v 6.894531 h 6.894532 v 13.789066 h 6.894531 V 96.521484 h 13.789063 v 13.789066 h 6.894531 V 96.521484 h 13.789062 v 13.789066 h 6.892578 V 96.521484 h 13.789063 v 13.789066 h 6.896484 V 96.521484 h 6.894532 z"
|
||||||
|
transform="matrix(0.75,0,0,0.75,22.624,23)" />
|
||||||
|
<path
|
||||||
|
id="path9-3"
|
||||||
|
style="isolation:isolate;mix-blend-mode:normal;fill:#76b900;fill-opacity:1;stroke:#000000;stroke-width:0.375;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 95.016578,95.391113 -0.0051,-5.170896 10.341782,-0.01017 -0.005,-5.170896 -10.341789,0.01017 -0.01017,-10.341791 10.341799,-0.01018 -0.005,-5.170895 -10.341787,0.01018 -0.01017,-10.340328 10.341797,-0.01017 -0.005,-5.170896 -10.341794,0.01017 -0.01018,-10.341792 10.341784,-0.01017 -0.005,-5.170895 -10.341791,0.01017 -0.0051,-5.170897 -5.170898,0.0051 -0.01017,-10.341796 -5.170896,0.0051 0.01017,10.341797 -10.341792,0.01017 -0.01017,-10.341795 -5.170897,0.0051 0.01017,10.341796 -10.341791,0.01017 -0.01017,-10.341796 -5.169431,0.0051 0.01017,10.341795 -10.341792,0.01018 -0.01017,-10.341797 -5.172361,0.0051 0.01017,10.341795 -5.170894,0.0051 z" />
|
||||||
|
<rect
|
||||||
|
x="38.146"
|
||||||
|
y="38.512001"
|
||||||
|
width="51.708"
|
||||||
|
height="51.708"
|
||||||
|
fill="rgb(77,77,77)"
|
||||||
|
id="rect11"
|
||||||
|
style="fill:none" />
|
||||||
|
<path
|
||||||
|
d="m 67.238,81.543 c 3.416,0 7.169,-0.866 10.056,-2.262 l -1.395,-4.474 c -2.165,0.818 -4.86,1.299 -7.169,1.299 -7.313,0 -11.884,-4.811 -11.884,-12.317 0,-7.073 4.138,-11.114 11.162,-11.114 2.646,0 5.678,0.529 7.843,1.395 l 1.732,-4.811 c -2.839,-1.347 -6.111,-2.069 -9.43,-2.069 -10.537,0 -17.754,6.976 -17.754,17.465 0,10.104 6.832,16.888 16.839,16.888 z"
|
||||||
|
fill="#dddddd"
|
||||||
|
id="path13"
|
||||||
|
style="stroke:#000000;stroke-opacity:1;stroke-width:0.5;stroke-miterlimit:4;stroke-dasharray:none;fill:#ffffff" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,82 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Gravit.io -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
style="isolation:isolate"
|
||||||
|
viewBox="0 0 82.733002 82.733002"
|
||||||
|
width="82.733002pt"
|
||||||
|
height="82.733002pt"
|
||||||
|
version="1.1"
|
||||||
|
id="svg19"
|
||||||
|
sodipodi:docname="gpu-hybrid.svg"
|
||||||
|
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview21"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:document-units="pt"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="4.96875"
|
||||||
|
inkscape:cx="15.798742"
|
||||||
|
inkscape:cy="54.742138"
|
||||||
|
inkscape:window-width="2048"
|
||||||
|
inkscape:window-height="1083"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg19" />
|
||||||
|
<defs
|
||||||
|
id="defs5">
|
||||||
|
<clipPath
|
||||||
|
id="_clipPath_uVSrFPVusaaYDISWhfCjIlGBeb9FaZAr">
|
||||||
|
<rect
|
||||||
|
width="128"
|
||||||
|
height="128"
|
||||||
|
id="rect2"
|
||||||
|
x="0"
|
||||||
|
y="0" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
clip-path="url(#_clipPath_uVSrFPVusaaYDISWhfCjIlGBeb9FaZAr)"
|
||||||
|
id="g17"
|
||||||
|
transform="translate(-22.624,-23)">
|
||||||
|
<rect
|
||||||
|
width="128"
|
||||||
|
height="128"
|
||||||
|
style="fill:#000000"
|
||||||
|
fill-opacity="0"
|
||||||
|
id="rect7"
|
||||||
|
x="0"
|
||||||
|
y="0" />
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
<path
|
||||||
|
d="m 38.137,23 v 10.342 h -5.171 v 5.17 H 22.624 v 5.171 H 32.966 V 54.025 H 22.624 v 5.171 H 32.966 V 69.537 H 22.624 v 5.171 H 32.966 V 85.05 H 22.624 v 5.17 h 10.342 v 5.171 h 5.171 v 10.342 h 5.171 V 95.391 h 10.341 v 10.342 H 58.82 V 95.391 h 10.342 v 10.342 h 5.17 V 95.391 h 10.342 v 10.342 h 5.171 V 95.391 h 5.171 V 90.22 h 10.341 V 85.05 H 95.016 V 74.708 h 10.341 V 69.537 H 95.016 V 59.196 h 10.341 V 54.025 H 95.016 V 43.683 h 10.341 V 38.512 H 95.016 v -5.17 H 89.845 V 23 H 84.674 V 33.342 H 74.332 V 23 h -5.17 V 33.342 H 58.82 V 23 H 53.649 V 33.342 H 43.308 V 23 Z"
|
||||||
|
fill="rgb(77,77,77)"
|
||||||
|
id="path9"
|
||||||
|
style="fill:#ed1c24;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||||
|
<rect
|
||||||
|
x="38.146"
|
||||||
|
y="38.512001"
|
||||||
|
width="51.708"
|
||||||
|
height="51.708"
|
||||||
|
fill="rgb(77,77,77)"
|
||||||
|
id="rect11"
|
||||||
|
style="fill:#76b900;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="m 51.464,79 h 5.663 V 66.118 H 70.916 V 79 h 5.62 V 49 h -5.62 V 61.147 H 57.127 V 49 h -5.663 z"
|
||||||
|
fill="rgb(221,221,221)"
|
||||||
|
id="path13"
|
||||||
|
style="fill:#ececec;stroke:#000000;stroke-opacity:1;stroke-width:0.5;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Gravit.io -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
style="isolation:isolate"
|
||||||
|
viewBox="0 0 82.410369 82.410377"
|
||||||
|
width="82.41037pt"
|
||||||
|
height="82.410378pt"
|
||||||
|
version="1.1"
|
||||||
|
id="svg17"
|
||||||
|
sodipodi:docname="gpu-integrated.svg"
|
||||||
|
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview19"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:document-units="pt"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="4.96875"
|
||||||
|
inkscape:cx="14.389937"
|
||||||
|
inkscape:cy="54.742138"
|
||||||
|
inkscape:window-width="2048"
|
||||||
|
inkscape:window-height="1083"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg17" />
|
||||||
|
<defs
|
||||||
|
id="defs5">
|
||||||
|
<clipPath
|
||||||
|
id="_clipPath_GAg6mcrkdqJgsmZOjvo6JT6R1d3r9FoI">
|
||||||
|
<rect
|
||||||
|
width="128"
|
||||||
|
height="128"
|
||||||
|
id="rect2"
|
||||||
|
x="0"
|
||||||
|
y="0" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<path
|
||||||
|
style="fill:#ed1c24;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 15.92217,76.9033 V 71.771223 H 13.28066 10.63915 V 69.129715 66.488208 H 5.507075 0.375 v -2.037735 -2.037735 h 5.132075 5.132075 v -5.73585 -5.73585 H 5.507075 0.375 V 48.903302 46.865566 H 5.507075 10.63915 V 41.205189 35.544811 H 5.507075 0.375 V 33.431604 31.318396 H 5.507075 10.63915 V 25.658019 19.997641 H 5.507075 0.375 V 17.959906 15.92217 h 5.132075 5.132075 v -2.641509 -2.64151 h 2.64151 2.64151 V 5.5070763 0.375 h 2.037736 2.037735 v 5.1320763 5.1320747 h 5.660378 5.660377 V 5.5070763 0.375 h 2.113207 2.113208 v 5.1320763 5.1320747 h 5.660377 5.660378 V 5.5070763 0.375 h 1.947066 1.947063 l 0.09711,1.8490563 c 0.07996,1.52246 0.251242,6.717377 0.253724,7.695141 0.0014,0.5274167 -0.04609,0.5197837 3.905982,0.6286047 1.618867,0.04457 3.945285,0.123799 5.16981,0.176053 l 2.226412,0.09501 V 5.5969303 0.375 h 2.037735 2.037743 v 5.1320763 5.1320747 h 2.641507 2.641508 v 2.64151 2.641509 h 5.132077 5.13207 v 2.037736 2.037735 h -5.13207 -5.132077 v 5.660378 5.660377 h 5.132077 5.13207 v 2.113208 2.113207 h -5.13207 -5.132077 v 5.660378 5.660377 h 5.132077 5.13207 v 2.037736 2.037736 h -5.13207 -5.132077 v 5.73585 5.73585 h 5.132077 5.13207 v 2.037735 2.037735 h -5.13207 -5.132077 v 2.641507 2.641508 H 69.129718 66.488211 V 76.9033 82.035378 H 64.450468 62.412733 V 76.9033 71.771223 h -5.73585 -5.735846 V 76.9033 82.035378 H 48.903301 46.865566 V 76.9033 71.771223 H 41.205188 35.544811 V 76.9033 82.035378 H 33.431603 31.318396 V 76.9033 71.771223 H 25.658019 19.997641 V 76.9033 82.035378 H 17.959906 15.92217 Z"
|
||||||
|
id="path1355" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Gravit.io -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
style="isolation:isolate"
|
||||||
|
viewBox="0 0 82.410369 82.410377"
|
||||||
|
width="82.41037pt"
|
||||||
|
height="82.410378pt"
|
||||||
|
version="1.1"
|
||||||
|
id="svg17"
|
||||||
|
sodipodi:docname="gpu-nvidia.svg"
|
||||||
|
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview19"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:document-units="pt"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="4.96875"
|
||||||
|
inkscape:cx="14.389937"
|
||||||
|
inkscape:cy="54.742138"
|
||||||
|
inkscape:window-width="2048"
|
||||||
|
inkscape:window-height="1083"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg17" />
|
||||||
|
<defs
|
||||||
|
id="defs5">
|
||||||
|
<clipPath
|
||||||
|
id="_clipPath_GAg6mcrkdqJgsmZOjvo6JT6R1d3r9FoI">
|
||||||
|
<rect
|
||||||
|
width="128"
|
||||||
|
height="128"
|
||||||
|
id="rect2"
|
||||||
|
x="0"
|
||||||
|
y="0" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<path
|
||||||
|
style="fill:#76b900;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 15.92217,76.9033 V 71.771223 H 13.28066 10.63915 V 69.129715 66.488208 H 5.507075 0.375 v -2.037735 -2.037735 h 5.132075 5.132075 v -5.73585 -5.73585 H 5.507075 0.375 V 48.903302 46.865566 H 5.507075 10.63915 V 41.205189 35.544811 H 5.507075 0.375 V 33.431604 31.318396 H 5.507075 10.63915 V 25.658019 19.997641 H 5.507075 0.375 V 17.959906 15.92217 h 5.132075 5.132075 v -2.641509 -2.64151 h 2.64151 2.64151 V 5.5070763 0.375 h 2.037736 2.037735 v 5.1320763 5.1320747 h 5.660378 5.660377 V 5.5070763 0.375 h 2.113207 2.113208 v 5.1320763 5.1320747 h 5.660377 5.660378 V 5.5070763 0.375 h 1.947066 1.947063 l 0.09711,1.8490563 c 0.07996,1.52246 0.251242,6.717377 0.253724,7.695141 0.0014,0.5274167 -0.04609,0.5197837 3.905982,0.6286047 1.618867,0.04457 3.945285,0.123799 5.16981,0.176053 l 2.226412,0.09501 V 5.5969303 0.375 h 2.037735 2.037743 v 5.1320763 5.1320747 h 2.641507 2.641508 v 2.64151 2.641509 h 5.132077 5.13207 v 2.037736 2.037735 h -5.13207 -5.132077 v 5.660378 5.660377 h 5.132077 5.13207 v 2.113208 2.113207 h -5.13207 -5.132077 v 5.660378 5.660377 h 5.132077 5.13207 v 2.037736 2.037736 h -5.13207 -5.132077 v 5.73585 5.73585 h 5.132077 5.13207 v 2.037735 2.037735 h -5.13207 -5.132077 v 2.641507 2.641508 H 69.129718 66.488211 V 76.9033 82.035378 H 64.450468 62.412733 V 76.9033 71.771223 h -5.73585 -5.735846 V 76.9033 82.035378 H 48.903301 46.865566 V 76.9033 71.771223 H 41.205188 35.544811 V 76.9033 82.035378 H 33.431603 31.318396 V 76.9033 71.771223 H 25.658019 19.997641 V 76.9033 82.035378 H 17.959906 15.92217 Z"
|
||||||
|
id="path1355" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1,86 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Gravit.io -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
style="isolation:isolate"
|
||||||
|
viewBox="0 0 82.733002 82.733002"
|
||||||
|
width="82.733002pt"
|
||||||
|
height="82.733002pt"
|
||||||
|
version="1.1"
|
||||||
|
id="svg19"
|
||||||
|
sodipodi:docname="gpu-vfio.svg"
|
||||||
|
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview21"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:document-units="pt"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="7.8686859"
|
||||||
|
inkscape:cx="55.091791"
|
||||||
|
inkscape:cy="55.155334"
|
||||||
|
inkscape:window-width="2048"
|
||||||
|
inkscape:window-height="1083"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg19" />
|
||||||
|
<defs
|
||||||
|
id="defs5">
|
||||||
|
<clipPath
|
||||||
|
id="_clipPath_ZEBBDq6ZtbLgkRXocTHUWTcRbcURZIva">
|
||||||
|
<rect
|
||||||
|
width="128"
|
||||||
|
height="128"
|
||||||
|
id="rect2"
|
||||||
|
x="0"
|
||||||
|
y="0" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
clip-path="url(#_clipPath_ZEBBDq6ZtbLgkRXocTHUWTcRbcURZIva)"
|
||||||
|
id="g17"
|
||||||
|
transform="translate(-22.624,-23)">
|
||||||
|
<rect
|
||||||
|
width="128"
|
||||||
|
height="128"
|
||||||
|
style="fill:#000000"
|
||||||
|
fill-opacity="0"
|
||||||
|
id="rect7"
|
||||||
|
x="0"
|
||||||
|
y="0" />
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
<path
|
||||||
|
id="path9"
|
||||||
|
style="opacity:1;mix-blend-mode:normal;fill:#ed1c24;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 13.789062,13.789062 v 6.894532 H 0 v 6.894531 H 13.789062 V 41.367188 H 0 v 6.894531 H 13.789062 V 62.048828 H 0 v 6.894531 H 13.789062 V 82.732422 H 0 v 6.894531 h 13.789062 v 6.894531 h 6.894532 v 13.789066 h 6.894531 V 96.521484 h 13.789063 v 13.789066 h 6.894531 V 96.521484 h 13.789062 v 13.789066 h 6.892578 V 96.521484 h 13.789063 v 13.789066 h 6.896484 V 96.521484 h 6.894532 z"
|
||||||
|
transform="matrix(0.75,0,0,0.75,22.624,23)" />
|
||||||
|
<path
|
||||||
|
id="path9-3"
|
||||||
|
style="isolation:isolate;mix-blend-mode:normal;fill:#76b900;fill-opacity:1;stroke:#000000;stroke-width:0.375;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 95.016578,95.391113 -0.0051,-5.170896 10.341782,-0.01017 -0.005,-5.170896 -10.341789,0.01017 -0.01017,-10.341791 10.341799,-0.01018 -0.005,-5.170895 -10.341787,0.01018 -0.01017,-10.340328 10.341797,-0.01017 -0.005,-5.170896 -10.341794,0.01017 -0.01018,-10.341792 10.341784,-0.01017 -0.005,-5.170895 -10.341791,0.01017 -0.0051,-5.170897 -5.170898,0.0051 -0.01017,-10.341796 -5.170896,0.0051 0.01017,10.341797 -10.341792,0.01017 -0.01017,-10.341795 -5.170897,0.0051 0.01017,10.341796 -10.341791,0.01017 -0.01017,-10.341796 -5.169431,0.0051 0.01017,10.341795 -10.341792,0.01018 -0.01017,-10.341797 -5.172361,0.0051 0.01017,10.341795 -5.170894,0.0051 z" />
|
||||||
|
<rect
|
||||||
|
x="38.146"
|
||||||
|
y="38.512001"
|
||||||
|
width="51.708"
|
||||||
|
height="51.708"
|
||||||
|
fill="rgb(77,77,77)"
|
||||||
|
id="rect11"
|
||||||
|
style="fill:none" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
d="m 38.016104,57.595764 h 6.037 l 11.263,-31.267 h -5.947 l -8.064,24.374 -7.75,-24.374 h -6.758 z"
|
||||||
|
fill="#dddddd"
|
||||||
|
id="path849"
|
||||||
|
style="isolation:isolate;stroke:#000000;stroke-opacity:1;stroke-width:0.5;stroke-miterlimit:4;stroke-dasharray:none;fill:#ffffff" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- Generator: Gravit.io --><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="141 297.96 507.339 471.04" width="507.339pt" height="471.04pt"><g><g><g><g><path d=" M 537.075 443.463 L 537.075 443.463 L 564.723 427.335 C 504.903 324.571 375.966 285.313 269.027 337.304 L 269.027 297.96 L 237.027 297.96 L 237.027 393.96 L 333.027 393.96 L 333.027 361.96 L 292.147 361.96 C 382.665 323.563 487.612 358.486 537.075 443.463 Z " fill="rgb(204,64,64)"/><path d=" M 245.027 692.775 C 170.84 632.524 151.314 527.353 198.931 444.488 L 171.187 428.488 C 116.876 523.063 137.426 642.887 220.147 713.96 L 181.027 713.96 L 181.027 745.96 L 277.027 745.96 L 277.027 649.96 L 245.027 649.96 L 245.027 692.775 L 245.027 692.775 Z " fill="rgb(204,64,64)"/><path d=" M 580.451 499.048 L 512.579 566.936 L 535.203 589.56 L 562.403 562.36 C 551.734 661.49 468.201 736.724 368.499 737 L 368.499 769 C 487.289 768.688 585.833 677.011 594.707 558.552 L 625.715 589.56 L 648.339 566.936 L 580.451 499.048 Z " fill="rgb(204,64,64)"/></g></g></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rog_anime"
|
name = "rog_anime"
|
||||||
version = "1.0.4"
|
version = "1.1.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>"]
|
||||||
|
|||||||
|
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 |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
@@ -1,7 +1,18 @@
|
|||||||
|
use std::{
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
thread::sleep,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
#[cfg(feature = "dbus")]
|
#[cfg(feature = "dbus")]
|
||||||
use zvariant_derive::Type;
|
use zvariant_derive::Type;
|
||||||
|
|
||||||
|
use crate::{error::AnimeError, AnimTime, AnimeGif};
|
||||||
|
|
||||||
/// The first 7 bytes of a USB packet are accounted for by `USB_PREFIX1` and `USB_PREFIX2`
|
/// The first 7 bytes of a USB packet are accounted for by `USB_PREFIX1` and `USB_PREFIX2`
|
||||||
const BLOCK_START: usize = 7;
|
const BLOCK_START: usize = 7;
|
||||||
/// *Not* inclusive, the byte before this is the final for each "pane"
|
/// *Not* inclusive, the byte before this is the final for each "pane"
|
||||||
@@ -79,3 +90,94 @@ impl From<AnimeDataBuffer> for AnimePacketType {
|
|||||||
buffers
|
buffers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This runs the animations as a blocking loop by using the `callback` to write data
|
||||||
|
pub fn run_animation(
|
||||||
|
frames: &AnimeGif,
|
||||||
|
do_early_return: Arc<AtomicBool>,
|
||||||
|
callback: &dyn Fn(AnimeDataBuffer) -> Result<(), AnimeError>,
|
||||||
|
) -> Result<(), AnimeError> {
|
||||||
|
let mut count = 0;
|
||||||
|
let start = Instant::now();
|
||||||
|
|
||||||
|
let mut timed = false;
|
||||||
|
let mut run_time = frames.total_frame_time();
|
||||||
|
if let AnimTime::Fade(time) = frames.duration() {
|
||||||
|
if let Some(middle) = time.show_for() {
|
||||||
|
run_time = middle + time.total_fade_time();
|
||||||
|
}
|
||||||
|
// add a small buffer
|
||||||
|
run_time += Duration::from_millis(250);
|
||||||
|
timed = true;
|
||||||
|
} else if let AnimTime::Time(time) = frames.duration() {
|
||||||
|
run_time = time;
|
||||||
|
timed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// After setting up all the data
|
||||||
|
let mut fade_in = Duration::from_millis(0);
|
||||||
|
let mut fade_out = Duration::from_millis(0);
|
||||||
|
let mut fade_in_step = 0.0;
|
||||||
|
let mut fade_in_accum = 0.0;
|
||||||
|
let mut fade_out_step = 0.0;
|
||||||
|
let mut fade_out_accum;
|
||||||
|
if let AnimTime::Fade(time) = frames.duration() {
|
||||||
|
fade_in = time.fade_in();
|
||||||
|
fade_out = time.fade_out();
|
||||||
|
fade_in_step = 1.0 / fade_in.as_secs_f32();
|
||||||
|
fade_out_step = 1.0 / fade_out.as_secs_f32();
|
||||||
|
|
||||||
|
if time.total_fade_time() > run_time {
|
||||||
|
println!("Total fade in/out time larger than gif run time. Setting fades to half");
|
||||||
|
fade_in = run_time / 2;
|
||||||
|
fade_in_step = 1.0 / (run_time / 2).as_secs_f32();
|
||||||
|
|
||||||
|
fade_out = run_time / 2;
|
||||||
|
fade_out_step = 1.0 / (run_time / 2).as_secs_f32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
'animation: loop {
|
||||||
|
for frame in frames.frames() {
|
||||||
|
let frame_start = Instant::now();
|
||||||
|
if do_early_return.load(Ordering::SeqCst) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let mut output = frame.frame().clone();
|
||||||
|
|
||||||
|
if let AnimTime::Fade(_) = frames.duration() {
|
||||||
|
if frame_start <= start + fade_in {
|
||||||
|
for pixel in output.get_mut() {
|
||||||
|
*pixel = (*pixel as f32 * fade_in_accum) as u8;
|
||||||
|
}
|
||||||
|
fade_in_accum = fade_in_step * (frame_start - start).as_secs_f32();
|
||||||
|
} else if frame_start > (start + run_time) - fade_out {
|
||||||
|
if run_time > (frame_start - start) {
|
||||||
|
fade_out_accum =
|
||||||
|
fade_out_step * (run_time - (frame_start - start)).as_secs_f32();
|
||||||
|
} else {
|
||||||
|
fade_out_accum = 0.0;
|
||||||
|
}
|
||||||
|
for pixel in output.get_mut() {
|
||||||
|
*pixel = (*pixel as f32 * fade_out_accum) as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(output)?;
|
||||||
|
|
||||||
|
if timed && Instant::now().duration_since(start) > run_time {
|
||||||
|
break 'animation;
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(frame.delay());
|
||||||
|
}
|
||||||
|
if let AnimTime::Count(times) = frames.duration() {
|
||||||
|
count += 1;
|
||||||
|
if count >= times {
|
||||||
|
break 'animation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ impl AnimeDiagonal {
|
|||||||
duration: Option<Duration>,
|
duration: Option<Duration>,
|
||||||
bright: f32,
|
bright: f32,
|
||||||
) -> Result<Self, AnimeError> {
|
) -> Result<Self, AnimeError> {
|
||||||
use pix::el::Pixel;
|
|
||||||
let data = std::fs::read(path)?;
|
let data = std::fs::read(path)?;
|
||||||
let data = std::io::Cursor::new(data);
|
let data = std::io::Cursor::new(data);
|
||||||
let decoder = png_pong::Decoder::new(data)?.into_steps();
|
let decoder = png_pong::Decoder::new(data)?.into_steps();
|
||||||
@@ -56,22 +55,78 @@ impl AnimeDiagonal {
|
|||||||
|
|
||||||
let mut matrix = AnimeDiagonal::new(duration);
|
let mut matrix = AnimeDiagonal::new(duration);
|
||||||
|
|
||||||
let width;
|
|
||||||
match raster {
|
match raster {
|
||||||
|
png_pong::PngRaster::Gray8(ras) => {
|
||||||
|
Self::pixels_from_8bit(ras, &mut matrix, bright, true)
|
||||||
|
}
|
||||||
png_pong::PngRaster::Graya8(ras) => {
|
png_pong::PngRaster::Graya8(ras) => {
|
||||||
width = ras.width();
|
Self::pixels_from_8bit(ras, &mut matrix, bright, true)
|
||||||
for (y, row) in ras.pixels().chunks(width as usize).enumerate() {
|
}
|
||||||
for (x, px) in row.iter().enumerate() {
|
png_pong::PngRaster::Rgb8(ras) => {
|
||||||
let v = <u8>::from(px.one() * bright);
|
Self::pixels_from_8bit(ras, &mut matrix, bright, false)
|
||||||
matrix.0[y][x] = v;
|
}
|
||||||
}
|
png_pong::PngRaster::Rgba8(ras) => {
|
||||||
}
|
Self::pixels_from_8bit(ras, &mut matrix, bright, false)
|
||||||
|
}
|
||||||
|
png_pong::PngRaster::Gray16(ras) => {
|
||||||
|
Self::pixels_from_16bit(ras, &mut matrix, bright, true)
|
||||||
|
}
|
||||||
|
png_pong::PngRaster::Rgb16(ras) => {
|
||||||
|
Self::pixels_from_16bit(ras, &mut matrix, bright, false)
|
||||||
|
}
|
||||||
|
png_pong::PngRaster::Graya16(ras) => {
|
||||||
|
Self::pixels_from_16bit(ras, &mut matrix, bright, true)
|
||||||
|
}
|
||||||
|
png_pong::PngRaster::Rgba16(ras) => {
|
||||||
|
Self::pixels_from_16bit(ras, &mut matrix, bright, false)
|
||||||
}
|
}
|
||||||
_ => return Err(AnimeError::Format),
|
_ => return Err(AnimeError::Format),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(matrix)
|
Ok(matrix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pixels_from_8bit<P>(ras: pix::Raster<P>, matrix: &mut AnimeDiagonal, bright: f32, grey: bool)
|
||||||
|
where
|
||||||
|
P: pix::el::Pixel<Chan = pix::chan::Ch8>,
|
||||||
|
{
|
||||||
|
let width = ras.width();
|
||||||
|
for (y, row) in ras.pixels().chunks(width as usize).enumerate() {
|
||||||
|
for (x, px) in row.iter().enumerate() {
|
||||||
|
let v = if grey {
|
||||||
|
<u8>::from(px.one()) as f32
|
||||||
|
} else {
|
||||||
|
(<u8>::from(px.one()) / 3) as f32
|
||||||
|
+ (<u8>::from(px.two()) / 3) as f32
|
||||||
|
+ (<u8>::from(px.three()) / 3) as f32
|
||||||
|
};
|
||||||
|
matrix.0[y][x] = (v * bright) as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pixels_from_16bit<P>(
|
||||||
|
ras: pix::Raster<P>,
|
||||||
|
matrix: &mut AnimeDiagonal,
|
||||||
|
bright: f32,
|
||||||
|
grey: bool,
|
||||||
|
) where
|
||||||
|
P: pix::el::Pixel<Chan = pix::chan::Ch16>,
|
||||||
|
{
|
||||||
|
let width = ras.width();
|
||||||
|
for (y, row) in ras.pixels().chunks(width as usize).enumerate() {
|
||||||
|
for (x, px) in row.iter().enumerate() {
|
||||||
|
let v = if grey {
|
||||||
|
(<u16>::from(px.one()) >> 8) as f32
|
||||||
|
} else {
|
||||||
|
((<u16>::from(px.one()) / 3) >> 8) as f32
|
||||||
|
+ ((<u16>::from(px.two()) / 3) >> 8) as f32
|
||||||
|
+ ((<u16>::from(px.three()) / 3) >> 8) as f32
|
||||||
|
};
|
||||||
|
matrix.0[y][x] = (v * bright) as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&AnimeDiagonal> for AnimeDataBuffer {
|
impl From<&AnimeDiagonal> for AnimeDataBuffer {
|
||||||
|
|||||||