mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-01-22 17:33:19 +01:00
Compare commits
309 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2726f3a67 | ||
|
|
663f87d5e2 | ||
|
|
377bb4d6ad | ||
|
|
98e60db2da | ||
|
|
11ac46df11 | ||
|
|
05bec93644 | ||
|
|
c77e7cf1ce | ||
|
|
d30f7dc2ea | ||
|
|
759606fca3 | ||
|
|
25ed8bdeed | ||
|
|
de61a15b7a | ||
|
|
16700e55f4 | ||
|
|
5833a011ce | ||
|
|
62577ee0e4 | ||
|
|
dac35996b1 | ||
|
|
5c2bcad7c6 | ||
|
|
81f6c18a24 | ||
|
|
bcc3d42789 | ||
|
|
36c34cb3dd | ||
|
|
9b82b875f1 | ||
|
|
bddccc696f | ||
|
|
51e9f10087 | ||
|
|
911ff8690e | ||
|
|
25823dc6b7 | ||
|
|
cba8e1a473 | ||
|
|
fb98827a1a | ||
|
|
b9296862df | ||
|
|
450205f9a9 | ||
|
|
82431ee25b | ||
|
|
f11aea02a8 | ||
|
|
2d6d669c22 | ||
|
|
f9cebf9221 | ||
|
|
a00808313e | ||
|
|
3426591d32 | ||
|
|
ef3b6636f5 | ||
|
|
ee9e0a1e31 | ||
|
|
9e84997cbf | ||
|
|
2b22f82b72 | ||
|
|
7a1b45071d | ||
|
|
ad63c429cb | ||
|
|
a790d9a499 | ||
|
|
be51a1ab77 | ||
|
|
d9a88e7cc3 | ||
|
|
d785e17f95 | ||
|
|
efe64119c6 | ||
|
|
128bc3fce1 | ||
|
|
2123f369ad | ||
|
|
2b7a2a5be3 | ||
|
|
1d9e89ef3d | ||
|
|
4011b3ebd4 | ||
|
|
f7456f0144 | ||
|
|
2ed2d82e03 | ||
|
|
d40f4733e2 | ||
|
|
98dc155e41 | ||
|
|
fd3384decc | ||
|
|
a1a9c7077a | ||
|
|
62aa1fe04f | ||
|
|
a7b98c67ee | ||
|
|
e7bbd99178 | ||
|
|
e2d3f99d91 | ||
|
|
1cad5afc0d | ||
|
|
fd31ac458d | ||
|
|
6292b3361d | ||
|
|
ab7a4bbad3 | ||
|
|
0f2d89858e | ||
|
|
19ffcf3376 | ||
|
|
72c0b24ead | ||
|
|
0ddfe76c31 | ||
|
|
c05c8ba648 | ||
|
|
ccdc576319 | ||
|
|
39b16ffc91 | ||
|
|
72ff1ab3ab | ||
|
|
e7c4619ee9 | ||
|
|
71fcb382ea | ||
|
|
e5b2e3ef11 | ||
|
|
dfd39522a8 | ||
|
|
2759c28196 | ||
|
|
207e199016 | ||
|
|
5f8cbdb94b | ||
|
|
0853c16d5e | ||
|
|
0be04726ca | ||
|
|
a672a86cc4 | ||
|
|
c7ff3b44dc | ||
|
|
f0ebda9ecd | ||
|
|
b984176cd0 | ||
|
|
599b1cc9ad | ||
|
|
d93df8752e | ||
|
|
8a9564bbfa | ||
|
|
5455e345b2 | ||
|
|
520101fea1 | ||
|
|
e866b4eeb1 | ||
|
|
c5c46738ee | ||
|
|
27ed95bd3e | ||
|
|
ea9ca79a8f | ||
|
|
4a97f173be | ||
|
|
8f35220c5f | ||
|
|
c3880d055d | ||
|
|
b661f67084 | ||
|
|
abd2ca8601 | ||
|
|
0905ed8ad4 | ||
|
|
c1268d4aad | ||
|
|
5ed47abc32 | ||
|
|
718bb8b86f | ||
|
|
5ab9642b79 | ||
|
|
14acab9a9c | ||
|
|
e4dd485dd4 | ||
|
|
ab1d75e5ec | ||
|
|
e4c5df6cca | ||
|
|
9ee7ee26a2 | ||
|
|
e8ebdacb91 | ||
|
|
b97921fea2 | ||
|
|
a3423195a6 | ||
|
|
f55edfbae0 | ||
|
|
3e065b6715 | ||
|
|
f14d1ad61e | ||
|
|
85d4e9cabd | ||
|
|
e47a9cffd7 | ||
|
|
ca93dc7215 | ||
|
|
93a646773c | ||
|
|
1cba693469 | ||
|
|
166149b351 | ||
|
|
1b11b6d8fb | ||
|
|
02568299df | ||
|
|
acdc93596c | ||
|
|
22e26adfb6 | ||
|
|
4730e645ba | ||
|
|
d203fab70d | ||
|
|
792fae3ed7 | ||
|
|
e443ab00c9 | ||
|
|
aee54f5756 | ||
|
|
00904e9603 | ||
|
|
b1212585e2 | ||
|
|
faca084cff | ||
|
|
89dc0b3501 | ||
|
|
ea988279a8 | ||
|
|
219bd559b6 | ||
|
|
ad1ef9b8a2 | ||
|
|
59795c605c | ||
|
|
a36ac2b6d3 | ||
|
|
a20837f252 | ||
|
|
1353fe3fdb | ||
|
|
770bd12a5c | ||
|
|
af2f5592f0 | ||
|
|
9686c41ac4 | ||
|
|
fbdb0514d2 | ||
|
|
1f5650d26b | ||
|
|
14db97c476 | ||
|
|
66a501ecf6 | ||
|
|
6b129763d4 | ||
|
|
a0368d4345 | ||
|
|
f2f090a88f | ||
|
|
6c7e1a6467 | ||
|
|
92ca7bc70d | ||
|
|
dd1e6b845b | ||
|
|
882fa9bed8 | ||
|
|
9c7dfb4030 | ||
|
|
f855908c82 | ||
|
|
293a087b8a | ||
|
|
fd72a04bb8 | ||
|
|
f131a3fa70 | ||
|
|
ccf8d8df91 | ||
|
|
b970d364f7 | ||
|
|
1da68ea69d | ||
|
|
e62e7e8eca | ||
|
|
1b1d10c461 | ||
|
|
14ea0f6d83 | ||
|
|
5107a6c39c | ||
|
|
2c77ec9e24 | ||
|
|
817a66bdf1 | ||
|
|
664a3d5533 | ||
|
|
37bc5e45b9 | ||
|
|
a18692ef1e | ||
|
|
1b023d0f5f | ||
|
|
74f74e73c4 | ||
|
|
9c7df9ad39 | ||
|
|
94adf5d24d | ||
|
|
8dbdb68175 | ||
|
|
89002eb5ec | ||
|
|
dc9ef8cf54 | ||
|
|
667697d042 | ||
|
|
bc92fa11f9 | ||
|
|
f5d5681b49 | ||
|
|
1c8e50843b | ||
|
|
487d140bd5 | ||
|
|
661ea8d3bf | ||
|
|
28d1ed6ab3 | ||
|
|
903b978e86 | ||
|
|
519f6bd46b | ||
|
|
a94a8ca28d | ||
|
|
f9dca2da5d | ||
|
|
df88ff1acb | ||
|
|
cb5aa0f170 | ||
|
|
4ea79f966e | ||
|
|
b8bc1a01b3 | ||
|
|
0e5d1815bd | ||
|
|
64e8cb65d0 | ||
|
|
7122fbaca8 | ||
|
|
3142353f98 | ||
|
|
484ca692ad | ||
|
|
1ebdfada96 | ||
|
|
3bc9dfcda1 | ||
|
|
895e5d2ca3 | ||
|
|
564992719e | ||
|
|
a737d240be | ||
|
|
d89c1ebf26 | ||
|
|
be7686bb46 | ||
|
|
4f70055f85 | ||
|
|
91ca049298 | ||
|
|
635d0378ac | ||
|
|
1c729316f7 | ||
|
|
4701c019a8 | ||
|
|
ca0d8bda4b | ||
|
|
a271ffbb10 | ||
|
|
00babaf949 | ||
|
|
2f844ac151 | ||
|
|
5178bf1d1a | ||
|
|
116afb9b6c | ||
|
|
4468a58487 | ||
|
|
2f73577e91 | ||
|
|
c1cffc8f59 | ||
|
|
6d8f85c154 | ||
|
|
0674e7f61c | ||
|
|
fde2f3ba15 | ||
|
|
70493d1a93 | ||
|
|
c20d0a76a0 | ||
|
|
e6952e241a | ||
|
|
b40812928a | ||
|
|
8cdc9773c9 | ||
|
|
8d30282edf | ||
|
|
4ba44560a9 | ||
|
|
cdc9ca7b58 | ||
|
|
7eae7c5664 | ||
|
|
739a0ffa63 | ||
|
|
637360095c | ||
|
|
4b34ab83fb | ||
|
|
ac605cbc00 | ||
|
|
4b38e5daa6 | ||
|
|
1c007b4216 | ||
|
|
193f9dfa1e | ||
|
|
1366422d96 | ||
|
|
4e778a3d28 | ||
|
|
be05508110 | ||
|
|
9119229d41 | ||
|
|
5c43c31331 | ||
|
|
014604724f | ||
|
|
7d076368e9 | ||
|
|
5d6ed5c365 | ||
|
|
a2b8f0f93c | ||
|
|
5fe8416c65 | ||
|
|
1b4d7a95af | ||
|
|
e8627fde4c | ||
|
|
6b0edc6da1 | ||
|
|
f6ad631a0f | ||
|
|
f6393a3926 | ||
|
|
d51384c3a1 | ||
|
|
78f18959fb | ||
|
|
7a661a585e | ||
|
|
f4f7a1e648 | ||
|
|
b6e3e5e823 | ||
|
|
41b1bd23d6 | ||
|
|
69458a0595 | ||
|
|
5fd107df27 | ||
|
|
2558057e9f | ||
|
|
8111daaf1d | ||
|
|
672acb234f | ||
|
|
9725062fb9 | ||
|
|
c7b1624313 | ||
|
|
67b97f1d43 | ||
|
|
6498fd1349 | ||
|
|
e371229b6c | ||
|
|
0fac33a8ff | ||
|
|
b0da062577 | ||
|
|
ca41bd59de | ||
|
|
efcad3f6f9 | ||
|
|
fa2255cbaf | ||
|
|
02b9bac899 | ||
|
|
a1fcf5023c | ||
|
|
2f8ea80e6d | ||
|
|
b798cf6a4e | ||
|
|
3da848d131 | ||
|
|
a88c33c201 | ||
|
|
7b0f037cba | ||
|
|
91b1456d06 | ||
|
|
c3b02a2bb0 | ||
|
|
8e4b7d53f4 | ||
|
|
a44145f487 | ||
|
|
bb7b3a81fb | ||
|
|
19607d71c3 | ||
|
|
96f281d789 | ||
|
|
7613eded95 | ||
|
|
50eccd2b1d | ||
|
|
ba54007102 | ||
|
|
a028f5375f | ||
|
|
9ec02cd727 | ||
|
|
4b46ece09a | ||
|
|
086bbd0908 | ||
|
|
c94eaa473e | ||
|
|
b1b809834b | ||
|
|
84183288ec | ||
|
|
86cbef83b6 | ||
|
|
006fb632c4 | ||
|
|
e3636ed8ce | ||
|
|
cfd207f251 | ||
|
|
d4c68546e7 | ||
|
|
6f4a7e16dc | ||
|
|
f64253d633 | ||
|
|
124c17aadc | ||
|
|
ab40f9fcbf | ||
|
|
5cdfa5a8d4 |
@@ -2,11 +2,17 @@
|
||||
|
||||
set -e
|
||||
|
||||
echo 'find -name \*.slint | xargs slint-tr-extractor -o rog-control-center/translations/en/rog-control-center.po'
|
||||
find -name \*.slint | xargs slint-tr-extractor -o rog-control-center/translations/en/rog-control-center.po
|
||||
|
||||
echo '+cargo +nightly fmt --all -- --check'
|
||||
cargo +nightly fmt --all -- --check
|
||||
|
||||
echo '+cargo clippy --all -- -D warnings'
|
||||
cargo clippy --all -- -D warnings
|
||||
|
||||
echo '+cargo test --all'
|
||||
cargo test --all
|
||||
cargo test --all -- --test-threads=1
|
||||
|
||||
echo '+cargo cranky'
|
||||
cargo cranky
|
||||
cargo cranky
|
||||
|
||||
@@ -52,7 +52,7 @@ test:
|
||||
<<: *rust_cache
|
||||
script:
|
||||
- mkdir -p .git/hooks > /dev/null
|
||||
- cargo test --all
|
||||
- cargo test --all -- --test-threads=1
|
||||
|
||||
release:
|
||||
only:
|
||||
@@ -63,8 +63,8 @@ release:
|
||||
- make && make vendor
|
||||
artifacts:
|
||||
paths:
|
||||
- vendor_asusctl*.tar.xz
|
||||
- cargo-config
|
||||
- vendor_asusctl*.tar.xz
|
||||
- cargo-config
|
||||
|
||||
pages:
|
||||
stage: deploy
|
||||
|
||||
32
.gitlab/issue_templates/default.md
Normal file
32
.gitlab/issue_templates/default.md
Normal file
@@ -0,0 +1,32 @@
|
||||
## Issue description
|
||||
|
||||
(** I can not support distros which are outdated by default. This includes Ubuntu at least 50% of the time, and definitely includes Mint. **)
|
||||
(Summarize the bug encountered)
|
||||
|
||||
## Steps to reproduce
|
||||
|
||||
(How can the issue be reproduced)
|
||||
|
||||
## What is the current bug behavior?
|
||||
|
||||
(What actually happens)
|
||||
|
||||
## What is the expected correct behavior?
|
||||
|
||||
(What you should see instead)
|
||||
|
||||
## Relevant logs and/or screenshots
|
||||
|
||||
(run `journalctl -b -u asusd > ~/asusd.log` and attach `~/asusd.log`)
|
||||
|
||||
(Paste any relevant logs - use code blocks (```) to format console output, logs, and code, as
|
||||
it's very hard to read otherwise.)
|
||||
|
||||
## System details
|
||||
|
||||
- Distro:
|
||||
- Kernel: (`uname -r`)
|
||||
- Desktop:
|
||||
- Xorg or wayland: ??
|
||||
|
||||
/label ~bug ~reproducable ~needs-investigation
|
||||
595
CHANGELOG.md
595
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
5745
Cargo.lock
generated
5745
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
79
Cargo.toml
79
Cargo.toml
@@ -1,34 +1,61 @@
|
||||
[workspace]
|
||||
members = ["asusctl", "asusd", "asusd-user", "config-traits", "cpuctl", "dmi-id", "rog-platform", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "rog-control-center", "simulators"]
|
||||
default-members = ["asusctl", "asusd", "asusd-user", "cpuctl", "rog-control-center"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "5.0.6"
|
||||
version = "6.1.2"
|
||||
rust-version = "1.82"
|
||||
license = "MPL-2.0"
|
||||
readme = "README.md"
|
||||
authors = ["Luke <luke@ljones.dev>"]
|
||||
repository = "https://gitlab.com/asus-linux/asusctl"
|
||||
homepage = "https://gitlab.com/asus-linux/asusctl"
|
||||
description = "Laptop feature control for ASUS ROG laptops and others"
|
||||
edition = "2021"
|
||||
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"asusctl",
|
||||
"asusd",
|
||||
"asusd-user",
|
||||
"config-traits",
|
||||
"dmi-id",
|
||||
"rog-platform",
|
||||
"rog-dbus",
|
||||
"rog-anime",
|
||||
"rog-aura",
|
||||
"rog-profiles",
|
||||
"rog-control-center",
|
||||
"rog-slash",
|
||||
"simulators",
|
||||
"rog-scsi",
|
||||
]
|
||||
|
||||
default-members = ["asusctl", "asusd", "asusd-user", "rog-control-center"]
|
||||
|
||||
[workspace.dependencies]
|
||||
async-trait = "^0.1"
|
||||
tokio = { version = "^1.23.0", default-features = false, features = ["macros", "sync"]}
|
||||
tokio = { version = "^1.39.0", default-features = false, features = [
|
||||
"macros",
|
||||
"sync",
|
||||
"time",
|
||||
"rt",
|
||||
"rt-multi-thread",
|
||||
] }
|
||||
concat-idents = "^1.1"
|
||||
dirs = "^4.0"
|
||||
smol = "^1.3"
|
||||
smol = "^2.0"
|
||||
mio = "0.8.11"
|
||||
|
||||
zbus = "~3.14.1"
|
||||
logind-zbus = { version = "~3.1" } #, default-features = false, features = ["non_blocking"] }
|
||||
futures-util = "0.3.31"
|
||||
zbus = "5.1.1"
|
||||
logind-zbus = { version = "5.0.0" } #, default-features = false, features = ["non_blocking"] }
|
||||
|
||||
serde = "^1.0"
|
||||
serde_derive = "^1.0"
|
||||
serde_json = "^1.0"
|
||||
toml = "^0.5.10"
|
||||
serde = { version = "^1.0", features = ["serde_derive"] }
|
||||
ron = "*"
|
||||
typeshare = "1.0.0"
|
||||
|
||||
log = "^0.4"
|
||||
env_logger = "^0.10.0"
|
||||
|
||||
glam = { version = "^0.22", features = ["serde"] }
|
||||
gumdrop = "^0.8"
|
||||
udev = "^0.7"
|
||||
udev = { version = "^0.8", features = ["mio"] }
|
||||
rusb = "^0.9"
|
||||
inotify = "^0.10.0"
|
||||
|
||||
@@ -37,22 +64,28 @@ pix = "^0.13"
|
||||
tinybmp = "^0.4.0"
|
||||
gif = "^0.12.0"
|
||||
|
||||
versions = "4.1"
|
||||
versions = "6.2"
|
||||
|
||||
notify-rust = { git = "https://github.com/flukejones/notify-rust.git", default-features = false, features = ["z"] }
|
||||
notify-rust = { version = "4.11.4", features = ["z", "async"] }
|
||||
|
||||
sg = { git = "https://github.com/flukejones/sg-rs.git" }
|
||||
|
||||
[profile.release]
|
||||
# thin = 57s, asusd = 9.0M
|
||||
# fat = 72s, asusd = 6.4M
|
||||
lto = "fat"
|
||||
lto = "thin"
|
||||
debug = false
|
||||
opt-level = 3
|
||||
panic = "abort"
|
||||
#codegen-units = 1
|
||||
# codegen-units = 1
|
||||
|
||||
[profile.dev]
|
||||
debug = true
|
||||
opt-level = 1
|
||||
# codegen-units = 1
|
||||
|
||||
[profile.dev.package."*"]
|
||||
opt-level = 1
|
||||
# codegen-units = 1
|
||||
|
||||
[profile.bench]
|
||||
debug = false
|
||||
@@ -61,4 +94,4 @@ opt-level = 3
|
||||
[workspace.dependencies.cargo-husky]
|
||||
version = "1"
|
||||
default-features = false
|
||||
features = ["user-hooks"]
|
||||
features = ["user-hooks"]
|
||||
|
||||
37
MANUAL.md
37
MANUAL.md
@@ -1,5 +1,7 @@
|
||||
# asusctrl manual
|
||||
|
||||
**NOTE:** this manual is in need of an update in some places. If you find issues please file issue reports.
|
||||
|
||||
`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.
|
||||
|
||||
@@ -8,11 +10,10 @@ but can also be used with non-asus laptops with reduced features.
|
||||
- `asusd`: The main system daemon. It is autostarted by a udev rule and systemd unit.
|
||||
- `asusd-user`: The user level daemon. Currently will run an anime sequence, with RGB keyboard sequences soon.
|
||||
- `asusctl`: The CLI for interacting with the system daemon
|
||||
- `asus-notify`: A notification daemon with a user systemd unit that can be enabled.
|
||||
|
||||
## `asusd`
|
||||
|
||||
`asusd` is the main system-level daemon which will control/load/save various settings in a safe way for the user, along with exposing a *safe* dbus interface for these interactions. This section covers only the daemon plus the various configuration file options.
|
||||
`asusd` is the main system-level daemon which will control/load/save various settings in a safe way for the user, along with exposing a _safe_ dbus interface for these interactions. This section covers only the daemon plus the various configuration file options.
|
||||
|
||||
The functionality that `asusd` exposes is:
|
||||
|
||||
@@ -56,6 +57,7 @@ Almost all modern ASUS laptops have charging limit control now. This can be cont
|
||||
```json
|
||||
"bat_charge_limit": 80,
|
||||
```
|
||||
|
||||
where the number is a percentage.
|
||||
|
||||
### Bios control
|
||||
@@ -63,7 +65,7 @@ where the number is a percentage.
|
||||
Some options that you find in Armory Crate are available under this controller, so far there is:
|
||||
|
||||
- POST sound: this is the sound you hear on bios boot post
|
||||
- GPU MUX: this controls if the dGPU is the *only* GPU, making it the main GPU and disabling the iGPU
|
||||
- GPU MUX: this controls if the dGPU is the _only_ GPU, making it the main GPU and disabling the iGPU
|
||||
|
||||
These options are not written to the config file as they are stored in efivars. The only way to change these is to use the exposed safe dbus methods, or use the `asusctl` CLI tool.
|
||||
|
||||
@@ -72,6 +74,7 @@ These options are not written to the config file as they are stored in efivars.
|
||||
asusctl can support setting a power profile via platform_profile drivers. This requires [power-profiles-daemon](https://gitlab.freedesktop.org/hadess/power-profiles-daemon) v0.10.0 minimum. It also requires the kernel patch for platform_profile support to be applied form [here](https://lkml.org/lkml/2021/8/18/1022) - this patch is merged to 5.15 kernel upstream.
|
||||
|
||||
A common use of asusctl is to bind the `fn+f5` (fan) key to `asusctl profile -n` to cycle through the 3 profiles:
|
||||
|
||||
1. Balanced
|
||||
2. Performance
|
||||
3. Quiet
|
||||
@@ -97,7 +100,7 @@ There is one more controller; the support controller. The sole pupose of this co
|
||||
|
||||
## asusd-user
|
||||
|
||||
`asusd-user` is a usermode daemon. The intended purpose is to provide a method for users to run there own custom per-key keyboard effects and modes, AniMe sequences, and possibly their own profiles - all without overwriting the *base* system config. As such some parts of the system daemon will migrate to the user daemon over time with the expectation that the Linux system runs both.
|
||||
`asusd-user` is a usermode daemon. The intended purpose is to provide a method for users to run there own custom per-key keyboard effects and modes, AniMe sequences, and possibly their own profiles - all without overwriting the _base_ system config. As such some parts of the system daemon will migrate to the user daemon over time with the expectation that the Linux system runs both.
|
||||
|
||||
As of now only AniMe is active in this with configuration in `~/.config/rog/`. On first run defaults are created that are intended to work as examples.
|
||||
|
||||
@@ -177,6 +180,7 @@ An Aura config itself is a file with contents:
|
||||
```
|
||||
|
||||
If your laptop supports multizone, `"led"` can also be `"Zone": <one of the following>`
|
||||
|
||||
- `SingleZone` // Keyboards with only one zone
|
||||
- `ZonedKbLeft` // keyboard left
|
||||
- `ZonedKbLeftMid` // keyboard left-middle
|
||||
@@ -238,6 +242,7 @@ Each object in the array can be one of:
|
||||
##### AsusAnimation
|
||||
|
||||
`AsusAnimation` is specifically for running the gif files that Armory Crate comes with. `asusctl` includes all of these in `/usr/share/asusd/anime/asus/`
|
||||
|
||||
```json
|
||||
"AsusAnimation": {
|
||||
"file": "<FILE_PATH>",
|
||||
@@ -260,7 +265,7 @@ Virtually the same as `AsusAnimation` but for png files, typically created in th
|
||||
|
||||
##### ImageAnimation
|
||||
|
||||
`ImageAnimation` can play *any* gif of any size.
|
||||
`ImageAnimation` can play _any_ gif of any size.
|
||||
|
||||
```json
|
||||
"ImageAnimation": {
|
||||
@@ -319,6 +324,7 @@ Must be full path: `"/usr/share/asusd/anime/asus/gaming/Controller.gif"` or `/ho
|
||||
**<FLOAT>**
|
||||
|
||||
A number from 0.0-1.0.
|
||||
|
||||
- `brightness`: If it is brightness it is combined with the system daemon global brightness
|
||||
- `scale`: 1.0 is the original size with lower number shrinking, larger growing
|
||||
- `angle`: Rotation angle in radians
|
||||
@@ -327,6 +333,7 @@ A number from 0.0-1.0.
|
||||
**<TIME>**
|
||||
|
||||
Time is the length of time to run the gif for:
|
||||
|
||||
```json
|
||||
"time": {
|
||||
"Time": {
|
||||
@@ -335,17 +342,23 @@ Time is the length of time to run the gif for:
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
A cycle is how many gif loops to run:
|
||||
|
||||
```json
|
||||
"time": {
|
||||
"Cycles": 2
|
||||
},
|
||||
```
|
||||
|
||||
`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": {
|
||||
@@ -364,6 +377,7 @@ A cycle is how many gif loops to run:
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
`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).
|
||||
|
||||
@@ -404,26 +418,19 @@ asusctl <command> <subcommand> --help
|
||||
|
||||
To switch to next/previous Aura modes you will need to bind both the aura keys (if available) to one of:
|
||||
**Next**
|
||||
|
||||
```
|
||||
asusctl led-mode -n
|
||||
```
|
||||
|
||||
**Previous**
|
||||
|
||||
```
|
||||
asusctl led-mode -p
|
||||
```
|
||||
|
||||
To switch Fan/Thermal profiles you need to bind the Fn+F5 key to `asusctl profile -n`.
|
||||
|
||||
## User NOTIFICATIONS via dbus
|
||||
|
||||
If you have a notifications handler set up, or are using KDE or Gnome then you
|
||||
can enable the user service to get basic notifications when something changes.
|
||||
|
||||
```
|
||||
systemctl --user enable asus-notify.service
|
||||
systemctl --user start asus-notify.service
|
||||
```
|
||||
|
||||
# License & Trademarks
|
||||
|
||||
Mozilla Public License 2 (MPL-2.0)
|
||||
|
||||
27
Makefile
27
Makefile
@@ -1,4 +1,4 @@
|
||||
VERSION := $(shell /usr/bin/grep -Pm1 'version = "(\d.\d.\d)"' Cargo.toml | cut -d'"' -f2)
|
||||
VERSION := $(shell /usr/bin/grep -Pm1 'version = "(\d+.\d+.\d+.*)"' Cargo.toml | cut -d'"' -f2)
|
||||
|
||||
INSTALL = install
|
||||
INSTALL_PROGRAM = ${INSTALL} -D -m 0755
|
||||
@@ -30,6 +30,11 @@ else
|
||||
TARGET = debug
|
||||
endif
|
||||
|
||||
X11 ?= 0
|
||||
ifeq ($(X11),1)
|
||||
ARGS += --features "rog-control-center/x11"
|
||||
endif
|
||||
|
||||
VENDORED ?= 0
|
||||
ifeq ($(VENDORED),1)
|
||||
ARGS += --frozen
|
||||
@@ -113,24 +118,12 @@ vendor:
|
||||
mv .cargo/config ./cargo-config
|
||||
rm -rf .cargo
|
||||
rm -rf vendor
|
||||
cargo vendor-filterer --platform x86_64-unknown-linux-gnu vendor
|
||||
cargo vendor-filterer --all-features --platform x86_64-unknown-linux-gnu vendor
|
||||
tar pcfJ vendor_asusctl_$(VERSION).tar.xz vendor
|
||||
rm -rf vendor
|
||||
|
||||
bindings:
|
||||
typeshare ./rog-anime/src/ --lang=typescript --output-file=bindings/ts/anime.ts
|
||||
typeshare ./rog-aura/src/ --lang=typescript --output-file=bindings/ts/aura.ts
|
||||
typeshare ./rog-profiles/src/ --lang=typescript --output-file=bindings/ts/profiles.ts
|
||||
typeshare ./rog-platform/src/ --lang=typescript --output-file=bindings/ts/platform.ts
|
||||
|
||||
introspect:
|
||||
gdbus introspect --system -d org.asuslinux.Daemon -o /org/asuslinux/Platform -x > bindings/dbus-xml/org-asuslinux-platform-4.xml
|
||||
gdbus introspect --system -d org.asuslinux.Daemon -o /org/asuslinux/Aura -x > bindings/dbus-xml/org-asuslinux-aura-4.xml
|
||||
gdbus introspect --system -d org.asuslinux.Daemon -o /org/asuslinux/Anime -x > bindings/dbus-xml/org-asuslinux-anime-4.xml
|
||||
gdbus introspect --system -d org.asuslinux.Daemon -o /org/asuslinux/FanCurves -x > bindings/dbus-xml/org-asuslinux-fan-curves-4.xml
|
||||
xmlstarlet ed -L -O -d '//interface[@name="org.freedesktop.DBus.Introspectable"]' bindings/dbus-xml/org-asuslinux-*
|
||||
xmlstarlet ed -L -O -d '//interface[@name="org.freedesktop.DBus.Properties"]' bindings/dbus-xml/org-asuslinux-*
|
||||
xmlstarlet ed -L -O -d '//interface[@name="org.freedesktop.DBus.Peer"]' bindings/dbus-xml/org-asuslinux-*
|
||||
translate:
|
||||
find -name \*.slint | xargs slint-tr-extractor -o rog-control-center/translations/en/rog-control-center.po
|
||||
|
||||
build:
|
||||
ifeq ($(VENDORED),1)
|
||||
@@ -139,7 +132,7 @@ ifeq ($(VENDORED),1)
|
||||
tar pxf vendor_asusctl_$(VERSION).tar.xz
|
||||
endif
|
||||
cargo build $(ARGS)
|
||||
ifneq ($(STRIP_BINARIES),0)
|
||||
ifeq ($(STRIP_BINARIES),1)
|
||||
strip -s ./target/$(TARGET)/$(BIN_C)
|
||||
strip -s ./target/$(TARGET)/$(BIN_D)
|
||||
strip -s ./target/$(TARGET)/$(BIN_U)
|
||||
|
||||
88
README.md
88
README.md
@@ -1,6 +1,6 @@
|
||||
# `asusctl` for ASUS ROG
|
||||
|
||||
[Become a Patron!](https://www.patreon.com/bePatron?u=7602281) - [Asus Linux Website](https://asus-linux.org/)
|
||||
[](https://www.patreon.com/bePatron?u=7602281) [](https://ko-fi.com/V7V5CLU67) - [Asus Linux Website](https://asus-linux.org/)
|
||||
|
||||
**WARNING:** Many features are developed in tandem with kernel patches. If you see a feature is missing you either need a patched kernel or latest release.
|
||||
|
||||
@@ -11,7 +11,15 @@ Now includes a GUI, `rog-control-center`.
|
||||
|
||||
## Kernel support
|
||||
|
||||
**The minimum supported kernel version is 6.6**
|
||||
Due to on-going driver work the minimum suggested kernel version is always **the latest*, as improvements and fixes are continuous.
|
||||
|
||||
Support for some new features is not avilable unless you run a patched kernel with the work I am doing [in this github repo](https://github.com/flukejones/linux/tree/wip/ally-6.13). Use the linked branch, or `wip/ally-6.12`. Everything that is done here is upstreamed eventually (a long process).
|
||||
|
||||
Z13 devices will need [these](https://lore.kernel.org/linux-input/20240416090402.31057-1-luke@ljones.dev/T/#t)
|
||||
|
||||
## X11 support
|
||||
|
||||
X11 is not supported at all, as in I will not help you with X11 issues if there are any due to limited time and it being unmaintained itself. You can however build `rog-control-center` with it enabled `cargo build --features "rog-control-center/x11"`.
|
||||
|
||||
## Goals
|
||||
|
||||
@@ -31,7 +39,7 @@ See the [rog-aura readme](./rog-aura/README.md) for more details.
|
||||
|
||||
## Discord
|
||||
|
||||
[Discord server link](https://discord.gg/WTHnqabm)
|
||||
[](https://discord.gg/B8GftRW2Hd)
|
||||
|
||||
## SUPPORTED LAPTOPS
|
||||
|
||||
@@ -41,7 +49,9 @@ to this:
|
||||
```
|
||||
Bus 001 Device 002: ID 0b05:1866 ASUSTek Computer, Inc. N-KEY Device
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
Bus 003 Device 002: ID 0b05:19b6 ASUSTek Computer, Inc. [unknown]
|
||||
```
|
||||
@@ -51,49 +61,32 @@ and AniMe parts should work regardless of your latop make.
|
||||
|
||||
## Implemented
|
||||
|
||||
- [X] System daemon
|
||||
- [X] GUI app (includes tray and notifications)
|
||||
- [X] Setting/modifying built-in LED modes
|
||||
- [X] Per-key LED setting
|
||||
- [X] Fancy LED modes (See examples) (currently being reworked)
|
||||
- [X] AniMatrix display on G14 and M16 models that include it
|
||||
- [X] Set battery charge limit (with kernel supporting this)
|
||||
- [X] Fan curve control on supported laptops (G14/G15, some TUF like FA507)
|
||||
- [X] Toggle bios setting for boot/POST sound
|
||||
- [X] Toggle GPU MUX (g-sync, or called MUX on 2022+ laptops)
|
||||
The list is a bit outdated as many features have been enabled in the Linux kernel with upstream patches and then supported in asusctl suite.
|
||||
|
||||
- [x] System daemon
|
||||
- [x] GUI app (includes tray and notifications)
|
||||
- [x] Setting/modifying built-in LED modes
|
||||
- [x] Per-key LED setting
|
||||
- [x] Fancy LED modes (See examples) (currently being reworked)
|
||||
- [x] AniMatrix display on G14 and M16 models that include it
|
||||
- [x] Set battery charge limit (with kernel supporting this)
|
||||
- [x] Fan curve control on supported laptops (G14/G15, some TUF like FA507)
|
||||
- [x] Toggle bios setting for boot/POST sound
|
||||
- [x] Toggle GPU MUX (g-sync, or called MUX on 2022+ laptops)
|
||||
|
||||
# GUI
|
||||
|
||||
A gui is now in the repo - ROG Control Center. At this time it is still a WIP, but it has almost all features in place already.
|
||||
|
||||

|
||||

|
||||

|
||||
**NOTE**: Xorg is not supported.
|
||||
|
||||
# BUILDING
|
||||
|
||||
Requirements are rust >= 1.57 installed from rustup.io if the distro provided version is too old, and `make`.
|
||||
|
||||
**Ubuntu (unsuported):**
|
||||
|
||||
apt install libgtk-3-dev libpango1.0-dev libgdk-pixbuf-2.0-dev libglib2.0-dev cmake libclang-dev libudev-dev libayatana-appindicator3-1
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
source "$HOME/.cargo/env"
|
||||
make
|
||||
sudo make install
|
||||
|
||||
**popos (unsuported):**
|
||||
|
||||
sudo apt install cmake libclang-dev libudev-dev libgtk-3-dev libclang-dev libglib2.0-dev libatkmm-1.6-dev libpangomm-1.4-dev librust-gdk-pixbuf-dev
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
source "$HOME/.cargo/env"
|
||||
make
|
||||
sudo make install
|
||||
|
||||
Rust and cargo are required, they can be installed from [rustup.rs](https://rustup.rs/) or from the distro repos if newer than 1.75.
|
||||
|
||||
**fedora:**
|
||||
|
||||
dnf install cmake clang-devel systemd-devel glib2-devel cairo-devel atkmm-devel pangomm-devel gdk-pixbuf2-devel gtk3-devel libappindicator-gtk3
|
||||
dnf install cmake clang-devel libxkbcommon-devel systemd-devel expat-devel pcre2-devel libzstd-devel gtk3-devel
|
||||
make
|
||||
sudo make install
|
||||
|
||||
@@ -102,28 +95,43 @@ Requirements are rust >= 1.57 installed from rustup.io if the distro provided ve
|
||||
Works with KDE Plasma (without GTK packages)
|
||||
|
||||
zypper in -t pattern devel_basis
|
||||
zypper in rustup make cmake systemd-devel clang-devel llvm-devel gdk-pixbuf-devel cairo-devel pango-devel freetype-devel gtk3-devel libexpat-devel libayatana-indicator3-7
|
||||
zypper in rustup make cmake clang-devel libxkbcommon-devel systemd-devel expat-devel pcre2-devel libzstd-devel gtk3-devel
|
||||
make
|
||||
sudo make install
|
||||
|
||||
**Debian(unsuported):**
|
||||
|
||||
officially unsuported,but you can still try and test it by yourself(some features may not be available).
|
||||
|
||||
sudo apt install libclang-dev libudev-dev libfontconfig-dev build-essential cmake libxkbcommon-dev
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
make
|
||||
sudo make install
|
||||
|
||||
**Ubuntu, Popos (unsuported):**
|
||||
|
||||
instructions removed as outdated
|
||||
|
||||
## Installing
|
||||
|
||||
- Fedora copr = https://copr.fedorainfracloud.org/coprs/lukenukem/asus-linux/
|
||||
- openSUSE = https://download.opensuse.org/repositories/home:/luke_nukem:/asus/
|
||||
- Ubuntu = not supported due to packaging woes, but you can build and install on your own.
|
||||
|
||||
=======
|
||||
|
||||
The default init method is to use the udev rule, this ensures that the service is
|
||||
started when the device is initialised and ready.
|
||||
|
||||
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.
|
||||
|
||||
## Upgrading
|
||||
|
||||
If you are upgrading from a previous installed version, you will need to restart the service or reboot.
|
||||
|
||||
```
|
||||
$ 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.
|
||||
|
||||
## Uninstalling
|
||||
|
||||
Run `sudo make uninstall` in the source repo, and remove `/etc/asusd/`.
|
||||
@@ -140,7 +148,7 @@ Dbus introsepction XML requires with `make introspection` requires `anime_sim` t
|
||||
|
||||
## AniMe Matrix simulator
|
||||
|
||||
A simulator using SDL2 can be built using `cargo build --package rog_simulators` and run with `./target/debug/anime_sim`. Once started `asusd` will need restarting to pick it up. If running this sim on a laptop *with* the display, the simulated display will be used instead of the physical display.
|
||||
A simulator using SDL2 can be built using `cargo build --package rog_simulators` and run with `./target/debug/anime_sim`. Once started `asusd` will need restarting to pick it up. If running this sim on a laptop _with_ the display, the simulated display will be used instead of the physical display.
|
||||
|
||||
## Supporting more laptops
|
||||
|
||||
|
||||
@@ -1,26 +1,29 @@
|
||||
[package]
|
||||
name = "asusctl"
|
||||
license = "MPL-2.0"
|
||||
authors = ["Luke D Jones <luke@ljones.dev>"]
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
version.workspace = true
|
||||
readme.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
rog_anime = { path = "../rog-anime" }
|
||||
rog_scsi = { path = "../rog-scsi" }
|
||||
rog_slash = { path = "../rog-slash" }
|
||||
rog_aura = { path = "../rog-aura" }
|
||||
rog_dbus = { path = "../rog-dbus" }
|
||||
rog_profiles = { path = "../rog-profiles" }
|
||||
rog_platform = { path = "../rog-platform" }
|
||||
asusd = { path = "../asusd" }
|
||||
dmi_id = { path = "../dmi-id" }
|
||||
|
||||
log.workspace = true
|
||||
env_logger.workspace = true
|
||||
|
||||
ron.workspace = true
|
||||
gumdrop.workspace = true
|
||||
toml.workspace = true
|
||||
zbus.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
gif.workspace = true
|
||||
tinybmp.workspace = true
|
||||
glam.workspace = true
|
||||
rog_dbus = { path = "../rog-dbus" }
|
||||
|
||||
cargo-husky.workspace = true
|
||||
@@ -5,10 +5,12 @@ use std::process::exit;
|
||||
|
||||
use rog_anime::usb::get_anime_type;
|
||||
use rog_anime::{AnimeDiagonal, AnimeType};
|
||||
use rog_dbus::RogDbusClientBlocking;
|
||||
use rog_dbus::zbus_anime::AnimeProxyBlocking;
|
||||
use zbus::blocking::Connection;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||
let conn = Connection::system().unwrap();
|
||||
let proxy = AnimeProxyBlocking::new(&conn).unwrap();
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() != 3 {
|
||||
@@ -21,16 +23,12 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
Path::new(&args[1]),
|
||||
None,
|
||||
args[2].parse::<f32>().unwrap(),
|
||||
AnimeType::GA401,
|
||||
AnimeType::GA401
|
||||
)?;
|
||||
|
||||
let anime_type = get_anime_type()?;
|
||||
let anime_type = get_anime_type();
|
||||
|
||||
client
|
||||
.proxies()
|
||||
.anime()
|
||||
.write(matrix.into_data_buffer(anime_type)?)
|
||||
.unwrap();
|
||||
proxy.write(matrix.into_data_buffer(anime_type)?).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ use std::time::Duration;
|
||||
|
||||
use rog_anime::usb::get_anime_type;
|
||||
use rog_anime::{AnimeDiagonal, AnimeType};
|
||||
use rog_dbus::RogDbusClientBlocking;
|
||||
use rog_dbus::zbus_anime::AnimeProxyBlocking;
|
||||
use zbus::blocking::Connection;
|
||||
|
||||
// In usable data:
|
||||
// Top row start at 1, ends at 32
|
||||
@@ -11,7 +12,8 @@ use rog_dbus::RogDbusClientBlocking;
|
||||
// 74w x 36h diagonal used by the windows app
|
||||
|
||||
fn main() {
|
||||
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||
let conn = Connection::system().unwrap();
|
||||
let proxy = AnimeProxyBlocking::new(&conn).unwrap();
|
||||
|
||||
for step in (2..50).rev() {
|
||||
let mut matrix = AnimeDiagonal::new(AnimeType::GA401, None);
|
||||
@@ -27,10 +29,8 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
let anime_type = get_anime_type().unwrap();
|
||||
client
|
||||
.proxies()
|
||||
.anime()
|
||||
let anime_type = get_anime_type();
|
||||
proxy
|
||||
.write(matrix.into_data_buffer(anime_type).unwrap())
|
||||
.unwrap();
|
||||
sleep(Duration::from_millis(300));
|
||||
|
||||
@@ -4,10 +4,12 @@ use std::thread::sleep;
|
||||
|
||||
use rog_anime::usb::get_anime_type;
|
||||
use rog_anime::{ActionData, ActionLoader, Sequences};
|
||||
use rog_dbus::RogDbusClientBlocking;
|
||||
use rog_dbus::zbus_anime::AnimeProxyBlocking;
|
||||
use zbus::blocking::Connection;
|
||||
|
||||
fn main() {
|
||||
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||
let conn = Connection::system().unwrap();
|
||||
let proxy = AnimeProxyBlocking::new(&conn).unwrap();
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() != 3 {
|
||||
@@ -17,27 +19,20 @@ fn main() {
|
||||
|
||||
let path = Path::new(&args[1]);
|
||||
let brightness = args[2].parse::<f32>().unwrap();
|
||||
let anime_type = get_anime_type().unwrap();
|
||||
let anime_type = get_anime_type();
|
||||
let mut seq = Sequences::new(anime_type);
|
||||
seq.insert(
|
||||
0,
|
||||
&ActionLoader::AsusAnimation {
|
||||
file: path.into(),
|
||||
time: rog_anime::AnimTime::Infinite,
|
||||
brightness,
|
||||
},
|
||||
)
|
||||
seq.insert(0, &ActionLoader::AsusAnimation {
|
||||
file: path.into(),
|
||||
time: rog_anime::AnimTime::Infinite,
|
||||
brightness
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
loop {
|
||||
for action in seq.iter() {
|
||||
if let ActionData::Animation(frames) = action {
|
||||
for frame in frames.frames() {
|
||||
client
|
||||
.proxies()
|
||||
.anime()
|
||||
.write(frame.frame().clone())
|
||||
.unwrap();
|
||||
proxy.write(frame.frame().clone()).unwrap();
|
||||
sleep(frame.delay());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ use std::convert::TryFrom;
|
||||
|
||||
use rog_anime::usb::get_anime_type;
|
||||
use rog_anime::{AnimeDataBuffer, AnimeGrid};
|
||||
use rog_dbus::RogDbusClientBlocking;
|
||||
use rog_dbus::zbus_anime::AnimeProxyBlocking;
|
||||
use zbus::blocking::Connection;
|
||||
|
||||
// In usable data:
|
||||
// Top row start at 1, ends at 32
|
||||
@@ -10,8 +11,10 @@ use rog_dbus::RogDbusClientBlocking;
|
||||
// 74w x 36h diagonal used by the windows app
|
||||
|
||||
fn main() {
|
||||
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||
let anime_type = get_anime_type().unwrap();
|
||||
let conn = Connection::system().unwrap();
|
||||
let proxy = AnimeProxyBlocking::new(&conn).unwrap();
|
||||
|
||||
let anime_type = get_anime_type();
|
||||
let mut matrix = AnimeGrid::new(anime_type);
|
||||
let tmp = matrix.get_mut();
|
||||
|
||||
@@ -43,5 +46,5 @@ fn main() {
|
||||
|
||||
let matrix = <AnimeDataBuffer>::try_from(matrix).unwrap();
|
||||
|
||||
client.proxies().anime().write(matrix).unwrap();
|
||||
proxy.write(matrix).unwrap();
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
use rog_anime::usb::get_anime_type;
|
||||
use rog_anime::AnimeDataBuffer;
|
||||
use rog_dbus::RogDbusClientBlocking;
|
||||
use rog_dbus::zbus_anime::AnimeProxyBlocking;
|
||||
use zbus::blocking::Connection;
|
||||
|
||||
// In usable data:
|
||||
// Top row start at 1, ends at 32
|
||||
|
||||
fn main() {
|
||||
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||
let anime_type = get_anime_type().unwrap();
|
||||
let conn = Connection::system().unwrap();
|
||||
let proxy = AnimeProxyBlocking::new(&conn).unwrap();
|
||||
let anime_type = get_anime_type();
|
||||
let mut matrix = AnimeDataBuffer::new(anime_type);
|
||||
matrix.data_mut()[1] = 100; // start = 1
|
||||
for n in matrix.data_mut()[2..32].iter_mut() {
|
||||
@@ -127,5 +129,5 @@ fn main() {
|
||||
matrix.data_mut()[1244] = 100; // end
|
||||
println!("{:?}", &matrix);
|
||||
|
||||
client.proxies().anime().write(matrix).unwrap();
|
||||
proxy.write(matrix).unwrap();
|
||||
}
|
||||
|
||||
@@ -6,10 +6,12 @@ use std::process::exit;
|
||||
|
||||
use rog_anime::usb::get_anime_type;
|
||||
use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2};
|
||||
use rog_dbus::RogDbusClientBlocking;
|
||||
use rog_dbus::zbus_anime::AnimeProxyBlocking;
|
||||
use zbus::blocking::Connection;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||
let conn = Connection::system().unwrap();
|
||||
let proxy = AnimeProxyBlocking::new(&conn).unwrap();
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() != 7 {
|
||||
@@ -18,24 +20,20 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
let anime_type = get_anime_type()?;
|
||||
let anime_type = get_anime_type();
|
||||
let matrix = AnimeImage::from_png(
|
||||
Path::new(&args[1]),
|
||||
args[2].parse::<f32>().unwrap(),
|
||||
args[3].parse::<f32>().unwrap(),
|
||||
Vec2::new(
|
||||
args[4].parse::<f32>().unwrap(),
|
||||
args[5].parse::<f32>().unwrap(),
|
||||
args[5].parse::<f32>().unwrap()
|
||||
),
|
||||
args[6].parse::<f32>().unwrap(),
|
||||
anime_type,
|
||||
anime_type
|
||||
)?;
|
||||
|
||||
client
|
||||
.proxies()
|
||||
.anime()
|
||||
.write(<AnimeDataBuffer>::try_from(&matrix)?)
|
||||
.unwrap();
|
||||
proxy.write(<AnimeDataBuffer>::try_from(&matrix)?).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -9,10 +9,12 @@ use std::time::Duration;
|
||||
|
||||
use rog_anime::usb::get_anime_type;
|
||||
use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2};
|
||||
use rog_dbus::RogDbusClientBlocking;
|
||||
use rog_dbus::zbus_anime::AnimeProxyBlocking;
|
||||
use zbus::blocking::Connection;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||
let conn = Connection::system().unwrap();
|
||||
let proxy = AnimeProxyBlocking::new(&conn).unwrap();
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() != 7 {
|
||||
@@ -21,17 +23,17 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
let anime_type = get_anime_type()?;
|
||||
let anime_type = get_anime_type();
|
||||
let mut matrix = AnimeImage::from_png(
|
||||
Path::new(&args[1]),
|
||||
args[2].parse::<f32>().unwrap(),
|
||||
args[3].parse::<f32>().unwrap(),
|
||||
Vec2::new(
|
||||
args[4].parse::<f32>().unwrap(),
|
||||
args[5].parse::<f32>().unwrap(),
|
||||
args[5].parse::<f32>().unwrap()
|
||||
),
|
||||
args[6].parse::<f32>().unwrap(),
|
||||
anime_type,
|
||||
anime_type
|
||||
)?;
|
||||
|
||||
loop {
|
||||
@@ -41,11 +43,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
}
|
||||
matrix.update();
|
||||
|
||||
client
|
||||
.proxies()
|
||||
.anime()
|
||||
.write(<AnimeDataBuffer>::try_from(&matrix)?)
|
||||
.unwrap();
|
||||
proxy.write(<AnimeDataBuffer>::try_from(&matrix)?).unwrap();
|
||||
sleep(Duration::from_micros(500));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
//! Using a combination of key-colour array plus a key layout to generate
|
||||
//! outputs.
|
||||
|
||||
use rog_aura::advanced::LedCode;
|
||||
use rog_aura::effects::{AdvancedEffects, Effect};
|
||||
use rog_aura::layouts::KeyLayout;
|
||||
use rog_aura::keyboard::{KeyLayout, LedCode};
|
||||
use rog_aura::Colour;
|
||||
use rog_dbus::RogDbusClientBlocking;
|
||||
use rog_dbus::zbus_aura::AuraProxyBlocking;
|
||||
use zbus::blocking::Connection;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let layout = KeyLayout::default_layout();
|
||||
|
||||
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||
let conn = Connection::system().unwrap();
|
||||
let proxy = AuraProxyBlocking::new(&conn).unwrap();
|
||||
|
||||
let mut seq = AdvancedEffects::new(true);
|
||||
|
||||
@@ -35,10 +36,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Colour {
|
||||
r: 200,
|
||||
g: 110,
|
||||
b: 0,
|
||||
b: 0
|
||||
},
|
||||
100,
|
||||
10,
|
||||
10
|
||||
));
|
||||
seq.push(zone);
|
||||
|
||||
@@ -62,7 +63,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
seq.next_state(&layout);
|
||||
let packets = seq.create_packets();
|
||||
|
||||
client.proxies().aura().direct_addressing_raw(packets)?;
|
||||
proxy.direct_addressing_raw(packets)?;
|
||||
std::thread::sleep(std::time::Duration::from_millis(33));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ pub struct AnimeCommand {
|
||||
#[options(no_short, meta = "", help = "Off with his head!!!")]
|
||||
pub off_with_his_head: Option<bool>,
|
||||
#[options(command)]
|
||||
pub command: Option<AnimeActions>,
|
||||
pub command: Option<AnimeActions>
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
@@ -54,7 +54,7 @@ pub enum AnimeActions {
|
||||
#[options(help = "display an animated diagonal/pixel-perfect GIF")]
|
||||
PixelGif(AnimeGifDiagonal),
|
||||
#[options(help = "change which builtin animations are shown")]
|
||||
SetBuiltins(Builtins),
|
||||
SetBuiltins(Builtins)
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
@@ -82,7 +82,7 @@ pub struct Builtins {
|
||||
)]
|
||||
pub shutdown: AnimShutdown,
|
||||
#[options(meta = "", help = "set/apply the animations <true/false>")]
|
||||
pub set: Option<bool>,
|
||||
pub set: Option<bool>
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
@@ -100,7 +100,7 @@ pub struct AnimeImage {
|
||||
#[options(meta = "", default = "0.0", help = "the angle in radians")]
|
||||
pub angle: f32,
|
||||
#[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")]
|
||||
pub bright: f32,
|
||||
pub bright: f32
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
@@ -110,7 +110,7 @@ pub struct AnimeImageDiagonal {
|
||||
#[options(meta = "", help = "full path to the png to display")]
|
||||
pub path: String,
|
||||
#[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")]
|
||||
pub bright: f32,
|
||||
pub bright: f32
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
@@ -134,7 +134,7 @@ pub struct AnimeGif {
|
||||
default = "1",
|
||||
help = "how many loops to play - 0 is infinite"
|
||||
)]
|
||||
pub loops: u32,
|
||||
pub loops: u32
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
@@ -150,5 +150,5 @@ pub struct AnimeGifDiagonal {
|
||||
default = "1",
|
||||
help = "how many loops to play - 0 is infinite"
|
||||
)]
|
||||
pub loops: u32,
|
||||
pub loops: u32
|
||||
}
|
||||
|
||||
@@ -10,14 +10,14 @@ pub struct LedPowerCommand1 {
|
||||
pub help: bool,
|
||||
#[options(meta = "", help = "Control if LEDs enabled while awake <true/false>")]
|
||||
pub awake: Option<bool>,
|
||||
#[options(meta = "", help = "Use with awake option <true/false>")]
|
||||
pub keyboard: Option<bool>,
|
||||
#[options(meta = "", help = "Use with awake option <true/false>")]
|
||||
pub lightbar: Option<bool>,
|
||||
#[options(help = "Use with awake option, if excluded defaults to false")]
|
||||
pub keyboard: bool,
|
||||
#[options(help = "Use with awake option, if excluded defaults to false")]
|
||||
pub lightbar: bool,
|
||||
#[options(meta = "", help = "Control boot animations <true/false>")]
|
||||
pub boot: Option<bool>,
|
||||
#[options(meta = "", help = "Control suspend animations <true/false>")]
|
||||
pub sleep: Option<bool>,
|
||||
pub sleep: Option<bool>
|
||||
}
|
||||
|
||||
#[derive(Options, Debug)]
|
||||
@@ -25,7 +25,7 @@ pub struct LedPowerCommand2 {
|
||||
#[options(help = "print help message")]
|
||||
pub help: bool,
|
||||
#[options(command)]
|
||||
pub command: Option<SetAuraZoneEnabled>,
|
||||
pub command: Option<SetAuraZoneEnabled>
|
||||
}
|
||||
|
||||
#[derive(Options, Debug)]
|
||||
@@ -41,6 +41,8 @@ pub enum SetAuraZoneEnabled {
|
||||
Lid(AuraPowerStates),
|
||||
#[options(help = "")]
|
||||
RearGlow(AuraPowerStates),
|
||||
#[options(help = "")]
|
||||
Ally(AuraPowerStates)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Options)]
|
||||
@@ -54,12 +56,12 @@ pub struct AuraPowerStates {
|
||||
#[options(help = "defaults to false if option unused")]
|
||||
pub sleep: bool,
|
||||
#[options(help = "defaults to false if option unused")]
|
||||
pub shutdown: bool,
|
||||
pub shutdown: bool
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
pub struct LedBrightness {
|
||||
level: Option<u8>,
|
||||
level: Option<u8>
|
||||
}
|
||||
impl LedBrightness {
|
||||
pub fn new(level: Option<u8>) -> Self {
|
||||
@@ -87,13 +89,14 @@ impl FromStr for LedBrightness {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[allow(clippy::to_string_trait_impl)]
|
||||
impl ToString for LedBrightness {
|
||||
fn to_string(&self) -> String {
|
||||
let s = match self.level {
|
||||
Some(0x00) => "low",
|
||||
Some(0x01) => "med",
|
||||
Some(0x02) => "high",
|
||||
_ => "unknown",
|
||||
_ => "unknown"
|
||||
};
|
||||
s.to_owned()
|
||||
}
|
||||
@@ -110,7 +113,7 @@ pub struct SingleSpeed {
|
||||
meta = "",
|
||||
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
|
||||
)]
|
||||
pub zone: AuraZone,
|
||||
pub zone: AuraZone
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Options, Default)]
|
||||
@@ -126,7 +129,7 @@ pub struct SingleSpeedDirection {
|
||||
meta = "",
|
||||
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
|
||||
)]
|
||||
pub zone: AuraZone,
|
||||
pub zone: AuraZone
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Options)]
|
||||
@@ -140,7 +143,7 @@ pub struct SingleColour {
|
||||
meta = "",
|
||||
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
|
||||
)]
|
||||
pub zone: AuraZone,
|
||||
pub zone: AuraZone
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Options)]
|
||||
@@ -156,7 +159,7 @@ pub struct SingleColourSpeed {
|
||||
meta = "",
|
||||
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
|
||||
)]
|
||||
pub zone: AuraZone,
|
||||
pub zone: AuraZone
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Options, Default)]
|
||||
@@ -174,7 +177,7 @@ pub struct TwoColourSpeed {
|
||||
meta = "",
|
||||
help = "set the zone for this effect e.g, 0, 1, one, logo, lightbar-left"
|
||||
)]
|
||||
pub zone: AuraZone,
|
||||
pub zone: AuraZone
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Options)]
|
||||
@@ -188,7 +191,7 @@ pub struct MultiZone {
|
||||
#[options(short = "c", meta = "", help = "set the RGB value e.g, ff00ff")]
|
||||
pub colour3: Colour,
|
||||
#[options(short = "d", meta = "", help = "set the RGB value e.g, ff00ff")]
|
||||
pub colour4: Colour,
|
||||
pub colour4: Colour
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Options)]
|
||||
@@ -204,7 +207,7 @@ pub struct MultiColourSpeed {
|
||||
#[options(short = "d", meta = "", help = "set the RGB value e.g, ff00ff")]
|
||||
pub colour4: Colour,
|
||||
#[options(no_long, meta = "", help = "set the speed: low, med, high")]
|
||||
pub speed: Speed,
|
||||
pub speed: Speed
|
||||
}
|
||||
|
||||
/// Byte value for setting the built-in mode.
|
||||
@@ -214,29 +217,29 @@ pub struct MultiColourSpeed {
|
||||
#[derive(Options)]
|
||||
pub enum SetAuraBuiltin {
|
||||
#[options(help = "set a single static colour")]
|
||||
Static(SingleColour),
|
||||
Static(SingleColour), // 0
|
||||
#[options(help = "pulse between one or two colours")]
|
||||
Breathe(TwoColourSpeed),
|
||||
Breathe(TwoColourSpeed), // 1
|
||||
#[options(help = "strobe through all colours")]
|
||||
Strobe(SingleSpeed),
|
||||
RainbowCycle(SingleSpeed), // 2
|
||||
#[options(help = "rainbow cycling in one of four directions")]
|
||||
Rainbow(SingleSpeedDirection),
|
||||
RainbowWave(SingleSpeedDirection), // 3
|
||||
#[options(help = "rain pattern mimicking raindrops")]
|
||||
Stars(TwoColourSpeed),
|
||||
Stars(TwoColourSpeed), // 4
|
||||
#[options(help = "rain pattern of three preset colours")]
|
||||
Rain(SingleSpeed),
|
||||
Rain(SingleSpeed), // 5
|
||||
#[options(help = "pressed keys are highlighted to fade")]
|
||||
Highlight(SingleColourSpeed),
|
||||
Highlight(SingleColourSpeed), // 6
|
||||
#[options(help = "pressed keys generate horizontal laser")]
|
||||
Laser(SingleColourSpeed),
|
||||
Laser(SingleColourSpeed), // 7
|
||||
#[options(help = "pressed keys ripple outwards like a splash")]
|
||||
Ripple(SingleColourSpeed),
|
||||
Ripple(SingleColourSpeed), // 8
|
||||
#[options(help = "set a rapid pulse")]
|
||||
Pulse(SingleColour),
|
||||
Pulse(SingleColour), // 10
|
||||
#[options(help = "set a vertical line zooming from left")]
|
||||
Comet(SingleColour),
|
||||
Comet(SingleColour), // 11
|
||||
#[options(help = "set a wide vertical line zooming from left")]
|
||||
Flash(SingleColour),
|
||||
Flash(SingleColour) // 12
|
||||
}
|
||||
|
||||
impl Default for SetAuraBuiltin {
|
||||
@@ -311,14 +314,14 @@ impl From<&SetAuraBuiltin> for AuraEffect {
|
||||
data.mode = AuraModeNum::Breathe;
|
||||
data
|
||||
}
|
||||
SetAuraBuiltin::Strobe(x) => {
|
||||
SetAuraBuiltin::RainbowCycle(x) => {
|
||||
let mut data: AuraEffect = x.into();
|
||||
data.mode = AuraModeNum::Strobe;
|
||||
data.mode = AuraModeNum::RainbowCycle;
|
||||
data
|
||||
}
|
||||
SetAuraBuiltin::Rainbow(x) => {
|
||||
SetAuraBuiltin::RainbowWave(x) => {
|
||||
let mut data: AuraEffect = x.into();
|
||||
data.mode = AuraModeNum::Rainbow;
|
||||
data.mode = AuraModeNum::RainbowWave;
|
||||
data
|
||||
}
|
||||
SetAuraBuiltin::Stars(x) => {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use gumdrop::Options;
|
||||
use rog_platform::platform::PlatformPolicy;
|
||||
use rog_platform::platform::PlatformProfile;
|
||||
|
||||
use crate::anime_cli::AnimeCommand;
|
||||
use crate::aura_cli::{LedBrightness, LedPowerCommand1, LedPowerCommand2, SetAuraBuiltin};
|
||||
use crate::fan_curve_cli::FanCurveCommand;
|
||||
use crate::scsi_cli::ScsiCommand;
|
||||
use crate::slash_cli::SlashCommand;
|
||||
|
||||
#[derive(Default, Options)]
|
||||
pub struct CliStart {
|
||||
@@ -21,18 +23,20 @@ pub struct CliStart {
|
||||
pub prev_kbd_bright: bool,
|
||||
#[options(meta = "", help = "Set your battery charge limit <20-100>")]
|
||||
pub chg_limit: Option<u8>,
|
||||
#[options(help = "Toggle one-shot battery charge to 100%")]
|
||||
pub one_shot_chg: bool,
|
||||
#[options(command)]
|
||||
pub command: Option<CliCommand>,
|
||||
pub command: Option<CliCommand>
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
pub enum CliCommand {
|
||||
#[options(help = "Set the keyboard lighting from built-in modes")]
|
||||
LedMode(LedModeCommand),
|
||||
Aura(LedModeCommand),
|
||||
#[options(help = "Set the LED power states")]
|
||||
LedPow1(LedPowerCommand1),
|
||||
AuraPowerOld(LedPowerCommand1),
|
||||
#[options(help = "Set the LED power states")]
|
||||
LedPow2(LedPowerCommand2),
|
||||
AuraPower(LedPowerCommand2),
|
||||
#[options(help = "Set or select platform_profile")]
|
||||
Profile(ProfileCommand),
|
||||
#[options(help = "Set, select, or modify fan curves if supported")]
|
||||
@@ -41,8 +45,15 @@ pub enum CliCommand {
|
||||
Graphics(GraphicsCommand),
|
||||
#[options(name = "anime", help = "Manage AniMe Matrix")]
|
||||
Anime(AnimeCommand),
|
||||
#[options(help = "Change bios settings")]
|
||||
Bios(BiosCommand),
|
||||
#[options(name = "slash", help = "Manage Slash Ledbar")]
|
||||
Slash(SlashCommand),
|
||||
#[options(name = "scsi", help = "Manage SCSI external drive")]
|
||||
Scsi(ScsiCommand),
|
||||
#[options(
|
||||
help = "Change platform settings. This is a new interface exposed by the asus-armoury \
|
||||
driver, some of the settings will be the same as the older platform interface"
|
||||
)]
|
||||
Armoury(ArmouryCommand)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Options)]
|
||||
@@ -60,7 +71,7 @@ pub struct ProfileCommand {
|
||||
pub profile_get: bool,
|
||||
|
||||
#[options(meta = "", help = "set the active profile")]
|
||||
pub profile_set: Option<PlatformPolicy>,
|
||||
pub profile_set: Option<PlatformProfile>
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
@@ -72,44 +83,22 @@ pub struct LedModeCommand {
|
||||
#[options(help = "switch to previous aura mode")]
|
||||
pub prev_mode: bool,
|
||||
#[options(command)]
|
||||
pub command: Option<SetAuraBuiltin>,
|
||||
pub command: Option<SetAuraBuiltin>
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
pub struct GraphicsCommand {
|
||||
#[options(help = "print help message")]
|
||||
pub help: bool,
|
||||
pub help: bool
|
||||
}
|
||||
|
||||
#[derive(Options, Debug)]
|
||||
pub struct BiosCommand {
|
||||
pub struct ArmouryCommand {
|
||||
#[options(help = "print help message")]
|
||||
pub help: bool,
|
||||
#[options(
|
||||
meta = "",
|
||||
short = "S",
|
||||
no_long,
|
||||
help = "set bios POST sound: asusctl -S <true/false>"
|
||||
free,
|
||||
help = "append each value name followed by the value to set. `-1` sets to default"
|
||||
)]
|
||||
pub post_sound_set: Option<bool>,
|
||||
#[options(no_long, short = "s", help = "read bios POST sound")]
|
||||
pub post_sound_get: bool,
|
||||
#[options(
|
||||
meta = "",
|
||||
short = "D",
|
||||
no_long,
|
||||
help = "Switch GPU MUX mode: 0 = Discrete, 1 = Optimus, reboot required"
|
||||
)]
|
||||
pub gpu_mux_mode_set: Option<u8>,
|
||||
#[options(no_long, short = "d", help = "get GPU mode")]
|
||||
pub gpu_mux_mode_get: bool,
|
||||
#[options(
|
||||
meta = "",
|
||||
short = "O",
|
||||
no_long,
|
||||
help = "Set device panel overdrive <true/false>"
|
||||
)]
|
||||
pub panel_overdrive_set: Option<bool>,
|
||||
#[options(no_long, short = "o", help = "get panel overdrive")]
|
||||
pub panel_overdrive_get: bool,
|
||||
pub free: Vec<String>
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use gumdrop::Options;
|
||||
use rog_platform::platform::PlatformPolicy;
|
||||
use rog_platform::platform::PlatformProfile;
|
||||
use rog_profiles::fan_curve_set::CurveData;
|
||||
use rog_profiles::FanCurvePU;
|
||||
|
||||
@@ -18,7 +18,7 @@ pub struct FanCurveCommand {
|
||||
meta = "",
|
||||
help = "profile to modify fan-curve for. Shows data if no options provided"
|
||||
)]
|
||||
pub mod_profile: Option<PlatformPolicy>,
|
||||
pub mod_profile: Option<PlatformProfile>,
|
||||
|
||||
#[options(
|
||||
meta = "",
|
||||
@@ -45,5 +45,5 @@ pub struct FanCurveCommand {
|
||||
help = "data format = 30c:1%,49c:2%,59c:3%,69c:4%,79c:31%,89c:49%,99c:56%,109c:58%. \
|
||||
`--mod-profile` required. If '%' is omitted the fan range is 0-255"
|
||||
)]
|
||||
pub data: Option<CurveData>,
|
||||
pub data: Option<CurveData>
|
||||
}
|
||||
|
||||
1204
asusctl/src/main.rs
1204
asusctl/src/main.rs
File diff suppressed because it is too large
Load Diff
35
asusctl/src/scsi_cli.rs
Normal file
35
asusctl/src/scsi_cli.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use gumdrop::Options;
|
||||
use rog_scsi::{AuraMode, Colour, Direction, Speed};
|
||||
|
||||
#[derive(Options)]
|
||||
pub struct ScsiCommand {
|
||||
#[options(help = "print help message")]
|
||||
pub help: bool,
|
||||
|
||||
#[options(help = "Enable the SCSI drive LEDs")]
|
||||
pub enable: Option<bool>,
|
||||
|
||||
#[options(meta = "", help = "Set LED mode (so 'list' for all options)")]
|
||||
pub mode: Option<AuraMode>,
|
||||
|
||||
#[options(
|
||||
meta = "",
|
||||
help = "Set LED mode speed <slowest, slow, med, fast, fastest> (does not apply to all)"
|
||||
)]
|
||||
pub speed: Option<Speed>,
|
||||
|
||||
#[options(
|
||||
meta = "",
|
||||
help = "Set LED mode direction <forward, reverse> (does not apply to all)"
|
||||
)]
|
||||
pub direction: Option<Direction>,
|
||||
|
||||
#[options(
|
||||
meta = "",
|
||||
help = "Set LED colours <hex>, specify up to 4 with repeated arg"
|
||||
)]
|
||||
pub colours: Vec<Colour>,
|
||||
|
||||
#[options(help = "list available animations")]
|
||||
pub list: bool
|
||||
}
|
||||
35
asusctl/src/slash_cli.rs
Normal file
35
asusctl/src/slash_cli.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use gumdrop::Options;
|
||||
use rog_slash::SlashMode;
|
||||
|
||||
#[derive(Options)]
|
||||
pub struct SlashCommand {
|
||||
#[options(help = "print help message")]
|
||||
pub help: bool,
|
||||
#[options(help = "Enable the Slash Ledbar")]
|
||||
pub enable: bool,
|
||||
#[options(help = "Disable the Slash Ledbar")]
|
||||
pub disable: bool,
|
||||
#[options(short = "l", meta = "", help = "Set brightness value <0-255>")]
|
||||
pub brightness: Option<u8>,
|
||||
#[options(meta = "", help = "Set interval value <0-5>")]
|
||||
pub interval: Option<u8>,
|
||||
#[options(meta = "", help = "Set SlashMode (so 'list' for all options)")]
|
||||
pub mode: Option<SlashMode>,
|
||||
#[options(help = "list available animations")]
|
||||
pub list: bool,
|
||||
|
||||
#[options(short = "B", meta = "", help = "Show the animation on boot")]
|
||||
pub show_on_boot: Option<bool>,
|
||||
#[options(short = "S", meta = "", help = "Show the animation on shutdown")]
|
||||
pub show_on_shutdown: Option<bool>,
|
||||
#[options(short = "s", meta = "", help = "Show the animation on sleep")]
|
||||
pub show_on_sleep: Option<bool>,
|
||||
#[options(short = "b", meta = "", help = "Show the animation on battery")]
|
||||
pub show_on_battery: Option<bool>,
|
||||
#[options(
|
||||
short = "w",
|
||||
meta = "",
|
||||
help = "Show the low-battery warning animation"
|
||||
)]
|
||||
pub show_battery_warning: Option<bool>
|
||||
}
|
||||
@@ -1,23 +1,28 @@
|
||||
[package]
|
||||
name = "asusd-user"
|
||||
license = "MPL-2.0"
|
||||
license.workspace = true
|
||||
version.workspace = true
|
||||
authors = ["Luke D Jones <luke@ljones.dev>"]
|
||||
edition = "2021"
|
||||
description = "Usermode daemon for user settings, anime, per-key lighting"
|
||||
readme.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[[bin]]
|
||||
name = "asusd-user"
|
||||
path = "src/daemon.rs"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
local_data = []
|
||||
|
||||
[dependencies]
|
||||
dirs.workspace = true
|
||||
smol.workspace = true
|
||||
|
||||
# serialisation
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde_derive.workspace = true
|
||||
ron.workspace = true
|
||||
|
||||
rog_anime = { path = "../rog-anime" }
|
||||
rog_aura = { path = "../rog-aura" }
|
||||
@@ -26,10 +31,4 @@ rog_platform = { path = "../rog-platform" }
|
||||
config-traits = { path = "../config-traits" }
|
||||
|
||||
zbus.workspace = true
|
||||
|
||||
# cli and logging
|
||||
log.workspace = true
|
||||
env_logger.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
cargo-husky.workspace = true
|
||||
@@ -3,10 +3,10 @@ use std::time::Duration;
|
||||
|
||||
use config_traits::{StdConfig, StdConfigLoad};
|
||||
use rog_anime::{ActionLoader, AnimTime, AnimeType, Fade, Sequences as AnimeSequences, Vec2};
|
||||
use rog_aura::advanced::LedCode;
|
||||
use rog_aura::effects::{AdvancedEffects as AuraSequences, Breathe, DoomFlicker, Effect, Static};
|
||||
use rog_aura::keyboard::LedCode;
|
||||
use rog_aura::{Colour, Speed};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
@@ -21,7 +21,7 @@ fn root_conf_dir() -> PathBuf {
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct ConfigAnime {
|
||||
pub name: String,
|
||||
pub anime: Vec<ActionLoader>,
|
||||
pub anime: Vec<ActionLoader>
|
||||
}
|
||||
|
||||
impl ConfigAnime {
|
||||
@@ -52,8 +52,8 @@ impl Default for ConfigAnime {
|
||||
time: AnimTime::Fade(Fade::new(
|
||||
Duration::from_secs(2),
|
||||
None,
|
||||
Duration::from_secs(2),
|
||||
)),
|
||||
Duration::from_secs(2)
|
||||
))
|
||||
},
|
||||
ActionLoader::AsusAnimation {
|
||||
file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(),
|
||||
@@ -61,8 +61,8 @@ impl Default for ConfigAnime {
|
||||
time: AnimTime::Fade(Fade::new(
|
||||
Duration::from_secs(6),
|
||||
None,
|
||||
Duration::from_secs(3),
|
||||
)),
|
||||
Duration::from_secs(3)
|
||||
))
|
||||
},
|
||||
ActionLoader::ImageAnimation {
|
||||
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
|
||||
@@ -73,8 +73,8 @@ impl Default for ConfigAnime {
|
||||
time: AnimTime::Fade(Fade::new(
|
||||
Duration::from_secs(2),
|
||||
Some(Duration::from_secs(2)),
|
||||
Duration::from_secs(2),
|
||||
)),
|
||||
Duration::from_secs(2)
|
||||
))
|
||||
},
|
||||
ActionLoader::Image {
|
||||
file: "/usr/share/asusd/anime/custom/rust.png".into(),
|
||||
@@ -84,9 +84,9 @@ impl Default for ConfigAnime {
|
||||
time: AnimTime::Fade(Fade::new(
|
||||
Duration::from_secs(2),
|
||||
Some(Duration::from_secs(1)),
|
||||
Duration::from_secs(2),
|
||||
Duration::from_secs(2)
|
||||
)),
|
||||
brightness: 0.6,
|
||||
brightness: 0.6
|
||||
},
|
||||
ActionLoader::Pause(Duration::from_secs(1)),
|
||||
ActionLoader::ImageAnimation {
|
||||
@@ -95,9 +95,9 @@ impl Default for ConfigAnime {
|
||||
angle: 0.0,
|
||||
translation: Vec2::new(3.0, 2.0),
|
||||
brightness: 0.5,
|
||||
time: AnimTime::Count(2),
|
||||
time: AnimTime::Count(2)
|
||||
},
|
||||
],
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,7 +121,7 @@ impl StdConfigLoad for ConfigAnime {}
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct ConfigAura {
|
||||
pub name: String,
|
||||
pub aura: AuraSequences,
|
||||
pub aura: AuraSequences
|
||||
}
|
||||
|
||||
impl ConfigAura {
|
||||
@@ -139,14 +139,14 @@ impl Default for ConfigAura {
|
||||
Colour {
|
||||
r: 255,
|
||||
g: 0,
|
||||
b: 20,
|
||||
b: 20
|
||||
},
|
||||
Colour {
|
||||
r: 20,
|
||||
g: 255,
|
||||
b: 0,
|
||||
b: 0
|
||||
},
|
||||
Speed::Low,
|
||||
Speed::Low
|
||||
));
|
||||
|
||||
seq.push(key.clone());
|
||||
@@ -161,7 +161,7 @@ impl Default for ConfigAura {
|
||||
LedCode::F,
|
||||
Colour { r: 255, g: 0, b: 0 },
|
||||
Colour { r: 255, g: 0, b: 0 },
|
||||
Speed::High,
|
||||
Speed::High
|
||||
));
|
||||
seq.push(key);
|
||||
|
||||
@@ -176,13 +176,13 @@ impl Default for ConfigAura {
|
||||
LedCode::N9,
|
||||
Colour { r: 0, g: 0, b: 255 },
|
||||
80,
|
||||
40,
|
||||
40
|
||||
));
|
||||
seq.push(key);
|
||||
|
||||
Self {
|
||||
name: "aura-default".to_owned(),
|
||||
aura: seq,
|
||||
aura: seq
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,14 +209,14 @@ pub struct ConfigBase {
|
||||
/// Name of active anime config file in the user config directory
|
||||
pub active_anime: Option<String>,
|
||||
/// Name of active aura config file in the user config directory
|
||||
pub active_aura: Option<String>,
|
||||
pub active_aura: Option<String>
|
||||
}
|
||||
|
||||
impl StdConfig for ConfigBase {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
active_anime: Some("anime-default".to_owned()),
|
||||
active_aura: Some("aura-default".to_owned()),
|
||||
active_aura: Some("aura-default".to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,10 @@ use std::time::{Duration, Instant};
|
||||
use config_traits::StdConfig;
|
||||
use rog_anime::error::AnimeError;
|
||||
use rog_anime::{ActionData, ActionLoader, AnimTime, Fade, Sequences, Vec2};
|
||||
use rog_dbus::RogDbusClientBlocking;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use zbus::dbus_interface;
|
||||
use rog_dbus::zbus_anime::AnimeProxyBlocking;
|
||||
use ron::ser::PrettyConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use zbus::interface;
|
||||
use zbus::zvariant::{ObjectPath, Type};
|
||||
|
||||
use crate::config::ConfigAnime;
|
||||
@@ -24,7 +25,7 @@ pub struct Timer {
|
||||
/// Used only for `TimeType::Timer`, milliseonds to fade the image in for
|
||||
fade_in: u64,
|
||||
/// Used only for `TimeType::Timer`, milliseonds to fade the image out for
|
||||
fade_out: u64,
|
||||
fade_out: u64
|
||||
}
|
||||
|
||||
impl From<Timer> for AnimTime {
|
||||
@@ -45,7 +46,7 @@ impl From<Timer> for AnimTime {
|
||||
}
|
||||
}
|
||||
TimeType::Count => AnimTime::Count(time.count as u32),
|
||||
TimeType::Infinite => AnimTime::Infinite,
|
||||
TimeType::Infinite => AnimTime::Infinite
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,33 +55,33 @@ impl From<Timer> for AnimTime {
|
||||
pub enum TimeType {
|
||||
Timer,
|
||||
Count,
|
||||
Infinite,
|
||||
Infinite
|
||||
}
|
||||
|
||||
/// The inner object exists to allow the zbus proxy to share it with a runner
|
||||
/// thread and a zbus server behind `Arc<Mutex<T>>`
|
||||
pub struct CtrlAnimeInner<'a> {
|
||||
sequences: Sequences,
|
||||
client: RogDbusClientBlocking<'a>,
|
||||
do_early_return: Arc<AtomicBool>,
|
||||
client: AnimeProxyBlocking<'a>,
|
||||
do_early_return: Arc<AtomicBool>
|
||||
}
|
||||
|
||||
impl<'a> CtrlAnimeInner<'static> {
|
||||
impl CtrlAnimeInner<'static> {
|
||||
pub fn new(
|
||||
sequences: Sequences,
|
||||
client: RogDbusClientBlocking<'static>,
|
||||
do_early_return: Arc<AtomicBool>,
|
||||
client: AnimeProxyBlocking<'static>,
|
||||
do_early_return: Arc<AtomicBool>
|
||||
) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
sequences,
|
||||
client,
|
||||
do_early_return,
|
||||
do_early_return
|
||||
})
|
||||
}
|
||||
|
||||
/// To be called on each main loop iteration to pump out commands to the
|
||||
/// anime
|
||||
pub fn run(&'a self) -> Result<(), Error> {
|
||||
pub fn run(&self) -> Result<(), Error> {
|
||||
if self.do_early_return.load(Ordering::SeqCst) {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -93,19 +94,13 @@ impl<'a> CtrlAnimeInner<'static> {
|
||||
return Ok(true); // Do safe exit
|
||||
}
|
||||
self.client
|
||||
.proxies()
|
||||
.anime()
|
||||
.write(output)
|
||||
.map_err(|e| AnimeError::Dbus(format!("{}", e)))
|
||||
.map(|_| false)
|
||||
});
|
||||
}
|
||||
ActionData::Image(image) => {
|
||||
self.client
|
||||
.proxies()
|
||||
.anime()
|
||||
.write(image.as_ref().clone())
|
||||
.ok();
|
||||
self.client.write(image.as_ref().clone()).ok();
|
||||
}
|
||||
ActionData::Pause(duration) => {
|
||||
let start = Instant::now();
|
||||
@@ -132,34 +127,31 @@ impl<'a> CtrlAnimeInner<'static> {
|
||||
|
||||
pub struct CtrlAnime<'a> {
|
||||
config: Arc<Mutex<ConfigAnime>>,
|
||||
client: RogDbusClientBlocking<'a>,
|
||||
client: AnimeProxyBlocking<'a>,
|
||||
inner: Arc<Mutex<CtrlAnimeInner<'a>>>,
|
||||
/// Must be the same Atomic as in CtrlAnimeInner
|
||||
inner_early_return: Arc<AtomicBool>,
|
||||
inner_early_return: Arc<AtomicBool>
|
||||
}
|
||||
|
||||
impl CtrlAnime<'static> {
|
||||
pub fn new(
|
||||
config: Arc<Mutex<ConfigAnime>>,
|
||||
inner: Arc<Mutex<CtrlAnimeInner<'static>>>,
|
||||
client: RogDbusClientBlocking<'static>,
|
||||
inner_early_return: Arc<AtomicBool>,
|
||||
client: AnimeProxyBlocking<'static>,
|
||||
inner_early_return: Arc<AtomicBool>
|
||||
) -> Result<Self, Error> {
|
||||
Ok(CtrlAnime {
|
||||
config,
|
||||
client,
|
||||
inner,
|
||||
inner_early_return,
|
||||
inner_early_return
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn add_to_server(self, server: &mut zbus::Connection) {
|
||||
server
|
||||
.object_server()
|
||||
.at(
|
||||
&ObjectPath::from_str_unchecked("/org/asuslinux/Anime"),
|
||||
self,
|
||||
)
|
||||
.at(&ObjectPath::from_str_unchecked("/xyz/ljones/Anime"), self)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
println!("CtrlAnime: add_to_server {}", err);
|
||||
@@ -175,14 +167,14 @@ impl CtrlAnime<'static> {
|
||||
// - Do actions
|
||||
// - Write config if required
|
||||
// - Unset inner_early_return
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
#[interface(name = "xyz.ljones.Asusd")]
|
||||
impl CtrlAnime<'static> {
|
||||
pub fn insert_asus_gif(
|
||||
&mut self,
|
||||
index: u32,
|
||||
file: &str,
|
||||
time: Timer,
|
||||
brightness: f32,
|
||||
brightness: f32
|
||||
) -> zbus::fdo::Result<String> {
|
||||
if let Ok(mut config) = self.config.try_lock() {
|
||||
let time: AnimTime = time.into();
|
||||
@@ -190,7 +182,7 @@ impl CtrlAnime<'static> {
|
||||
let action = ActionLoader::AsusAnimation {
|
||||
file: file.into(),
|
||||
brightness,
|
||||
time,
|
||||
time
|
||||
};
|
||||
|
||||
// Must make the inner run loop return early
|
||||
@@ -205,11 +197,12 @@ impl CtrlAnime<'static> {
|
||||
config.anime.push(action);
|
||||
config.write();
|
||||
|
||||
let json = serde_json::to_string_pretty(&*config).expect("Parse config to JSON failed");
|
||||
let ron = ron::ser::to_string_pretty(&*config, PrettyConfig::new().depth_limit(4))
|
||||
.expect("Parse config to RON failed");
|
||||
|
||||
// Release the inner run loop again
|
||||
self.inner_early_return.store(false, Ordering::SeqCst);
|
||||
return Ok(json);
|
||||
return Ok(ron);
|
||||
}
|
||||
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
|
||||
}
|
||||
@@ -223,7 +216,7 @@ impl CtrlAnime<'static> {
|
||||
angle: f32,
|
||||
xy: (f32, f32),
|
||||
time: Timer,
|
||||
brightness: f32,
|
||||
brightness: f32
|
||||
) -> zbus::fdo::Result<String> {
|
||||
if let Ok(mut config) = self.config.try_lock() {
|
||||
let time: AnimTime = time.into();
|
||||
@@ -235,7 +228,7 @@ impl CtrlAnime<'static> {
|
||||
angle,
|
||||
translation,
|
||||
brightness,
|
||||
time,
|
||||
time
|
||||
};
|
||||
|
||||
// Must make the inner run loop return early
|
||||
@@ -250,12 +243,11 @@ impl CtrlAnime<'static> {
|
||||
config.anime.push(action);
|
||||
config.write();
|
||||
|
||||
let json =
|
||||
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
|
||||
|
||||
let ron = ron::ser::to_string_pretty(&*config, PrettyConfig::new().depth_limit(4))
|
||||
.expect("Parse config to RON failed");
|
||||
// Release the inner run loop again
|
||||
self.inner_early_return.store(false, Ordering::SeqCst);
|
||||
return Ok(json);
|
||||
return Ok(ron);
|
||||
}
|
||||
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
|
||||
}
|
||||
@@ -269,7 +261,7 @@ impl CtrlAnime<'static> {
|
||||
angle: f32,
|
||||
xy: (f32, f32),
|
||||
time: Timer,
|
||||
brightness: f32,
|
||||
brightness: f32
|
||||
) -> zbus::fdo::Result<String> {
|
||||
if let Ok(mut config) = self.config.try_lock() {
|
||||
let file = Path::new(&file);
|
||||
@@ -280,7 +272,7 @@ impl CtrlAnime<'static> {
|
||||
angle,
|
||||
translation: Vec2::new(xy.0, xy.1),
|
||||
brightness,
|
||||
time,
|
||||
time
|
||||
};
|
||||
|
||||
// Must make the inner run loop return early
|
||||
@@ -295,12 +287,11 @@ impl CtrlAnime<'static> {
|
||||
config.anime.push(action);
|
||||
config.write();
|
||||
|
||||
let json =
|
||||
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
|
||||
|
||||
let ron = ron::ser::to_string_pretty(&*config, PrettyConfig::new().depth_limit(4))
|
||||
.expect("Parse config to RON failed");
|
||||
// Release the inner run loop again
|
||||
self.inner_early_return.store(false, Ordering::SeqCst);
|
||||
return Ok(json);
|
||||
return Ok(ron);
|
||||
}
|
||||
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
|
||||
}
|
||||
@@ -320,12 +311,11 @@ impl CtrlAnime<'static> {
|
||||
config.anime.push(action);
|
||||
config.write();
|
||||
|
||||
let json =
|
||||
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
|
||||
|
||||
let ron = ron::ser::to_string_pretty(&*config, PrettyConfig::new().depth_limit(4))
|
||||
.expect("Parse config to RON failed");
|
||||
// Release the inner run loop again
|
||||
self.inner_early_return.store(false, Ordering::SeqCst);
|
||||
return Ok(json);
|
||||
return Ok(ron);
|
||||
}
|
||||
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
|
||||
}
|
||||
@@ -343,12 +333,11 @@ impl CtrlAnime<'static> {
|
||||
}
|
||||
config.write();
|
||||
|
||||
let json =
|
||||
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
|
||||
|
||||
let ron = ron::ser::to_string_pretty(&*config, PrettyConfig::new().depth_limit(4))
|
||||
.expect("Parse config to RON failed");
|
||||
// Release the inner run loop again
|
||||
self.inner_early_return.store(false, Ordering::SeqCst);
|
||||
return Ok(json);
|
||||
return Ok(ron);
|
||||
}
|
||||
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
|
||||
}
|
||||
@@ -356,13 +345,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().set_enable_display(on).ok();
|
||||
self.client.set_enable_display(on).ok();
|
||||
// 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().set_enable_display(on).ok();
|
||||
self.client.set_enable_display(on).ok();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -7,9 +7,11 @@ use asusd_user::config::*;
|
||||
use asusd_user::ctrl_anime::{CtrlAnime, CtrlAnimeInner};
|
||||
use config_traits::{StdConfig, StdConfigLoad};
|
||||
use rog_anime::usb::get_anime_type;
|
||||
use rog_aura::aura_detection::LaptopLedData;
|
||||
use rog_aura::layouts::KeyLayout;
|
||||
use rog_dbus::{RogDbusClientBlocking, DBUS_NAME};
|
||||
use rog_aura::aura_detection::LedSupportData;
|
||||
use rog_aura::keyboard::KeyLayout;
|
||||
use rog_dbus::zbus_anime::AnimeProxyBlocking;
|
||||
use rog_dbus::zbus_aura::AuraProxyBlocking;
|
||||
use rog_dbus::{list_iface_blocking, DBUS_NAME};
|
||||
use smol::Executor;
|
||||
use zbus::Connection;
|
||||
|
||||
@@ -32,25 +34,22 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!(" rog-dbus v{}", rog_dbus::VERSION);
|
||||
println!("rog-platform v{}", rog_platform::VERSION);
|
||||
|
||||
let (client, _) = RogDbusClientBlocking::new()?;
|
||||
let supported = client
|
||||
.proxies()
|
||||
.platform()
|
||||
.supported_interfaces()
|
||||
.unwrap_or_default()
|
||||
.contains(&"Anime".to_string());
|
||||
let conn = zbus::blocking::Connection::system().unwrap();
|
||||
|
||||
let supported = list_iface_blocking()?;
|
||||
let config = ConfigBase::new().load();
|
||||
let executor = Executor::new();
|
||||
|
||||
let early_return = Arc::new(AtomicBool::new(false));
|
||||
// Set up the anime data and run loop/thread
|
||||
if supported {
|
||||
if supported.contains(&"xyz.ljones.Anime".to_string()) {
|
||||
if let Some(cfg) = config.active_anime {
|
||||
let anime_type = get_anime_type()?;
|
||||
let anime_type = get_anime_type();
|
||||
let anime_config = ConfigAnime::new().set_name(cfg).load();
|
||||
let anime = anime_config.create(anime_type)?;
|
||||
let anime_config = Arc::new(Mutex::new(anime_config));
|
||||
|
||||
let anime_proxy_blocking = AnimeProxyBlocking::new(&conn).unwrap();
|
||||
executor
|
||||
.spawn(async move {
|
||||
// Create server
|
||||
@@ -59,12 +58,21 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
// Inner behind mutex required for thread safety
|
||||
let inner = Arc::new(Mutex::new(
|
||||
CtrlAnimeInner::new(anime, client, early_return.clone()).unwrap(),
|
||||
CtrlAnimeInner::new(
|
||||
anime,
|
||||
anime_proxy_blocking.clone(),
|
||||
early_return.clone()
|
||||
)
|
||||
.unwrap()
|
||||
));
|
||||
// Need new client object for dbus control part
|
||||
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||
let anime_control =
|
||||
CtrlAnime::new(anime_config, inner.clone(), client, early_return).unwrap();
|
||||
let anime_control = CtrlAnime::new(
|
||||
anime_config,
|
||||
inner.clone(),
|
||||
anime_proxy_blocking,
|
||||
early_return
|
||||
)
|
||||
.unwrap();
|
||||
anime_control.add_to_server(&mut connection).await;
|
||||
loop {
|
||||
if let Ok(inner) = inner.clone().try_lock() {
|
||||
@@ -81,7 +89,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut aura_config = ConfigAura::new().set_name(cfg).load();
|
||||
// let baord_name = std::fs::read_to_string(BOARD_NAME)?;
|
||||
|
||||
let led_support = LaptopLedData::get_data();
|
||||
let led_support = LedSupportData::get_data("");
|
||||
|
||||
let layout = KeyLayout::find_layout(led_support, PathBuf::from(DATA_DIR))
|
||||
.map_err(|e| {
|
||||
@@ -89,22 +97,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
})
|
||||
.unwrap_or_else(|_| KeyLayout::default_layout());
|
||||
|
||||
let aura_proxy_blocking = AuraProxyBlocking::new(&conn).unwrap();
|
||||
executor
|
||||
.spawn(async move {
|
||||
// Create server
|
||||
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||
// let connection = Connection::session().await.unwrap();
|
||||
// connection.request_name(DBUS_NAME).await.unwrap();
|
||||
|
||||
loop {
|
||||
aura_config.aura.next_state(&layout);
|
||||
let packets = aura_config.aura.create_packets();
|
||||
|
||||
client
|
||||
.proxies()
|
||||
.aura()
|
||||
.direct_addressing_raw(packets)
|
||||
.unwrap();
|
||||
aura_proxy_blocking.direct_addressing_raw(packets).unwrap();
|
||||
std::thread::sleep(std::time::Duration::from_millis(33));
|
||||
}
|
||||
})
|
||||
|
||||
@@ -8,7 +8,7 @@ pub enum Error {
|
||||
ConfigLoadFail,
|
||||
ConfigLockFail,
|
||||
XdgVars,
|
||||
Anime(AnimeError),
|
||||
Anime(AnimeError)
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
@@ -19,7 +19,7 @@ impl fmt::Display for Error {
|
||||
Error::ConfigLoadFail => write!(f, "Failed to load user config"),
|
||||
Error::ConfigLockFail => write!(f, "Failed to lock user config"),
|
||||
Error::XdgVars => write!(f, "XDG environment vars appear unset"),
|
||||
Error::Anime(err) => write!(f, "Anime error: {}", err),
|
||||
Error::Anime(err) => write!(f, "Anime error: {}", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! # `DBus` interface proxy for: `org.asuslinux.Daemon`
|
||||
//! # `DBus` interface proxy for: `xyz.ljones.Asusd`
|
||||
//!
|
||||
//! This code was generated by `zbus-xmlgen` `1.0.0` from `DBus` introspection
|
||||
//! data. Source: `Interface '/org/asuslinux/Anime' from service
|
||||
//! 'org.asuslinux.Daemon' on session bus`.
|
||||
//! data. Source: `Interface '/xyz/ljones/Anime' from service
|
||||
//! 'xyz.ljones.Asusd' on session bus`.
|
||||
//!
|
||||
//! You may prefer to adapt it, instead of using it verbatim.
|
||||
//!
|
||||
@@ -21,12 +21,9 @@
|
||||
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
|
||||
use zbus::dbus_proxy;
|
||||
use zbus::proxy;
|
||||
|
||||
#[dbus_proxy(
|
||||
interface = "org.asuslinux.Daemon",
|
||||
default_path = "/org/asuslinux/Anime"
|
||||
)]
|
||||
#[proxy(interface = "xyz.ljones.Asusd", default_path = "/xyz/ljones/Anime")]
|
||||
trait Daemon {
|
||||
/// InsertAsusGif method
|
||||
fn insert_asus_gif(
|
||||
@@ -35,7 +32,7 @@ trait Daemon {
|
||||
file: &str,
|
||||
time: u32,
|
||||
count: u32,
|
||||
brightness: f64,
|
||||
brightness: f64
|
||||
) -> zbus::Result<String>;
|
||||
|
||||
/// InsertImage method
|
||||
@@ -46,7 +43,7 @@ trait Daemon {
|
||||
scale: f64,
|
||||
angle: f64,
|
||||
xy: &(f64, f64),
|
||||
brightness: f64,
|
||||
brightness: f64
|
||||
) -> zbus::Result<String>;
|
||||
|
||||
/// InsertImageGif method
|
||||
@@ -59,7 +56,7 @@ trait Daemon {
|
||||
xy: &(f64, f64),
|
||||
time: u32,
|
||||
count: u32,
|
||||
brightness: f64,
|
||||
brightness: f64
|
||||
) -> zbus::Result<String>;
|
||||
|
||||
/// InsertPause method
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
[package]
|
||||
name = "asusd"
|
||||
license = "MPL-2.0"
|
||||
license.workspace = true
|
||||
version.workspace = true
|
||||
readme = "README.md"
|
||||
authors = ["Luke <luke@ljones.dev>"]
|
||||
repository = "https://gitlab.com/asus-linux/asus-nb-ctrl"
|
||||
homepage = "https://gitlab.com/asus-linux/asus-nb-ctrl"
|
||||
description = "A daemon app for ASUS GX502 and similar laptops to control missing features"
|
||||
edition = "2021"
|
||||
readme.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[[bin]]
|
||||
name = "asusd"
|
||||
@@ -16,30 +16,32 @@ path = "src/daemon.rs"
|
||||
[dependencies]
|
||||
config-traits = { path = "../config-traits" }
|
||||
rog_anime = { path = "../rog-anime", features = ["dbus"] }
|
||||
rog_slash = { path = "../rog-slash", features = ["dbus"] }
|
||||
rog_aura = { path = "../rog-aura", features = ["dbus"] }
|
||||
rog_scsi = { path = "../rog-scsi", features = ["dbus"] }
|
||||
rog_platform = { path = "../rog-platform" }
|
||||
rog_profiles = { path = "../rog-profiles" }
|
||||
dmi_id = { path = "../dmi-id" }
|
||||
futures-lite = "*"
|
||||
udev.workspace = true
|
||||
inotify.workspace = true
|
||||
|
||||
async-trait.workspace = true
|
||||
mio.workspace = true
|
||||
tokio.workspace = true
|
||||
# console-subscriber = "0.2.0"
|
||||
|
||||
# cli and logging
|
||||
log.workspace = true
|
||||
env_logger.workspace = true
|
||||
|
||||
futures-util.workspace = true
|
||||
zbus.workspace = true
|
||||
logind-zbus.workspace = true
|
||||
|
||||
# serialisation
|
||||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
|
||||
concat-idents.workspace = true
|
||||
|
||||
systemd-zbus = "*"
|
||||
|
||||
[dev-dependencies]
|
||||
cargo-husky.workspace = true
|
||||
cargo-husky.workspace = true
|
||||
|
||||
425
asusd/src/asus_armoury.rs
Normal file
425
asusd/src/asus_armoury.rs
Normal file
@@ -0,0 +1,425 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use config_traits::StdConfig;
|
||||
use futures_util::lock::Mutex;
|
||||
use log::{debug, error, info};
|
||||
use rog_platform::asus_armoury::{AttrValue, Attribute, FirmwareAttribute, FirmwareAttributes};
|
||||
use rog_platform::platform::{PlatformProfile, RogPlatform};
|
||||
use rog_platform::power::AsusPower;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use zbus::object_server::SignalEmitter;
|
||||
use zbus::zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Type, Value};
|
||||
use zbus::{fdo, interface, Connection};
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::error::RogError;
|
||||
use crate::{Reloadable, ASUS_ZBUS_PATH};
|
||||
|
||||
const MOD_NAME: &str = "asus_armoury";
|
||||
|
||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, Type, Value, OwnedValue)]
|
||||
pub struct PossibleValues {
|
||||
strings: Vec<String>,
|
||||
nums: Vec<i32>
|
||||
}
|
||||
|
||||
fn dbus_path_for_attr(attr_name: &str) -> OwnedObjectPath {
|
||||
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{attr_name}")).into()
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AsusArmouryAttribute {
|
||||
attr: Attribute,
|
||||
config: Arc<Mutex<Config>>,
|
||||
/// platform control required here for access to PPD or Throttle profile
|
||||
platform: RogPlatform,
|
||||
power: AsusPower
|
||||
}
|
||||
|
||||
impl AsusArmouryAttribute {
|
||||
pub fn new(
|
||||
attr: Attribute,
|
||||
platform: RogPlatform,
|
||||
power: AsusPower,
|
||||
config: Arc<Mutex<Config>>
|
||||
) -> Self {
|
||||
Self {
|
||||
attr,
|
||||
config,
|
||||
platform,
|
||||
power
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn move_to_zbus(self, connection: &Connection) -> Result<(), RogError> {
|
||||
let path = dbus_path_for_attr(self.attr.name());
|
||||
connection
|
||||
.object_server()
|
||||
.at(path.clone(), self)
|
||||
.await
|
||||
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
|
||||
.ok();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn watch_and_notify(
|
||||
&mut self,
|
||||
signal_ctxt: SignalEmitter<'static>
|
||||
) -> Result<(), RogError> {
|
||||
use futures_util::StreamExt;
|
||||
|
||||
let name = self.name();
|
||||
macro_rules! watch_value_notify {
|
||||
($attr_str:expr, $fn_prop_changed:ident) => {
|
||||
match self.attr.get_watcher($attr_str) {
|
||||
Ok(watch) => {
|
||||
let name = <&str>::from(name);
|
||||
let ctrl = self.clone();
|
||||
let sig = signal_ctxt.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut buffer = [0; 32];
|
||||
watch
|
||||
.into_event_stream(&mut buffer)
|
||||
.unwrap()
|
||||
.for_each(|_| async {
|
||||
debug!("{} changed", name);
|
||||
ctrl.$fn_prop_changed(&sig).await.ok();
|
||||
})
|
||||
.await;
|
||||
});
|
||||
}
|
||||
Err(e) => info!(
|
||||
"inotify watch failed: {}. You can ignore this if your device does not \
|
||||
support the feature",
|
||||
e
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// "current_value", "default_value", "min_value", "max_value"
|
||||
watch_value_notify!("current_value", current_value_changed);
|
||||
watch_value_notify!("default_value", default_value_changed);
|
||||
watch_value_notify!("min_value", min_value_changed);
|
||||
watch_value_notify!("max_value", max_value_changed);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Reloadable for AsusArmouryAttribute {
|
||||
async fn reload(&mut self) -> Result<(), RogError> {
|
||||
info!("Reloading {}", self.attr.name());
|
||||
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
|
||||
let power_plugged = self
|
||||
.power
|
||||
.get_online()
|
||||
.map_err(|e| {
|
||||
error!("Could not get power status: {e:?}");
|
||||
e
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let config = if power_plugged == 1 {
|
||||
&self.config.lock().await.ac_profile_tunings
|
||||
} else {
|
||||
&self.config.lock().await.dc_profile_tunings
|
||||
};
|
||||
if let Some(tuning) = config.get(&profile) {
|
||||
if tuning.enabled {
|
||||
if let Some(tune) = tuning.group.get(&self.name()) {
|
||||
self.attr
|
||||
.set_current_value(&AttrValue::Integer(*tune))
|
||||
.map_err(|e| {
|
||||
error!("Could not set {} value: {e:?}", self.attr.name());
|
||||
self.attr.base_path_exists();
|
||||
e
|
||||
})?;
|
||||
info!("Set {} to {:?}", self.attr.name(), tune);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// If return is `-1` on a property then there is avilable value for that
|
||||
/// property
|
||||
#[interface(name = "xyz.ljones.AsusArmoury")]
|
||||
impl AsusArmouryAttribute {
|
||||
#[zbus(property)]
|
||||
fn name(&self) -> FirmwareAttribute {
|
||||
self.attr.name().into()
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn available_attrs(&self) -> Vec<String> {
|
||||
let mut attrs = Vec::new();
|
||||
if !matches!(self.attr.default_value(), AttrValue::None) {
|
||||
attrs.push("default_value".to_string());
|
||||
}
|
||||
if !matches!(self.attr.min_value(), AttrValue::None) {
|
||||
attrs.push("min_value".to_string());
|
||||
}
|
||||
if !matches!(self.attr.max_value(), AttrValue::None) {
|
||||
attrs.push("max_value".to_string());
|
||||
}
|
||||
if !matches!(self.attr.scalar_increment(), AttrValue::None) {
|
||||
attrs.push("scalar_increment".to_string());
|
||||
}
|
||||
if !matches!(self.attr.possible_values(), AttrValue::None) {
|
||||
attrs.push("possible_values".to_string());
|
||||
}
|
||||
// TODO: Don't unwrap, use error
|
||||
if let Ok(value) = self.attr.current_value().map_err(|e| {
|
||||
error!("Failed to read: {e:?}");
|
||||
e
|
||||
}) {
|
||||
if !matches!(value, AttrValue::None) {
|
||||
attrs.push("current_value".to_string());
|
||||
}
|
||||
}
|
||||
attrs
|
||||
}
|
||||
|
||||
/// If return is `-1` then there is no default value
|
||||
#[zbus(property)]
|
||||
async fn default_value(&self) -> i32 {
|
||||
match self.attr.default_value() {
|
||||
AttrValue::Integer(i) => *i,
|
||||
_ => -1
|
||||
}
|
||||
}
|
||||
|
||||
async fn restore_default(&self) -> fdo::Result<()> {
|
||||
self.attr.restore_default()?;
|
||||
if self.name().is_ppt() {
|
||||
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
|
||||
let power_plugged = self
|
||||
.power
|
||||
.get_online()
|
||||
.map_err(|e| {
|
||||
error!("Could not get power status: {e:?}");
|
||||
e
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut config = self.config.lock().await;
|
||||
let tuning = config.select_tunings(power_plugged == 1, profile);
|
||||
if let Some(tune) = tuning.group.get_mut(&self.name()) {
|
||||
if let AttrValue::Integer(i) = self.attr.default_value() {
|
||||
*tune = *i;
|
||||
}
|
||||
}
|
||||
if tuning.enabled {
|
||||
self.attr
|
||||
.set_current_value(self.attr.default_value())
|
||||
.map_err(|e| {
|
||||
error!("Could not set value: {e:?}");
|
||||
e
|
||||
})?;
|
||||
}
|
||||
config.write();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn min_value(&self) -> i32 {
|
||||
match self.attr.min_value() {
|
||||
AttrValue::Integer(i) => *i,
|
||||
_ => -1
|
||||
}
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn max_value(&self) -> i32 {
|
||||
match self.attr.max_value() {
|
||||
AttrValue::Integer(i) => *i,
|
||||
_ => -1
|
||||
}
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn scalar_increment(&self) -> i32 {
|
||||
match self.attr.scalar_increment() {
|
||||
AttrValue::Integer(i) => *i,
|
||||
_ => -1
|
||||
}
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn possible_values(&self) -> Vec<i32> {
|
||||
match self.attr.possible_values() {
|
||||
AttrValue::EnumInt(i) => i.clone(),
|
||||
_ => Vec::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn current_value(&self) -> fdo::Result<i32> {
|
||||
if self.name().is_ppt() {
|
||||
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
|
||||
let power_plugged = self
|
||||
.power
|
||||
.get_online()
|
||||
.map_err(|e| {
|
||||
error!("Could not get power status: {e:?}");
|
||||
e
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let mut config = self.config.lock().await;
|
||||
let tuning = config.select_tunings(power_plugged == 1, profile);
|
||||
if let Some(tune) = tuning.group.get(&self.name()) {
|
||||
return Ok(*tune);
|
||||
} else if let AttrValue::Integer(i) = self.attr.default_value() {
|
||||
return Ok(*i);
|
||||
}
|
||||
return Err(fdo::Error::Failed(
|
||||
"Could not read current value".to_string()
|
||||
));
|
||||
}
|
||||
|
||||
if let Ok(AttrValue::Integer(i)) = self.attr.current_value() {
|
||||
return Ok(i);
|
||||
}
|
||||
Err(fdo::Error::Failed(
|
||||
"Could not read current value".to_string()
|
||||
))
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn set_current_value(&mut self, value: i32) -> fdo::Result<()> {
|
||||
if self.name().is_ppt() {
|
||||
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
|
||||
let power_plugged = self
|
||||
.power
|
||||
.get_online()
|
||||
.map_err(|e| {
|
||||
error!("Could not get power status: {e:?}");
|
||||
e
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut config = self.config.lock().await;
|
||||
let tuning = config.select_tunings(power_plugged == 1, profile);
|
||||
|
||||
if let Some(tune) = tuning.group.get_mut(&self.name()) {
|
||||
*tune = value;
|
||||
} else {
|
||||
tuning.group.insert(self.name(), value);
|
||||
debug!("Store tuning config for {} = {:?}", self.attr.name(), value);
|
||||
}
|
||||
if tuning.enabled {
|
||||
self.attr
|
||||
.set_current_value(&AttrValue::Integer(value))
|
||||
.map_err(|e| {
|
||||
error!("Could not set value: {e:?}");
|
||||
e
|
||||
})?;
|
||||
}
|
||||
} else {
|
||||
self.attr
|
||||
.set_current_value(&AttrValue::Integer(value))
|
||||
.map_err(|e| {
|
||||
error!("Could not set value: {e:?}");
|
||||
e
|
||||
})?;
|
||||
|
||||
let has_attr = self
|
||||
.config
|
||||
.lock()
|
||||
.await
|
||||
.armoury_settings
|
||||
.contains_key(&self.name());
|
||||
if has_attr {
|
||||
if let Some(setting) = self
|
||||
.config
|
||||
.lock()
|
||||
.await
|
||||
.armoury_settings
|
||||
.get_mut(&self.name())
|
||||
{
|
||||
*setting = value
|
||||
}
|
||||
} else {
|
||||
debug!("Adding config for {}", self.attr.name());
|
||||
self.config
|
||||
.lock()
|
||||
.await
|
||||
.armoury_settings
|
||||
.insert(self.name(), value);
|
||||
debug!("Set config for {} = {:?}", self.attr.name(), value);
|
||||
}
|
||||
}
|
||||
self.config.lock().await.write();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start_attributes_zbus(
|
||||
conn: &Connection,
|
||||
platform: RogPlatform,
|
||||
power: AsusPower,
|
||||
attributes: FirmwareAttributes,
|
||||
config: Arc<Mutex<Config>>
|
||||
) -> Result<(), RogError> {
|
||||
for attr in attributes.attributes() {
|
||||
let mut attr = AsusArmouryAttribute::new(
|
||||
attr.clone(),
|
||||
platform.clone(),
|
||||
power.clone(),
|
||||
config.clone()
|
||||
);
|
||||
attr.reload().await?;
|
||||
|
||||
let path = dbus_path_for_attr(attr.attr.name());
|
||||
let sig = zbus::object_server::SignalEmitter::new(conn, path)?;
|
||||
attr.watch_and_notify(sig).await?;
|
||||
|
||||
attr.move_to_zbus(conn).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn set_config_or_default(
|
||||
attrs: &FirmwareAttributes,
|
||||
config: &mut Config,
|
||||
power_plugged: bool,
|
||||
profile: PlatformProfile
|
||||
) {
|
||||
for attr in attrs.attributes().iter() {
|
||||
let name: FirmwareAttribute = attr.name().into();
|
||||
if name.is_ppt() {
|
||||
let tuning = config.select_tunings(power_plugged, profile);
|
||||
if !tuning.enabled {
|
||||
debug!("Tuning group is not enabled, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(tune) = tuning.group.get(&name) {
|
||||
attr.set_current_value(&AttrValue::Integer(*tune))
|
||||
.map_err(|e| {
|
||||
error!("Failed to set {}: {e}", <&str>::from(name));
|
||||
})
|
||||
.ok();
|
||||
} else {
|
||||
let default = attr.default_value();
|
||||
attr.set_current_value(default)
|
||||
.map_err(|e| {
|
||||
error!("Failed to set {}: {e}", <&str>::from(name));
|
||||
})
|
||||
.ok();
|
||||
if let AttrValue::Integer(i) = default {
|
||||
tuning.group.insert(name, *i);
|
||||
info!(
|
||||
"Set default tuning config for {} = {:?}",
|
||||
<&str>::from(name),
|
||||
i
|
||||
);
|
||||
// config.write();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
182
asusd/src/aura_anime/config.rs
Normal file
182
asusd/src/aura_anime/config.rs
Normal file
@@ -0,0 +1,182 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use config_traits::{StdConfig, StdConfigLoad};
|
||||
use rog_anime::error::AnimeError;
|
||||
use rog_anime::usb::Brightness;
|
||||
use rog_anime::{
|
||||
ActionData, ActionLoader, AnimTime, Animations, AnimeType, DeviceState, Fade, Vec2
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const CONFIG_FILE: &str = "anime.ron";
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
|
||||
pub struct AniMeConfigCached {
|
||||
pub system: Vec<ActionData>,
|
||||
pub boot: Vec<ActionData>,
|
||||
pub wake: Vec<ActionData>,
|
||||
pub shutdown: Vec<ActionData>
|
||||
}
|
||||
|
||||
impl AniMeConfigCached {
|
||||
pub fn init_from_config(
|
||||
&mut self,
|
||||
config: &AniMeConfig,
|
||||
anime_type: AnimeType
|
||||
) -> Result<(), AnimeError> {
|
||||
let mut sys = Vec::with_capacity(config.system.len());
|
||||
for ani in &config.system {
|
||||
sys.push(ActionData::from_anime_action(anime_type, ani)?);
|
||||
}
|
||||
self.system = sys;
|
||||
|
||||
let mut boot = Vec::with_capacity(config.boot.len());
|
||||
for ani in &config.boot {
|
||||
boot.push(ActionData::from_anime_action(anime_type, ani)?);
|
||||
}
|
||||
self.boot = boot;
|
||||
|
||||
let mut wake = Vec::with_capacity(config.wake.len());
|
||||
for ani in &config.wake {
|
||||
wake.push(ActionData::from_anime_action(anime_type, ani)?);
|
||||
}
|
||||
self.wake = wake;
|
||||
|
||||
let mut shutdown = Vec::with_capacity(config.shutdown.len());
|
||||
for ani in &config.shutdown {
|
||||
shutdown.push(ActionData::from_anime_action(anime_type, ani)?);
|
||||
}
|
||||
self.shutdown = shutdown;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Config for base system actions for the anime display
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct AniMeConfig {
|
||||
#[serde(skip)]
|
||||
pub anime_type: AnimeType,
|
||||
pub system: Vec<ActionLoader>,
|
||||
pub boot: Vec<ActionLoader>,
|
||||
pub wake: Vec<ActionLoader>,
|
||||
pub shutdown: Vec<ActionLoader>,
|
||||
// pub brightness: f32,
|
||||
pub display_enabled: bool,
|
||||
pub display_brightness: Brightness,
|
||||
pub builtin_anims_enabled: bool,
|
||||
pub off_when_unplugged: bool,
|
||||
pub off_when_suspended: bool,
|
||||
pub off_when_lid_closed: bool,
|
||||
pub brightness_on_battery: Brightness,
|
||||
pub builtin_anims: Animations
|
||||
}
|
||||
|
||||
impl Default for AniMeConfig {
|
||||
fn default() -> Self {
|
||||
AniMeConfig {
|
||||
anime_type: AnimeType::GA402,
|
||||
system: Vec::new(),
|
||||
boot: Vec::new(),
|
||||
wake: Vec::new(),
|
||||
shutdown: Vec::new(),
|
||||
// brightness: 1.0,
|
||||
display_enabled: true,
|
||||
display_brightness: Brightness::Med,
|
||||
builtin_anims_enabled: true,
|
||||
off_when_unplugged: true,
|
||||
off_when_suspended: true,
|
||||
off_when_lid_closed: true,
|
||||
brightness_on_battery: Brightness::Low,
|
||||
builtin_anims: Animations::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfig for AniMeConfig {
|
||||
fn new() -> Self {
|
||||
Self::create_default()
|
||||
}
|
||||
|
||||
fn file_name(&self) -> String {
|
||||
CONFIG_FILE.to_owned()
|
||||
}
|
||||
|
||||
fn config_dir() -> std::path::PathBuf {
|
||||
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfigLoad for AniMeConfig {}
|
||||
|
||||
impl From<&AniMeConfig> for DeviceState {
|
||||
fn from(config: &AniMeConfig) -> Self {
|
||||
DeviceState {
|
||||
display_enabled: config.display_enabled,
|
||||
display_brightness: config.display_brightness,
|
||||
builtin_anims_enabled: config.builtin_anims_enabled,
|
||||
builtin_anims: config.builtin_anims,
|
||||
off_when_unplugged: config.off_when_unplugged,
|
||||
off_when_suspended: config.off_when_suspended,
|
||||
off_when_lid_closed: config.off_when_lid_closed,
|
||||
brightness_on_battery: config.brightness_on_battery
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AniMeConfig {
|
||||
// fn clamp_config_brightness(mut config: &mut AnimeConfig) {
|
||||
// if config.brightness < 0.0 || config.brightness > 1.0 {
|
||||
// warn!(
|
||||
// "Clamped brightness to [0.0 ; 1.0], was {}",
|
||||
// config.brightness
|
||||
// );
|
||||
// config.brightness = f32::max(0.0, f32::min(1.0, config.brightness));
|
||||
// }
|
||||
// }
|
||||
|
||||
fn create_default() -> Self {
|
||||
// create a default config here
|
||||
AniMeConfig {
|
||||
system: vec![],
|
||||
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::Fade(Fade::new(
|
||||
Duration::from_secs(2),
|
||||
Some(Duration::from_secs(2)),
|
||||
Duration::from_secs(2)
|
||||
))
|
||||
},
|
||||
],
|
||||
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::Fade(Fade::new(
|
||||
Duration::from_secs(2),
|
||||
Some(Duration::from_secs(2)),
|
||||
Duration::from_secs(2)
|
||||
))
|
||||
},
|
||||
],
|
||||
shutdown: vec![
|
||||
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: 1.0,
|
||||
time: AnimTime::Infinite
|
||||
},
|
||||
],
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
248
asusd/src/aura_anime/mod.rs
Normal file
248
asusd/src/aura_anime/mod.rs
Normal file
@@ -0,0 +1,248 @@
|
||||
pub mod config;
|
||||
/// Implements `CtrlTask`, Reloadable, `ZbusRun`
|
||||
pub mod trait_impls;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::thread::sleep;
|
||||
|
||||
use config_traits::StdConfig;
|
||||
use futures_util::lock::Mutex;
|
||||
use log::{debug, error, info, warn};
|
||||
use rog_anime::usb::{
|
||||
pkt_flush, pkt_set_brightness, pkt_set_enable_display, pkt_set_enable_powersave_anim,
|
||||
pkts_for_init, Brightness
|
||||
};
|
||||
use rog_anime::{ActionData, AnimeDataBuffer, AnimePacketType};
|
||||
use rog_platform::hid_raw::HidRaw;
|
||||
use rog_platform::usb_raw::USBRaw;
|
||||
|
||||
use self::config::{AniMeConfig, AniMeConfigCached};
|
||||
use crate::error::RogError;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AniMe {
|
||||
hid: Option<Arc<Mutex<HidRaw>>>,
|
||||
usb: Option<Arc<Mutex<USBRaw>>>,
|
||||
config: Arc<Mutex<AniMeConfig>>,
|
||||
cache: AniMeConfigCached,
|
||||
// set to force thread to exit
|
||||
thread_exit: Arc<AtomicBool>,
|
||||
// Set to false when the thread exits
|
||||
thread_running: Arc<AtomicBool>
|
||||
}
|
||||
|
||||
impl AniMe {
|
||||
pub fn new(
|
||||
hid: Option<Arc<Mutex<HidRaw>>>,
|
||||
usb: Option<Arc<Mutex<USBRaw>>>,
|
||||
config: Arc<Mutex<AniMeConfig>>
|
||||
) -> Self {
|
||||
Self {
|
||||
hid,
|
||||
usb,
|
||||
config,
|
||||
cache: AniMeConfigCached::default(),
|
||||
thread_exit: Arc::new(AtomicBool::new(false)),
|
||||
thread_running: Arc::new(AtomicBool::new(false))
|
||||
}
|
||||
}
|
||||
|
||||
/// Will fail if something is already holding the config lock
|
||||
async fn do_init_cache(&mut self) {
|
||||
if let Some(mut config) = self.config.try_lock() {
|
||||
if let Err(e) = self.cache.init_from_config(&config, config.anime_type) {
|
||||
error!(
|
||||
"Trying to cache the Anime Config failed, will reset to default config: {e:?}"
|
||||
);
|
||||
config.rename_file_old();
|
||||
*config = AniMeConfig::new();
|
||||
config.write();
|
||||
} else {
|
||||
debug!("Initialised AniMe cache");
|
||||
}
|
||||
} else {
|
||||
error!("AniMe Matrix could not init cache")
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialise the device if required.
|
||||
pub async fn do_initialization(&mut self) -> Result<(), RogError> {
|
||||
self.do_init_cache().await;
|
||||
let pkts = pkts_for_init();
|
||||
self.write_bytes(&pkts[0]).await?;
|
||||
self.write_bytes(&pkts[1]).await?;
|
||||
debug!("Succesfully initialised AniMe matrix display");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
|
||||
if let Some(hid) = &self.hid {
|
||||
hid.lock().await.write_bytes(message)?;
|
||||
} else if let Some(usb) = &self.usb {
|
||||
usb.lock().await.write_bytes(message)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write only a data packet. This will modify the leds brightness using the
|
||||
/// global brightness set in config.
|
||||
async fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) -> Result<(), RogError> {
|
||||
for led in buffer.data_mut().iter_mut() {
|
||||
let mut bright = *led as f32;
|
||||
if bright > 254.0 {
|
||||
bright = 254.0;
|
||||
}
|
||||
*led = bright as u8;
|
||||
}
|
||||
let data = AnimePacketType::try_from(buffer)?;
|
||||
for row in &data {
|
||||
self.write_bytes(row).await?;
|
||||
}
|
||||
self.write_bytes(&pkt_flush()).await
|
||||
}
|
||||
|
||||
pub async fn set_builtins_enabled(
|
||||
&self,
|
||||
enabled: bool,
|
||||
bright: Brightness
|
||||
) -> Result<(), RogError> {
|
||||
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
|
||||
.await?;
|
||||
self.write_bytes(&pkt_set_enable_display(enabled)).await?;
|
||||
self.write_bytes(&pkt_set_brightness(bright)).await?;
|
||||
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Start an action thread. This is classed as a singleton and there should
|
||||
/// be only one running - so the thread uses atomics to signal run/exit.
|
||||
///
|
||||
/// Because this also writes to the usb device, other write tries (display
|
||||
/// only) *must* get the mutex lock and set the `thread_exit` atomic.
|
||||
async fn run_thread(&self, actions: Vec<ActionData>, mut once: bool) {
|
||||
if actions.is_empty() {
|
||||
warn!("AniMe system actions was empty");
|
||||
return;
|
||||
}
|
||||
|
||||
self.write_bytes(&pkt_set_enable_powersave_anim(false))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("rog_anime::run_animation:callback {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
let thread_exit = self.thread_exit.clone();
|
||||
let thread_running = self.thread_running.clone();
|
||||
let anime_type = self.config.lock().await.anime_type;
|
||||
let inner = self.clone();
|
||||
|
||||
// Loop rules:
|
||||
// - Lock the mutex **only when required**. That is, the lock must be held for
|
||||
// the shortest duration possible.
|
||||
// - An AtomicBool used for thread exit should be checked in every loop,
|
||||
// including nested
|
||||
|
||||
// The only reason for this outer thread is to prevent blocking while waiting
|
||||
// for the next spawned thread to exit
|
||||
// TODO: turn this in to async task (maybe? COuld still risk blocking main
|
||||
// thread)
|
||||
tokio::spawn(async move {
|
||||
info!("AniMe new system thread started");
|
||||
// First two loops are to ensure we *do* aquire a lock on the mutex
|
||||
// The reason the loop is required is because the USB writes can block
|
||||
// for up to 10ms. We can't fail to get the atomics.
|
||||
while thread_running.load(Ordering::SeqCst) {
|
||||
// Make any running loop exit first
|
||||
thread_exit.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
info!("AniMe no previous system thread running (now)");
|
||||
thread_exit.store(false, Ordering::SeqCst);
|
||||
thread_running.store(true, Ordering::SeqCst);
|
||||
'main: loop {
|
||||
for action in &actions {
|
||||
if thread_exit.load(Ordering::SeqCst) {
|
||||
break 'main;
|
||||
}
|
||||
match action {
|
||||
ActionData::Animation(frames) => {
|
||||
// TODO: sort all this out
|
||||
rog_anime::run_animation(frames, &|frame| {
|
||||
if thread_exit.load(Ordering::Acquire) {
|
||||
info!("rog-anime: animation sub-loop was asked to exit");
|
||||
return Ok(true); // Do safe exit
|
||||
}
|
||||
let inner = inner.clone();
|
||||
tokio::task::spawn(async move {
|
||||
inner
|
||||
.write_data_buffer(frame)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("rog_anime::run_animation:callback {}", err);
|
||||
})
|
||||
.ok();
|
||||
});
|
||||
Ok(false) // Don't exit yet
|
||||
});
|
||||
if thread_exit.load(Ordering::Acquire) {
|
||||
info!("rog-anime: sub-loop exited and main loop exiting now");
|
||||
break 'main;
|
||||
}
|
||||
}
|
||||
ActionData::Image(image) => {
|
||||
once = false;
|
||||
inner
|
||||
.write_data_buffer(image.as_ref().clone())
|
||||
.await
|
||||
.map_err(|e| error!("{}", e))
|
||||
.ok();
|
||||
}
|
||||
ActionData::Pause(duration) => sleep(*duration),
|
||||
ActionData::AudioEq
|
||||
| ActionData::SystemInfo
|
||||
| ActionData::TimeDate
|
||||
| ActionData::Matrix => {}
|
||||
}
|
||||
}
|
||||
if thread_exit.load(Ordering::SeqCst) {
|
||||
break 'main;
|
||||
}
|
||||
if once || actions.is_empty() {
|
||||
break 'main;
|
||||
}
|
||||
}
|
||||
// Clear the display on exit
|
||||
if let Ok(data) =
|
||||
AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()])
|
||||
.map_err(|e| error!("{}", e))
|
||||
{
|
||||
inner
|
||||
.write_data_buffer(data)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("rog_anime::run_animation:callback {}", err);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
// A write can block for many milliseconds so lets not hold the config lock for
|
||||
// the same period
|
||||
let enabled = inner.config.lock().await.builtin_anims_enabled;
|
||||
inner
|
||||
.write_bytes(&pkt_set_enable_powersave_anim(enabled))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("rog_anime::run_animation:callback {}", err);
|
||||
})
|
||||
.ok();
|
||||
// Loop ended, set the atmonics
|
||||
thread_running.store(false, Ordering::SeqCst);
|
||||
info!("AniMe system thread exited");
|
||||
})
|
||||
.await
|
||||
.map(|err| info!("AniMe system thread: {:?}", err))
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
503
asusd/src/aura_anime/trait_impls.rs
Normal file
503
asusd/src/aura_anime/trait_impls.rs
Normal file
@@ -0,0 +1,503 @@
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use config_traits::StdConfig;
|
||||
use log::{debug, error, warn};
|
||||
use logind_zbus::manager::ManagerProxy;
|
||||
use rog_anime::usb::{
|
||||
pkt_set_brightness, pkt_set_builtin_animations, pkt_set_enable_display,
|
||||
pkt_set_enable_powersave_anim, Brightness
|
||||
};
|
||||
use rog_anime::{Animations, AnimeDataBuffer, DeviceState};
|
||||
use zbus::object_server::SignalEmitter;
|
||||
use zbus::proxy::CacheProperties;
|
||||
use zbus::zvariant::OwnedObjectPath;
|
||||
use zbus::{interface, Connection};
|
||||
|
||||
use super::config::AniMeConfig;
|
||||
use super::AniMe;
|
||||
use crate::error::RogError;
|
||||
use crate::Reloadable;
|
||||
|
||||
async fn get_logind_manager<'a>() -> ManagerProxy<'a> {
|
||||
let connection = Connection::system()
|
||||
.await
|
||||
.expect("Controller could not create dbus connection");
|
||||
|
||||
ManagerProxy::builder(&connection)
|
||||
.cache_properties(CacheProperties::No)
|
||||
.build()
|
||||
.await
|
||||
.expect("Controller could not create ManagerProxy")
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AniMeZbus(AniMe);
|
||||
|
||||
impl AniMeZbus {
|
||||
pub fn new(anime: AniMe) -> Self {
|
||||
Self(anime)
|
||||
}
|
||||
|
||||
pub async fn start_tasks(
|
||||
mut self,
|
||||
connection: &Connection,
|
||||
path: OwnedObjectPath
|
||||
) -> Result<(), RogError> {
|
||||
// let task = zbus.clone();
|
||||
self.reload()
|
||||
.await
|
||||
.unwrap_or_else(|err| warn!("Controller error: {}", err));
|
||||
connection
|
||||
.object_server()
|
||||
.at(path.clone(), self)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Couldn't add server at path: {path}, {e:?}");
|
||||
e
|
||||
})?;
|
||||
debug!("start_tasks was successful");
|
||||
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.
|
||||
#[interface(name = "xyz.ljones.Anime")]
|
||||
impl AniMeZbus {
|
||||
/// Writes a data stream of length. Will force system thread to exit until
|
||||
/// it is restarted
|
||||
async fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> {
|
||||
let bright = self.0.config.lock().await.display_brightness;
|
||||
self.0.set_builtins_enabled(false, bright).await?;
|
||||
self.0.thread_exit.store(true, Ordering::SeqCst);
|
||||
self.0.write_data_buffer(input).await.map_err(|err| {
|
||||
warn!("ctrl_anime::run_animation:callback {}", err);
|
||||
err
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set base brightness level
|
||||
#[zbus(property)]
|
||||
async fn brightness(&self) -> Brightness {
|
||||
if let Some(config) = self.0.config.try_lock() {
|
||||
return config.display_brightness;
|
||||
}
|
||||
Brightness::Off
|
||||
}
|
||||
|
||||
/// Set base brightness level
|
||||
#[zbus(property)]
|
||||
async fn set_brightness(&self, brightness: Brightness) {
|
||||
self.0
|
||||
.write_bytes(&pkt_set_brightness(brightness))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("ctrl_anime::set_brightness {}", err);
|
||||
})
|
||||
.ok();
|
||||
self.0
|
||||
.write_bytes(&pkt_set_enable_display(brightness != Brightness::Off))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("ctrl_anime::set_brightness {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
let mut config = self.0.config.lock().await;
|
||||
config.display_enabled = brightness != Brightness::Off;
|
||||
config.display_brightness = brightness;
|
||||
config.write();
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn builtins_enabled(&self) -> bool {
|
||||
if let Some(config) = self.0.config.try_lock() {
|
||||
return config.builtin_anims_enabled;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Enable the builtin animations or not. This is quivalent to "Powersave
|
||||
/// animations" in Armory crate
|
||||
#[zbus(property)]
|
||||
async fn set_builtins_enabled(&self, enabled: bool) {
|
||||
let mut config = self.0.config.lock().await;
|
||||
let brightness = config.display_brightness;
|
||||
self.0
|
||||
.set_builtins_enabled(enabled, brightness)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("ctrl_anime::set_builtins_enabled {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
if !enabled {
|
||||
let anime_type = config.anime_type;
|
||||
let data = vec![255u8; anime_type.data_length()];
|
||||
if let Ok(tmp) = AnimeDataBuffer::from_vec(anime_type, data).map_err(|err| {
|
||||
warn!("ctrl_anime::set_builtins_enabled {}", err);
|
||||
}) {
|
||||
self.0
|
||||
.write_bytes(tmp.data())
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("ctrl_anime::set_builtins_enabled {}", err);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
config.builtin_anims_enabled = enabled;
|
||||
config.write();
|
||||
if enabled {
|
||||
self.0.thread_exit.store(true, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn builtin_animations(&self) -> Animations {
|
||||
if let Some(config) = self.0.config.try_lock() {
|
||||
return config.builtin_anims;
|
||||
}
|
||||
Animations::default()
|
||||
}
|
||||
|
||||
/// Set which builtin animation is used for each stage
|
||||
#[zbus(property)]
|
||||
async fn set_builtin_animations(&self, settings: Animations) {
|
||||
self.0
|
||||
.write_bytes(&pkt_set_builtin_animations(
|
||||
settings.boot, settings.awake, settings.sleep, settings.shutdown
|
||||
))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("ctrl_anime::run_animation:callback {}", err);
|
||||
})
|
||||
.ok();
|
||||
self.0
|
||||
.write_bytes(&pkt_set_enable_powersave_anim(true))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("ctrl_anime::run_animation:callback {}", err);
|
||||
})
|
||||
.ok();
|
||||
let mut config = self.0.config.lock().await;
|
||||
config.display_enabled = true;
|
||||
config.builtin_anims = settings;
|
||||
config.write();
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn enable_display(&self) -> bool {
|
||||
if let Some(config) = self.0.config.try_lock() {
|
||||
return config.display_enabled;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Set whether the AniMe is enabled at all
|
||||
#[zbus(property)]
|
||||
async fn set_enable_display(&self, enabled: bool) {
|
||||
self.0
|
||||
.write_bytes(&pkt_set_enable_display(enabled))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("ctrl_anime::run_animation:callback {}", err);
|
||||
})
|
||||
.ok();
|
||||
let mut config = self.0.config.lock().await;
|
||||
config.display_enabled = enabled;
|
||||
config.write();
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn off_when_unplugged(&self) -> bool {
|
||||
if let Some(config) = self.0.config.try_lock() {
|
||||
return config.off_when_unplugged;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Set if to turn the AniMe Matrix off when external power is unplugged
|
||||
#[zbus(property)]
|
||||
async fn set_off_when_unplugged(&self, enabled: bool) {
|
||||
let manager = get_logind_manager().await;
|
||||
let pow = manager.on_external_power().await.unwrap_or_default();
|
||||
|
||||
self.0
|
||||
.write_bytes(&pkt_set_enable_display(!pow && !enabled))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
let mut config = self.0.config.lock().await;
|
||||
config.off_when_unplugged = enabled;
|
||||
config.write();
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn off_when_suspended(&self) -> bool {
|
||||
if let Some(config) = self.0.config.try_lock() {
|
||||
return config.off_when_suspended;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Set if to turn the AniMe Matrix off when the laptop is suspended
|
||||
#[zbus(property)]
|
||||
async fn set_off_when_suspended(&self, enabled: bool) {
|
||||
let mut config = self.0.config.lock().await;
|
||||
config.off_when_suspended = enabled;
|
||||
config.write();
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn off_when_lid_closed(&self) -> bool {
|
||||
if let Some(config) = self.0.config.try_lock() {
|
||||
return config.off_when_lid_closed;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Set if to turn the AniMe Matrix off when the lid is closed
|
||||
#[zbus(property)]
|
||||
async fn set_off_when_lid_closed(&self, enabled: bool) {
|
||||
let manager = get_logind_manager().await;
|
||||
let lid = manager.lid_closed().await.unwrap_or_default();
|
||||
|
||||
self.0
|
||||
.write_bytes(&pkt_set_enable_display(lid && !enabled))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
let mut config = self.0.config.lock().await;
|
||||
config.off_when_lid_closed = enabled;
|
||||
config.write();
|
||||
}
|
||||
|
||||
/// The main loop is the base system set action if the user isn't running
|
||||
/// the user daemon
|
||||
async fn run_main_loop(&self, start: bool) {
|
||||
if start {
|
||||
self.0.thread_exit.store(true, Ordering::SeqCst);
|
||||
self.0.run_thread(self.0.cache.system.clone(), false).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the device state as stored by asusd
|
||||
// #[zbus(property)]
|
||||
async fn device_state(&self) -> DeviceState {
|
||||
DeviceState::from(&*self.0.config.lock().await)
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::CtrlTask for AniMeZbus {
|
||||
fn zbus_path() -> &'static str {
|
||||
"ANIME_ZBUS_PATH"
|
||||
}
|
||||
|
||||
async fn create_tasks(&self, _: SignalEmitter<'static>) -> Result<(), RogError> {
|
||||
let inner1 = self.0.clone();
|
||||
let inner2 = self.0.clone();
|
||||
let inner3 = self.0.clone();
|
||||
let inner4 = self.0.clone();
|
||||
self.create_sys_event_tasks(
|
||||
move |sleeping| {
|
||||
// on_sleep
|
||||
let inner = inner1.clone();
|
||||
async move {
|
||||
let config = inner.config.lock().await.clone();
|
||||
if config.display_enabled {
|
||||
inner.thread_exit.store(true, Ordering::Release); // ensure clean slate
|
||||
|
||||
inner
|
||||
.write_bytes(&pkt_set_enable_display(
|
||||
!(sleeping && config.off_when_suspended)
|
||||
))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::off_when_suspended {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
if config.builtin_anims_enabled {
|
||||
inner
|
||||
.write_bytes(&pkt_set_enable_powersave_anim(
|
||||
!(sleeping && config.off_when_suspended)
|
||||
))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::off_when_suspended {}", err);
|
||||
})
|
||||
.ok();
|
||||
} else if !sleeping && !config.builtin_anims_enabled {
|
||||
// Run custom wake animation
|
||||
inner
|
||||
.write_bytes(&pkt_set_enable_powersave_anim(false))
|
||||
.await
|
||||
.ok(); // ensure builtins are disabled
|
||||
|
||||
inner.run_thread(inner.cache.wake.clone(), true).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
move |shutting_down| {
|
||||
// on_shutdown
|
||||
let inner = inner2.clone();
|
||||
async move {
|
||||
let AniMeConfig {
|
||||
display_enabled,
|
||||
builtin_anims_enabled,
|
||||
..
|
||||
} = *inner.config.lock().await;
|
||||
if display_enabled && !builtin_anims_enabled {
|
||||
if shutting_down {
|
||||
inner.run_thread(inner.cache.shutdown.clone(), true).await;
|
||||
} else {
|
||||
inner.run_thread(inner.cache.boot.clone(), true).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
move |lid_closed| {
|
||||
let inner = inner3.clone();
|
||||
// on lid change
|
||||
async move {
|
||||
let AniMeConfig {
|
||||
off_when_lid_closed,
|
||||
builtin_anims_enabled,
|
||||
..
|
||||
} = *inner.config.lock().await;
|
||||
if off_when_lid_closed {
|
||||
if builtin_anims_enabled {
|
||||
inner
|
||||
.write_bytes(&pkt_set_enable_powersave_anim(!lid_closed))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::off_when_suspended {}", err);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
inner
|
||||
.write_bytes(&pkt_set_enable_display(!lid_closed))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
},
|
||||
move |power_plugged| {
|
||||
let inner = inner4.clone();
|
||||
// on power change
|
||||
async move {
|
||||
let AniMeConfig {
|
||||
off_when_unplugged,
|
||||
builtin_anims_enabled,
|
||||
brightness_on_battery,
|
||||
..
|
||||
} = *inner.config.lock().await;
|
||||
if off_when_unplugged {
|
||||
if builtin_anims_enabled {
|
||||
inner
|
||||
.write_bytes(&pkt_set_enable_powersave_anim(power_plugged))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::off_when_suspended {}", err);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
inner
|
||||
.write_bytes(&pkt_set_enable_display(power_plugged))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::off_when_unplugged {}", err);
|
||||
})
|
||||
.ok();
|
||||
} else {
|
||||
inner
|
||||
.write_bytes(&pkt_set_brightness(brightness_on_battery))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::off_when_unplugged {}", err);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Reloadable for AniMeZbus {
|
||||
async fn reload(&mut self) -> Result<(), RogError> {
|
||||
let AniMeConfig {
|
||||
builtin_anims_enabled,
|
||||
builtin_anims,
|
||||
display_enabled,
|
||||
display_brightness,
|
||||
off_when_lid_closed,
|
||||
off_when_unplugged,
|
||||
..
|
||||
} = *self.0.config.lock().await;
|
||||
|
||||
// Set builtins
|
||||
if builtin_anims_enabled {
|
||||
self.0
|
||||
.write_bytes(&pkt_set_builtin_animations(
|
||||
builtin_anims.boot,
|
||||
builtin_anims.awake,
|
||||
builtin_anims.sleep,
|
||||
builtin_anims.shutdown
|
||||
))
|
||||
.await?;
|
||||
}
|
||||
// Builtins enabled or na?
|
||||
self.0
|
||||
.set_builtins_enabled(builtin_anims_enabled, display_brightness)
|
||||
.await?;
|
||||
|
||||
let manager = get_logind_manager().await;
|
||||
let lid_closed = manager.lid_closed().await.unwrap_or_default();
|
||||
let power_plugged = manager.on_external_power().await.unwrap_or_default();
|
||||
|
||||
let turn_off =
|
||||
(lid_closed && off_when_lid_closed) || (!power_plugged && off_when_unplugged);
|
||||
self.0
|
||||
.write_bytes(&pkt_set_enable_display(!turn_off))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::reload {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
if turn_off || !display_enabled {
|
||||
self.0.write_bytes(&pkt_set_enable_display(false)).await?;
|
||||
// early return so we don't run animation thread
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !builtin_anims_enabled && !self.0.cache.boot.is_empty() {
|
||||
self.0
|
||||
.write_bytes(&pkt_set_enable_powersave_anim(false))
|
||||
.await
|
||||
.ok();
|
||||
|
||||
let action = self.0.cache.boot.clone();
|
||||
self.0.run_thread(action, true).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
416
asusd/src/aura_laptop/config.rs
Normal file
416
asusd/src/aura_laptop/config.rs
Normal file
@@ -0,0 +1,416 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use config_traits::{StdConfig, StdConfigLoad};
|
||||
use log::{debug, info, warn};
|
||||
use rog_aura::aura_detection::LedSupportData;
|
||||
use rog_aura::keyboard::LaptopAuraPower;
|
||||
use rog_aura::{
|
||||
AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, Direction, LedBrightness, Speed, GRADIENT
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::RogError;
|
||||
|
||||
#[derive(Deserialize, Serialize, Default, Debug, Clone)]
|
||||
// #[serde(default)]
|
||||
pub struct AuraConfig {
|
||||
#[serde(skip)]
|
||||
pub led_type: AuraDeviceType,
|
||||
#[serde(skip)]
|
||||
pub support_data: LedSupportData,
|
||||
pub config_name: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub ally_fix: Option<bool>,
|
||||
pub brightness: LedBrightness,
|
||||
pub current_mode: AuraModeNum,
|
||||
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub multizone: Option<BTreeMap<AuraModeNum, Vec<AuraEffect>>>,
|
||||
pub multizone_on: bool,
|
||||
pub enabled: LaptopAuraPower,
|
||||
#[serde(skip)]
|
||||
pub per_key_mode_active: bool
|
||||
}
|
||||
|
||||
impl StdConfig for AuraConfig {
|
||||
/// Detect the keyboard type and load from default DB if data available
|
||||
fn new() -> Self {
|
||||
panic!("This should not be used");
|
||||
}
|
||||
|
||||
fn file_name(&self) -> String {
|
||||
if self.config_name.is_empty() {
|
||||
panic!("Config file name should not be empty");
|
||||
}
|
||||
self.config_name.to_owned()
|
||||
}
|
||||
|
||||
fn config_dir() -> std::path::PathBuf {
|
||||
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfigLoad for AuraConfig {}
|
||||
|
||||
impl AuraConfig {
|
||||
/// Detect the keyboard type and load from default DB if data available
|
||||
pub fn new(prod_id: &str) -> Self {
|
||||
info!("Setting up AuraConfig for {prod_id:?}");
|
||||
// create a default config here
|
||||
let device_type = AuraDeviceType::from(prod_id);
|
||||
if device_type == AuraDeviceType::Unknown {
|
||||
warn!("idProduct:{prod_id:?} is unknown");
|
||||
}
|
||||
let support_data = LedSupportData::get_data(prod_id);
|
||||
let enabled = LaptopAuraPower::new(device_type, &support_data);
|
||||
let mut config = AuraConfig {
|
||||
led_type: device_type,
|
||||
support_data,
|
||||
config_name: format!("aura_{prod_id}.ron"),
|
||||
ally_fix: None,
|
||||
brightness: LedBrightness::Med,
|
||||
current_mode: AuraModeNum::Static,
|
||||
builtins: BTreeMap::new(),
|
||||
multizone: None,
|
||||
multizone_on: false,
|
||||
enabled,
|
||||
per_key_mode_active: false
|
||||
};
|
||||
|
||||
for n in &config.support_data.basic_modes {
|
||||
debug!("creating default for {n}");
|
||||
config
|
||||
.builtins
|
||||
.insert(*n, AuraEffect::default_with_mode(*n));
|
||||
|
||||
if !config.support_data.basic_zones.is_empty() {
|
||||
let mut default = vec![];
|
||||
for (i, tmp) in config.support_data.basic_zones.iter().enumerate() {
|
||||
default.push(AuraEffect {
|
||||
mode: *n,
|
||||
zone: *tmp,
|
||||
colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]),
|
||||
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
|
||||
speed: Speed::Med,
|
||||
direction: Direction::Left
|
||||
});
|
||||
}
|
||||
if let Some(m) = config.multizone.as_mut() {
|
||||
m.insert(*n, default);
|
||||
} else {
|
||||
let mut tmp = BTreeMap::new();
|
||||
tmp.insert(*n, default);
|
||||
config.multizone = Some(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
config
|
||||
}
|
||||
|
||||
/// Set the mode data, current mode, and if multizone enabled.
|
||||
///
|
||||
/// Multipurpose, will accept `AuraEffect` with zones and put in the correct
|
||||
/// store.
|
||||
pub fn set_builtin(&mut self, effect: AuraEffect) {
|
||||
self.current_mode = effect.mode;
|
||||
if effect.zone() == AuraZone::None {
|
||||
self.builtins.insert(*effect.mode(), effect);
|
||||
self.multizone_on = false;
|
||||
} else {
|
||||
if let Some(multi) = self.multizone.as_mut() {
|
||||
if let Some(fx) = multi.get_mut(effect.mode()) {
|
||||
for fx in fx.iter_mut() {
|
||||
if fx.zone == effect.zone {
|
||||
*fx = effect;
|
||||
return;
|
||||
}
|
||||
}
|
||||
fx.push(effect);
|
||||
} else {
|
||||
multi.insert(*effect.mode(), vec![effect]);
|
||||
}
|
||||
} else {
|
||||
let mut tmp = BTreeMap::new();
|
||||
tmp.insert(*effect.mode(), vec![effect]);
|
||||
self.multizone = Some(tmp);
|
||||
}
|
||||
self.multizone_on = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_multizone(&self, aura_type: AuraModeNum) -> Option<&[AuraEffect]> {
|
||||
if let Some(multi) = &self.multizone {
|
||||
return multi.get(&aura_type).map(|v| v.as_slice());
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Create a default for the `current_mode` if multizone and no config
|
||||
/// exists.
|
||||
pub fn create_multizone_default(&mut self) -> Result<(), RogError> {
|
||||
let mut default = vec![];
|
||||
for (i, tmp) in self.support_data.basic_zones.iter().enumerate() {
|
||||
default.push(AuraEffect {
|
||||
mode: self.current_mode,
|
||||
zone: *tmp,
|
||||
colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]),
|
||||
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
|
||||
speed: Speed::Med,
|
||||
direction: Direction::Left
|
||||
});
|
||||
}
|
||||
if default.is_empty() {
|
||||
return Err(RogError::AuraEffectNotSupported);
|
||||
}
|
||||
|
||||
if let Some(multizones) = self.multizone.as_mut() {
|
||||
multizones.insert(self.current_mode, default);
|
||||
} else {
|
||||
let mut tmp = BTreeMap::new();
|
||||
tmp.insert(self.current_mode, default);
|
||||
self.multizone = Some(tmp);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reload the config from disk then verify and update it if required.
|
||||
/// Always rewrites the file to disk.
|
||||
pub fn load_and_update_config(prod_id: &str) -> AuraConfig {
|
||||
// New loads data from the DB also
|
||||
let mut config_init = AuraConfig::new(prod_id);
|
||||
// config_init.set_filename(prod_id);
|
||||
let mut config_loaded = config_init.clone().load();
|
||||
// update the initialised data with what we loaded from disk
|
||||
for mode_init in &mut config_init.builtins {
|
||||
// update init values from loaded values if they exist
|
||||
if let Some(loaded) = config_loaded.builtins.get(mode_init.0) {
|
||||
*mode_init.1 = loaded.clone();
|
||||
}
|
||||
}
|
||||
// Then replace just incase the initialised data contains new modes added
|
||||
config_loaded.builtins = config_init.builtins;
|
||||
config_loaded.support_data = config_init.support_data;
|
||||
config_loaded.led_type = config_init.led_type;
|
||||
config_loaded.ally_fix = config_init.ally_fix;
|
||||
|
||||
for enabled_init in &mut config_init.enabled.states {
|
||||
for enabled in &mut config_loaded.enabled.states {
|
||||
if enabled.zone == enabled_init.zone {
|
||||
*enabled_init = *enabled;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
config_loaded.enabled = config_init.enabled;
|
||||
|
||||
if let (Some(mut multizone_init), Some(multizone_loaded)) =
|
||||
(config_init.multizone, config_loaded.multizone.as_mut())
|
||||
{
|
||||
for mode in multizone_init.iter_mut() {
|
||||
// update init values from loaded values if they exist
|
||||
if let Some(loaded) = multizone_loaded.get(mode.0) {
|
||||
let mut new_set = Vec::new();
|
||||
let data = LedSupportData::get_data(prod_id);
|
||||
// only reuse a zone mode if the mode is supported
|
||||
for mode in loaded {
|
||||
if data.basic_modes.contains(&mode.mode) {
|
||||
new_set.push(mode.clone());
|
||||
}
|
||||
}
|
||||
*mode.1 = new_set;
|
||||
}
|
||||
}
|
||||
*multizone_loaded = multizone_init;
|
||||
}
|
||||
|
||||
config_loaded.write();
|
||||
config_loaded
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rog_aura::keyboard::AuraPowerState;
|
||||
use rog_aura::{
|
||||
AuraEffect, AuraModeNum, AuraZone, Colour, Direction, LedBrightness, PowerZones, Speed
|
||||
};
|
||||
|
||||
use super::AuraConfig;
|
||||
|
||||
#[test]
|
||||
fn set_multizone_4key_config() {
|
||||
std::env::set_var("BOARD_NAME", "");
|
||||
let mut config = AuraConfig::new("19b6");
|
||||
|
||||
let effect = AuraEffect {
|
||||
colour1: Colour {
|
||||
r: 0xff,
|
||||
g: 0x00,
|
||||
b: 0xff
|
||||
},
|
||||
zone: AuraZone::Key1,
|
||||
..Default::default()
|
||||
};
|
||||
config.set_builtin(effect);
|
||||
|
||||
assert!(config.multizone.is_some());
|
||||
|
||||
let effect = AuraEffect {
|
||||
colour1: Colour {
|
||||
r: 0x00,
|
||||
g: 0xff,
|
||||
b: 0xff
|
||||
},
|
||||
zone: AuraZone::Key2,
|
||||
..Default::default()
|
||||
};
|
||||
config.set_builtin(effect);
|
||||
|
||||
let effect = AuraEffect {
|
||||
colour1: Colour {
|
||||
r: 0xff,
|
||||
g: 0xff,
|
||||
b: 0x00
|
||||
},
|
||||
zone: AuraZone::Key3,
|
||||
..Default::default()
|
||||
};
|
||||
config.set_builtin(effect);
|
||||
|
||||
let effect = AuraEffect {
|
||||
colour1: Colour {
|
||||
r: 0x00,
|
||||
g: 0xff,
|
||||
b: 0x00
|
||||
},
|
||||
zone: AuraZone::Key4,
|
||||
..Default::default()
|
||||
};
|
||||
let effect_clone = effect.clone();
|
||||
config.set_builtin(effect);
|
||||
// This should replace existing
|
||||
config.set_builtin(effect_clone);
|
||||
|
||||
let res = config.multizone.unwrap();
|
||||
let sta = res.get(&AuraModeNum::Static).unwrap();
|
||||
assert_eq!(sta.len(), 4);
|
||||
assert_eq!(sta[0].colour1, Colour {
|
||||
r: 0xff,
|
||||
g: 0x00,
|
||||
b: 0xff
|
||||
});
|
||||
assert_eq!(sta[1].colour1, Colour {
|
||||
r: 0x00,
|
||||
g: 0xff,
|
||||
b: 0xff
|
||||
});
|
||||
assert_eq!(sta[2].colour1, Colour {
|
||||
r: 0xff,
|
||||
g: 0xff,
|
||||
b: 0x00
|
||||
});
|
||||
assert_eq!(sta[3].colour1, Colour {
|
||||
r: 0x00,
|
||||
g: 0xff,
|
||||
b: 0x00
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_multizone_multimode_config() {
|
||||
std::env::set_var("BOARD_NAME", "");
|
||||
let mut config = AuraConfig::new("19b6");
|
||||
|
||||
let effect = AuraEffect {
|
||||
zone: AuraZone::Key1,
|
||||
..Default::default()
|
||||
};
|
||||
config.set_builtin(effect);
|
||||
|
||||
assert!(config.multizone.is_some());
|
||||
|
||||
let effect = AuraEffect {
|
||||
zone: AuraZone::Key2,
|
||||
mode: AuraModeNum::Breathe,
|
||||
..Default::default()
|
||||
};
|
||||
config.set_builtin(effect);
|
||||
|
||||
let effect = AuraEffect {
|
||||
zone: AuraZone::Key3,
|
||||
mode: AuraModeNum::Comet,
|
||||
..Default::default()
|
||||
};
|
||||
config.set_builtin(effect);
|
||||
|
||||
let effect = AuraEffect {
|
||||
zone: AuraZone::Key4,
|
||||
mode: AuraModeNum::Pulse,
|
||||
..Default::default()
|
||||
};
|
||||
config.set_builtin(effect);
|
||||
|
||||
let res = config.multizone.unwrap();
|
||||
let sta = res.get(&AuraModeNum::Static).unwrap();
|
||||
assert_eq!(sta.len(), 1);
|
||||
|
||||
let sta = res.get(&AuraModeNum::Breathe).unwrap();
|
||||
assert_eq!(sta.len(), 1);
|
||||
|
||||
let sta = res.get(&AuraModeNum::Comet).unwrap();
|
||||
assert_eq!(sta.len(), 1);
|
||||
|
||||
let sta = res.get(&AuraModeNum::Pulse).unwrap();
|
||||
assert_eq!(sta.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_0x1866_g531i() {
|
||||
std::env::set_var("BOARD_NAME", "G513I");
|
||||
let mut config = AuraConfig::new("1866");
|
||||
|
||||
assert_eq!(config.brightness, LedBrightness::Med);
|
||||
assert_eq!(config.builtins.len(), 5);
|
||||
assert_eq!(config.builtins.first_entry().unwrap().get(), &AuraEffect {
|
||||
mode: AuraModeNum::Static,
|
||||
zone: AuraZone::None,
|
||||
colour1: Colour { r: 166, g: 0, b: 0 },
|
||||
colour2: Colour { r: 0, g: 0, b: 0 },
|
||||
speed: Speed::Med,
|
||||
direction: Direction::Right
|
||||
});
|
||||
assert_eq!(config.enabled.states.len(), 1);
|
||||
assert_eq!(config.enabled.states[0], AuraPowerState {
|
||||
zone: PowerZones::KeyboardAndLightbar,
|
||||
boot: true,
|
||||
awake: true,
|
||||
sleep: true,
|
||||
shutdown: true
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_0x19b6_g634j() {
|
||||
std::env::set_var("BOARD_NAME", "G634J");
|
||||
let mut config = AuraConfig::new("19b6");
|
||||
|
||||
assert_eq!(config.brightness, LedBrightness::Med);
|
||||
assert_eq!(config.builtins.len(), 12);
|
||||
assert_eq!(config.builtins.first_entry().unwrap().get(), &AuraEffect {
|
||||
mode: AuraModeNum::Static,
|
||||
zone: AuraZone::None,
|
||||
colour1: Colour { r: 166, g: 0, b: 0 },
|
||||
colour2: Colour { r: 0, g: 0, b: 0 },
|
||||
speed: Speed::Med,
|
||||
direction: Direction::Right
|
||||
});
|
||||
assert_eq!(config.enabled.states.len(), 4);
|
||||
assert_eq!(config.enabled.states[0], AuraPowerState {
|
||||
zone: PowerZones::Keyboard,
|
||||
boot: true,
|
||||
awake: true,
|
||||
sleep: true,
|
||||
shutdown: true
|
||||
});
|
||||
}
|
||||
}
|
||||
229
asusd/src/aura_laptop/mod.rs
Normal file
229
asusd/src/aura_laptop/mod.rs
Normal file
@@ -0,0 +1,229 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use config::AuraConfig;
|
||||
use config_traits::StdConfig;
|
||||
use futures_util::lock::{Mutex, MutexGuard};
|
||||
use log::info;
|
||||
use rog_aura::keyboard::{AuraLaptopUsbPackets, LedUsbPackets};
|
||||
use rog_aura::usb::{AURA_LAPTOP_LED_APPLY, AURA_LAPTOP_LED_SET};
|
||||
use rog_aura::{AuraDeviceType, AuraEffect, LedBrightness, PowerZones, AURA_LAPTOP_LED_MSG_LEN};
|
||||
use rog_platform::hid_raw::HidRaw;
|
||||
use rog_platform::keyboard_led::KeyboardBacklight;
|
||||
|
||||
use crate::error::RogError;
|
||||
|
||||
pub mod config;
|
||||
pub mod trait_impls;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Aura {
|
||||
pub hid: Option<Arc<Mutex<HidRaw>>>,
|
||||
pub backlight: Option<Arc<Mutex<KeyboardBacklight>>>,
|
||||
pub config: Arc<Mutex<AuraConfig>>
|
||||
}
|
||||
|
||||
impl Aura {
|
||||
/// Initialise the device if required.
|
||||
pub async fn do_initialization(&self) -> Result<(), RogError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn lock_config(&self) -> MutexGuard<AuraConfig> {
|
||||
self.config.lock().await
|
||||
}
|
||||
|
||||
/// Will lock the internal config and update. If anything else has locked
|
||||
/// this in scope then a deadlock can occur.
|
||||
pub async fn update_config(&self) -> Result<(), RogError> {
|
||||
let mut config = self.config.lock().await;
|
||||
let bright = if let Some(bl) = self.backlight.as_ref() {
|
||||
bl.lock().await.get_brightness().unwrap_or_default()
|
||||
} else {
|
||||
config.brightness.into()
|
||||
};
|
||||
config.read();
|
||||
config.brightness = bright.into();
|
||||
config.write();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn write_current_config_mode(&self, config: &mut AuraConfig) -> Result<(), RogError> {
|
||||
if config.multizone_on {
|
||||
let mode = config.current_mode;
|
||||
let mut create = false;
|
||||
// There is no multizone config for this mode so create one here
|
||||
// using the colours of rainbow if it exists, or first available
|
||||
// mode, or random
|
||||
if config.multizone.is_none() {
|
||||
create = true;
|
||||
} else if let Some(multizones) = config.multizone.as_ref() {
|
||||
if !multizones.contains_key(&mode) {
|
||||
create = true;
|
||||
}
|
||||
}
|
||||
if create {
|
||||
info!("No user-set config for zone founding, attempting a default");
|
||||
config.create_multizone_default()?;
|
||||
}
|
||||
|
||||
if let Some(multizones) = config.multizone.as_mut() {
|
||||
if let Some(set) = multizones.get(&mode) {
|
||||
for mode in set.clone() {
|
||||
self.write_effect_and_apply(config.led_type, &mode).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let mode = config.current_mode;
|
||||
if let Some(effect) = config.builtins.get(&mode).cloned() {
|
||||
self.write_effect_and_apply(config.led_type, &effect)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write the AuraEffect to the device. Will lock `backlight` or `hid`.
|
||||
///
|
||||
/// If per-key or software-mode is active it must be marked as disabled in
|
||||
/// config.
|
||||
pub async fn write_effect_and_apply(
|
||||
&self,
|
||||
dev_type: AuraDeviceType,
|
||||
mode: &AuraEffect
|
||||
) -> Result<(), RogError> {
|
||||
if matches!(dev_type, AuraDeviceType::LaptopKeyboardTuf) {
|
||||
if let Some(platform) = &self.backlight {
|
||||
let buf = [
|
||||
1, mode.mode as u8, mode.colour1.r, mode.colour1.g, mode.colour1.b,
|
||||
mode.speed as u8
|
||||
];
|
||||
platform.lock().await.set_kbd_rgb_mode(&buf)?;
|
||||
}
|
||||
} else if let Some(hid_raw) = &self.hid {
|
||||
let bytes: [u8; AURA_LAPTOP_LED_MSG_LEN] = mode.into();
|
||||
let hid_raw = hid_raw.lock().await;
|
||||
hid_raw.write_bytes(&bytes)?;
|
||||
hid_raw.write_bytes(&AURA_LAPTOP_LED_SET)?;
|
||||
// Changes won't persist unless apply is set
|
||||
hid_raw.write_bytes(&AURA_LAPTOP_LED_APPLY)?;
|
||||
} else {
|
||||
return Err(RogError::NoAuraKeyboard);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn set_brightness(&self, value: u8) -> Result<(), RogError> {
|
||||
if let Some(backlight) = &self.backlight {
|
||||
backlight.lock().await.set_brightness(value)?;
|
||||
return Ok(());
|
||||
}
|
||||
Err(RogError::MissingFunction(
|
||||
"No LED backlight control available".to_string()
|
||||
))
|
||||
}
|
||||
|
||||
/// Set combination state for boot animation/sleep animation/all leds/keys
|
||||
/// leds/side leds LED active
|
||||
pub async fn set_power_states(&self, config: &AuraConfig) -> Result<(), RogError> {
|
||||
if matches!(config.led_type, rog_aura::AuraDeviceType::LaptopKeyboardTuf) {
|
||||
if let Some(backlight) = &self.backlight {
|
||||
// TODO: tuf bool array
|
||||
let buf = config.enabled.to_bytes(config.led_type);
|
||||
backlight.lock().await.set_kbd_rgb_state(&buf)?;
|
||||
}
|
||||
} else if let Some(hid_raw) = &self.hid {
|
||||
let hid_raw = hid_raw.lock().await;
|
||||
if let Some(p) = config.enabled.states.first() {
|
||||
if p.zone == PowerZones::Ally {
|
||||
let msg = [
|
||||
0x5d,
|
||||
0xd1,
|
||||
0x09,
|
||||
0x01,
|
||||
p.new_to_byte() as u8,
|
||||
0x0,
|
||||
0x0
|
||||
];
|
||||
hid_raw.write_bytes(&msg)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let bytes = config.enabled.to_bytes(config.led_type);
|
||||
let msg = [
|
||||
0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2], bytes[3]
|
||||
];
|
||||
hid_raw.write_bytes(&msg)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write an effect block. This is for per-key, but can be repurposed to
|
||||
/// write the raw factory mode packets - when doing this it is expected that
|
||||
/// only the first `Vec` (`effect[0]`) is valid.
|
||||
pub async fn write_effect_block(
|
||||
&self,
|
||||
config: &mut AuraConfig,
|
||||
effect: &AuraLaptopUsbPackets
|
||||
) -> Result<(), RogError> {
|
||||
if config.brightness == LedBrightness::Off {
|
||||
config.brightness = LedBrightness::Med;
|
||||
config.write();
|
||||
}
|
||||
|
||||
let pkt_type = effect[0][1];
|
||||
const PER_KEY_TYPE: u8 = 0xbc;
|
||||
|
||||
if let Some(hid_raw) = &self.hid {
|
||||
let hid_raw = hid_raw.lock().await;
|
||||
if pkt_type != PER_KEY_TYPE {
|
||||
config.per_key_mode_active = false;
|
||||
hid_raw.write_bytes(&effect[0])?;
|
||||
hid_raw.write_bytes(&AURA_LAPTOP_LED_SET)?;
|
||||
// hid_raw.write_bytes(&LED_APPLY)?;
|
||||
} else {
|
||||
if !config.per_key_mode_active {
|
||||
let init = LedUsbPackets::get_init_msg();
|
||||
hid_raw.write_bytes(&init)?;
|
||||
config.per_key_mode_active = true;
|
||||
}
|
||||
for row in effect.iter() {
|
||||
hid_raw.write_bytes(row)?;
|
||||
}
|
||||
}
|
||||
} else if matches!(config.led_type, rog_aura::AuraDeviceType::LaptopKeyboardTuf) {
|
||||
if let Some(tuf) = &self.backlight {
|
||||
for row in effect.iter() {
|
||||
let r = row[9];
|
||||
let g = row[10];
|
||||
let b = row[11];
|
||||
tuf.lock().await.set_kbd_rgb_mode(&[
|
||||
0, 0, r, g, b, 0
|
||||
])?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn fix_ally_power(&mut self) -> Result<(), RogError> {
|
||||
if self.config.lock().await.led_type == AuraDeviceType::Ally {
|
||||
if let Some(hid_raw) = &self.hid {
|
||||
let mut config = self.config.lock().await;
|
||||
if config.ally_fix.is_none() {
|
||||
let msg = [
|
||||
0x5d, 0xbd, 0x01, 0xff, 0xff, 0xff, 0xff
|
||||
];
|
||||
hid_raw.lock().await.write_bytes(&msg)?;
|
||||
info!("Reset Ally power settings to base");
|
||||
config.ally_fix = Some(true);
|
||||
}
|
||||
config.write();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
343
asusd/src/aura_laptop/trait_impls.rs
Normal file
343
asusd/src/aura_laptop/trait_impls.rs
Normal file
@@ -0,0 +1,343 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use config_traits::StdConfig;
|
||||
use log::{debug, error, info, warn};
|
||||
use rog_aura::keyboard::{AuraLaptopUsbPackets, LaptopAuraPower};
|
||||
use rog_aura::{AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, LedBrightness, PowerZones};
|
||||
use zbus::fdo::Error as ZbErr;
|
||||
use zbus::object_server::SignalEmitter;
|
||||
use zbus::zvariant::OwnedObjectPath;
|
||||
use zbus::{interface, Connection};
|
||||
|
||||
use super::Aura;
|
||||
use crate::error::RogError;
|
||||
use crate::{CtrlTask, Reloadable};
|
||||
|
||||
pub const AURA_ZBUS_NAME: &str = "Aura";
|
||||
pub const AURA_ZBUS_PATH: &str = "/xyz/ljones";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AuraZbus(Aura);
|
||||
|
||||
impl AuraZbus {
|
||||
pub fn new(aura: Aura) -> Self {
|
||||
Self(aura)
|
||||
}
|
||||
|
||||
pub async fn start_tasks(
|
||||
mut self,
|
||||
connection: &Connection,
|
||||
// _signal_ctx: SignalEmitter<'static>,
|
||||
path: OwnedObjectPath
|
||||
) -> Result<(), RogError> {
|
||||
// let task = zbus.clone();
|
||||
// let signal_ctx = signal_ctx.clone();
|
||||
self.reload()
|
||||
.await
|
||||
.unwrap_or_else(|err| warn!("Controller error: {}", err));
|
||||
connection
|
||||
.object_server()
|
||||
.at(path.clone(), self)
|
||||
.await
|
||||
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
|
||||
.ok();
|
||||
// TODO: skip this until we keep handles to tasks so they can be killed
|
||||
// task.create_tasks(signal_ctx).await
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// The main interface for changing, reading, or notfying
|
||||
///
|
||||
/// LED commands are split between Brightness, Modes, Per-Key
|
||||
#[interface(name = "xyz.ljones.Aura")]
|
||||
impl AuraZbus {
|
||||
/// Return the device type for this Aura keyboard
|
||||
#[zbus(property)]
|
||||
async fn device_type(&self) -> AuraDeviceType {
|
||||
self.0.config.lock().await.led_type
|
||||
}
|
||||
|
||||
/// Return the current LED brightness
|
||||
#[zbus(property)]
|
||||
async fn brightness(&self) -> Result<LedBrightness, ZbErr> {
|
||||
if let Some(bl) = self.0.backlight.as_ref() {
|
||||
return Ok(bl.lock().await.get_brightness().map(|n| n.into())?);
|
||||
}
|
||||
Err(ZbErr::Failed("No sysfs brightness control".to_string()))
|
||||
}
|
||||
|
||||
/// Set the keyboard brightness level (0-3)
|
||||
#[zbus(property)]
|
||||
async fn set_brightness(&mut self, brightness: LedBrightness) -> Result<(), ZbErr> {
|
||||
if let Some(bl) = self.0.backlight.as_ref() {
|
||||
return Ok(bl.lock().await.set_brightness(brightness.into())?);
|
||||
}
|
||||
Err(ZbErr::Failed("No sysfs brightness control".to_string()))
|
||||
}
|
||||
|
||||
/// Total levels of brightness available
|
||||
#[zbus(property)]
|
||||
async fn supported_brightness(&self) -> Vec<LedBrightness> {
|
||||
vec![
|
||||
LedBrightness::Off,
|
||||
LedBrightness::Low,
|
||||
LedBrightness::Med,
|
||||
LedBrightness::High,
|
||||
]
|
||||
}
|
||||
|
||||
/// The total available modes
|
||||
#[zbus(property)]
|
||||
async fn supported_basic_modes(&self) -> Result<Vec<AuraModeNum>, ZbErr> {
|
||||
let config = self.0.config.lock().await;
|
||||
Ok(config.builtins.keys().cloned().collect())
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn supported_basic_zones(&self) -> Result<Vec<AuraZone>, ZbErr> {
|
||||
let config = self.0.config.lock().await;
|
||||
Ok(config.support_data.basic_zones.clone())
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn supported_power_zones(&self) -> Result<Vec<PowerZones>, ZbErr> {
|
||||
let config = self.0.config.lock().await;
|
||||
Ok(config.support_data.power_zones.clone())
|
||||
}
|
||||
|
||||
/// The current mode data
|
||||
#[zbus(property)]
|
||||
async fn led_mode(&self) -> Result<AuraModeNum, ZbErr> {
|
||||
// entirely possible to deadlock here, so use try instead of lock()
|
||||
// let ctrl = self.0.lock().await;
|
||||
// Ok(config.current_mode)
|
||||
if let Some(config) = self.0.config.try_lock() {
|
||||
Ok(config.current_mode)
|
||||
} else {
|
||||
Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Set an Aura effect if the effect mode or zone is supported.
|
||||
///
|
||||
/// On success the aura config file is read to refresh cached values, then
|
||||
/// the effect is stored and config written to disk.
|
||||
#[zbus(property)]
|
||||
async fn set_led_mode(&mut self, num: AuraModeNum) -> Result<(), ZbErr> {
|
||||
let mut config = self.0.config.lock().await;
|
||||
config.current_mode = num;
|
||||
self.0.write_current_config_mode(&mut config).await?;
|
||||
if config.brightness == LedBrightness::Off {
|
||||
config.brightness = LedBrightness::Med;
|
||||
}
|
||||
self.0.set_brightness(config.brightness.into()).await?;
|
||||
config.write();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The current mode data
|
||||
#[zbus(property)]
|
||||
async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> {
|
||||
// entirely possible to deadlock here, so use try instead of lock()
|
||||
if let Some(config) = self.0.config.try_lock() {
|
||||
let mode = config.current_mode;
|
||||
match config.builtins.get(&mode) {
|
||||
Some(effect) => Ok(effect.clone()),
|
||||
None => Err(ZbErr::Failed("Could not get the current effect".into()))
|
||||
}
|
||||
} else {
|
||||
Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Set an Aura effect if the effect mode or zone is supported.
|
||||
///
|
||||
/// On success the aura config file is read to refresh cached values, then
|
||||
/// the effect is stored and config written to disk.
|
||||
#[zbus(property)]
|
||||
async fn set_led_mode_data(&mut self, effect: AuraEffect) -> Result<(), ZbErr> {
|
||||
let mut config = self.0.config.lock().await;
|
||||
if !config.support_data.basic_modes.contains(&effect.mode)
|
||||
|| effect.zone != AuraZone::None
|
||||
&& !config.support_data.basic_zones.contains(&effect.zone)
|
||||
{
|
||||
return Err(ZbErr::NotSupported(format!(
|
||||
"The Aura effect is not supported: {effect:?}"
|
||||
)));
|
||||
}
|
||||
|
||||
self.0
|
||||
.write_effect_and_apply(config.led_type, &effect)
|
||||
.await?;
|
||||
if config.brightness == LedBrightness::Off {
|
||||
config.brightness = LedBrightness::Med;
|
||||
}
|
||||
self.0.set_brightness(config.brightness.into()).await?;
|
||||
config.set_builtin(effect);
|
||||
config.write();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the data set for every mode available
|
||||
async fn all_mode_data(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
|
||||
let config = self.0.config.lock().await;
|
||||
config.builtins.clone()
|
||||
}
|
||||
|
||||
// As property doesn't work for AuraPowerDev (complexity of serialization?)
|
||||
#[zbus(property)]
|
||||
async fn led_power(&self) -> LaptopAuraPower {
|
||||
let config = self.0.config.lock().await;
|
||||
config.enabled.clone()
|
||||
}
|
||||
|
||||
/// Set a variety of states, input is array of enum.
|
||||
/// `enabled` sets if the sent array should be disabled or enabled
|
||||
///
|
||||
/// For Modern ROG devices the "enabled" flag is ignored.
|
||||
#[zbus(property)]
|
||||
async fn set_led_power(&mut self, options: LaptopAuraPower) -> Result<(), ZbErr> {
|
||||
let mut config = self.0.config.lock().await;
|
||||
for opt in options.states {
|
||||
let zone = opt.zone;
|
||||
for config in config.enabled.states.iter_mut() {
|
||||
if config.zone == zone {
|
||||
*config = opt;
|
||||
}
|
||||
}
|
||||
}
|
||||
config.write();
|
||||
Ok(self.0.set_power_states(&config).await.map_err(|e| {
|
||||
warn!("{}", e);
|
||||
e
|
||||
})?)
|
||||
}
|
||||
|
||||
/// On machine that have some form of either per-key keyboard or per-zone
|
||||
/// this can be used to write custom effects over dbus. The input is a
|
||||
/// nested `Vec<Vec<8>>` where `Vec<u8>` is a raw USB packet
|
||||
async fn direct_addressing_raw(&self, data: AuraLaptopUsbPackets) -> Result<(), ZbErr> {
|
||||
let mut config = self.0.config.lock().await;
|
||||
self.0.write_effect_block(&mut config, &data).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CtrlTask for AuraZbus {
|
||||
fn zbus_path() -> &'static str {
|
||||
"/xyz/ljones"
|
||||
}
|
||||
|
||||
async fn create_tasks(&self, _: SignalEmitter<'static>) -> Result<(), RogError> {
|
||||
let inner1 = self.0.clone();
|
||||
let inner3 = self.0.clone();
|
||||
self.create_sys_event_tasks(
|
||||
move |sleeping| {
|
||||
let inner1 = inner1.clone();
|
||||
// unwrap as we want to bomb out of the task
|
||||
async move {
|
||||
if !sleeping {
|
||||
info!("CtrlKbdLedTask reloading brightness and modes");
|
||||
if let Some(backlight) = &inner1.backlight {
|
||||
backlight
|
||||
.lock()
|
||||
.await
|
||||
.set_brightness(inner1.config.lock().await.brightness.into())
|
||||
.map_err(|e| {
|
||||
error!("CtrlKbdLedTask: {e}");
|
||||
e
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
let mut config = inner1.config.lock().await;
|
||||
inner1
|
||||
.write_current_config_mode(&mut config)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("CtrlKbdLedTask: {e}");
|
||||
e
|
||||
})
|
||||
.unwrap();
|
||||
} else if sleeping {
|
||||
inner1
|
||||
.update_config()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("CtrlKbdLedTask: {e}");
|
||||
e
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
},
|
||||
move |_shutting_down| {
|
||||
let inner3 = inner3.clone();
|
||||
async move {
|
||||
info!("CtrlKbdLedTask reloading brightness and modes");
|
||||
if let Some(backlight) = &inner3.backlight {
|
||||
// unwrap as we want to bomb out of the task
|
||||
backlight
|
||||
.lock()
|
||||
.await
|
||||
.set_brightness(inner3.config.lock().await.brightness.into())
|
||||
.map_err(|e| {
|
||||
error!("CtrlKbdLedTask: {e}");
|
||||
e
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
},
|
||||
move |_lid_closed| {
|
||||
// on lid change
|
||||
async move {}
|
||||
},
|
||||
move |_power_plugged| {
|
||||
// power change
|
||||
async move {}
|
||||
}
|
||||
)
|
||||
.await;
|
||||
|
||||
// let ctrl2 = self.0.clone();
|
||||
// let ctrl = self.0.lock().await;
|
||||
// if ctrl.led_node.has_brightness_control() {
|
||||
// let watch = ctrl.led_node.monitor_brightness()?;
|
||||
// tokio::spawn(async move {
|
||||
// let mut buffer = [0; 32];
|
||||
// watch
|
||||
// .into_event_stream(&mut buffer)
|
||||
// .unwrap()
|
||||
// .for_each(|_| async {
|
||||
// if let Some(lock) = ctrl2.try_lock() {
|
||||
// load_save(true, lock).unwrap(); // unwrap as we want
|
||||
// // to
|
||||
// // bomb out of the
|
||||
// // task
|
||||
// }
|
||||
// })
|
||||
// .await;
|
||||
// });
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Reloadable for AuraZbus {
|
||||
async fn reload(&mut self) -> Result<(), RogError> {
|
||||
self.0.fix_ally_power().await?;
|
||||
debug!("reloading keyboard mode");
|
||||
let mut config = self.0.lock_config().await;
|
||||
self.0.write_current_config_mode(&mut config).await?;
|
||||
debug!("reloading power states");
|
||||
self.0
|
||||
.set_power_states(&config)
|
||||
.await
|
||||
.map_err(|err| warn!("{err}"))
|
||||
.ok();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
541
asusd/src/aura_manager.rs
Normal file
541
asusd/src/aura_manager.rs
Normal file
@@ -0,0 +1,541 @@
|
||||
// Plan:
|
||||
// - Manager has udev monitor on USB looking for ROG devices
|
||||
// - If a device is found, add it to watch
|
||||
// - Add it to Zbus server
|
||||
// - If udev sees device removed then remove the zbus path
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use dmi_id::DMIID;
|
||||
use futures_lite::future::block_on;
|
||||
use futures_util::lock::Mutex;
|
||||
use log::{debug, error, info, warn};
|
||||
use mio::{Events, Interest, Poll, Token};
|
||||
use rog_platform::error::PlatformError;
|
||||
use rog_platform::hid_raw::HidRaw;
|
||||
use udev::{Device, MonitorBuilder};
|
||||
use zbus::zvariant::{ObjectPath, OwnedObjectPath};
|
||||
use zbus::Connection;
|
||||
|
||||
use crate::aura_anime::trait_impls::AniMeZbus;
|
||||
use crate::aura_laptop::trait_impls::AuraZbus;
|
||||
use crate::aura_scsi::trait_impls::ScsiZbus;
|
||||
use crate::aura_slash::trait_impls::SlashZbus;
|
||||
use crate::aura_types::DeviceHandle;
|
||||
use crate::error::RogError;
|
||||
use crate::ASUS_ZBUS_PATH;
|
||||
|
||||
const MOD_NAME: &str = "aura";
|
||||
|
||||
/// Returns only the Device details concatenated in a form usable for
|
||||
/// adding/appending to a filename
|
||||
pub fn filename_partial(parent: &Device) -> Option<OwnedObjectPath> {
|
||||
if let Some(id_product) = parent.attribute_value("idProduct") {
|
||||
let id_product = id_product.to_string_lossy();
|
||||
let mut path = if let Some(devnum) = parent.attribute_value("devnum") {
|
||||
let devnum = devnum.to_string_lossy();
|
||||
if let Some(devpath) = parent.attribute_value("devpath") {
|
||||
let devpath = devpath.to_string_lossy();
|
||||
format!("{id_product}_{devnum}_{devpath}")
|
||||
} else {
|
||||
format!("{id_product}_{devnum}")
|
||||
}
|
||||
} else {
|
||||
format!("{id_product}")
|
||||
};
|
||||
if path.contains('.') {
|
||||
warn!("dbus path for {id_product} contains `.`, removing");
|
||||
path.replace('.', "").clone_into(&mut path);
|
||||
}
|
||||
return Some(ObjectPath::from_str_unchecked(&path).into());
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn dbus_path_for_dev(parent: &Device) -> Option<OwnedObjectPath> {
|
||||
if let Some(filename) = filename_partial(parent) {
|
||||
return Some(
|
||||
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{filename}"))
|
||||
.into()
|
||||
);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn dbus_path_for_tuf() -> OwnedObjectPath {
|
||||
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/tuf")).into()
|
||||
}
|
||||
|
||||
fn dbus_path_for_slash() -> OwnedObjectPath {
|
||||
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/slash")).into()
|
||||
}
|
||||
|
||||
fn dbus_path_for_anime() -> OwnedObjectPath {
|
||||
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/anime")).into()
|
||||
}
|
||||
|
||||
fn dbus_path_for_scsi(prod_id: &str) -> OwnedObjectPath {
|
||||
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{prod_id}_scsi")).into()
|
||||
}
|
||||
|
||||
fn dev_prop_matches(dev: &Device, prop: &str, value: &str) -> bool {
|
||||
if let Some(p) = dev.property_value(prop) {
|
||||
return p == value;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// A device.
|
||||
///
|
||||
/// Each controller within should track its dbus path so it can be removed if
|
||||
/// required.
|
||||
pub struct AsusDevice {
|
||||
device: DeviceHandle,
|
||||
dbus_path: OwnedObjectPath
|
||||
}
|
||||
|
||||
pub struct DeviceManager {
|
||||
_dbus_connection: Connection
|
||||
}
|
||||
|
||||
impl DeviceManager {
|
||||
async fn init_hid_devices(
|
||||
connection: &Connection,
|
||||
device: Device
|
||||
) -> Result<Vec<AsusDevice>, RogError> {
|
||||
let mut devices = Vec::new();
|
||||
if let Some(usb_device) = device.parent_with_subsystem_devtype("usb", "usb_device")? {
|
||||
if let Some(usb_id) = usb_device.attribute_value("idProduct") {
|
||||
if let Some(vendor_id) = usb_device.attribute_value("idVendor") {
|
||||
if vendor_id != "0b05" {
|
||||
debug!("Not ASUS vendor ID");
|
||||
return Ok(devices);
|
||||
}
|
||||
// Almost all devices are identified by the productId.
|
||||
// So let's see what we have and:
|
||||
// 1. Generate an interface path
|
||||
// 2. Create the device
|
||||
// Use the top-level endpoint, not the parent
|
||||
if let Ok(hidraw) = HidRaw::from_device(device) {
|
||||
debug!("Testing device {usb_id:?}");
|
||||
let dev = Arc::new(Mutex::new(hidraw));
|
||||
// SLASH DEVICE
|
||||
if let Ok(dev_type) = DeviceHandle::new_slash_hid(
|
||||
dev.clone(),
|
||||
usb_id.to_str().unwrap_or_default()
|
||||
)
|
||||
.await
|
||||
{
|
||||
if let DeviceHandle::Slash(slash) = dev_type.clone() {
|
||||
let path =
|
||||
dbus_path_for_dev(&usb_device).unwrap_or(dbus_path_for_slash());
|
||||
let ctrl = SlashZbus::new(slash);
|
||||
ctrl.start_tasks(connection, path.clone()).await.unwrap();
|
||||
devices.push(AsusDevice {
|
||||
device: dev_type,
|
||||
dbus_path: path
|
||||
});
|
||||
}
|
||||
}
|
||||
// ANIME MATRIX DEVICE
|
||||
if let Ok(dev_type) = DeviceHandle::maybe_anime_hid(
|
||||
dev.clone(),
|
||||
usb_id.to_str().unwrap_or_default()
|
||||
)
|
||||
.await
|
||||
{
|
||||
if let DeviceHandle::AniMe(anime) = dev_type.clone() {
|
||||
let path =
|
||||
dbus_path_for_dev(&usb_device).unwrap_or(dbus_path_for_anime());
|
||||
let ctrl = AniMeZbus::new(anime);
|
||||
ctrl.start_tasks(connection, path.clone()).await.unwrap();
|
||||
devices.push(AsusDevice {
|
||||
device: dev_type,
|
||||
dbus_path: path
|
||||
});
|
||||
}
|
||||
}
|
||||
// AURA LAPTOP DEVICE
|
||||
if let Ok(dev_type) = DeviceHandle::maybe_laptop_aura(
|
||||
Some(dev),
|
||||
usb_id.to_str().unwrap_or_default()
|
||||
)
|
||||
.await
|
||||
{
|
||||
if let DeviceHandle::Aura(aura) = dev_type.clone() {
|
||||
let path =
|
||||
dbus_path_for_dev(&usb_device).unwrap_or(dbus_path_for_tuf());
|
||||
let ctrl = AuraZbus::new(aura);
|
||||
ctrl.start_tasks(connection, path.clone()).await.unwrap();
|
||||
devices.push(AsusDevice {
|
||||
device: dev_type,
|
||||
dbus_path: path
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(devices)
|
||||
}
|
||||
|
||||
/// To be called on daemon startup
|
||||
async fn init_all_hid(connection: &Connection) -> Result<Vec<AsusDevice>, RogError> {
|
||||
// track and ensure we use only one hidraw per prod_id
|
||||
// let mut interfaces = HashSet::new();
|
||||
let mut devices: Vec<AsusDevice> = Vec::new();
|
||||
|
||||
let mut enumerator = udev::Enumerator::new().map_err(|err| {
|
||||
warn!("{}", err);
|
||||
PlatformError::Udev("enumerator failed".into(), err)
|
||||
})?;
|
||||
|
||||
enumerator.match_subsystem("hidraw").map_err(|err| {
|
||||
warn!("{}", err);
|
||||
PlatformError::Udev("match_subsystem failed".into(), err)
|
||||
})?;
|
||||
|
||||
for device in enumerator
|
||||
.scan_devices()
|
||||
.map_err(|e| PlatformError::IoPath("enumerator".to_owned(), e))?
|
||||
{
|
||||
devices.append(&mut Self::init_hid_devices(connection, device).await?);
|
||||
}
|
||||
|
||||
Ok(devices)
|
||||
}
|
||||
|
||||
async fn init_scsi(
|
||||
connection: &Connection,
|
||||
device: &Device,
|
||||
path: OwnedObjectPath
|
||||
) -> Option<AsusDevice> {
|
||||
// "ID_MODEL_ID" "1932"
|
||||
// "ID_VENDOR_ID" "0b05"
|
||||
if dev_prop_matches(device, "ID_VENDOR_ID", "0b05") {
|
||||
if let Some(dev_node) = device.devnode() {
|
||||
let prod_id = device
|
||||
.property_value("ID_MODEL_ID")
|
||||
.unwrap_or_default()
|
||||
.to_string_lossy();
|
||||
if let Ok(dev_type) =
|
||||
DeviceHandle::maybe_scsi(dev_node.as_os_str().to_str().unwrap(), &prod_id).await
|
||||
{
|
||||
if let DeviceHandle::Scsi(scsi) = dev_type.clone() {
|
||||
let ctrl = ScsiZbus::new(scsi);
|
||||
ctrl.start_tasks(connection, path.clone()).await.unwrap();
|
||||
return Some(AsusDevice {
|
||||
device: dev_type,
|
||||
dbus_path: path
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
async fn init_all_scsi(connection: &Connection) -> Result<Vec<AsusDevice>, RogError> {
|
||||
// track and ensure we use only one hidraw per prod_id
|
||||
// let mut interfaces = HashSet::new();
|
||||
let mut devices: Vec<AsusDevice> = Vec::new();
|
||||
|
||||
let mut enumerator = udev::Enumerator::new().map_err(|err| {
|
||||
warn!("{}", err);
|
||||
PlatformError::Udev("enumerator failed".into(), err)
|
||||
})?;
|
||||
|
||||
enumerator.match_subsystem("block").map_err(|err| {
|
||||
warn!("{}", err);
|
||||
PlatformError::Udev("match_subsystem failed".into(), err)
|
||||
})?;
|
||||
|
||||
let mut found = Vec::new();
|
||||
for device in enumerator
|
||||
.scan_devices()
|
||||
.map_err(|e| PlatformError::IoPath("enumerator".to_owned(), e))?
|
||||
{
|
||||
if let Some(serial) = device.property_value("ID_SERIAL_SHORT") {
|
||||
let serial = serial.to_string_lossy().to_string();
|
||||
let path = dbus_path_for_scsi(&serial);
|
||||
if found.contains(&path) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(dev) = Self::init_scsi(connection, &device, path.clone()).await {
|
||||
devices.push(dev);
|
||||
found.push(path);
|
||||
}
|
||||
} else {
|
||||
debug!("No serial for SCSI device: {:?}", device.devpath());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(devices)
|
||||
}
|
||||
|
||||
pub async fn find_all_devices(connection: &Connection) -> Vec<AsusDevice> {
|
||||
let mut devices: Vec<AsusDevice> = Vec::new();
|
||||
// HID first, always
|
||||
if let Ok(devs) = &mut Self::init_all_hid(connection).await {
|
||||
devices.append(devs);
|
||||
}
|
||||
// USB after, need to check if HID picked something up and if so, skip it
|
||||
let mut do_anime = true;
|
||||
let mut do_slash = true;
|
||||
let mut do_kb_backlight = true;
|
||||
for dev in devices.iter() {
|
||||
if matches!(dev.device, DeviceHandle::Slash(_)) {
|
||||
do_slash = false;
|
||||
}
|
||||
if matches!(dev.device, DeviceHandle::AniMe(_)) {
|
||||
do_anime = false;
|
||||
}
|
||||
if matches!(dev.device, DeviceHandle::Aura(_) | DeviceHandle::OldAura(_)) {
|
||||
do_kb_backlight = false;
|
||||
}
|
||||
}
|
||||
|
||||
if do_slash {
|
||||
if let Ok(dev_type) = DeviceHandle::new_slash_usb().await {
|
||||
if let DeviceHandle::Slash(slash) = dev_type.clone() {
|
||||
let path = dbus_path_for_slash();
|
||||
let ctrl = SlashZbus::new(slash);
|
||||
ctrl.start_tasks(connection, path.clone()).await.unwrap();
|
||||
devices.push(AsusDevice {
|
||||
device: dev_type,
|
||||
dbus_path: path
|
||||
});
|
||||
}
|
||||
} else {
|
||||
info!("Tested device was not Slash");
|
||||
}
|
||||
}
|
||||
|
||||
if do_anime {
|
||||
if let Ok(dev_type) = DeviceHandle::maybe_anime_usb().await {
|
||||
// TODO: this is copy/pasted
|
||||
if let DeviceHandle::AniMe(anime) = dev_type.clone() {
|
||||
let path = dbus_path_for_anime();
|
||||
let ctrl = AniMeZbus::new(anime);
|
||||
if ctrl
|
||||
.start_tasks(connection, path.clone())
|
||||
.await
|
||||
.map_err(|e| error!("Failed to start tasks: {e:?}, not adding this device"))
|
||||
.is_ok()
|
||||
{
|
||||
devices.push(AsusDevice {
|
||||
device: dev_type,
|
||||
dbus_path: path
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info!("Tested device was not AniMe Matrix");
|
||||
}
|
||||
}
|
||||
|
||||
if do_kb_backlight {
|
||||
// TUF AURA LAPTOP DEVICE
|
||||
// product_name = ASUS TUF Gaming F15 FX507ZE_FX507ZE
|
||||
// product_family = ASUS TUF Gaming F15
|
||||
let product_name = DMIID::new().unwrap_or_default().product_name;
|
||||
let product_family = DMIID::new().unwrap_or_default().product_family;
|
||||
info!(
|
||||
"No USB keyboard aura, system is {product_name}, try using sysfs backlight control"
|
||||
);
|
||||
if product_name.contains("TUF") || product_family.contains("TUF") {
|
||||
info!("TUF laptop, try using sysfs backlight control");
|
||||
if let Ok(dev_type) = DeviceHandle::maybe_laptop_aura(None, "tuf").await {
|
||||
if let DeviceHandle::Aura(aura) = dev_type.clone() {
|
||||
let path = dbus_path_for_tuf();
|
||||
let ctrl = AuraZbus::new(aura);
|
||||
ctrl.start_tasks(connection, path.clone()).await.unwrap();
|
||||
devices.push(AsusDevice {
|
||||
device: dev_type,
|
||||
dbus_path: path
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(devs) = &mut Self::init_all_scsi(connection).await {
|
||||
devices.append(devs);
|
||||
}
|
||||
|
||||
devices
|
||||
}
|
||||
|
||||
pub async fn new(connection: Connection) -> Result<Self, RogError> {
|
||||
let conn_copy = connection.clone();
|
||||
let devices = Self::find_all_devices(&conn_copy).await;
|
||||
info!("Found {} valid devices on startup", devices.len());
|
||||
let devices = Arc::new(Mutex::new(devices));
|
||||
let manager = Self {
|
||||
_dbus_connection: connection
|
||||
};
|
||||
|
||||
// TODO: The /sysfs/ LEDs don't cause events, so they need to be manually
|
||||
// checked for and added
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let mut monitor = MonitorBuilder::new()?.listen()?;
|
||||
let mut poll = Poll::new()?;
|
||||
let mut events = Events::with_capacity(1024);
|
||||
poll.registry()
|
||||
.register(&mut monitor, Token(0), Interest::READABLE)?;
|
||||
|
||||
let rt = tokio::runtime::Runtime::new().expect("Unable to create Runtime");
|
||||
let _enter = rt.enter();
|
||||
loop {
|
||||
if poll.poll(&mut events, None).is_err() {
|
||||
continue;
|
||||
}
|
||||
for event in monitor.iter() {
|
||||
let action = event
|
||||
.action()
|
||||
.unwrap_or_default()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
let subsys = if let Some(subsys) = event.subsystem() {
|
||||
subsys.to_string_lossy().to_string()
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let devices = devices.clone();
|
||||
let conn_copy = conn_copy.clone();
|
||||
block_on(async move {
|
||||
// SCSCI devs
|
||||
if subsys == "block" {
|
||||
if action == "remove" {
|
||||
if let Some(serial) =
|
||||
event.device().property_value("ID_SERIAL_SHORT")
|
||||
{
|
||||
let serial = serial.to_string_lossy().to_string();
|
||||
let path = dbus_path_for_scsi(&serial);
|
||||
|
||||
let index = if let Some(index) = devices
|
||||
.lock()
|
||||
.await
|
||||
.iter()
|
||||
.position(|dev| dev.dbus_path == path)
|
||||
{
|
||||
index
|
||||
} else {
|
||||
if dev_prop_matches(&event.device(), "ID_VENDOR_ID", "0b05")
|
||||
{
|
||||
warn!("No device for dbus path: {path:?}");
|
||||
}
|
||||
return Ok(());
|
||||
};
|
||||
info!("removing: {path:?}");
|
||||
let dev = devices.lock().await.remove(index);
|
||||
let path = path.clone();
|
||||
if let DeviceHandle::Scsi(_) = dev.device {
|
||||
conn_copy
|
||||
.object_server()
|
||||
.remove::<ScsiZbus, _>(&path)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
} else if action == "add" {
|
||||
let evdev = event.device();
|
||||
if let Some(serial) = evdev.property_value("ID_SERIAL_SHORT") {
|
||||
let serial = serial.to_string_lossy().to_string();
|
||||
let path = dbus_path_for_scsi(&serial);
|
||||
if let Some(new_devs) =
|
||||
Self::init_scsi(&conn_copy, &evdev, path).await
|
||||
{
|
||||
devices.lock().await.append(&mut vec![new_devs]);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if subsys == "hidraw" {
|
||||
if let Some(parent) =
|
||||
event.parent_with_subsystem_devtype("usb", "usb_device")?
|
||||
{
|
||||
if action == "remove" {
|
||||
if let Some(path) = dbus_path_for_dev(&parent) {
|
||||
// Find the indexs of devices matching the path
|
||||
let removals: Vec<usize> = devices
|
||||
.lock()
|
||||
.await
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, dev)| {
|
||||
if dev.dbus_path == path {
|
||||
Some(i)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if removals.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
info!("removing: {path:?}");
|
||||
// Iter in reverse so as to not screw up indexing
|
||||
for index in removals.iter().rev() {
|
||||
let dev = devices.lock().await.remove(*index);
|
||||
let path = path.clone();
|
||||
let res = match dev.device {
|
||||
DeviceHandle::Aura(_) => {
|
||||
conn_copy
|
||||
.object_server()
|
||||
.remove::<AuraZbus, _>(&path)
|
||||
.await?
|
||||
}
|
||||
DeviceHandle::Slash(_) => {
|
||||
conn_copy
|
||||
.object_server()
|
||||
.remove::<SlashZbus, _>(&path)
|
||||
.await?
|
||||
}
|
||||
DeviceHandle::AniMe(_) => {
|
||||
conn_copy
|
||||
.object_server()
|
||||
.remove::<AniMeZbus, _>(&path)
|
||||
.await?
|
||||
}
|
||||
DeviceHandle::Scsi(_) => {
|
||||
conn_copy
|
||||
.object_server()
|
||||
.remove::<ScsiZbus, _>(&path)
|
||||
.await?
|
||||
}
|
||||
_ => todo!()
|
||||
};
|
||||
info!("AuraManager removed: {path:?}, {res}");
|
||||
}
|
||||
}
|
||||
} else if action == "add" {
|
||||
let evdev = event.device();
|
||||
if let Ok(mut new_devs) =
|
||||
Self::init_hid_devices(&conn_copy, evdev)
|
||||
.await
|
||||
.map_err(|e| error!("Couldn't add new device: {e:?}"))
|
||||
{
|
||||
devices.lock().await.append(&mut new_devs);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Ok::<(), RogError>(())
|
||||
})
|
||||
.map_err(|e| error!("{e:?}"))
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
// Required for return type on spawn
|
||||
#[allow(unreachable_code)]
|
||||
Ok::<(), RogError>(())
|
||||
});
|
||||
Ok(manager)
|
||||
}
|
||||
}
|
||||
114
asusd/src/aura_scsi/config.rs
Normal file
114
asusd/src/aura_scsi/config.rs
Normal file
@@ -0,0 +1,114 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use config_traits::{StdConfig, StdConfigLoad};
|
||||
use rog_aura::AuraDeviceType;
|
||||
use rog_scsi::{AuraEffect, AuraMode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const CONFIG_FILE: &str = "scsi.ron";
|
||||
|
||||
/// Config for base system actions for the anime display
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct ScsiConfig {
|
||||
#[serde(skip)]
|
||||
pub dev_type: AuraDeviceType,
|
||||
pub enabled: bool,
|
||||
pub current_mode: AuraMode,
|
||||
pub modes: BTreeMap<AuraMode, AuraEffect>
|
||||
}
|
||||
|
||||
impl ScsiConfig {
|
||||
pub fn get_effect(&mut self, mode: AuraMode) -> Option<&AuraEffect> {
|
||||
self.modes.get(&mode)
|
||||
}
|
||||
|
||||
pub fn save_effect(&mut self, effect: AuraEffect) {
|
||||
self.current_mode = effect.mode;
|
||||
self.modes.insert(*effect.mode(), effect);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ScsiConfig {
|
||||
fn default() -> Self {
|
||||
ScsiConfig {
|
||||
enabled: true,
|
||||
current_mode: AuraMode::Static,
|
||||
dev_type: AuraDeviceType::ScsiExtDisk,
|
||||
modes: BTreeMap::from([
|
||||
(AuraMode::Off, AuraEffect::default_with_mode(AuraMode::Off)),
|
||||
(
|
||||
AuraMode::Static,
|
||||
AuraEffect::default_with_mode(AuraMode::Static)
|
||||
),
|
||||
(
|
||||
AuraMode::Breathe,
|
||||
AuraEffect::default_with_mode(AuraMode::Breathe)
|
||||
),
|
||||
(
|
||||
AuraMode::Flashing,
|
||||
AuraEffect::default_with_mode(AuraMode::Flashing)
|
||||
),
|
||||
(
|
||||
AuraMode::RainbowCycle,
|
||||
AuraEffect::default_with_mode(AuraMode::RainbowCycle)
|
||||
),
|
||||
(
|
||||
AuraMode::RainbowWave,
|
||||
AuraEffect::default_with_mode(AuraMode::RainbowWave)
|
||||
),
|
||||
(
|
||||
AuraMode::RainbowCycleBreathe,
|
||||
AuraEffect::default_with_mode(AuraMode::RainbowCycleBreathe)
|
||||
),
|
||||
(
|
||||
AuraMode::ChaseFade,
|
||||
AuraEffect::default_with_mode(AuraMode::ChaseFade)
|
||||
),
|
||||
(
|
||||
AuraMode::RainbowCycleChaseFade,
|
||||
AuraEffect::default_with_mode(AuraMode::RainbowCycleChaseFade)
|
||||
),
|
||||
(
|
||||
AuraMode::Chase,
|
||||
AuraEffect::default_with_mode(AuraMode::Chase)
|
||||
),
|
||||
(
|
||||
AuraMode::RainbowCycleChase,
|
||||
AuraEffect::default_with_mode(AuraMode::RainbowCycleChase)
|
||||
),
|
||||
(
|
||||
AuraMode::RainbowCycleWave,
|
||||
AuraEffect::default_with_mode(AuraMode::RainbowCycleWave)
|
||||
),
|
||||
(
|
||||
AuraMode::RainbowPulseChase,
|
||||
AuraEffect::default_with_mode(AuraMode::RainbowPulseChase)
|
||||
),
|
||||
(
|
||||
AuraMode::RandomFlicker,
|
||||
AuraEffect::default_with_mode(AuraMode::RandomFlicker)
|
||||
),
|
||||
(
|
||||
AuraMode::DoubleFade,
|
||||
AuraEffect::default_with_mode(AuraMode::DoubleFade)
|
||||
)
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfig for ScsiConfig {
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn file_name(&self) -> String {
|
||||
CONFIG_FILE.to_owned()
|
||||
}
|
||||
|
||||
fn config_dir() -> std::path::PathBuf {
|
||||
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfigLoad for ScsiConfig {}
|
||||
45
asusd/src/aura_scsi/mod.rs
Normal file
45
asusd/src/aura_scsi/mod.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use config::ScsiConfig;
|
||||
use futures_util::lock::{Mutex, MutexGuard};
|
||||
use rog_scsi::{AuraEffect, Device, Task};
|
||||
|
||||
use crate::error::RogError;
|
||||
|
||||
pub mod config;
|
||||
pub mod trait_impls;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ScsiAura {
|
||||
device: Arc<Mutex<Device>>,
|
||||
config: Arc<Mutex<ScsiConfig>>
|
||||
}
|
||||
|
||||
impl ScsiAura {
|
||||
pub fn new(device: Arc<Mutex<Device>>, config: Arc<Mutex<ScsiConfig>>) -> Self {
|
||||
Self { device, config }
|
||||
}
|
||||
|
||||
pub async fn lock_config(&self) -> MutexGuard<ScsiConfig> {
|
||||
self.config.lock().await
|
||||
}
|
||||
|
||||
pub async fn write_effect(&self, effect: &AuraEffect) -> Result<(), RogError> {
|
||||
let tasks: Vec<Task> = effect.into();
|
||||
for task in &tasks {
|
||||
self.device.lock().await.perform(task).ok();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initialise the device if required. Locks the internal config so be wary
|
||||
/// of deadlocks.
|
||||
pub async fn do_initialization(&self) -> Result<(), RogError> {
|
||||
let config = self.config.lock().await;
|
||||
let mode = config.current_mode;
|
||||
if let Some(effect) = config.modes.get(&mode) {
|
||||
self.write_effect(effect).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
116
asusd/src/aura_scsi/trait_impls.rs
Normal file
116
asusd/src/aura_scsi/trait_impls.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use config_traits::StdConfig;
|
||||
use log::error;
|
||||
use rog_aura::AuraDeviceType;
|
||||
use rog_scsi::{AuraEffect, AuraMode};
|
||||
use zbus::fdo::Error as ZbErr;
|
||||
use zbus::zvariant::OwnedObjectPath;
|
||||
use zbus::{interface, Connection};
|
||||
|
||||
use super::ScsiAura;
|
||||
use crate::error::RogError;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ScsiZbus(ScsiAura);
|
||||
|
||||
impl ScsiZbus {
|
||||
pub fn new(scsi: ScsiAura) -> Self {
|
||||
Self(scsi)
|
||||
}
|
||||
|
||||
pub async fn start_tasks(
|
||||
self,
|
||||
connection: &Connection,
|
||||
path: OwnedObjectPath
|
||||
) -> Result<(), RogError> {
|
||||
connection
|
||||
.object_server()
|
||||
.at(path.clone(), self)
|
||||
.await
|
||||
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
|
||||
.ok();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[interface(name = "xyz.ljones.ScsiAura")]
|
||||
impl ScsiZbus {
|
||||
/// Return the device type for this Aura keyboard
|
||||
#[zbus(property)]
|
||||
async fn device_type(&self) -> AuraDeviceType {
|
||||
self.0.config.lock().await.dev_type
|
||||
}
|
||||
|
||||
/// Get enabled or not
|
||||
#[zbus(property)]
|
||||
async fn enabled(&self) -> bool {
|
||||
let lock = self.0.lock_config().await;
|
||||
lock.enabled
|
||||
}
|
||||
|
||||
/// Set enabled true or false
|
||||
#[zbus(property)]
|
||||
async fn set_enabled(&self, enabled: bool) {
|
||||
let mut config = self.0.lock_config().await;
|
||||
config.enabled = enabled;
|
||||
config.write();
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn led_mode(&self) -> u8 {
|
||||
let config = self.0.lock_config().await;
|
||||
config.current_mode as u8
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn set_led_mode(&self, mode: AuraMode) -> Result<(), zbus::Error> {
|
||||
let mut config = self.0.lock_config().await;
|
||||
if let Some(effect) = config.get_effect(mode) {
|
||||
self.0
|
||||
.write_effect(effect)
|
||||
.await
|
||||
.map_err(|e| zbus::Error::Failure(format!("{e:?}")))?;
|
||||
} else {
|
||||
return Err(zbus::Error::Failure("Mode data does not exist".to_string()));
|
||||
}
|
||||
config.current_mode = mode;
|
||||
config.write();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The current mode data
|
||||
#[zbus(property)]
|
||||
async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> {
|
||||
// entirely possible to deadlock here, so use try instead of lock()
|
||||
if let Some(config) = self.0.config.try_lock() {
|
||||
let mode = config.current_mode;
|
||||
match config.modes.get(&mode) {
|
||||
Some(effect) => Ok(effect.clone()),
|
||||
None => Err(ZbErr::Failed("Could not get the current effect".into()))
|
||||
}
|
||||
} else {
|
||||
Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Set an Aura effect if the effect mode or zone is supported.
|
||||
///
|
||||
/// On success the aura config file is read to refresh cached values, then
|
||||
/// the effect is stored and config written to disk.
|
||||
#[zbus(property)]
|
||||
async fn set_led_mode_data(&mut self, effect: AuraEffect) -> Result<(), ZbErr> {
|
||||
self.0.write_effect(&effect).await?;
|
||||
|
||||
let mut config = self.0.config.lock().await;
|
||||
config.save_effect(effect);
|
||||
config.write();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the data set for every mode available
|
||||
async fn all_mode_data(&self) -> BTreeMap<AuraMode, AuraEffect> {
|
||||
let config = self.0.config.lock().await;
|
||||
config.modes.clone()
|
||||
}
|
||||
}
|
||||
64
asusd/src/aura_slash/config.rs
Normal file
64
asusd/src/aura_slash/config.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
use config_traits::{StdConfig, StdConfigLoad};
|
||||
use rog_slash::{DeviceState, SlashMode, SlashType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const CONFIG_FILE: &str = "slash.ron";
|
||||
|
||||
/// Config for base system actions for the anime display
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct SlashConfig {
|
||||
#[serde(skip)]
|
||||
pub slash_type: SlashType,
|
||||
pub enabled: bool,
|
||||
pub brightness: u8,
|
||||
pub display_interval: u8,
|
||||
pub display_mode: SlashMode,
|
||||
pub show_on_boot: bool,
|
||||
pub show_on_shutdown: bool,
|
||||
pub show_on_sleep: bool,
|
||||
pub show_on_battery: bool,
|
||||
pub show_battery_warning: bool
|
||||
}
|
||||
|
||||
impl Default for SlashConfig {
|
||||
fn default() -> Self {
|
||||
SlashConfig {
|
||||
enabled: true,
|
||||
brightness: 255,
|
||||
display_interval: 0,
|
||||
display_mode: SlashMode::Bounce,
|
||||
slash_type: SlashType::Unsupported,
|
||||
show_on_boot: true,
|
||||
show_on_shutdown: true,
|
||||
show_on_sleep: true,
|
||||
show_on_battery: true,
|
||||
show_battery_warning: true
|
||||
}
|
||||
}
|
||||
}
|
||||
impl StdConfig for SlashConfig {
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn file_name(&self) -> String {
|
||||
CONFIG_FILE.to_owned()
|
||||
}
|
||||
|
||||
fn config_dir() -> std::path::PathBuf {
|
||||
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfigLoad for SlashConfig {}
|
||||
|
||||
impl From<&SlashConfig> for DeviceState {
|
||||
fn from(config: &SlashConfig) -> Self {
|
||||
DeviceState {
|
||||
slash_enabled: config.enabled,
|
||||
slash_brightness: config.brightness,
|
||||
slash_interval: config.display_interval,
|
||||
slash_mode: config.display_mode
|
||||
}
|
||||
}
|
||||
}
|
||||
70
asusd/src/aura_slash/mod.rs
Normal file
70
asusd/src/aura_slash/mod.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use config::SlashConfig;
|
||||
use futures_util::lock::{Mutex, MutexGuard};
|
||||
use rog_platform::hid_raw::HidRaw;
|
||||
use rog_platform::usb_raw::USBRaw;
|
||||
use rog_slash::usb::{get_options_packet, pkt_set_mode, pkts_for_init};
|
||||
use rog_slash::SlashType;
|
||||
|
||||
use crate::error::RogError;
|
||||
|
||||
pub mod config;
|
||||
pub mod trait_impls;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Slash {
|
||||
hid: Option<Arc<Mutex<HidRaw>>>,
|
||||
usb: Option<Arc<Mutex<USBRaw>>>,
|
||||
config: Arc<Mutex<SlashConfig>>
|
||||
}
|
||||
|
||||
impl Slash {
|
||||
pub fn new(
|
||||
hid: Option<Arc<Mutex<HidRaw>>>,
|
||||
usb: Option<Arc<Mutex<USBRaw>>>,
|
||||
config: Arc<Mutex<SlashConfig>>
|
||||
) -> Self {
|
||||
Self { hid, usb, config }
|
||||
}
|
||||
|
||||
pub async fn lock_config(&self) -> MutexGuard<SlashConfig> {
|
||||
self.config.lock().await
|
||||
}
|
||||
|
||||
pub async fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
|
||||
if let Some(hid) = &self.hid {
|
||||
hid.lock().await.write_bytes(message)?;
|
||||
} else if let Some(usb) = &self.usb {
|
||||
usb.lock().await.write_bytes(message)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initialise the device if required. Locks the internal config so be wary
|
||||
/// of deadlocks.
|
||||
pub async fn do_initialization(&self) -> Result<(), RogError> {
|
||||
// Don't try to initialise these models as the asus drivers already did
|
||||
let config = self.config.lock().await;
|
||||
if !matches!(config.slash_type, SlashType::GA605 | SlashType::GU605) {
|
||||
for pkt in &pkts_for_init(config.slash_type) {
|
||||
self.write_bytes(pkt).await?;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply config upon initialization
|
||||
let option_packets = get_options_packet(
|
||||
config.slash_type,
|
||||
config.enabled,
|
||||
config.brightness,
|
||||
config.display_interval
|
||||
);
|
||||
self.write_bytes(&option_packets).await?;
|
||||
|
||||
let mode_packets = pkt_set_mode(config.slash_type, config.display_mode);
|
||||
// self.node.write_bytes(&mode_packets[0])?;
|
||||
self.write_bytes(&mode_packets[1]).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
287
asusd/src/aura_slash/trait_impls.rs
Normal file
287
asusd/src/aura_slash/trait_impls.rs
Normal file
@@ -0,0 +1,287 @@
|
||||
use config_traits::StdConfig;
|
||||
use log::{debug, error, warn};
|
||||
use rog_slash::usb::{
|
||||
get_battery_saver_packet, get_boot_packet, get_low_battery_packet, get_options_packet,
|
||||
get_shutdown_packet, get_sleep_packet, pkt_save, pkt_set_mode
|
||||
};
|
||||
use rog_slash::{DeviceState, SlashMode};
|
||||
use zbus::zvariant::OwnedObjectPath;
|
||||
use zbus::{interface, Connection};
|
||||
|
||||
use super::Slash;
|
||||
use crate::error::RogError;
|
||||
use crate::Reloadable;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SlashZbus(Slash);
|
||||
|
||||
impl SlashZbus {
|
||||
pub fn new(slash: Slash) -> Self {
|
||||
Self(slash)
|
||||
}
|
||||
|
||||
pub async fn start_tasks(
|
||||
mut self,
|
||||
connection: &Connection,
|
||||
path: OwnedObjectPath
|
||||
) -> Result<(), RogError> {
|
||||
// let task = zbus.clone();
|
||||
self.reload()
|
||||
.await
|
||||
.unwrap_or_else(|err| warn!("Controller error: {}", err));
|
||||
connection
|
||||
.object_server()
|
||||
.at(path.clone(), self)
|
||||
.await
|
||||
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
|
||||
.ok();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[interface(name = "xyz.ljones.Slash")]
|
||||
impl SlashZbus {
|
||||
/// Get enabled or not
|
||||
#[zbus(property)]
|
||||
async fn enabled(&self) -> bool {
|
||||
let lock = self.0.lock_config().await;
|
||||
lock.enabled
|
||||
}
|
||||
|
||||
/// Set enabled true or false
|
||||
#[zbus(property)]
|
||||
async fn set_enabled(&self, enabled: bool) {
|
||||
let mut config = self.0.lock_config().await;
|
||||
let brightness = if enabled && config.brightness == 0 {
|
||||
0x88
|
||||
} else {
|
||||
config.brightness
|
||||
};
|
||||
self.0
|
||||
.write_bytes(&get_options_packet(
|
||||
config.slash_type,
|
||||
enabled,
|
||||
brightness,
|
||||
config.display_interval
|
||||
))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("ctrl_slash::set_options {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
config.enabled = enabled;
|
||||
config.brightness = brightness;
|
||||
config.write();
|
||||
}
|
||||
|
||||
/// Get brightness level
|
||||
#[zbus(property)]
|
||||
async fn brightness(&self) -> u8 {
|
||||
let config = self.0.lock_config().await;
|
||||
config.brightness
|
||||
}
|
||||
|
||||
/// Set brightness level
|
||||
#[zbus(property)]
|
||||
async fn set_brightness(&self, brightness: u8) {
|
||||
let mut config = self.0.lock_config().await;
|
||||
let enabled = brightness > 0;
|
||||
self.0
|
||||
.write_bytes(&get_options_packet(
|
||||
config.slash_type,
|
||||
enabled,
|
||||
brightness,
|
||||
config.display_interval
|
||||
))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("ctrl_slash::set_options {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
config.enabled = enabled;
|
||||
config.brightness = brightness;
|
||||
config.write();
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn interval(&self) -> u8 {
|
||||
let config = self.0.lock_config().await;
|
||||
config.display_interval
|
||||
}
|
||||
|
||||
/// Set interval between slash animations (0-255)
|
||||
#[zbus(property)]
|
||||
async fn set_interval(&self, interval: u8) {
|
||||
let mut config = self.0.lock_config().await;
|
||||
self.0
|
||||
.write_bytes(&get_options_packet(
|
||||
config.slash_type, config.enabled, config.brightness, interval
|
||||
))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("ctrl_slash::set_options {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
config.display_interval = interval;
|
||||
config.write();
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn mode(&self) -> zbus::fdo::Result<u8> {
|
||||
let config = self.0.lock_config().await;
|
||||
Ok(config.display_interval)
|
||||
}
|
||||
|
||||
/// Set interval between slash animations (0-255)
|
||||
#[zbus(property)]
|
||||
async fn set_mode(&self, mode: SlashMode) -> zbus::Result<()> {
|
||||
let mut config = self.0.lock_config().await;
|
||||
|
||||
let command_packets = pkt_set_mode(config.slash_type, mode);
|
||||
// self.node.write_bytes(&command_packets[0])?;
|
||||
self.0.write_bytes(&command_packets[1]).await?;
|
||||
self.0.write_bytes(&pkt_save(config.slash_type)).await?;
|
||||
|
||||
config.display_mode = mode;
|
||||
config.write();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the device state as stored by asusd
|
||||
// #[zbus(property)]
|
||||
async fn device_state(&self) -> DeviceState {
|
||||
let config = self.0.lock_config().await;
|
||||
DeviceState::from(&*config)
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn show_on_boot(&self) -> zbus::fdo::Result<bool> {
|
||||
let config = self.0.lock_config().await;
|
||||
Ok(config.show_on_boot)
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn set_show_on_boot(&self, enable: bool) -> zbus::Result<()> {
|
||||
let mut config = self.0.lock_config().await;
|
||||
self.0
|
||||
.write_bytes(&get_boot_packet(config.slash_type, enable))
|
||||
.await?;
|
||||
config.show_on_boot = enable;
|
||||
config.write();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn show_on_sleep(&self) -> zbus::fdo::Result<bool> {
|
||||
let config = self.0.lock_config().await;
|
||||
Ok(config.show_on_sleep)
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn set_show_on_sleep(&self, enable: bool) -> zbus::Result<()> {
|
||||
let mut config = self.0.lock_config().await;
|
||||
self.0
|
||||
.write_bytes(&get_sleep_packet(config.slash_type, enable))
|
||||
.await?;
|
||||
config.show_on_sleep = enable;
|
||||
config.write();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn show_on_shutdown(&self) -> zbus::fdo::Result<bool> {
|
||||
let config = self.0.lock_config().await;
|
||||
Ok(config.show_on_shutdown)
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn set_show_on_shutdown(&self, enable: bool) -> zbus::Result<()> {
|
||||
let mut config = self.0.lock_config().await;
|
||||
self.0
|
||||
.write_bytes(&get_shutdown_packet(config.slash_type, enable))
|
||||
.await?;
|
||||
config.show_on_shutdown = enable;
|
||||
config.write();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn show_on_battery(&self) -> zbus::fdo::Result<bool> {
|
||||
let config = self.0.lock_config().await;
|
||||
Ok(config.show_on_battery)
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn set_show_on_battery(&self, enable: bool) -> zbus::Result<()> {
|
||||
let mut config = self.0.lock_config().await;
|
||||
self.0
|
||||
.write_bytes(&get_battery_saver_packet(config.slash_type, enable))
|
||||
.await?;
|
||||
config.show_on_battery = enable;
|
||||
config.write();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn show_battery_warning(&self) -> zbus::fdo::Result<bool> {
|
||||
let config = self.0.lock_config().await;
|
||||
Ok(config.show_battery_warning)
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn set_show_battery_warning(&self, enable: bool) -> zbus::Result<()> {
|
||||
let mut config = self.0.lock_config().await;
|
||||
self.0
|
||||
.write_bytes(&get_low_battery_packet(config.slash_type, enable))
|
||||
.await?;
|
||||
config.show_battery_warning = enable;
|
||||
config.write();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Reloadable for SlashZbus {
|
||||
async fn reload(&mut self) -> Result<(), RogError> {
|
||||
debug!("reloading slash settings");
|
||||
let config = self.0.lock_config().await;
|
||||
self.0
|
||||
.write_bytes(&get_options_packet(
|
||||
config.slash_type,
|
||||
config.enabled,
|
||||
config.brightness,
|
||||
config.display_interval
|
||||
))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("set_options {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
macro_rules! write_bytes_with_warning {
|
||||
($packet_fn:expr, $cfg:ident, $warn_msg:expr) => {
|
||||
self.0
|
||||
.write_bytes(&$packet_fn(config.slash_type, config.$cfg))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("{} {}", $warn_msg, err);
|
||||
})
|
||||
.ok();
|
||||
};
|
||||
}
|
||||
|
||||
write_bytes_with_warning!(get_boot_packet, show_on_boot, "show_on_boot");
|
||||
write_bytes_with_warning!(get_sleep_packet, show_on_sleep, "show_on_sleep");
|
||||
write_bytes_with_warning!(get_shutdown_packet, show_on_shutdown, "show_on_shutdown");
|
||||
write_bytes_with_warning!(get_battery_saver_packet, show_on_battery, "show_on_battery");
|
||||
write_bytes_with_warning!(
|
||||
get_low_battery_packet,
|
||||
show_battery_warning,
|
||||
"show_battery_warning"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
209
asusd/src/aura_types.rs
Normal file
209
asusd/src/aura_types.rs
Normal file
@@ -0,0 +1,209 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use config_traits::{StdConfig, StdConfigLoad};
|
||||
use futures_util::lock::Mutex;
|
||||
use log::{debug, error, info};
|
||||
use rog_anime::error::AnimeError;
|
||||
use rog_anime::usb::get_anime_type;
|
||||
use rog_anime::AnimeType;
|
||||
use rog_aura::AuraDeviceType;
|
||||
use rog_platform::hid_raw::HidRaw;
|
||||
use rog_platform::keyboard_led::KeyboardBacklight;
|
||||
use rog_platform::usb_raw::USBRaw;
|
||||
use rog_scsi::{open_device, ScsiType};
|
||||
use rog_slash::error::SlashError;
|
||||
use rog_slash::SlashType;
|
||||
|
||||
use crate::aura_anime::config::AniMeConfig;
|
||||
use crate::aura_anime::AniMe;
|
||||
use crate::aura_laptop::config::AuraConfig;
|
||||
use crate::aura_laptop::Aura;
|
||||
use crate::aura_scsi::config::ScsiConfig;
|
||||
use crate::aura_scsi::ScsiAura;
|
||||
use crate::aura_slash::config::SlashConfig;
|
||||
use crate::aura_slash::Slash;
|
||||
use crate::error::RogError;
|
||||
|
||||
pub enum _DeviceHandle {
|
||||
/// The AniMe devices require USBRaw as they are not HID devices
|
||||
Usb(USBRaw),
|
||||
HidRaw(HidRaw),
|
||||
LedClass(KeyboardBacklight),
|
||||
/// TODO
|
||||
MulticolourLed,
|
||||
None
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum DeviceHandle {
|
||||
Aura(Aura),
|
||||
Slash(Slash),
|
||||
/// The AniMe devices require USBRaw as they are not HID devices
|
||||
AniMe(AniMe),
|
||||
Scsi(ScsiAura),
|
||||
Ally(Arc<Mutex<HidRaw>>),
|
||||
OldAura(Arc<Mutex<HidRaw>>),
|
||||
/// TUF laptops have an aditional set of attributes added to the LED /sysfs/
|
||||
TufLedClass(Arc<Mutex<HidRaw>>),
|
||||
/// TODO
|
||||
MulticolourLed,
|
||||
None
|
||||
}
|
||||
|
||||
impl DeviceHandle {
|
||||
/// Try Slash HID. If one exists it is initialsed and returned.
|
||||
pub async fn new_slash_hid(
|
||||
device: Arc<Mutex<HidRaw>>,
|
||||
prod_id: &str
|
||||
) -> Result<Self, RogError> {
|
||||
debug!("Testing for HIDRAW Slash");
|
||||
let slash_type = SlashType::from_dmi();
|
||||
if matches!(slash_type, SlashType::Unsupported)
|
||||
|| slash_type
|
||||
.prod_id_str()
|
||||
.to_lowercase()
|
||||
.trim_start_matches("0x")
|
||||
!= prod_id
|
||||
{
|
||||
log::info!("Unknown or invalid slash: {prod_id:?}, skipping");
|
||||
return Err(RogError::NotFound("No slash device".to_string()));
|
||||
}
|
||||
info!("Found slash type {slash_type:?}: {prod_id}");
|
||||
|
||||
let mut config = SlashConfig::new().load();
|
||||
config.slash_type = slash_type;
|
||||
let slash = Slash::new(Some(device), None, Arc::new(Mutex::new(config)));
|
||||
slash.do_initialization().await?;
|
||||
Ok(Self::Slash(slash))
|
||||
}
|
||||
|
||||
/// Try Slash USB. If one exists it is initialsed and returned.
|
||||
pub async fn new_slash_usb() -> Result<Self, RogError> {
|
||||
debug!("Testing for USB Slash");
|
||||
let slash_type = SlashType::from_dmi();
|
||||
if matches!(slash_type, SlashType::Unsupported) {
|
||||
return Err(RogError::Slash(SlashError::NoDevice));
|
||||
}
|
||||
|
||||
if let Ok(usb) = USBRaw::new(slash_type.prod_id()) {
|
||||
info!("Found Slash USB {slash_type:?}");
|
||||
|
||||
let mut config = SlashConfig::new().load();
|
||||
config.slash_type = slash_type;
|
||||
let slash = Slash::new(
|
||||
None,
|
||||
Some(Arc::new(Mutex::new(usb))),
|
||||
Arc::new(Mutex::new(config))
|
||||
);
|
||||
slash.do_initialization().await?;
|
||||
Ok(Self::Slash(slash))
|
||||
} else {
|
||||
Err(RogError::NotFound("No slash device found".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Try AniMe Matrix HID. If one exists it is initialsed and returned.
|
||||
pub async fn maybe_anime_hid(
|
||||
_device: Arc<Mutex<HidRaw>>,
|
||||
_prod_id: &str
|
||||
) -> Result<Self, RogError> {
|
||||
// TODO: can't use HIDRAW for anime at the moment
|
||||
Err(RogError::NotFound(
|
||||
"Can't use anime over hidraw yet. Skip.".to_string()
|
||||
))
|
||||
|
||||
// debug!("Testing for HIDRAW AniMe");
|
||||
// let anime_type = AnimeType::from_dmi();
|
||||
// dbg!(prod_id);
|
||||
// if matches!(anime_type, AnimeType::Unsupported) || prod_id != "193b"
|
||||
// { log::info!("Unknown or invalid AniMe: {prod_id:?},
|
||||
// skipping"); return Err(RogError::NotFound("No
|
||||
// anime-matrix device".to_string())); }
|
||||
// info!("Found AniMe Matrix HIDRAW {anime_type:?}: {prod_id}");
|
||||
|
||||
// let mut config = AniMeConfig::new().load();
|
||||
// config.anime_type = anime_type;
|
||||
// let mut anime = AniMe::new(Some(device), None,
|
||||
// Arc::new(Mutex::new(config))); anime.do_initialization().
|
||||
// await?; Ok(Self::AniMe(anime))
|
||||
}
|
||||
|
||||
pub async fn maybe_anime_usb() -> Result<Self, RogError> {
|
||||
debug!("Testing for USB AniMe");
|
||||
let anime_type = get_anime_type();
|
||||
if matches!(anime_type, AnimeType::Unsupported) {
|
||||
info!("No Anime Matrix capable laptop found");
|
||||
return Err(RogError::Anime(AnimeError::NoDevice));
|
||||
}
|
||||
|
||||
if let Ok(usb) = USBRaw::new(0x193b) {
|
||||
info!("Found AniMe Matrix USB {anime_type:?}");
|
||||
|
||||
let mut config = AniMeConfig::new().load();
|
||||
config.anime_type = anime_type;
|
||||
let mut anime = AniMe::new(
|
||||
None,
|
||||
Some(Arc::new(Mutex::new(usb))),
|
||||
Arc::new(Mutex::new(config))
|
||||
);
|
||||
anime.do_initialization().await?;
|
||||
Ok(Self::AniMe(anime))
|
||||
} else {
|
||||
Err(RogError::NotFound(
|
||||
"No AnimeMatrix device found".to_string()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn maybe_scsi(dev_node: &str, prod_id: &str) -> Result<Self, RogError> {
|
||||
debug!("Testing for SCSI");
|
||||
let prod_id = ScsiType::from(prod_id);
|
||||
if prod_id == ScsiType::Unsupported {
|
||||
log::info!("Unknown or invalid SCSI: {prod_id:?}, skipping");
|
||||
return Err(RogError::NotFound("No SCSI device".to_string()));
|
||||
}
|
||||
info!("Found SCSI device {prod_id:?} on {dev_node}");
|
||||
|
||||
let mut config = ScsiConfig::new().load();
|
||||
config.dev_type = AuraDeviceType::ScsiExtDisk;
|
||||
let dev = Arc::new(Mutex::new(open_device(dev_node)?));
|
||||
let scsi = ScsiAura::new(dev, Arc::new(Mutex::new(config)));
|
||||
scsi.do_initialization().await?;
|
||||
Ok(Self::Scsi(scsi))
|
||||
}
|
||||
|
||||
pub async fn maybe_laptop_aura(
|
||||
device: Option<Arc<Mutex<HidRaw>>>,
|
||||
prod_id: &str
|
||||
) -> Result<Self, RogError> {
|
||||
debug!("Testing for laptop aura");
|
||||
let aura_type = AuraDeviceType::from(prod_id);
|
||||
if !matches!(
|
||||
aura_type,
|
||||
AuraDeviceType::LaptopKeyboard2021
|
||||
| AuraDeviceType::LaptopKeyboardPre2021
|
||||
| AuraDeviceType::LaptopKeyboardTuf
|
||||
) {
|
||||
log::info!("Unknown or invalid laptop aura: {prod_id:?}, skipping");
|
||||
return Err(RogError::NotFound("No laptop aura device".to_string()));
|
||||
}
|
||||
info!("Found laptop aura type {prod_id:?}");
|
||||
|
||||
let backlight = KeyboardBacklight::new()
|
||||
.map_err(|e| error!("Keyboard backlight error: {e:?}"))
|
||||
.map_or(None, |k| {
|
||||
info!("Found sysfs backlight control");
|
||||
Some(Arc::new(Mutex::new(k)))
|
||||
});
|
||||
|
||||
let mut config = AuraConfig::load_and_update_config(prod_id);
|
||||
config.led_type = aura_type;
|
||||
let aura = Aura {
|
||||
hid: device,
|
||||
backlight,
|
||||
config: Arc::new(Mutex::new(config))
|
||||
};
|
||||
aura.do_initialization().await?;
|
||||
Ok(Self::Aura(aura))
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,90 @@
|
||||
use config_traits::{StdConfig, StdConfigLoad2};
|
||||
use rog_platform::platform::PlatformPolicy;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use config_traits::{StdConfig, StdConfigLoad1};
|
||||
use rog_platform::asus_armoury::FirmwareAttribute;
|
||||
use rog_platform::cpu::CPUEPP;
|
||||
use rog_platform::platform::PlatformProfile;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const CONFIG_FILE: &str = "asusd.ron";
|
||||
|
||||
#[derive(Deserialize, Serialize, Default, Debug)]
|
||||
#[derive(Default, Clone, Deserialize, Serialize, PartialEq)]
|
||||
pub struct Tuning {
|
||||
pub enabled: bool,
|
||||
pub group: HashMap<FirmwareAttribute, i32>
|
||||
}
|
||||
type Tunings = HashMap<PlatformProfile, Tuning>;
|
||||
|
||||
#[derive(Deserialize, Serialize, PartialEq)]
|
||||
pub struct Config {
|
||||
/// Save charge limit for restoring on boot
|
||||
// The current charge limit applied
|
||||
pub charge_control_end_threshold: u8,
|
||||
pub panel_od: bool,
|
||||
pub mini_led_mode: bool,
|
||||
pub disable_nvidia_powerd_on_battery: bool,
|
||||
pub ac_command: String,
|
||||
pub bat_command: String,
|
||||
/// Restored on boot as well as when power is plugged
|
||||
/// Save charge limit for restoring
|
||||
#[serde(skip)]
|
||||
pub platform_policy_to_restore: PlatformPolicy,
|
||||
pub platform_policy_on_battery: PlatformPolicy,
|
||||
pub platform_policy_on_ac: PlatformPolicy,
|
||||
//
|
||||
pub ppt_pl1_spl: Option<u8>,
|
||||
pub ppt_pl2_sppt: Option<u8>,
|
||||
pub ppt_fppt: Option<u8>,
|
||||
pub ppt_apu_sppt: Option<u8>,
|
||||
pub ppt_platform_sppt: Option<u8>,
|
||||
pub nv_dynamic_boost: Option<u8>,
|
||||
pub nv_temp_target: Option<u8>,
|
||||
pub base_charge_control_end_threshold: u8,
|
||||
pub disable_nvidia_powerd_on_battery: bool,
|
||||
/// An optional command/script to run when power is changed to AC
|
||||
pub ac_command: String,
|
||||
/// An optional command/script to run when power is changed to battery
|
||||
pub bat_command: String,
|
||||
/// Set true if energy_performance_preference should be set if the
|
||||
/// platform profile is changed
|
||||
pub platform_profile_linked_epp: bool,
|
||||
/// Which platform profile to use on battery power
|
||||
pub platform_profile_on_battery: PlatformProfile,
|
||||
/// Should the throttle policy be set on bat/ac change?
|
||||
pub change_platform_profile_on_battery: bool,
|
||||
/// Which platform profile to use on AC power
|
||||
pub platform_profile_on_ac: PlatformProfile,
|
||||
/// Should the platform profile be set on bat/ac change?
|
||||
pub change_platform_profile_on_ac: bool,
|
||||
/// The energy_performance_preference for this platform profile
|
||||
pub profile_quiet_epp: CPUEPP,
|
||||
/// The energy_performance_preference for this platform profile
|
||||
pub profile_balanced_epp: CPUEPP,
|
||||
/// The energy_performance_preference for this platform profile
|
||||
pub profile_performance_epp: CPUEPP,
|
||||
pub ac_profile_tunings: Tunings,
|
||||
pub dc_profile_tunings: Tunings,
|
||||
pub armoury_settings: HashMap<FirmwareAttribute, i32>,
|
||||
/// Temporary state for AC/Batt
|
||||
#[serde(skip)]
|
||||
pub last_power_plugged: u8
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn select_tunings(&mut self, power_plugged: bool, profile: PlatformProfile) -> &mut Tuning {
|
||||
let config = if power_plugged {
|
||||
&mut self.ac_profile_tunings
|
||||
} else {
|
||||
&mut self.dc_profile_tunings
|
||||
};
|
||||
config.entry(profile).or_insert_with(Tuning::default)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
charge_control_end_threshold: 100,
|
||||
base_charge_control_end_threshold: 100,
|
||||
disable_nvidia_powerd_on_battery: true,
|
||||
ac_command: Default::default(),
|
||||
bat_command: Default::default(),
|
||||
platform_profile_linked_epp: true,
|
||||
platform_profile_on_battery: PlatformProfile::Quiet,
|
||||
change_platform_profile_on_battery: true,
|
||||
platform_profile_on_ac: PlatformProfile::Performance,
|
||||
change_platform_profile_on_ac: true,
|
||||
profile_quiet_epp: CPUEPP::Power,
|
||||
profile_balanced_epp: CPUEPP::BalancePower,
|
||||
profile_performance_epp: CPUEPP::Performance,
|
||||
ac_profile_tunings: HashMap::default(),
|
||||
dc_profile_tunings: HashMap::default(),
|
||||
armoury_settings: HashMap::default(),
|
||||
last_power_plugged: Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfig for Config {
|
||||
@@ -33,68 +92,85 @@ impl StdConfig for Config {
|
||||
Config {
|
||||
charge_control_end_threshold: 100,
|
||||
disable_nvidia_powerd_on_battery: true,
|
||||
platform_policy_on_battery: PlatformPolicy::Quiet,
|
||||
platform_policy_on_ac: PlatformPolicy::Performance,
|
||||
platform_profile_on_battery: PlatformProfile::Quiet,
|
||||
platform_profile_on_ac: PlatformProfile::Performance,
|
||||
ac_command: String::new(),
|
||||
bat_command: String::new(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn config_dir() -> std::path::PathBuf {
|
||||
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
|
||||
}
|
||||
|
||||
fn file_name(&self) -> String {
|
||||
CONFIG_FILE.to_owned()
|
||||
}
|
||||
|
||||
fn config_dir() -> std::path::PathBuf {
|
||||
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfigLoad2<Config462, Config472> for Config {}
|
||||
impl StdConfigLoad1<Config601> for Config {}
|
||||
|
||||
#[derive(Deserialize, Serialize, Default, Debug)]
|
||||
pub struct Config472 {
|
||||
/// Save charge limit for restoring on boot
|
||||
pub bat_charge_limit: u8,
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Config601 {
|
||||
pub charge_control_end_threshold: u8,
|
||||
#[serde(skip)]
|
||||
pub base_charge_control_end_threshold: u8,
|
||||
pub panel_od: bool,
|
||||
pub boot_sound: bool,
|
||||
pub mini_led_mode: bool,
|
||||
pub disable_nvidia_powerd_on_battery: bool,
|
||||
pub ac_command: String,
|
||||
pub bat_command: String,
|
||||
pub platform_profile_linked_epp: bool,
|
||||
pub platform_profile_on_battery: PlatformProfile,
|
||||
pub change_platform_profile_on_battery: bool,
|
||||
pub platform_profile_on_ac: PlatformProfile,
|
||||
pub change_platform_profile_on_ac: bool,
|
||||
pub profile_quiet_epp: CPUEPP,
|
||||
pub profile_balanced_epp: CPUEPP,
|
||||
pub profile_performance_epp: CPUEPP,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub ppt_pl1_spl: Option<u8>,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub ppt_pl2_sppt: Option<u8>,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub ppt_pl3_fppt: Option<u8>,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub ppt_fppt: Option<u8>,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub ppt_apu_sppt: Option<u8>,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub ppt_platform_sppt: Option<u8>,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub nv_dynamic_boost: Option<u8>,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub nv_temp_target: Option<u8>,
|
||||
#[serde(skip)]
|
||||
pub last_power_plugged: u8
|
||||
}
|
||||
|
||||
impl From<Config472> for Config {
|
||||
fn from(c: Config472) -> Self {
|
||||
impl From<Config601> for Config {
|
||||
fn from(c: Config601) -> Self {
|
||||
Self {
|
||||
charge_control_end_threshold: c.bat_charge_limit,
|
||||
panel_od: c.panel_od,
|
||||
disable_nvidia_powerd_on_battery: true,
|
||||
// Restore the base charge limit
|
||||
charge_control_end_threshold: c.charge_control_end_threshold,
|
||||
base_charge_control_end_threshold: c.charge_control_end_threshold,
|
||||
disable_nvidia_powerd_on_battery: c.disable_nvidia_powerd_on_battery,
|
||||
ac_command: c.ac_command,
|
||||
bat_command: c.bat_command,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Config462 {
|
||||
/// Save charge limit for restoring on boot
|
||||
pub bat_charge_limit: u8,
|
||||
pub panel_od: bool,
|
||||
pub disable_nvidia_powerd_on_battery: bool,
|
||||
pub ac_command: String,
|
||||
pub bat_command: String,
|
||||
}
|
||||
|
||||
impl From<Config462> for Config {
|
||||
fn from(c: Config462) -> Self {
|
||||
Self {
|
||||
charge_control_end_threshold: c.bat_charge_limit,
|
||||
panel_od: c.panel_od,
|
||||
disable_nvidia_powerd_on_battery: true,
|
||||
ac_command: String::new(),
|
||||
bat_command: String::new(),
|
||||
..Default::default()
|
||||
platform_profile_linked_epp: c.platform_profile_linked_epp,
|
||||
platform_profile_on_battery: c.platform_profile_on_battery,
|
||||
change_platform_profile_on_battery: c.change_platform_profile_on_battery,
|
||||
platform_profile_on_ac: c.platform_profile_on_ac,
|
||||
change_platform_profile_on_ac: c.change_platform_profile_on_ac,
|
||||
profile_quiet_epp: c.profile_quiet_epp,
|
||||
profile_balanced_epp: c.profile_balanced_epp,
|
||||
profile_performance_epp: c.profile_performance_epp,
|
||||
last_power_plugged: c.last_power_plugged,
|
||||
ac_profile_tunings: HashMap::default(),
|
||||
dc_profile_tunings: HashMap::default(),
|
||||
armoury_settings: HashMap::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,229 +0,0 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use config_traits::{StdConfig, StdConfigLoad2};
|
||||
use rog_anime::error::AnimeError;
|
||||
use rog_anime::usb::Brightness;
|
||||
use rog_anime::{
|
||||
ActionData, ActionLoader, AnimTime, Animations, AnimeType, DeviceState, Fade, Vec2,
|
||||
};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
const CONFIG_FILE: &str = "anime.ron";
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct AnimeConfigV460 {
|
||||
pub system: Vec<ActionLoader>,
|
||||
pub boot: Vec<ActionLoader>,
|
||||
pub wake: Vec<ActionLoader>,
|
||||
pub sleep: Vec<ActionLoader>,
|
||||
pub shutdown: Vec<ActionLoader>,
|
||||
pub brightness: f32,
|
||||
}
|
||||
|
||||
impl From<AnimeConfigV460> for AnimeConfig {
|
||||
fn from(c: AnimeConfigV460) -> AnimeConfig {
|
||||
AnimeConfig {
|
||||
system: c.system,
|
||||
boot: c.boot,
|
||||
wake: c.wake,
|
||||
shutdown: c.shutdown,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct AnimeConfigV472 {
|
||||
pub model_override: Option<AnimeType>,
|
||||
pub system: Vec<ActionLoader>,
|
||||
pub boot: Vec<ActionLoader>,
|
||||
pub wake: Vec<ActionLoader>,
|
||||
pub sleep: Vec<ActionLoader>,
|
||||
pub shutdown: Vec<ActionLoader>,
|
||||
pub brightness: f32,
|
||||
pub display_enabled: bool,
|
||||
pub display_brightness: Brightness,
|
||||
pub builtin_anims_enabled: bool,
|
||||
pub builtin_anims: Animations,
|
||||
}
|
||||
|
||||
impl From<AnimeConfigV472> for AnimeConfig {
|
||||
fn from(c: AnimeConfigV472) -> AnimeConfig {
|
||||
AnimeConfig {
|
||||
system: c.system,
|
||||
boot: c.boot,
|
||||
wake: c.wake,
|
||||
shutdown: c.shutdown,
|
||||
model_override: c.model_override,
|
||||
display_enabled: c.display_enabled,
|
||||
display_brightness: c.display_brightness,
|
||||
builtin_anims_enabled: c.builtin_anims_enabled,
|
||||
builtin_anims: c.builtin_anims,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Default)]
|
||||
pub struct AnimeConfigCached {
|
||||
pub system: Vec<ActionData>,
|
||||
pub boot: Vec<ActionData>,
|
||||
pub wake: Vec<ActionData>,
|
||||
pub shutdown: Vec<ActionData>,
|
||||
}
|
||||
|
||||
impl AnimeConfigCached {
|
||||
pub fn init_from_config(
|
||||
&mut self,
|
||||
config: &AnimeConfig,
|
||||
anime_type: AnimeType,
|
||||
) -> Result<(), AnimeError> {
|
||||
let mut sys = Vec::with_capacity(config.system.len());
|
||||
for ani in &config.system {
|
||||
sys.push(ActionData::from_anime_action(anime_type, ani)?);
|
||||
}
|
||||
self.system = sys;
|
||||
|
||||
let mut boot = Vec::with_capacity(config.boot.len());
|
||||
for ani in &config.boot {
|
||||
boot.push(ActionData::from_anime_action(anime_type, ani)?);
|
||||
}
|
||||
self.boot = boot;
|
||||
|
||||
let mut wake = Vec::with_capacity(config.wake.len());
|
||||
for ani in &config.wake {
|
||||
wake.push(ActionData::from_anime_action(anime_type, ani)?);
|
||||
}
|
||||
self.wake = wake;
|
||||
|
||||
let mut shutdown = Vec::with_capacity(config.shutdown.len());
|
||||
for ani in &config.shutdown {
|
||||
shutdown.push(ActionData::from_anime_action(anime_type, ani)?);
|
||||
}
|
||||
self.shutdown = shutdown;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Config for base system actions for the anime display
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct AnimeConfig {
|
||||
pub model_override: Option<AnimeType>,
|
||||
pub system: Vec<ActionLoader>,
|
||||
pub boot: Vec<ActionLoader>,
|
||||
pub wake: Vec<ActionLoader>,
|
||||
pub shutdown: Vec<ActionLoader>,
|
||||
// pub brightness: f32,
|
||||
pub display_enabled: bool,
|
||||
pub display_brightness: Brightness,
|
||||
pub builtin_anims_enabled: bool,
|
||||
pub off_when_unplugged: bool,
|
||||
pub off_when_suspended: bool,
|
||||
pub off_when_lid_closed: bool,
|
||||
pub brightness_on_battery: Brightness,
|
||||
pub builtin_anims: Animations,
|
||||
}
|
||||
|
||||
impl Default for AnimeConfig {
|
||||
fn default() -> Self {
|
||||
AnimeConfig {
|
||||
model_override: None,
|
||||
system: Vec::new(),
|
||||
boot: Vec::new(),
|
||||
wake: Vec::new(),
|
||||
shutdown: Vec::new(),
|
||||
// brightness: 1.0,
|
||||
display_enabled: true,
|
||||
display_brightness: Brightness::Med,
|
||||
builtin_anims_enabled: true,
|
||||
off_when_unplugged: true,
|
||||
off_when_suspended: true,
|
||||
off_when_lid_closed: true,
|
||||
brightness_on_battery: Brightness::Low,
|
||||
builtin_anims: Animations::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfig for AnimeConfig {
|
||||
fn new() -> Self {
|
||||
Self::create_default()
|
||||
}
|
||||
|
||||
fn config_dir() -> std::path::PathBuf {
|
||||
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
|
||||
}
|
||||
|
||||
fn file_name(&self) -> String {
|
||||
CONFIG_FILE.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfigLoad2<AnimeConfigV460, AnimeConfigV472> for AnimeConfig {}
|
||||
|
||||
impl From<&AnimeConfig> for DeviceState {
|
||||
fn from(config: &AnimeConfig) -> Self {
|
||||
DeviceState {
|
||||
display_enabled: config.display_enabled,
|
||||
display_brightness: config.display_brightness,
|
||||
builtin_anims_enabled: config.builtin_anims_enabled,
|
||||
builtin_anims: config.builtin_anims,
|
||||
off_when_unplugged: config.off_when_unplugged,
|
||||
off_when_suspended: config.off_when_suspended,
|
||||
off_when_lid_closed: config.off_when_lid_closed,
|
||||
brightness_on_battery: config.brightness_on_battery,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AnimeConfig {
|
||||
// fn clamp_config_brightness(mut config: &mut AnimeConfig) {
|
||||
// if config.brightness < 0.0 || config.brightness > 1.0 {
|
||||
// warn!(
|
||||
// "Clamped brightness to [0.0 ; 1.0], was {}",
|
||||
// config.brightness
|
||||
// );
|
||||
// config.brightness = f32::max(0.0, f32::min(1.0, config.brightness));
|
||||
// }
|
||||
// }
|
||||
|
||||
fn create_default() -> Self {
|
||||
// create a default config here
|
||||
AnimeConfig {
|
||||
system: vec![],
|
||||
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::Fade(Fade::new(
|
||||
Duration::from_secs(2),
|
||||
Some(Duration::from_secs(2)),
|
||||
Duration::from_secs(2),
|
||||
)),
|
||||
}],
|
||||
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::Fade(Fade::new(
|
||||
Duration::from_secs(2),
|
||||
Some(Duration::from_secs(2)),
|
||||
Duration::from_secs(2),
|
||||
)),
|
||||
}],
|
||||
shutdown: vec![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: 1.0,
|
||||
time: AnimTime::Infinite,
|
||||
}],
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,288 +0,0 @@
|
||||
pub mod config;
|
||||
/// Implements `CtrlTask`, Reloadable, `ZbusRun`
|
||||
pub mod trait_impls;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::thread::sleep;
|
||||
|
||||
use ::zbus::export::futures_util::lock::Mutex;
|
||||
use log::{error, info, warn};
|
||||
use rog_anime::error::AnimeError;
|
||||
use rog_anime::usb::{
|
||||
get_anime_type, pkt_flush, pkt_set_brightness, pkt_set_enable_display,
|
||||
pkt_set_enable_powersave_anim, pkts_for_init, Brightness,
|
||||
};
|
||||
use rog_anime::{ActionData, AnimeDataBuffer, AnimePacketType, AnimeType};
|
||||
use rog_platform::hid_raw::HidRaw;
|
||||
use rog_platform::usb_raw::USBRaw;
|
||||
|
||||
use self::config::{AnimeConfig, AnimeConfigCached};
|
||||
use crate::error::RogError;
|
||||
|
||||
enum Node {
|
||||
Usb(USBRaw),
|
||||
Hid(HidRaw),
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
|
||||
// TODO: map and pass on errors
|
||||
match self {
|
||||
Node::Usb(u) => {
|
||||
u.write_bytes(message).ok();
|
||||
}
|
||||
Node::Hid(h) => {
|
||||
h.write_bytes(message).ok();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_builtins_enabled(&self, enabled: bool, bright: Brightness) -> Result<(), RogError> {
|
||||
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))?;
|
||||
self.write_bytes(&pkt_set_enable_display(enabled))?;
|
||||
self.write_bytes(&pkt_set_brightness(bright))?;
|
||||
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CtrlAnime {
|
||||
// node: HidRaw,
|
||||
node: Node,
|
||||
anime_type: AnimeType,
|
||||
cache: AnimeConfigCached,
|
||||
config: AnimeConfig,
|
||||
// set to force thread to exit
|
||||
thread_exit: Arc<AtomicBool>,
|
||||
// Set to false when the thread exits
|
||||
thread_running: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl CtrlAnime {
|
||||
#[inline]
|
||||
pub fn new(config: AnimeConfig) -> Result<CtrlAnime, RogError> {
|
||||
let usb = USBRaw::new(0x193b).ok();
|
||||
let hid = HidRaw::new("193b").ok();
|
||||
let node = if usb.is_some() {
|
||||
unsafe { Node::Usb(usb.unwrap_unchecked()) }
|
||||
} else if hid.is_some() {
|
||||
unsafe { Node::Hid(hid.unwrap_unchecked()) }
|
||||
} else {
|
||||
return Err(RogError::Anime(AnimeError::NoDevice));
|
||||
};
|
||||
|
||||
// TODO: something better to set wakeups disabled
|
||||
if matches!(node, Node::Usb(_)) {
|
||||
if let Ok(mut enumerator) = udev::Enumerator::new() {
|
||||
enumerator.match_subsystem("usb").ok();
|
||||
enumerator.match_attribute("idProduct", "193b").ok();
|
||||
|
||||
if let Ok(mut enumer) = enumerator.scan_devices() {
|
||||
if let Some(mut dev) = enumer.next() {
|
||||
dev.set_attribute_value("power/wakeup", "disabled").ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut anime_type = get_anime_type()?;
|
||||
if let AnimeType::Unknown = anime_type {
|
||||
if let Some(model) = config.model_override {
|
||||
warn!("Overriding the Animatrix type as {model:?}");
|
||||
anime_type = model;
|
||||
}
|
||||
}
|
||||
|
||||
info!("Device has an AniMe Matrix display: {anime_type:?}");
|
||||
let mut cache = AnimeConfigCached::default();
|
||||
cache.init_from_config(&config, anime_type)?;
|
||||
|
||||
let ctrl = CtrlAnime {
|
||||
node,
|
||||
anime_type,
|
||||
cache,
|
||||
config,
|
||||
thread_exit: Arc::new(AtomicBool::new(false)),
|
||||
thread_running: Arc::new(AtomicBool::new(false)),
|
||||
};
|
||||
ctrl.do_initialization()?;
|
||||
|
||||
Ok(ctrl)
|
||||
}
|
||||
|
||||
// let device = CtrlAnime::get_device(0x0b05, 0x193b)?;
|
||||
|
||||
/// Start an action thread. This is classed as a singleton and there should
|
||||
/// be only one running - so the thread uses atomics to signal run/exit.
|
||||
///
|
||||
/// Because this also writes to the usb device, other write tries (display
|
||||
/// only) *must* get the mutex lock and set the `thread_exit` atomic.
|
||||
async fn run_thread(inner: Arc<Mutex<CtrlAnime>>, actions: Vec<ActionData>, mut once: bool) {
|
||||
if actions.is_empty() {
|
||||
warn!("AniMe system actions was empty");
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(lock) = inner.try_lock() {
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_enable_powersave_anim(false))
|
||||
.map_err(|err| {
|
||||
warn!("rog_anime::run_animation:callback {}", err);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
// Loop rules:
|
||||
// - Lock the mutex **only when required**. That is, the lock must be held for
|
||||
// the shortest duration possible.
|
||||
// - An AtomicBool used for thread exit should be checked in every loop,
|
||||
// including nested
|
||||
|
||||
// The only reason for this outer thread is to prevent blocking while waiting
|
||||
// for the next spawned thread to exit
|
||||
// TODO: turn this in to async task (maybe? COuld still risk blocking main
|
||||
// thread)
|
||||
std::thread::Builder::new()
|
||||
.name("AniMe system thread start".into())
|
||||
.spawn(move || {
|
||||
info!("AniMe new system thread started");
|
||||
// Getting copies of these Atomics is done *in* the thread to ensure
|
||||
// we don't block other threads/main
|
||||
let thread_exit;
|
||||
let thread_running;
|
||||
let anime_type;
|
||||
loop {
|
||||
if let Some(lock) = inner.try_lock() {
|
||||
thread_exit = lock.thread_exit.clone();
|
||||
thread_running = lock.thread_running.clone();
|
||||
anime_type = lock.anime_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// First two loops are to ensure we *do* aquire a lock on the mutex
|
||||
// The reason the loop is required is because the USB writes can block
|
||||
// for up to 10ms. We can't fail to get the atomics.
|
||||
while thread_running.load(Ordering::SeqCst) {
|
||||
// Make any running loop exit first
|
||||
thread_exit.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
info!("AniMe no previous system thread running (now)");
|
||||
thread_exit.store(false, Ordering::SeqCst);
|
||||
thread_running.store(true, Ordering::SeqCst);
|
||||
'main: loop {
|
||||
for action in &actions {
|
||||
if thread_exit.load(Ordering::SeqCst) {
|
||||
break 'main;
|
||||
}
|
||||
match action {
|
||||
ActionData::Animation(frames) => {
|
||||
rog_anime::run_animation(frames, &|frame| {
|
||||
if thread_exit.load(Ordering::Acquire) {
|
||||
info!("rog-anime: animation sub-loop was asked to exit");
|
||||
return Ok(true); // Do safe exit
|
||||
}
|
||||
inner
|
||||
.try_lock()
|
||||
.map(|lock| {
|
||||
lock.write_data_buffer(frame)
|
||||
.map_err(|err| {
|
||||
warn!(
|
||||
"rog_anime::run_animation:callback {}",
|
||||
err
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
false // Don't exit yet
|
||||
})
|
||||
.map_or_else(
|
||||
|| {
|
||||
warn!("rog_anime::run_animation:callback failed");
|
||||
Err(AnimeError::NoFrames)
|
||||
},
|
||||
Ok,
|
||||
)
|
||||
});
|
||||
if thread_exit.load(Ordering::Acquire) {
|
||||
info!("rog-anime: sub-loop exited and main loop exiting now");
|
||||
break 'main;
|
||||
}
|
||||
}
|
||||
ActionData::Image(image) => {
|
||||
once = false;
|
||||
if let Some(lock) = inner.try_lock() {
|
||||
lock.write_data_buffer(image.as_ref().clone())
|
||||
.map_err(|e| error!("{}", e))
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
ActionData::Pause(duration) => sleep(*duration),
|
||||
ActionData::AudioEq
|
||||
| ActionData::SystemInfo
|
||||
| ActionData::TimeDate
|
||||
| ActionData::Matrix => {}
|
||||
}
|
||||
}
|
||||
if thread_exit.load(Ordering::SeqCst) {
|
||||
break 'main;
|
||||
}
|
||||
if once || actions.is_empty() {
|
||||
break 'main;
|
||||
}
|
||||
}
|
||||
// Clear the display on exit
|
||||
if let Some(lock) = inner.try_lock() {
|
||||
if let Ok(data) =
|
||||
AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()])
|
||||
.map_err(|e| error!("{}", e))
|
||||
{
|
||||
lock.write_data_buffer(data)
|
||||
.map_err(|err| {
|
||||
warn!("rog_anime::run_animation:callback {}", err);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_enable_powersave_anim(
|
||||
lock.config.builtin_anims_enabled,
|
||||
))
|
||||
.map_err(|err| {
|
||||
warn!("rog_anime::run_animation:callback {}", err);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
// Loop ended, set the atmonics
|
||||
thread_running.store(false, Ordering::SeqCst);
|
||||
info!("AniMe system thread exited");
|
||||
})
|
||||
.map(|err| info!("AniMe system thread: {:?}", err))
|
||||
.ok();
|
||||
}
|
||||
|
||||
/// Write only a data packet. This will modify the leds brightness using the
|
||||
/// global brightness set in config.
|
||||
fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) -> Result<(), RogError> {
|
||||
for led in buffer.data_mut().iter_mut() {
|
||||
let mut bright = *led as f32;
|
||||
if bright > 254.0 {
|
||||
bright = 254.0;
|
||||
}
|
||||
*led = bright as u8;
|
||||
}
|
||||
let data = AnimePacketType::try_from(buffer)?;
|
||||
for row in &data {
|
||||
self.node.write_bytes(row)?;
|
||||
}
|
||||
self.node.write_bytes(&pkt_flush())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_initialization(&self) -> Result<(), RogError> {
|
||||
let pkts = pkts_for_init();
|
||||
self.node.write_bytes(&pkts[0])?;
|
||||
self.node.write_bytes(&pkts[1])?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,432 +0,0 @@
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use config_traits::StdConfig;
|
||||
use log::warn;
|
||||
use logind_zbus::manager::ManagerProxy;
|
||||
use rog_anime::usb::{
|
||||
pkt_set_brightness, pkt_set_builtin_animations, pkt_set_enable_display,
|
||||
pkt_set_enable_powersave_anim, Brightness,
|
||||
};
|
||||
use rog_anime::{Animations, AnimeDataBuffer, DeviceState};
|
||||
use zbus::export::futures_util::lock::Mutex;
|
||||
use zbus::{dbus_interface, CacheProperties, Connection, SignalContext};
|
||||
|
||||
use super::CtrlAnime;
|
||||
use crate::error::RogError;
|
||||
|
||||
pub const ANIME_ZBUS_NAME: &str = "Anime";
|
||||
pub const ANIME_ZBUS_PATH: &str = "/org/asuslinux/Anime";
|
||||
|
||||
async fn get_logind_manager<'a>() -> ManagerProxy<'a> {
|
||||
let connection = Connection::system()
|
||||
.await
|
||||
.expect("Controller could not create dbus connection");
|
||||
|
||||
ManagerProxy::builder(&connection)
|
||||
.cache_properties(CacheProperties::No)
|
||||
.build()
|
||||
.await
|
||||
.expect("Controller could not create ManagerProxy")
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>);
|
||||
|
||||
/// The struct with the main dbus methods requires this trait
|
||||
#[async_trait]
|
||||
impl crate::ZbusRun for CtrlAnimeZbus {
|
||||
async fn add_to_server(self, server: &mut Connection) {
|
||||
Self::add_to_server_helper(self, ANIME_ZBUS_PATH, server).await;
|
||||
}
|
||||
}
|
||||
|
||||
// None of these calls can be guarnateed to succeed unless we loop until okay
|
||||
// If the try_lock *does* succeed then any other thread trying to lock will not
|
||||
// grab it until we finish.
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
impl CtrlAnimeZbus {
|
||||
/// Writes a data stream of length. Will force system thread to exit until
|
||||
/// it is restarted
|
||||
async fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> {
|
||||
let lock = self.0.lock().await;
|
||||
lock.thread_exit.store(true, Ordering::SeqCst);
|
||||
lock.write_data_buffer(input).map_err(|err| {
|
||||
warn!("ctrl_anime::run_animation:callback {}", err);
|
||||
err
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set base brightness level
|
||||
#[dbus_interface(property)]
|
||||
async fn brightness(&self) -> Brightness {
|
||||
let lock = self.0.lock().await;
|
||||
lock.config.display_brightness
|
||||
}
|
||||
|
||||
/// Set base brightness level
|
||||
#[dbus_interface(property)]
|
||||
async fn set_brightness(&self, brightness: Brightness) {
|
||||
let mut lock = self.0.lock().await;
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_brightness(brightness))
|
||||
.map_err(|err| {
|
||||
warn!("ctrl_anime::set_brightness {}", err);
|
||||
})
|
||||
.ok();
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_enable_display(brightness != Brightness::Off))
|
||||
.map_err(|err| {
|
||||
warn!("ctrl_anime::set_brightness {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
lock.config.display_enabled = brightness != Brightness::Off;
|
||||
lock.config.display_brightness = brightness;
|
||||
lock.config.write();
|
||||
}
|
||||
|
||||
#[dbus_interface(property)]
|
||||
async fn builtins_enabled(&self) -> bool {
|
||||
let lock = self.0.lock().await;
|
||||
lock.config.builtin_anims_enabled
|
||||
}
|
||||
|
||||
/// Enable the builtin animations or not. This is quivalent to "Powersave
|
||||
/// animations" in Armory crate
|
||||
#[dbus_interface(property)]
|
||||
async fn set_builtins_enabled(&self, enabled: bool) {
|
||||
let mut lock = self.0.lock().await;
|
||||
lock.node
|
||||
.set_builtins_enabled(enabled, lock.config.display_brightness)
|
||||
.map_err(|err| {
|
||||
warn!("ctrl_anime::set_builtins_enabled {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
if !enabled {
|
||||
let data = vec![255u8; lock.anime_type.data_length()];
|
||||
if let Ok(tmp) = AnimeDataBuffer::from_vec(lock.anime_type, data).map_err(|err| {
|
||||
warn!("ctrl_anime::set_builtins_enabled {}", err);
|
||||
}) {
|
||||
lock.node
|
||||
.write_bytes(tmp.data())
|
||||
.map_err(|err| {
|
||||
warn!("ctrl_anime::set_builtins_enabled {}", err);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
lock.config.builtin_anims_enabled = enabled;
|
||||
lock.config.write();
|
||||
if enabled {
|
||||
lock.thread_exit.store(true, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
#[dbus_interface(property)]
|
||||
async fn builtin_animations(&self) -> Animations {
|
||||
let lock = self.0.lock().await;
|
||||
lock.config.builtin_anims
|
||||
}
|
||||
|
||||
/// Set which builtin animation is used for each stage
|
||||
#[dbus_interface(property)]
|
||||
async fn set_builtin_animations(&self, settings: Animations) {
|
||||
let mut lock = self.0.lock().await;
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_builtin_animations(
|
||||
settings.boot,
|
||||
settings.awake,
|
||||
settings.sleep,
|
||||
settings.shutdown,
|
||||
))
|
||||
.map_err(|err| {
|
||||
warn!("ctrl_anime::run_animation:callback {}", err);
|
||||
})
|
||||
.ok();
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_enable_powersave_anim(true))
|
||||
.map_err(|err| {
|
||||
warn!("ctrl_anime::run_animation:callback {}", err);
|
||||
})
|
||||
.ok();
|
||||
lock.config.display_enabled = true;
|
||||
lock.config.builtin_anims = settings;
|
||||
lock.config.write();
|
||||
}
|
||||
|
||||
#[dbus_interface(property)]
|
||||
async fn enable_display(&self) -> bool {
|
||||
let lock = self.0.lock().await;
|
||||
lock.config.display_enabled
|
||||
}
|
||||
|
||||
/// Set whether the AniMe is enabled at all
|
||||
#[dbus_interface(property)]
|
||||
async fn set_enable_display(&self, enabled: bool) {
|
||||
let mut lock = self.0.lock().await;
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_enable_display(enabled))
|
||||
.map_err(|err| {
|
||||
warn!("ctrl_anime::run_animation:callback {}", err);
|
||||
})
|
||||
.ok();
|
||||
lock.config.display_enabled = enabled;
|
||||
lock.config.write();
|
||||
}
|
||||
|
||||
#[dbus_interface(property)]
|
||||
async fn off_when_unplugged(&self) -> bool {
|
||||
let lock = self.0.lock().await;
|
||||
lock.config.off_when_unplugged
|
||||
}
|
||||
|
||||
/// Set if to turn the AniMe Matrix off when external power is unplugged
|
||||
#[dbus_interface(property)]
|
||||
async fn set_off_when_unplugged(&self, enabled: bool) {
|
||||
let mut lock = self.0.lock().await;
|
||||
let manager = get_logind_manager().await;
|
||||
let pow = manager.on_external_power().await.unwrap_or_default();
|
||||
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_enable_display(!pow && !enabled))
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
lock.config.off_when_unplugged = enabled;
|
||||
lock.config.write();
|
||||
}
|
||||
|
||||
#[dbus_interface(property)]
|
||||
async fn off_when_suspended(&self) -> bool {
|
||||
let lock = self.0.lock().await;
|
||||
lock.config.off_when_suspended
|
||||
}
|
||||
|
||||
/// Set if to turn the AniMe Matrix off when the laptop is suspended
|
||||
#[dbus_interface(property)]
|
||||
async fn set_off_when_suspended(&self, enabled: bool) {
|
||||
let mut lock = self.0.lock().await;
|
||||
lock.config.off_when_suspended = enabled;
|
||||
lock.config.write();
|
||||
}
|
||||
|
||||
#[dbus_interface(property)]
|
||||
async fn off_when_lid_closed(&self) -> bool {
|
||||
let lock = self.0.lock().await;
|
||||
lock.config.off_when_lid_closed
|
||||
}
|
||||
|
||||
/// Set if to turn the AniMe Matrix off when the lid is closed
|
||||
#[dbus_interface(property)]
|
||||
async fn set_off_when_lid_closed(&self, enabled: bool) {
|
||||
let mut lock = self.0.lock().await;
|
||||
let manager = get_logind_manager().await;
|
||||
let lid = manager.lid_closed().await.unwrap_or_default();
|
||||
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_enable_display(lid && !enabled))
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
lock.config.off_when_lid_closed = enabled;
|
||||
lock.config.write();
|
||||
}
|
||||
|
||||
/// The main loop is the base system set action if the user isn't running
|
||||
/// the user daemon
|
||||
async fn run_main_loop(&self, start: bool) {
|
||||
if start {
|
||||
let lock = self.0.lock().await;
|
||||
lock.thread_exit.store(true, Ordering::SeqCst);
|
||||
CtrlAnime::run_thread(self.0.clone(), lock.cache.system.clone(), false).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the device state as stored by asusd
|
||||
// #[dbus_interface(property)]
|
||||
async fn device_state(&self) -> DeviceState {
|
||||
let lock = self.0.lock().await;
|
||||
DeviceState::from(&lock.config)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl crate::CtrlTask for CtrlAnimeZbus {
|
||||
fn zbus_path() -> &'static str {
|
||||
ANIME_ZBUS_PATH
|
||||
}
|
||||
|
||||
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
|
||||
let inner1 = self.0.clone();
|
||||
let inner2 = self.0.clone();
|
||||
let inner3 = self.0.clone();
|
||||
let inner4 = self.0.clone();
|
||||
self.create_sys_event_tasks(
|
||||
move |sleeping| {
|
||||
// on_sleep
|
||||
let inner = inner1.clone();
|
||||
async move {
|
||||
let lock = inner.lock().await;
|
||||
if lock.config.display_enabled {
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_enable_powersave_anim(
|
||||
!(sleeping && lock.config.off_when_suspended),
|
||||
))
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::off_when_suspended {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
lock.thread_exit.store(true, Ordering::Release); // ensure clean slate
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_enable_display(
|
||||
!(sleeping && lock.config.off_when_suspended),
|
||||
))
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::off_when_suspended {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
if !sleeping && !lock.config.builtin_anims_enabled {
|
||||
CtrlAnime::run_thread(inner.clone(), lock.cache.wake.clone(), true)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
move |shutting_down| {
|
||||
// on_shutdown
|
||||
let inner = inner2.clone();
|
||||
async move {
|
||||
let lock = inner.lock().await;
|
||||
if lock.config.display_enabled && !lock.config.builtin_anims_enabled {
|
||||
if shutting_down {
|
||||
CtrlAnime::run_thread(inner.clone(), lock.cache.shutdown.clone(), true)
|
||||
.await;
|
||||
} else {
|
||||
CtrlAnime::run_thread(inner.clone(), lock.cache.boot.clone(), true)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
move |lid_closed| {
|
||||
let inner = inner3.clone();
|
||||
// on lid change
|
||||
async move {
|
||||
let lock = inner.lock().await;
|
||||
if lock.config.off_when_lid_closed {
|
||||
if lock.config.builtin_anims_enabled {
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_enable_powersave_anim(!lid_closed))
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::off_when_suspended {}", err);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_enable_display(!lid_closed))
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
},
|
||||
move |power_plugged| {
|
||||
let inner = inner4.clone();
|
||||
// on power change
|
||||
async move {
|
||||
let lock = inner.lock().await;
|
||||
if lock.config.off_when_unplugged {
|
||||
if lock.config.builtin_anims_enabled {
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_enable_powersave_anim(power_plugged))
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::off_when_suspended {}", err);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_enable_display(power_plugged))
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::off_when_unplugged {}", err);
|
||||
})
|
||||
.ok();
|
||||
} else {
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_brightness(lock.config.brightness_on_battery))
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::off_when_unplugged {}", err);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl crate::Reloadable for CtrlAnimeZbus {
|
||||
async fn reload(&mut self) -> Result<(), RogError> {
|
||||
if let Some(lock) = self.0.try_lock() {
|
||||
let anim = &lock.config.builtin_anims;
|
||||
// Set builtins
|
||||
if lock.config.builtin_anims_enabled {
|
||||
lock.node.write_bytes(&pkt_set_builtin_animations(
|
||||
anim.boot,
|
||||
anim.awake,
|
||||
anim.sleep,
|
||||
anim.shutdown,
|
||||
))?;
|
||||
}
|
||||
// Builtins enabled or na?
|
||||
lock.node.set_builtins_enabled(
|
||||
lock.config.builtin_anims_enabled,
|
||||
lock.config.display_brightness,
|
||||
)?;
|
||||
|
||||
let manager = get_logind_manager().await;
|
||||
let lid_closed = manager.lid_closed().await.unwrap_or_default();
|
||||
let power_plugged = manager.on_external_power().await.unwrap_or_default();
|
||||
|
||||
let turn_off = (lid_closed && lock.config.off_when_lid_closed)
|
||||
|| (!power_plugged && lock.config.off_when_unplugged);
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_enable_display(!turn_off))
|
||||
.map_err(|err| {
|
||||
warn!("create_sys_event_tasks::reload {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
if turn_off || !lock.config.display_enabled {
|
||||
lock.node.write_bytes(&pkt_set_enable_display(false))?;
|
||||
// early return so we don't run animation thread
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !lock.config.builtin_anims_enabled && !lock.cache.boot.is_empty() {
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_enable_powersave_anim(false))
|
||||
.ok();
|
||||
|
||||
let action = lock.cache.boot.clone();
|
||||
CtrlAnime::run_thread(self.0.clone(), action, true).await;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,388 +0,0 @@
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
|
||||
use config_traits::{StdConfig, StdConfigLoad};
|
||||
use log::{debug, warn};
|
||||
use rog_aura::aura_detection::{LaptopLedData, ASUS_KEYBOARD_DEVICES};
|
||||
use rog_aura::power::AuraPower;
|
||||
use rog_aura::usb::{AuraDevRog1, AuraDevTuf, AuraDevice, AuraPowerDev};
|
||||
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Direction, LedBrightness, Speed, GRADIENT};
|
||||
use rog_platform::hid_raw::HidRaw;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
const CONFIG_FILE: &str = "aura.ron";
|
||||
|
||||
/// Enable/disable LED control in various states such as
|
||||
/// when the device is awake, suspended, shutting down or
|
||||
/// booting.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum AuraPowerConfig {
|
||||
AuraDevTuf(HashSet<AuraDevTuf>),
|
||||
AuraDevRog1(HashSet<AuraDevRog1>),
|
||||
AuraDevRog2(AuraPower),
|
||||
}
|
||||
|
||||
impl AuraPowerConfig {
|
||||
/// Invalid for TUF laptops
|
||||
pub fn to_bytes(control: &Self) -> [u8; 4] {
|
||||
match control {
|
||||
AuraPowerConfig::AuraDevTuf(_) => [0, 0, 0, 0],
|
||||
AuraPowerConfig::AuraDevRog1(c) => {
|
||||
let c: Vec<AuraDevRog1> = c.iter().copied().collect();
|
||||
AuraDevRog1::to_bytes(&c)
|
||||
}
|
||||
AuraPowerConfig::AuraDevRog2(c) => c.to_bytes(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_tuf_bool_array(control: &Self) -> Option<[bool; 5]> {
|
||||
if let Self::AuraDevTuf(c) = control {
|
||||
return Some([
|
||||
true,
|
||||
c.contains(&AuraDevTuf::Boot),
|
||||
c.contains(&AuraDevTuf::Awake),
|
||||
c.contains(&AuraDevTuf::Sleep),
|
||||
c.contains(&AuraDevTuf::Keyboard),
|
||||
]);
|
||||
}
|
||||
|
||||
if let Self::AuraDevRog1(c) = control {
|
||||
return Some([
|
||||
true,
|
||||
c.contains(&AuraDevRog1::Boot),
|
||||
c.contains(&AuraDevRog1::Awake),
|
||||
c.contains(&AuraDevRog1::Sleep),
|
||||
c.contains(&AuraDevRog1::Keyboard),
|
||||
]);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn set_tuf(&mut self, power: AuraDevTuf, on: bool) {
|
||||
if let Self::AuraDevTuf(p) = self {
|
||||
if on {
|
||||
p.insert(power);
|
||||
} else {
|
||||
p.remove(&power);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_0x1866(&mut self, power: AuraDevRog1, on: bool) {
|
||||
if let Self::AuraDevRog1(p) = self {
|
||||
if on {
|
||||
p.insert(power);
|
||||
} else {
|
||||
p.remove(&power);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_0x19b6(&mut self, power: AuraPower) {
|
||||
if let Self::AuraDevRog2(p) = self {
|
||||
*p = power;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&AuraPowerConfig> for AuraPowerDev {
|
||||
fn from(config: &AuraPowerConfig) -> Self {
|
||||
match config {
|
||||
AuraPowerConfig::AuraDevTuf(d) => AuraPowerDev {
|
||||
tuf: d.iter().copied().collect(),
|
||||
..Default::default()
|
||||
},
|
||||
AuraPowerConfig::AuraDevRog1(d) => AuraPowerDev {
|
||||
old_rog: d.iter().copied().collect(),
|
||||
..Default::default()
|
||||
},
|
||||
AuraPowerConfig::AuraDevRog2(d) => AuraPowerDev {
|
||||
rog: d.clone(),
|
||||
..Default::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
// #[serde(default)]
|
||||
pub struct AuraConfig {
|
||||
pub brightness: LedBrightness,
|
||||
pub current_mode: AuraModeNum,
|
||||
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
|
||||
pub multizone: Option<BTreeMap<AuraModeNum, Vec<AuraEffect>>>,
|
||||
pub multizone_on: bool,
|
||||
pub enabled: AuraPowerConfig,
|
||||
}
|
||||
|
||||
impl StdConfig for AuraConfig {
|
||||
/// Detect the keyboard type and load from default DB if data available
|
||||
fn new() -> Self {
|
||||
warn!("AuraConfig: creating new config");
|
||||
let mut prod_id = AuraDevice::Unknown;
|
||||
for prod in ASUS_KEYBOARD_DEVICES {
|
||||
if HidRaw::new(prod.into()).is_ok() {
|
||||
prod_id = prod;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Self::from_default_support(prod_id, &LaptopLedData::get_data())
|
||||
}
|
||||
|
||||
fn config_dir() -> std::path::PathBuf {
|
||||
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
|
||||
}
|
||||
|
||||
fn file_name(&self) -> String {
|
||||
CONFIG_FILE.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfigLoad for AuraConfig {}
|
||||
|
||||
impl AuraConfig {
|
||||
pub fn from_default_support(prod_id: AuraDevice, support_data: &LaptopLedData) -> Self {
|
||||
// create a default config here
|
||||
let enabled = if prod_id.is_new_style() {
|
||||
AuraPowerConfig::AuraDevRog2(AuraPower::new_all_on())
|
||||
} else if prod_id.is_tuf_style() {
|
||||
AuraPowerConfig::AuraDevTuf(HashSet::from([
|
||||
AuraDevTuf::Awake,
|
||||
AuraDevTuf::Boot,
|
||||
AuraDevTuf::Sleep,
|
||||
AuraDevTuf::Keyboard,
|
||||
]))
|
||||
} else {
|
||||
AuraPowerConfig::AuraDevRog1(HashSet::from([
|
||||
AuraDevRog1::Awake,
|
||||
AuraDevRog1::Boot,
|
||||
AuraDevRog1::Sleep,
|
||||
AuraDevRog1::Keyboard,
|
||||
AuraDevRog1::Lightbar,
|
||||
]))
|
||||
};
|
||||
let mut config = AuraConfig {
|
||||
brightness: LedBrightness::Med,
|
||||
current_mode: AuraModeNum::Static,
|
||||
builtins: BTreeMap::new(),
|
||||
multizone: None,
|
||||
multizone_on: false,
|
||||
enabled,
|
||||
};
|
||||
|
||||
for n in &support_data.basic_modes {
|
||||
debug!("AuraConfig: creating default for {n}");
|
||||
config
|
||||
.builtins
|
||||
.insert(*n, AuraEffect::default_with_mode(*n));
|
||||
|
||||
if !support_data.basic_zones.is_empty() {
|
||||
let mut default = vec![];
|
||||
for (i, tmp) in support_data.basic_zones.iter().enumerate() {
|
||||
default.push(AuraEffect {
|
||||
mode: *n,
|
||||
zone: *tmp,
|
||||
colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]),
|
||||
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
|
||||
speed: Speed::Med,
|
||||
direction: Direction::Left,
|
||||
});
|
||||
}
|
||||
if let Some(m) = config.multizone.as_mut() {
|
||||
m.insert(*n, default);
|
||||
} else {
|
||||
let mut tmp = BTreeMap::new();
|
||||
tmp.insert(*n, default);
|
||||
config.multizone = Some(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
config
|
||||
}
|
||||
|
||||
/// Set the mode data, current mode, and if multizone enabled.
|
||||
///
|
||||
/// Multipurpose, will accept `AuraEffect` with zones and put in the correct
|
||||
/// store.
|
||||
pub fn set_builtin(&mut self, effect: AuraEffect) {
|
||||
self.current_mode = effect.mode;
|
||||
if effect.zone() == AuraZone::None {
|
||||
self.builtins.insert(*effect.mode(), effect);
|
||||
self.multizone_on = false;
|
||||
} else {
|
||||
if let Some(multi) = self.multizone.as_mut() {
|
||||
if let Some(fx) = multi.get_mut(effect.mode()) {
|
||||
for fx in fx.iter_mut() {
|
||||
if fx.zone == effect.zone {
|
||||
*fx = effect;
|
||||
return;
|
||||
}
|
||||
}
|
||||
fx.push(effect);
|
||||
} else {
|
||||
multi.insert(*effect.mode(), vec![effect]);
|
||||
}
|
||||
} else {
|
||||
let mut tmp = BTreeMap::new();
|
||||
tmp.insert(*effect.mode(), vec![effect]);
|
||||
self.multizone = Some(tmp);
|
||||
}
|
||||
self.multizone_on = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_multizone(&self, aura_type: AuraModeNum) -> Option<&[AuraEffect]> {
|
||||
if let Some(multi) = &self.multizone {
|
||||
return multi.get(&aura_type).map(|v| v.as_slice());
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rog_aura::aura_detection::LaptopLedData;
|
||||
use rog_aura::usb::AuraDevice;
|
||||
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour};
|
||||
|
||||
use super::AuraConfig;
|
||||
|
||||
#[test]
|
||||
fn set_multizone_4key_config() {
|
||||
let mut config =
|
||||
AuraConfig::from_default_support(AuraDevice::X19b6, &LaptopLedData::default());
|
||||
|
||||
let effect = AuraEffect {
|
||||
colour1: Colour {
|
||||
r: 0xff,
|
||||
g: 0x00,
|
||||
b: 0xff,
|
||||
},
|
||||
zone: AuraZone::Key1,
|
||||
..Default::default()
|
||||
};
|
||||
config.set_builtin(effect);
|
||||
|
||||
assert!(config.multizone.is_some());
|
||||
|
||||
let effect = AuraEffect {
|
||||
colour1: Colour {
|
||||
r: 0x00,
|
||||
g: 0xff,
|
||||
b: 0xff,
|
||||
},
|
||||
zone: AuraZone::Key2,
|
||||
..Default::default()
|
||||
};
|
||||
config.set_builtin(effect);
|
||||
|
||||
let effect = AuraEffect {
|
||||
colour1: Colour {
|
||||
r: 0xff,
|
||||
g: 0xff,
|
||||
b: 0x00,
|
||||
},
|
||||
zone: AuraZone::Key3,
|
||||
..Default::default()
|
||||
};
|
||||
config.set_builtin(effect);
|
||||
|
||||
let effect = AuraEffect {
|
||||
colour1: Colour {
|
||||
r: 0x00,
|
||||
g: 0xff,
|
||||
b: 0x00,
|
||||
},
|
||||
zone: AuraZone::Key4,
|
||||
..Default::default()
|
||||
};
|
||||
let effect_clone = effect.clone();
|
||||
config.set_builtin(effect);
|
||||
// This should replace existing
|
||||
config.set_builtin(effect_clone);
|
||||
|
||||
let res = config.multizone.unwrap();
|
||||
let sta = res.get(&AuraModeNum::Static).unwrap();
|
||||
assert_eq!(sta.len(), 4);
|
||||
assert_eq!(
|
||||
sta[0].colour1,
|
||||
Colour {
|
||||
r: 0xff,
|
||||
g: 0x00,
|
||||
b: 0xff
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
sta[1].colour1,
|
||||
Colour {
|
||||
r: 0x00,
|
||||
g: 0xff,
|
||||
b: 0xff
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
sta[2].colour1,
|
||||
Colour {
|
||||
r: 0xff,
|
||||
g: 0xff,
|
||||
b: 0x00
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
sta[3].colour1,
|
||||
Colour {
|
||||
r: 0x00,
|
||||
g: 0xff,
|
||||
b: 0x00
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_multizone_multimode_config() {
|
||||
let mut config =
|
||||
AuraConfig::from_default_support(AuraDevice::X19b6, &LaptopLedData::default());
|
||||
|
||||
let effect = AuraEffect {
|
||||
zone: AuraZone::Key1,
|
||||
..Default::default()
|
||||
};
|
||||
config.set_builtin(effect);
|
||||
|
||||
assert!(config.multizone.is_some());
|
||||
|
||||
let effect = AuraEffect {
|
||||
zone: AuraZone::Key2,
|
||||
mode: AuraModeNum::Breathe,
|
||||
..Default::default()
|
||||
};
|
||||
config.set_builtin(effect);
|
||||
|
||||
let effect = AuraEffect {
|
||||
zone: AuraZone::Key3,
|
||||
mode: AuraModeNum::Comet,
|
||||
..Default::default()
|
||||
};
|
||||
config.set_builtin(effect);
|
||||
|
||||
let effect = AuraEffect {
|
||||
zone: AuraZone::Key4,
|
||||
mode: AuraModeNum::Pulse,
|
||||
..Default::default()
|
||||
};
|
||||
config.set_builtin(effect);
|
||||
|
||||
let res = config.multizone.unwrap();
|
||||
let sta = res.get(&AuraModeNum::Static).unwrap();
|
||||
assert_eq!(sta.len(), 1);
|
||||
|
||||
let sta = res.get(&AuraModeNum::Breathe).unwrap();
|
||||
assert_eq!(sta.len(), 1);
|
||||
|
||||
let sta = res.get(&AuraModeNum::Comet).unwrap();
|
||||
assert_eq!(sta.len(), 1);
|
||||
|
||||
let sta = res.get(&AuraModeNum::Pulse).unwrap();
|
||||
assert_eq!(sta.len(), 1);
|
||||
}
|
||||
}
|
||||
@@ -1,363 +0,0 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use config_traits::{StdConfig, StdConfigLoad};
|
||||
use dmi_id::DMIID;
|
||||
use log::{info, warn};
|
||||
use rog_aura::advanced::{LedUsbPackets, UsbPackets};
|
||||
use rog_aura::aura_detection::{LaptopLedData, ASUS_KEYBOARD_DEVICES};
|
||||
use rog_aura::usb::{AuraDevice, LED_APPLY, LED_SET};
|
||||
use rog_aura::{AuraEffect, Direction, LedBrightness, Speed, GRADIENT, LED_MSG_LEN};
|
||||
use rog_platform::hid_raw::HidRaw;
|
||||
use rog_platform::keyboard_led::KeyboardLed;
|
||||
|
||||
use super::config::{AuraConfig, AuraPowerConfig};
|
||||
use crate::error::RogError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LEDNode {
|
||||
KbdLed(KeyboardLed),
|
||||
Rog(HidRaw),
|
||||
None,
|
||||
}
|
||||
|
||||
pub struct CtrlKbdLed {
|
||||
// TODO: config stores the keyboard type as an AuraPower, use or update this
|
||||
pub led_prod: AuraDevice,
|
||||
pub led_node: LEDNode,
|
||||
pub sysfs_node: KeyboardLed,
|
||||
pub supported_data: LaptopLedData,
|
||||
pub per_key_mode_active: bool,
|
||||
pub config: AuraConfig,
|
||||
}
|
||||
|
||||
impl CtrlKbdLed {
|
||||
pub fn new(supported_basic_modes: LaptopLedData) -> Result<Self, RogError> {
|
||||
let mut led_prod = AuraDevice::Unknown;
|
||||
let mut usb_node = None;
|
||||
for prod in ASUS_KEYBOARD_DEVICES {
|
||||
match HidRaw::new(prod.into()) {
|
||||
Ok(node) => {
|
||||
led_prod = prod;
|
||||
usb_node = Some(node);
|
||||
info!(
|
||||
"Looked for keyboard controller 0x{}: Found",
|
||||
<&str>::from(prod)
|
||||
);
|
||||
break;
|
||||
}
|
||||
Err(err) => info!(
|
||||
"Looked for keyboard controller 0x{}: {err}",
|
||||
<&str>::from(prod)
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
let rgb_led = KeyboardLed::new()?;
|
||||
|
||||
if usb_node.is_none() && !rgb_led.has_kbd_rgb_mode() {
|
||||
let dmi = DMIID::new().unwrap_or_default();
|
||||
if dmi.dmi_family.contains("TUF") {
|
||||
warn!(
|
||||
"kbd_rgb_mode was not found in the /sys/. You require a minimum 6.1 kernel \
|
||||
and a supported TUF laptop"
|
||||
);
|
||||
}
|
||||
return Err(RogError::NoAuraKeyboard);
|
||||
}
|
||||
|
||||
let led_node = if let Some(rog) = usb_node {
|
||||
info!("Found ROG USB keyboard");
|
||||
LEDNode::Rog(rog)
|
||||
} else if rgb_led.has_kbd_rgb_mode() {
|
||||
info!("Found TUF keyboard");
|
||||
LEDNode::KbdLed(rgb_led.clone())
|
||||
} else {
|
||||
LEDNode::None
|
||||
};
|
||||
|
||||
// New loads data fromt he DB also
|
||||
let mut config_init = AuraConfig::new();
|
||||
let mut config_loaded = config_init.clone().load();
|
||||
// update the initialised data with what we loaded from disk
|
||||
for mode in &mut config_init.builtins {
|
||||
// update init values from loaded values if they exist
|
||||
if let Some(loaded) = config_loaded.builtins.get(mode.0) {
|
||||
*mode.1 = loaded.clone();
|
||||
}
|
||||
}
|
||||
// Then replace just incase the initialised data contains new modes added
|
||||
config_loaded.builtins = config_init.builtins;
|
||||
|
||||
if let (Some(mut multizone_init), Some(multizone_loaded)) =
|
||||
(config_init.multizone, config_loaded.multizone.as_mut())
|
||||
{
|
||||
for mode in multizone_init.iter_mut() {
|
||||
// update init values from loaded values if they exist
|
||||
if let Some(loaded) = multizone_loaded.get(mode.0) {
|
||||
let mut new_set = Vec::new();
|
||||
// only reuse a zone mode if the mode is supported
|
||||
for mode in loaded {
|
||||
if supported_basic_modes.basic_modes.contains(&mode.mode) {
|
||||
new_set.push(mode.clone());
|
||||
}
|
||||
}
|
||||
*mode.1 = new_set;
|
||||
}
|
||||
}
|
||||
*multizone_loaded = multizone_init;
|
||||
}
|
||||
|
||||
let ctrl = CtrlKbdLed {
|
||||
led_prod,
|
||||
led_node, // on TUF this is the same as rgb_led / kd_brightness
|
||||
sysfs_node: rgb_led, // If was none then we already returned above
|
||||
supported_data: supported_basic_modes,
|
||||
per_key_mode_active: false,
|
||||
config: config_loaded,
|
||||
};
|
||||
Ok(ctrl)
|
||||
}
|
||||
|
||||
/// Set combination state for boot animation/sleep animation/all leds/keys
|
||||
/// leds/side leds LED active
|
||||
pub(super) fn set_power_states(&mut self) -> Result<(), RogError> {
|
||||
if let LEDNode::KbdLed(platform) = &mut self.led_node {
|
||||
if let Some(pwr) = AuraPowerConfig::to_tuf_bool_array(&self.config.enabled) {
|
||||
let buf = [1, pwr[1] as u8, pwr[2] as u8, pwr[3] as u8, pwr[4] as u8];
|
||||
platform.set_kbd_rgb_state(&buf)?;
|
||||
}
|
||||
} else if let LEDNode::Rog(hid_raw) = &self.led_node {
|
||||
let bytes = AuraPowerConfig::to_bytes(&self.config.enabled);
|
||||
let message = [0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2], bytes[3]];
|
||||
|
||||
hid_raw.write_bytes(&message)?;
|
||||
hid_raw.write_bytes(&LED_SET)?;
|
||||
// Changes won't persist unless apply is set
|
||||
hid_raw.write_bytes(&LED_APPLY)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write an effect block. This is for per-key, but can be repurposed to
|
||||
/// write the raw factory mode packets - when doing this it is expected that
|
||||
/// only the first `Vec` (`effect[0]`) is valid.
|
||||
pub fn write_effect_block(&mut self, effect: &UsbPackets) -> Result<(), RogError> {
|
||||
if self.config.brightness == LedBrightness::Off {
|
||||
self.config.brightness = LedBrightness::Med;
|
||||
self.config.write();
|
||||
}
|
||||
|
||||
let pkt_type = effect[0][1];
|
||||
const PER_KEY_TYPE: u8 = 0xbc;
|
||||
|
||||
if pkt_type != PER_KEY_TYPE {
|
||||
self.per_key_mode_active = false;
|
||||
if let LEDNode::Rog(hid_raw) = &self.led_node {
|
||||
hid_raw.write_bytes(&effect[0])?;
|
||||
hid_raw.write_bytes(&LED_SET)?;
|
||||
// hid_raw.write_bytes(&LED_APPLY)?;
|
||||
}
|
||||
} else {
|
||||
if !self.per_key_mode_active {
|
||||
if let LEDNode::Rog(hid_raw) = &self.led_node {
|
||||
let init = LedUsbPackets::get_init_msg();
|
||||
hid_raw.write_bytes(&init)?;
|
||||
}
|
||||
self.per_key_mode_active = true;
|
||||
}
|
||||
if let LEDNode::Rog(hid_raw) = &self.led_node {
|
||||
for row in effect.iter() {
|
||||
hid_raw.write_bytes(row)?;
|
||||
}
|
||||
} else if let LEDNode::KbdLed(tuf) = &self.led_node {
|
||||
for row in effect.iter() {
|
||||
let r = row[9];
|
||||
let g = row[10];
|
||||
let b = row[11];
|
||||
tuf.set_kbd_rgb_mode(&[0, 0, r, g, b, 0])?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_mode(&mut self, mode: &AuraEffect) -> Result<(), RogError> {
|
||||
if let LEDNode::KbdLed(platform) = &self.led_node {
|
||||
let buf = [
|
||||
1,
|
||||
mode.mode as u8,
|
||||
mode.colour1.r,
|
||||
mode.colour1.g,
|
||||
mode.colour1.b,
|
||||
mode.speed as u8,
|
||||
];
|
||||
platform.set_kbd_rgb_mode(&buf)?;
|
||||
} else if let LEDNode::Rog(hid_raw) = &self.led_node {
|
||||
let bytes: [u8; LED_MSG_LEN] = mode.into();
|
||||
hid_raw.write_bytes(&bytes)?;
|
||||
hid_raw.write_bytes(&LED_SET)?;
|
||||
// Changes won't persist unless apply is set
|
||||
hid_raw.write_bytes(&LED_APPLY)?;
|
||||
} else {
|
||||
return Err(RogError::NoAuraKeyboard);
|
||||
}
|
||||
self.per_key_mode_active = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn write_current_config_mode(&mut self) -> Result<(), RogError> {
|
||||
if self.config.multizone_on {
|
||||
let mode = self.config.current_mode;
|
||||
let mut create = false;
|
||||
// There is no multizone config for this mode so create one here
|
||||
// using the colours of rainbow if it exists, or first available
|
||||
// mode, or random
|
||||
if self.config.multizone.is_none() {
|
||||
create = true;
|
||||
} else if let Some(multizones) = self.config.multizone.as_ref() {
|
||||
if !multizones.contains_key(&mode) {
|
||||
create = true;
|
||||
}
|
||||
}
|
||||
if create {
|
||||
info!("No user-set config for zone founding, attempting a default");
|
||||
self.create_multizone_default()?;
|
||||
}
|
||||
|
||||
if let Some(multizones) = self.config.multizone.as_mut() {
|
||||
if let Some(set) = multizones.get(&mode) {
|
||||
for mode in set.clone() {
|
||||
self.write_mode(&mode)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let mode = self.config.current_mode;
|
||||
if let Some(effect) = self.config.builtins.get(&mode).cloned() {
|
||||
self.write_mode(&effect)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a default for the `current_mode` if multizone and no config
|
||||
/// exists.
|
||||
fn create_multizone_default(&mut self) -> Result<(), RogError> {
|
||||
let mut default = vec![];
|
||||
for (i, tmp) in self.supported_data.basic_zones.iter().enumerate() {
|
||||
default.push(AuraEffect {
|
||||
mode: self.config.current_mode,
|
||||
zone: *tmp,
|
||||
colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]),
|
||||
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
|
||||
speed: Speed::Med,
|
||||
direction: Direction::Left,
|
||||
});
|
||||
}
|
||||
if default.is_empty() {
|
||||
return Err(RogError::AuraEffectNotSupported);
|
||||
}
|
||||
|
||||
if let Some(multizones) = self.config.multizone.as_mut() {
|
||||
multizones.insert(self.config.current_mode, default);
|
||||
} else {
|
||||
let mut tmp = BTreeMap::new();
|
||||
tmp.insert(self.config.current_mode, default);
|
||||
self.config.multizone = Some(tmp);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rog_aura::aura_detection::{LaptopLedData, PowerZones};
|
||||
use rog_aura::usb::AuraDevice;
|
||||
use rog_aura::{AuraModeNum, AuraZone};
|
||||
use rog_platform::keyboard_led::KeyboardLed;
|
||||
|
||||
use super::CtrlKbdLed;
|
||||
use crate::ctrl_aura::config::AuraConfig;
|
||||
use crate::ctrl_aura::controller::LEDNode;
|
||||
|
||||
#[test]
|
||||
fn create_multizone_if_no_config() {
|
||||
// Checking to ensure set_mode errors when unsupported modes are tried
|
||||
let config = AuraConfig::from_default_support(AuraDevice::X19b6, &LaptopLedData::default());
|
||||
let supported_basic_modes = LaptopLedData {
|
||||
board_name: String::new(),
|
||||
layout_name: "ga401".to_owned(),
|
||||
basic_modes: vec![AuraModeNum::Static],
|
||||
basic_zones: vec![],
|
||||
advanced_type: rog_aura::AdvancedAuraType::None,
|
||||
power_zones: vec![PowerZones::Keyboard, PowerZones::RearGlow],
|
||||
};
|
||||
let mut controller = CtrlKbdLed {
|
||||
led_prod: AuraDevice::X19b6,
|
||||
led_node: LEDNode::None,
|
||||
sysfs_node: KeyboardLed::default(),
|
||||
supported_data: supported_basic_modes,
|
||||
per_key_mode_active: false,
|
||||
config,
|
||||
};
|
||||
|
||||
assert!(controller.config.multizone.is_none());
|
||||
assert!(controller.create_multizone_default().is_err());
|
||||
assert!(controller.config.multizone.is_none());
|
||||
|
||||
controller.supported_data.basic_zones.push(AuraZone::Key1);
|
||||
controller.supported_data.basic_zones.push(AuraZone::Key2);
|
||||
assert!(controller.create_multizone_default().is_ok());
|
||||
assert!(controller.config.multizone.is_some());
|
||||
|
||||
let m = controller.config.multizone.unwrap();
|
||||
assert!(m.contains_key(&AuraModeNum::Static));
|
||||
let e = m.get(&AuraModeNum::Static).unwrap();
|
||||
assert_eq!(e.len(), 2);
|
||||
assert_eq!(e[0].zone, AuraZone::Key1);
|
||||
assert_eq!(e[1].zone, AuraZone::Key2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn next_mode_create_multizone_if_no_config() {
|
||||
// Checking to ensure set_mode errors when unsupported modes are tried
|
||||
let config = AuraConfig::from_default_support(AuraDevice::X19b6, &LaptopLedData::default());
|
||||
let supported_basic_modes = LaptopLedData {
|
||||
board_name: String::new(),
|
||||
layout_name: "ga401".to_owned(),
|
||||
basic_modes: vec![AuraModeNum::Static],
|
||||
basic_zones: vec![AuraZone::Key1, AuraZone::Key2],
|
||||
advanced_type: rog_aura::AdvancedAuraType::None,
|
||||
power_zones: vec![PowerZones::Keyboard, PowerZones::RearGlow],
|
||||
};
|
||||
let mut controller = CtrlKbdLed {
|
||||
led_prod: AuraDevice::X19b6,
|
||||
led_node: LEDNode::None,
|
||||
sysfs_node: KeyboardLed::default(),
|
||||
supported_data: supported_basic_modes,
|
||||
per_key_mode_active: false,
|
||||
config,
|
||||
};
|
||||
|
||||
assert!(controller.config.multizone.is_none());
|
||||
controller.config.multizone_on = true;
|
||||
// This is called in toggle_mode. It will error here because we have no
|
||||
// keyboard node in tests.
|
||||
assert_eq!(
|
||||
controller
|
||||
.write_current_config_mode()
|
||||
.unwrap_err()
|
||||
.to_string(),
|
||||
"No supported Aura keyboard"
|
||||
);
|
||||
assert!(controller.config.multizone.is_some());
|
||||
|
||||
let m = controller.config.multizone.unwrap();
|
||||
assert!(m.contains_key(&AuraModeNum::Static));
|
||||
let e = m.get(&AuraModeNum::Static).unwrap();
|
||||
assert_eq!(e.len(), 2);
|
||||
assert_eq!(e[0].zone, AuraZone::Key1);
|
||||
assert_eq!(e[1].zone, AuraZone::Key2);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
pub mod config;
|
||||
pub mod controller;
|
||||
/// Implements `CtrlTask`, `Reloadable`, `ZbusRun`
|
||||
pub mod trait_impls;
|
||||
@@ -1,291 +0,0 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use config_traits::StdConfig;
|
||||
use log::{debug, error, info, warn};
|
||||
use rog_aura::advanced::UsbPackets;
|
||||
use rog_aura::aura_detection::PowerZones;
|
||||
use rog_aura::usb::{AuraDevice, AuraPowerDev};
|
||||
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, LedBrightness};
|
||||
use zbus::export::futures_util::lock::{Mutex, MutexGuard};
|
||||
use zbus::export::futures_util::StreamExt;
|
||||
use zbus::fdo::Error as ZbErr;
|
||||
use zbus::{dbus_interface, Connection, SignalContext};
|
||||
|
||||
use super::controller::CtrlKbdLed;
|
||||
use crate::error::RogError;
|
||||
use crate::CtrlTask;
|
||||
|
||||
pub const AURA_ZBUS_NAME: &str = "Aura";
|
||||
pub const AURA_ZBUS_PATH: &str = "/org/asuslinux/Aura";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CtrlAuraZbus(pub Arc<Mutex<CtrlKbdLed>>);
|
||||
|
||||
impl CtrlAuraZbus {
|
||||
fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> {
|
||||
let bright = lock.sysfs_node.get_brightness()?;
|
||||
lock.config.read();
|
||||
lock.config.brightness = bright.into();
|
||||
lock.config.write();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl crate::ZbusRun for CtrlAuraZbus {
|
||||
async fn add_to_server(self, server: &mut Connection) {
|
||||
Self::add_to_server_helper(self, AURA_ZBUS_PATH, server).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// The main interface for changing, reading, or notfying signals
|
||||
///
|
||||
/// LED commands are split between Brightness, Modes, Per-Key
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
impl CtrlAuraZbus {
|
||||
/// Return the device type for this Aura keyboard
|
||||
#[dbus_interface(property)]
|
||||
async fn device_type(&self) -> AuraDevice {
|
||||
let ctrl = self.0.lock().await;
|
||||
ctrl.led_prod
|
||||
}
|
||||
|
||||
/// Return the current LED brightness
|
||||
#[dbus_interface(property)]
|
||||
async fn brightness(&self) -> Result<LedBrightness, ZbErr> {
|
||||
let ctrl = self.0.lock().await;
|
||||
Ok(ctrl.sysfs_node.get_brightness().map(|n| n.into())?)
|
||||
}
|
||||
|
||||
/// Set the keyboard brightness level (0-3)
|
||||
#[dbus_interface(property)]
|
||||
async fn set_brightness(&mut self, brightness: LedBrightness) -> Result<(), ZbErr> {
|
||||
let ctrl = self.0.lock().await;
|
||||
Ok(ctrl.sysfs_node.set_brightness(brightness.into())?)
|
||||
}
|
||||
|
||||
/// Total levels of brightness available
|
||||
#[dbus_interface(property)]
|
||||
async fn supported_brightness(&self) -> Vec<LedBrightness> {
|
||||
vec![
|
||||
LedBrightness::Off,
|
||||
LedBrightness::Low,
|
||||
LedBrightness::Med,
|
||||
LedBrightness::High,
|
||||
]
|
||||
}
|
||||
|
||||
/// The total available modes
|
||||
#[dbus_interface(property)]
|
||||
async fn supported_basic_modes(&self) -> Result<Vec<AuraModeNum>, ZbErr> {
|
||||
let ctrl = self.0.lock().await;
|
||||
Ok(ctrl.config.builtins.keys().cloned().collect())
|
||||
}
|
||||
|
||||
#[dbus_interface(property)]
|
||||
async fn supported_basic_zones(&self) -> Result<Vec<AuraZone>, ZbErr> {
|
||||
let ctrl = self.0.lock().await;
|
||||
Ok(ctrl.supported_data.basic_zones.clone())
|
||||
}
|
||||
|
||||
#[dbus_interface(property)]
|
||||
async fn supported_power_zones(&self) -> Result<Vec<PowerZones>, ZbErr> {
|
||||
let ctrl = self.0.lock().await;
|
||||
Ok(ctrl.supported_data.power_zones.clone())
|
||||
}
|
||||
|
||||
/// The current mode data
|
||||
#[dbus_interface(property)]
|
||||
async fn led_mode(&self) -> Result<AuraModeNum, ZbErr> {
|
||||
let ctrl = self.0.lock().await;
|
||||
Ok(ctrl.config.current_mode)
|
||||
}
|
||||
|
||||
/// Set an Aura effect if the effect mode or zone is supported.
|
||||
///
|
||||
/// On success the aura config file is read to refresh cached values, then
|
||||
/// the effect is stored and config written to disk.
|
||||
#[dbus_interface(property)]
|
||||
async fn set_led_mode(&mut self, num: AuraModeNum) -> Result<(), ZbErr> {
|
||||
let mut ctrl = self.0.lock().await;
|
||||
ctrl.config.current_mode = num;
|
||||
ctrl.write_current_config_mode()?;
|
||||
if ctrl.config.brightness == LedBrightness::Off {
|
||||
ctrl.config.brightness = LedBrightness::Med;
|
||||
}
|
||||
ctrl.sysfs_node
|
||||
.set_brightness(ctrl.config.brightness.into())?;
|
||||
ctrl.config.write();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The current mode data
|
||||
#[dbus_interface(property)]
|
||||
async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> {
|
||||
let ctrl = self.0.lock().await;
|
||||
let mode = ctrl.config.current_mode;
|
||||
match ctrl.config.builtins.get(&mode) {
|
||||
Some(effect) => Ok(effect.clone()),
|
||||
None => Err(ZbErr::Failed("Could not get the current effect".into())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set an Aura effect if the effect mode or zone is supported.
|
||||
///
|
||||
/// On success the aura config file is read to refresh cached values, then
|
||||
/// the effect is stored and config written to disk.
|
||||
#[dbus_interface(property)]
|
||||
async fn set_led_mode_data(&mut self, effect: AuraEffect) -> Result<(), ZbErr> {
|
||||
let mut ctrl = self.0.lock().await;
|
||||
if !ctrl.supported_data.basic_modes.contains(&effect.mode)
|
||||
|| effect.zone != AuraZone::None
|
||||
&& !ctrl.supported_data.basic_zones.contains(&effect.zone)
|
||||
{
|
||||
return Err(ZbErr::NotSupported(format!(
|
||||
"The Aura effect is not supported: {effect:?}"
|
||||
)));
|
||||
}
|
||||
|
||||
ctrl.write_mode(&effect)?;
|
||||
if ctrl.config.brightness == LedBrightness::Off {
|
||||
ctrl.config.brightness = LedBrightness::Med;
|
||||
}
|
||||
ctrl.sysfs_node
|
||||
.set_brightness(ctrl.config.brightness.into())?;
|
||||
ctrl.config.set_builtin(effect);
|
||||
ctrl.config.write();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the data set for every mode available
|
||||
async fn all_mode_data(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
|
||||
let ctrl = self.0.lock().await;
|
||||
ctrl.config.builtins.clone()
|
||||
}
|
||||
|
||||
// As property doesn't work for AuraPowerDev (complexity of serialization?)
|
||||
#[dbus_interface(property)]
|
||||
async fn led_power(&self) -> AuraPowerDev {
|
||||
let ctrl = self.0.lock().await;
|
||||
AuraPowerDev::from(&ctrl.config.enabled)
|
||||
}
|
||||
|
||||
/// Set a variety of states, input is array of enum.
|
||||
/// `enabled` sets if the sent array should be disabled or enabled
|
||||
///
|
||||
/// For Modern ROG devices the "enabled" flag is ignored.
|
||||
#[dbus_interface(property)]
|
||||
async fn set_led_power(&mut self, options: (AuraPowerDev, bool)) -> Result<(), ZbErr> {
|
||||
let enabled = options.1;
|
||||
let options = options.0;
|
||||
let mut ctrl = self.0.lock().await;
|
||||
for p in options.tuf {
|
||||
ctrl.config.enabled.set_tuf(p, enabled);
|
||||
}
|
||||
for p in options.old_rog {
|
||||
ctrl.config.enabled.set_0x1866(p, enabled);
|
||||
}
|
||||
ctrl.config.enabled.set_0x19b6(options.rog);
|
||||
ctrl.config.write();
|
||||
Ok(ctrl.set_power_states().map_err(|e| {
|
||||
warn!("{}", e);
|
||||
e
|
||||
})?)
|
||||
}
|
||||
|
||||
/// On machine that have some form of either per-key keyboard or per-zone
|
||||
/// this can be used to write custom effects over dbus. The input is a
|
||||
/// nested `Vec<Vec<8>>` where `Vec<u8>` is a raw USB packet
|
||||
async fn direct_addressing_raw(&self, data: UsbPackets) -> Result<(), ZbErr> {
|
||||
let mut ctrl = self.0.lock().await;
|
||||
ctrl.write_effect_block(&data)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CtrlTask for CtrlAuraZbus {
|
||||
fn zbus_path() -> &'static str {
|
||||
AURA_ZBUS_PATH
|
||||
}
|
||||
|
||||
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
|
||||
let load_save = |start: bool, mut lock: MutexGuard<'_, CtrlKbdLed>| {
|
||||
// If waking up
|
||||
if !start {
|
||||
info!("CtrlKbdLedTask reloading brightness and modes");
|
||||
lock.sysfs_node
|
||||
.set_brightness(lock.config.brightness.into())
|
||||
.map_err(|e| error!("CtrlKbdLedTask: {e}"))
|
||||
.ok();
|
||||
lock.write_current_config_mode()
|
||||
.map_err(|e| error!("CtrlKbdLedTask: {e}"))
|
||||
.ok();
|
||||
} else if start {
|
||||
Self::update_config(&mut lock)
|
||||
.map_err(|e| error!("CtrlKbdLedTask: {e}"))
|
||||
.ok();
|
||||
}
|
||||
};
|
||||
|
||||
let inner1 = self.0.clone();
|
||||
let inner3 = self.0.clone();
|
||||
self.create_sys_event_tasks(
|
||||
move |sleeping| {
|
||||
let inner1 = inner1.clone();
|
||||
async move {
|
||||
let lock = inner1.lock().await;
|
||||
load_save(sleeping, lock);
|
||||
}
|
||||
},
|
||||
move |_shutting_down| {
|
||||
let inner3 = inner3.clone();
|
||||
async move {
|
||||
let lock = inner3.lock().await;
|
||||
load_save(false, lock);
|
||||
}
|
||||
},
|
||||
move |_lid_closed| {
|
||||
// on lid change
|
||||
async move {}
|
||||
},
|
||||
move |_power_plugged| {
|
||||
// power change
|
||||
async move {}
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
let ctrl2 = self.0.clone();
|
||||
let ctrl = self.0.lock().await;
|
||||
let watch = ctrl.sysfs_node.monitor_brightness()?;
|
||||
tokio::spawn(async move {
|
||||
let mut buffer = [0; 32];
|
||||
watch
|
||||
.into_event_stream(&mut buffer)
|
||||
.unwrap()
|
||||
.for_each(|_| async {
|
||||
if let Some(lock) = ctrl2.try_lock() {
|
||||
load_save(true, lock);
|
||||
}
|
||||
})
|
||||
.await;
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl crate::Reloadable for CtrlAuraZbus {
|
||||
async fn reload(&mut self) -> Result<(), RogError> {
|
||||
let mut ctrl = self.0.lock().await;
|
||||
debug!("CtrlKbdLedZbus: reloading keyboard mode");
|
||||
ctrl.write_current_config_mode()?;
|
||||
debug!("CtrlKbdLedZbus: reloading power states");
|
||||
ctrl.set_power_states().map_err(|err| warn!("{err}")).ok();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,29 @@
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use config_traits::{StdConfig, StdConfigLoad};
|
||||
use futures_lite::StreamExt;
|
||||
use futures_util::lock::Mutex;
|
||||
use log::{debug, error, info, warn};
|
||||
use rog_platform::platform::{PlatformPolicy, RogPlatform};
|
||||
use rog_platform::platform::{PlatformProfile, RogPlatform};
|
||||
use rog_profiles::error::ProfileError;
|
||||
use rog_profiles::fan_curve_set::CurveData;
|
||||
use rog_profiles::{find_fan_curve_node, FanCurvePU, FanCurveProfiles};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use tokio::sync::Mutex;
|
||||
use zbus::{dbus_interface, Connection, SignalContext};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use zbus::object_server::SignalEmitter;
|
||||
use zbus::{interface, Connection};
|
||||
|
||||
use crate::error::RogError;
|
||||
use crate::{CtrlTask, CONFIG_PATH_BASE};
|
||||
|
||||
const MOD_NAME: &str = "FanCurveZbus";
|
||||
pub const FAN_CURVE_ZBUS_NAME: &str = "FanCurves";
|
||||
pub const FAN_CURVE_ZBUS_PATH: &str = "/org/asuslinux/FanCurves";
|
||||
pub const FAN_CURVE_ZBUS_PATH: &str = "/xyz/ljones";
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Default)]
|
||||
pub struct FanCurveConfig {
|
||||
pub balanced: Vec<CurveData>,
|
||||
pub performance: Vec<CurveData>,
|
||||
pub quiet: Vec<CurveData>,
|
||||
pub profiles: FanCurveProfiles,
|
||||
#[serde(skip)]
|
||||
pub current: u8,
|
||||
pub current: PlatformProfile
|
||||
}
|
||||
|
||||
impl StdConfig for FanCurveConfig {
|
||||
@@ -50,98 +47,79 @@ impl StdConfigLoad for FanCurveConfig {}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CtrlFanCurveZbus {
|
||||
config: Arc<Mutex<FanCurveConfig>>,
|
||||
fan_curves: Arc<Mutex<FanCurveProfiles>>,
|
||||
platform: RogPlatform,
|
||||
platform: RogPlatform
|
||||
}
|
||||
|
||||
// Non-zbus-derive impl
|
||||
impl CtrlFanCurveZbus {
|
||||
pub fn new() -> Result<Self, RogError> {
|
||||
let platform = RogPlatform::new()?;
|
||||
if platform.has_throttle_thermal_policy() {
|
||||
info!("{MOD_NAME}: Device has profile control available");
|
||||
if platform.has_platform_profile() {
|
||||
info!("Device has profile control available");
|
||||
find_fan_curve_node()?;
|
||||
info!("{MOD_NAME}: Device has fan curves available");
|
||||
let mut config = FanCurveConfig::new();
|
||||
info!("Device has fan curves available");
|
||||
let mut config = FanCurveConfig::new().load();
|
||||
let mut fan_curves = FanCurveProfiles::default();
|
||||
|
||||
// Only do defaults if the config doesn't already exist
|
||||
if !config.file_path().exists() {
|
||||
info!("{MOD_NAME}: Fetching default fan curves");
|
||||
// Only do defaults if the config doesn't already exist\
|
||||
if config.profiles.balanced.is_empty() || !config.file_path().exists() {
|
||||
info!("Fetching default fan curves");
|
||||
|
||||
let current = platform.get_platform_profile()?;
|
||||
for this in [
|
||||
PlatformPolicy::Balanced,
|
||||
PlatformPolicy::Performance,
|
||||
PlatformPolicy::Quiet,
|
||||
PlatformProfile::Balanced,
|
||||
PlatformProfile::Performance,
|
||||
PlatformProfile::Quiet
|
||||
] {
|
||||
// For each profile we need to switch to it before we
|
||||
// can read the existing values from hardware. The ACPI method used
|
||||
// for this is what limits us.
|
||||
let next = PlatformPolicy::get_next_profile(this);
|
||||
platform.set_throttle_thermal_policy(next.into())?;
|
||||
platform.set_platform_profile(this.into())?;
|
||||
let mut dev = find_fan_curve_node()?;
|
||||
fan_curves.set_active_curve_to_defaults(this, &mut dev)?;
|
||||
|
||||
let active = platform
|
||||
.get_throttle_thermal_policy()
|
||||
.map_or(PlatformPolicy::Balanced, |t| t.into());
|
||||
|
||||
info!("{MOD_NAME}: {active:?}:");
|
||||
for curve in fan_curves.get_fan_curves_for(active) {
|
||||
info!("{this:?}:");
|
||||
for curve in fan_curves.get_fan_curves_for(this) {
|
||||
info!("{}", String::from(curve));
|
||||
}
|
||||
}
|
||||
platform.set_platform_profile(current.as_str())?;
|
||||
config.profiles = fan_curves;
|
||||
config.write();
|
||||
} else {
|
||||
info!("{MOD_NAME}: Fan curves previously stored, loading...");
|
||||
info!("Fan curves previously stored, loading...");
|
||||
config = config.load();
|
||||
fan_curves.balanced = config.balanced.clone();
|
||||
fan_curves.performance = config.performance.clone();
|
||||
fan_curves.quiet = config.quiet.clone();
|
||||
}
|
||||
|
||||
return Ok(Self {
|
||||
config: Arc::new(Mutex::new(config)),
|
||||
fan_curves: Arc::new(Mutex::new(fan_curves)),
|
||||
platform,
|
||||
platform
|
||||
});
|
||||
}
|
||||
|
||||
Err(ProfileError::NotSupported.into())
|
||||
}
|
||||
|
||||
pub async fn update_profiles_from_config(&self) {
|
||||
self.fan_curves.lock().await.balanced = self.config.lock().await.balanced.clone();
|
||||
self.fan_curves.lock().await.performance = self.config.lock().await.performance.clone();
|
||||
self.fan_curves.lock().await.quiet = self.config.lock().await.quiet.clone();
|
||||
}
|
||||
|
||||
/// Because this locks both config and fan_curves, it means nothing else can
|
||||
/// hold a lock across this function call. Stupid choice to do this and
|
||||
/// needs to be fixed.
|
||||
pub async fn update_config_from_profiles(&self) {
|
||||
self.config.lock().await.balanced = self.fan_curves.lock().await.balanced.clone();
|
||||
self.config.lock().await.performance = self.fan_curves.lock().await.performance.clone();
|
||||
self.config.lock().await.quiet = self.fan_curves.lock().await.quiet.clone();
|
||||
}
|
||||
}
|
||||
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
#[interface(name = "xyz.ljones.FanCurves")]
|
||||
impl CtrlFanCurveZbus {
|
||||
/// Set all fan curves for a profile to enabled status. Will also activate a
|
||||
/// fan curve if in the same profile mode
|
||||
async fn set_fan_curves_enabled(
|
||||
&mut self,
|
||||
profile: PlatformPolicy,
|
||||
enabled: bool,
|
||||
profile: PlatformProfile,
|
||||
enabled: bool
|
||||
) -> zbus::fdo::Result<()> {
|
||||
self.fan_curves
|
||||
self.config
|
||||
.lock()
|
||||
.await
|
||||
.profiles
|
||||
.set_profile_curves_enabled(profile, enabled);
|
||||
self.fan_curves
|
||||
self.config
|
||||
.lock()
|
||||
.await
|
||||
.profiles
|
||||
.write_profile_curve_to_platform(profile, &mut find_fan_curve_node()?)?;
|
||||
self.update_config_from_profiles().await;
|
||||
self.config.lock().await.write();
|
||||
Ok(())
|
||||
}
|
||||
@@ -150,32 +128,34 @@ impl CtrlFanCurveZbus {
|
||||
/// activate a fan curve if in the same profile mode
|
||||
async fn set_profile_fan_curve_enabled(
|
||||
&mut self,
|
||||
profile: PlatformPolicy,
|
||||
profile: PlatformProfile,
|
||||
fan: FanCurvePU,
|
||||
enabled: bool,
|
||||
enabled: bool
|
||||
) -> zbus::fdo::Result<()> {
|
||||
self.fan_curves
|
||||
self.config
|
||||
.lock()
|
||||
.await
|
||||
.profiles
|
||||
.set_profile_fan_curve_enabled(profile, fan, enabled);
|
||||
self.fan_curves
|
||||
self.config
|
||||
.lock()
|
||||
.await
|
||||
.profiles
|
||||
.write_profile_curve_to_platform(profile, &mut find_fan_curve_node()?)?;
|
||||
self.update_config_from_profiles().await;
|
||||
self.config.lock().await.write();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the fan-curve data for the currently active PlatformPolicy
|
||||
/// Get the fan-curve data for the currently active ThrottlePolicy
|
||||
async fn fan_curve_data(
|
||||
&mut self,
|
||||
profile: PlatformPolicy,
|
||||
profile: PlatformProfile
|
||||
) -> zbus::fdo::Result<Vec<CurveData>> {
|
||||
let curve = self
|
||||
.fan_curves
|
||||
.config
|
||||
.lock()
|
||||
.await
|
||||
.profiles
|
||||
.get_fan_curves_for(profile)
|
||||
.to_vec();
|
||||
Ok(curve)
|
||||
@@ -185,18 +165,40 @@ impl CtrlFanCurveZbus {
|
||||
/// Will also activate the fan curve if the user is in the same mode.
|
||||
async fn set_fan_curve(
|
||||
&mut self,
|
||||
profile: PlatformPolicy,
|
||||
curve: CurveData,
|
||||
profile: PlatformProfile,
|
||||
curve: CurveData
|
||||
) -> zbus::fdo::Result<()> {
|
||||
self.fan_curves
|
||||
self.config
|
||||
.lock()
|
||||
.await
|
||||
.profiles
|
||||
.save_fan_curve(curve, profile)?;
|
||||
self.fan_curves
|
||||
let active: PlatformProfile = self.platform.get_platform_profile()?.into();
|
||||
if active == profile {
|
||||
self.config
|
||||
.lock()
|
||||
.await
|
||||
.profiles
|
||||
.write_profile_curve_to_platform(profile, &mut find_fan_curve_node()?)?;
|
||||
}
|
||||
self.config.lock().await.write();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reset the stored (self) and device curves to the defaults of the
|
||||
/// platform.
|
||||
///
|
||||
/// Each platform_profile has a different default and the default can be
|
||||
/// read only for the currently active profile.
|
||||
async fn set_curves_to_defaults(&mut self, profile: PlatformProfile) -> zbus::fdo::Result<()> {
|
||||
let active = self.platform.get_platform_profile()?;
|
||||
self.platform.set_platform_profile(profile.into())?;
|
||||
self.config
|
||||
.lock()
|
||||
.await
|
||||
.write_profile_curve_to_platform(profile, &mut find_fan_curve_node()?)?;
|
||||
self.update_config_from_profiles().await;
|
||||
.profiles
|
||||
.set_active_curve_to_defaults(profile, &mut find_fan_curve_node()?)?;
|
||||
self.platform.set_platform_profile(active.as_str())?;
|
||||
self.config.lock().await.write();
|
||||
Ok(())
|
||||
}
|
||||
@@ -206,85 +208,67 @@ impl CtrlFanCurveZbus {
|
||||
///
|
||||
/// Each platform_profile has a different default and the defualt can be
|
||||
/// read only for the currently active profile.
|
||||
async fn set_active_curve_to_defaults(&mut self) -> zbus::fdo::Result<()> {
|
||||
let active = self.platform.get_throttle_thermal_policy()?;
|
||||
self.fan_curves
|
||||
async fn reset_profile_curves(&self, profile: PlatformProfile) -> zbus::fdo::Result<()> {
|
||||
let active = self.platform.get_platform_profile()?;
|
||||
|
||||
self.platform.set_platform_profile(profile.into())?;
|
||||
self.config
|
||||
.lock()
|
||||
.await
|
||||
.set_active_curve_to_defaults(active.into(), &mut find_fan_curve_node()?)?;
|
||||
self.update_config_from_profiles().await;
|
||||
self.config.lock().await.write();
|
||||
Ok(())
|
||||
}
|
||||
.profiles
|
||||
.set_active_curve_to_defaults((&active).into(), &mut find_fan_curve_node()?)?;
|
||||
self.platform.set_platform_profile(active.as_str())?;
|
||||
|
||||
/// 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.
|
||||
async fn reset_profile_curves(&self, profile: PlatformPolicy) -> zbus::fdo::Result<()> {
|
||||
let active = self
|
||||
.platform
|
||||
.get_throttle_thermal_policy()
|
||||
.unwrap_or(PlatformPolicy::Balanced.into());
|
||||
|
||||
self.platform.set_throttle_thermal_policy(profile.into())?;
|
||||
self.fan_curves
|
||||
.lock()
|
||||
.await
|
||||
.set_active_curve_to_defaults(active.into(), &mut find_fan_curve_node()?)?;
|
||||
self.platform.set_throttle_thermal_policy(active)?;
|
||||
|
||||
self.update_config_from_profiles().await;
|
||||
self.config.lock().await.write();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl crate::ZbusRun for CtrlFanCurveZbus {
|
||||
async fn add_to_server(self, server: &mut Connection) {
|
||||
Self::add_to_server_helper(self, FAN_CURVE_ZBUS_PATH, server).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CtrlTask for CtrlFanCurveZbus {
|
||||
fn zbus_path() -> &'static str {
|
||||
FAN_CURVE_ZBUS_PATH
|
||||
}
|
||||
|
||||
async fn create_tasks(&self, _signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
|
||||
let watch_throttle_thermal_policy = self.platform.monitor_throttle_thermal_policy()?;
|
||||
async fn create_tasks(&self, _signal_ctxt: SignalEmitter<'static>) -> Result<(), RogError> {
|
||||
let watch_platform_profile = self.platform.monitor_platform_profile()?;
|
||||
let platform = self.platform.clone();
|
||||
let config = self.config.clone();
|
||||
let fan_curves = self.fan_curves.clone();
|
||||
let fan_curves = self.config.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut buffer = [0; 32];
|
||||
if let Ok(mut stream) = watch_throttle_thermal_policy.into_event_stream(&mut buffer) {
|
||||
if let Ok(mut stream) = watch_platform_profile.into_event_stream(&mut buffer) {
|
||||
while (stream.next().await).is_some() {
|
||||
debug!("watch_throttle_thermal_policy changed");
|
||||
if let Ok(profile) = platform.get_throttle_thermal_policy().map_err(|e| {
|
||||
error!("{MOD_NAME}: get_throttle_thermal_policy error: {e}");
|
||||
}) {
|
||||
debug!("watch_platform_profile changed");
|
||||
if let Ok(profile) =
|
||||
platform
|
||||
.get_platform_profile()
|
||||
.map(|p| p.into())
|
||||
.map_err(|e| {
|
||||
error!("get_platform_profile error: {e}");
|
||||
})
|
||||
{
|
||||
if profile != config.lock().await.current {
|
||||
fan_curves
|
||||
.lock()
|
||||
.await
|
||||
.profiles
|
||||
.write_profile_curve_to_platform(
|
||||
profile.into(),
|
||||
&mut find_fan_curve_node().unwrap(),
|
||||
profile,
|
||||
&mut find_fan_curve_node().unwrap()
|
||||
)
|
||||
.map_err(|e| {
|
||||
warn!("{MOD_NAME}: write_profile_curve_to_platform, {}", e)
|
||||
})
|
||||
.map_err(|e| warn!("write_profile_curve_to_platform, {}", e))
|
||||
.ok();
|
||||
config.lock().await.current = profile;
|
||||
}
|
||||
}
|
||||
}
|
||||
dbg!("STREAM ENDED");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -292,20 +276,17 @@ impl CtrlTask for CtrlFanCurveZbus {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl crate::Reloadable for CtrlFanCurveZbus {
|
||||
/// Fetch the active profile and use that to set all related components up
|
||||
async fn reload(&mut self) -> Result<(), RogError> {
|
||||
// let active = self.platform.get_throttle_thermal_policy()?.into();
|
||||
// if let Ok(mut device) = find_fan_curve_node() {
|
||||
// // 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
|
||||
// self.fan_curves
|
||||
// .lock()
|
||||
// .await
|
||||
// .write_profile_curve_to_platform(active, &mut device)?;
|
||||
// }
|
||||
let active = self.platform.get_platform_profile()?.into();
|
||||
let mut config = self.config.lock().await;
|
||||
if let Ok(mut device) = find_fan_curve_node() {
|
||||
config
|
||||
.profiles
|
||||
.write_profile_curve_to_platform(active, &mut device)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,38 +1,36 @@
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::io::Write;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use ::zbus::export::futures_util::lock::Mutex;
|
||||
use ::zbus::Connection;
|
||||
use asusd::asus_armoury::start_attributes_zbus;
|
||||
use asusd::aura_manager::DeviceManager;
|
||||
use asusd::config::Config;
|
||||
use asusd::ctrl_anime::config::AnimeConfig;
|
||||
use asusd::ctrl_anime::trait_impls::CtrlAnimeZbus;
|
||||
use asusd::ctrl_anime::CtrlAnime;
|
||||
use asusd::ctrl_aura::controller::CtrlKbdLed;
|
||||
use asusd::ctrl_aura::trait_impls::CtrlAuraZbus;
|
||||
use asusd::ctrl_fancurves::CtrlFanCurveZbus;
|
||||
use asusd::ctrl_platform::CtrlPlatform;
|
||||
use asusd::{print_board_info, CtrlTask, Reloadable, ZbusRun, DBUS_NAME};
|
||||
use config_traits::{StdConfig, StdConfigLoad2};
|
||||
use log::{error, info, warn};
|
||||
use rog_aura::aura_detection::LaptopLedData;
|
||||
use tokio::time::sleep;
|
||||
use zbus::SignalContext;
|
||||
use asusd::{print_board_info, start_tasks, CtrlTask, DBUS_NAME};
|
||||
use config_traits::{StdConfig, StdConfigLoad1};
|
||||
use futures_util::lock::Mutex;
|
||||
use log::{error, info};
|
||||
use rog_platform::asus_armoury::FirmwareAttributes;
|
||||
use rog_platform::platform::RogPlatform;
|
||||
use rog_platform::power::AsusPower;
|
||||
use zbus::fdo::ObjectManager;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// console_subscriber::init();
|
||||
let mut logger = env_logger::Builder::new();
|
||||
logger
|
||||
.parse_default_env()
|
||||
.target(env_logger::Target::Stdout)
|
||||
.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()))
|
||||
.format_timestamp(None)
|
||||
.filter_level(log::LevelFilter::Debug)
|
||||
.init();
|
||||
|
||||
let is_service = match env::var_os("IS_SERVICE") {
|
||||
Some(val) => val == "1",
|
||||
None => false,
|
||||
None => true
|
||||
};
|
||||
|
||||
if !is_service {
|
||||
@@ -46,6 +44,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
info!(" daemon v{}", asusd::VERSION);
|
||||
info!(" rog-anime v{}", rog_anime::VERSION);
|
||||
info!(" rog-slash v{}", rog_slash::VERSION);
|
||||
info!(" rog-aura v{}", rog_aura::VERSION);
|
||||
info!(" rog-profiles v{}", rog_profiles::VERSION);
|
||||
info!("rog-platform v{}", rog_platform::VERSION);
|
||||
@@ -61,82 +60,61 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
|
||||
// println!("{:?}", supported.supported_functions());
|
||||
|
||||
// Start zbus server
|
||||
let mut connection = Connection::system().await?;
|
||||
let mut server = Connection::system().await?;
|
||||
server.object_server().at("/", ObjectManager).await.unwrap();
|
||||
|
||||
let config = Config::new().load();
|
||||
let cfg_path = config.file_path();
|
||||
let config = Arc::new(Mutex::new(config));
|
||||
|
||||
// supported.add_to_server(&mut connection).await;
|
||||
|
||||
match CtrlPlatform::new(config.clone()) {
|
||||
Ok(ctrl) => {
|
||||
let sig_ctx = CtrlPlatform::signal_context(&connection)?;
|
||||
start_tasks(ctrl, &mut connection, sig_ctx).await?;
|
||||
}
|
||||
Err(err) => {
|
||||
error!("CtrlPlatform: {}", err);
|
||||
}
|
||||
}
|
||||
let platform = RogPlatform::new()?; // TODO: maybe needs async mutex?
|
||||
let power = AsusPower::new()?; // TODO: maybe needs async mutex?
|
||||
let attributes = FirmwareAttributes::new();
|
||||
start_attributes_zbus(
|
||||
&server,
|
||||
platform.clone(),
|
||||
power.clone(),
|
||||
attributes.clone(),
|
||||
config.clone()
|
||||
)
|
||||
.await?;
|
||||
|
||||
match CtrlFanCurveZbus::new() {
|
||||
Ok(ctrl) => {
|
||||
let sig_ctx = CtrlFanCurveZbus::signal_context(&connection)?;
|
||||
start_tasks(ctrl, &mut connection, sig_ctx).await?;
|
||||
let sig_ctx = CtrlFanCurveZbus::signal_context(&server)?;
|
||||
start_tasks(ctrl, &mut server, sig_ctx).await?;
|
||||
}
|
||||
Err(err) => {
|
||||
error!("FanCurves: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
match CtrlAnime::new(AnimeConfig::new().load()) {
|
||||
match CtrlPlatform::new(
|
||||
platform,
|
||||
power,
|
||||
attributes,
|
||||
config.clone(),
|
||||
&cfg_path,
|
||||
CtrlPlatform::signal_context(&server)?
|
||||
) {
|
||||
Ok(ctrl) => {
|
||||
let zbus = CtrlAnimeZbus(Arc::new(Mutex::new(ctrl)));
|
||||
let sig_ctx = CtrlAnimeZbus::signal_context(&connection)?;
|
||||
start_tasks(zbus, &mut connection, sig_ctx).await?;
|
||||
let sig_ctx = CtrlPlatform::signal_context(&server)?;
|
||||
start_tasks(ctrl, &mut server, sig_ctx).await?;
|
||||
}
|
||||
Err(err) => {
|
||||
info!("AniMe control: {}", err);
|
||||
error!("CtrlPlatform: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
let laptop = LaptopLedData::get_data();
|
||||
// CtrlKbdLed deviates from the config pattern above due to requiring a keyboard
|
||||
// detection first
|
||||
match CtrlKbdLed::new(laptop) {
|
||||
Ok(ctrl) => {
|
||||
let zbus = CtrlAuraZbus(Arc::new(Mutex::new(ctrl)));
|
||||
let sig_ctx = CtrlAuraZbus::signal_context(&connection)?;
|
||||
start_tasks(zbus, &mut connection, sig_ctx).await?;
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Keyboard control: {}", err);
|
||||
}
|
||||
}
|
||||
let _ = DeviceManager::new(server.clone()).await?;
|
||||
|
||||
// Request dbus name after finishing initalizing all functions
|
||||
connection.request_name(DBUS_NAME).await?;
|
||||
server.request_name(DBUS_NAME).await?;
|
||||
|
||||
info!("Startup success, begining dbus server loop");
|
||||
loop {
|
||||
// This is just a blocker to idle and ensure the reator reacts
|
||||
sleep(Duration::from_millis(1000)).await;
|
||||
server.executor().tick().await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn start_tasks<T>(
|
||||
mut zbus: T,
|
||||
connection: &mut Connection,
|
||||
signal_ctx: SignalContext<'static>,
|
||||
) -> Result<(), Box<dyn Error>>
|
||||
where
|
||||
T: ZbusRun + Reloadable + CtrlTask + Clone,
|
||||
{
|
||||
let task = zbus.clone();
|
||||
|
||||
zbus.reload()
|
||||
.await
|
||||
.unwrap_or_else(|err| warn!("Controller error: {}", err));
|
||||
zbus.add_to_server(connection).await;
|
||||
|
||||
task.create_tasks(signal_ctx).await.ok();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ use config_traits::ron;
|
||||
use rog_anime::error::AnimeError;
|
||||
use rog_platform::error::PlatformError;
|
||||
use rog_profiles::error::ProfileError;
|
||||
use rog_slash::error::SlashError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RogError {
|
||||
@@ -31,11 +32,12 @@ pub enum RogError {
|
||||
NoAuraKeyboard,
|
||||
NoAuraNode,
|
||||
Anime(AnimeError),
|
||||
Slash(SlashError),
|
||||
Platform(PlatformError),
|
||||
SystemdUnitAction(String),
|
||||
SystemdUnitWaitTimeout(String),
|
||||
Command(String, std::io::Error),
|
||||
ParseRon(ron::Error),
|
||||
ParseRon(ron::Error)
|
||||
}
|
||||
|
||||
impl fmt::Display for RogError {
|
||||
@@ -72,6 +74,7 @@ impl fmt::Display for RogError {
|
||||
RogError::NoAuraKeyboard => write!(f, "No supported Aura keyboard"),
|
||||
RogError::NoAuraNode => write!(f, "No Aura keyboard node found"),
|
||||
RogError::Anime(deets) => write!(f, "AniMe Matrix error: {}", deets),
|
||||
RogError::Slash(deets) => write!(f, "Slash error: {}", deets),
|
||||
RogError::Platform(deets) => write!(f, "Asus Platform error: {}", deets),
|
||||
RogError::SystemdUnitAction(action) => {
|
||||
write!(f, "systemd unit action {} failed", action)
|
||||
@@ -84,7 +87,7 @@ impl fmt::Display for RogError {
|
||||
)
|
||||
}
|
||||
RogError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error),
|
||||
RogError::ParseRon(error) => write!(f, "Parse config error: {}", error),
|
||||
RogError::ParseRon(error) => write!(f, "Parse config error: {}", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,6 +106,12 @@ impl From<AnimeError> for RogError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SlashError> for RogError {
|
||||
fn from(err: SlashError) -> Self {
|
||||
RogError::Slash(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PlatformError> for RogError {
|
||||
fn from(err: PlatformError) -> Self {
|
||||
RogError::Platform(err)
|
||||
@@ -133,3 +142,10 @@ impl From<RogError> for zbus::fdo::Error {
|
||||
zbus::fdo::Error::Failed(format!("{}", err))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RogError> for zbus::Error {
|
||||
#[inline]
|
||||
fn from(err: RogError) -> Self {
|
||||
zbus::Error::Failure(format!("{}", err))
|
||||
}
|
||||
}
|
||||
|
||||
254
asusd/src/lib.rs
254
asusd/src/lib.rs
@@ -1,35 +1,41 @@
|
||||
#![deny(unused_must_use)]
|
||||
/// Configuration loading, saving
|
||||
pub mod config;
|
||||
/// Control of anime matrix display
|
||||
pub mod ctrl_anime;
|
||||
/// Keyboard LED brightness control, RGB, and LED display modes
|
||||
pub mod ctrl_aura;
|
||||
/// Control platform profiles + fan-curves if available
|
||||
pub mod ctrl_fancurves;
|
||||
/// Control ASUS bios function such as boot sound, Optimus/Dedicated gfx mode
|
||||
pub mod ctrl_platform;
|
||||
|
||||
pub mod asus_armoury;
|
||||
pub mod aura_anime;
|
||||
pub mod aura_laptop;
|
||||
pub mod aura_manager;
|
||||
pub mod aura_scsi;
|
||||
pub mod aura_slash;
|
||||
pub mod aura_types;
|
||||
pub mod error;
|
||||
|
||||
use std::future::Future;
|
||||
use std::time::Duration;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use dmi_id::DMIID;
|
||||
use futures_lite::stream::StreamExt;
|
||||
use log::{debug, info, warn};
|
||||
use logind_zbus::manager::ManagerProxy;
|
||||
use tokio::time::sleep;
|
||||
use zbus::object_server::{Interface, SignalEmitter};
|
||||
use zbus::proxy::CacheProperties;
|
||||
use zbus::zvariant::ObjectPath;
|
||||
use zbus::{CacheProperties, Connection, SignalContext};
|
||||
use zbus::Connection;
|
||||
|
||||
use crate::error::RogError;
|
||||
|
||||
const CONFIG_PATH_BASE: &str = "/etc/asusd/";
|
||||
pub static DBUS_NAME: &str = "org.asuslinux.Daemon";
|
||||
pub static DBUS_PATH: &str = "/org/asuslinux/Daemon";
|
||||
pub static DBUS_IFACE: &str = "org.asuslinux.Daemon";
|
||||
pub const ASUS_ZBUS_PATH: &str = "/xyz/ljones";
|
||||
|
||||
pub static DBUS_NAME: &str = "xyz.ljones.Asusd";
|
||||
pub static DBUS_PATH: &str = "/xyz/ljones/Daemon";
|
||||
pub static DBUS_IFACE: &str = "xyz.ljones.Asusd";
|
||||
|
||||
/// This macro adds a function which spawns an `inotify` task on the passed in
|
||||
/// `Executor`.
|
||||
@@ -38,7 +44,7 @@ pub static DBUS_IFACE: &str = "org.asuslinux.Daemon";
|
||||
/// methods to be available:
|
||||
/// - `<name>() -> SomeValue`, functionally is a getter, but is allowed to have
|
||||
/// side effects.
|
||||
/// - `notify_<name>(SignalContext, SomeValue)`
|
||||
/// - `notify_<name>(SignalEmitter, SomeValue)`
|
||||
///
|
||||
/// In most cases if `SomeValue` is stored in a config then `<name>()` getter is
|
||||
/// expected to update it. The getter should *never* write back to the path or
|
||||
@@ -51,17 +57,17 @@ pub static DBUS_IFACE: &str = "org.asuslinux.Daemon";
|
||||
/// task_watch_item!(panel_od platform);
|
||||
/// task_watch_item!(gpu_mux_mode platform);
|
||||
/// }
|
||||
/// ```
|
||||
/// ```\
|
||||
/// // TODO: this is kind of useless if it can't trigger some action
|
||||
#[macro_export]
|
||||
macro_rules! task_watch_item {
|
||||
($name:ident $self_inner:ident) => {
|
||||
($name:ident $name_str:literal $self_inner:ident) => {
|
||||
concat_idents::concat_idents!(fn_name = watch_, $name {
|
||||
async fn fn_name(
|
||||
&self,
|
||||
signal_ctxt: SignalContext<'static>,
|
||||
signal_ctxt: SignalEmitter<'static>,
|
||||
) -> Result<(), RogError> {
|
||||
use zbus::export::futures_util::StreamExt;
|
||||
use futures_util::StreamExt;
|
||||
|
||||
let ctrl = self.clone();
|
||||
concat_idents::concat_idents!(watch_fn = monitor_, $name {
|
||||
@@ -71,12 +77,15 @@ macro_rules! task_watch_item {
|
||||
let mut buffer = [0; 32];
|
||||
watch.into_event_stream(&mut buffer).unwrap().for_each(|_| async {
|
||||
if let Ok(value) = ctrl.$name() { // get new value from zbus method
|
||||
concat_idents::concat_idents!(notif_fn = $name, _changed {
|
||||
ctrl.notif_fn(&signal_ctxt).await.ok();
|
||||
});
|
||||
let mut lock = ctrl.config.lock().await;
|
||||
lock.$name = value;
|
||||
lock.write();
|
||||
if ctrl.config.lock().await.$name != value {
|
||||
log::debug!("{} was changed to {} externally", $name_str, value);
|
||||
concat_idents::concat_idents!(notif_fn = $name, _changed {
|
||||
ctrl.notif_fn(&signal_ctxt).await.ok();
|
||||
});
|
||||
let mut lock = ctrl.config.lock().await;
|
||||
lock.$name = value;
|
||||
lock.write();
|
||||
}
|
||||
}
|
||||
}).await;
|
||||
});
|
||||
@@ -96,7 +105,7 @@ macro_rules! task_watch_item_notify {
|
||||
concat_idents::concat_idents!(fn_name = watch_, $name {
|
||||
async fn fn_name(
|
||||
&self,
|
||||
signal_ctxt: SignalContext<'static>,
|
||||
signal_ctxt: SignalEmitter<'static>,
|
||||
) -> Result<(), RogError> {
|
||||
use zbus::export::futures_util::StreamExt;
|
||||
|
||||
@@ -130,45 +139,57 @@ pub fn print_board_info() {
|
||||
info!("Board name: {}", dmi.board_name);
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait Reloadable {
|
||||
async fn reload(&mut self) -> Result<(), RogError>;
|
||||
fn reload(&mut self) -> impl Future<Output = Result<(), RogError>> + Send;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait ZbusRun {
|
||||
async fn add_to_server(self, server: &mut Connection);
|
||||
pub trait ReloadAndNotify {
|
||||
type Data: Send;
|
||||
|
||||
async fn add_to_server_helper(
|
||||
iface: impl zbus::Interface,
|
||||
fn reload_and_notify(
|
||||
&mut self,
|
||||
signal_context: &SignalEmitter<'static>,
|
||||
data: Self::Data
|
||||
) -> impl Future<Output = Result<(), RogError>> + Send;
|
||||
}
|
||||
|
||||
pub trait ZbusRun {
|
||||
fn add_to_server(self, server: &mut Connection) -> impl Future<Output = ()> + Send;
|
||||
|
||||
fn add_to_server_helper(
|
||||
iface: impl Interface,
|
||||
path: &str,
|
||||
server: &mut Connection,
|
||||
) {
|
||||
server
|
||||
.object_server()
|
||||
.at(&ObjectPath::from_str_unchecked(path), iface)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("{}: add_to_server {}", path, err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
server: &mut Connection
|
||||
) -> impl Future<Output = ()> + Send {
|
||||
async move {
|
||||
server
|
||||
.object_server()
|
||||
.at(&ObjectPath::from_str_unchecked(path), iface)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("{}: add_to_server {}", path, err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set up a task to run on the async executor
|
||||
#[async_trait]
|
||||
pub trait CtrlTask {
|
||||
fn zbus_path() -> &'static str;
|
||||
|
||||
fn signal_context(connection: &Connection) -> Result<SignalContext<'static>, zbus::Error> {
|
||||
SignalContext::new(connection, Self::zbus_path())
|
||||
fn signal_context(connection: &Connection) -> Result<SignalEmitter<'static>, zbus::Error> {
|
||||
SignalEmitter::new(connection, Self::zbus_path())
|
||||
}
|
||||
|
||||
/// Implement to set up various tasks that may be required, using the
|
||||
/// `Executor`. No blocking loops are allowed, or they must be run on a
|
||||
/// separate thread.
|
||||
async fn create_tasks(&self, signal: SignalContext<'static>) -> Result<(), RogError>;
|
||||
fn create_tasks(
|
||||
&self,
|
||||
signal: SignalEmitter<'static>
|
||||
) -> impl Future<Output = Result<(), RogError>> + Send;
|
||||
|
||||
// /// Create a timed repeating task
|
||||
// async fn repeating_task(&self, millis: u64, mut task: impl FnMut() + Send +
|
||||
@@ -186,95 +207,89 @@ pub trait CtrlTask {
|
||||
///
|
||||
/// The closures can potentially block, so execution time should be the
|
||||
/// minimal possible such as save a variable.
|
||||
async fn create_sys_event_tasks<
|
||||
Fut1,
|
||||
Fut2,
|
||||
Fut3,
|
||||
Fut4,
|
||||
F1: Send + 'static,
|
||||
F2: Send + 'static,
|
||||
F3: Send + 'static,
|
||||
F4: Send + 'static,
|
||||
>(
|
||||
fn create_sys_event_tasks<Fut1, Fut2, Fut3, Fut4, F1, F2, F3, F4>(
|
||||
&self,
|
||||
mut on_prepare_for_sleep: F1,
|
||||
mut on_prepare_for_shutdown: F2,
|
||||
mut on_lid_change: F3,
|
||||
mut on_external_power_change: F4,
|
||||
) where
|
||||
F1: FnMut(bool) -> Fut1,
|
||||
F2: FnMut(bool) -> Fut2,
|
||||
F3: FnMut(bool) -> Fut3,
|
||||
F4: FnMut(bool) -> Fut4,
|
||||
mut on_external_power_change: F4
|
||||
) -> impl Future<Output = ()> + Send
|
||||
where
|
||||
F1: FnMut(bool) -> Fut1 + Send + 'static,
|
||||
F2: FnMut(bool) -> Fut2 + Send + 'static,
|
||||
F3: FnMut(bool) -> Fut3 + Send + 'static,
|
||||
F4: FnMut(bool) -> Fut4 + Send + 'static,
|
||||
Fut1: Future<Output = ()> + Send,
|
||||
Fut2: Future<Output = ()> + Send,
|
||||
Fut3: Future<Output = ()> + Send,
|
||||
Fut4: Future<Output = ()> + Send,
|
||||
Fut4: Future<Output = ()> + Send
|
||||
{
|
||||
let connection = Connection::system()
|
||||
.await
|
||||
.expect("Controller could not create dbus connection");
|
||||
async {
|
||||
let connection = Connection::system()
|
||||
.await
|
||||
.expect("Controller could not create dbus connection");
|
||||
|
||||
let manager = ManagerProxy::builder(&connection)
|
||||
.cache_properties(CacheProperties::No)
|
||||
.build()
|
||||
.await
|
||||
.expect("Controller could not create ManagerProxy");
|
||||
let manager = ManagerProxy::builder(&connection)
|
||||
.cache_properties(CacheProperties::No)
|
||||
.build()
|
||||
.await
|
||||
.expect("Controller could not create ManagerProxy");
|
||||
|
||||
let manager1 = manager.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Ok(mut notif) = manager1.receive_prepare_for_shutdown().await {
|
||||
while let Some(event) = notif.next().await {
|
||||
// blocks thread :|
|
||||
if let Ok(args) = event.args() {
|
||||
debug!("Doing on_prepare_for_shutdown({})", args.start);
|
||||
on_prepare_for_shutdown(args.start).await;
|
||||
let manager1 = manager.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Ok(mut notif) = manager1.receive_prepare_for_shutdown().await {
|
||||
while let Some(event) = notif.next().await {
|
||||
// blocks thread :|
|
||||
if let Ok(args) = event.args() {
|
||||
debug!("Doing on_prepare_for_shutdown({})", args.start);
|
||||
on_prepare_for_shutdown(args.start).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let manager2 = manager.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Ok(mut notif) = manager2.receive_prepare_for_sleep().await {
|
||||
while let Some(event) = notif.next().await {
|
||||
// blocks thread :|
|
||||
if let Ok(args) = event.args() {
|
||||
debug!("Doing on_prepare_for_sleep({})", args.start);
|
||||
on_prepare_for_sleep(args.start).await;
|
||||
let manager2 = manager.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Ok(mut notif) = manager2.receive_prepare_for_sleep().await {
|
||||
while let Some(event) = notif.next().await {
|
||||
// blocks thread :|
|
||||
if let Ok(args) = event.args() {
|
||||
debug!("Doing on_prepare_for_sleep({})", args.start);
|
||||
on_prepare_for_sleep(args.start).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let manager3 = manager.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut last_power = manager3.on_external_power().await.unwrap_or_default();
|
||||
let manager3 = manager.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut last_power = manager3.on_external_power().await.unwrap_or_default();
|
||||
|
||||
loop {
|
||||
if let Ok(next) = manager3.on_external_power().await {
|
||||
if next != last_power {
|
||||
last_power = next;
|
||||
on_external_power_change(next).await;
|
||||
loop {
|
||||
if let Ok(next) = manager3.on_external_power().await {
|
||||
if next != last_power {
|
||||
last_power = next;
|
||||
on_external_power_change(next).await;
|
||||
}
|
||||
}
|
||||
sleep(Duration::from_secs(2)).await;
|
||||
}
|
||||
sleep(Duration::from_secs(2)).await;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut last_lid = manager.lid_closed().await.unwrap_or_default();
|
||||
// need to loop on these as they don't emit signals
|
||||
loop {
|
||||
if let Ok(next) = manager.lid_closed().await {
|
||||
if next != last_lid {
|
||||
last_lid = next;
|
||||
on_lid_change(next).await;
|
||||
tokio::spawn(async move {
|
||||
let mut last_lid = manager.lid_closed().await.unwrap_or_default();
|
||||
// need to loop on these as they don't emit signals
|
||||
loop {
|
||||
if let Ok(next) = manager.lid_closed().await {
|
||||
if next != last_lid {
|
||||
last_lid = next;
|
||||
on_lid_change(next).await;
|
||||
}
|
||||
}
|
||||
sleep(Duration::from_secs(2)).await;
|
||||
}
|
||||
sleep(Duration::from_secs(2)).await;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,3 +298,22 @@ pub trait GetSupported {
|
||||
|
||||
fn get_supported() -> Self::A;
|
||||
}
|
||||
|
||||
pub async fn start_tasks<T>(
|
||||
mut zbus: T,
|
||||
connection: &mut Connection,
|
||||
signal_ctx: SignalEmitter<'static>
|
||||
) -> Result<(), RogError>
|
||||
where
|
||||
T: ZbusRun + Reloadable + CtrlTask + Clone
|
||||
{
|
||||
let zbus_clone = zbus.clone();
|
||||
|
||||
zbus.reload()
|
||||
.await
|
||||
.unwrap_or_else(|err| warn!("Controller error: {}", err));
|
||||
zbus.add_to_server(connection).await;
|
||||
|
||||
zbus_clone.create_tasks(signal_ctx).await.ok();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.asuslinux.Daemon">
|
||||
<!--
|
||||
Writes a data stream of length. Will force system thread to exit until
|
||||
it is restarted
|
||||
-->
|
||||
<method name="Write">
|
||||
<arg name="input" type="(ays)" direction="in"/>
|
||||
</method>
|
||||
<!--
|
||||
The main loop is the base system set action if the user isn't running
|
||||
the user daemon
|
||||
-->
|
||||
<method name="RunMainLoop">
|
||||
<arg name="start" type="b" direction="in"/>
|
||||
</method>
|
||||
<!--
|
||||
Get the device state as stored by asusd
|
||||
-->
|
||||
<method name="DeviceState">
|
||||
<arg type="(bub(ssss)bbbu)" direction="out"/>
|
||||
</method>
|
||||
<!--
|
||||
Set base brightness level
|
||||
-->
|
||||
<!--
|
||||
Set base brightness level
|
||||
-->
|
||||
<property name="Brightness" type="u" access="readwrite"/>
|
||||
<!--
|
||||
Set which builtin animation is used for each stage
|
||||
-->
|
||||
<property name="BuiltinAnimations" type="(ssss)" access="readwrite"/>
|
||||
<!--
|
||||
Enable the builtin animations or not. This is quivalent to "Powersave
|
||||
animations" in Armory crate
|
||||
-->
|
||||
<property name="BuiltinsEnabled" type="b" access="readwrite"/>
|
||||
<!--
|
||||
Set whether the AniMe is enabled at all
|
||||
-->
|
||||
<property name="EnableDisplay" type="b" access="readwrite"/>
|
||||
<!--
|
||||
Set if to turn the AniMe Matrix off when the lid is closed
|
||||
-->
|
||||
<property name="OffWhenLidClosed" type="b" access="readwrite"/>
|
||||
<!--
|
||||
Set if to turn the AniMe Matrix off when the laptop is suspended
|
||||
-->
|
||||
<property name="OffWhenSuspended" type="b" access="readwrite"/>
|
||||
<!--
|
||||
Set if to turn the AniMe Matrix off when external power is unplugged
|
||||
-->
|
||||
<property name="OffWhenUnplugged" type="b" access="readwrite"/>
|
||||
</interface>
|
||||
</node>
|
||||
@@ -1,67 +0,0 @@
|
||||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.asuslinux.Daemon">
|
||||
<!--
|
||||
Get the data set for every mode available
|
||||
-->
|
||||
<method name="AllModeData">
|
||||
<arg type="a{u(uu(yyy)(yyy)ss)}" direction="out"/>
|
||||
</method>
|
||||
<!--
|
||||
On machine that have some form of either per-key keyboard or per-zone
|
||||
this can be used to write custom effects over dbus. The input is a
|
||||
nested `Vec<Vec<8>>` where `Vec<u8>` is a raw USB packet
|
||||
-->
|
||||
<method name="DirectAddressingRaw">
|
||||
<arg name="data" type="aay" direction="in"/>
|
||||
</method>
|
||||
<!--
|
||||
Return the current LED brightness
|
||||
-->
|
||||
<!--
|
||||
Set the keyboard brightness level (0-3)
|
||||
-->
|
||||
<property name="Brightness" type="u" access="readwrite"/>
|
||||
<!--
|
||||
Return the device type for this Aura keyboard
|
||||
-->
|
||||
<property name="DeviceType" type="s" access="read"/>
|
||||
<!--
|
||||
The current mode data
|
||||
-->
|
||||
<!--
|
||||
Set an Aura effect if the effect mode or zone is supported.
|
||||
|
||||
On success the aura config file is read to refresh cached values, then
|
||||
the effect is stored and config written to disk.
|
||||
-->
|
||||
<property name="LedMode" type="u" access="readwrite"/>
|
||||
<!--
|
||||
The current mode data
|
||||
-->
|
||||
<!--
|
||||
Set an Aura effect if the effect mode or zone is supported.
|
||||
|
||||
On success the aura config file is read to refresh cached values, then
|
||||
the effect is stored and config written to disk.
|
||||
-->
|
||||
<property name="LedModeData" type="(uu(yyy)(yyy)ss)" access="readwrite"/>
|
||||
<!--
|
||||
Set a variety of states, input is array of enum.
|
||||
`enabled` sets if the sent array should be disabled or enabled
|
||||
|
||||
For Modern ROG devices the "enabled" flag is ignored.
|
||||
-->
|
||||
<property name="LedPower" type="(ayay((ubbbb)(ubbbb)(ubbbb)(ubbbb)(ubbbb)))" access="readwrite"/>
|
||||
<!--
|
||||
The total available modes
|
||||
-->
|
||||
<property name="SupportedBasicModes" type="au" access="read"/>
|
||||
<property name="SupportedBasicZones" type="au" access="read"/>
|
||||
<!--
|
||||
Total levels of brightness available
|
||||
-->
|
||||
<property name="SupportedBrightness" type="au" access="read"/>
|
||||
<property name="SupportedPowerZones" type="au" access="read"/>
|
||||
</interface>
|
||||
</node>
|
||||
@@ -1,56 +0,0 @@
|
||||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.asuslinux.Daemon">
|
||||
<!--
|
||||
Set all fan curves for a profile to enabled status. Will also activate a
|
||||
fan curve if in the same profile mode
|
||||
-->
|
||||
<method name="SetFanCurvesEnabled">
|
||||
<arg name="profile" type="s" direction="in"/>
|
||||
<arg name="enabled" type="b" direction="in"/>
|
||||
</method>
|
||||
<!--
|
||||
Set a single fan curve for a profile to enabled status. Will also
|
||||
activate a fan curve if in the same profile mode
|
||||
-->
|
||||
<method name="SetProfileFanCurveEnabled">
|
||||
<arg name="profile" type="s" direction="in"/>
|
||||
<arg name="fan" type="s" direction="in"/>
|
||||
<arg name="enabled" type="b" direction="in"/>
|
||||
</method>
|
||||
<!--
|
||||
Get the fan-curve data for the currently active PlatformPolicy
|
||||
-->
|
||||
<method name="FanCurveData">
|
||||
<arg name="profile" type="s" direction="in"/>
|
||||
<arg type="a(s(yyyyyyyy)(yyyyyyyy)b)" direction="out"/>
|
||||
</method>
|
||||
<!--
|
||||
Set the fan curve for the specified profile.
|
||||
Will also activate the fan curve if the user is in the same mode.
|
||||
-->
|
||||
<method name="SetFanCurve">
|
||||
<arg name="profile" type="s" direction="in"/>
|
||||
<arg name="curve" type="(s(yyyyyyyy)(yyyyyyyy)b)" direction="in"/>
|
||||
</method>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<method name="SetActiveCurveToDefaults">
|
||||
</method>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<method name="ResetProfileCurves">
|
||||
<arg name="profile" type="s" direction="in"/>
|
||||
</method>
|
||||
</interface>
|
||||
</node>
|
||||
@@ -1,46 +0,0 @@
|
||||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.asuslinux.Daemon">
|
||||
<!--
|
||||
Returns a list of property names that this system supports
|
||||
-->
|
||||
<method name="SupportedProperties">
|
||||
<arg type="as" direction="out"/>
|
||||
</method>
|
||||
<method name="SupportedInterfaces">
|
||||
<arg type="as" direction="out"/>
|
||||
</method>
|
||||
<!--
|
||||
Toggle to next platform_profile. Names provided by `Profiles`.
|
||||
If fan-curves are supported will also activate a fan curve for profile.
|
||||
-->
|
||||
<method name="NextThrottleThermalPolicy">
|
||||
</method>
|
||||
<property name="ChargeControlEndThreshold" type="y" access="readwrite"/>
|
||||
<property name="DgpuDisable" type="b" access="read"/>
|
||||
<property name="EgpuEnable" type="b" access="read"/>
|
||||
<property name="GpuMuxMode" type="y" access="readwrite"/>
|
||||
<!--
|
||||
Get the `panel_od` value from platform. Updates the stored value in
|
||||
internal config also.
|
||||
-->
|
||||
<property name="MiniLedMode" type="b" access="readwrite"/>
|
||||
<property name="NvDynamicBoost" type="y" access="readwrite"/>
|
||||
<property name="NvTempTarget" type="y" access="readwrite"/>
|
||||
<!--
|
||||
Get the `panel_od` value from platform. Updates the stored value in
|
||||
internal config also.
|
||||
-->
|
||||
<property name="PanelOd" type="b" access="readwrite"/>
|
||||
<property name="PostAnimationSound" type="b" access="readwrite"/>
|
||||
<property name="PptApuSppt" type="y" access="readwrite"/>
|
||||
<property name="PptFppt" type="y" access="readwrite"/>
|
||||
<!--
|
||||
************************************************************************
|
||||
-->
|
||||
<property name="PptPl1Spl" type="y" access="readwrite"/>
|
||||
<property name="PptPl2Sppt" type="y" access="readwrite"/>
|
||||
<property name="PptPlatformSppt" type="y" access="readwrite"/>
|
||||
<property name="ThrottleThermalPolicy" type="s" access="readwrite"/>
|
||||
</interface>
|
||||
</node>
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
Generated by typeshare 1.7.0
|
||||
*/
|
||||
|
||||
export enum AnimBooting {
|
||||
GlitchConstruction = "GlitchConstruction",
|
||||
StaticEmergence = "StaticEmergence",
|
||||
}
|
||||
|
||||
export enum AnimAwake {
|
||||
BinaryBannerScroll = "BinaryBannerScroll",
|
||||
RogLogoGlitch = "RogLogoGlitch",
|
||||
}
|
||||
|
||||
export enum AnimSleeping {
|
||||
BannerSwipe = "BannerSwipe",
|
||||
Starfield = "Starfield",
|
||||
}
|
||||
|
||||
export enum AnimShutdown {
|
||||
GlitchOut = "GlitchOut",
|
||||
SeeYa = "SeeYa",
|
||||
}
|
||||
|
||||
export interface Animations {
|
||||
boot: AnimBooting;
|
||||
awake: AnimAwake;
|
||||
sleep: AnimSleeping;
|
||||
shutdown: AnimShutdown;
|
||||
}
|
||||
|
||||
/** Base LED brightness of the display */
|
||||
export enum Brightness {
|
||||
Off = "Off",
|
||||
Low = "Low",
|
||||
Med = "Med",
|
||||
High = "High",
|
||||
}
|
||||
|
||||
export interface DeviceState {
|
||||
display_enabled: boolean;
|
||||
display_brightness: Brightness;
|
||||
builtin_anims_enabled: boolean;
|
||||
builtin_anims: Animations;
|
||||
off_when_unplugged: boolean;
|
||||
off_when_suspended: boolean;
|
||||
off_when_lid_closed: boolean;
|
||||
brightness_on_battery: Brightness;
|
||||
}
|
||||
|
||||
export enum AnimeType {
|
||||
GA401 = "GA401",
|
||||
GA402 = "GA402",
|
||||
GU604 = "GU604",
|
||||
Unknown = "Unknown",
|
||||
}
|
||||
|
||||
@@ -1,233 +0,0 @@
|
||||
/*
|
||||
Generated by typeshare 1.7.0
|
||||
*/
|
||||
|
||||
/** Represents the per-key raw USB packets */
|
||||
export type UsbPackets = number[][];
|
||||
|
||||
/**
|
||||
* A `UsbPackets` contains all data to change the full set of keyboard
|
||||
* key colours individually.
|
||||
*
|
||||
* Each row of the internal array is a full HID packet that can be sent
|
||||
* to the keyboard EC. One row controls one group of keys, these keys are not
|
||||
* necessarily all on the same row of the keyboard, with some splitting between
|
||||
* two rows.
|
||||
*/
|
||||
export interface LedUsbPackets {
|
||||
/** The packet data used to send data to the USB keyboard */
|
||||
usb_packets: UsbPackets;
|
||||
/**
|
||||
* Wether or not this packet collection is zoned. The determines which
|
||||
* starting bytes are used and what the indexing is for lightbar RGB
|
||||
* colours
|
||||
*/
|
||||
zoned: boolean;
|
||||
}
|
||||
|
||||
export interface Colour {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
}
|
||||
|
||||
/** Enum of modes that convert to the actual number required by a USB HID packet */
|
||||
export enum AuraModeNum {
|
||||
Static = "Static",
|
||||
Breathe = "Breathe",
|
||||
Strobe = "Strobe",
|
||||
Rainbow = "Rainbow",
|
||||
Star = "Star",
|
||||
Rain = "Rain",
|
||||
Highlight = "Highlight",
|
||||
Laser = "Laser",
|
||||
Ripple = "Ripple",
|
||||
Pulse = "Pulse",
|
||||
Comet = "Comet",
|
||||
Flash = "Flash",
|
||||
}
|
||||
|
||||
/** Base effects have no zoning, while multizone is 1-4 */
|
||||
export enum AuraZone {
|
||||
/** Used if keyboard has no zones, or if setting all */
|
||||
None = "None",
|
||||
/** Leftmost zone */
|
||||
Key1 = "Key1",
|
||||
/** Zone after leftmost */
|
||||
Key2 = "Key2",
|
||||
/** Zone second from right */
|
||||
Key3 = "Key3",
|
||||
/** Rightmost zone */
|
||||
Key4 = "Key4",
|
||||
/** Logo on the lid (or elsewhere?) */
|
||||
Logo = "Logo",
|
||||
/** The left part of a lightbar (typically on the front of laptop) */
|
||||
BarLeft = "BarLeft",
|
||||
/** The right part of a lightbar */
|
||||
BarRight = "BarRight",
|
||||
}
|
||||
|
||||
export enum Speed {
|
||||
Low = "Low",
|
||||
Med = "Med",
|
||||
High = "High",
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for Rainbow mode.
|
||||
*
|
||||
* Enum corresponds to the required integer value
|
||||
*/
|
||||
export enum Direction {
|
||||
Right = "Right",
|
||||
Left = "Left",
|
||||
Up = "Up",
|
||||
Down = "Down",
|
||||
}
|
||||
|
||||
/**
|
||||
* Default factory modes structure. This easily converts to an USB HID packet
|
||||
* with:
|
||||
* ```rust
|
||||
* // let bytes: [u8; LED_MSG_LEN] = mode.into();
|
||||
* ```
|
||||
*/
|
||||
export interface AuraEffect {
|
||||
/** The effect type */
|
||||
mode: AuraModeNum;
|
||||
/** `AuraZone::None` for no zone or zoneless keyboards */
|
||||
zone: AuraZone;
|
||||
/** Primary colour for all modes */
|
||||
colour1: Colour;
|
||||
/** Secondary colour in some modes like Breathing or Stars */
|
||||
colour2: Colour;
|
||||
/** One of three speeds for modes that support speed (most that animate) */
|
||||
speed: Speed;
|
||||
/** Up, down, left, right. Only Rainbow mode seems to use this */
|
||||
direction: Direction;
|
||||
}
|
||||
|
||||
/** The powerr zones this laptop supports */
|
||||
export enum PowerZones {
|
||||
/** The logo on some laptop lids */
|
||||
Logo = "Logo",
|
||||
/** The full keyboard (not zones) */
|
||||
Keyboard = "Keyboard",
|
||||
/** The lightbar, typically on the front of the laptop */
|
||||
Lightbar = "Lightbar",
|
||||
/** The leds that may be placed around the edge of the laptop lid */
|
||||
Lid = "Lid",
|
||||
/** The led strip on the rear of some laptops */
|
||||
RearGlow = "RearGlow",
|
||||
}
|
||||
|
||||
export interface KbAuraPowerState {
|
||||
zone: PowerZones;
|
||||
boot: boolean;
|
||||
awake: boolean;
|
||||
sleep: boolean;
|
||||
shutdown: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Track and control the Aura keyboard power state
|
||||
*
|
||||
* # Bits for newer 0x18c6, 0x19B6, 0x1a30, keyboard models
|
||||
*
|
||||
* | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Label |
|
||||
* |--------|---------|---------|---------|----------|
|
||||
* |00000001| 00000000| 00000000| 00000000|boot_logo_|
|
||||
* |00000010| 00000000| 00000000| 00000000|boot_keyb_|
|
||||
* |00000100| 00000000| 00000000| 00000000|awake_logo|
|
||||
* |00001000| 00000000| 00000000| 00000000|awake_keyb|
|
||||
* |00010000| 00000000| 00000000| 00000000|sleep_logo|
|
||||
* |00100000| 00000000| 00000000| 00000000|sleep_keyb|
|
||||
* |01000000| 00000000| 00000000| 00000000|shut_logo_|
|
||||
* |10000000| 00000000| 00000000| 00000000|shut_keyb_|
|
||||
* |00000000| 00000010| 00000000| 00000000|boot_bar__|
|
||||
* |00000000| 00000100| 00000000| 00000000|awake_bar_|
|
||||
* |00000000| 00001000| 00000000| 00000000|sleep_bar_|
|
||||
* |00000000| 00010000| 00000000| 00000000|shut_bar__|
|
||||
* |00000000| 00000000| 00000001| 00000000|boot_lid__|
|
||||
* |00000000| 00000000| 00000010| 00000000|awkae_lid_|
|
||||
* |00000000| 00000000| 00000100| 00000000|sleep_lid_|
|
||||
* |00000000| 00000000| 00001000| 00000000|shut_lid__|
|
||||
* |00000000| 00000000| 00000000| 00000001|boot_rear_|
|
||||
* |00000000| 00000000| 00000000| 00000010|awake_rear|
|
||||
* |00000000| 00000000| 00000000| 00000100|sleep_rear|
|
||||
* |00000000| 00000000| 00000000| 00001000|shut_rear_|
|
||||
*/
|
||||
export interface AuraPower {
|
||||
keyboard: KbAuraPowerState;
|
||||
logo: KbAuraPowerState;
|
||||
lightbar: KbAuraPowerState;
|
||||
lid: KbAuraPowerState;
|
||||
rear_glow: KbAuraPowerState;
|
||||
}
|
||||
|
||||
export enum AuraDevTuf {
|
||||
Boot = "Boot",
|
||||
Awake = "Awake",
|
||||
Sleep = "Sleep",
|
||||
Keyboard = "Keyboard",
|
||||
}
|
||||
|
||||
/**
|
||||
* # Bits for older 0x1866 keyboard model
|
||||
*
|
||||
* Keybord and Lightbar require Awake, Boot and Sleep apply to both
|
||||
* Keybord and Lightbar regardless of if either are enabled (or Awake is
|
||||
* enabled)
|
||||
*
|
||||
* | Byte 1 | Byte 2 | Byte 3 | function | hex |
|
||||
* |------------|------------|------------|----------|----------|
|
||||
* | 0000, 0000 | 0000, 0000 | 0000, 0010 | Awake | 00,00,02 |
|
||||
* | 0000, 1000 | 0000, 0000 | 0000, 0000 | Keyboard | 08,00,00 |
|
||||
* | 0000, 0100 | 0000, 0101 | 0000, 0000 | Lightbar | 04,05,00 |
|
||||
* | 1100, 0011 | 0001, 0010 | 0000, 1001 | Boot/Sht | c3,12,09 |
|
||||
* | 0011, 0000 | 0000, 1000 | 0000, 0100 | Sleep | 30,08,04 |
|
||||
* | 1111, 1111 | 0001, 1111 | 0000, 1111 | all on | |
|
||||
*/
|
||||
export enum AuraDevRog1 {
|
||||
Awake = "Awake",
|
||||
Keyboard = "Keyboard",
|
||||
Lightbar = "Lightbar",
|
||||
Boot = "Boot",
|
||||
Sleep = "Sleep",
|
||||
}
|
||||
|
||||
/** This struct is intended as a helper to pass args to generic dbus interface */
|
||||
export interface AuraPowerDev {
|
||||
/**
|
||||
* TUF laptops use a similar style of control to the older ROG devices but
|
||||
* through WMI
|
||||
*/
|
||||
tuf: AuraDevTuf[];
|
||||
/**
|
||||
* Pre-0x19b6 devices use a different smaller scheme to the newer ROG
|
||||
* devices
|
||||
*/
|
||||
old_rog: AuraDevRog1[];
|
||||
/** ASUS standardised control scheme from 2020 onwards */
|
||||
rog: AuraPower;
|
||||
}
|
||||
|
||||
export enum LedBrightness {
|
||||
Off = "Off",
|
||||
Low = "Low",
|
||||
Med = "Med",
|
||||
High = "High",
|
||||
}
|
||||
|
||||
export enum AuraDevice {
|
||||
Tuf = "Tuf",
|
||||
X1854 = "X1854",
|
||||
X1869 = "X1869",
|
||||
X1866 = "X1866",
|
||||
X18c6 = "X18c6",
|
||||
X19b6 = "X19b6",
|
||||
X1a30 = "X1a30",
|
||||
X1abe = "X1abe",
|
||||
Unknown = "Unknown",
|
||||
}
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
Generated by typeshare 1.7.0
|
||||
*/
|
||||
|
||||
export enum CPUGovernor {
|
||||
Performance = "Performance",
|
||||
Powersave = "Powersave",
|
||||
BadValue = "BadValue",
|
||||
}
|
||||
|
||||
export enum CPUEPP {
|
||||
Default = "Default",
|
||||
Performance = "Performance",
|
||||
BalancePerformance = "BalancePerformance",
|
||||
BalancePower = "BalancePower",
|
||||
Power = "Power",
|
||||
}
|
||||
|
||||
export enum GpuMode {
|
||||
Discrete = "Discrete",
|
||||
Optimus = "Optimus",
|
||||
Integrated = "Integrated",
|
||||
Egpu = "Egpu",
|
||||
Vfio = "Vfio",
|
||||
Ultimate = "Ultimate",
|
||||
Error = "Error",
|
||||
NotSupported = "NotSupported",
|
||||
}
|
||||
|
||||
/** `throttle_thermal_policy` in asus_wmi */
|
||||
export enum PlatformPolicy {
|
||||
Balanced = "Balanced",
|
||||
Performance = "Performance",
|
||||
Quiet = "Quiet",
|
||||
}
|
||||
|
||||
/** CamelCase names of the properties. Intended for use with DBUS */
|
||||
export enum Properties {
|
||||
ChargeControlEndThreshold = "ChargeControlEndThreshold",
|
||||
DgpuDisable = "DgpuDisable",
|
||||
GpuMuxMode = "GpuMuxMode",
|
||||
PostAnimationSound = "PostAnimationSound",
|
||||
PanelOd = "PanelOd",
|
||||
MiniLedMode = "MiniLedMode",
|
||||
EgpuEnable = "EgpuEnable",
|
||||
PlatformPolicy = "PlatformPolicy",
|
||||
PptPl1Spl = "PptPl1Spl",
|
||||
PptPl2Sppt = "PptPl2Sppt",
|
||||
PptFppt = "PptFppt",
|
||||
PptApuSppt = "PptApuSppt",
|
||||
PptPlatformSppt = "PptPlatformSppt",
|
||||
NvDynamicBoost = "NvDynamicBoost",
|
||||
NvTempTarget = "NvTempTarget",
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
Generated by typeshare 1.7.0
|
||||
*/
|
||||
|
||||
export enum FanCurvePU {
|
||||
CPU = "CPU",
|
||||
GPU = "GPU",
|
||||
MID = "MID",
|
||||
}
|
||||
|
||||
export interface CurveData {
|
||||
fan: FanCurvePU;
|
||||
pwm: [number, number, number, number, number, number, number, number];
|
||||
temp: [number, number, number, number, number, number, number, number];
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
/** Main purpose of `FanCurves` is to enable restoring state on system boot */
|
||||
export interface FanCurveProfiles {
|
||||
balanced: CurveData[];
|
||||
performance: CurveData[];
|
||||
quiet: CurveData[];
|
||||
}
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
[package]
|
||||
name = "config-traits"
|
||||
license = "MPL-2.0"
|
||||
authors = ["Luke D Jones <luke@ljones.dev>"]
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
version.workspace = true
|
||||
readme.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
serde_json.workspace = true
|
||||
toml.workspace = true
|
||||
ron.workspace = true
|
||||
|
||||
log.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
cargo-husky.workspace = true
|
||||
@@ -3,10 +3,7 @@
|
||||
//! updating them from previous versions where fields or names are changed in
|
||||
//! some way.
|
||||
//!
|
||||
//! The end canonical file format is `.ron` as this supports rust types well,
|
||||
//! and includes the ability to add commenting, and is less verbose than `json`.
|
||||
//! Currently the crate will also try to parse from `json` and `toml` if the
|
||||
//! `ron` parsing fails, then update to `ron` format.
|
||||
//! The end canonical file format is `.ron` as this supports rust types well
|
||||
|
||||
use std::fs::{self, create_dir, File, OpenOptions};
|
||||
use std::io::{Read, Write};
|
||||
@@ -22,7 +19,7 @@ use serde::Serialize;
|
||||
/// implemented, the rest are intended to be free methods.
|
||||
pub trait StdConfig
|
||||
where
|
||||
Self: Serialize + DeserializeOwned,
|
||||
Self: Serialize + DeserializeOwned
|
||||
{
|
||||
/// Taking over the standard `new()` to ensure things can be generic
|
||||
fn new() -> Self;
|
||||
@@ -92,6 +89,7 @@ where
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(false)
|
||||
.open(self.file_path())
|
||||
.unwrap_or_else(|e| panic!("Could not open {:?} {e}", self.file_path()))
|
||||
}
|
||||
@@ -109,6 +107,20 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Open and parse the config file to self from ron format
|
||||
fn read_new(&self) -> Option<Self> {
|
||||
if let Ok(data) = fs::read_to_string(self.file_path()) {
|
||||
if data.is_empty() {
|
||||
warn!("File is empty {:?}", self.file_path());
|
||||
} else if let Ok(data) = ron::from_str(&data) {
|
||||
return Some(data);
|
||||
} else {
|
||||
warn!("Could not deserialise {:?}", self.file_path());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Write the config file data to pretty ron format
|
||||
fn write(&self) {
|
||||
let mut file = match File::create(self.file_path()) {
|
||||
@@ -134,11 +146,7 @@ where
|
||||
|
||||
/// Renames the existing file to `<file>-old`
|
||||
fn rename_file_old(&self) {
|
||||
warn!(
|
||||
"Renaming {} to {}-old and recreating config",
|
||||
self.file_name(),
|
||||
self.file_name()
|
||||
);
|
||||
warn!("Renaming {} to {}-old", self.file_name(), self.file_name());
|
||||
let mut cfg_old = self.file_path().to_string_lossy().to_string();
|
||||
cfg_old.push_str("-old");
|
||||
std::fs::rename(self.file_path(), cfg_old).unwrap_or_else(|err| {
|
||||
@@ -195,7 +203,7 @@ macro_rules! std_config_load {
|
||||
/// new one created
|
||||
pub trait $trait_name<$($generic),*>
|
||||
where
|
||||
Self: $crate::StdConfig +std::fmt::Debug + DeserializeOwned + Serialize,
|
||||
Self: $crate::StdConfig + DeserializeOwned + Serialize,
|
||||
$($generic: DeserializeOwned + Into<Self>),*
|
||||
{
|
||||
fn load(mut self) -> Self {
|
||||
@@ -206,21 +214,9 @@ macro_rules! std_config_load {
|
||||
if let Ok(data) = ron::from_str(&buf) {
|
||||
self = data;
|
||||
log::info!("Parsed RON for {:?}", std::any::type_name::<Self>());
|
||||
} else if let Ok(data) = serde_json::from_str(&buf) {
|
||||
self = data;
|
||||
log::info!("Parsed JSON for {:?}", std::any::type_name::<Self>());
|
||||
} else if let Ok(data) = toml::from_str(&buf) {
|
||||
self = data;
|
||||
log::info!("Parsed TOML for {:?}", std::any::type_name::<Self>());
|
||||
} $(else if let Ok(data) = ron::from_str::<$generic>(&buf) {
|
||||
} $(else if let Ok(data) = ron::from_str::<$generic>(&buf) {
|
||||
self = data.into();
|
||||
log::info!("New version failed, trying previous: Parsed RON for {:?}", std::any::type_name::<$generic>());
|
||||
} else if let Ok(data) = serde_json::from_str::<$generic>(&buf) {
|
||||
self = data.into();
|
||||
log::info!("New version failed, trying previous: Parsed JSON for {:?}", std::any::type_name::<$generic>());
|
||||
} else if let Ok(data) = toml::from_str::<$generic>(&buf) {
|
||||
self = data.into();
|
||||
log::info!("Newvious version failed, trying previous: Parsed TOML for {:?}", std::any::type_name::<$generic>());
|
||||
})* else {
|
||||
self.rename_file_old();
|
||||
self = Self::new();
|
||||
@@ -274,6 +270,8 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
let _ = Test {};
|
||||
|
||||
impl crate::StdConfigLoad1<Old1> for Test {}
|
||||
}
|
||||
|
||||
@@ -323,6 +321,8 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
let _ = Test {};
|
||||
|
||||
impl crate::StdConfigLoad3<Old1, Old2, Old3> for Test {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
[package]
|
||||
name = "cpuctl"
|
||||
license = "MPL-2.0"
|
||||
edition = "2021"
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
@@ -1,14 +0,0 @@
|
||||
pub fn add(left: usize, right: usize) -> usize {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
||||
@@ -3,24 +3,24 @@
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
<busconfig>
|
||||
<policy group="adm">
|
||||
<allow send_destination="org.asuslinux.Daemon"/>
|
||||
<allow receive_sender="org.asuslinux.Daemon"/>
|
||||
<allow send_destination="xyz.ljones.Asusd"/>
|
||||
<allow receive_sender="xyz.ljones.Asusd"/>
|
||||
</policy>
|
||||
<policy group="sudo">
|
||||
<allow send_destination="org.asuslinux.Daemon"/>
|
||||
<allow receive_sender="org.asuslinux.Daemon"/>
|
||||
<allow send_destination="xyz.ljones.Asusd"/>
|
||||
<allow receive_sender="xyz.ljones.Asusd"/>
|
||||
</policy>
|
||||
<policy group="users">
|
||||
<allow send_destination="org.asuslinux.Daemon"/>
|
||||
<allow receive_sender="org.asuslinux.Daemon"/>
|
||||
<allow send_destination="xyz.ljones.Asusd"/>
|
||||
<allow receive_sender="xyz.ljones.Asusd"/>
|
||||
</policy>
|
||||
<policy group="wheel">
|
||||
<allow send_destination="org.asuslinux.Daemon"/>
|
||||
<allow receive_sender="org.asuslinux.Daemon"/>
|
||||
<allow send_destination="xyz.ljones.Asusd"/>
|
||||
<allow receive_sender="xyz.ljones.Asusd"/>
|
||||
</policy>
|
||||
<policy user="root">
|
||||
<allow own="org.asuslinux.Daemon"/>
|
||||
<allow send_destination="org.asuslinux.Daemon"/>
|
||||
<allow receive_sender="org.asuslinux.Daemon"/>
|
||||
<allow own="xyz.ljones.Asusd"/>
|
||||
<allow send_destination="xyz.ljones.Asusd"/>
|
||||
<allow receive_sender="xyz.ljones.Asusd"/>
|
||||
</policy>
|
||||
</busconfig>
|
||||
|
||||
@@ -7,6 +7,8 @@ ENV{DMI_FAMILY}=="*ROG*", GOTO="asusd_start"
|
||||
ENV{DMI_FAMILY}=="*Zephyrus*", GOTO="asusd_start"
|
||||
ENV{DMI_FAMILY}=="*Strix*", GOTO="asusd_start"
|
||||
ENV{DMI_FAMILY}=="*Vivo*ook*", GOTO="asusd_start"
|
||||
ENV{DMI_FAMILY}=="*Zenbook*", GOTO="asusd_start"
|
||||
ENV{DMI_FAMILY}=="*ProArt*", GOTO="asusd_start"
|
||||
# No match so
|
||||
GOTO="asusd_end"
|
||||
|
||||
|
||||
@@ -6,12 +6,14 @@ After=nvidia-powerd.service systemd-udevd.service
|
||||
|
||||
[Service]
|
||||
Environment=IS_SERVICE=1
|
||||
Environment=RUST_LOG="info"
|
||||
# ExecStartPre=/bin/sleep 2 # was required only for slow devices
|
||||
Environment=RUST_LOG="debug"
|
||||
# required to prevent init issues with hid_asus and MCU
|
||||
ExecStartPre=/bin/sleep 1
|
||||
ExecStart=/usr/bin/asusd
|
||||
Restart=on-failure
|
||||
RestartSec=1
|
||||
Type=dbus
|
||||
BusName=org.asuslinux.Daemon
|
||||
BusName=xyz.ljones.Asusd
|
||||
SELinuxContext=system_u:system_r:unconfined_t:s0
|
||||
#SELinuxContext=system_u:object_r:modules_object_t:s0
|
||||
#SELinuxContext=system_u:object_r:modules_object_t:s0
|
||||
TimeoutSec=10
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 75 KiB |
@@ -135,7 +135,7 @@ impl crate::ZbusAdd for CtrlAnimeZbus {
|
||||
}
|
||||
}
|
||||
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
#[dbus_interface(name = "xyz.ljones.Asusd")]
|
||||
impl CtrlAnimeZbus {
|
||||
async fn <zbus method>() {
|
||||
let lock = self.inner.lock().await;
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/* eslint-env node */
|
||||
module.exports = {
|
||||
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
plugins: ["@typescript-eslint"],
|
||||
root: true,
|
||||
|
||||
rules: {
|
||||
// enable additional rules
|
||||
indent: ["error", 4],
|
||||
"linebreak-style": ["error", "unix"],
|
||||
quotes: ["error", "double"],
|
||||
semi: ["error", "always"],
|
||||
|
||||
// override configuration set by extending "eslint:recommended"
|
||||
"no-empty": "warn",
|
||||
"no-cond-assign": ["error", "always"],
|
||||
|
||||
// disable rules from base configurations
|
||||
"for-direction": "off",
|
||||
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
},
|
||||
};
|
||||
@@ -1,13 +0,0 @@
|
||||
# Generated files
|
||||
/@types/gir-generated/*
|
||||
|
||||
# Build outputes
|
||||
/dist/
|
||||
/build/
|
||||
|
||||
# Node configuration and modules
|
||||
/package.json
|
||||
/node_modules/
|
||||
|
||||
# Files I prefer not to be formatted
|
||||
*.md
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"printWidth": 100,
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"trailingComma": "all",
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "always"
|
||||
}
|
||||
@@ -1,373 +0,0 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
@@ -1,21 +0,0 @@
|
||||
# asusctl
|
||||
|
||||
Requires `asusd` to be installed and running.
|
||||
|
||||
## build and install
|
||||
|
||||
```
|
||||
npm install
|
||||
npm run build && gnome-extensions install asusctl-gnome@asus-linux.org.zip --force
|
||||
npm run build && gnome-extensions enable asusctl-gnome@asus-linux.org.zip
|
||||
```
|
||||
|
||||
You will need to restart Gnome after installing or updating
|
||||
|
||||
## development
|
||||
|
||||
```
|
||||
npm run build
|
||||
gnome-extensions install asusctl-gnome@asus-linux.org.zip --force
|
||||
MUTTER_DEBUG_DUMMY_MODE_SPECS=1366x768 dbus-run-session -- gnome-shell --nested --wayland
|
||||
```
|
||||
@@ -1,67 +0,0 @@
|
||||
import { build } from "esbuild";
|
||||
import { exec } from "child_process";
|
||||
import { copyFileSync, cpSync } from "fs";
|
||||
import { resolve, dirname } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import AdmZip from "adm-zip";
|
||||
import metadata from "./src/metadata.json" assert { type: "json" };
|
||||
|
||||
build({
|
||||
entryPoints: ["src/extension.ts"],
|
||||
outdir: "dist",
|
||||
bundle: true,
|
||||
// Do not remove the functions `enable()`, `disable()` and `init()`
|
||||
treeShaking: false,
|
||||
// firefox60 // Since GJS 1.53.90
|
||||
// firefox68 // Since GJS 1.63.90
|
||||
// firefox78 // Since GJS 1.65.90
|
||||
// firefox91 // Since GJS 1.71.1
|
||||
// firefox102 // Since GJS 1.73.2
|
||||
target: "firefox102",
|
||||
//platform: "neutral",
|
||||
platform: "node",
|
||||
// mainFields: ['main'],
|
||||
// conditions: ['require', 'default'],
|
||||
format: "esm",
|
||||
external: ["gi://*", "resource://*", "system", "gettext", "cairo"],
|
||||
}).then(() => {
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const metaSrc = resolve(__dirname, "src/metadata.json");
|
||||
const metaDist = resolve(__dirname, "dist/metadata.json");
|
||||
const schemaSrc = resolve(__dirname, "schemas");
|
||||
const schemaDist = resolve(__dirname, "dist/schemas");
|
||||
const dbusXmlSrc = resolve(__dirname, "../../bindings/dbus-xml");
|
||||
const dbusXmlDist = resolve(__dirname, "dist/resources/dbus");
|
||||
const zipFilename = `${metadata.uuid}.zip`;
|
||||
const zipDist = resolve(__dirname, zipFilename);
|
||||
|
||||
exec("glib-compile-schemas schemas/", (error, stdout, stderr) => {
|
||||
console.log("stdout: " + stdout);
|
||||
console.log("stderr: " + stderr);
|
||||
});
|
||||
|
||||
copyFileSync(metaSrc, metaDist);
|
||||
|
||||
cpSync(schemaSrc, schemaDist, { recursive: true }, (err) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
cpSync(dbusXmlSrc, dbusXmlDist, { recursive: true }, (err) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
const zip = new AdmZip();
|
||||
zip.addLocalFolder(resolve(__dirname, "dist"));
|
||||
zip.writeZip(zipDist);
|
||||
|
||||
console.log(`Build complete. Zip file: ${zipFilename}\n`);
|
||||
console.log(`Install with: gnome-extensions install ${zipFilename}`);
|
||||
console.log(`Update with: gnome-extensions install ${zipFilename} --force`);
|
||||
console.log(`Enable with: gnome-extensions enable ${metadata.uuid} --user`);
|
||||
});
|
||||
@@ -1,51 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
## Script to initialise dev-environment (types)
|
||||
gv="44"
|
||||
wd=${PWD}
|
||||
|
||||
# cleanup
|
||||
rm -rf @types
|
||||
|
||||
# generate GJS from gir (this does not include the extensions)
|
||||
echo "Generating GJS types from gir.."
|
||||
npx ts-for-gir generate Shell-12 St-12 Gtk-4.0 \
|
||||
-g /usr/share/gir-1.0 \
|
||||
-g /usr/share/gnome-shell \
|
||||
-g /usr/share/gnome-shell/gir-1.0 \
|
||||
-g /usr/lib64/mutter-12 \
|
||||
-t esm -o @types/Gjs
|
||||
|
||||
# get latest js (44) in this case and create the types for it
|
||||
echo "Generating GJS Extension (Gex) types from extension source.."
|
||||
mkdir -p ./_tmp/
|
||||
cd ./_tmp
|
||||
wget -q -O gnome-shell-js-${gv}.tar.gz https://gitlab.gnome.org/GNOME/gnome-shell/-/archive/gnome-${gv}/gnome-shell-gnome-${gv}.tar.gz?path=js
|
||||
tar xf gnome-shell-js-${gv}.tar.gz
|
||||
cd gnome-shell-gnome-${gv}-js
|
||||
cat >tsconfig.json <<EOL
|
||||
{
|
||||
"include": ["js/ui/*"],
|
||||
"exclude": [
|
||||
"js/ui/shellDBus.js",
|
||||
"node_modules",
|
||||
"**/node_modules/*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "gex-types",
|
||||
"declarationMap": true,
|
||||
"lib": ["es2019"]
|
||||
}
|
||||
}
|
||||
EOL
|
||||
npx tsc
|
||||
cd ${wd}
|
||||
mv ./_tmp/gnome-shell-gnome-${gv}-js/gex-types @types/Gex
|
||||
# rm -rf ./_tmp/
|
||||
|
||||
echo "done."
|
||||
|
||||
exit 0
|
||||
2747
desktop-extensions/gnome-45/package-lock.json
generated
2747
desktop-extensions/gnome-45/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,55 +0,0 @@
|
||||
{
|
||||
"name": "asusctl-gnome",
|
||||
"version": "5.0.0-RC1",
|
||||
"description": "asusctl-gnome a gnome extension exposing some of the base features of asusd in a helpful and easy to use way",
|
||||
"type": "module",
|
||||
"main": "dist/extension.js",
|
||||
"scripts": {
|
||||
"clear": "rm -rf dist",
|
||||
"compile": "tsc --build tsconfig.json",
|
||||
"build:app": "node esbuild.js",
|
||||
"build": "yarn run clear && yarn run build:app",
|
||||
"validate": "tsc --noEmit",
|
||||
"generate:gir-types": "ts-for-gir generate",
|
||||
"check:types": "tsc --build tsconfig.types.json",
|
||||
"lint": "eslint .",
|
||||
"format": "prettier . -w"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@girs/gnome-shell": "^45.0.0-beta2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.60.1",
|
||||
"@typescript-eslint/parser": "^5.60.1",
|
||||
"adm-zip": "^0.5.10",
|
||||
"esbuild": "^0.19.5",
|
||||
"eslint": "^8.51.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"prettier": "^3.0.3",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@girs/gjs": "^3.2.5",
|
||||
"@girs/gobject-2.0": "^2.78.0-3.2.5",
|
||||
"@girs/st-13": "^13.0.0-3.2.5"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+ssh://git@gitlab.com/asus-linux/asusctl.git"
|
||||
},
|
||||
"keywords": [
|
||||
"gnome-shell",
|
||||
"extension",
|
||||
"asusctl",
|
||||
"asus",
|
||||
"rog",
|
||||
"gnome",
|
||||
"gjs",
|
||||
"typescript"
|
||||
],
|
||||
"author": "Armas Spann, Marco Laux, Luke Jones",
|
||||
"license": "MPL-2",
|
||||
"bugs": {
|
||||
"url": "https://gitlab.com/asus-linux/asusctl/issues"
|
||||
},
|
||||
"homepage": "https://gitlab.com/asus-linux/asusctl/desktop-extensions/gnome#readme"
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<schemalist gettext-domain="AsusctlGnomeExtension">
|
||||
<schema id="org.gnome.shell.extensions.asusctl-gnome" path="/org/gnome/shell/extensions/asusctl-gnome/" >
|
||||
<key type="b" name="mini-led-enabled">
|
||||
<default>false</default>
|
||||
</key>
|
||||
<key type="b" name="panel-od-enabled">
|
||||
<default>false</default>
|
||||
</key>
|
||||
<key type="b" name="anime-power">
|
||||
<default>false</default>
|
||||
</key>
|
||||
<key type="b" name="anime-builtins">
|
||||
<default>false</default>
|
||||
</key>
|
||||
<key name="charge-level" type="u">
|
||||
<range min="20" max="100"/>
|
||||
<default>100</default>
|
||||
</key>
|
||||
<key type="s" name="primary-quickmenu-toggle">
|
||||
<default>"mini-led"</default>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
||||
@@ -1 +0,0 @@
|
||||
../../../bindings/ts
|
||||
@@ -1,127 +0,0 @@
|
||||
import { Extension, gettext as _ } from "@girs/gnome-shell/extensions/extension";
|
||||
import * as platform from "./bindings/platform";
|
||||
import { AsusQuickToggle } from "./modules/rog_quick_toggle";
|
||||
import { AsusMenuToggle } from "./modules/rog_menu_toggle";
|
||||
import { AsusIndicator } from "./modules/rog_indicator";
|
||||
import { AsusSlider } from "./modules/rog_slider_100pc";
|
||||
import { FeatureMenuToggle } from "./modules/quick_menus/laptop_features";
|
||||
import { DbusBase } from "./modules/dbus_proxy";
|
||||
import { main } from "@girs/gnome-shell/ui";
|
||||
|
||||
export const uuid = "asusctl-gnome@asus-linux.org";
|
||||
export default class AsusExtension extends Extension {
|
||||
// public dbus_aura: AuraDbus = new AuraDbus;
|
||||
// public dbus_anime: AnimeDbus = new AnimeDbus;
|
||||
public dbus_platform: DbusBase | undefined;
|
||||
public dbus_anime: DbusBase | undefined;
|
||||
|
||||
private individual = false;
|
||||
public supported_properties!: platform.Properties;
|
||||
public supported_interfaces: string[] = [];
|
||||
private feature_menu = null;
|
||||
private panel_od = null;
|
||||
private mini_led = null;
|
||||
private anime_display = null;
|
||||
private anime_builtins = null;
|
||||
private charge_thres = null;
|
||||
// private _feature: typeof FeatureMenuToggle;
|
||||
|
||||
async enable() {
|
||||
log(this.path);
|
||||
|
||||
if (this.dbus_platform == undefined) {
|
||||
this.dbus_platform = new DbusBase("org-asuslinux-platform-4.xml", "/org/asuslinux/Platform");
|
||||
await this.dbus_platform.start();
|
||||
}
|
||||
|
||||
if (this.dbus_anime == undefined) {
|
||||
this.dbus_anime = new DbusBase("org-asuslinux-anime-4.xml", "/org/asuslinux/Anime");
|
||||
await this.dbus_anime.start();
|
||||
}
|
||||
|
||||
this.supported_interfaces = this.dbus_platform?.proxy.SupportedInterfacesSync()[0];
|
||||
this.supported_properties = this.dbus_platform?.proxy.SupportedPropertiesSync()[0];
|
||||
log(this.supported_interfaces);
|
||||
log(this.supported_properties);
|
||||
|
||||
// new AsusIndicator("selection-mode-symbolic", "mini-led-enabled");
|
||||
// new AsusIndicator("selection-mode-symbolic", "panel-od-enabled");
|
||||
|
||||
if (!this.individual) {
|
||||
if (this.feature_menu == null)
|
||||
this.feature_menu = new FeatureMenuToggle(this.dbus_platform, this.dbus_anime);
|
||||
} else {
|
||||
if (this.supported_properties.includes("PanelOd") && this.dbus_platform.proxy.PanelOd != null)
|
||||
if (this.panel_od == null) {
|
||||
this.panel_od = new AsusQuickToggle(
|
||||
this.dbus_platform,
|
||||
"PanelOd",
|
||||
"panel-od-enabled",
|
||||
"Panel Overdrive",
|
||||
);
|
||||
}
|
||||
|
||||
if (this.supported_properties.includes("MiniLed") && this.dbus_platform.proxy.MiniLed != null)
|
||||
if (this.mini_led == null) {
|
||||
this.mini_led = new AsusQuickToggle(
|
||||
this.dbus_platform,
|
||||
"MiniLed",
|
||||
"mini-led-enabled",
|
||||
"Mini-LED",
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
this.supported_interfaces.includes("Anime") &&
|
||||
this.dbus_anime.proxy.EnableDisplay != null
|
||||
)
|
||||
if (this.anime_display == null) {
|
||||
this.anime_display = new AsusQuickToggle(
|
||||
this.dbus_anime,
|
||||
"EnableDisplay",
|
||||
"anime-power",
|
||||
"AniMe Display",
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
this.supported_interfaces.includes("Anime") &&
|
||||
this.dbus_anime.proxy.BuiltinsEnabled != null
|
||||
)
|
||||
if (this.anime_builtins == null) {
|
||||
this.anime_builtins = new AsusQuickToggle(
|
||||
this.dbus_anime,
|
||||
"BuiltinsEnabled",
|
||||
"anime-builtins",
|
||||
"Use builtins",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
this.supported_properties.includes("ChargeControlEndThreshold") &&
|
||||
this.dbus_platform.proxy.ChargeControlEndThreshold != null
|
||||
)
|
||||
if (this.charge_thres == null) {
|
||||
this.charge_thres = new AsusSlider(
|
||||
this.dbus_platform,
|
||||
"ChargeControlEndThreshold",
|
||||
"charge-level",
|
||||
"Charge Level",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
disable() {
|
||||
this.dbus_platform?.stop();
|
||||
this.dbus_anime?.stop();
|
||||
|
||||
this.feature_menu?.destroy();
|
||||
feature_menu?.destroy();
|
||||
panel_od?.destroy();
|
||||
mini_led?.destroy();
|
||||
anime_display?.destroy();
|
||||
anime_builtins?.destroy();
|
||||
charge_thres?.destroy();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"name": "asusctl-gnome",
|
||||
"description": "asusctl-gnome a gnome extension exposing some of the base features of asusd in a helpful and easy to use way",
|
||||
"uuid": "asusctl-gnome@asus-linux.org",
|
||||
"uuid-dev": "asusctl-gnome-dev@asus-linux.org",
|
||||
"settings-schema": "org.gnome.shell.extensions.asusctl-gnome",
|
||||
"version": "4.3.2",
|
||||
"shell-version": ["45"]
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
import { DbusBase } from "../dbus_proxy";
|
||||
import {
|
||||
DeviceState,
|
||||
AnimBooting,
|
||||
Brightness,
|
||||
AnimAwake,
|
||||
AnimSleeping,
|
||||
AnimShutdown,
|
||||
} from "../../bindings/anime";
|
||||
|
||||
export class AnimeDbus extends DbusBase {
|
||||
deviceState: DeviceState = {
|
||||
display_enabled: false,
|
||||
display_brightness: Brightness.Med,
|
||||
builtin_anims_enabled: false,
|
||||
builtin_anims: {
|
||||
boot: AnimBooting.GlitchConstruction,
|
||||
awake: AnimAwake.BinaryBannerScroll,
|
||||
sleep: AnimSleeping.BannerSwipe,
|
||||
shutdown: AnimShutdown.GlitchOut,
|
||||
},
|
||||
off_when_unplugged: false,
|
||||
off_when_suspended: false,
|
||||
off_when_lid_closed: false,
|
||||
};
|
||||
|
||||
// TODO: interface or something to enforce requirement of "sync()" method
|
||||
public notifyAnimeStateSubscribers: any[] = [];
|
||||
|
||||
constructor() {
|
||||
super("org-asuslinux-anime-4", "/org/asuslinux/Anime");
|
||||
}
|
||||
|
||||
_parseData(data: any) {
|
||||
if (data.length > 0) {
|
||||
this.deviceState.display_enabled = data[0];
|
||||
this.deviceState.display_brightness = Brightness[data[1] as Brightness];
|
||||
this.deviceState.builtin_anims_enabled = data[2];
|
||||
this.deviceState.builtin_anims.boot = AnimBooting[data[3][0] as AnimBooting];
|
||||
this.deviceState.builtin_anims.awake = AnimAwake[data[3][1] as AnimAwake];
|
||||
this.deviceState.builtin_anims.sleep = AnimSleeping[data[3][2] as AnimSleeping];
|
||||
this.deviceState.builtin_anims.shutdown = AnimShutdown[data[3][3] as AnimShutdown];
|
||||
this.deviceState.off_when_unplugged = data[4];
|
||||
this.deviceState.off_when_suspended = data[5];
|
||||
this.deviceState.off_when_lid_closed = data[6];
|
||||
}
|
||||
}
|
||||
|
||||
public getDeviceState() {
|
||||
if (this.isRunning()) {
|
||||
try {
|
||||
// janky shit going on with DeviceStateSync
|
||||
this._parseData(this.dbus_proxy.DeviceStateSync());
|
||||
//@ts-ignore
|
||||
log("Anime Matrix: display_enabled: " + this.deviceState.display_enabled);
|
||||
//@ts-ignore
|
||||
log("Anime Matrix: display_brightness: " + this.deviceState.display_brightness);
|
||||
//@ts-ignore
|
||||
log("Anime Matrix: builtin_anims_enabled: " + this.deviceState.builtin_anims_enabled);
|
||||
//@ts-ignore
|
||||
log("Anime Matrix: builtin_anims: " + this.deviceState.builtin_anims);
|
||||
//@ts-ignore
|
||||
log("Anime Matrix: off_when_unplugged: " + this.deviceState.off_when_unplugged);
|
||||
//@ts-ignore
|
||||
log("Anime Matrix: off_when_suspended: " + this.deviceState.off_when_suspended);
|
||||
//@ts-ignore
|
||||
log("Anime Matrix: off_when_lid_closed: " + this.deviceState.off_when_lid_closed);
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
log("Failed to fetch DeviceState!", e);
|
||||
}
|
||||
}
|
||||
return this.deviceState;
|
||||
}
|
||||
|
||||
async start() {
|
||||
await super.start();
|
||||
this.getDeviceState();
|
||||
|
||||
this.dbus_proxy.connectSignal(
|
||||
"NotifyDeviceState",
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(proxy: any = null, name: string, data: string) => {
|
||||
if (proxy) {
|
||||
// idiot xml parsing mneans the get is not nested while this is
|
||||
this._parseData(data[0]);
|
||||
this.notifyAnimeStateSubscribers.forEach((sub) => {
|
||||
sub.sync();
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async stop() {
|
||||
await super.stop();
|
||||
}
|
||||
}
|
||||
@@ -1,300 +0,0 @@
|
||||
import {
|
||||
AuraDevRog1,
|
||||
AuraDevTuf,
|
||||
AuraDevice,
|
||||
AuraEffect,
|
||||
AuraModeNum,
|
||||
AuraPower,
|
||||
AuraPowerDev,
|
||||
AuraZone,
|
||||
Direction,
|
||||
PowerZones,
|
||||
Speed,
|
||||
} from "../../bindings/aura";
|
||||
import { DbusBase } from "./base";
|
||||
|
||||
export class AuraDbus extends DbusBase {
|
||||
public device: AuraDevice = AuraDevice.Unknown;
|
||||
public current_aura_mode: AuraModeNum = AuraModeNum.Static;
|
||||
public aura_modes: Map<AuraModeNum, AuraEffect> = new Map();
|
||||
public leds_powered: AuraPowerDev = {
|
||||
tuf: [],
|
||||
old_rog: [],
|
||||
rog: {
|
||||
keyboard: {
|
||||
zone: PowerZones.Keyboard,
|
||||
boot: false,
|
||||
awake: false,
|
||||
sleep: false,
|
||||
shutdown: false,
|
||||
},
|
||||
logo: {
|
||||
zone: PowerZones.Logo,
|
||||
boot: false,
|
||||
awake: false,
|
||||
sleep: false,
|
||||
shutdown: false,
|
||||
},
|
||||
lightbar: {
|
||||
zone: PowerZones.Lightbar,
|
||||
boot: false,
|
||||
awake: false,
|
||||
sleep: false,
|
||||
shutdown: false,
|
||||
},
|
||||
lid: {
|
||||
zone: PowerZones.Lid,
|
||||
boot: false,
|
||||
awake: false,
|
||||
sleep: false,
|
||||
shutdown: false,
|
||||
},
|
||||
rear_glow: {
|
||||
zone: PowerZones.RearGlow,
|
||||
boot: false,
|
||||
awake: false,
|
||||
sleep: false,
|
||||
shutdown: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
// TODO: interface or something to enforce requirement of "sync()" method
|
||||
public notifyAuraModeSubscribers: any[] = [];
|
||||
public notifyAuraPowerSubscribers: any[] = [];
|
||||
|
||||
constructor() {
|
||||
super("org-asuslinux-aura-4", "/org/asuslinux/Aura");
|
||||
}
|
||||
|
||||
public getDevice() {
|
||||
if (this.isRunning()) {
|
||||
try {
|
||||
this.device = AuraDevice[this.dbus_proxy.DeviceTypeSync() as AuraDevice];
|
||||
//@ts-ignore
|
||||
log("LED device: " + this.device);
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
log("Failed to fetch supported functionalities", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_parsePowerStates(data: any[]) {
|
||||
const power: AuraPowerDev = this.leds_powered;
|
||||
|
||||
power.tuf = data[0].map((value: string) => {
|
||||
return AuraDevTuf[value as AuraDevTuf];
|
||||
});
|
||||
power.old_rog = data[1].map((value: string) => {
|
||||
return AuraDevRog1[value as AuraDevRog1];
|
||||
});
|
||||
power.rog = {
|
||||
keyboard: {
|
||||
zone: PowerZones[data[2][0][0] as PowerZones],
|
||||
boot: data[2][0][1],
|
||||
awake: data[2][0][2],
|
||||
sleep: data[2][0][3],
|
||||
shutdown: data[2][0][4],
|
||||
},
|
||||
logo: {
|
||||
zone: PowerZones[data[2][1][0] as PowerZones],
|
||||
boot: data[2][1][1],
|
||||
awake: data[2][1][2],
|
||||
sleep: data[2][1][3],
|
||||
shutdown: data[2][1][4],
|
||||
},
|
||||
lightbar: {
|
||||
zone: PowerZones[data[2][2][0] as PowerZones],
|
||||
boot: data[2][2][1],
|
||||
awake: data[2][2][2],
|
||||
sleep: data[2][2][3],
|
||||
shutdown: data[2][2][4],
|
||||
},
|
||||
lid: {
|
||||
zone: PowerZones[data[2][3][0] as PowerZones],
|
||||
boot: data[2][3][1],
|
||||
awake: data[2][3][2],
|
||||
sleep: data[2][3][3],
|
||||
shutdown: data[2][3][4],
|
||||
},
|
||||
rear_glow: {
|
||||
zone: PowerZones[data[2][4][0] as PowerZones],
|
||||
boot: data[2][4][1],
|
||||
awake: data[2][4][2],
|
||||
sleep: data[2][4][3],
|
||||
shutdown: data[2][4][4],
|
||||
},
|
||||
};
|
||||
|
||||
return power;
|
||||
}
|
||||
|
||||
public getLedPower() {
|
||||
if (this.isRunning()) {
|
||||
try {
|
||||
const data = this.dbus_proxy.LedPowerSync();
|
||||
this.leds_powered = this._parsePowerStates(data);
|
||||
//@ts-ignore
|
||||
log("LED power tuf: " + this.leds_powered.tuf);
|
||||
//@ts-ignore
|
||||
log("LED power x1866: " + this.leds_powered.old_rog);
|
||||
//@ts-ignore
|
||||
log("LED power x19b6: " + this.leds_powered.rog);
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
log("Failed to fetch supported functionalities", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getLedMode() {
|
||||
if (this.isRunning()) {
|
||||
try {
|
||||
this.current_aura_mode = AuraModeNum[this.dbus_proxy.LedModeSync() as AuraModeNum];
|
||||
//@ts-ignore
|
||||
log("Current LED mode:", this.current_aura_mode);
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
log("Failed to fetch supported functionalities", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public setLedMode(mode: AuraEffect) {
|
||||
if (this.isRunning()) {
|
||||
try {
|
||||
this.dbus_proxy.SetLedModeSync([
|
||||
mode.mode,
|
||||
mode.zone,
|
||||
[mode.colour1.r, mode.colour1.g, mode.colour1.b],
|
||||
[mode.colour2.r, mode.colour2.g, mode.colour2.b],
|
||||
mode.speed,
|
||||
mode.direction,
|
||||
]);
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
log("Failed to fetch supported functionalities", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_parseAuraEffect(data: any[]) {
|
||||
const aura: AuraEffect = {
|
||||
mode: AuraModeNum[data[0] as AuraModeNum],
|
||||
zone: AuraZone[data[1] as AuraZone],
|
||||
colour1: {
|
||||
r: parseInt(data[2][0]),
|
||||
g: parseInt(data[2][1]),
|
||||
b: parseInt(data[2][2]),
|
||||
},
|
||||
colour2: {
|
||||
r: parseInt(data[3][0]),
|
||||
g: parseInt(data[3][1]),
|
||||
b: parseInt(data[3][2]),
|
||||
},
|
||||
speed: Speed[data[4] as Speed],
|
||||
direction: Direction[data[5] as Direction],
|
||||
};
|
||||
return aura;
|
||||
}
|
||||
|
||||
// Return a list of the available modes, and the current settings for each
|
||||
public getLedModes() {
|
||||
// {'Breathe': ('Breathe', 'None', (166, 0, 0), (0, 0, 0), 'Med', 'Right'),
|
||||
// 'Comet': ('Comet', 'None', (166, 0, 0), (0, 0, 0), 'Med', 'Right'),
|
||||
// 'Static': ('Static', 'None', (78, 0, 0), (0, 0, 0), 'Med', 'Right'),
|
||||
// 'Strobe': ('Strobe', 'None', (166, 0, 0), (0, 0, 0), 'Med', 'Right')}
|
||||
if (this.isRunning()) {
|
||||
try {
|
||||
const _data = this.dbus_proxy.LedModesSync();
|
||||
for (const key in _data[0]) {
|
||||
const data = _data[0][key];
|
||||
const aura: AuraEffect = this._parseAuraEffect(data);
|
||||
this.aura_modes.set(AuraModeNum[key as AuraModeNum], aura);
|
||||
}
|
||||
|
||||
for (const [key, value] of this.aura_modes) {
|
||||
//@ts-ignore
|
||||
log(key, value.zone, value.colour1.r, value.speed, value.direction);
|
||||
}
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
log("Failed to fetch supported functionalities", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async start() {
|
||||
try {
|
||||
await super.start();
|
||||
this.getDevice();
|
||||
this.getLedPower();
|
||||
this.getLedMode();
|
||||
this.getLedModes();
|
||||
|
||||
//@ts-ignore
|
||||
log("Current LED mode data:", this.aura_modes.get(this.current_aura_mode)?.speed);
|
||||
|
||||
this.dbus_proxy.connectSignal("NotifyLed", (proxy: any = null, name: string, data: any) => {
|
||||
if (proxy) {
|
||||
const aura: AuraEffect = this._parseAuraEffect(data[0]);
|
||||
this.current_aura_mode = aura.mode;
|
||||
this.aura_modes.set(aura.mode, aura);
|
||||
//@ts-ignore
|
||||
log(
|
||||
"LED data has changed to ",
|
||||
aura.mode,
|
||||
aura.zone,
|
||||
aura.colour1.r,
|
||||
aura.speed,
|
||||
aura.direction,
|
||||
);
|
||||
this.notifyAuraModeSubscribers.forEach((sub) => {
|
||||
sub.sync();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.dbus_proxy.connectSignal(
|
||||
"NotifyPowerStates",
|
||||
(proxy: any = null, name: string, data: any) => {
|
||||
if (proxy) {
|
||||
const power: AuraPowerDev = this._parsePowerStates(data[0]);
|
||||
this.leds_powered = power;
|
||||
switch (this.device) {
|
||||
case AuraDevice.Tuf:
|
||||
//@ts-ignore
|
||||
log("LED power has changed to ", this.leds_powered.tuf);
|
||||
break;
|
||||
case AuraDevice.X1854:
|
||||
case AuraDevice.X1869:
|
||||
case AuraDevice.X18c6:
|
||||
//@ts-ignore
|
||||
log("LED power has changed to ", this.leds_powered.old_rog);
|
||||
break;
|
||||
case AuraDevice.X19b6:
|
||||
case AuraDevice.X1a30:
|
||||
//@ts-ignore
|
||||
log("LED power has changed to ", this.leds_powered.rog);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
//@ts-ignore
|
||||
log("LED power has changed to ", this.leds_powered.rog);
|
||||
this.notifyAuraPowerSubscribers.forEach((sub) => {
|
||||
sub.sync();
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
log("Supported DBus initialization failed!", e);
|
||||
}
|
||||
}
|
||||
|
||||
async stop() {
|
||||
await super.stop();
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
import { Extension, gettext as _ } from "@girs/gnome-shell/extensions/extension";
|
||||
import { Gio } from "@girs/gio-2.0";
|
||||
import { GLib } from "@girs/glib-2.0";
|
||||
import { imports } from "@girs/gjs";
|
||||
|
||||
// Reads the contents of a file contained in the global resources archive. The data
|
||||
// is returned as a string.
|
||||
export function getStringResource(path: string | null) {
|
||||
const data = Gio.resources_lookup_data(path, 0);
|
||||
return new TextDecoder().decode(data.get_data()?.buffer);
|
||||
}
|
||||
|
||||
export class DbusBase {
|
||||
proxy!: Gio.DBusProxy;
|
||||
connected = false;
|
||||
ifaceXml = "";
|
||||
dbus_path = "";
|
||||
|
||||
constructor(file_name: string, dbus_path: string) {
|
||||
let extensionObject = Extension.lookupByUUID("asusctl-gnome@asus-linux.org");
|
||||
const path = extensionObject?.path + "/resources/dbus/" + file_name;
|
||||
const [ok, data] = GLib.file_get_contents(path);
|
||||
if (!ok) {
|
||||
throw new Error("could not read interface file");
|
||||
}
|
||||
this.ifaceXml = imports.byteArray.toString(data);
|
||||
this.dbus_path = dbus_path;
|
||||
}
|
||||
|
||||
async start() {
|
||||
//@ts-ignore
|
||||
log(`Starting ${this.dbus_path} dbus module`);
|
||||
try {
|
||||
log(this.ifaceXml);
|
||||
this.proxy = Gio.DBusProxy.makeProxyWrapper(this.ifaceXml)(
|
||||
Gio.DBus.system,
|
||||
"org.asuslinux.Daemon",
|
||||
this.dbus_path,
|
||||
);
|
||||
|
||||
this.connected = true;
|
||||
//@ts-ignore
|
||||
log(`${this.dbus_path} client started successfully.`);
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
logError(`${this.xml_resource} dbus init failed!`, e);
|
||||
}
|
||||
}
|
||||
|
||||
async stop() {
|
||||
//@ts-ignore
|
||||
log(`Stopping ${this.xml_resource} dbus module`);
|
||||
|
||||
if (this.connected && this.proxy != undefined) {
|
||||
this.proxy.run_dispose();
|
||||
this.proxy = undefined;
|
||||
this.connected = false;
|
||||
}
|
||||
}
|
||||
|
||||
isRunning(): boolean {
|
||||
return this.connected;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import * as Main from "resource:///org/gnome/shell/ui/main.js";
|
||||
import { QuickToggle } from "resource:///org/gnome/shell/ui/quickSettings.js";
|
||||
|
||||
export function addQuickSettingsItems(items: [typeof QuickToggle], width = 1) {
|
||||
const QuickSettingsMenu = Main.panel.statusArea.quickSettings;
|
||||
items.forEach((item) => QuickSettingsMenu.menu.addItem(item, width));
|
||||
// Ensure the tile(s) are above the background apps menu
|
||||
for (const item of items) {
|
||||
QuickSettingsMenu.menu._grid.set_child_below_sibling(
|
||||
item,
|
||||
QuickSettingsMenu._backgroundApps.quickSettingsItems[0],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
import { addQuickSettingsItems } from "../helpers";
|
||||
import { AuraDbus } from "../dbus/aura";
|
||||
import { AuraEffect, AuraModeNum } from "../../bindings/aura";
|
||||
import GObject from "gi://GObject";
|
||||
|
||||
import * as PopupMenu from "resource:///org/gnome/shell/ui/popupMenu.js";
|
||||
import * as QuickSettings from "resource:///org/gnome/shell/ui/quickSettings.js";
|
||||
|
||||
export const AuraMenuToggle = GObject.registerClass(
|
||||
class AuraMenuToggle extends QuickSettings.QuickMenuToggle {
|
||||
private _dbus_aura: AuraDbus;
|
||||
private _last_mode: AuraModeNum = AuraModeNum.Static;
|
||||
|
||||
constructor(dbus_aura: AuraDbus) {
|
||||
super({
|
||||
title: "Aura Modes",
|
||||
iconName: "selection-mode-symbolic",
|
||||
toggleMode: true,
|
||||
});
|
||||
this._dbus_aura = dbus_aura;
|
||||
|
||||
this.connectObject(this);
|
||||
|
||||
this.menu.setHeader("selection-mode-symbolic", this._dbus_aura.current_aura_mode);
|
||||
|
||||
this._itemsSection = new PopupMenu.PopupMenuSection();
|
||||
|
||||
this._dbus_aura.aura_modes.forEach((mode, key) => {
|
||||
this._itemsSection.addAction(
|
||||
key,
|
||||
() => {
|
||||
this._dbus_aura.setLedMode(mode);
|
||||
this.sync();
|
||||
},
|
||||
"",
|
||||
);
|
||||
});
|
||||
|
||||
this.menu.addMenuItem(this._itemsSection);
|
||||
|
||||
// Add an entry-point for more settings
|
||||
// this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
// const settingsItem = this.menu.addAction("More Settings",
|
||||
// () => ExtensionUtils.openPrefs());
|
||||
// // Ensure the settings are unavailable when the screen is locked
|
||||
// settingsItem.visible = Main.sessionMode.allowSettings;
|
||||
// this.menu._settingsActions[Me.uuid] = settingsItem;
|
||||
|
||||
this.connectObject(
|
||||
"clicked",
|
||||
() => {
|
||||
let mode: AuraEffect | undefined;
|
||||
if (this._dbus_aura.current_aura_mode == AuraModeNum.Static) {
|
||||
mode = this._dbus_aura.aura_modes.get(this._last_mode);
|
||||
} else {
|
||||
mode = this._dbus_aura.aura_modes.get(AuraModeNum.Static);
|
||||
}
|
||||
if (mode != undefined) {
|
||||
this._dbus_aura.setLedMode(mode);
|
||||
this.sync();
|
||||
}
|
||||
},
|
||||
this,
|
||||
);
|
||||
|
||||
this._dbus_aura.notifyAuraModeSubscribers.push(this);
|
||||
this.sync();
|
||||
|
||||
addQuickSettingsItems([this]);
|
||||
}
|
||||
|
||||
sync() {
|
||||
const checked = this._dbus_aura.current_aura_mode != AuraModeNum.Static;
|
||||
this.title = this._dbus_aura.current_aura_mode;
|
||||
if (
|
||||
this._last_mode != this._dbus_aura.current_aura_mode &&
|
||||
this._dbus_aura.current_aura_mode != AuraModeNum.Static
|
||||
) {
|
||||
this._last_mode = this._dbus_aura.current_aura_mode;
|
||||
}
|
||||
|
||||
if (this.checked !== checked) this.set({ checked });
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -1,216 +0,0 @@
|
||||
import { Extension, gettext as _ } from "@girs/gnome-shell/extensions/extension";
|
||||
import { quickSettings, popupMenu } from "@girs/gnome-shell/ui";
|
||||
import { GObject } from "@girs/gobject-2.0";
|
||||
|
||||
import { DbusBase } from "../../modules/dbus_proxy";
|
||||
|
||||
import { addQuickSettingsItems } from "../helpers";
|
||||
|
||||
import * as AsusExtension from "../../extension";
|
||||
import * as platform from "../../bindings/platform";
|
||||
import { uuid } from "../../extension";
|
||||
import { AsusMenuToggle } from "../rog_menu_toggle";
|
||||
|
||||
export const FeatureMenuToggle = GObject.registerClass(
|
||||
class FeatureMenuToggle extends quickSettings.QuickMenuToggle {
|
||||
private dbus_platform: DbusBase;
|
||||
private dbus_anime: DbusBase;
|
||||
private last_selection = "mini-led";
|
||||
private supported_properties!: platform.Properties;
|
||||
private supported_interfaces: string[] = [];
|
||||
|
||||
private miniLed?: typeof AsusMenuToggle;
|
||||
private panelOd?: typeof AsusMenuToggle;
|
||||
private animeDisplayPower?: typeof AsusMenuToggle;
|
||||
private animePowersaveAnim?: typeof AsusMenuToggle;
|
||||
_itemsSection: popupMenu.PopupMenuSection;
|
||||
|
||||
constructor(dbus_platform: DbusBase, dbus_anime: DbusBase) {
|
||||
super({
|
||||
label: "Laptop",
|
||||
toggle_mode: true,
|
||||
icon_name: "selection-mode-symbolic",
|
||||
});
|
||||
this.label = "Laptop";
|
||||
this.title = "Laptop";
|
||||
this.dbus_platform = dbus_platform;
|
||||
this.dbus_anime = dbus_anime;
|
||||
|
||||
this.menu.setHeader("selection-mode-symbolic", "Laptop features");
|
||||
|
||||
this.last_selection = Extension.lookupByUUID(AsusExtension.uuid)
|
||||
?.getSettings()
|
||||
.get_string("primary-quickmenu-toggle")!;
|
||||
|
||||
this.supported_interfaces = this.dbus_platform?.proxy.SupportedInterfacesSync()[0];
|
||||
this.supported_properties = this.dbus_platform?.proxy.SupportedPropertiesSync()[0];
|
||||
|
||||
// TODO: temporary block
|
||||
if (this.last_selection == "mini-led" && !this.supported_properties.includes("MiniLed")) {
|
||||
this.last_selection = "panel-od";
|
||||
} else if (
|
||||
this.last_selection == "panel-od" &&
|
||||
!this.supported_properties.includes("PanelOd")
|
||||
) {
|
||||
this.last_selection = "anime-power";
|
||||
} else if (
|
||||
this.last_selection == "anime-power" &&
|
||||
!this.supported_interfaces.includes("Anime")
|
||||
) {
|
||||
this.last_selection = "mini-led";
|
||||
} else if (this.last_selection.length == 0) {
|
||||
this.last_selection = "panel-od";
|
||||
}
|
||||
|
||||
// AsusExtension.extension._settings.connect('changed::primary-quickmenu-toggle', this.sync);
|
||||
Extension.lookupByUUID(uuid)
|
||||
?.getSettings()
|
||||
.set_string("primary-quickmenu-toggle", this.last_selection);
|
||||
|
||||
this._itemsSection = new popupMenu.PopupMenuSection();
|
||||
if (this.supported_properties.includes("MiniLed")) {
|
||||
if (this.miniLed == null) {
|
||||
this.miniLed = new AsusMenuToggle(
|
||||
this.dbus_platform,
|
||||
"MiniLed",
|
||||
"mini-led-enabled",
|
||||
"Mini-LED Enabled",
|
||||
);
|
||||
this._itemsSection.addMenuItem(this.miniLed, 0);
|
||||
this.miniLed.toggle_callback = () => {
|
||||
this.last_selection = "mini-led";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (this.supported_properties.includes("PanelOd")) {
|
||||
if (this.panelOd == null) {
|
||||
this.panelOd = new AsusMenuToggle(
|
||||
this.dbus_platform,
|
||||
"PanelOd",
|
||||
"panel-od-enabled",
|
||||
"Panel Overdrive Enabled",
|
||||
);
|
||||
this._itemsSection.addMenuItem(this.panelOd, 1);
|
||||
this.panelOd.toggle_callback = () => {
|
||||
this.last_selection = "panel-od";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (this.supported_interfaces.includes("Anime")) {
|
||||
if (this.animeDisplayPower == null) {
|
||||
this.animeDisplayPower = new AsusMenuToggle(
|
||||
this.dbus_anime,
|
||||
"EnableDisplay",
|
||||
"anime-power",
|
||||
"AniMe Display Enabled",
|
||||
);
|
||||
this._itemsSection.addMenuItem(this.animeDisplayPower, 2);
|
||||
this.animeDisplayPower.toggle_callback = () => {
|
||||
this.last_selection = "anime-power";
|
||||
};
|
||||
}
|
||||
|
||||
if (this.animePowersaveAnim == null) {
|
||||
this.animePowersaveAnim = new AsusMenuToggle(
|
||||
this.dbus_anime,
|
||||
"BuiltinsEnabled",
|
||||
"anime-builtins",
|
||||
"AniMe Built-in Animations",
|
||||
);
|
||||
this._itemsSection.addMenuItem(this.animePowersaveAnim, 3);
|
||||
this.animePowersaveAnim.toggle_callback = () => {
|
||||
this.last_selection = "anime-builtins";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
this.connectObject(
|
||||
"clicked",
|
||||
() => {
|
||||
this._toggle();
|
||||
},
|
||||
this,
|
||||
);
|
||||
|
||||
this.menu.addMenuItem(this._itemsSection, 0);
|
||||
|
||||
this.dbus_platform?.proxy.connect("g-properties-changed", (_proxy, changed, invalidated) => {
|
||||
//const properties = changed.deepUnpack();
|
||||
this.sync();
|
||||
});
|
||||
|
||||
this.dbus_anime?.proxy.connect("g-properties-changed", (_proxy, changed, invalidated) => {
|
||||
//const properties = changed.deepUnpack();
|
||||
this.sync();
|
||||
});
|
||||
|
||||
// // Add an entry-point for more extension._settings
|
||||
// this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
// const settingsItem = this.menu.addAction("More Settings",
|
||||
// () => ExtensionUtils.openPrefs());
|
||||
// // Ensure the extension._settings are unavailable when the screen is locked
|
||||
// settingsItem.visible = Main.sessionMode.allowSettings;
|
||||
// this.menu._settingsActions[Me.uuid] = settingsItem;
|
||||
|
||||
this.sync();
|
||||
addQuickSettingsItems([this]);
|
||||
}
|
||||
|
||||
_toggle() {
|
||||
if (this.last_selection == "mini-led" && this.miniLed != null) {
|
||||
if (this.checked !== this.dbus_platform.proxy.MiniLed)
|
||||
this.dbus_platform.proxy.MiniLed = this.checked;
|
||||
}
|
||||
|
||||
if (this.last_selection == "panel-od" && this.panelOd != null) {
|
||||
if (this.checked !== this.dbus_platform.proxy.PanelOd) {
|
||||
this.dbus_platform.proxy.PanelOd = this.checked;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.last_selection == "anime-power" && this.animeDisplayPower != null) {
|
||||
if (this.checked !== this.dbus_anime.proxy.EnableDisplay)
|
||||
this.dbus_anime.proxy.EnableDisplay = this.checked;
|
||||
}
|
||||
|
||||
if (this.last_selection == "anime-builtins" && this.animePowersaveAnim != null) {
|
||||
if (this.checked !== this.dbus_anime.proxy.BuiltinsEnabled)
|
||||
this.dbus_anime.proxy.BuiltinsEnabled = this.checked;
|
||||
}
|
||||
}
|
||||
|
||||
sync() {
|
||||
let checked = false;
|
||||
if (this.last_selection == "mini-led" && this.miniLed != null) {
|
||||
this.title = this.miniLed.title;
|
||||
checked = this.dbus_platform.proxy.MiniLed;
|
||||
}
|
||||
|
||||
if (this.last_selection == "panel-od" && this.panelOd != null) {
|
||||
this.title = this.panelOd.title;
|
||||
checked = this.dbus_platform.proxy.PanelOd;
|
||||
}
|
||||
|
||||
if (this.last_selection == "anime-power" && this.animeDisplayPower != null) {
|
||||
this.title = this.animeDisplayPower.title;
|
||||
checked = this.dbus_anime.proxy.EnableDisplay;
|
||||
}
|
||||
|
||||
if (this.last_selection == "anime-builtins" && this.animePowersaveAnim != null) {
|
||||
this.title = this.animePowersaveAnim.title;
|
||||
checked = this.dbus_anime.proxy.BuiltinsEnabled;
|
||||
}
|
||||
|
||||
// if (this.animePowersaveAnim != null) {
|
||||
// }
|
||||
|
||||
if (this.checked !== checked) this.set({ checked });
|
||||
}
|
||||
|
||||
destroy() {
|
||||
// this.panelOd?.destroy();
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Extension, gettext as _ } from "@girs/gnome-shell/extensions/extension";
|
||||
import { quickSettings, main } from "@girs/gnome-shell/ui";
|
||||
import { Gio } from "@girs/gio-2.0";
|
||||
import { GObject } from "@girs/gobject-2.0";
|
||||
import { uuid } from "../extension";
|
||||
//import { DbusBase } from '../dbus_proxy';
|
||||
|
||||
export const AsusIndicator = GObject.registerClass(
|
||||
class AsusIndicator extends quickSettings.SystemIndicator {
|
||||
private _indicator: any;
|
||||
private _settings: Gio.Settings | undefined;
|
||||
|
||||
constructor(icon_name: string, setting_name: string) {
|
||||
super();
|
||||
// Create an icon for the indicator
|
||||
this._indicator = this._addIndicator();
|
||||
this._indicator.icon_name = icon_name;
|
||||
|
||||
// Showing an indicator when the feature is enabled
|
||||
this._settings = Extension.lookupByUUID(uuid)?.getSettings();
|
||||
this._settings?.bind(setting_name, this._indicator, "visible", Gio.SettingsBindFlags.DEFAULT);
|
||||
|
||||
main.panel.statusArea.quickSettings.addExternalIndicator(this);
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -1,50 +0,0 @@
|
||||
import { popupMenu } from "@girs/gnome-shell/ui";
|
||||
import { GObject } from "@girs/gobject-2.0";
|
||||
import { DbusBase } from "./dbus_proxy";
|
||||
|
||||
export const AsusMenuToggle = GObject.registerClass(
|
||||
class AsusMenuToggle extends popupMenu.PopupSwitchMenuItem {
|
||||
public title: string = "";
|
||||
dbus!: DbusBase;
|
||||
prop_name: string = "";
|
||||
public toggle_callback = () => {};
|
||||
|
||||
constructor(dbus: DbusBase, prop_name: string, setting: string, title: string) {
|
||||
super(title, true);
|
||||
this.prop_name = prop_name;
|
||||
this.dbus = dbus;
|
||||
this.title = title;
|
||||
|
||||
this.dbus?.proxy.connect("g-properties-changed", (_proxy, changed, invalidated) => {
|
||||
const properties = changed.deepUnpack();
|
||||
// .find() fails on some shit for some reason
|
||||
for (const v of Object.entries(properties)) {
|
||||
if (v[0] == this.prop_name) {
|
||||
this.sync();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.connectObject("toggled", () => this._toggleMode(), this);
|
||||
|
||||
this.connect("destroy", () => {
|
||||
this.destroy();
|
||||
});
|
||||
|
||||
this.sync();
|
||||
}
|
||||
|
||||
_toggleMode() {
|
||||
// hacky shit, index to get base object property and set it
|
||||
const state = this.dbus.proxy[this.prop_name];
|
||||
if (this.state !== state) this.dbus.proxy[this.prop_name] = this.state;
|
||||
this.toggle_callback();
|
||||
}
|
||||
|
||||
sync() {
|
||||
const state = this.dbus.proxy[this.prop_name];
|
||||
if (this.state !== state) this.setToggleState(state);
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -1,64 +0,0 @@
|
||||
import { Extension, gettext as _ } from "@girs/gnome-shell/extensions/extension";
|
||||
import { addQuickSettingsItems } from "./helpers";
|
||||
import { quickSettings } from "@girs/gnome-shell/ui";
|
||||
import { Gio } from "@girs/gio-2.0";
|
||||
import { GObject } from "@girs/gobject-2.0";
|
||||
import { uuid } from "../extension";
|
||||
import { DbusBase } from "./dbus_proxy";
|
||||
|
||||
export const AsusQuickToggle = GObject.registerClass(
|
||||
class AsusQuickToggle extends quickSettings.QuickToggle {
|
||||
dbus!: DbusBase;
|
||||
prop_name: string = "";
|
||||
public toggle_callback = () => {};
|
||||
|
||||
constructor(dbus: DbusBase, prop_name: string, setting: string, title: string) {
|
||||
super({
|
||||
label: title,
|
||||
icon_name: "selection-mode-symbolic",
|
||||
toggle_mode: true,
|
||||
});
|
||||
this.prop_name = prop_name;
|
||||
this.label = title;
|
||||
this.dbus = dbus;
|
||||
|
||||
this.dbus?.proxy.connect("g-properties-changed", (_proxy, changed, invalidated) => {
|
||||
const properties = changed.deepUnpack();
|
||||
// .find() fails on some shit for some reason
|
||||
for (const v of Object.entries(properties)) {
|
||||
if (v[0] == this.prop_name) {
|
||||
const checked = v[1].unpack();
|
||||
if (this.checked !== checked) this.checked = checked;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.connectObject("clicked", () => this._toggleMode(), this);
|
||||
|
||||
this.connect("destroy", () => {
|
||||
this.destroy();
|
||||
});
|
||||
|
||||
Extension.lookupByUUID(uuid)
|
||||
?.getSettings()
|
||||
.bind(setting, this, "checked", Gio.SettingsBindFlags.DEFAULT);
|
||||
|
||||
this.sync();
|
||||
|
||||
addQuickSettingsItems([this]);
|
||||
}
|
||||
|
||||
_toggleMode() {
|
||||
// hacky shit, index to get base object property and set it
|
||||
const checked = this.dbus.proxy[this.prop_name];
|
||||
if (this.checked !== checked) this.dbus.proxy[this.prop_name] = this.checked;
|
||||
this.toggle_callback();
|
||||
}
|
||||
|
||||
sync() {
|
||||
const checked = this.dbus.proxy[this.prop_name];
|
||||
if (this.checked !== checked) this.set({ checked });
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -1,75 +0,0 @@
|
||||
import { Extension, gettext as _ } from "@girs/gnome-shell/extensions/extension";
|
||||
import { addQuickSettingsItems } from "./helpers";
|
||||
import { quickSettings } from "@girs/gnome-shell/ui";
|
||||
import { Gio } from "@girs/gio-2.0";
|
||||
import { GObject } from "@girs/gobject-2.0";
|
||||
import { uuid } from "../extension";
|
||||
import { DbusBase } from "./dbus_proxy";
|
||||
|
||||
export const AsusSlider = GObject.registerClass(
|
||||
class AsusSlider extends quickSettings.QuickSlider {
|
||||
private dbus: DbusBase;
|
||||
private settings: any = undefined;
|
||||
private setting = "";
|
||||
private prop_name = "";
|
||||
|
||||
constructor(dbus: DbusBase, prop_name: string, setting: string, title: string) {
|
||||
super({
|
||||
label: title,
|
||||
icon_name: "selection-mode-symbolic",
|
||||
});
|
||||
this.label = title;
|
||||
this.dbus = dbus;
|
||||
this.setting = setting;
|
||||
this.prop_name = prop_name;
|
||||
this.settings = Extension.lookupByUUID(uuid)?.getSettings();
|
||||
|
||||
this._sliderChangedId = this.slider.connect("drag-end", this._onSliderChanged.bind(this));
|
||||
|
||||
// Binding the slider to a GSettings key
|
||||
|
||||
this.settings.connect(`changed::${this.setting}`, this._onSettingsChanged.bind(this));
|
||||
|
||||
// Set an accessible name for the slider
|
||||
this.slider.accessible_name = title;
|
||||
|
||||
this.dbus?.proxy.connect("g-properties-changed", (_proxy, changed, invalidated) => {
|
||||
const properties = changed.deepUnpack();
|
||||
// .find() fails on some shit for some reason
|
||||
for (const v of Object.entries(properties)) {
|
||||
if (v[0] == this.prop_name) {
|
||||
const checked = v[1].unpack();
|
||||
this._sync();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._sync();
|
||||
this._onSettingsChanged();
|
||||
|
||||
addQuickSettingsItems([this], 2);
|
||||
}
|
||||
|
||||
_onSettingsChanged() {
|
||||
// Prevent the slider from emitting a change signal while being updated
|
||||
this.slider.block_signal_handler(this._sliderChangedId);
|
||||
this.slider.value = this.settings.get_uint(this.setting) / 100.0;
|
||||
this.slider.unblock_signal_handler(this._sliderChangedId);
|
||||
}
|
||||
|
||||
_onSliderChanged() {
|
||||
// Assuming our GSettings holds values between 0..100, adjust for the
|
||||
// slider taking values between 0..1
|
||||
const percent = Math.floor(this.slider.value * 100);
|
||||
const stored = Math.floor(this.settings.get_uint(this.setting) / 100.0);
|
||||
if (this.slider.value !== stored) this.dbus.proxy[this.prop_name] = percent;
|
||||
this.settings.set_uint(this.setting, percent);
|
||||
}
|
||||
|
||||
_sync() {
|
||||
const value = this.dbus.proxy[this.prop_name];
|
||||
if (this.slider.value !== value / 100) this.settings.set_uint(this.setting, value);
|
||||
}
|
||||
},
|
||||
);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user