Compare commits

...

99 Commits
3.6.2 ... 4.0.5

Author SHA1 Message Date
Luke D. Jones
a925cbaed5 Bump version 2021-10-27 23:19:31 +13:00
Luke Jones
7d2201d873 Merge branch 'fluke/bugfixes' into 'main'
Add led modes for G513QR

Closes #144 and #143

See merge request asus-linux/asusctl!86
2021-10-27 10:18:31 +00:00
Luke D. Jones
c0c1608d44 Update deps 2021-10-27 23:15:25 +13:00
Luke D. Jones
7bc6c83a04 Check and pass error if charge limit not in 20-100 range
Closes #144
2021-10-27 23:04:44 +13:00
Luke D. Jones
3f0df82f2d Parse percentages in fan curve only if '%' provided otherwise range is 0-255 2021-10-27 22:42:32 +13:00
Luke D. Jones
328ff0251b Add led modes for G513QR
Closes #143
2021-10-27 22:35:48 +13:00
Luke Jones
c52582a413 Merge branch 'fluke/bugfixes' into 'main'
Bugfixes

Closes #139 and #140

See merge request asus-linux/asusctl!85
2021-10-02 07:34:55 +00:00
Luke D. Jones
3aa6eee306 Bugfixes
- Spawn tasks on individual threads
- Don't force a default of fan-curve on reload
- Add missing profile commands
- Begin obsoleting the graphics switch command in favour of supergfxctl
- Slim down the notification daemon to pure ASUS notifications

Bad behaviour in fan-curve new function that was forcing a re-init
to default on reload. Remove this and only save config again after
loading the config file and writing a curve (hidden side effect of
write is that a zeroed array is defaulted to read-from-system - this
needs to be changed too).

Closes #140, #139
2021-10-02 20:31:14 +13:00
Luke Jones
7d47faba0e Merge branch 'plasma-logout' into 'main'
Add kde logout prompt as fallback to gnome-session-quit

See merge request asus-linux/asusctl!82
2021-09-21 08:07:43 +00:00
Luke Jones
f6fb477898 Merge branch 'main' into 'main'
Updated asusd-ledmodes.toml with ROG Strix G713QM

See merge request asus-linux/asusctl!83
2021-09-21 08:06:59 +00:00
Abhijith M
8963960d4b Updated asusd-ledmodes.toml with ROG Strix G713QM 2021-09-21 02:09:53 +00:00
Janghyub Seo
ef973f676b Add kde logout prompt as fallback to gnome-session-quit 2021-09-19 02:13:11 +09:00
Luke D. Jones
812f9ea30e Add lock update 2021-09-16 22:38:09 +12:00
Luke Jones
ff843b1241 Merge branch 'main' into 'main'
Add G533QS to supported models

See merge request asus-linux/asusctl!81
2021-09-16 10:30:09 +00:00
George Dumitrescu
59e7af149d Add G533QS to supported models 2021-09-16 10:07:48 +03:00
Luke D. Jones
6f14c85287 Revert supergfxctl deps to git 2021-09-16 16:03:41 +12:00
Luke D. Jones
ac0dec4dbf Bump daemon version for release 2021-09-16 11:20:17 +12:00
Luke D. Jones
e3d192412e Bugfixes 2021-09-16 11:19:05 +12:00
Luke Jones
9fadb6db30 Merge branch 'necessary129-main-patch-69023' into 'main'
Add another Strix G17 model

See merge request asus-linux/asusctl!80
2021-09-15 00:06:31 +00:00
Shamil K
f8a1b71866 Add another Strix G17 model 2021-09-14 12:00:04 +00:00
Luke Jones
c0a55acba7 Merge branch 'main' into 'main'
Adding ROG Flow X13 to LED modes known devices

See merge request asus-linux/asusctl!79
2021-09-14 11:45:56 +00:00
Joseph Ferano
4f232de634 Adding ROG Flow X13 to LED modes known devices 2021-09-14 18:05:42 +07:00
Luke Jones
d351ebdaa0 Merge branch 'fluke/fan_curves_v13' into 'main'
Fluke/fan curves v13

See merge request asus-linux/asusctl!78
2021-09-14 02:55:51 +00:00
Luke D. Jones
ab195e1d84 Fan curve enablement
- Add CtrlProfileTask
- Add method to reset active profile curve to platform default
- Wrap the zbus methods for profiles + fan curves
- Enable CLI args for fan curves
- CLI mod and save curves
2021-09-14 14:52:15 +12:00
Luke D. Jones
7041d77256 Fix asusd-ledmodes.toml 2021-09-11 17:03:32 +12:00
Luke D. Jones
99dd052d54 Bump version in readme 2021-09-11 00:27:21 +12:00
Luke Jones
de9942609e Merge branch 'fluke/fan_curves_v9' into 'main'
Fluke/fan curves v9

See merge request asus-linux/asusctl!77
2021-09-10 12:26:14 +00:00
Luke D. Jones
b939a9d331 Fetch and store fan curve correctly 2021-09-11 00:25:46 +12:00
Luke D. Jones
0a565a7a5c Fleshing out functions and zbus controls 2021-09-08 23:33:42 +12:00
Luke D. Jones
bfaa478a4a Begin syncing changes with patch series 2021-09-07 20:20:37 +12:00
Luke Jones
f895a5623e Merge branch 'doc-ubuntu' into 'main'
Add ubuntu repo install commands

See merge request asus-linux/asusctl!76
2021-09-06 03:48:19 +00:00
Guy Sheffer
b7c869bd64 Add ubuntu repo install commands 2021-09-05 10:55:16 +03:00
Luke D. Jones
5f677bc3b9 Provide extra help tips. Update manual 2021-08-29 21:30:30 +12:00
Luke D. Jones
ccfadc2fcb Updates to asusctl cli app 2021-08-28 11:47:01 +12:00
Luke D. Jones
c6cc304a42 Clean up unwrap()'s. Print out info in asusctl if error 2021-08-28 11:07:47 +12:00
Luke D. Jones
3d41a7978a Fix crash when platform_profile not supported
Closes #130
2021-08-28 09:33:23 +12:00
Luke D. Jones
43fc467d58 Fix rm of not installed components 2021-08-28 00:25:51 +12:00
Luke D. Jones
6aba60f604 Try to re-get AniMe dev handle on write error 2021-08-27 22:31:50 +12:00
Luke D. Jones
a13dedf500 Cleanup 2021-08-27 20:46:04 +12:00
dragonn
cb490f23e9 Requesting dbus name after finishing initalizaction 2021-08-27 20:45:37 +12:00
Luke Jones
7dc8a743b9 Merge branch 'fluke/platform_curves' into 'main'
platform_profile + fan curve support

Closes #129 and #128

See merge request asus-linux/asusctl!75
2021-08-27 08:27:10 +00:00
Luke D. Jones
52f3b5a7bf Charge limit: try to support BAT<n>
Closes #128
2021-08-27 20:22:15 +12:00
Luke D. Jones
e89e7ca10f Add LED brightness pre/next cycle
Closes #129
2021-08-27 20:15:15 +12:00
Luke D. Jones
2431dd9e93 Remove supergfxctl to own repo 2021-08-26 16:06:57 +12:00
Luke D. Jones
453d3091c1 Bump major versions 2021-08-26 14:23:28 +12:00
Luke D. Jones
8db37491f3 Mention breaking changes in log 2021-08-26 13:57:19 +12:00
Luke D. Jones
cf915b9e00 Move anime data. Twiddle supergfxctl 2021-08-26 13:53:54 +12:00
Luke D. Jones
326ca37847 rog-supported crate 2021-08-26 13:17:17 +12:00
Luke D. Jones
498e604531 Major update to supergfx and others 2021-08-26 11:44:11 +12:00
Luke D. Jones
60b7f3be69 Begin cleanup 2021-08-25 11:42:32 +12:00
Luke D. Jones
6ceb5cf939 Major restructure to move gfx control out to crate 2021-08-25 11:16:23 +12:00
Luke D. Jones
0ed97db4c1 Temporary checkpoint 2021-08-24 12:35:57 +12:00
Luke D. Jones
8fcd05c2bb Append small extra help to bios option cli 2021-08-12 10:35:37 +12:00
Luke D. Jones
6de4590f27 Update fan curve to use git repo 2021-08-12 10:21:09 +12:00
Luke Jones
b097fd4da9 Merge branch 'fluke/anime' into 'main'
AniMe: more png colour type support

Closes #122 and #121

See merge request asus-linux/asusctl!73
2021-08-11 08:31:33 +00:00
Luke D. Jones
2a8e05707d AniMe: more png colour type support
Closes #121, #122
2021-08-11 20:27:44 +12:00
Luke Jones
a54e112978 Merge branch 'no_star_for_you' into 'main'
Remove Star from G513IH supported modes

See merge request asus-linux/asusctl!72
2021-08-04 01:04:57 +00:00
pshem
5785eb981d Remove Star from G513IH supported modes 2021-08-02 10:06:48 +01:00
Luke Jones
49234af08b Merge branch 'fluke/profiles' into 'main'
Fluke/profiles

See merge request asus-linux/asusctl!71
2021-08-01 22:53:38 +00:00
Luke D. Jones
8f5717def8 Version bump, various fixes 2021-08-02 10:50:17 +12:00
Luke D. Jones
9e55c0b2ca Fix incorrect name match of Star keyboard effect 2021-08-02 10:28:50 +12:00
Luke Jones
dd6ee91364 Merge branch 'main' into 'main'
Enable multizone support on Strix 513IH

See merge request asus-linux/asusctl!69
2021-08-01 22:03:30 +00:00
Luke Jones
efbb838ca1 Merge branch 'G513QY-ledmodes' into 'main'
add G513QY ledmodes

See merge request asus-linux/asusctl!70
2021-08-01 21:27:59 +00:00
Sonny Piers
daf7d39d41 add G513QY ledmodes 2021-08-01 23:12:59 +02:00
pshem
28b1194924 Fix multizone support on Strix 513IH 2021-08-01 18:00:04 +01:00
Luke Jones
73d95ca187 Merge branch 'fluke/profiles' into 'main'
fix: Profile select by name is correctly choosing

Closes #96 and #100

See merge request asus-linux/asusctl!68
2021-07-31 12:19:28 +00:00
Luke D. Jones
00b7c6482f fix: remove the parsing for root supported functions 2021-08-01 00:15:38 +12:00
Luke D. Jones
29c26e8c89 fix: Profile select by name is correctly choosing
Closes #100 #96
2021-08-01 00:10:11 +12:00
Luke D. Jones
fb124dd228 Fix clippy warnings 2021-07-31 23:09:23 +12:00
Luke D. Jones
0599be02dc fix: remove dbg!() output 2021-07-31 23:03:17 +12:00
Luke Jones
81a88263a9 Merge branch 'fluke/cli-fixes' into 'main'
Fluke/cli fixes

See merge request asus-linux/asusctl!67
2021-07-31 11:00:43 +00:00
Luke D. Jones
cea1fd2540 Fix small issues with CLI 2021-07-31 22:57:17 +12:00
Luke Jones
f2ced3bc7c Merge branch 'main' into 'main'
Add Zephyrus M GM501GS

Closes #109

See merge request asus-linux/asusctl!66
2021-07-30 23:48:58 +00:00
pshem
dc2a05894b Add Zephyrus M GM501GS 2021-07-30 17:45:00 +01:00
Luke D. Jones
9ee962ad09 systemd: sleep 2 after unit init 2021-07-30 14:35:44 +12:00
Luke Jones
eb7ef0af4f Merge branch 'main' into 'main'
add a protection to not allow running asusd in terminal

Closes #104

See merge request asus-linux/asusctl!64
2021-07-30 00:12:17 +00:00
Luke Jones
dc51120c27 Merge branch 'main' into 'main'
Add G533QR and 513IH to ledmodes

See merge request asus-linux/asusctl!65
2021-07-30 00:11:05 +00:00
pshem
fc4c2c4346 Add support for 513IH led modes 2021-07-24 19:34:09 +01:00
pshem
3f6be037c1 Add G533QR to ledmodes 2021-07-24 14:39:11 +01:00
Luke Jones
0a80c97f02 Update README.md 2021-07-13 04:36:39 +00:00
dragonn
88abafc728 add a protection to not allow running asusd in terminal 2021-07-09 18:40:38 +02:00
Luke Jones
ac880a0363 Merge branch 'fluke/gfx-mode-changes' into 'main'
Fluke/gfx mode changes

See merge request asus-linux/asusctl!63
2021-06-10 21:06:56 +00:00
Luke D. Jones
baebd51d99 Better control of gfx modes 2021-06-11 09:03:50 +12:00
Luke D. Jones
226620eb53 Fix up modes and icons 2021-06-07 19:19:49 +12:00
Luke D. Jones
1b34079d14 Trial of blocking vfio/compute unless in integrated mode 2021-06-07 11:02:21 +12:00
Luke D. Jones
8deeffcdad Minor fix to compute/vfio switch 2021-06-06 21:33:31 +12:00
Luke D. Jones
b7e45d7305 Bump versions for release 2021-06-06 21:02:07 +12:00
Luke D. Jones
d9077db234 Bugfix configs 2021-06-06 20:58:19 +12:00
Luke D. Jones
439b006342 Force change to integrated if in nvidia or hybrid mode
Force change to integrated if in nvidia or hybrid mode and user tries
to switch to vfio or compute
2021-06-06 13:44:34 +12:00
Luke D. Jones
ffa74d52e5 Fix LED brightness apply on resume 2021-06-06 12:12:17 +12:00
Luke Jones
6ccdd703e6 Merge branch 'fluke/anime-fade' into 'main'
Fluke/anime fade

See merge request asus-linux/asusctl!62
2021-05-31 01:07:17 +00:00
Luke D. Jones
bb910344b8 Basic fade in/out of gifs 2021-05-31 10:06:35 +12:00
Luke D. Jones
b9c4ff9ca7 Add GA503Q led modes 2021-05-30 10:20:17 +12:00
Luke D. Jones
62a18d4e57 Minor cleanup of bios controller 2021-05-29 15:33:47 +12:00
Luke D. Jones
f520e381a9 Attempt to provide more info to users gfx switching 2021-05-29 11:42:15 +12:00
Luke D. Jones
1dd543ddf3 Simplify the notifier 2021-05-27 13:22:08 +12:00
Luke Jones
a7ef63bd8a Merge branch 'formatted_supported_functions_message' into 'main'
Improving message formatting of supported functions

See merge request asus-linux/asusctl!61
2021-05-26 19:44:47 +00:00
KoStard
db43c0f2a4 Improving message formatting of supported functions
- Implementing fmt::Display for SupportedFunctions and sub-structs
2021-05-26 20:09:44 +04:00
Luke D. Jones
f0e5bb4ad1 dbus: send/recv notifications for bios options 2021-05-26 21:17:45 +12:00
126 changed files with 3688 additions and 3664 deletions

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@ cargo-config
.idea
vendor-*
vendor_*
.vscode-ctags

View File

@@ -6,6 +6,89 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [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
### Changed
- Bugfix: write correct fan modes for profiles

606
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
[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]
lto = true
@@ -13,4 +13,4 @@ opt-level = 1
[profile.bench]
debug = false
opt-level = 3
opt-level = 3

142
MANUAL.md
View File

@@ -16,57 +16,15 @@ but can also be used with non-asus laptops with reduced features.
The functionality that `asusd` exposes is:
- graphics switching
- anime control
- led keyboard control (aura)
- charge limiting
- 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.
### 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
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 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)
- CPU turbo boost enable or disable
- Fan presets. These are 0: Normal, 1: Boost, 2: Silent.
- Fan curves, override fan-preset. AMD only.
#### 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.
A common use of asusctl is to bind the `fn+f5` (fan) key to `asusctl profile -n` to cycle through the 3 profiles:
1. Balanced
2. Performance
3. Quiet
#### 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
@@ -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` can play *any* gif of any size.
@@ -269,6 +211,7 @@ Each object in the array can be one of:
<FLOAT>,
<FLOAT>
],
"time": <TIME>,
"brightness": <FLOAT>
}
},
@@ -318,10 +261,31 @@ A cycle is how many gif loops to run:
"Cycles": 2
},
```
`Infinite` means that this gif will never end:
`Infinite` means that this gif will never end:
```json
"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>**

View File

@@ -16,8 +16,6 @@ BIN_D := asusd
BIN_U := asusd-user
BIN_N := asus-notify
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')
@@ -45,35 +43,48 @@ install:
$(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_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/$(LEDCFG)" "$(DESTDIR)/etc/asusd/$(LEDCFG)"
$(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_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/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_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/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:
rm -f "$(DESTDIR)$(bindir)/$(BIN_C)"
rm -f "$(DESTDIR)$(bindir)/$(BIN_D)"
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)/etc/asusd/$(LEDCFG)"
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 -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_green.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)$(datarootdir)/fish/vendor_completions.d/asusctl.fish"
rm -rf "$(DESTDIR)$(datarootdir)/asusd"

View File

@@ -5,6 +5,11 @@
`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.
## Kernel patches required
1. https://lkml.org/lkml/2021/8/20/232
2. https://lkml.org/lkml/2021/8/18/1022
## 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.
@@ -25,7 +30,7 @@ Various patches are required for keyboard support. See [this post](https://asus-
## Discord
[Discord server link](https://discord.gg/ngbdKabAnP)
[Discord server link](https://discord.gg/4ZKGd7Un5t)
## 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] Fancy LED modes (See examples) (currently being reworked)
- [X] Saving settings for reload
- [X] Logging - required for journalctl
- [X] AniMatrix display on G14 models that include it (currently being reworked)
- [X] AniMatrix display on G14 models that include it
- [X] Set battery charge limit (with kernel supporting this)
- [X] Fan curve control on G14 + G15 thanks to @Yarn1
- [X] Graphics mode switching between iGPU, dGPU, on-demand, and vfio (for VM pass-through)
+ [X] Requires only a logout/login
- [X] Fan curve control on G14 + G15. Requires kernel patch (should reach 5.15 kernel)
- [X] Toggle bios setting for boot/POST sound
- [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`.
**Ubuntu*:** `apt install libclang-dev libudev-dev`
**Ubuntu:** `apt install libclang-dev libudev-dev`
**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.
### 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.
@@ -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.
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
Run `sudo make uninstall` in the source repo, and remove `/etc/asusd/`.
@@ -111,4 +116,4 @@ Reference to any ASUS products, services, processes, or other information and/or
The use of ROG and ASUS trademarks within this website and associated tools and libraries is only to provide a recognisable identifier to users to enable them to associate that these tools will work with ASUS ROG laptops.
---
---

View File

@@ -1,20 +1,21 @@
[package]
name = "asus-notify"
version = "3.0.0"
version = "3.0.2"
authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
zbus = "^1.9"
# serialisation
serde_json = "^1.0"
rog_dbus = { path = "../rog-dbus" }
rog_aura = { path = "../rog-aura" }
rog_supported = { path = "../rog-supported" }
rog_profiles = { path = "../rog-profiles" }
rog_types = { path = "../rog-types" }
daemon = { path = "../daemon" }
[dependencies.notify-rust]
version = "^4.3"
default-features = false
features = ["z"]
features = ["z"]

View File

@@ -1,25 +1,46 @@
use notify_rust::{Hint, Notification, NotificationHandle};
use rog_aura::AuraEffect;
use rog_dbus::{DbusProxies, Signals};
use rog_profiles::profiles::{FanLevel, Profile};
use rog_profiles::Profile;
use std::error::Error;
use std::thread::sleep;
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>> {
println!("asus-notify version {}", env!("CARGO_PKG_VERSION"));
println!(" daemon version {}", daemon::VERSION);
println!(" rog-dbus version {}", rog_dbus::VERSION);
let (proxies, conn) = DbusProxies::new()?;
let signals = Signals::new(&proxies)?;
let mut last_profile_notif: 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 mut last_notification: Option<NotificationHandle> = None;
let recv = proxies.setup_recv(conn);
let mut err_count = 0;
loop {
sleep(Duration::from_millis(100));
if let Err(err) = recv.next_signal() {
@@ -36,60 +57,30 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}
err_count = 0;
if let Ok(vendor) = signals.gfx_vendor.try_recv() {
if let Some(notif) = last_gfx_notif.take() {
notif.close();
}
let x = do_notif(&format!(
"Graphics mode changed to {}",
<&str>::from(vendor)
))?;
last_gfx_notif = Some(x);
if let Ok(data) = signals.led_mode.try_recv() {
notify!(do_led_notif, last_notification, &data);
}
if let Ok(limit) = signals.charge.try_recv() {
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.profile.try_recv() {
notify!(do_thermal_notif, last_notification, &data);
}
if let Ok(profile) = signals.profile.try_recv() {
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);
if let Ok(data) = signals.charge.try_recv() {
notify!(do_charge_notif, last_notification, &data);
}
}
}
fn do_thermal_notif(profile: &Profile) -> Result<NotificationHandle, Box<dyn Error>> {
let fan = profile.fan_preset;
let turbo = if profile.turbo { "enabled" } else { "disabled" };
let icon = match fan {
FanLevel::Normal => "asus_notif_yellow",
FanLevel::Boost => "asus_notif_red",
FanLevel::Silent => "asus_notif_green",
let icon = match profile {
Profile::Balanced => "asus_notif_yellow",
Profile::Performance => "asus_notif_red",
Profile::Quiet => "asus_notif_green",
};
let profile: &str = (*profile).into();
let x = Notification::new()
.summary("ASUS ROG")
.body(&format!(
"Thermal profile changed to {}, turbo {}",
profile.name.to_uppercase(),
turbo
"Thermal profile changed to {}",
profile.to_uppercase(),
))
.hint(Hint::Resident(true))
.timeout(2000)
@@ -100,11 +91,14 @@ fn do_thermal_notif(profile: &Profile) -> Result<NotificationHandle, Box<dyn Err
Ok(x)
}
fn do_notif(body: &str) -> Result<NotificationHandle, Box<dyn Error>> {
let x = Notification::new()
.summary("ASUS ROG")
.body(body)
.timeout(2000)
.show()?;
Ok(x)
fn do_led_notif(ledmode: &AuraEffect) -> Result<NotificationHandle, notify_rust::error::Error> {
base_notification!(&format!(
"Keyboard LED mode changed to {}",
ledmode.mode_name()
))
}
fn do_charge_notif(limit: &u8) -> Result<NotificationHandle, notify_rust::error::Error> {
base_notification!(&format!("Battery charge limit changed to {}", limit))
}

View File

@@ -1,23 +1,26 @@
[package]
name = "asusctl"
version = "3.5.0"
version = "4.0.5"
authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
zbus = "^1.9.1"
rog_anime = { path = "../rog-anime" }
rog_aura = { path = "../rog-aura" }
rog_dbus = { path = "../rog-dbus" }
rog_profiles = { path = "../rog-profiles" }
rog_types = { path = "../rog-types" }
rog_fan_curve = { version = "^0.1", features = ["serde"] }
rog_supported = { path = "../rog-supported" }
daemon = { path = "../daemon" }
gumdrop = "^0.8"
yansi-term = "^0.1"
toml = "^0.5.8"
sysfs-class = "^0.1.2"
[dev-dependencies]
tinybmp = "^0.2.3"
glam = "0.14.0"
rog_dbus = { path = "../rog-dbus" }
gif = "^0.11.2"
gif = "^0.11.2"

View File

@@ -1,6 +1,6 @@
use std::{env, path::Path, thread::sleep};
use rog_anime::{ActionData, AnimeAction, Sequences};
use rog_anime::{ActionData, ActionLoader, Sequences};
use rog_dbus::RogDbusClient;
fn main() {
@@ -17,7 +17,7 @@ fn main() {
let mut seq = Sequences::new();
seq.insert(
0,
&AnimeAction::AsusAnimation {
&ActionLoader::AsusAnimation {
file: path.into(),
time: rog_anime::AnimTime::Infinite,
brightness,

View File

@@ -69,7 +69,7 @@ pub struct AnimeCommand {
pub enum AnimeActions {
#[options(help = "change all leds brightness")]
Leds(AnimeLeds),
#[options(help = "display an 8bit greyscale png")]
#[options(help = "display an image png")]
Image(AnimeImage),
}

View File

@@ -124,6 +124,8 @@ pub struct MultiColourSpeed {
/// Byte value for setting the built-in mode.
///
/// Enum corresponds to the required integer value
///
// NOTE: The option names here must match those in rog-aura crate
#[derive(Options)]
pub enum SetAuraBuiltin {
#[options(help = "set a single static colour")]
@@ -135,7 +137,7 @@ pub enum SetAuraBuiltin {
#[options(help = "rainbow cycling in one of four directions")]
Rainbow(SingleSpeedDirection),
#[options(help = "rain pattern mimicking raindrops")]
Star(TwoColourSpeed),
Stars(TwoColourSpeed),
#[options(help = "rain pattern of three preset colours")]
Rain(SingleSpeed),
#[options(help = "pressed keys are highlighted to fade")]
@@ -233,7 +235,7 @@ impl From<&SetAuraBuiltin> for AuraEffect {
data.mode = AuraModeNum::Rainbow;
data
}
SetAuraBuiltin::Star(x) => {
SetAuraBuiltin::Stars(x) => {
let mut data: AuraEffect = x.into();
data.mode = AuraModeNum::Star;
data

92
asusctl/src/cli_opts.rs Normal file
View File

@@ -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,
}

View File

@@ -1,112 +1,26 @@
mod anime_cli;
mod aura_cli;
mod cli_opts;
mod profiles_cli;
use crate::aura_cli::{LedBrightness, SetAuraBuiltin};
use crate::cli_opts::*;
use anime_cli::{AnimeActions, AnimeCommand};
use gumdrop::{Opt, Options};
use profiles_cli::ProfileCommand;
use profiles_cli::{FanCurveCommand, ProfileCommand};
use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2, ANIME_DATA_LEN};
use rog_aura::{self, AuraEffect};
use rog_dbus::RogDbusClient;
use rog_profiles::profiles::Profile;
use rog_types::{
gfx_vendors::GfxVendors,
supported::{
FanCpuSupportedFunctions, LedSupportedFunctions, RogBiosSupportedFunctions,
SupportedFunctions,
},
use rog_profiles::error::ProfileError;
use rog_supported::SupportedFunctions;
use rog_supported::{
AnimeSupportedFunctions, LedSupportedFunctions, PlatformProfileFunctions,
RogBiosSupportedFunctions,
};
use std::process::Command;
use std::{env::args, path::Path};
use yansi_term::Colour::Green;
use yansi_term::Colour::Red;
#[derive(Default, Options)]
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,
}
const CONFIG_ADVICE: &str = "A config file need to be removed so a new one can be generated";
fn main() -> Result<(), Box<dyn std::error::Error>> {
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()?;
if parsed.help {
print_supported_help(&supported, &parsed);
println!("\nSee https://asus-linux.org/faq/ for additional help");
std::process::exit(1);
}
let supported = dbus
.proxies()
.supported()
.get_supported_functions()
.map_err(|e| {
print_error_help(Box::new(e), None);
std::process::exit(4);
})
.unwrap();
if parsed.version {
println!(" asusctl v{}", env!("CARGO_PKG_VERSION"));
println!(" rog-dbus v{}", rog_dbus::VERSION);
println!("rog-types v{}", rog_types::VERSION);
print_versions();
println!();
print_laptop_info();
return Ok(());
}
match parsed.command {
Some(CliCommand::LedMode(mode)) => handle_led_mode(&dbus, &supported.keyboard_led, &mode)?,
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);
}
if let Err(err) = do_parsed(&parsed, &supported, &dbus) {
print_error_help(err, Some(&supported));
}
let matrix = AnimeImage::from_png(
Path::new(&image.path),
image.scale,
image.angle,
Vec2::new(image.x_pos, image.y_pos),
image.bright,
)?;
Ok(())
}
dbus.proxies()
.anime()
.write(<AnimeDataBuffer>::from(&matrix))
.unwrap();
}
}
}
fn print_error_help(err: Box<dyn std::error::Error>, supported: Option<&SupportedFunctions>) {
if do_diagnose("asusd") {
println!("\nError: {}\n", err);
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 => {
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
{
println!("{}", CliStart::usage());
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() {
None => {
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 {
let dat = dbus.proxies().supported().get_supported_functions()?;
println!("Supported laptop functions:\n{:?}", dat);
println!("Supported laptop functions:\n\n{}", supported);
}
if let Some(chg_limit) = parsed.chg_limit {
@@ -233,98 +195,60 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
fn print_supported_help(supported: &SupportedFunctions, parsed: &CliStart) {
// As help option don't work with `parse_args_default`
// we will call `parse_args_default_or_exit` instead
let usage: Vec<String> = parsed.self_usage().lines().map(|s| s.to_string()).collect();
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() -> Result<(), Box<dyn std::error::Error>> {
println!("Please use supergfxctl for graphics switching. supergfxctl is the result of making asusctl graphics switching generic so all laptops can use it");
println!("This command will be removed in future");
Ok(())
}
fn do_gfx(
fn handle_anime(
dbus: &RogDbusClient,
supported: &RogBiosSupportedFunctions,
command: GraphicsCommand,
_supported: &AnimeSupportedFunctions,
cmd: &AnimeCommand,
) -> Result<(), Box<dyn std::error::Error>> {
if command.mode.is_none() && !command.get && !command.pow && !command.force || command.help {
println!("{}", command.self_usage());
}
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);
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);
}
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 {
let res = dbus.proxies().gfx().gfx_get_mode()?;
println!("Current graphics mode: {}", <&str>::from(res));
if let Some(anime_turn) = cmd.turn {
dbus.proxies().anime().set_led_power(anime_turn.into())?
}
if command.pow {
let res = dbus.proxies().gfx().gfx_get_pwr()?;
match res {
rog_types::gfx_vendors::GfxPower::Active => {
println!("Current power status: {}", Red.paint(<&str>::from(&res)))
if let Some(anime_boot) = cmd.boot {
dbus.proxies()
.anime()
.set_system_animations(anime_boot.into())?
}
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(())
@@ -347,23 +271,21 @@ fn handle_led_mode(
println!("{}\n", mode.self_usage());
println!("Commands available");
let commands: Vec<String> = LedModeCommand::command_list()
.unwrap()
.lines()
.map(|s| s.to_string())
.collect();
for command in commands.iter().filter(|command| {
for mode in &supported.stock_led_modes {
if command.contains(<&str>::from(mode)) {
if let Some(cmdlist) = LedModeCommand::command_list() {
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_string()).collect();
for command in commands.iter().filter(|command| {
for mode in &supported.stock_led_modes {
if command.contains(&<&str>::from(mode).to_lowercase()) {
return true;
}
}
if supported.multizone_led_mode {
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");
@@ -410,109 +332,105 @@ fn handle_led_mode(
fn handle_profile(
dbus: &RogDbusClient,
supported: &FanCpuSupportedFunctions,
supported: &PlatformProfileFunctions,
cmd: &ProfileCommand,
) -> Result<(), Box<dyn std::error::Error>> {
if !cmd.next
&& !cmd.create // TODO
&& !cmd.list
&& cmd.profile.is_none()
&& !cmd.active_name
&& !cmd.active_data
&& !cmd.profiles_data
&& cmd.remove.is_none()
&& cmd.curve.is_none() // TODO
&& cmd.fan_preset.is_none() // TODO
&& cmd.turbo.is_none() // TODO
&& cmd.max_percentage.is_none() // TODO
&& cmd.min_percentage.is_none()
// TODO
{
if !supported.fan_curves {
println!("Profiles not supported by either this kernel or by the laptop.");
return Err(ProfileError::NotSupported.into());
}
println!("Warning: Profiles now depend on power-profiles-daemon v0.9+ which may not be in your install");
println!(" If you have unexpected behaviour or have only two profiles in your desktop control");
println!(" you need to manually install from https://gitlab.freedesktop.org/hadess/power-profiles-daemon");
println!(" Fedora and Arch distros will get the update soon...\n");
if !cmd.next && !cmd.list && cmd.profile_set.is_none() && !cmd.profile_get {
if !cmd.help {
println!("Missing arg or command\n");
}
let usage: Vec<String> = 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);
}
println!("{}", ProfileCommand::usage());
if let Some(lst) = cmd.self_command_list() {
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);
}
if cmd.next {
dbus.proxies().profile().next_fan()?;
}
if let Some(profile) = &cmd.remove {
dbus.proxies().profile().remove(profile)?
dbus.proxies().profile().next_profile()?;
} else if let Some(profile) = cmd.profile_set {
dbus.proxies().profile().set_active_profile(profile)?;
}
if cmd.list {
let profile_names = dbus.proxies().profile().profile_names()?;
println!("Available profiles are {:?}", profile_names);
let res = dbus.proxies().profile().profiles()?;
res.iter().for_each(|p| println!("{:?}", p));
}
if cmd.active_name {
println!(
"Active profile: {:?}",
dbus.proxies().profile().active_name()?
);
if cmd.profile_get {
let res = dbus.proxies().profile().active_profile()?;
println!("Active profile is {:?}", res);
}
if cmd.active_data {
println!("Active profile:");
println!("{:?}", dbus.proxies().profile().active_data()?);
Ok(())
}
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:");
for s in dbus.proxies().profile().all_profile_data()? {
println!("{:?}", s);
if !cmd.get_enabled && !cmd.default && cmd.mod_profile.is_none() {
if !cmd.help {
println!("Missing arg or command\n");
}
println!("{}", FanCurveCommand::usage());
if let Some(lst) = cmd.self_command_list() {
println!("\n{}", lst);
}
std::process::exit(1);
}
let mut set_profile = false;
let mut profile;
if cmd.create {
profile = Profile::default();
set_profile = true;
} else {
profile = dbus.proxies().profile().active_data()?;
if (cmd.enabled.is_some() || cmd.fan.is_some() || cmd.data.is_some())
&& cmd.mod_profile.is_none()
{
println!("--enabled, --fan, and --data options require --mod-profile");
std::process::exit(666);
}
if let Some(turbo) = cmd.turbo {
set_profile = true;
profile.turbo = turbo;
if cmd.get_enabled {
let res = dbus.proxies().profile().enabled_fan_profiles()?;
println!("{:?}", res);
}
if let Some(min) = cmd.min_percentage {
set_profile = true;
profile.min_percentage = min;
if cmd.default {
dbus.proxies().profile().set_active_curve_to_defaults()?;
}
if let Some(max) = cmd.max_percentage {
set_profile = true;
profile.max_percentage = max;
}
if let Some(preset) = cmd.fan_preset {
set_profile = true;
profile.fan_preset = preset;
}
if let Some(ref curve) = cmd.curve {
set_profile = true;
profile.fan_curve = curve.as_config_string();
}
if let Some(ref name) = cmd.profile {
set_profile = true;
profile.name = name.clone();
}
if set_profile {
dbus.proxies().profile().new_or_modify(&profile)?;
if let Some(profile) = cmd.mod_profile {
if cmd.enabled.is_none() && cmd.data.is_none() {
let data = dbus.proxies().profile().fan_curve_data(profile)?;
let data = toml::to_string(&data)?;
println!("\nFan curves for {:?}\n\n{}", profile, data);
}
if let Some(enabled) = cmd.enabled {
dbus.proxies()
.profile()
.set_fan_curve_enabled(profile, enabled)?;
}
if let Some(mut curve) = cmd.data.clone() {
let fan = cmd.fan.unwrap_or_default();
curve.set_fan(fan);
dbus.proxies().profile().set_fan_curve(curve, profile)?;
}
}
Ok(())
@@ -571,3 +489,27 @@ fn handle_bios_option(
}
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
}

View File

@@ -1,6 +1,5 @@
use gumdrop::Options;
use rog_fan_curve::{Curve, Fan};
use rog_profiles::profiles::FanLevel;
use rog_profiles::{fan_curve_set::CurveData, FanCurvePU, Profile};
#[derive(Debug, Clone, Options)]
pub struct ProfileCommand {
@@ -8,46 +7,45 @@ pub struct ProfileCommand {
pub help: bool,
#[options(help = "toggle to next profile in list")]
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")]
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(meta = "", help = "enable or disable cpu turbo")]
pub turbo: Option<bool>,
#[options(meta = "", help = "set min cpu scaling (intel)")]
pub min_percentage: Option<u8>,
#[options(meta = "", help = "set max cpu scaling (intel)")]
pub max_percentage: Option<u8>,
#[options(help = "get profile")]
pub profile_get: bool,
#[options(meta = "", help = "set the active profile")]
pub profile_set: Option<Profile>,
}
#[derive(Debug, Clone, Options)]
pub struct FanCurveCommand {
#[options(help = "print help message")]
pub help: bool,
#[options(help = "get enabled fan profiles")]
pub get_enabled: bool,
#[options(help = "set the active profile's fan curve to default")]
pub default: bool,
#[options(meta = "", help = "<silent, normal, boost>")]
pub fan_preset: Option<FanLevel>,
#[options(
meta = "",
parse(try_from_str = "parse_fan_curve"),
help = "set fan curve"
help = "profile to modify fan-curve for. Shows data if no options provided"
)]
pub curve: Option<Curve>,
#[options(free)]
pub profile: Option<String>,
}
pub mod_profile: Option<Profile>,
#[options(
meta = "",
help = "enable or disable <true/false> fan curve. `mod-profile` required"
)]
pub enabled: Option<bool>,
#[options(
meta = "",
help = "select fan <cpu/gpu> to modify. `mod-profile` required"
)]
pub fan: Option<FanCurvePU>,
fn parse_fan_curve(data: &str) -> Result<Curve, String> {
let curve = Curve::from_config_str(data)?;
if let Err(err) = curve.check_safety(Fan::Cpu) {
return Err(format!("Unsafe curve {:?}", err));
}
if let Err(err) = curve.check_safety(Fan::Gpu) {
return Err(format!("Unsafe curve {:?}", err));
}
Ok(curve)
#[options(
meta = "",
help = "data format = 30c:1%,49c:2%,59c:3%,69c:4%,79c:31%,89c:49%,99c:56%,109c:58%.
`--mod-profile` required. If '%' is omitted the fan range is 0-255"
)]
pub data: Option<CurveData>,
}

View File

@@ -1,6 +1,6 @@
[package]
name = "daemon-user"
version = "1.1.1"
version = "1.2.0"
authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2018"
description = "Usermode daemon for user settings, anime, per-key lighting"
@@ -21,7 +21,7 @@ serde_derive = "^1.0"
rog_anime = { path = "../rog-anime" }
rog_dbus = { path = "../rog-dbus" }
rog_types = { path = "../rog-types" }
rog_supported = { path = "../rog-supported" }
dirs = "3.0.1"

View File

@@ -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 crate::dbus::DbusEvents;
use serde_derive::{Deserialize, Serialize};
use std::time::Duration;
use std::{
@@ -17,6 +17,44 @@ use zvariant_derive::Type;
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)]
pub enum TimeType {
Timer,
@@ -29,14 +67,14 @@ pub enum TimeType {
pub struct CtrlAnimeInner<'a> {
sequences: Sequences,
client: RogDbusClient<'a>,
do_early_return: &'a AtomicBool,
do_early_return: Arc<AtomicBool>,
}
impl<'a> CtrlAnimeInner<'static> {
pub fn new(
sequences: Sequences,
client: RogDbusClient<'static>,
do_early_return: &'static AtomicBool,
do_early_return: Arc<AtomicBool>,
) -> Result<Self, Error> {
Ok(Self {
sequences,
@@ -45,7 +83,7 @@ impl<'a> CtrlAnimeInner<'static> {
})
}
/// 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) {
return Ok(());
}
@@ -53,39 +91,20 @@ impl<'a> CtrlAnimeInner<'static> {
for action in self.sequences.iter() {
match action {
ActionData::Animation(frames) => {
let mut count = 0;
let start = Instant::now();
'animation: loop {
for frame in frames.frames() {
if self.do_early_return.load(Ordering::SeqCst) {
return Ok(());
}
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;
}
}
}
rog_anime::run_animation(frames, self.do_early_return.clone(), &|output| {
self.client
.proxies()
.anime()
.write(output)
.map_err(|e| AnimeError::Dbus(format!("{}", e)))
})?;
}
ActionData::Image(image) => {
self.client
.proxies()
.anime()
.write(image.as_ref().clone())
.unwrap();
.ok();
}
ActionData::Pause(duration) => {
let start = Instant::now();
@@ -115,7 +134,7 @@ pub struct CtrlAnime<'a> {
client: RogDbusClient<'a>,
inner: Arc<Mutex<CtrlAnimeInner<'a>>>,
/// Must be the same Atomic as in CtrlAnimeInner
inner_early_return: &'a AtomicBool,
inner_early_return: Arc<AtomicBool>,
}
impl<'a> CtrlAnime<'static> {
@@ -123,7 +142,7 @@ impl<'a> CtrlAnime<'static> {
config: Arc<Mutex<UserAnimeConfig>>,
inner: Arc<Mutex<CtrlAnimeInner<'static>>>,
client: RogDbusClient<'static>,
inner_early_return: &'static AtomicBool,
inner_early_return: Arc<AtomicBool>,
) -> Result<Self, Error> {
Ok(CtrlAnime {
config,
@@ -159,18 +178,13 @@ impl CtrlAnime<'static> {
&mut self,
index: u32,
file: String,
time: TimeType,
count: u32,
time: Timer,
brightness: f32,
) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() {
let time: AnimTime = match time {
TimeType::Timer => AnimTime::Time(Duration::from_millis(count as u64)),
TimeType::Count => AnimTime::Cycles(count),
TimeType::Infinite => AnimTime::Infinite,
};
let time: AnimTime = time.into();
let file = Path::new(&file);
let action = AnimeAction::AsusAnimation {
let action = ActionLoader::AsusAnimation {
file: file.into(),
brightness,
time,
@@ -205,19 +219,14 @@ impl CtrlAnime<'static> {
scale: f32,
angle: f32,
xy: (f32, f32),
time: TimeType,
count: u32,
time: Timer,
brightness: f32,
) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() {
let time: AnimTime = match time {
TimeType::Timer => AnimTime::Time(Duration::from_millis(count as u64)),
TimeType::Count => AnimTime::Cycles(count),
TimeType::Infinite => AnimTime::Infinite,
};
let time: AnimTime = time.into();
let file = Path::new(&file);
let translation = Vec2::new(xy.0, xy.1);
let action = AnimeAction::ImageAnimation {
let action = ActionLoader::ImageAnimation {
file: file.into(),
scale,
angle,
@@ -248,6 +257,7 @@ impl CtrlAnime<'static> {
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
}
#[allow(clippy::too_many_arguments)]
pub fn insert_image(
&mut self,
index: u32,
@@ -255,16 +265,19 @@ impl CtrlAnime<'static> {
scale: f32,
angle: f32,
xy: (f32, f32),
time: Timer,
brightness: f32,
) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() {
let file = Path::new(&file);
let action = AnimeAction::Image {
let time = time.into();
let action = ActionLoader::Image {
file: file.into(),
scale,
angle,
translation: Vec2::new(xy.0, xy.1),
brightness,
time,
};
// 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> {
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
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<()> {
// Operations here need to be in specific order
if on {
self.client.proxies().anime().toggle_on(on)?;
self.client.proxies().anime().set_led_power(on)?;
// Let the inner loop run
self.inner_early_return.store(false, Ordering::SeqCst);
} else {
// Must make the inner run loop return early
self.inner_early_return.store(true, Ordering::SeqCst);
self.client.proxies().anime().toggle_on(on)?;
self.client.proxies().anime().set_led_power(on)?;
}
Ok(())
}

View File

@@ -11,16 +11,13 @@ use zbus::{fdo, Connection};
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>> {
println!(" user daemon v{}", rog_user::VERSION);
println!(" rog-anime v{}", rog_anime::VERSION);
println!(" rog-dbus v{}", rog_dbus::VERSION);
println!(" rog-types v{}", rog_types::VERSION);
println!(" user daemon v{}", rog_user::VERSION);
println!(" rog-anime v{}", rog_anime::VERSION);
println!(" rog-dbus v{}", rog_dbus::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 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
if supported.anime_ctrl.0 {
let early_return = Arc::new(AtomicBool::new(false));
// Inner behind mutex required for thread safety
let inner = Arc::new(Mutex::new(CtrlAnimeInner::new(
anime,
client,
&ANIME_INNER_EARLY_RETURN,
early_return.clone(),
)?));
// Need new client object for dbus control part
let (client, _) = RogDbusClient::new().unwrap();
let anime_control = CtrlAnime::new(
anime_config,
inner.clone(),
client,
&ANIME_INNER_EARLY_RETURN,
)?;
let (client, _) = RogDbusClient::new()?;
let anime_control = CtrlAnime::new(anime_config, inner.clone(), client, early_return)?;
anime_control.add_to_server(&mut server);
// Thread using inner
let _anime_thread = thread::Builder::new()
.name("Anime User".into())
.spawn(move || loop {
if let Ok(inner) = inner.try_lock() {
inner.run().unwrap();
inner.run().ok();
}
})?;
}

View File

@@ -4,7 +4,7 @@ use std::{
time::Duration,
};
use rog_anime::{AnimTime, AnimeAction, Sequences, Vec2};
use rog_anime::{ActionLoader, AnimTime, Fade, Sequences, Vec2};
use serde_derive::{Deserialize, Serialize};
use crate::error::Error;
@@ -12,7 +12,7 @@ use crate::error::Error;
#[derive(Debug, Deserialize, Serialize)]
pub struct UserAnimeConfig {
pub name: String,
pub anime: Vec<AnimeAction>,
pub anime: Vec<ActionLoader>,
}
impl UserAnimeConfig {
@@ -95,34 +95,56 @@ impl Default for UserAnimeConfig {
Self {
name: "default".to_string(),
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(),
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(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
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(),
scale: 1.0,
angle: 0.0,
translation: Vec2::default(),
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(1)),
Duration::from_secs(2),
)),
brightness: 0.6,
},
AnimeAction::Pause(Duration::from_secs(6)),
AnimeAction::ImageAnimation {
ActionLoader::Pause(Duration::from_secs(1)),
ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
scale: 0.9,
angle: 0.0,
translation: Vec2::new(3.0, 2.0),
brightness: 0.5,
time: AnimTime::Cycles(2),
time: AnimTime::Count(2),
},
],
}

View File

@@ -1,6 +1,6 @@
[package]
name = "daemon"
version = "3.6.2"
version = "4.0.5"
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
@@ -20,7 +20,7 @@ path = "src/daemon.rs"
[dependencies]
rog_anime = { path = "../rog-anime" }
rog_aura = { path = "../rog-aura" }
rog_types = { path = "../rog-types" }
rog_supported = { path = "../rog-supported" }
rog_profiles = { path = "../rog-profiles" }
rog_dbus = { path = "../rog-dbus" }
rusb = "^0.8"
@@ -39,8 +39,7 @@ logind-zbus = "^0.7.1"
serde = "^1.0"
serde_derive = "^1.0"
serde_json = "^1.0"
toml = "^0.5"
toml = "^0.5.8"
# Device control
sysfs-class = "^0.1.2" # used for backlight control and baord ID
rog_fan_curve = { version = "0.1", features = ["serde"] }

View File

@@ -1,85 +1,23 @@
use log::{error, info, warn};
use rog_profiles::profiles::{FanLevel, Profile};
use rog_types::gfx_vendors::GfxVendors;
use log::{error, warn};
use serde_derive::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use crate::config_old::*;
use crate::VERSION;
pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
pub static AURA_CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
#[derive(Deserialize, Serialize)]
pub struct Config {
pub gfx_mode: GfxVendors,
/// 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,
/// Save charge limit for restoring on boot
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 {
fn new() -> Self {
Config {
bat_charge_limit: 100,
}
}
/// `load` will attempt to read the config, and panic if the dir is missing
pub fn load() -> Self {
let mut file = OpenOptions::new()
@@ -89,46 +27,30 @@ impl Config {
.open(&CONFIG_PATH)
.unwrap_or_else(|_| panic!("The directory /etc/asusd/ is missing")); // okay to cause panic here
let mut buf = String::new();
let config;
if let Ok(read_len) = file.read_to_string(&mut buf) {
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 {
if let Ok(data) = serde_json::from_str(&buf) {
return data;
} else if let Ok(data) = serde_json::from_str::<ConfigV352>(&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::<ConfigV341>(&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::<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);
warn!(
"Could not deserialise {}.\nWill rename to {}-old and recreate config",
CONFIG_PATH, CONFIG_PATH
);
let cfg_old = CONFIG_PATH.to_string() + "-old";
std::fs::rename(CONFIG_PATH, cfg_old).unwrap_or_else(|err| {
panic!(
"Could not rename. Please remove {} then restart service: Error {}",
CONFIG_PATH, err
)
});
config = Self::new();
}
} else {
config = Self::new()
}
Config::create_default(&mut file)
}
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.write();
config
}
@@ -142,24 +64,12 @@ impl Config {
if l == 0 {
warn!("File is empty {}", CONFIG_PATH);
} else {
let x: Config = serde_json::from_str(&buf)
*self = serde_json::from_str(&buf)
.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) {
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");

View File

@@ -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
}
}

View File

@@ -1,6 +1,7 @@
use crate::VERSION;
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 std::fs::{File, OpenOptions};
use std::io::{Read, Write};
@@ -11,10 +12,10 @@ pub static ANIME_CACHE_PATH: &str = "/etc/asusd/anime-cache.conf";
#[derive(Deserialize, Serialize)]
pub struct AnimeConfigV341 {
pub system: Option<AnimeAction>,
pub boot: Option<AnimeAction>,
pub suspend: Option<AnimeAction>,
pub shutdown: Option<AnimeAction>,
pub system: Option<ActionLoader>,
pub boot: Option<ActionLoader>,
pub suspend: Option<ActionLoader>,
pub shutdown: Option<ActionLoader>,
}
impl AnimeConfigV341 {
@@ -49,10 +50,10 @@ impl AnimeConfigV341 {
#[derive(Deserialize, Serialize)]
pub struct AnimeConfigV352 {
pub system: Vec<AnimeAction>,
pub boot: Vec<AnimeAction>,
pub wake: Vec<AnimeAction>,
pub shutdown: Vec<AnimeAction>,
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>,
pub brightness: f32,
}
@@ -110,10 +111,10 @@ impl AnimeConfigCached {
/// Config for base system actions for the anime display
#[derive(Deserialize, Serialize)]
pub struct AnimeConfig {
pub system: Vec<AnimeAction>,
pub boot: Vec<AnimeAction>,
pub wake: Vec<AnimeAction>,
pub shutdown: Vec<AnimeAction>,
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>,
pub brightness: f32,
pub awake_enabled: bool,
pub boot_anim_enabled: bool,
@@ -165,8 +166,17 @@ impl AnimeConfig {
info!("Updated config version to: {}", VERSION);
return config;
}
warn!("Could not deserialise {}", ANIME_CONFIG_PATH);
panic!("Please remove {} then restart asusd", ANIME_CONFIG_PATH);
warn!(
"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)
@@ -176,23 +186,31 @@ impl AnimeConfig {
// create a default config here
let config = AnimeConfig {
system: vec![],
boot: vec![AnimeAction::ImageAnimation {
boot: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
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(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
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(),
scale: 0.9,
angle: 0.0,

View File

@@ -1,32 +1,33 @@
pub mod config;
pub mod zbus;
use ::zbus::Connection;
use log::{error, info, warn};
use logind_zbus::ManagerProxy;
use rog_anime::{
error::AnimeError,
usb::{
pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on, pkts_for_init, PROD_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 std::{
cell::RefCell,
error::Error,
sync::{Arc, Mutex},
thread::sleep,
time::Instant,
};
use std::{
sync::atomic::{AtomicBool, Ordering},
time::Duration,
};
use zbus::{dbus_interface, Connection};
use zvariant::ObjectPath;
use crate::{
config_anime::{AnimeConfig, AnimeConfigCached},
error::RogError,
GetSupported,
};
use crate::{error::RogError, GetSupported};
use self::config::{AnimeConfig, AnimeConfigCached};
impl GetSupported for CtrlAnime {
type A = AnimeSupportedFunctions;
@@ -37,7 +38,8 @@ impl GetSupported for CtrlAnime {
}
pub struct CtrlAnime {
handle: DeviceHandle<rusb::GlobalContext>,
_node: String,
handle: RefCell<DeviceHandle<rusb::GlobalContext>>,
cache: AnimeConfigCached,
config: AnimeConfig,
// set to force thread to exit
@@ -49,6 +51,55 @@ pub struct CtrlAnime {
impl CtrlAnime {
#[inline]
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
let device = CtrlAnime::get_device(0x0b05, 0x193b)?;
@@ -65,20 +116,7 @@ impl CtrlAnime {
err
})?;
info!("Device has an AniMe Matrix display");
let mut cache = AnimeConfigCached::default();
cache.init_from_config(&config)?;
let ctrl = CtrlAnime {
handle: device,
cache,
config,
thread_exit: Arc::new(AtomicBool::new(false)),
thread_running: Arc::new(AtomicBool::new(false)),
};
ctrl.do_initialization();
Ok(ctrl)
Ok(device)
}
fn get_device(vendor: u16, product: u16) -> Result<Device<rusb::GlobalContext>, rusb::Error> {
@@ -144,31 +182,25 @@ impl CtrlAnime {
for action in actions.iter() {
match action {
ActionData::Animation(frames) => {
let mut count = 0;
let start = Instant::now();
'animation: loop {
for frame in frames.frames() {
if let Ok(lock) = inner.try_lock() {
lock.write_data_buffer(frame.frame().clone());
}
if let AnimTime::Time(time) = frames.duration() {
if Instant::now().duration_since(start) > time {
break 'animation;
}
}
sleep(frame.delay());
// Need to check for early exit condition here or it might run
// until end of gif or time
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
}
if let AnimTime::Cycles(times) = frames.duration() {
count += 1;
if count >= times {
break 'animation;
}
}
if let Err(err) = rog_anime::run_animation(
frames,
thread_exit.clone(),
&|frame| {
inner
.try_lock()
.map(|lock| lock.write_data_buffer(frame))
.map_err(|err| {
warn!("rog_anime::run_animation: {}", err);
AnimeError::NoFrames
})
},
) {
warn!("rog_anime::run_animation: {}", err);
break 'main;
};
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
}
ActionData::Image(image) => {
@@ -203,7 +235,14 @@ impl CtrlAnime {
}
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
0x09, // request
0x35e, // value
@@ -214,9 +253,24 @@ impl CtrlAnime {
Ok(_) => {}
Err(err) => match err {
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
@@ -251,9 +305,11 @@ pub struct CtrlAnimeTask<'a> {
impl<'a> CtrlAnimeTask<'a> {
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();
// Run this action when the system starts shutting down
@@ -344,124 +400,3 @@ impl crate::Reloadable for CtrlAnimeReloader {
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<()>;
}

View File

@@ -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<()>;
}

View File

@@ -90,7 +90,7 @@ impl AuraConfig {
let mut buf = String::new();
if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len == 0 {
return AuraConfig::create_default(&mut file, &supported_led_modes);
return AuraConfig::create_default(&mut file, supported_led_modes);
} else {
if let Ok(data) = serde_json::from_str(&buf) {
return data;
@@ -105,11 +105,20 @@ impl AuraConfig {
info!("Updated AuraConfig version");
return config;
}
warn!("Could not deserialise {}", AURA_CONFIG_PATH);
panic!("Please remove {} then restart asusd", AURA_CONFIG_PATH);
warn!(
"Could not deserialise {}.\nWill rename to {}-old and recreate config",
AURA_CONFIG_PATH, AURA_CONFIG_PATH
);
let cfg_old = AURA_CONFIG_PATH.to_string() + "-old";
std::fs::rename(AURA_CONFIG_PATH, cfg_old).unwrap_or_else(|err| {
panic!(
"Could not rename. Please remove {} then restart service: Error {}",
AURA_CONFIG_PATH, err
)
});
}
}
AuraConfig::create_default(&mut file, &supported_led_modes)
AuraConfig::create_default(&mut file, supported_led_modes)
}
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));
}
/// 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) {
match effect.zone() {
AuraZone::None => {

View File

@@ -2,11 +2,12 @@
static KBD_BRIGHT_PATH: &str = "/sys/class/leds/asus::kbd_backlight/brightness";
use crate::{
config_aura::AuraConfig,
error::RogError,
laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES},
CtrlTask,
};
use log::{info, warn};
use logind_zbus::ManagerProxy;
use rog_aura::{
usb::{
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,
};
use rog_types::supported::LedSupportedFunctions;
use std::fs::OpenOptions;
use rog_supported::LedSupportedFunctions;
use std::io::{Read, Write};
use std::path::Path;
use std::sync::Arc;
use std::sync::Mutex;
use std::{fs::OpenOptions, thread::spawn};
use zbus::Connection;
use crate::GetSupported;
use super::config::AuraConfig;
impl GetSupported for CtrlKbdLed {
type A = LedSupportedFunctions;
@@ -50,32 +54,82 @@ pub struct CtrlKbdLed {
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 {
fn do_task(&self) -> Result<(), RogError> {
if let Ok(mut lock) = self.0.try_lock() {
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();
impl<'a> CtrlKbdLedTask<'a> {
pub fn new(inner: Arc<Mutex<CtrlKbdLed>>) -> Self {
let connection =
Connection::new_system().expect("CtrlKbdLedTask could not create dbus connection");
let manager =
ManagerProxy::new(&connection).expect("CtrlKbdLedTask could not create ManagerProxy");
let c1 = inner.clone();
// Run this action when the system wakes up from sleep
manager
.connect_prepare_for_sleep(move |sleep| {
if !sleep {
let c1 = c1.clone();
spawn(move || {
// wait a fraction for things to wake up properly
//std::thread::sleep(Duration::from_millis(100));
loop {
if let Ok(ref mut lock) = c1.try_lock() {
lock.set_brightness(lock.config.brightness).ok();
break;
}
}
});
}
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(())
}
@@ -187,6 +241,28 @@ impl CtrlKbdLed {
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
pub(super) fn set_states_enabled(&self, awake: bool, sleep: bool) -> Result<(), RogError> {
let bytes = if awake && sleep {
@@ -319,7 +395,7 @@ impl CtrlKbdLed {
self.config.read();
if let Some(data) = self.config.builtins.get(&next) {
self.write_mode(&data)?;
self.write_mode(data)?;
self.config.current_mode = next;
}
self.config.write();
@@ -330,7 +406,7 @@ impl CtrlKbdLed {
#[inline]
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);
}
let bytes: [u8; LED_MSG_LEN] = mode.into();

View File

@@ -1,2 +1,3 @@
pub mod config;
pub mod controller;
pub mod zbus;

View File

@@ -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)]
fn awake_enabled(&self) -> bool {
if let Ok(ctrl) = self.0.try_lock() {

View File

@@ -1,7 +1,7 @@
use crate::{config::Config, error::RogError, GetSupported};
//use crate::dbus::DbusEvents;
use log::{info, warn};
use rog_types::supported::ChargeSupportedFunctions;
use rog_supported::ChargeSupportedFunctions;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;
@@ -10,7 +10,9 @@ use std::sync::Mutex;
use zbus::dbus_interface;
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 {
type A = ChargeSupportedFunctions;
@@ -28,7 +30,10 @@ pub struct CtrlCharge {
#[dbus_interface(name = "org.asuslinux.Daemon")]
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() {
self.set(limit, &mut config)
.map_err(|err| {
@@ -43,6 +48,7 @@ impl CtrlCharge {
})
.ok();
}
Ok(())
}
pub fn limit(&self) -> i8 {
@@ -88,8 +94,12 @@ impl CtrlCharge {
}
fn get_battery_path() -> Result<&'static str, RogError> {
if Path::new(BAT_CHARGE_PATH).exists() {
Ok(BAT_CHARGE_PATH)
if Path::new(BAT_CHARGE_PATH0).exists() {
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 {
Err(RogError::MissingFunction(
"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> {
if !(20..=100).contains(&limit) {
warn!(
"Unable to set battery charge limit, must be between 20-100: requested {}",
limit
);
return Err(RogError::ChargeLimit(limit));
}
let path = Self::get_battery_path()?;
let mut file = OpenOptions::new()
.write(true)
.open(BAT_CHARGE_PATH)
.map_err(|err| RogError::Path(BAT_CHARGE_PATH.into(), err))?;
.open(path)
.map_err(|err| RogError::Path(path.into(), err))?;
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);
config.read();

View File

@@ -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(())
}
}

View File

@@ -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)
}
}

View File

@@ -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"#;

View File

@@ -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(())
}
}

View File

@@ -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();
}
}

View File

@@ -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));
}
}

View File

@@ -1,102 +1,151 @@
use crate::error::RogError;
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;
use std::sync::{Arc, Mutex};
pub struct CtrlFanAndCpu {
pub config: Arc<Mutex<Config>>,
use crate::error::RogError;
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 {
type A = FanCpuSupportedFunctions;
impl GetSupported for CtrlPlatformProfile {
type A = PlatformProfileFunctions;
fn get_supported() -> Self::A {
FanCpuSupportedFunctions {
stock_fan_modes: Profile::get_fan_path().is_ok(),
min_max_freq: Profile::get_intel_supported(),
fan_curve_set: rog_fan_curve::Board::from_board_name().is_some(),
if !Profile::is_platform_profile_supported() {
warn!(
r#"
platform_profile kernel interface not found, your laptop does not support this, or the interface is missing.
To enable profile support you require a kernel with the following patch applied:
https://lkml.org/lkml/2021/8/18/1022
"#
);
}
let res = 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> {
if let Ok(mut cfg) = self.config.clone().try_lock() {
let active = cfg.active_profile.clone();
if let Some(existing) = cfg.power_profiles.get_mut(&active) {
existing.set_system_all()?;
cfg.write();
if let Some(curves) = &mut self.config.fan_curves {
if let Ok(mut device) = FanCurveProfiles::get_device() {
// There is a possibility that the curve was default zeroed, so this call initialises
// the data from system read and we need to save it after
curves.write_profile_curve_to_platform(self.config.active_profile, &mut device)?;
self.config.write();
}
}
Ok(())
}
}
impl CtrlFanAndCpu {
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
Profile::get_fan_path()?;
info!("Device has thermal throttle control");
Ok(CtrlFanAndCpu { config })
}
impl CtrlPlatformProfile {
pub fn new(config: ProfileConfig) -> Result<Self, RogError> {
if Profile::is_platform_profile_supported() {
info!("Device has profile control available");
/// Toggle to next profile in list
pub(super) fn do_next_profile(&mut self) -> Result<(), RogError> {
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;
if FanCurveProfiles::get_device().is_ok() {
info!("Device has fan curves available");
}
let profile = config.toggle_profiles[i].clone();
return Ok(CtrlPlatformProfile { config });
}
if let Some(existing) = config.power_profiles.get(&profile) {
existing.set_system_all()?;
config.active_profile = existing.name.clone();
config.write();
info!("Profile was changed to: {}", profile);
Err(ProfileError::NotSupported.into())
}
pub fn save_config(&self) {
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(())
}
pub(super) fn set_active(&mut self, profile: &str) -> Result<(), RogError> {
if let Ok(mut config) = self.config.clone().try_lock() {
config.read();
if let Some(existing) = config.power_profiles.get(profile) {
existing.set_system_all()?;
config.active_profile = existing.name.clone();
config.write();
info!("Profile was changed to: {}", profile);
pub(super) fn set_active_curve_to_defaults(&mut self) -> Result<(), RogError> {
if let Some(curves) = self.config.fan_curves.as_mut() {
if let Ok(mut device) = FanCurveProfiles::get_device() {
curves.set_active_curve_to_defaults(self.config.active_profile, &mut device)?;
}
}
Ok(())
}
}
pub struct CtrlProfileTask {
ctrl: Arc<Mutex<CtrlPlatformProfile>>,
}
impl CtrlProfileTask {
pub fn new(ctrl: Arc<Mutex<CtrlPlatformProfile>>) -> Self {
Self { ctrl }
}
}
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(())
}
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(())
}
}

View File

@@ -1,3 +1,3 @@
pub mod zbus;
pub mod config;
pub mod controller;
pub mod zbus;

View File

@@ -1,153 +1,179 @@
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::Mutex;
use zbus::{dbus_interface, fdo::Error};
use zvariant::ObjectPath;
use super::controller::CtrlFanAndCpu;
use super::controller::CtrlPlatformProfile;
pub struct FanAndCpuZbus {
inner: Arc<Mutex<CtrlFanAndCpu>>,
static UNSUPPORTED_MSG: &str =
"Fan curves are not supported on this laptop or you require a patched kernel";
pub struct ProfileZbus {
inner: Arc<Mutex<CtrlPlatformProfile>>,
}
impl FanAndCpuZbus {
pub fn new(inner: Arc<Mutex<CtrlFanAndCpu>>) -> Self {
impl ProfileZbus {
pub fn new(inner: Arc<Mutex<CtrlPlatformProfile>>) -> Self {
Self { inner }
}
}
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl FanAndCpuZbus {
/// Create new profile and make active
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
impl ProfileZbus {
/// Fetch profile names
fn profiles(&mut self) -> zbus::fdo::Result<Vec<Profile>> {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() {
cfg.read();
return Ok(cfg.power_profiles.values().cloned().collect());
}
if let Ok(profiles) = Profile::get_profile_names() {
return Ok(profiles);
}
Err(Error::Failed(
"Failed to get all profile details".to_string(),
))
}
fn profile_names(&self) -> zbus::fdo::Result<Vec<String>> {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() {
cfg.read();
let profile_names = cfg.power_profiles.keys().cloned().collect::<Vec<String>>();
return Ok(profile_names);
}
/// Toggle to next platform_profile. Names provided by `Profiles`.
/// If fan-curves are supported will also activate a fan curve for profile.
fn next_profile(&mut self) {
if let Ok(mut ctrl) = self.inner.try_lock() {
ctrl.set_next_profile()
.unwrap_or_else(|err| warn!("{}", err));
ctrl.save_config();
}
Err(Error::Failed("Failed to get all profile names".to_string()))
self.do_notification();
}
fn remove(&self, profile: &str) -> zbus::fdo::Result<()> {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() {
cfg.read();
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(());
}
/// Fetch the active profile name
fn active_profile(&mut self) -> zbus::fdo::Result<Profile> {
if let Ok(mut ctrl) = self.inner.try_lock() {
ctrl.config.read();
return Ok(ctrl.config.active_profile);
}
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)]
fn notify_profile(&self, profile: &Profile) -> zbus::Result<()> {}
}
impl FanAndCpuZbus {
impl ProfileZbus {
fn do_notification(&self) {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(cfg) = ctrl.config.clone().try_lock() {
if let Some(profile) = cfg.power_profiles.get(&cfg.active_profile) {
self.notify_profile(&profile)
.unwrap_or_else(|err| warn!("{}", err));
}
}
self.notify_profile(&ctrl.config.active_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) {
server
.at(

View File

@@ -1,6 +1,6 @@
use crate::{config::Config, error::RogError, GetSupported};
use log::{error, info, warn};
use rog_types::supported::RogBiosSupportedFunctions;
use rog_supported::RogBiosSupportedFunctions;
use std::fs::OpenOptions;
use std::io::BufRead;
use std::io::{Read, Write};
@@ -28,8 +28,8 @@ impl GetSupported for CtrlRogBios {
fn get_supported() -> Self::A {
RogBiosSupportedFunctions {
post_sound_toggle: CtrlRogBios::check_path_exists(ASUS_POST_LOGO_SOUND).is_ok(),
dedicated_gfx_toggle: CtrlRogBios::check_path_exists(ASUS_SWITCH_GRAPHIC_MODE).is_ok(),
post_sound_toggle: Path::new(ASUS_POST_LOGO_SOUND).exists(),
dedicated_gfx_toggle: Path::new(ASUS_SWITCH_GRAPHIC_MODE).exists(),
}
}
}
@@ -63,8 +63,6 @@ impl CtrlRogBios {
#[dbus_interface(signal)]
pub fn notify_dedicated_graphic_mode(&self, dedicated: bool) -> zbus::Result<()> {}
// // // // // // // // // //
pub fn set_post_boot_sound(&mut self, on: bool) {
Self::set_boot_sound(on)
.map_err(|err| {
@@ -116,22 +114,17 @@ impl crate::Reloadable for CtrlRogBios {
impl CtrlRogBios {
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
match CtrlRogBios::check_path_exists(ASUS_SWITCH_GRAPHIC_MODE) {
Ok(_) => {
CtrlRogBios::set_path_mutable(ASUS_SWITCH_GRAPHIC_MODE)?;
}
Err(err) => {
info!("ROG Switchable Graphics (bios) not detected: {}", err);
}
if Path::new(ASUS_SWITCH_GRAPHIC_MODE).exists() {
CtrlRogBios::set_path_mutable(ASUS_SWITCH_GRAPHIC_MODE)?;
} else {
info!("G-Sync Switchable Graphics not detected");
info!("If your laptop is not a G-Sync enabled laptop then you can ignore this. Standard graphics switching will still work.");
}
match CtrlRogBios::check_path_exists(ASUS_POST_LOGO_SOUND) {
Ok(_) => {
CtrlRogBios::set_path_mutable(ASUS_POST_LOGO_SOUND)?;
}
Err(err) => {
info!("ROG boot sound toggle (bios) not detected: {}", err);
}
if Path::new(ASUS_POST_LOGO_SOUND).exists() {
CtrlRogBios::set_path_mutable(ASUS_POST_LOGO_SOUND)?;
} else {
info!("Switch for POST boot sound not detected");
}
Ok(CtrlRogBios { _config: config })
@@ -147,19 +140,8 @@ impl CtrlRogBios {
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 {
if CtrlRogBios::check_path_exists(ASUS_SWITCH_GRAPHIC_MODE).is_ok() {
return true;
}
false
Path::new(ASUS_SWITCH_GRAPHIC_MODE).exists()
}
pub fn get_gfx_mode() -> Result<i8, RogError> {
@@ -186,7 +168,7 @@ impl CtrlRogBios {
.map_err(|err| RogError::Path(path.into(), err))?;
let mut data = Vec::new();
file.read_to_end(&mut data).unwrap();
file.read_to_end(&mut data)?;
let idx = data.len() - 1;
if dedicated {
@@ -200,17 +182,6 @@ impl CtrlRogBios {
.map_err(|err| RogError::Path(path.into(), err))?;
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(())
}
@@ -298,7 +269,7 @@ impl CtrlRogBios {
RogError::Write(module_include.to_string_lossy().to_string(), err)
})?;
// add nvidia modules to module_include
file.write_all(modules.concat().as_bytes()).unwrap();
file.write_all(modules.concat().as_bytes())?;
} else {
let file = std::fs::OpenOptions::new()
.read(true)
@@ -321,7 +292,7 @@ impl CtrlRogBios {
.map_err(|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)?;
}
}

View File

@@ -5,20 +5,20 @@ use zvariant::ObjectPath;
use zvariant_derive::Type;
use crate::{
ctrl_anime::CtrlAnime, ctrl_charge::CtrlCharge, ctrl_leds::controller::CtrlKbdLed,
ctrl_profiles::controller::CtrlFanAndCpu, ctrl_rog_bios::CtrlRogBios, GetSupported,
ctrl_anime::CtrlAnime, ctrl_aura::controller::CtrlKbdLed, ctrl_charge::CtrlCharge,
ctrl_profiles::controller::CtrlPlatformProfile, ctrl_rog_bios::CtrlRogBios, GetSupported,
};
use rog_types::supported::{
AnimeSupportedFunctions, ChargeSupportedFunctions, FanCpuSupportedFunctions,
LedSupportedFunctions, RogBiosSupportedFunctions,
use rog_supported::{
AnimeSupportedFunctions, ChargeSupportedFunctions, LedSupportedFunctions,
PlatformProfileFunctions, RogBiosSupportedFunctions,
};
#[derive(Serialize, Deserialize, Type)]
pub struct SupportedFunctions {
pub anime_ctrl: AnimeSupportedFunctions,
pub charge_ctrl: ChargeSupportedFunctions,
pub fan_cpu_ctrl: FanCpuSupportedFunctions,
pub platform_profile: PlatformProfileFunctions,
pub keyboard_led: LedSupportedFunctions,
pub rog_bios_ctrl: RogBiosSupportedFunctions,
}
@@ -53,7 +53,7 @@ impl GetSupported for SupportedFunctions {
anime_ctrl: CtrlAnime::get_supported(),
keyboard_led: CtrlKbdLed::get_supported(),
charge_ctrl: CtrlCharge::get_supported(),
fan_cpu_ctrl: CtrlFanAndCpu::get_supported(),
platform_profile: CtrlPlatformProfile::get_supported(),
rog_bios_ctrl: CtrlRogBios::get_supported(),
}
}

View File

@@ -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::io::Write;
use std::sync::Arc;
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 std::convert::Into;
use zbus::fdo;
use zbus::Connection;
use zvariant::ObjectPath;
use daemon::error::RogError;
use daemon::{
config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported,
};
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>> {
let mut logger = env_logger::Builder::new();
@@ -35,12 +43,26 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
.filter(None, LevelFilter::Info)
.init();
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-types v{}", rog_types::VERSION);
let is_service = match env::var_os("IS_SERVICE") {
Some(val) => val == "1",
None => false,
};
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()?;
Ok(())
@@ -50,18 +72,14 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
fn start_daemon() -> Result<(), Box<dyn Error>> {
let supported = SupportedFunctions::get_supported();
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
let connection = Connection::new_system()?;
fdo::DBusProxy::new(&connection)?
.request_name(DBUS_NAME, fdo::RequestNameFlags::ReplaceExisting.into())?;
let mut object_server = zbus::ObjectServer::new(&connection);
let fdo_connection = fdo::DBusProxy::new(&connection)?;
let mut object_server = ObjectServer::new(&connection);
let config = Config::load();
let enable_gfx_switching = config.gfx_managed;
let config = Arc::new(Mutex::new(config));
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) => {
// Do a reload of any settings
ctrl.reload()
@@ -92,16 +110,32 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
}
}
match CtrlFanAndCpu::new(config.clone()) {
Ok(mut ctrl) => {
ctrl.reload()
.unwrap_or_else(|err| warn!("Profile control: {}", err));
let tmp = Arc::new(Mutex::new(ctrl));
FanAndCpuZbus::new(tmp).add_to_server(&mut object_server);
}
Err(err) => {
error!("Profile control: {}", err);
if Profile::is_platform_profile_supported() {
let profile_config = ProfileConfig::load(PROFILE_CONFIG_PATH.into());
match CtrlPlatformProfile::new(profile_config) {
Ok(mut ctrl) => {
ctrl.reload()
.unwrap_or_else(|err| warn!("Profile control: {}", err));
let tmp = Arc::new(Mutex::new(ctrl));
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()) {
@@ -116,7 +150,14 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
let zbus = CtrlAnimeZbus(inner.clone());
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) => {
error!("AniMe control: {}", err);
@@ -135,92 +176,26 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
.unwrap_or_else(|err| warn!("Keyboard LED control: {}", err));
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) => {
error!("Keyboard control: {}", err);
}
}
// Graphics switching requires some checks on boot specifically for g-sync capable laptops
if enable_gfx_switching {
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();
// Request dbus name after finishing initalizing all functions
fdo_connection.request_name(DBUS_NAME, fdo::RequestNameFlags::ReplaceExisting.into())?;
// Loop to check errors and iterate zbus server
loop {
if let Err(err) = &handle {
error!("{}", err);
}
if let Err(err) = object_server.try_handle_next() {
error!("{}", err);
}

View File

@@ -1,14 +1,9 @@
use rog_fan_curve::CurveError;
use rog_profiles::error::ProfileError;
use rog_types::error::GraphicsError;
use std::convert::From;
use std::fmt;
use crate::ctrl_gfx::error::GfxError;
#[derive(Debug)]
pub enum RogError {
ParseFanLevel,
ParseVendor,
ParseLed,
MissingProfile(String),
@@ -18,25 +13,22 @@ pub enum RogError {
Write(String, std::io::Error),
NotSupported,
NotFound(String),
FanCurve(CurveError),
DoTask(String),
MissingFunction(String),
MissingLedBrightNode(String, std::io::Error),
ReloadFail(String),
GfxSwitching(GfxError),
Profiles(ProfileError),
Initramfs(String),
Modprobe(String),
Command(String, std::io::Error),
Io(std::io::Error),
Zbus(zbus::Error),
ChargeLimit(u8),
}
impl fmt::Display for RogError {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
RogError::ParseFanLevel => write!(f, "Parse profile error"),
RogError::ParseVendor => write!(f, "Parse gfx vendor error"),
RogError::ParseLed => write!(f, "Parse LED error"),
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::NotSupported => write!(f, "Not supported"),
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::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::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::Initramfs(detail) => write!(f, "Initiramfs 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::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 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 {
fn from(err: ProfileError) -> Self {
RogError::Profiles(err)

View File

@@ -10,10 +10,8 @@ pub const ASUS_KEYBOARD_DEVICES: [&str; 4] = ["1866", "1869", "1854", "19b6"];
pub fn print_board_info() {
let dmi = sysfs_class::DmiId::default();
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");
info!("Product name: {}", prod_name.trim());
info!("Product family: {}", prod_family.trim());
info!("Board name: {}", board_name.trim());
}

View File

@@ -1,17 +1,12 @@
#![deny(unused_must_use)]
/// Configuration loading, saving
pub mod config;
pub mod config_anime;
pub mod config_aura;
pub(crate) mod config_old;
/// Control of AniMe matrix display
pub mod ctrl_anime;
/// Keyboard LED brightness control, RGB, and LED display modes
pub mod ctrl_aura;
/// Control of battery charge level
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
///
/// Intel machines can control:
@@ -32,7 +27,7 @@ pub mod laptops;
/// Fetch all supported functions for the laptop
pub mod ctrl_supported;
mod error;
pub mod error;
use crate::error::RogError;
use config::Config;

View File

@@ -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"

View File

@@ -1,4 +0,0 @@
Section "ServerLayout"
Identifier "layout"
Option "AllowNVIDIAGPUScreens"
EndSection

View File

@@ -3,6 +3,7 @@ Description=ASUS Notebook Control
After=basic.target syslog.target
[Service]
Environment=IS_SERVICE=1
ExecStart=/usr/bin/asusd
Restart=on-failure
Type=dbus

View File

@@ -12,6 +12,13 @@ standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight
multizone = false
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]]
prod_family = "ROG Zephyrus M15"
board_names = ["GU502LW"]
@@ -35,14 +42,14 @@ per_key = false
[[led_data]]
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"]
multizone = false
per_key = true
[[led_data]]
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"]
multizone = true
per_key = false
@@ -89,9 +96,38 @@ standard = ["Static", "Breathe", "Pulse"]
multizone = 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]]
prod_family = "ROG Zephyrus"
board_names = ["GX550L"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = false
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

View File

@@ -2,9 +2,11 @@
Description=ASUS Notebook Control
StartLimitInterval=200
StartLimitBurst=2
Before=display-manager.service
Before=multi-user.target
[Service]
Environment=IS_SERVICE=1
ExecStartPre=/bin/sleep 2
ExecStart=/usr/bin/asusd
Restart=on-failure
Restart=always

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
}
}
]
}

View File

@@ -1,6 +1,6 @@
[package]
name = "rog_anime"
version = "1.0.4"
version = "1.1.0"
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]

View File

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

View File

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View File

Before

Width:  |  Height:  |  Size: 137 KiB

After

Width:  |  Height:  |  Size: 137 KiB

View File

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 109 KiB

View File

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

View File

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View File

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View File

Before

Width:  |  Height:  |  Size: 234 KiB

After

Width:  |  Height:  |  Size: 234 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 63 KiB

View File

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

View File

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 132 KiB

View File

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 90 KiB

View File

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

View File

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 112 KiB

View File

Before

Width:  |  Height:  |  Size: 170 KiB

After

Width:  |  Height:  |  Size: 170 KiB

View File

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

Before

Width:  |  Height:  |  Size: 152 KiB

After

Width:  |  Height:  |  Size: 152 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -1,7 +1,18 @@
use std::{
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
thread::sleep,
time::{Duration, Instant},
};
use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "dbus")]
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`
const BLOCK_START: usize = 7;
/// *Not* inclusive, the byte before this is the final for each "pane"
@@ -79,3 +90,94 @@ impl From<AnimeDataBuffer> for AnimePacketType {
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(())
}

View File

@@ -48,7 +48,6 @@ impl AnimeDiagonal {
duration: Option<Duration>,
bright: f32,
) -> Result<Self, AnimeError> {
use pix::el::Pixel;
let data = std::fs::read(path)?;
let data = std::io::Cursor::new(data);
let decoder = png_pong::Decoder::new(data)?.into_steps();
@@ -56,22 +55,78 @@ impl AnimeDiagonal {
let mut matrix = AnimeDiagonal::new(duration);
let width;
match raster {
png_pong::PngRaster::Gray8(ras) => {
Self::pixels_from_8bit(ras, &mut matrix, bright, true)
}
png_pong::PngRaster::Graya8(ras) => {
width = ras.width();
for (y, row) in ras.pixels().chunks(width as usize).enumerate() {
for (x, px) in row.iter().enumerate() {
let v = <u8>::from(px.one() * bright);
matrix.0[y][x] = v;
}
}
Self::pixels_from_8bit(ras, &mut matrix, bright, true)
}
png_pong::PngRaster::Rgb8(ras) => {
Self::pixels_from_8bit(ras, &mut matrix, bright, false)
}
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),
};
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 {

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