Compare commits

...

34 Commits
3.2.1 ... 3.4.0

Author SHA1 Message Date
Luke D Jones
6ebf0c2bb2 Update makefile 2021-04-11 20:39:15 +12:00
Luke D Jones
77c658c94e Release 3.4.0 2021-04-11 20:36:31 +12:00
Luke Jones
df64a51372 Merge branch 'fluke/anime-cli' into 'main'
anime: prep rog-anime for publish, rename *all* AniMe~ to Anime

See merge request asus-linux/asusctl!50
2021-04-11 06:54:05 +00:00
Luke D Jones
0657c6cc74 anime: prep rog-anime for publish, rename *all* AniMe~ to Anime 2021-04-11 16:26:52 +12:00
Luke Jones
f116905e85 Merge branch 'fluke/anime-cli' into 'main'
anime: add zbus methods

See merge request asus-linux/asus-nb-ctrl!49
2021-04-10 09:58:56 +00:00
Luke D Jones
e515741efa anime: add zbus methods 2021-04-10 21:54:08 +12:00
Luke Jones
d516abdc92 Merge branch 'fluke/anime-cli' into 'main'
anime: tweak gif animation time types

See merge request asus-linux/asus-nb-ctrl!48
2021-04-09 11:21:35 +00:00
Luke D Jones
ece565de1c anime: tweak gif animation time types 2021-04-09 23:17:50 +12:00
Luke Jones
eb83d1a835 Merge branch 'fluke/anime-cli' into 'main'
anime: CLI and user-daemon work

See merge request asus-linux/asus-nb-ctrl!47
2021-04-09 08:45:06 +00:00
Luke D Jones
7d0f15d738 anime: CLI and user-daemon work 2021-04-09 20:41:25 +12:00
Luke Jones
8010da0891 Merge branch 'fluke/anime-cli' into 'main'
Fluke/anime cli

See merge request asus-linux/asus-nb-ctrl!46
2021-04-07 07:28:35 +00:00
Luke D Jones
aa500c35c4 daemon: revert zbus to 1.9.1 in daemon 2021-04-07 15:05:55 +12:00
Luke D Jones
2af33a0416 daemon: revert zbus to 1.9.1 in daemon 2021-04-07 15:05:26 +12:00
Luke D Jones
9b4ed6eb62 anime: discard frames if specified 2021-04-06 22:03:06 +12:00
Luke D Jones
47c1ca9fe4 anime: gif-image 2021-04-06 21:43:57 +12:00
Luke Jones
3cd624daf0 Merge branch 'fluke/anime-cli' into 'main'
Anime: Tweak the diagonal data to be more correct

See merge request asus-linux/asus-nb-ctrl!45
2021-04-06 02:01:41 +00:00
Luke D Jones
fa16864a3e Tweak the diagonal to be more correct 2021-04-06 13:56:02 +12:00
Luke Jones
bfc31b06d5 Merge branch 'fluke/anime-diag' into 'main'
Fluke/anime diag

See merge request asus-linux/asus-nb-ctrl!44
2021-04-05 09:12:04 +00:00
Luke D Jones
d854f7da1b Prepare for user saving of anime sequences 2021-04-05 21:06:53 +12:00
Luke D Jones
6d746b21a5 Anime gifs 2021-04-05 17:12:00 +12:00
Luke D Jones
226c083a51 Diagonal data structure 2021-04-05 00:02:05 +12:00
Luke Jones
de59d00949 Merge branch 'fluke/gfx-zbus-cleanup' into 'main'
Fluke/gfx zbus cleanup (incorrectly named branch)

Closes #72

See merge request asus-linux/asus-nb-ctrl!42
2021-04-03 08:46:17 +00:00
Luke D Jones
7ff01f12e9 Add extra models to ledmodes
- Configurable anime example
- Gfx power states as enum

Closes #72
2021-04-03 21:42:39 +13:00
Luke Jones
fbc248177a Merge branch 'fluke/gfx-vfio-optional' into 'main'
Put vfio behind config option

See merge request asus-linux/asus-nb-ctrl!43
2021-04-02 23:54:36 +00:00
Luke D Jones
fc3d7653f5 Add missing if condition for vfio 2021-04-03 12:50:13 +13:00
Luke D Jones
2dc70ea6af Put vfio behind config option 2021-04-03 09:59:36 +13:00
Luke D Jones
01345b28a5 Add extra models to ledmodes 2021-03-29 19:36:30 +13:00
Luke Jones
4eeacea832 Merge branch 'fluke/vfio' into 'main'
Bugfix vfio/integrated

See merge request asus-linux/asus-nb-ctrl!41
2021-03-24 23:04:23 +00:00
Luke D Jones
6bf0fdd117 Bugfix vfio/integrated 2021-03-25 11:14:59 +13:00
Luke Jones
7fcde7df17 Merge branch 'fluke/vfio-builtin' into 'main'
Fluke/vfio builtin

See merge request asus-linux/asus-nb-ctrl!40
2021-03-24 06:45:35 +00:00
Luke D Jones
543b0b817f Try remove nouveau 2021-03-24 19:44:40 +13:00
Luke D Jones
5a7d31fdf6 Bugfixes to session handler. Add extra profile commands
- Better handling of session tracking
- List all profile data
- Get active profile name
- Get active profile data
2021-03-24 16:30:13 +13:00
Luke D Jones
301c532b65 Formatting 2021-03-23 13:45:57 +13:00
Luke D Jones
df7ae4d014 Fix: non-rgb keyboard backlight control 2021-03-23 13:44:07 +13:00
118 changed files with 6547 additions and 1331 deletions

View File

@@ -6,6 +6,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
# [3.4.0] - 2021-04-11
### Changed
- Revert zbus to 1.9.1
- Use enum to show power states, and catch missing pci path for nvidia.
- Partial user-daemon for anime/per-key done, `asusd-user`. Includes asusd-user systemd unit.
- user-daemon provides dbus emthods to insert anime actions, remove from index, set leds on/off
+ COnfig file is stored in `~/.config/rog/rog-user.cfg`
- AniMe display parts split out to individual crate in preparation for publishing
on crates.io
# [3.3.0] - 2021-04-3
### Changed
- Add ledmodes for G733QS
- Add ledmodes for GA401Q
- Default to vfio disabled in configuration. Will now hard-error if enabled and
the kernel modules are builtin. To enable vfio switching `"gfx_vfio_enable": false,`
must be changed to `true` in `/etc/asusd/asusd.conf`
# [3.2.4] - 2021-03-24
### Changed
- Ignore vfio-builtin error if switching to integrated
# [3.2.3] - 2021-03-24
### Changed
- Better handling of session tracking
### Added
- List all profile data
- Get active profile name
- Get active profile data
# [3.2.2] - 2021-03-23
### Changed
- Fix brightness control, again, for non-RGB keyboards
# [3.2.1] - 2021-03-21
### Changed
- Fix brightness control

419
Cargo.lock generated
View File

@@ -1,5 +1,13 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aho-corasick"
version = "0.7.15"
@@ -28,15 +36,19 @@ dependencies = [
"daemon",
"notify-rust",
"rog_dbus",
"rog_types",
"serde_json",
]
[[package]]
name = "asusctl"
version = "3.1.4"
version = "3.4.0"
dependencies = [
"daemon",
"gif",
"glam",
"gumdrop",
"rog_anime",
"rog_dbus",
"rog_types",
"serde_json",
@@ -64,15 +76,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "async-lock"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1996609732bde4a9988bc42125f55f2af5f3c36370e27c778d5191a4a1b63bfb"
dependencies = [
"event-listener",
]
[[package]]
name = "atty"
version = "0.2.14"
@@ -168,6 +171,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "concurrent-queue"
version = "1.2.2"
@@ -196,12 +205,13 @@ dependencies = [
[[package]]
name = "daemon"
version = "3.2.1"
version = "3.4.0"
dependencies = [
"env_logger",
"intel-pstate",
"log",
"logind-zbus",
"rog_anime",
"rog_dbus",
"rog_fan_curve",
"rog_types",
@@ -212,10 +222,26 @@ dependencies = [
"sysfs-class",
"toml",
"udev",
"zbus 2.0.0-beta.3",
"zbus",
"zvariant",
]
[[package]]
name = "daemon-user"
version = "1.0.0"
dependencies = [
"dirs 3.0.1",
"rog_anime",
"rog_dbus",
"rog_types",
"serde",
"serde_derive",
"serde_json",
"zbus",
"zvariant",
"zvariant_derive",
]
[[package]]
name = "derivative"
version = "2.2.0"
@@ -224,7 +250,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
@@ -238,6 +264,26 @@ dependencies = [
"winapi",
]
[[package]]
name = "dirs"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]]
name = "enumflags2"
version = "0.6.4"
@@ -256,7 +302,7 @@ checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
@@ -282,16 +328,10 @@ dependencies = [
"proc-macro2",
"quote 1.0.9",
"rustversion",
"syn 1.0.64",
"syn 1.0.69",
"synstructure",
]
[[package]]
name = "event-listener"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59"
[[package]]
name = "fastrand"
version = "1.4.0"
@@ -303,9 +343,9 @@ dependencies = [
[[package]]
name = "futures"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1"
checksum = "a9d5813545e459ad3ca1bff9915e9ad7f1a47dc6a91b627ce321d5863b7dd253"
dependencies = [
"futures-channel",
"futures-core",
@@ -318,9 +358,9 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939"
checksum = "ce79c6a52a299137a6013061e0cf0e688fce5d7f1bc60125f520912fdb29ec25"
dependencies = [
"futures-core",
"futures-sink",
@@ -328,15 +368,15 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94"
checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815"
[[package]]
name = "futures-executor"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "891a4b7b96d84d5940084b2a37632dd65deeae662c114ceaa2c879629c9c0ad1"
checksum = "10f6cb7042eda00f0049b1d2080aa4b93442997ee507eb3828e8bd7577f94c9d"
dependencies = [
"futures-core",
"futures-task",
@@ -345,9 +385,9 @@ dependencies = [
[[package]]
name = "futures-io"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59"
checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04"
[[package]]
name = "futures-lite"
@@ -366,33 +406,33 @@ dependencies = [
[[package]]
name = "futures-macro"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7"
checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b"
dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
name = "futures-sink"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3"
checksum = "5c5629433c555de3d82861a7a4e3794a4c40040390907cfbfd7143a92a426c23"
[[package]]
name = "futures-task"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80"
checksum = "ba7aa51095076f3ba6d9a1f702f74bd05ec65f555d70d2033d55ba8d69f581bc"
[[package]]
name = "futures-util"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1"
checksum = "3c144ad54d60f23927f0a6b6d816e4271278b64f005ad65e4e35291d2de9c025"
dependencies = [
"futures-channel",
"futures-core",
@@ -420,14 +460,22 @@ dependencies = [
]
[[package]]
name = "getrandom"
version = "0.2.2"
name = "gif"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
checksum = "5a668f699973d0f573d15749b7002a9ac9e1f9c6b220e7b165601334c173d8de"
dependencies = [
"cfg-if 1.0.0",
"libc",
"wasi 0.10.2+wasi-snapshot-preview1",
"color_quant",
"weezl",
]
[[package]]
name = "glam"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "333928d5eb103c5d4050533cec0384302db6be8ef7d3cebd30ec6a35350353da"
dependencies = [
"serde",
]
[[package]]
@@ -447,7 +495,7 @@ checksum = "915ef07c710d84733522461de2a734d4d62a3fd39a4d4f404c2f385ef8618d05"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
@@ -459,12 +507,6 @@ dependencies = [
"libc",
]
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "humantime"
version = "2.1.0"
@@ -504,9 +546,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.90"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba4aede83fc3617411dc6993bc8c70919750c1c257c6ca6a502aed6e0e2394ae"
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
[[package]]
name = "libudev-sys"
@@ -541,14 +583,14 @@ dependencies = [
[[package]]
name = "logind-zbus"
version = "0.6.1"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6671f6cf88b63e9d63009f4f6f8826fcc265c51a9977e4c7dc1263d743e0dfbb"
checksum = "dca64bea11e365933e0c4a1a9342f0122d1d2822a09c87dab0e1d314adb353a2"
dependencies = [
"serde",
"serde_json",
"zbus 2.0.0-beta.3",
"zbus_macros 2.0.0-beta.3",
"zbus",
"zbus_macros",
"zvariant",
"zvariant_derive",
]
@@ -561,7 +603,7 @@ checksum = "3dfb6b71a9a89cd38b395d994214297447e8e63b1ba5708a9a2b0b1048ceda76"
dependencies = [
"cc",
"chrono",
"dirs",
"dirs 1.0.5",
"objc-foundation",
]
@@ -581,10 +623,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
[[package]]
name = "nb-connect"
version = "1.0.3"
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670361df1bc2399ee1ff50406a0d422587dd3bb0da596e1978fe8e05dabddf4f"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
"adler",
"autocfg",
]
[[package]]
name = "nb-connect"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19900e7eee95eb2b3c2e26d12a874cc80aaf750e31be6fcbe743ead369fa45d"
dependencies = [
"libc",
"socket2",
@@ -603,18 +655,6 @@ dependencies = [
"void",
]
[[package]]
name = "nix"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ccba0cfe4fdf15982d1674c69b1fd80bad427d293849982668dfe454bd61f2"
dependencies = [
"bitflags 1.2.1",
"cc",
"cfg-if 1.0.0",
"libc",
]
[[package]]
name = "nom"
version = "5.1.2"
@@ -634,7 +674,7 @@ dependencies = [
"mac-notification-sys",
"serde",
"winrt-notification",
"zbus 1.8.0",
"zbus",
"zvariant",
"zvariant_derive",
]
@@ -717,6 +757,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pix"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bea9d5c668f13b4a1b97d848780e00cfabf76eb83538129c264c0c6d6a968047"
[[package]]
name = "pkg-config"
version = "0.3.19"
@@ -724,24 +770,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
[[package]]
name = "polling"
version = "2.0.2"
name = "png_pong"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2a7bc6b2a29e632e45451c941832803a18cce6781db04de8a04696cdca8bde4"
checksum = "75851150081bd473079e03e2fa00e25557bcb19706e502b095ca71ce392b70ff"
dependencies = [
"cfg-if 0.1.10",
"miniz_oxide",
"pix",
]
[[package]]
name = "polling"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fc12d774e799ee9ebae13f4076ca003b40d18a11ac0f3641e6f899618580b7b"
dependencies = [
"cfg-if 1.0.0",
"libc",
"log",
"wepoll-sys",
"winapi",
]
[[package]]
name = "ppv-lite86"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "proc-macro-crate"
version = "0.1.5"
@@ -760,7 +810,7 @@ dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
"version_check",
]
@@ -789,9 +839,9 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
[[package]]
name = "proc-macro2"
version = "1.0.24"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
dependencies = [
"unicode-xid 0.2.1",
]
@@ -811,46 +861,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
dependencies = [
"getrandom 0.2.2",
]
[[package]]
name = "rand_hc"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
dependencies = [
"rand_core",
]
[[package]]
name = "redox_syscall"
version = "0.1.57"
@@ -863,7 +873,7 @@ version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
dependencies = [
"getrandom 0.1.16",
"getrandom",
"redox_syscall",
"rust-argon2",
]
@@ -886,14 +896,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
[[package]]
name = "rog_dbus"
version = "3.0.0"
name = "rog_anime"
version = "1.0.2"
dependencies = [
"gif",
"glam",
"pix",
"png_pong",
"serde",
"serde_derive",
"zbus",
"zvariant",
"zvariant_derive",
]
[[package]]
name = "rog_dbus"
version = "3.2.0"
dependencies = [
"rog_anime",
"rog_fan_curve",
"rog_types",
"serde_json",
"zbus 1.8.0",
"zbus_macros 1.8.0",
"zbus",
"zbus_macros",
"zvariant",
]
@@ -908,7 +934,7 @@ dependencies = [
[[package]]
name = "rog_types"
version = "3.1.1"
version = "3.2.0"
dependencies = [
"gumdrop",
"rog_fan_curve",
@@ -920,9 +946,9 @@ dependencies = [
[[package]]
name = "rusb"
version = "0.7.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c470dc7dc6e4710b6f85e9c4aa4650bc742260b39a36328180578db76fa258c1"
checksum = "12f3264859095257507e4c011ab420ff9b2d9cc3349c6c08a1d3a019260bb437"
dependencies = [
"libc",
"libusb1-sys",
@@ -960,22 +986,22 @@ checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
[[package]]
name = "serde"
version = "1.0.124"
version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f"
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.124"
version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b"
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
@@ -997,30 +1023,15 @@ checksum = "2dc6b7951b17b051f3210b063f12cc17320e2fe30ae05b0fe2a3abb068551c76"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
name = "sha1"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
[[package]]
name = "slab"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]]
name = "slotmap"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab3003725ae562cf995f3dc82bb99e70926e09000396816765bb6d7adbe740b1"
dependencies = [
"version_check",
]
[[package]]
name = "smart-default"
version = "0.6.0"
@@ -1029,16 +1040,15 @@ checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
name = "socket2"
version = "0.3.19"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2"
dependencies = [
"cfg-if 1.0.0",
"libc",
"winapi",
]
@@ -1072,9 +1082,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.64"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f"
checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb"
dependencies = [
"proc-macro2",
"quote 1.0.9",
@@ -1098,7 +1108,7 @@ checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
"unicode-xid 0.2.1",
]
@@ -1122,11 +1132,12 @@ dependencies = [
[[package]]
name = "time"
version = "0.1.43"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi",
]
@@ -1150,9 +1161,9 @@ dependencies = [
[[package]]
name = "udev"
version = "0.6.0"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "307c2b8c8a320a38365def5bb3ee92d146d405655196230f7a445fe4da6749f6"
checksum = "3193363f52bb34c6708ac2ffedcb5f7e5874f0329ef68e1315f27d8d768eb568"
dependencies = [
"libc",
"libudev-sys",
@@ -1179,9 +1190,9 @@ checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
[[package]]
name = "vec-arena"
version = "1.0.0"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eafc1b9b2dfc6f5529177b62cf806484db55b32dc7c9658a118e11bbeb33061d"
checksum = "34b2f665b594b07095e3ac3f718e13c2197143416fae4c5706cffb7b1af8d7f1"
[[package]]
name = "version_check"
@@ -1209,9 +1220,15 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "weezl"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a32b378380f4e9869b22f0b5177c68a5519f03b3454fde0b291455ddbae266c"
[[package]]
name = "wepoll-sys"
@@ -1295,9 +1312,9 @@ dependencies = [
[[package]]
name = "zbus"
version = "1.8.0"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40b4d4aa39daed4e32aed75f0c37b969184949a0fdfd5f2e1277abfda61f02a8"
checksum = "2326acc379a3ac4e34b794089f5bdb17086bf29a5fdf619b7b4cc772dc2e9dad"
dependencies = [
"async-io",
"byteorder",
@@ -1306,66 +1323,26 @@ dependencies = [
"fastrand",
"futures",
"nb-connect",
"nix 0.17.0",
"nix",
"once_cell",
"polling",
"scoped-tls",
"serde",
"serde_repr",
"zbus_macros 1.8.0",
"zvariant",
]
[[package]]
name = "zbus"
version = "2.0.0-beta.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e1e656194618d167524f97e88ff9bf87f2b1e8bf58f357b2a7abfdff8cc85c9"
dependencies = [
"async-io",
"async-lock",
"byteorder",
"derivative",
"enumflags2",
"event-listener",
"futures-core",
"futures-sink",
"futures-util",
"hex",
"nix 0.19.1",
"once_cell",
"rand",
"scoped-tls",
"serde",
"serde_repr",
"sha1",
"slotmap",
"zbus_macros 2.0.0-beta.3",
"zbus_macros",
"zvariant",
]
[[package]]
name = "zbus_macros"
version = "1.8.0"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc141cda72384bef359badf1808e391d3968f9299e8f3c3cbb78dafa1e0930"
checksum = "a482c56029e48681b89b92b5db3c446db0915e8dd1052c0328a574eda38d5f93"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.64",
]
[[package]]
name = "zbus_macros"
version = "2.0.0-beta.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcd4cb372bc2cade3f2323e4104112dceb6819f5dd9afa98515b4e821d232932"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
@@ -1389,5 +1366,5 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
]

View File

@@ -1,5 +1,5 @@
[workspace]
members = ["asusctl", "asus-notify", "daemon", "rog-types", "rog-dbus"]
members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-types", "rog-dbus", "rog-anime"]
[profile.release]
lto = true

View File

@@ -13,6 +13,7 @@ zshcpl = $(datarootdir)/zsh/site-functions
BIN_C := asusctl
BIN_D := asusd
BIN_U := asusd-user
BIN_N := asus-notify
LEDCFG := asusd-ledmodes.toml
X11CFG := 90-nvidia-screen-G05.conf
@@ -42,6 +43,7 @@ distclean:
install:
$(INSTALL_PROGRAM) "./target/release/$(BIN_C)" "$(DESTDIR)$(bindir)/$(BIN_C)"
$(INSTALL_PROGRAM) "./target/release/$(BIN_D)" "$(DESTDIR)$(bindir)/$(BIN_D)"
$(INSTALL_PROGRAM) "./target/release/$(BIN_U)" "$(DESTDIR)$(bindir)/$(BIN_U)"
$(INSTALL_PROGRAM) "./target/release/$(BIN_N)" "$(DESTDIR)$(bindir)/$(BIN_N)"
$(INSTALL_DATA) "./data/$(PMRULES)" "$(DESTDIR)$(libdir)/udev/rules.d/$(PMRULES)"
$(INSTALL_DATA) "./data/$(BIN_D).rules" "$(DESTDIR)$(libdir)/udev/rules.d/99-$(BIN_D).rules"
@@ -50,11 +52,13 @@ install:
$(INSTALL_DATA) "./data/$(X11CFG)" "$(DESTDIR)$(datarootdir)/X11/xorg.conf.d/$(X11CFG)"
$(INSTALL_DATA) "./data/$(BIN_D).service" "$(DESTDIR)$(libdir)/systemd/system/$(BIN_D).service"
$(INSTALL_DATA) "./data/$(BIN_N).service" "$(DESTDIR)$(libdir)/systemd/user/$(BIN_N).service"
$(INSTALL_DATA) "./data/$(BIN_U).service" "$(DESTDIR)$(libdir)/systemd/user/$(BIN_U).service"
$(INSTALL_DATA) "./data/icons/asus_notif_yellow.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_yellow.png"
$(INSTALL_DATA) "./data/icons/asus_notif_green.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_green.png"
$(INSTALL_DATA) "./data/icons/asus_notif_red.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_red.png"
$(INSTALL_DATA) "./data/_asusctl" "$(DESTDIR)$(zshcpl)/_asusctl"
$(INSTALL_DATA) "./data/completions/asusctl.fish" "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish"
cd data && find "./anime" -type f -exec install -Dm 755 "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \;
uninstall:
rm -f "$(DESTDIR)$(bindir)/$(BIN_C)"
@@ -72,6 +76,7 @@ uninstall:
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_red.png"
rm -f "$(DESTDIR)$(zshcpl)/_asusctl"
rm -f "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish"
rm -rf "$(DESTDIR)$(datarootdir)/asusd"
update:
cargo update
@@ -82,13 +87,13 @@ vendor:
echo 'directory = "vendor"' >> .cargo/config
mv .cargo/config ./cargo-config
rm -rf .cargo
tar pcfJ vendor_asus-nb-ctrl_$(VERSION).tar.xz vendor
tar pcfJ vendor_asusctl_$(VERSION).tar.xz vendor
rm -rf vendor
build:
ifeq ($(VENDORED),1)
@echo "version = $(VERSION)"
tar pxf vendor_asus-nb-ctrl_$(VERSION).tar.xz
tar pxf vendor_asusctl_$(VERSION).tar.xz
endif
cargo build $(ARGS)

150
README.md
View File

@@ -5,29 +5,26 @@
`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.
**NOTICE:**
## Goals
This app is developed and tested on fedora only. Support is not provided for Arch or Arch based distros.
1. To provide an interface for rootless control of some system functions most users wish to control such as fan speeds, keyboard LEDs, graphics modes.
2. Enable third-party apps to use the above with dbus methods
3. To make the above as easy as possible for new users
Point 3 means that the list of supported distros is very narrow - fedora is explicitly
supported, while Ubuntu and openSUSE are level-2 support. All other distros are *not*
supported (while asusd might still run fine on them). For best support use fedora 32+ Workstation.
**NOTICE:**
The following is *not* required for 5.11 kernel versions, as this version includes
all the required patches.
---
This program requires the kernel patch [here](https://www.spinics.net/lists/linux-input/msg68977.html) to be applied.
Alternatively you may use the dkms module for 'hid-asus-rog` from one of the
repositories [here](https://download.opensuse.org/repositories/home:/luke_nukem:/asus/).
1. The following is *not* required for 5.11 kernel versions, as this version includes all the required patches.
2. 2021 hardware has a new keyboard prod_id and the patch is included in 5.12+
The patch enables the following in kernel:
'hid-asus-rog` DKMS module from [here](https://download.opensuse.org/repositories/home:/luke_nukem:/asus/).
The module enables the following in kernel:
- Initialising the keyboard
- All hotkeys (FN+Key combos)
- Control of keyboard brightness using FN+Key combos (not RGB)
- FN+F5 (fan) to toggle fan modes
You will not get RGB control in kernel (yet), and `asusd` + `asusctl` is required
to change modes and RGB settings.
Many other patches for these laptops, AMD and Intel based, are working their way
in to the kernel.
## Discord
@@ -52,13 +49,14 @@ will probably suffer another rename once it becomes generic enough to do so.
- [X] User notifications daemon
- [X] Setting/modifying built-in LED modes
- [X] Per-key LED setting
- [X] Fancy LED modes (See examples)
- [X] Fancy LED modes (See examples) (currently being reworked)
- [X] Saving settings for reload
- [X] Logging - required for journalctl
- [X] AniMatrix display on G14 models that include it
- [X] AniMatrix display on G14 models that include it (currently being reworked)
- [X] Set battery charge limit (with kernel supporting this)
- [X] Fancy fan control on G14 + G15 thanks to @Yarn1
- [X] Graphics mode switching between iGPU, dGPU, and On-Demand
- [X] Fan curve control on G14 + G15 thanks to @Yarn1
- [X] Graphics mode switching between iGPU, dGPU, on-demand, and vfio (for VM pass-through)
+ [X] Requires only a logout/login
- [X] Toggle bios setting for boot/POST sound
- [X] Toggle bios setting for "dedicated gfx" mode on supported laptops (g-sync)
@@ -66,13 +64,20 @@ will probably suffer another rename once it becomes generic enough to do so.
## Graphics switching
A new feature has been added to enable switching graphics modes. This can be disabled
in the config with `"manage_gfx": false,`. Additionally there is an extra setting
for laptops capable of g-sync dedicated gfx mode to enable the graphics switching
to switch on dedicated gfx for "nvidia" mode.
`asusd` can switch graphics modes between:
- `integrated`, uses the iGPU only and force-disables the dGPU
- `hybrid`, enables Nvidia prime-offload mode
- `nvidia`, uses the Nvidia gpu only
- `vfio`, binds the Nvidia gpu to vfio for VM pass-through
The CLI option for this does not require root until it asks for it, and provides
instructions.
**Rebootless note:** You must edit `/etc/default/grub` to remove `nvidia-drm.modeset=1`
from the line `GRUB_CMDLINE_LINUX=` and then recreate your grub config. In fedora
you can do this with `sudo grub2-mkconfig -o /etc/grub2.cfg` - other distro may be
similar but with a different config location.
This can be disabled in the config with `"manage_gfx": false,`. Additionally there
is an extra setting for laptops capable of g-sync dedicated gfx mode to enable the
graphics switching to switch on dedicated gfx for "nvidia" mode.
This switcher conflicts with other gpu switchers like optimus-manager, suse-prime
or ubuntu-prime, system76-power, and bbswitch. If you have issues with `asusd`
@@ -81,6 +86,12 @@ stray configs blocking nvidia modules from loading in:
- `/etc/modprobe.d/`
- `/usr/lib/modprope.d/`
**VFIO NOTE:** The vfio modules *must not* be compiled into the kernel, they need
to be separate modules. If you don't plan to use vfio mode then you can ignore this
otherwise you may need a custom built kernel.
To enable vfio switching you need to edit `/etc/asusd/asusd.conf` and change `"gfx_vfio_enable": false,` to true.
### Power management udev rule
If you have installed the Nvidia driver manually you will require the
@@ -89,8 +100,8 @@ If you have installed the Nvidia driver manually you will require the
### fedora and openSUSE
You *may* need a file `/etc/dracut.conf.d/90-nvidia-dracut-G05.conf` installed
to stop dracut including the nvidia modules in the ramdisk. This is espeically
true if you manually installed the nvidia drivers.
to stop dracut including the nvidia modules in the ramdisk if you manually
installed the nvidia drivers.
```
# filename /etc/dracut.conf.d/90-nvidia-dracut-G05.conf
@@ -109,24 +120,21 @@ Models GA401, GA502, GU502 support LED brightness change only (no RGB).
If you model isn't getting the correct led modes, you can edit the file
`/etc/asusd/asusd-ledmodes.toml`, the LED Mode numbers are as follows:
```
0 STATIC
1 BREATHING
2 STROBE
3 RAINBOW
4 STAR
5 RAIN
6 HIGHLIGHT
7 LASER
8 RIPPLE
10 PULSE
11 COMET
12 FLASH
13 MULTISTATIC
255 PER_KEY
```
- Static
- Breathe
- Strobe
- Rainbow
- Star
- Rain
- Highlight
- Laser
- Ripple
- Pulse
- Comet
- Flash
use `cat /sys/class/dmi/id/product_name` to get details about your laptop.
use `cat /sys/class/dmi/id/product_name` to get details about your laptop. You
must restart the `asusd.service` after editing.
# Keybinds
@@ -156,8 +164,6 @@ Packaging and auto-builds are available [here](https://build.opensuse.org/packag
Download repositories are available [here](https://download.opensuse.org/repositories/home:/luke_nukem:/asus/)
Alternatively check the releases page for f33 RPM.
---
Run `make` then `sudo make install` then reboot.
@@ -182,26 +188,8 @@ can be added on request). You will need to install the alternative service from
Run `sudo make uninstall` in the source repo, and remove `/etc/asusd/`.
## Updating
If there has been a config file format change your config will be overwritten. This will
become less of an issue once the feature set is nailed down. Work is happening to enable
parsing of older configs and transferring settings to new.
# USAGE
**NOTE! Fan mode toggling requires a newer kernel**. I'm unsure when the patches
required for it got merged - I've tested with the 5.6.6 kernel and above only.
To see if the fan-mode changed cat either:
- `cat /sys/devices/platform/asus-nb-wmi/throttle_thermal_policy` or
- `cat /sys/devices/platform/asus-nb-wmi/fan_boost_mode`
The numbers are 0 = Normal/Balanced, 1 = Boost, 2 = Silent.
Running the program as a daemon manually will require root. Standard (non-daemon)
mode expects to be communicating with the daemon mode over dbus.
Commands are given by:
```
@@ -221,23 +209,6 @@ Some commands may have subcommands:
asusctl <command> <subcommand> --help
```
## Daemon mode
If the daemon service is enabled then on boot the following will be reloaded from save:
- LED brightness
- Last used built-in mode
- fan-boost/thermal mode
- battery charging limit
The daemon also saves the settings per mode as the keyboard does not do this
itself - this means cycling through modes with the Aura keys will use the
settings that were used via CLI.
Daemon mode creates a config file at `/etc/asusd/asusd.conf` which you can edit a
little of. Most parts will be byte arrays, but you can adjust things like
`mode_performance`.
## User NOTIFICATIONS via dbus
If you have a notifications handler set up, or are using KDE or Gnome then you
@@ -247,12 +218,9 @@ can enable the user service to get basic notifications when something changes.
systemctl --user enable asus-notify.service
systemctl --user start asus-notify.service
```
# OTHER
## DBUS Input
See [README_DBUS.md](./README_DBUS.md).
## AniMe input
You will want to look at what MeuMeu has done with [https://github.com/Meumeu/ZephyrusBling/](https://github.com/Meumeu/ZephyrusBling/)
@@ -278,11 +246,3 @@ omit_drivers+=" nvidia nvidia-drm nvidia-modeset nvidia-uvm "
# License
Mozilla Public License 2 (MPL-2.0)
# Credits
- [flukejones](https://github.com/flukejones/), project maintainer.
- [tuxuser](https://github.com/tuxuser/)
- [aspann](https://github.com/aspann)
- [meumeu](https://github.com/Meumeu)
- Anyone missed? Please contact me

View File

@@ -1,115 +0,0 @@
# DBUS Guide
**WARNING: In progress updates**
Interface name = org.asuslinux.Daemon
Paths:
- `/org/asuslinux/Gfx`
+ `SetVendor` (string)
+ `NotifyVendor` (recv vendor label string)
- `/org/asuslinux/Led`
+ `LedMode` (AuraMode as json)
+ `LedModes` (array[AuraMode] as json)
+ `SetLedMode` (AuraMode -> json)
+ `NotifyLed` (recv json data)
- `/org/asuslinux/Anime`
+ `SetAnime` (byte array data)
- `/org/asuslinux/Charge`
+ `Limit` (u8)
+ `SetLimit` (u8)
+ `NotifyCharge` (recv i8)
- `/org/asuslinux/Profile`
+ `Profile` (recv current profile data as json string)
+ `Profiles` (recv profiles data as json string (map))
+ `SetProfile` (event -> json)
+ `NotifyProfile` (recv current profile name)
All `Notify*` methods are signals.
### SetLed
This method expects a string of JSON as input. The JSON is of format such:
```
{
"Static": {
"colour": [ 255, 0, 0]
}
}
```
The possible contents of a mode are:
- `"colour": [u8, u8, u8],`
- `"speed": <String>,` <Low, Med, High>
- `"direction": <String>,` <Up, Down, Left, Right>
Modes may or may not be available for a specific laptop (TODO: dbus getter for
supported modes). Modes are:
- `"Static": { "colour": <colour> },`
- `"Pulse": { "colour": <colour> },`
- `"Comet": { "colour": <colour> },`
- `"Flash": { "colour": <colour> },`
- `"Strobe": { "speed": <speed> },`
- `"Rain": { "speed": <speed> },`
- `"Laser": { "colour": <colour>, "speed": <speed> },`
- `"Ripple": { "colour": <colour>, "speed": <speed> },`
- `"Highlight": { "colour": <colour>, "speed": <speed> },`
- `"Rainbow": { "direction": <direction>, "speed": <speed> },`
- `"Breathe": { "colour": <colour>, "colour2": <colour>, "speed": <speed> },`
- `"Star": { "colour": <colour>, "colour2": <colour>, "speed": <speed> },`
- `"MultiStatic": { "colour1": <colour>, "colour2": <colour>, , "colour3": <colour>, "colour4": <colour> },`
Additionally to the above there is `"RGB": [[u8; 64]; 11]` which is for per-key
setting of LED's but this requires some refactoring to make it easily useable over
dbus.
Lastly, there is `"LedBrightness": <u8>` which accepts 0-3 for off, low, med, high.
### SetFanMode
Accepts an integer from the following:
- `0`: Normal
- `1`: Boost mode
- `2`: Silent mode
## dbus-send examples:
```
dbus-send --system --type=method_call --dest=org.asuslinux.Daemon /org/asuslinux/Profile org.asuslinux.Daemon.NextProfile
```
## dbus-send examples OUTDATED
```
dbus-send --system --type=method_call --dest=org.asuslinux.Daemon /org/asuslinux/Daemon org.asuslinux.Daemon.SetKeyBacklight string:'{"Static": {"colour": [ 80, 0, 40]}}'
```
```
dbus-send --system --type=method_call --dest=org.asuslinux.Daemon /org/asuslinux/Daemon org.asuslinux.Daemon.SetKeyBacklight string:'{"Star":{"colour":[0,255,255],"colour2":[0,0,0],"speed":"Med"}}'
```
**Note:** setting colour2 to `[0,0,255]` activates random star colour. Colour2 has no effect on the
mode otherwise.
```
dbus-send --system --type=method_call --dest=org.asuslinux.Daemon /org/asuslinux/Daemon org.asuslinux.Daemon.SetKeyBacklight string:'{"Star":{"colour":[0,255,255],"colour2":[0,0,255],"speed":"Med"}}'
```
```
dbus-send --system --type=method_call --dest=org.asuslinux.Daemon /org/asuslinux/Daemon org.asuslinux.Daemon.SetKeyBacklight string:'{"LedBrightness":3}'
```
```
dbus-send --system --type=method_call --dest=org.asuslinux.Daemon /org/asuslinux/Daemon org.asuslinux.Daemon.SetFanMode byte:'2'
```
Monitoring dbus while sending commands via `rog-core` will give you the json structure if you are otherwise unsure, e.g: `dbus-monitor --system |grep -A2 asuslinux`.
## Getting an introspection .xml
```
dbus-send --system --print-reply --dest=org.asuslinux.Daemon /org/asuslinux/Charge org.freedesktop.DBus.Introspectable.Introspect > xml/asusd-charge.xml
```

View File

@@ -1,7 +0,0 @@
# TODO
- There is lots of code duplication. This should be turned in to macros (dbus stuff etc)
- Add a little more information to profile notifications such as freq min/max, fan curves
- Finish splitting out controllers to own crates
- Finish move to zbus in client when zbus has client signal watch
- Consider a rename again because the project is getting a lot less ASUS centric

View File

@@ -10,9 +10,10 @@ edition = "2018"
# serialisation
serde_json = "^1.0"
rog_dbus = { path = "../rog-dbus" }
rog_types = { path = "../rog-types" }
daemon = { path = "../daemon" }
[dependencies.notify-rust]
version = "^4.0"
version = "^4.3"
default-features = false
features = ["z"]

View File

@@ -1,7 +1,8 @@
use daemon::config::Profile;
use notify_rust::{Hint, Notification, NotificationHandle};
use rog_dbus::{DbusProxies, Signals};
use rog_types::profile::Profile;
use std::error::Error;
use std::thread::sleep;
use std::time::Duration;
fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -9,9 +10,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!(" daemon version {}", daemon::VERSION);
println!(" rog-dbus version {}", rog_dbus::VERSION);
// let mut cfg = Config::read_new()?;
// let mut last_profile = String::new();
let (proxies, conn) = DbusProxies::new()?;
let signals = Signals::new(&proxies)?;
@@ -21,9 +19,22 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut last_chrg_notif: Option<NotificationHandle> = None;
let recv = proxies.setup_recv(conn);
let mut err_count = 0;
loop {
std::thread::sleep(Duration::from_millis(100));
recv.next_signal().unwrap();
sleep(Duration::from_millis(100));
if let Err(err) = recv.next_signal() {
if err_count < 3 {
err_count += 1;
println!("{}", err);
}
if err_count == 3 {
err_count += 1;
println!("Max error count reached. Spooling silently.");
}
sleep(Duration::from_millis(2000));
continue;
}
err_count = 0;
if let Ok(mut lock) = signals.gfx_vendor.lock() {
if let Some(vendor) = lock.take() {

View File

@@ -1,6 +1,6 @@
[package]
name = "asusctl"
version = "3.1.4"
version = "3.4.0"
authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2018"
@@ -9,13 +9,15 @@ edition = "2018"
[dependencies]
# serialisation
serde_json = "^1.0"
rog_anime = { path = "../rog-anime" }
rog_dbus = { path = "../rog-dbus" }
rog_types = { path = "../rog-types" }
daemon = { path = "../daemon" }
gumdrop = "^0.8"
yansi-term = "^0.1"
[dev-dependencies]
tinybmp = "^0.2.3"
rog_dbus = { path = "../rog-dbus" }
glam = "0.14.0"
rog_dbus = { path = "../rog-dbus" }
gif = "^0.11.2"

View File

@@ -0,0 +1,26 @@
use std::{env, error::Error, path::Path, process::exit};
use rog_anime::{AnimeDataBuffer, AnimeDiagonal};
use rog_dbus::AuraDbusClient;
fn main() -> Result<(), Box<dyn Error>> {
let (client, _) = AuraDbusClient::new().unwrap();
let args: Vec<String> = env::args().into_iter().collect();
if args.len() != 3 {
println!("Usage: <filepath> <brightness>");
println!("e.g, asusctl/examples/doom_large.png 0.8");
exit(-1);
}
let matrix =
AnimeDiagonal::from_png(Path::new(&args[1]), None, args[2].parse::<f32>().unwrap())?;
client
.proxies()
.anime()
.write(<AnimeDataBuffer>::from(&matrix))
.unwrap();
Ok(())
}

View File

@@ -0,0 +1,32 @@
use std::{thread::sleep, time::Duration};
use rog_anime::{AnimeDataBuffer, AnimeDiagonal};
use rog_dbus::AuraDbusClient;
// In usable data:
// Top row start at 1, ends at 32
// 74w x 36h diagonal used by the windows app
fn main() {
let (client, _) = AuraDbusClient::new().unwrap();
for step in (2..50).rev() {
let mut matrix = AnimeDiagonal::new(None);
for c in (0..60).into_iter().step_by(step) {
for i in matrix.get_mut().iter_mut() {
i[c] = 50;
}
}
for c in (0..35).into_iter().step_by(step) {
for i in matrix.get_mut()[c].iter_mut() {
*i = 50;
}
}
let m = <AnimeDataBuffer>::from(&matrix);
client.proxies().anime().write(m).unwrap();
sleep(Duration::from_millis(300));
}
}

View File

@@ -0,0 +1,42 @@
use std::{env, path::Path, thread::sleep};
use rog_anime::{ActionData, AnimeAction, Sequences};
use rog_dbus::AuraDbusClient;
fn main() {
let (client, _) = AuraDbusClient::new().unwrap();
let args: Vec<String> = env::args().into_iter().collect();
if args.len() != 3 {
println!("Please supply filepath and brightness");
return;
}
let path = Path::new(&args[1]);
let brightness = args[2].parse::<f32>().unwrap();
let mut seq = Sequences::new();
seq.insert(
0,
&AnimeAction::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();
sleep(frame.delay());
}
}
}
}
}

View File

@@ -0,0 +1,44 @@
use rog_anime::{AnimeDataBuffer, AnimeGrid};
use rog_dbus::AuraDbusClient;
// In usable data:
// Top row start at 1, ends at 32
// 74w x 36h diagonal used by the windows app
fn main() {
let (client, _) = AuraDbusClient::new().unwrap();
let mut matrix = AnimeGrid::new(None);
let tmp = matrix.get_mut();
let mut i = 0;
for (y, row) in tmp.iter_mut().enumerate() {
if y % 2 == 0 && i + 1 != row.len() - 1 {
i += 1;
dbg!(i);
}
row[row.len() - i] = 0x22;
if i > 5 {
row[row.len() - i + 5] = 0x22;
}
if i > 10 {
row[row.len() - i + 10] = 0x22;
}
if i > 15 {
row[row.len() - i + 15] = 0x22;
}
if i > 20 {
row[row.len() - i + 20] = 0x22;
}
if i > 25 {
row[row.len() - i + 25] = 0x22;
}
}
let matrix = <AnimeDataBuffer>::from(matrix);
client.proxies().anime().write(matrix).unwrap();
}

View File

@@ -0,0 +1,129 @@
use rog_anime::AnimeDataBuffer;
use rog_dbus::AuraDbusClient;
// In usable data:
// Top row start at 1, ends at 32
fn main() {
let (client, _) = AuraDbusClient::new().unwrap();
let mut matrix = AnimeDataBuffer::new();
matrix.get_mut()[1] = 100; // start = 1
for n in matrix.get_mut()[2..32].iter_mut() {
*n = 250;
}
matrix.get_mut()[32] = 100; // end
matrix.get_mut()[34] = 100; // start x = 0
matrix.get_mut()[66] = 100; // end
matrix.get_mut()[69] = 100; // start x = 1
matrix.get_mut()[101] = 100; // end
matrix.get_mut()[102] = 100; // start
matrix.get_mut()[134] = 100; // end
matrix.get_mut()[137] = 100; // start
matrix.get_mut()[169] = 100; // end
matrix.get_mut()[170] = 100; // start
matrix.get_mut()[202] = 100; // end
matrix.get_mut()[204] = 100; // start
matrix.get_mut()[236] = 100; // end
matrix.get_mut()[237] = 100; // start
matrix.get_mut()[268] = 100; // end
matrix.get_mut()[270] = 100; // start
matrix.get_mut()[301] = 100; // end
matrix.get_mut()[302] = 100; // start
matrix.get_mut()[332] = 100; // end
matrix.get_mut()[334] = 100; // start
matrix.get_mut()[364] = 100; // end
matrix.get_mut()[365] = 100; // start
matrix.get_mut()[394] = 100; // end
matrix.get_mut()[396] = 100; // start
matrix.get_mut()[425] = 100; // end
matrix.get_mut()[426] = 100; // start
matrix.get_mut()[454] = 100; // end
matrix.get_mut()[456] = 100; // start
matrix.get_mut()[484] = 100; // end
matrix.get_mut()[485] = 100; // start
matrix.get_mut()[512] = 100; // end
matrix.get_mut()[514] = 100; // start
matrix.get_mut()[541] = 100; // end
matrix.get_mut()[542] = 100; // start
matrix.get_mut()[568] = 100; // end
matrix.get_mut()[570] = 100; // start
matrix.get_mut()[596] = 100; // end
matrix.get_mut()[597] = 100; // start
matrix.get_mut()[622] = 100; // end
matrix.get_mut()[624] = 100; // start
matrix.get_mut()[649] = 100; // end
matrix.get_mut()[650] = 100; // start
matrix.get_mut()[674] = 100; // end
matrix.get_mut()[676] = 100; // start
matrix.get_mut()[700] = 100; // end
matrix.get_mut()[701] = 100; // start
matrix.get_mut()[724] = 100; // end
matrix.get_mut()[726] = 100; // start
matrix.get_mut()[749] = 100; // end
matrix.get_mut()[750] = 100; // start
matrix.get_mut()[772] = 100; // end
matrix.get_mut()[774] = 100; // start
matrix.get_mut()[796] = 100; // end
matrix.get_mut()[797] = 100; // start
matrix.get_mut()[818] = 100; // end
matrix.get_mut()[820] = 100; // start
matrix.get_mut()[841] = 100; // end
matrix.get_mut()[842] = 100; // start
matrix.get_mut()[862] = 100; // end
matrix.get_mut()[864] = 100; // start
matrix.get_mut()[884] = 100; // end
matrix.get_mut()[885] = 100; // start
matrix.get_mut()[904] = 100; // end
matrix.get_mut()[906] = 100; // start
matrix.get_mut()[925] = 100; // end
matrix.get_mut()[926] = 100; // start
matrix.get_mut()[944] = 100; // end
matrix.get_mut()[946] = 100; // start
matrix.get_mut()[964] = 100; // end
matrix.get_mut()[965] = 100; // start
matrix.get_mut()[982] = 100; // end
matrix.get_mut()[984] = 100; // start
matrix.get_mut()[1001] = 100; // end
matrix.get_mut()[1002] = 100; // start
matrix.get_mut()[1018] = 100; // end
matrix.get_mut()[1020] = 100; // start
matrix.get_mut()[1036] = 100; // end
matrix.get_mut()[1037] = 100; // start
matrix.get_mut()[1052] = 100; // end
matrix.get_mut()[1054] = 100; // start
matrix.get_mut()[1069] = 100; // end
matrix.get_mut()[1070] = 100; // start
matrix.get_mut()[1084] = 100; // end
matrix.get_mut()[1086] = 100; // start
matrix.get_mut()[1100] = 100; // end
matrix.get_mut()[1101] = 100; // start
matrix.get_mut()[1114] = 100; // end
matrix.get_mut()[1116] = 100; // start
matrix.get_mut()[1129] = 100; // end
matrix.get_mut()[1130] = 100; // start
matrix.get_mut()[1142] = 100; // end
matrix.get_mut()[1144] = 100; // start
matrix.get_mut()[1156] = 100; // end
matrix.get_mut()[1157] = 100; // start
matrix.get_mut()[1168] = 100; // end
matrix.get_mut()[1170] = 100; // start
matrix.get_mut()[1181] = 100; // end
matrix.get_mut()[1182] = 100; // start
matrix.get_mut()[1192] = 100; // end
matrix.get_mut()[1194] = 100; // start
matrix.get_mut()[1204] = 100; // end
matrix.get_mut()[1205] = 100; // start
matrix.get_mut()[1214] = 100; // end
matrix.get_mut()[1216] = 100; // start
matrix.get_mut()[1225] = 100; // end
matrix.get_mut()[1226] = 100; // start
matrix.get_mut()[1234] = 100; // end
matrix.get_mut()[1236] = 100; // start
for n in matrix.get_mut()[1237..1244].iter_mut() {
*n = 250;
}
matrix.get_mut()[1244] = 100; // end
println!("{:?}", &matrix);
client.proxies().anime().write(matrix).unwrap();
}

View File

@@ -0,0 +1,36 @@
use std::{env, error::Error, path::Path, process::exit};
use rog_anime::{
AnimeDataBuffer, {AnimeImage, Vec2},
};
use rog_dbus::AuraDbusClient;
fn main() -> Result<(), Box<dyn Error>> {
let (client, _) = AuraDbusClient::new().unwrap();
let args: Vec<String> = env::args().into_iter().collect();
if args.len() != 7 {
println!("Usage: <filepath> <scale> <angle> <x pos> <y pos> <brightness>");
println!("e.g, asusctl/examples/doom_large.png 0.9 0.4 0.0 0.0 0.8");
exit(-1);
}
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[6].parse::<f32>().unwrap(),
)?;
client
.proxies()
.anime()
.write(<AnimeDataBuffer>::from(&matrix))
.unwrap();
Ok(())
}

View File

@@ -0,0 +1,45 @@
use std::{
env, error::Error, f32::consts::PI, path::Path, process::exit, thread::sleep, time::Duration,
};
use rog_anime::{
AnimeDataBuffer, {AnimeImage, Vec2},
};
use rog_dbus::AuraDbusClient;
fn main() -> Result<(), Box<dyn Error>> {
let (client, _) = AuraDbusClient::new().unwrap();
let args: Vec<String> = env::args().into_iter().collect();
if args.len() != 7 {
println!("Usage: <filepath> <scale> <angle> <x pos> <y pos> <brightness>");
println!("e.g, asusctl/examples/doom_large.png 0.9 0.4 0.0 0.0 0.8");
exit(-1);
}
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[6].parse::<f32>().unwrap(),
)?;
loop {
matrix.angle += 0.05;
if matrix.angle > PI * 2.0 {
matrix.angle = 0.0
}
matrix.update();
client
.proxies()
.anime()
.write(<AnimeDataBuffer>::from(&matrix))
.unwrap();
sleep(Duration::from_micros(500));
}
}

View File

@@ -1,42 +0,0 @@
use rog_dbus::AuraDbusClient;
use rog_types::anime_matrix::{AniMeImageBuffer, AniMePacketType, HEIGHT, WIDTH};
use tinybmp::{Bmp, Pixel};
fn main() {
let (client, _) = AuraDbusClient::new().unwrap();
let bmp =
Bmp::from_slice(include_bytes!("non-skewed_r.bmp")).expect("Failed to parse BMP image");
let pixels: Vec<Pixel> = bmp.into_iter().collect();
//assert_eq!(pixels.len(), 56 * 56);
// Try an outline, top and right
let mut matrix = AniMeImageBuffer::new();
// Aligned left
for (i, px) in pixels.iter().enumerate() {
if (px.x as usize / 2) < WIDTH && (px.y as usize) < HEIGHT && px.x % 2 == 0 {
let mut c = px.color as u32;
matrix.get_mut()[px.y as usize][px.x as usize / 2] = c as u8;
}
}
// Throw an alignment border up
// {
// let tmp = matrix.get_mut();
// for x in tmp[0].iter_mut() {
// *x = 0xff;
// }
// for row in tmp.iter_mut() {
// row[row.len() - 1] = 0xff;
// }
// }
matrix.debug_print();
let mut matrix: AniMePacketType = AniMePacketType::from(matrix);
// println!("{:?}", matrix[0].to_vec());
// println!("{:?}", matrix[1].to_vec());
//client.proxies().anime().set_brightness(&mut matrix).unwrap();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
asusctl/examples/doom.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

BIN
asusctl/examples/ferris.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

BIN
asusctl/examples/nudoom.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

@@ -10,7 +10,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
loop {
let count = 49;
for _ in 0..count {
*key_colours.key(Key::ROG).unwrap().0 += 5;
*key_colours.key(Key::Rog).unwrap().0 += 5;
*key_colours.key(Key::L).unwrap().0 += 5;
*key_colours.key(Key::I).unwrap().0 += 5;
*key_colours.key(Key::N).unwrap().0 += 5;
@@ -19,7 +19,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
dbus.proxies().led().set_per_key(&key_colours)?;
}
for _ in 0..count {
*key_colours.key(Key::ROG).unwrap().0 -= 5;
*key_colours.key(Key::Rog).unwrap().0 -= 5;
*key_colours.key(Key::L).unwrap().0 -= 5;
*key_colours.key(Key::I).unwrap().0 -= 5;
*key_colours.key(Key::N).unwrap().0 -= 5;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

BIN
asusctl/examples/rust.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

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

@@ -0,0 +1,92 @@
use gumdrop::Options;
use rog_types::error::AuraError;
use std::str::FromStr;
#[derive(Copy, Clone, Debug)]
pub enum AnimeStatusValue {
On,
Off,
}
impl FromStr for AnimeStatusValue {
type Err = AuraError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
match s.as_str() {
"on" => Ok(AnimeStatusValue::On),
"off" => Ok(AnimeStatusValue::Off),
_ => {
print!("Invalid argument, must be one of: on, off");
Err(AuraError::ParseAnime)
}
}
}
}
impl From<AnimeStatusValue> for bool {
fn from(value: AnimeStatusValue) -> Self {
match value {
AnimeStatusValue::On => true,
AnimeStatusValue::Off => false,
}
}
}
#[derive(Options)]
pub struct AnimeLeds {
#[options(help = "print help message")]
help: bool,
#[options(
no_long,
required,
short = "b",
meta = "",
help = "set all leds brightness value"
)]
led_brightness: u8,
}
impl AnimeLeds {
pub fn led_brightness(&self) -> u8 {
self.led_brightness
}
}
#[derive(Options)]
pub struct AnimeCommand {
#[options(help = "print help message")]
pub help: bool,
#[options(
meta = "",
help = "turn on/off the panel (accept/reject write requests)"
)]
pub turn: Option<AnimeStatusValue>,
#[options(meta = "", help = "turn on/off the panel at boot (with Asus effect)")]
pub boot: Option<AnimeStatusValue>,
#[options(command)]
pub command: Option<AnimeActions>,
}
#[derive(Options)]
pub enum AnimeActions {
#[options(help = "change all leds brightness")]
Leds(AnimeLeds),
#[options(help = "display an 8bit greyscale png")]
Image(AnimeImage),
}
#[derive(Options)]
pub struct AnimeImage {
#[options(help = "print help message")]
pub help: bool,
#[options(meta = "", help = "full path to the png to display")]
pub path: String,
#[options(meta = "", default = "1.0", help = "scale 1.0 == normal")]
pub scale: f32,
#[options(meta = "", default = "0.0", help = "x position (float)")]
pub x_pos: f32,
#[options(meta = "", default = "0.0", help = "y position (float)")]
pub y_pos: f32,
#[options(meta = "", default = "0.0", help = "the angle in radians")]
pub angle: f32,
#[options(meta = "", default = "1.0", help = "brightness 0.0-1.0")]
pub bright: f32,
}

View File

@@ -1,19 +1,26 @@
mod anime_cli;
mod aura_cli;
use crate::aura_cli::{LedBrightness, SetAuraBuiltin};
use daemon::{
ctrl_fan_cpu::FanCpuSupportedFunctions, ctrl_leds::LedSupportedFunctions,
ctrl_rog_bios::RogBiosSupportedFunctions, ctrl_supported::SupportedFunctions,
};
use anime_cli::{AnimeActions, AnimeCommand};
use gumdrop::{Opt, Options};
use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2, ANIME_DATA_LEN};
use rog_dbus::AuraDbusClient;
use rog_types::{anime_matrix::{AniMeDataBuffer, FULL_PANE_LEN}, aura_modes::{self, AuraEffect, AuraModeNum}, cli_options::{AniMeActions, AniMeStatusValue}, gfx_vendors::GfxVendors, profile::{FanLevel, ProfileCommand, ProfileEvent}};
use std::env::args;
use rog_types::{
aura_modes::{self, AuraEffect, AuraModeNum},
gfx_vendors::GfxVendors,
profile::{FanLevel, ProfileCommand, ProfileEvent},
supported::{
FanCpuSupportedFunctions, LedSupportedFunctions, RogBiosSupportedFunctions,
SupportedFunctions,
},
};
use std::{env::args, path::Path};
use yansi_term::Colour::Green;
use yansi_term::Colour::Red;
#[derive(Default, Options)]
struct CLIStart {
struct CliStart {
#[options(help_flag, help = "print help message")]
help: bool,
#[options(help = "show program version number")]
@@ -42,7 +49,7 @@ enum CliCommand {
#[options(help = "Set the graphics mode")]
Graphics(GraphicsCommand),
#[options(name = "anime", help = "Manage AniMe Matrix")]
AniMe(AniMeCommand),
Anime(AnimeCommand),
#[options(help = "Change bios settings")]
Bios(BiosCommand),
}
@@ -76,21 +83,6 @@ struct GraphicsCommand {
force: bool,
}
#[derive(Options)]
struct AniMeCommand {
#[options(help = "print help message")]
help: bool,
#[options(
meta = "",
help = "turn on/off the panel (accept/reject write requests)"
)]
turn: Option<AniMeStatusValue>,
#[options(meta = "", help = "turn on/off the panel at boot (with Asus effect)")]
boot: Option<AniMeStatusValue>,
#[options(command)]
command: Option<AniMeActions>,
}
#[derive(Options, Debug)]
struct BiosCommand {
#[options(help = "print help message")]
@@ -112,14 +104,14 @@ struct BiosCommand {
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<String> = args().skip(1).collect();
let parsed: CLIStart;
let parsed: CliStart;
let missing_argument_k = gumdrop::Error::missing_argument(Opt::Short('k'));
match CLIStart::parse_args_default(&args) {
match CliStart::parse_args_default(&args) {
Ok(p) => {
parsed = p;
}
Err(err) if err.to_string() == missing_argument_k.to_string() => {
parsed = CLIStart {
parsed = CliStart {
kbd_bright: Some(LedBrightness::new(None)),
..Default::default()
};
@@ -137,6 +129,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
if parsed.help {
print_supported_help(&supported, &parsed);
println!("\nSee https://asus-linux.org/faq/ for additional help");
std::process::exit(1);
}
@@ -152,7 +145,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Some(CliCommand::LedMode(mode)) => handle_led_mode(&dbus, &supported.keyboard_led, &mode)?,
Some(CliCommand::Profile(cmd)) => handle_profile(&dbus, &supported.fan_cpu_ctrl, &cmd)?,
Some(CliCommand::Graphics(cmd)) => do_gfx(&dbus, &supported.rog_bios_ctrl, cmd)?,
Some(CliCommand::AniMe(cmd)) => {
Some(CliCommand::Anime(cmd)) => {
if (cmd.command.is_none() && cmd.boot.is_none() && cmd.turn.is_none()) || cmd.help {
println!("Missing arg or command\n\n{}", cmd.self_usage());
if let Some(lst) = cmd.self_command_list() {
@@ -167,10 +160,33 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}
if let Some(action) = cmd.command {
match action {
AniMeActions::Leds(anime_leds) => {
let mut data = AniMeDataBuffer::new();
data.set([anime_leds.led_brightness(); FULL_PANE_LEN]);
dbus.proxies().anime().write_direct(data)?;
AnimeActions::Leds(anime_leds) => {
let data = AnimeDataBuffer::from_vec(
[anime_leds.led_brightness(); ANIME_DATA_LEN].to_vec(),
);
dbus.proxies().anime().write(data)?;
}
AnimeActions::Image(image) => {
if image.help_requested() {
println!("Missing arg or command\n\n{}", image.self_usage());
if let Some(lst) = image.self_command_list() {
println!("\n{}", lst);
}
std::process::exit(1);
}
let matrix = AnimeImage::from_png(
Path::new(&image.path),
image.scale,
image.angle,
Vec2::new(image.x_pos, image.y_pos),
image.bright,
)?;
dbus.proxies()
.anime()
.write(<AnimeDataBuffer>::from(&matrix))
.unwrap();
}
}
}
@@ -183,9 +199,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
&& parsed.chg_limit.is_none())
|| parsed.help
{
println!("{}", CLIStart::usage());
println!("{}", CliStart::usage());
println!();
println!("{}", CLIStart::command_list().unwrap());
println!("{}", CliStart::command_list().unwrap());
}
}
}
@@ -196,7 +212,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let level = dbus.proxies().led().get_led_brightness()?;
println!("Current keyboard led brightness: {}", level.to_string());
}
Some(level) => dbus.proxies().led().set_led_brightness(<aura_modes::LedBrightness>::from(level))?,
Some(level) => dbus
.proxies()
.led()
.set_led_brightness(<aura_modes::LedBrightness>::from(level))?,
}
}
@@ -214,7 +233,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
fn print_supported_help(supported: &SupportedFunctions, parsed: &CLIStart) {
fn print_supported_help(supported: &SupportedFunctions, parsed: &CliStart) {
// As help option don't work with `parse_args_default`
// we will call `parse_args_default_or_exit` instead
let usage: Vec<String> = parsed.self_usage().lines().map(|s| s.to_string()).collect();
@@ -301,10 +320,11 @@ fn do_gfx(
}
if command.pow {
let res = dbus.proxies().gfx().gfx_get_pwr()?;
if res.contains("active") {
println!("Current power status: {}", Red.paint(&res));
} else {
println!("Current power status: {}", Green.paint(&res));
match res {
rog_types::gfx_vendors::GfxPower::Active => {
println!("Current power status: {}", Red.paint(<&str>::from(&res)))
}
_ => println!("Current power status: {}", Green.paint(<&str>::from(&res))),
}
}
Ok(())
@@ -380,6 +400,9 @@ fn handle_profile(
if !cmd.next
&& !cmd.create
&& !cmd.list
&& !cmd.active_name
&& !cmd.active_data
&& !cmd.profiles_data
&& cmd.remove.is_none()
&& cmd.curve.is_none()
&& cmd.max_percentage.is_none()
@@ -410,12 +433,34 @@ fn handle_profile(
if cmd.next {
dbus.proxies().profile().next_fan()?;
} else if cmd.list {
let profile_names = dbus.proxies().profile().profile_names()?;
println!("Available profiles are {}", profile_names);
} else if let Some(profile) = &cmd.remove {
}
if let Some(profile) = &cmd.remove {
dbus.proxies().profile().remove(profile)?
} else {
}
if cmd.list {
let profile_names = dbus.proxies().profile().profile_names()?;
println!("Available profiles are {:?}", profile_names);
}
if cmd.active_name {
println!(
"Active profile: {:?}",
dbus.proxies().profile().active_profile_name()?
);
}
if cmd.active_data {
println!("Active profile:");
for s in dbus.proxies().profile().active_profile_data()?.lines() {
println!("{}", s);
}
}
if cmd.profiles_data {
println!("Profiles:");
for s in dbus.proxies().profile().all_profile_data()?.lines() {
println!("{}", s);
}
}
if cmd.profile.is_some() {
dbus.proxies()
.profile()
.write_command(&ProfileEvent::Cli(cmd.clone()))?

30
daemon-user/Cargo.toml Normal file
View File

@@ -0,0 +1,30 @@
[package]
name = "daemon-user"
version = "1.0.0"
authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2018"
description = "Usermode daemon for user settings, anime, per-key lighting"
[lib]
name = "rog_user"
path = "src/lib.rs"
[[bin]]
name = "asusd-user"
path = "src/daemon.rs"
[dependencies]
# serialisation
serde = "^1.0"
serde_json = "^1.0"
serde_derive = "^1.0"
rog_anime = { path = "../rog-anime" }
rog_dbus = { path = "../rog-dbus" }
rog_types = { path = "../rog-types" }
dirs = "3.0.1"
zbus = "^1.9.1"
zvariant = "^2.6"
zvariant_derive = "^2.6"

14
daemon-user/README.md Normal file
View File

@@ -0,0 +1,14 @@
# daemon-user
This crate is for the binary of `asusd-user` and its helper lib.
The purpose of `asusd-user` is to run in userland and provide the user + third-party apps an interface for such things as creating AniMe sequences (and more in future, see todo list).
`asusd-user` should try to be as simple as possible while allowing a decent degree of control.
## TODO
- [ ] CLI for basic settings/interaction
- [ ] RGB keyboard per-key programs
- [ ] User profiles (fan, cpu etc). These would be replacing the system-daemon profiles only when the user is active, otherwise system-daemon defaults to system settings.
- [ ] Audio EQ visualiser - for use with anime + keyboard lighting

View File

@@ -0,0 +1,342 @@
use rog_anime::{ActionData, AnimTime, AnimeAction, Sequences, Vec2};
use rog_dbus::AuraDbusClient;
//use crate::dbus::DbusEvents;
use serde_derive::{Deserialize, Serialize};
use std::time::Duration;
use std::{
path::Path,
sync::{
atomic::{AtomicBool, Ordering},
Mutex,
},
};
use std::{sync::Arc, thread::sleep, time::Instant};
use zbus::dbus_interface;
use zvariant::ObjectPath;
use zvariant_derive::Type;
use crate::{error::Error, user_config::UserConfig};
#[derive(Debug, Clone, Deserialize, Serialize, Type)]
pub enum TimeType {
Timer,
Count,
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: AuraDbusClient<'a>,
do_early_return: &'a AtomicBool,
}
impl<'a> CtrlAnimeInner<'static> {
pub fn new(
sequences: Sequences,
client: AuraDbusClient<'static>,
do_early_return: &'static AtomicBool,
) -> Result<Self, Error> {
Ok(Self {
sequences,
client,
do_early_return,
})
}
/// To be called on each main loop iteration to pump out commands to the anime
pub fn run(&self) -> Result<(), Error> {
if self.do_early_return.load(Ordering::SeqCst) {
return Ok(());
}
for action in self.sequences.iter() {
match action {
ActionData::Animation(frames) => {
let mut count = 0;
let start = Instant::now();
'animation: loop {
for frame in frames.frames() {
if self.do_early_return.load(Ordering::SeqCst) {
return Ok(());
}
self.client
.proxies()
.anime()
.write(frame.frame().clone())
.unwrap();
if let AnimTime::Time(time) = frames.duration() {
if Instant::now().duration_since(start) > time {
break 'animation;
}
}
sleep(frame.delay());
}
if let AnimTime::Cycles(times) = frames.duration() {
count += 1;
if count >= times {
break 'animation;
}
}
}
}
ActionData::Image(image) => {
self.client
.proxies()
.anime()
.write(image.as_ref().clone())
.unwrap();
}
ActionData::Pause(duration) => {
let start = Instant::now();
'pause: loop {
if self.do_early_return.load(Ordering::SeqCst) {
return Ok(());
}
if Instant::now().duration_since(start) > *duration {
break 'pause;
}
sleep(Duration::from_millis(1));
}
}
ActionData::AudioEq => {}
ActionData::SystemInfo => {}
ActionData::TimeDate => {}
ActionData::Matrix => {}
}
}
Ok(())
}
}
pub struct CtrlAnime<'a> {
config: Arc<Mutex<UserConfig>>,
client: AuraDbusClient<'a>,
inner: Arc<Mutex<CtrlAnimeInner<'a>>>,
/// Must be the same Atomic as in CtrlAnimeInner
inner_early_return: &'a AtomicBool,
}
impl<'a> CtrlAnime<'static> {
pub fn new(
config: Arc<Mutex<UserConfig>>,
inner: Arc<Mutex<CtrlAnimeInner<'static>>>,
client: AuraDbusClient<'static>,
inner_early_return: &'static AtomicBool,
) -> Result<Self, Error> {
Ok(CtrlAnime {
config,
inner,
client,
inner_early_return,
})
}
pub fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(
&ObjectPath::from_str_unchecked("/org/asuslinux/Anime"),
self,
)
.map_err(|err| {
println!("CtrlAnime: add_to_server {}", err);
err
})
.ok();
}
}
// The pattern for a zbus method is:
// - Get config lock if required
// - Set inner_early_return to stop the inner run loop temporarily
// - Do actions
// - Write config if required
// - Unset inner_early_return
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlAnime<'static> {
pub fn insert_asus_gif(
&mut self,
index: u32,
file: String,
time: TimeType,
count: u32,
brightness: f32,
) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() {
let time: AnimTime = match time {
TimeType::Timer => AnimTime::Time(Duration::from_millis(count as u64)),
TimeType::Count => AnimTime::Cycles(count),
TimeType::Infinite => AnimTime::Infinite,
};
let file = Path::new(&file);
let action = AnimeAction::AsusAnimation {
file: file.into(),
brightness,
time,
};
// Must make the inner run loop return early
self.inner_early_return.store(true, Ordering::SeqCst);
if let Ok(mut controller) = self.inner.lock() {
controller.sequences.insert(index as usize, &action)?;
}
config.anime.push(action);
config.write()?;
let json = serde_json::to_string_pretty(&*config).expect("Parse config to JSON failed");
// Release the inner run loop again
self.inner_early_return.store(false, Ordering::SeqCst);
return Ok(json);
}
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
}
#[allow(clippy::too_many_arguments)]
pub fn insert_image_gif(
&mut self,
index: u32,
file: String,
scale: f32,
angle: f32,
xy: (f32, f32),
time: TimeType,
count: u32,
brightness: f32,
) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() {
let time: AnimTime = match time {
TimeType::Timer => AnimTime::Time(Duration::from_millis(count as u64)),
TimeType::Count => AnimTime::Cycles(count),
TimeType::Infinite => AnimTime::Infinite,
};
let file = Path::new(&file);
let translation = Vec2::new(xy.0, xy.1);
let action = AnimeAction::ImageAnimation {
file: file.into(),
scale,
angle,
translation,
brightness,
time,
};
// Must make the inner run loop return early
self.inner_early_return.store(true, Ordering::SeqCst);
if let Ok(mut controller) = self.inner.lock() {
controller.sequences.insert(index as usize, &action)?;
}
config.anime.push(action);
config.write()?;
let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
// Release the inner run loop again
self.inner_early_return.store(false, Ordering::SeqCst);
return Ok(json);
}
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
}
pub fn insert_image(
&mut self,
index: u32,
file: String,
scale: f32,
angle: f32,
xy: (f32, f32),
brightness: f32,
) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() {
let file = Path::new(&file);
let action = AnimeAction::Image {
file: file.into(),
scale,
angle,
translation: Vec2::new(xy.0, xy.1),
brightness,
};
// Must make the inner run loop return early
self.inner_early_return.store(true, Ordering::SeqCst);
if let Ok(mut controller) = self.inner.lock() {
controller.sequences.insert(index as usize, &action)?;
}
config.anime.push(action);
config.write()?;
let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
// Release the inner run loop again
self.inner_early_return.store(false, Ordering::SeqCst);
return Ok(json);
}
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
}
pub fn insert_pause(&mut self, index: u32, millis: u64) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() {
let action = AnimeAction::Pause(Duration::from_millis(millis));
// Must make the inner run loop return early
self.inner_early_return.store(true, Ordering::SeqCst);
if let Ok(mut controller) = self.inner.lock() {
controller.sequences.insert(index as usize, &action)?;
}
config.anime.push(action);
config.write()?;
let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
// Release the inner run loop again
self.inner_early_return.store(false, Ordering::SeqCst);
return Ok(json);
}
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
}
pub fn remove_item(&mut self, index: u32) -> zbus::fdo::Result<String> {
if let Ok(mut config) = self.config.try_lock() {
// Must make the inner run loop return early
self.inner_early_return.store(true, Ordering::SeqCst);
if let Ok(mut controller) = self.inner.lock() {
controller.sequences.remove_item(index as usize);
}
if (index as usize) < config.anime.len() {
config.anime.remove(index as usize);
}
config.write()?;
let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
// Release the inner run loop again
self.inner_early_return.store(false, Ordering::SeqCst);
return Ok(json);
}
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
}
pub fn set_state(&mut self, on: bool) -> zbus::fdo::Result<()> {
// Operations here need to be in specific order
if on {
self.client.proxies().anime().toggle_on(on)?;
// Let the inner loop run
self.inner_early_return.store(false, Ordering::SeqCst);
} else {
// Must make the inner run loop return early
self.inner_early_return.store(true, Ordering::SeqCst);
self.client.proxies().anime().toggle_on(on)?;
}
Ok(())
}
}

67
daemon-user/src/daemon.rs Normal file
View File

@@ -0,0 +1,67 @@
use rog_dbus::AuraDbusClient;
use rog_types::supported::SupportedFunctions;
use rog_user::{
ctrl_anime::{CtrlAnime, CtrlAnimeInner},
user_config::*,
DBUS_NAME,
};
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
use zbus::{fdo, Connection};
use std::sync::atomic::AtomicBool;
/// The anime loop needs an atomic to make it exit early if required
static ANIME_INNER_EARLY_RETURN: AtomicBool = AtomicBool::new(false);
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!(" rog-dbus version {}", rog_dbus::VERSION);
let (client, _) = AuraDbusClient::new().unwrap();
let supported = client.proxies().supported().get_supported_functions()?;
let supported = serde_json::from_str::<SupportedFunctions>(&&supported).unwrap();
let mut config = UserConfig::new();
config.load_config()?;
let anime = config.create_anime()?;
let config = Arc::new(Mutex::new(config));
// Create server
let connection = Connection::new_session()?;
fdo::DBusProxy::new(&connection)?
.request_name(DBUS_NAME, fdo::RequestNameFlags::ReplaceExisting.into())?;
let mut server = zbus::ObjectServer::new(&connection);
// Set up the anime data and run loop/thread
if supported.anime_ctrl.0 {
// Inner behind mutex required for thread safety
let inner = Arc::new(Mutex::new(CtrlAnimeInner::new(
anime,
client,
&ANIME_INNER_EARLY_RETURN,
)?));
// Need new client object for dbus control part
let (client, _) = AuraDbusClient::new().unwrap();
let anime_control =
CtrlAnime::new(config, inner.clone(), client, &ANIME_INNER_EARLY_RETURN)?;
anime_control.add_to_server(&mut server);
// Thread using inner
let _anime_thread = thread::Builder::new()
.name("Anime User".into())
.spawn(move || loop {
if let Ok(inner) = inner.try_lock() {
inner.run().unwrap();
}
})?;
}
if supported.keyboard_led.per_key_led_mode {}
loop {
if let Err(err) = server.try_handle_next() {
println!("{}", err);
}
}
}

45
daemon-user/src/error.rs Normal file
View File

@@ -0,0 +1,45 @@
use std::fmt;
use rog_anime::error::AnimeError;
#[derive(Debug)]
pub enum Error {
Io(std::io::Error),
ConfigLoadFail,
ConfigLockFail,
XdgVars,
Anime(AnimeError),
}
impl fmt::Display for Error {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Io(err) => write!(f, "Failed to open: {}", err),
Error::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),
}
}
}
impl std::error::Error for Error {}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Error::Io(err)
}
}
impl From<AnimeError> for Error {
fn from(err: AnimeError) -> Self {
Error::Anime(err)
}
}
impl From<Error> for zbus::fdo::Error {
fn from(err: Error) -> Self {
zbus::fdo::Error::Failed(format!("Anime zbus error: {}", err))
}
}

9
daemon-user/src/lib.rs Normal file
View File

@@ -0,0 +1,9 @@
pub mod user_config;
pub mod error;
pub mod ctrl_anime;
pub mod zbus_anime;
pub static DBUS_NAME: &str = "org.asuslinux.Daemon";

View File

@@ -0,0 +1,121 @@
use std::{
fs::{create_dir, OpenOptions},
io::{Read, Write},
time::Duration,
};
use rog_anime::{AnimTime, AnimeAction, Sequences, Vec2};
use serde_derive::{Deserialize, Serialize};
use crate::error::Error;
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct UserConfig {
pub anime: Vec<AnimeAction>,
}
impl UserConfig {
pub fn new() -> Self {
let x = Self {
anime: vec![
AnimeAction::AsusAnimation {
file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(),
brightness: 0.5,
time: AnimTime::Cycles(1),
},
AnimeAction::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
brightness: 0.5,
time: AnimTime::Time(Duration::from_secs(5)),
},
AnimeAction::Image {
file: "/usr/share/asusd/anime/custom/rust.png".into(),
scale: 1.0,
angle: 0.0,
translation: Vec2::default(),
brightness: 0.6,
},
AnimeAction::Pause(Duration::from_secs(6)),
AnimeAction::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
scale: 0.9,
angle: 0.0,
translation: Vec2::new(3.0, 2.0),
brightness: 0.5,
time: AnimTime::Cycles(2),
},
],
};
println!("{}", serde_json::to_string_pretty(&x).unwrap());
x
}
pub fn load_config(&mut self) -> Result<(), Error> {
let mut path = if let Some(dir) = dirs::config_dir() {
dir
} else {
return Err(Error::XdgVars);
};
path.push("rog");
if !path.exists() {
create_dir(path.clone())?;
}
path.push("rog-user.cfg");
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)?;
let mut buf = String::new();
if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len == 0 {
let json = serde_json::to_string_pretty(&self).unwrap();
file.write_all(json.as_bytes())?;
} else if let Ok(data) = serde_json::from_str::<UserConfig>(&buf) {
self.anime = data.anime;
return Ok(());
}
}
Ok(())
}
pub fn write(&self) -> Result<(), Error> {
let mut path = if let Some(dir) = dirs::config_dir() {
dir
} else {
return Err(Error::XdgVars);
};
path.push("rog");
if !path.exists() {
create_dir(path.clone())?;
}
path.push("rog-user.cfg");
let mut file = OpenOptions::new().write(true).create(true).truncate(true).open(&path)?;
let json = serde_json::to_string_pretty(&self).unwrap();
dbg!(&json);
file.write_all(json.as_bytes())?;
Ok(())
}
pub fn create_anime(&self) -> Result<Sequences, Error> {
let mut seq = Sequences::new();
for (idx, action) in self.anime.iter().enumerate() {
seq.insert(idx, action)?;
}
Ok(seq)
}
}

View File

@@ -0,0 +1,68 @@
//! # DBus interface proxy for: `org.asuslinux.Daemon`
//!
//! 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`.
//!
//! You may prefer to adapt it, instead of using it verbatim.
//!
//! More information can be found in the
//! [Writing a client proxy](https://dbus.pages.freedesktop.org/zbus/client.html)
//! section of the zbus documentation.
//!
//! This DBus object implements
//! [standard DBus interfaces](https://dbus.freedesktop.org/doc/dbus-specification.html),
//! (`org.freedesktop.DBus.*`) for which the following zbus proxies can be used:
//!
//! * [`zbus::fdo::PeerProxy`]
//! * [`zbus::fdo::IntrospectableProxy`]
//! * [`zbus::fdo::PropertiesProxy`]
//!
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
use zbus::dbus_proxy;
#[dbus_proxy(interface = "org.asuslinux.Daemon")]
trait Daemon {
/// InsertAsusGif method
fn insert_asus_gif(
&self,
index: u32,
file: &str,
time: u32,
count: u32,
brightness: f64,
) -> zbus::Result<String>;
/// InsertImage method
fn insert_image(
&self,
index: u32,
file: &str,
scale: f64,
angle: f64,
xy: &(f64, f64),
brightness: f64,
) -> zbus::Result<String>;
/// InsertImageGif method
fn insert_image_gif(
&self,
index: u32,
file: &str,
scale: f64,
angle: f64,
xy: &(f64, f64),
time: u32,
count: u32,
brightness: f64,
) -> zbus::Result<String>;
/// InsertPause method
fn insert_pause(&self, index: u32, millis: u64) -> zbus::Result<String>;
/// RemoveItem method
fn remove_item(&self, index: u32) -> zbus::Result<String>;
/// SetState method
fn set_state(&self, on: bool) -> zbus::Result<()>;
}

View File

@@ -1,6 +1,6 @@
[package]
name = "daemon"
version = "3.2.1"
version = "3.4.0"
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
@@ -18,18 +18,19 @@ name = "asusd"
path = "src/daemon.rs"
[dependencies]
rog_anime = { path = "../rog-anime" }
rog_types = { path = "../rog-types" }
rog_dbus = { path = "../rog-dbus" }
rusb = "^0.7"
rusb = "^0.8"
udev = "^0.6"
# cli and logging
log = "^0.4"
env_logger = "^0.8"
zbus = "^2.0.0-beta.3"
zvariant = "^2.5"
logind-zbus = "*"
zbus = "^1.9.1"
zvariant = "^2.6"
logind-zbus = "^0.7.1"
# serialisation
serde = "^1.0"

View File

@@ -1,6 +1,5 @@
use log::{error, info, warn};
use rog_fan_curve::Curve;
use rog_types::gfx_vendors::GfxVendors;
use rog_types::{gfx_vendors::GfxVendors, profile::Profile};
use serde_derive::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::fs::{File, OpenOptions};
@@ -16,6 +15,7 @@ pub static AURA_CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
pub struct Config {
pub gfx_mode: GfxVendors,
pub gfx_managed: bool,
pub gfx_vfio_enable: bool,
pub active_profile: String,
pub toggle_profiles: Vec<String>,
#[serde(skip)]
@@ -34,6 +34,7 @@ impl Default for Config {
Config {
gfx_mode: GfxVendors::Hybrid,
gfx_managed: true,
gfx_vfio_enable: false,
active_profile: "normal".into(),
toggle_profiles: vec!["normal".into(), "boost".into(), "silent".into()],
curr_fan_mode: 0,
@@ -64,6 +65,11 @@ impl Config {
} else {
if let Ok(data) = serde_json::from_str(&buf) {
return data;
} else if let Ok(data) = serde_json::from_str::<ConfigV324>(&buf) {
let config = data.into_current();
config.write();
info!("Updated config version to: {}", VERSION);
return config;
} else if let Ok(data) = serde_json::from_str::<ConfigV317>(&buf) {
let config = data.into_current();
config.write();
@@ -136,45 +142,3 @@ impl Config {
.unwrap_or_else(|err| error!("Could not write config: {}", err));
}
}
#[derive(Deserialize, Serialize)]
pub struct Profile {
pub min_percentage: u8,
pub max_percentage: u8,
pub turbo: bool,
pub fan_preset: u8,
pub fan_curve: Option<Curve>,
}
#[deprecated]
pub type CPUSettings = Profile;
impl Default for Profile {
fn default() -> Self {
Profile {
min_percentage: 0,
max_percentage: 100,
turbo: false,
fan_preset: 0,
fan_curve: None,
}
}
}
impl Profile {
pub fn new(
min_percentage: u8,
max_percentage: u8,
turbo: bool,
fan_preset: u8,
fan_curve: Option<Curve>,
) -> Self {
Profile {
min_percentage,
max_percentage,
turbo,
fan_preset,
fan_curve,
}
}
}

View File

@@ -67,7 +67,7 @@ impl AuraConfig {
} else {
if let Ok(data) = serde_json::from_str(&buf) {
return data;
} else if let Ok(data) = serde_json::from_str::<AuraConfigV320>(&buf) {
} else if let Ok(data) = serde_json::from_str::<AuraConfigV320>(&buf) {
let config = data.into_current();
config.write();
info!("Updated AuraConfig version");

View File

@@ -1,8 +1,8 @@
use rog_types::{aura_modes::AuraEffect, gfx_vendors::GfxVendors};
use rog_types::{aura_modes::AuraEffect, gfx_vendors::GfxVendors, profile::Profile};
use serde_derive::{Deserialize, Serialize};
use std::collections::BTreeMap;
use crate::config::{Config, Profile};
use crate::config::Config;
/// for parsing old v2.1.2 config
#[allow(dead_code)]
@@ -25,6 +25,7 @@ impl ConfigV212 {
gfx_mode: GfxVendors::Hybrid,
gfx_managed: self.gfx_managed,
active_profile: self.active_profile,
gfx_vfio_enable: false,
toggle_profiles: self.toggle_profiles,
curr_fan_mode: self.power_profile,
bat_charge_limit: self.bat_charge_limit,
@@ -53,6 +54,7 @@ impl ConfigV222 {
Config {
gfx_mode: GfxVendors::Hybrid,
gfx_managed: self.gfx_managed,
gfx_vfio_enable: false,
active_profile: self.active_profile,
toggle_profiles: self.toggle_profiles,
curr_fan_mode: self.power_profile,
@@ -84,6 +86,7 @@ impl ConfigV301 {
Config {
gfx_mode: GfxVendors::Hybrid,
gfx_managed: self.gfx_managed,
gfx_vfio_enable: false,
active_profile: self.active_profile,
toggle_profiles: self.toggle_profiles,
curr_fan_mode: self.curr_fan_mode,
@@ -115,6 +118,34 @@ impl ConfigV317 {
Config {
gfx_mode: GfxVendors::Hybrid,
gfx_managed: self.gfx_managed,
gfx_vfio_enable: false,
active_profile: self.active_profile,
toggle_profiles: self.toggle_profiles,
curr_fan_mode: self.curr_fan_mode,
bat_charge_limit: self.bat_charge_limit,
power_profiles: self.power_profiles,
}
}
}
#[derive(Deserialize, Serialize)]
pub struct ConfigV324 {
pub gfx_mode: GfxVendors,
pub gfx_managed: bool,
pub active_profile: String,
pub toggle_profiles: Vec<String>,
#[serde(skip)]
pub curr_fan_mode: u8,
pub bat_charge_limit: u8,
pub power_profiles: BTreeMap<String, Profile>,
}
impl ConfigV324 {
pub(crate) fn into_current(self) -> Config {
Config {
gfx_mode: GfxVendors::Hybrid,
gfx_managed: self.gfx_managed,
gfx_vfio_enable: false,
active_profile: self.active_profile,
toggle_profiles: self.toggle_profiles,
curr_fan_mode: self.curr_fan_mode,

View File

@@ -1,41 +1,25 @@
const INIT_STR: &str = "ASUS Tech.Inc.";
const PACKET_SIZE: usize = 640;
// Only these two packets must be 17 bytes
const DEV_PAGE: u8 = 0x5e;
// These bytes are in [1] position of the array
const WRITE: u8 = 0xc0;
const INIT: u8 = 0xc2;
const SET: u8 = 0xc3;
const APPLY: u8 = 0xc4;
// Used to turn the panel on and off
// The next byte can be 0x03 for "on" and 0x00 for "off"
const ON_OFF: u8 = 0x04;
use log::{error, info, warn};
use rog_types::{
anime_matrix::{
AniMeDataBuffer, AniMeImageBuffer, AniMePacketType, ANIME_PANE1_PREFIX, ANIME_PANE2_PREFIX,
use rog_anime::{
usb::{
pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on, pkts_for_init, PROD_ID,
VENDOR_ID,
},
error::AuraError,
AnimeDataBuffer, AnimePacketType,
};
use rog_types::supported::AnimeSupportedFunctions;
use rusb::{Device, DeviceHandle};
use std::error::Error;
use std::time::Duration;
use zbus::dbus_interface;
use zvariant::ObjectPath;
use crate::GetSupported;
use serde_derive::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct AnimeSupportedFunctions(pub bool);
impl GetSupported for CtrlAnimeDisplay {
type A = AnimeSupportedFunctions;
fn get_supported() -> Self::A {
AnimeSupportedFunctions(CtrlAnimeDisplay::get_device(0x0b05, 0x193b).is_ok())
AnimeSupportedFunctions(CtrlAnimeDisplay::get_device(VENDOR_ID, PROD_ID).is_ok())
}
}
@@ -43,23 +27,13 @@ pub struct CtrlAnimeDisplay {
handle: DeviceHandle<rusb::GlobalContext>,
}
//AnimatrixWrite
pub trait Dbus {
/// Write an image 34x56 pixels. Each pixel is 0-255 greyscale.
fn write_image(&self, input: AniMeImageBuffer);
/// Write a direct stream of data
fn write_direct(&self, input: AniMeDataBuffer);
fn set_on_off(&self, status: bool);
fn set_boot_on_off(&self, status: bool);
}
impl crate::ZbusAdd for CtrlAnimeDisplay {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at("/org/asuslinux/Anime", self)
.at(
&ObjectPath::from_str_unchecked("/org/asuslinux/Anime"),
self,
)
.map_err(|err| {
warn!("CtrlAnimeDisplay: add_to_server {}", err);
err
@@ -69,45 +43,19 @@ impl crate::ZbusAdd for CtrlAnimeDisplay {
}
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl Dbus for CtrlAnimeDisplay {
/// Writes a 34x56 image
fn write_image(&self, input: AniMeImageBuffer) {
self.write_image_buffer(input)
.map_or_else(|err| warn!("{}", err), |()| info!("Writing image to Anime"));
}
impl CtrlAnimeDisplay {
/// Writes a data stream of length
fn write_direct(&self, input: AniMeDataBuffer) {
self.write_data_buffer(input)
.map_or_else(|err| warn!("{}", err), |()| info!("Writing data to Anime"));
fn write(&self, input: AnimeDataBuffer) {
self.write_data_buffer(input);
}
fn set_on_off(&self, status: bool) {
let mut buffer = [0u8; PACKET_SIZE];
buffer[0] = DEV_PAGE;
buffer[1] = WRITE;
buffer[2] = ON_OFF;
if status {
buffer[3] = 0x03;
} else {
buffer[3] = 0x00;
}
self.write_bytes(&buffer);
self.write_bytes(&pkt_for_set_on(status));
}
fn set_boot_on_off(&self, status: bool) {
let status_str = if status { "on" } else { "off" };
self.do_set_boot(status).map_or_else(
|err| warn!("{}", err),
|()| info!("Turning {} the AniMe at boot/shutdown", status_str),
);
self.do_apply().map_or_else(
|err| warn!("{}", err),
|()| info!("Turning {} the AniMe at boot/shutdown", status_str),
);
fn set_boot_on_off(&self, on: bool) {
self.write_bytes(&pkt_for_set_boot(on));
self.write_bytes(&pkt_for_apply());
}
}
@@ -132,12 +80,11 @@ impl CtrlAnimeDisplay {
info!("Device has an AniMe Matrix display");
let ctrl = CtrlAnimeDisplay { handle: device };
ctrl.do_initialization()?;
ctrl.do_initialization();
Ok(ctrl)
}
#[inline]
fn get_device(vendor: u16, product: u16) -> Result<Device<rusb::GlobalContext>, rusb::Error> {
for device in rusb::devices()?.iter() {
let device_desc = device.device_descriptor()?;
@@ -148,8 +95,6 @@ impl CtrlAnimeDisplay {
Err(rusb::Error::NoDevice)
}
/// Should only be used if the bytes you are writing are verified correct
#[inline]
fn write_bytes(&self, message: &[u8]) {
match self.handle.write_control(
0x21, // request_type
@@ -166,99 +111,18 @@ impl CtrlAnimeDisplay {
},
}
}
#[inline]
fn write_data_buffer(&self, buffer: AniMeDataBuffer) -> Result<(), AuraError> {
let mut image = AniMePacketType::from(buffer);
image[0][..7].copy_from_slice(&ANIME_PANE1_PREFIX);
image[1][..7].copy_from_slice(&ANIME_PANE2_PREFIX);
for row in image.iter() {
fn write_data_buffer(&self, buffer: AnimeDataBuffer) {
let data = AnimePacketType::from(buffer);
for row in data.iter() {
self.write_bytes(row);
}
self.do_flush()?;
Ok(())
self.write_bytes(&pkt_for_flush());
}
/// Write an Animatrix image
///
/// The expected USB input here is *two* Vectors, 640 bytes in length. The two vectors
/// are each one half of the full image write.
///
/// After each write a flush is written, it is assumed that this tells the device to
/// go ahead and display the written bytes
///
/// # Note:
/// The vectors are expected to contain the full sequence of bytes as follows
///
/// - Write pane 1: 0x5e 0xc0 0x02 0x01 0x00 0x73 0x02 .. <led brightness>
/// - Write pane 2: 0x5e 0xc0 0x02 0x74 0x02 0x73 0x02 .. <led brightness>
///
/// Where led brightness is 0..255, low to high
#[inline]
fn write_image_buffer(&self, buffer: AniMeImageBuffer) -> Result<(), AuraError> {
let mut image = AniMePacketType::from(buffer);
image[0][..7].copy_from_slice(&ANIME_PANE1_PREFIX);
image[1][..7].copy_from_slice(&ANIME_PANE2_PREFIX);
for row in image.iter() {
self.write_bytes(row);
}
self.do_flush()?;
Ok(())
}
#[inline]
fn do_initialization(&self) -> Result<(), AuraError> {
let mut init = [0; PACKET_SIZE];
init[0] = DEV_PAGE; // This is the USB page we're using throughout
for (idx, byte) in INIT_STR.as_bytes().iter().enumerate() {
init[idx + 1] = *byte
}
self.write_bytes(&init);
// clear the init array and write other init message
for ch in init.iter_mut() {
*ch = 0;
}
init[0] = DEV_PAGE; // write it to be sure?
init[1] = INIT;
self.write_bytes(&init);
Ok(())
}
#[inline]
fn do_flush(&self) -> Result<(), AuraError> {
let mut flush = [0; PACKET_SIZE];
flush[0] = DEV_PAGE;
flush[1] = WRITE;
flush[2] = 0x03;
self.write_bytes(&flush);
Ok(())
}
#[inline]
fn do_set_boot(&self, status: bool) -> Result<(), AuraError> {
let mut flush = [0; PACKET_SIZE];
flush[0] = DEV_PAGE;
flush[1] = SET;
flush[2] = 0x01;
flush[3] = if status { 0x00 } else { 0x80 };
self.write_bytes(&flush);
Ok(())
}
#[inline]
fn do_apply(&self) -> Result<(), AuraError> {
let mut flush = [0; PACKET_SIZE];
flush[0] = DEV_PAGE;
flush[1] = APPLY;
flush[2] = 0x01;
flush[3] = 0x80;
self.write_bytes(&flush);
Ok(())
fn do_initialization(&self) {
let pkts = pkts_for_init();
self.write_bytes(&pkts[0]);
self.write_bytes(&pkts[1]);
}
}

View File

@@ -1,21 +1,17 @@
use crate::{config::Config, error::RogError, GetSupported};
//use crate::dbus::DbusEvents;
use log::{info, warn};
use serde_derive::{Deserialize, Serialize};
use rog_types::supported::ChargeSupportedFunctions;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;
use std::sync::Arc;
use std::sync::Mutex;
use zbus::dbus_interface;
use zvariant::ObjectPath;
static BAT_CHARGE_PATH: &str = "/sys/class/power_supply/BAT0/charge_control_end_threshold";
#[derive(Serialize, Deserialize)]
pub struct ChargeSupportedFunctions {
pub charge_level_set: bool,
}
impl GetSupported for CtrlCharge {
type A = ChargeSupportedFunctions;
@@ -63,7 +59,10 @@ impl CtrlCharge {
impl crate::ZbusAdd for CtrlCharge {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at("/org/asuslinux/Charge", self)
.at(
&ObjectPath::from_str_unchecked("/org/asuslinux/Charge"),
self,
)
.map_err(|err| {
warn!("CtrlCharge: add_to_server {}", err);
err

View File

@@ -1,40 +1,30 @@
use crate::error::RogError;
use crate::{
config::{Config, Profile},
GetSupported,
};
use crate::{config::Config, GetSupported};
use log::{info, warn};
use rog_types::profile::{FanLevel, ProfileEvent};
use serde_derive::{Deserialize, Serialize};
use rog_types::{profile::{FanLevel, Profile, ProfileEvent}, supported::FanCpuSupportedFunctions};
use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;
use std::sync::Arc;
use std::sync::Mutex;
use zbus::{dbus_interface, fdo::Error};
use zvariant::ObjectPath;
static FAN_TYPE_1_PATH: &str = "/sys/devices/platform/asus-nb-wmi/throttle_thermal_policy";
static FAN_TYPE_2_PATH: &str = "/sys/devices/platform/asus-nb-wmi/fan_boost_mode";
static AMD_BOOST_PATH: &str = "/sys/devices/system/cpu/cpufreq/boost";
pub struct CtrlFanAndCPU {
pub struct CtrlFanAndCpu {
pub path: &'static str,
config: Arc<Mutex<Config>>,
}
#[derive(Serialize, Deserialize)]
pub struct FanCpuSupportedFunctions {
pub stock_fan_modes: bool,
pub min_max_freq: bool,
pub fan_curve_set: bool,
}
impl GetSupported for CtrlFanAndCPU {
impl GetSupported for CtrlFanAndCpu {
type A = FanCpuSupportedFunctions;
fn get_supported() -> Self::A {
FanCpuSupportedFunctions {
stock_fan_modes: CtrlFanAndCPU::get_fan_path().is_ok(),
stock_fan_modes: CtrlFanAndCpu::get_fan_path().is_ok(),
min_max_freq: intel_pstate::PState::new().is_ok(),
fan_curve_set: rog_fan_curve::Board::from_board_name().is_some(),
}
@@ -42,11 +32,11 @@ impl GetSupported for CtrlFanAndCPU {
}
pub struct DbusFanAndCpu {
inner: Arc<Mutex<CtrlFanAndCPU>>,
inner: Arc<Mutex<CtrlFanAndCpu>>,
}
impl DbusFanAndCpu {
pub fn new(inner: Arc<Mutex<CtrlFanAndCPU>>) -> Self {
pub fn new(inner: Arc<Mutex<CtrlFanAndCpu>>) -> Self {
Self { inner }
}
}
@@ -90,60 +80,61 @@ impl DbusFanAndCpu {
}
/// Fetch the active profile name
fn active_profile_name(&mut self) -> String {
fn active_profile_name(&mut self) -> zbus::fdo::Result<String> {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() {
cfg.read();
return cfg.active_profile.clone();
return Ok(cfg.active_profile.clone());
}
}
"Failed".to_string()
Err(Error::Failed(
"Failed to get active profile name".to_string(),
))
}
// TODO: Profile can't implement Type because of Curve
/// Fetch the active profile details
fn profile(&mut self) -> String {
fn profile(&mut self) -> zbus::fdo::Result<String> {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() {
cfg.read();
if let Some(profile) = cfg.power_profiles.get(&cfg.active_profile) {
if let Ok(json) = serde_json::to_string(profile) {
return json;
if let Ok(json) = serde_json::to_string_pretty(profile) {
return Ok(json);
}
}
}
}
"Failed".to_string()
Err(Error::Failed(
"Failed to get active profile details".to_string(),
))
}
fn profiles(&mut self) -> String {
/// Fetch all profile data
fn profiles(&mut self) -> zbus::fdo::Result<String> {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() {
cfg.read();
if let Ok(json) = serde_json::to_string(&cfg.power_profiles) {
return json;
if let Ok(json) = serde_json::to_string_pretty(&cfg.power_profiles) {
return Ok(json);
}
}
}
"Failed".to_string()
Err(Error::Failed(
"Failed to get all profile details".to_string(),
))
}
fn profile_names(&self) -> String {
fn profile_names(&self) -> zbus::fdo::Result<Vec<String>> {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() {
cfg.read();
let profile_names: String = cfg
.power_profiles
.keys()
.cloned()
.collect::<Vec<String>>()
.join(", ");
return profile_names;
let profile_names = cfg.power_profiles.keys().cloned().collect::<Vec<String>>();
return Ok(profile_names);
}
}
"Failed".to_string()
Err(Error::Failed("Failed to get all profile names".to_string()))
}
fn remove(&self, profile: &str) -> zbus::fdo::Result<()> {
@@ -172,7 +163,7 @@ impl DbusFanAndCpu {
}
}
return Err(Error::Failed("Failed to lock configuration".to_string()));
Err(Error::Failed("Failed to lock configuration".to_string()))
}
#[dbus_interface(signal)]
@@ -182,7 +173,10 @@ impl DbusFanAndCpu {
impl crate::ZbusAdd for DbusFanAndCpu {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at("/org/asuslinux/Profile", self)
.at(
&ObjectPath::from_str_unchecked("/org/asuslinux/Profile"),
self,
)
.map_err(|err| {
warn!("DbusFanAndCpu: add_to_server {}", err);
err
@@ -191,7 +185,7 @@ impl crate::ZbusAdd for DbusFanAndCpu {
}
}
impl crate::Reloadable for CtrlFanAndCPU {
impl crate::Reloadable for CtrlFanAndCpu {
fn reload(&mut self) -> Result<(), RogError> {
if let Ok(mut config) = self.config.clone().try_lock() {
let profile = config.active_profile.clone();
@@ -205,11 +199,11 @@ impl crate::Reloadable for CtrlFanAndCPU {
}
}
impl CtrlFanAndCPU {
impl CtrlFanAndCpu {
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
let path = CtrlFanAndCPU::get_fan_path()?;
let path = CtrlFanAndCpu::get_fan_path()?;
info!("Device has thermal throttle control");
Ok(CtrlFanAndCPU { path, config })
Ok(CtrlFanAndCpu { path, config })
}
fn get_fan_path() -> Result<&'static str, RogError> {

View File

@@ -6,10 +6,16 @@ use crate::error::RogError;
#[derive(Debug)]
pub enum GfxError {
ParseVendor,
ParsePower,
Bus(String, std::io::Error),
DisplayManagerAction(String, ExitStatus),
DisplayManagerTimeout(String),
GsyncModeActive,
VfioBuiltin,
VfioDisabled,
MissingModule(String),
Modprobe(String),
Command(String, std::io::Error),
}
impl fmt::Display for GfxError {
@@ -17,6 +23,7 @@ impl fmt::Display for GfxError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
GfxError::ParseVendor => write!(f, "Could not parse vendor name"),
GfxError::ParsePower => write!(f, "Could not parse dGPU power status"),
GfxError::Bus(func, error) => write!(f, "Bus error: {}: {}", func, error),
GfxError::DisplayManagerAction(action, status) => {
write!(f, "Display-manager action {} failed: {}", action, status)
@@ -28,6 +35,16 @@ impl fmt::Display for GfxError {
f,
"Can not switch gfx modes when dedicated/G-Sync mode is active"
),
GfxError::VfioBuiltin => write!(
f,
"Can not switch to vfio mode if the modules are built in to kernel"
),
GfxError::VfioDisabled => {
write!(f, "Can not switch to vfio mode if disabled in config file")
}
GfxError::MissingModule(m) => write!(f, "The module {} is missing", m),
GfxError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail),
GfxError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error),
}
}
}

View File

@@ -3,18 +3,19 @@ use ctrl_gfx::*;
use ctrl_rog_bios::CtrlRogBios;
use log::{error, info, warn};
use logind_zbus::{
types::{SessionClass, SessionInfo, SessionType},
types::{SessionClass, SessionInfo, SessionState, SessionType},
ManagerProxy, SessionProxy,
};
use rog_types::gfx_vendors::{GfxRequiredUserAction, GfxVendors};
use std::sync::mpsc;
use rog_types::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors};
use std::{io::Write, ops::Add, path::Path, time::Instant};
use std::{iter::FromIterator, thread::JoinHandle};
use std::{process::Command, thread::sleep, time::Duration};
use std::{str::FromStr, sync::mpsc};
use std::{sync::Arc, sync::Mutex};
use sysfs_class::{PciDevice, SysClass};
use system::{GraphicsDevice, PciBus};
use zbus::{dbus_interface, Connection};
use zvariant::ObjectPath;
use crate::*;
@@ -33,7 +34,7 @@ pub struct CtrlGraphics {
trait Dbus {
fn vendor(&self) -> zbus::fdo::Result<GfxVendors>;
fn power(&self) -> String;
fn power(&self) -> zbus::fdo::Result<GfxPower>;
fn set_vendor(&mut self, vendor: GfxVendors) -> zbus::fdo::Result<GfxRequiredUserAction>;
fn notify_gfx(&self, vendor: &GfxVendors) -> zbus::Result<()>;
fn notify_action(&self, action: &GfxRequiredUserAction) -> zbus::Result<()>;
@@ -48,8 +49,11 @@ impl Dbus for CtrlGraphics {
})
}
fn power(&self) -> String {
Self::get_runtime_status().unwrap_or_else(|err| format!("Get power status failed: {}", err))
fn power(&self) -> zbus::fdo::Result<GfxPower> {
Self::get_runtime_status().map_err(|err| {
error!("GFX: {}", err);
zbus::fdo::Error::Failed(format!("GFX fail: {}", err))
})
}
fn set_vendor(&mut self, vendor: GfxVendors) -> zbus::fdo::Result<GfxRequiredUserAction> {
@@ -75,7 +79,7 @@ impl Dbus for CtrlGraphics {
impl ZbusAdd for CtrlGraphics {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at("/org/asuslinux/Gfx", self)
.at(&ObjectPath::from_str_unchecked("/org/asuslinux/Gfx"), self)
.map_err(|err| {
warn!("GFX: CtrlGraphics: add_to_server {}", err);
err
@@ -95,10 +99,8 @@ impl Reloadable for CtrlGraphics {
impl CtrlGraphics {
pub fn new(config: Arc<Mutex<Config>>) -> std::io::Result<CtrlGraphics> {
let bus = PciBus::new()?;
info!("GFX: Rescanning PCI bus");
bus.rescan()?;
let devs = PciDevice::all()?;
let functions = |parent: &PciDevice| -> Vec<PciDevice> {
@@ -169,7 +171,6 @@ impl CtrlGraphics {
config.gfx_mode = vendor;
config.write();
}
// TODO: Error here
}
/// Associated method to get which vendor mode is set
@@ -181,12 +182,22 @@ impl CtrlGraphics {
Ok(GfxVendors::Hybrid)
}
fn get_runtime_status() -> Result<String, RogError> {
const PATH: &str = "/sys/bus/pci/devices/0000:01:00.0/power/runtime_status";
let buf = std::fs::read_to_string(PATH).map_err(|err| RogError::Read(PATH.into(), err))?;
Ok(buf)
fn get_runtime_status() -> Result<GfxPower, RogError> {
let path = Path::new("/sys/bus/pci/devices/0000:01:00.0/power/runtime_status");
if path.exists() {
let buf = std::fs::read_to_string(path).map_err(|err| {
RogError::Read(
"/sys/bus/pci/devices/0000:01:00.0/power/runtime_status".to_string(),
err,
)
})?;
Ok(GfxPower::from_str(&buf)?)
} else {
Ok(GfxPower::Off)
}
}
/// Some systems have a fallback service to load nouveau if nvidia fails
fn toggle_fallback_service(vendor: GfxVendors) -> Result<(), RogError> {
let action = if vendor == GfxVendors::Nvidia {
info!("GFX: Enabling nvidia-fallback.service");
@@ -213,6 +224,7 @@ impl CtrlGraphics {
Ok(())
}
/// Write the appropriate xorg config for the chosen mode
fn write_xorg_conf(vendor: GfxVendors) -> Result<(), RogError> {
let text = if vendor == GfxVendors::Nvidia {
[PRIMARY_GPU_BEGIN, PRIMARY_GPU_NVIDIA, PRIMARY_GPU_END].concat()
@@ -239,6 +251,7 @@ impl CtrlGraphics {
Ok(())
}
/// Creates the full modprobe.conf required for vfio pass-through
fn get_vfio_conf(devices: &[GraphicsDevice]) -> Vec<u8> {
let mut vifo = MODPROBE_VFIO.to_vec();
for (d_count, dev) in devices.iter().enumerate() {
@@ -303,7 +316,7 @@ impl CtrlGraphics {
.map_err(|err| RogError::Command("device unbind error".into(), err))
}
fn do_driver_action(driver: &str, action: &str) -> Result<(), RogError> {
fn do_driver_action(driver: &str, action: &str) -> Result<(), GfxError> {
let mut cmd = Command::new(action);
cmd.arg(driver);
@@ -318,7 +331,7 @@ impl CtrlGraphics {
let output = cmd
.output()
.map_err(|err| RogError::Command(format!("{:?}", cmd), err))?;
.map_err(|err| GfxError::Command(format!("{:?}", cmd), err))?;
if !output.status.success() {
if output
.stderr
@@ -326,6 +339,9 @@ impl CtrlGraphics {
{
return Ok(());
}
if output.stderr.ends_with("is builtin.\n".as_bytes()) {
return Err(GfxError::VfioBuiltin);
}
if output.stderr.ends_with("Permission denied\n".as_bytes()) {
warn!(
"{} {} failed: {:?}",
@@ -336,6 +352,11 @@ impl CtrlGraphics {
warn!("GFX: It may be safe to ignore the above error, run `lsmod |grep {}` to confirm modules loaded", driver);
return Ok(());
}
if String::from_utf8_lossy(&output.stderr)
.contains(&format!("Module {} not found", driver))
{
return Err(GfxError::MissingModule(driver.into()));
}
if count >= MAX_TRIES {
let msg = format!(
"{} {} failed: {:?}",
@@ -343,14 +364,14 @@ impl CtrlGraphics {
driver,
String::from_utf8_lossy(&output.stderr)
);
return Err(RogError::Modprobe(msg));
return Err(GfxError::Modprobe(msg));
}
} else if output.status.success() {
return Ok(());
}
count += 1;
std::thread::sleep(std::time::Duration::from_millis(250));
std::thread::sleep(std::time::Duration::from_millis(50));
}
}
@@ -419,6 +440,7 @@ impl CtrlGraphics {
/// The daemon needs direct access to this function when it detects that the
pub fn do_vendor_tasks(
vendor: GfxVendors,
vfio_enable: bool,
devices: &[GraphicsDevice],
bus: &PciBus,
) -> Result<(), RogError> {
@@ -431,23 +453,33 @@ impl CtrlGraphics {
match vendor {
GfxVendors::Nvidia | GfxVendors::Hybrid | GfxVendors::Compute => {
for driver in VFIO_DRIVERS.iter() {
Self::do_driver_action(driver, "rmmod")?;
if vfio_enable {
for driver in VFIO_DRIVERS.iter() {
Self::do_driver_action(driver, "rmmod")?;
}
}
for driver in NVIDIA_DRIVERS.iter() {
Self::do_driver_action(driver, "modprobe")?;
}
}
GfxVendors::Vfio => {
for driver in NVIDIA_DRIVERS.iter() {
Self::do_driver_action(driver, "rmmod")?;
if vfio_enable {
Self::do_driver_action("nouveau", "rmmod")?;
for driver in NVIDIA_DRIVERS.iter() {
Self::do_driver_action(driver, "rmmod")?;
}
Self::unbind_only(&devices)?;
Self::do_driver_action("vfio-pci", "modprobe")?;
} else {
return Err(GfxError::VfioDisabled.into());
}
Self::unbind_only(&devices)?;
Self::do_driver_action("vfio-pci", "modprobe")?;
}
GfxVendors::Integrated => {
for driver in VFIO_DRIVERS.iter() {
Self::do_driver_action(driver, "rmmod")?;
Self::do_driver_action("nouveau", "rmmod")?;
if vfio_enable {
for driver in VFIO_DRIVERS.iter() {
Self::do_driver_action(driver, "rmmod")?;
}
}
for driver in NVIDIA_DRIVERS.iter() {
Self::do_driver_action(driver, "rmmod")?;
@@ -455,22 +487,23 @@ impl CtrlGraphics {
Self::unbind_remove_nvidia(&devices)?;
}
}
Ok(())
}
fn graphical_session_alive(
/// Check if the user has any graphical uiser sessions that are active or online
fn graphical_user_sessions_exist(
connection: &Connection,
sessions: &[SessionInfo],
) -> Result<bool, RogError> {
for session in sessions {
let session_proxy = SessionProxy::new(&connection, session)?;
let session_proxy = SessionProxy::new(connection, session)?;
if session_proxy.get_class()? == SessionClass::User {
match session_proxy.get_type()? {
SessionType::X11 | SessionType::Wayland | SessionType::MIR => {
//if session_proxy.get_active()? {
return Ok(true);
//}
match session_proxy.get_state()? {
SessionState::Online | SessionState::Active => return Ok(true),
SessionState::Closing | SessionState::Invalid => {}
}
}
_ => {}
}
@@ -499,12 +532,11 @@ impl CtrlGraphics {
loop {
let tmp = manager.list_sessions()?;
if !tmp.iter().eq(&sessions) {
warn!("GFX: Sessions list changed");
warn!("GFX: Old list:\n{:?}\nNew list:\n{:?}", &sessions, &tmp);
info!("GFX thread: Sessions list changed");
sessions = tmp;
}
if !Self::graphical_session_alive(&connection, &sessions)? {
if !Self::graphical_user_sessions_exist(&connection, &sessions)? {
break;
}
@@ -523,36 +555,28 @@ impl CtrlGraphics {
sleep(SLEEP_PERIOD);
}
info!("GFX: all graphical user sessions ended, continuing");
info!("GFX thread: all graphical user sessions ended, continuing");
Self::do_display_manager_action("stop")?;
Self::wait_display_manager_state("inactive")?;
match Self::wait_display_manager_state("inactive") {
Ok(_) => info!("GFX: display-manager stopped"),
Err(err) => {
warn!("GFX: {}", err);
warn!("GFX: Retry stop display manager");
Self::do_display_manager_action("stop")?;
Self::wait_display_manager_state("inactive")?;
}
}
let vfio_enable = if let Ok(config) = config.lock() {
config.gfx_vfio_enable
} else {
false
};
Self::do_vendor_tasks(vendor, &devices, &bus)?;
Self::do_display_manager_action("start")?;
if Self::wait_display_manager_state("active").is_err() {
error!("GFX: display-manager failed to start normally, attempting restart");
Self::do_display_manager_action("restart")?;
Self::wait_display_manager_state("active")?;
}
Self::do_vendor_tasks(vendor, vfio_enable, &devices, &bus)?;
Self::do_display_manager_action("restart")?;
// Save selected mode in case of reboot
Self::save_gfx_mode(vendor, config);
info!("GFX: display-manager started");
info!("GFX thread: display-manager started");
let v: &str = vendor.into();
info!("GFX: Graphics mode changed to {} successfully", v);
info!("GFX thread: Graphics mode changed to {} successfully", v);
Ok(format!("Graphics mode changed to {} successfully", v))
}
/// Before starting a new thread the old one *must* be cancelled
fn cancel_thread(&self) {
if let Ok(lock) = self.thread_kill.lock() {
if let Some(tx) = lock.as_ref() {
@@ -560,7 +584,7 @@ impl CtrlGraphics {
info!("GFX: Cancelling previous thread");
tx.send(true)
.map_err(|err| {
warn!("GFX: {}", err);
warn!("GFX thread: {}", err);
})
.ok();
}
@@ -605,6 +629,17 @@ impl CtrlGraphics {
return Err(GfxError::GsyncModeActive.into());
}
}
let vfio_enable = if let Ok(config) = self.config.lock() {
config.gfx_vfio_enable
} else {
false
};
if !vfio_enable && matches!(vendor, GfxVendors::Vfio) {
return Err(GfxError::VfioDisabled.into());
}
// Must always cancel any thread running
self.cancel_thread();
// determine which method we need here
@@ -618,7 +653,8 @@ impl CtrlGraphics {
info!("GFX: mode change does not require logout");
let devices = self.nvidia.clone();
let bus = self.bus.clone();
Self::do_vendor_tasks(vendor, &devices, &bus)?;
Self::do_vendor_tasks(vendor, vfio_enable, &devices, &bus)?;
info!("GFX: Graphics mode changed to {}", <&str>::from(vendor));
}
// TODO: undo if failed? Save last mode, catch errors...
Ok(action_required)
@@ -629,7 +665,14 @@ impl CtrlGraphics {
let vendor = self.get_gfx_mode()?;
let devices = self.nvidia.clone();
let bus = self.bus.clone();
Self::do_vendor_tasks(vendor, &devices, &bus)?;
let vfio_enable = if let Ok(config) = self.config.lock() {
config.gfx_vfio_enable
} else {
false
};
Self::do_vendor_tasks(vendor, vfio_enable, &devices, &bus)?;
Self::toggle_fallback_service(vendor)?;
Ok(())
}

View File

@@ -10,25 +10,17 @@ use crate::{
laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES},
};
use log::{error, info, warn};
use rog_types::{LED_MSG_LEN, aura_modes::{AuraEffect, AuraModeNum, LedBrightness}};
use rog_types::{LED_MSG_LEN, aura_modes::{AuraEffect, AuraModeNum, LedBrightness}, supported::LedSupportedFunctions};
use std::fs::OpenOptions;
use std::io::{Read, Write};
use std::path::Path;
use std::sync::Arc;
use std::sync::Mutex;
use zbus::dbus_interface;
use zvariant::ObjectPath;
use crate::GetSupported;
use serde_derive::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct LedSupportedFunctions {
pub brightness_set: bool,
pub stock_led_modes: Option<Vec<AuraModeNum>>,
pub multizone_led_mode: bool,
pub per_key_led_mode: bool,
}
impl GetSupported for CtrlKbdBacklight {
type A = LedSupportedFunctions;
@@ -37,14 +29,10 @@ impl GetSupported for CtrlKbdBacklight {
let multizone_led_mode = false;
let per_key_led_mode = false;
let laptop = LaptopLedData::get_data();
let stock_led_modes = if let Some(data) = laptop {
if data.standard.is_empty() {
None
} else {
Some(data.standard)
}
} else {
let stock_led_modes = if laptop.standard.is_empty() {
None
} else {
Some(laptop.standard)
};
LedSupportedFunctions {
@@ -74,16 +62,10 @@ impl DbusKbdBacklight {
}
}
trait Dbus {
fn set_led(&mut self, data: String);
fn ledmode(&self) -> String;
fn notify_led(&self, data: &str) -> zbus::Result<()>;
}
impl crate::ZbusAdd for DbusKbdBacklight {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at("/org/asuslinux/Led", self)
.at(&ObjectPath::from_str_unchecked("/org/asuslinux/Led"), self)
.map_err(|err| {
error!("DbusKbdBacklight: add_to_server {}", err);
})
@@ -249,7 +231,7 @@ impl crate::CtrlTask for CtrlKbdBacklight {
}
return Ok(());
}
Err(RogError::ParseLED)
Err(RogError::ParseLed)
}
}
@@ -318,15 +300,16 @@ impl CtrlKbdBacklight {
pub fn set_brightness(&self, brightness: LedBrightness) -> Result<(), RogError> {
let path = Path::new(&self.bright_node);
let mut file = OpenOptions::new()
.write(true)
.open(&path)
.map_err(|err| match err.kind() {
std::io::ErrorKind::NotFound => {
RogError::MissingLedBrightNode((&self.bright_node).into(), err)
}
_ => RogError::Path((&self.bright_node).into(), err),
})?;
let mut file =
OpenOptions::new()
.write(true)
.open(&path)
.map_err(|err| match err.kind() {
std::io::ErrorKind::NotFound => {
RogError::MissingLedBrightNode((&self.bright_node).into(), err)
}
_ => RogError::Path((&self.bright_node).into(), err),
})?;
file.write_all(&[brightness.as_char_code()])
.map_err(|err| RogError::Read("buffer".into(), err))?;
Ok(())

View File

@@ -1,6 +1,6 @@
use crate::{config::Config, error::RogError, GetSupported};
use log::{error, info, warn};
use serde_derive::{Deserialize, Serialize};
use rog_types::supported::RogBiosSupportedFunctions;
use std::fs::OpenOptions;
use std::io::BufRead;
use std::io::{Read, Write};
@@ -9,6 +9,7 @@ use std::process::Command;
use std::sync::Arc;
use std::sync::Mutex;
use zbus::dbus_interface;
use zvariant::ObjectPath;
const INITRAMFS_PATH: &str = "/usr/sbin/update-initramfs";
const DRACUT_PATH: &str = "/usr/bin/dracut";
@@ -22,12 +23,6 @@ pub struct CtrlRogBios {
_config: Arc<Mutex<Config>>,
}
#[derive(Serialize, Deserialize)]
pub struct RogBiosSupportedFunctions {
pub post_sound_toggle: bool,
pub dedicated_gfx_toggle: bool,
}
impl GetSupported for CtrlRogBios {
type A = RogBiosSupportedFunctions;
@@ -101,7 +96,10 @@ impl CtrlRogBios {
impl crate::ZbusAdd for CtrlRogBios {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at("/org/asuslinux/RogBios", self)
.at(
&ObjectPath::from_str_unchecked("/org/asuslinux/RogBios"),
self,
)
.map_err(|err| {
warn!("CtrlRogBios: add_to_server {}", err);
err

View File

@@ -1,14 +1,16 @@
use log::warn;
use serde_derive::{Deserialize, Serialize};
use zbus::dbus_interface;
use zvariant::ObjectPath;
use crate::{
ctrl_anime::{AnimeSupportedFunctions, CtrlAnimeDisplay},
ctrl_charge::{ChargeSupportedFunctions, CtrlCharge},
ctrl_fan_cpu::{CtrlFanAndCPU, FanCpuSupportedFunctions},
ctrl_leds::{CtrlKbdBacklight, LedSupportedFunctions},
ctrl_rog_bios::{CtrlRogBios, RogBiosSupportedFunctions},
GetSupported,
ctrl_anime::CtrlAnimeDisplay, ctrl_charge::CtrlCharge, ctrl_fan_cpu::CtrlFanAndCpu,
ctrl_leds::CtrlKbdBacklight, ctrl_rog_bios::CtrlRogBios, GetSupported,
};
use rog_types::supported::{
AnimeSupportedFunctions, ChargeSupportedFunctions, FanCpuSupportedFunctions,
LedSupportedFunctions, RogBiosSupportedFunctions,
};
#[derive(Serialize, Deserialize)]
@@ -30,7 +32,10 @@ impl SupportedFunctions {
impl crate::ZbusAdd for SupportedFunctions {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at("/org/asuslinux/Supported", self)
.at(
&ObjectPath::from_str_unchecked("/org/asuslinux/Supported"),
self,
)
.map_err(|err| {
warn!("SupportedFunctions: add_to_server {}", err);
err
@@ -47,7 +52,7 @@ impl GetSupported for SupportedFunctions {
keyboard_led: CtrlKbdBacklight::get_supported(),
anime_ctrl: CtrlAnimeDisplay::get_supported(),
charge_ctrl: CtrlCharge::get_supported(),
fan_cpu_ctrl: CtrlFanAndCPU::get_supported(),
fan_cpu_ctrl: CtrlFanAndCpu::get_supported(),
rog_bios_ctrl: CtrlRogBios::get_supported(),
}
}

View File

@@ -1,10 +1,13 @@
use daemon::{ctrl_fan_cpu::{CtrlFanAndCPU, DbusFanAndCpu}, laptops::LaptopLedData};
use daemon::ctrl_leds::{CtrlKbdBacklight, DbusKbdBacklight};
use daemon::{
config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported,
};
use daemon::{config_aura::AuraConfig, ctrl_charge::CtrlCharge};
use daemon::{ctrl_anime::CtrlAnimeDisplay, ctrl_gfx::gfx::CtrlGraphics};
use daemon::{
ctrl_fan_cpu::{CtrlFanAndCpu, DbusFanAndCpu},
laptops::LaptopLedData,
};
use daemon::{CtrlTask, Reloadable, ZbusAdd};
use log::LevelFilter;
@@ -20,6 +23,7 @@ use daemon::ctrl_rog_bios::CtrlRogBios;
use std::convert::Into;
use zbus::fdo;
use zbus::Connection;
use zvariant::ObjectPath;
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut logger = env_logger::Builder::new();
@@ -106,7 +110,12 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
warn!("Dedicated GFX toggle is on but driver mode is not nvidia \nSetting to nvidia driver mode");
let devices = ctrl.devices();
let bus = ctrl.bus();
CtrlGraphics::do_vendor_tasks(GfxVendors::Nvidia, &devices, &bus)?;
CtrlGraphics::do_vendor_tasks(
GfxVendors::Nvidia,
false,
&devices,
&bus,
)?;
} else if ded == 0 {
info!("Dedicated GFX toggle is off");
}
@@ -126,7 +135,7 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
// Collect tasks for task thread
let mut tasks: Vec<Arc<Mutex<dyn CtrlTask + Send>>> = Vec::new();
if let Ok(mut ctrl) = CtrlFanAndCPU::new(config).map_err(|err| {
if let Ok(mut ctrl) = CtrlFanAndCpu::new(config).map_err(|err| {
error!("Profile control: {}", err);
}) {
ctrl.reload()
@@ -135,28 +144,21 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
DbusFanAndCpu::new(tmp).add_to_server(&mut object_server);
};
if let Some(laptop) = LaptopLedData::get_data() {
if !laptop.standard.is_empty() {
let aura_config = AuraConfig::load(&laptop);
if let Ok(ctrl) = CtrlKbdBacklight::new(
laptop,
aura_config,
)
.map_err(|err| {
error!("Keyboard control: {}", err);
err
}) {
let tmp = Arc::new(Mutex::new(ctrl));
DbusKbdBacklight::new(tmp.clone()).add_to_server(&mut object_server);
tasks.push(tmp);
}
}}
let laptop = LaptopLedData::get_data();
let aura_config = AuraConfig::load(&laptop);
if let Ok(ctrl) = CtrlKbdBacklight::new(laptop, aura_config).map_err(|err| {
error!("Keyboard control: {}", err);
err
}) {
let tmp = Arc::new(Mutex::new(ctrl));
DbusKbdBacklight::new(tmp.clone()).add_to_server(&mut object_server);
tasks.push(tmp);
}
// TODO: implement messaging between threads to check fails
// These tasks generally read a sys path or file to check for a
// change
let _handle = std::thread::Builder::new()
let handle = std::thread::Builder::new()
.name("asusd watch".to_string())
.spawn(move || loop {
std::thread::sleep(std::time::Duration::from_millis(100));
@@ -173,18 +175,24 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
});
object_server
.with("/org/asuslinux/Charge", |obj: &CtrlCharge| {
let x = obj.limit();
obj.notify_charge(x as u8)
})
.with(
&ObjectPath::from_str_unchecked("/org/asuslinux/Charge"),
|obj: &CtrlCharge| {
let x = obj.limit();
obj.notify_charge(x as u8)
},
)
.map_err(|err| {
warn!("object_server notify_charge error: {}", err);
})
.ok();
loop {
if let Err(err) = &handle {
error!("{}", err);
}
if let Err(err) = object_server.try_handle_next() {
eprintln!("{}", err);
error!("{}", err);
}
}
}

View File

@@ -10,7 +10,7 @@ use crate::ctrl_gfx::error::GfxError;
pub enum RogError {
ParseFanLevel,
ParseVendor,
ParseLED,
ParseLed,
MissingProfile(String),
Udev(String, std::io::Error),
Path(String, std::io::Error),
@@ -38,7 +38,7 @@ impl fmt::Display for RogError {
match self {
RogError::ParseFanLevel => write!(f, "Parse profile error"),
RogError::ParseVendor => write!(f, "Parse gfx vendor error"),
RogError::ParseLED => write!(f, "Parse LED error"),
RogError::ParseLed => write!(f, "Parse LED error"),
RogError::MissingProfile(profile) => write!(f, "Profile does not exist {}", profile),
RogError::Udev(deets, error) => write!(f, "udev {}: {}", deets, error),
RogError::Path(path, error) => write!(f, "Path {}: {}", path, error),
@@ -80,6 +80,7 @@ impl From<GraphicsError> for RogError {
fn from(err: GraphicsError) -> Self {
match err {
GraphicsError::ParseVendor => RogError::GfxSwitching(GfxError::ParseVendor),
GraphicsError::ParsePower => RogError::GfxSwitching(GfxError::ParsePower),
}
}
}

View File

@@ -49,15 +49,24 @@ pub struct LaptopLedData {
}
impl LaptopLedData {
pub fn get_data() -> Option<Self> {
pub fn get_data() -> Self {
let dmi = sysfs_class::DmiId::default();
let board_name = dmi.board_name().expect("Could not get board_name");
let prod_family = dmi.product_family().expect("Could not get product_family");
if let Some(modes) = LedSupportFile::load_from_config() {
return modes.matcher(&prod_family, &board_name);
if let Some(data) = modes.matcher(&prod_family, &board_name) {
return data;
}
}
info!("Using generic LED control for keyboard brightness only");
LaptopLedData {
prod_family,
board_names: vec![board_name],
standard: vec![],
multizone: false,
per_key: false,
}
None
}
}

BIN
data/anime/asus/festive/Cupid.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
data/anime/asus/gaming/Bird.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
data/anime/asus/gaming/FPS.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
data/anime/asus/gaming/Fight.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
data/anime/asus/gaming/MOBA.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
data/anime/asus/gaming/UFO.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
data/anime/asus/music/DJ.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
data/anime/asus/rog/Fragment.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
data/anime/asus/rog/Sunset.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
data/anime/asus/trend/Dog.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

BIN
data/anime/asus/trend/Ski.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

BIN
data/anime/asus/trend/Wave.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
data/anime/custom/rust.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -35,14 +35,14 @@ per_key = false
[[led_data]]
prod_family = "ROG Strix"
board_names = ["G531GW"]
board_names = ["G531GW", "G733QS", "G733QR"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = false
per_key = true
[[led_data]]
prod_family = "ROG Strix"
board_names = ["GX531", "G512LV", "G712LV"]
board_names = ["GX531", "G512LV", "G712LV", "G712LW"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = true
per_key = false
@@ -80,4 +80,11 @@ prod_family = "ROG"
board_names = ["GL553VE"]
standard = ["Static", "Breathe", "Strobe"]
multizone = true
per_key = false
[[led_data]]
prod_family = "ROG Zephyrus G14"
board_names = ["GA401Q"]
standard = ["Static", "Breathe", "Pulse"]
multizone = false
per_key = false

14
data/asusd-user.service Normal file
View File

@@ -0,0 +1,14 @@
[Unit]
Description=ASUS User Daemon
StartLimitInterval=200
StartLimitBurst=2
[Service]
ExecStartPre=/usr/bin/sleep 2
ExecStart=/usr/bin/asusd-user
Restart=on-failure
RestartSec=1
Type=simple
[Install]
WantedBy=default.target

64
data/user-example.json Normal file
View File

@@ -0,0 +1,64 @@
{
"anime": [
{
"AsusAnimation": {
"file": "/usr/share/asusd/anime/asus/rog/Sunset.gif",
"time": {
"Cycles": 1
},
"brightness": 0.5
}
},
{
"ImageAnimation": {
"file": "/usr/share/asusd/anime/custom/sonic-run.gif",
"scale": 0.9,
"angle": 0.65,
"translation": [
0.0,
0.0
],
"time": {
"Time": {
"secs": 5,
"nanos": 0
}
},
"brightness": 0.5
}
},
{
"Image": {
"file": "/usr/share/asusd/anime/custom/rust.png",
"scale": 1.0,
"angle": 0.0,
"translation": [
0.0,
0.0
],
"brightness": 0.6
}
},
{
"Pause": {
"secs": 6,
"nanos": 0
}
},
{
"ImageAnimation": {
"file": "/usr/share/asusd/anime/custom/sonic-wait.gif",
"scale": 0.9,
"angle": 0.0,
"translation": [
3.0,
2.0
],
"time": {
"Cycles": 2
},
"brightness": 0.5
}
}
]
}

31
rog-anime/Cargo.toml Normal file
View File

@@ -0,0 +1,31 @@
[package]
name = "rog_anime"
version = "1.0.2"
license = "MPL-2.0"
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"
documentation = "https://docs.rs/rog-anime"
description = "Types useful for translating images and other data for display on the ASUS AniMe Matrix display"
keywords = ["ROG", "ASUS", "AniMe"]
edition = "2018"
exclude = ["data"]
[features]
default = ["dbus"]
dbus = ["zbus", "zvariant", "zvariant_derive"]
[dependencies]
png_pong = "^0.8.0"
pix = "0.13"
gif = "^0.11.2"
serde = "^1.0"
serde_derive = "^1.0"
glam = { version = "0.14.0", features = ["serde"] }
zbus = { version = "^1.9.1", optional = true }
zvariant = { version = "^2.6", optional = true }
zvariant_derive = { version = "^2.6", optional = true }

373
rog-anime/LICENSE Normal file
View File

@@ -0,0 +1,373 @@
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.

97
rog-anime/README.md Normal file
View File

@@ -0,0 +1,97 @@
# rog-anime
`rog-anime` is a crate for use with ASUS laptops that have an AniMe matrix display built in to them. The crate can be used with zbus to communicate with the `asusd` daemon from the project this crate is part of, or it can be used standalone to write the data directly to USB by transforming the data to USB HID packets using builtin functions.
Supported so far is:
- Sequences of data,
- Data can be:
+ Image<scale, rotate, position>
+ ASUS style gif
+ Plain image type gif
+ Pause
- Create USB HID packets for writing to the device
This crate is mostly purpose built for use with [`asus-nb-ctrl`](https://gitlab.com/asus-linux/asus-nb-ctrl) which is a complete daemon and toolset for Linux on ASUS ROG/TUF machines, but can be used in general for example building a new controller for Windows OS.
## Feature enablement
`dbus` is enabled by default - this uses `zvariant` to enable sending some types over dbus interfaces.
## Example
```rust
use std::{
env, error::Error, f32::consts::PI, path::Path, process::exit, thread::sleep, time::Duration,
};
use rog_anime::{
AniMeDataBuffer, {AniMeImage, Vec2},
};
use rog_dbus::AuraDbusClient;
fn main() -> Result<(), Box<dyn Error>> {
let (client, _) = AuraDbusClient::new().unwrap();
let mut image = AniMeImage::from_png(
Path::new("./doom.png"),
0.9, // scale
0.0, // rotation
Vec2::new(0.0, 0.0), // position
0.3, // brightness
)?;
loop {
image.angle += 0.05;
if image.angle > PI * 2.0 {
image.angle = 0.0
}
image.update();
client
.proxies()
.anime()
.write(<AniMeDataBuffer>::from(&image))
.unwrap();
sleep(Duration::from_micros(500));
}
}
```
## Example, USB HID
```rust
let mut image = AniMeImage::from_png(
Path::new("./doom.png"),
0.9, // scale
0.0, // rotation
Vec2::new(0.0, 0.0), // position
0.3, // brightness
)?;
// convert to intermediate packet format
let buffer = <AniMeDataBuffer>::from(&image)
// then to USB HID
let data = AniMePacketType::from(buffer);
// and then write direct
for packet in data.iter() {
write_usb(packet); // some usb call here
}
```
## data
- `data/controller.gif` is an example ASUS diagonally orientated gif.
- `data/diagonal-template.*` are templates for diagonal images or gifs.
See https://blog.joshwalsh.me/asus-anime-matrix/ for details on how
the diagonal layout works.
`diagonal-template.*` is provided from the website above. It is best to
export the final file to 36px height - no scaling is done in asusd or
rog-anime crate for diagonal displays.
## TODO:
- Diagonal font and text
- General font and text
- System info for Linux and Windows
- Audio EQ visual for Linux and Windows
- Time+Date display

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 323 KiB

73
rog-anime/src/data.rs Normal file
View File

@@ -0,0 +1,73 @@
use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "dbus")]
use zvariant_derive::Type;
/// The first 7 bytes of a USB packet are accounted for by `USB_PREFIX1` and `USB_PREFIX2`
const BLOCK_START: usize = 7;
/// *Not* inclusive, the byte before this is the final for each "pane"
const BLOCK_END: usize = 634;
/// Individual usable data length of each USB packet
const PANE_LEN: usize = BLOCK_END - BLOCK_START;
/// The length of usable data
pub const ANIME_DATA_LEN: usize = PANE_LEN * 2;
const USB_PREFIX1: [u8; 7] = [0x5e, 0xc0, 0x02, 0x01, 0x00, 0x73, 0x02];
const USB_PREFIX2: [u8; 7] = [0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02];
/// The minimal serializable data that can be transferred over wire types.
/// Other data structures in `rog_anime` will convert to this.
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AnimeDataBuffer(Vec<u8>);
impl Default for AnimeDataBuffer {
fn default() -> Self {
Self::new()
}
}
impl AnimeDataBuffer {
#[inline]
pub fn new() -> Self {
AnimeDataBuffer(vec![0u8; ANIME_DATA_LEN])
}
/// Get the inner data buffer
#[inline]
pub fn get(&self) -> &[u8] {
&self.0
}
/// Get a mutable slice of the inner buffer
#[inline]
pub fn get_mut(&mut self) -> &mut [u8] {
&mut self.0
}
/// Create from a vector of bytes
///
/// # Panics
/// Will panic if the vector length is not `ANIME_DATA_LEN`
#[inline]
pub fn from_vec(input: Vec<u8>) -> Self {
assert_eq!(input.len(), ANIME_DATA_LEN);
Self(input)
}
}
/// The two packets to be written to USB
pub type AnimePacketType = [[u8; 640]; 2];
impl From<AnimeDataBuffer> for AnimePacketType {
#[inline]
fn from(anime: AnimeDataBuffer) -> Self {
assert!(anime.0.len() == ANIME_DATA_LEN);
let mut buffers = [[0; 640]; 2];
for (idx, chunk) in anime.0.as_slice().chunks(PANE_LEN).enumerate() {
buffers[idx][BLOCK_START..BLOCK_END].copy_from_slice(chunk);
}
buffers[0][..7].copy_from_slice(&USB_PREFIX1);
buffers[1][..7].copy_from_slice(&USB_PREFIX2);
buffers
}
}

142
rog-anime/src/diagonal.rs Normal file
View File

@@ -0,0 +1,142 @@
use std::{path::Path, time::Duration};
use crate::{
data::{AnimeDataBuffer, ANIME_DATA_LEN},
error::AnimeError,
};
const WIDTH: usize = 74;
const HEIGHT: usize = 36;
/// Mostly intended to be used with ASUS gifs, but can be used for other purposes (like images)
#[derive(Debug, Clone)]
pub struct AnimeDiagonal([[u8; WIDTH]; HEIGHT], Option<Duration>);
impl Default for AnimeDiagonal {
#[inline]
fn default() -> Self {
Self::new(None)
}
}
impl AnimeDiagonal {
#[inline]
pub fn new(duration: Option<Duration>) -> Self {
Self([[0u8; WIDTH]; HEIGHT], duration)
}
#[inline]
pub fn get_mut(&mut self) -> &mut [[u8; WIDTH]; HEIGHT] {
&mut self.0
}
/// Get a full diagonal row where `x` `y` is the starting point and `len` is the length of data.
fn get_row(&self, x: usize, y: usize, len: usize) -> Vec<u8> {
let mut buf = Vec::with_capacity(len);
for i in 0..len {
let val = self.0[HEIGHT - y - i - 1][x + i];
buf.push(val);
}
buf
}
/// Generate the base image from inputs. The result can be displayed as is or
/// updated via scale, position, or angle then displayed again after `update()`.
#[inline]
pub fn from_png(
path: &Path,
duration: Option<Duration>,
bright: f32,
) -> Result<Self, AnimeError> {
use pix::el::Pixel;
let data = std::fs::read(path)?;
let data = std::io::Cursor::new(data);
let decoder = png_pong::Decoder::new(data)?.into_steps();
let png_pong::Step { raster, delay: _ } = decoder.last().ok_or(AnimeError::NoFrames)??;
let mut matrix = AnimeDiagonal::new(duration);
let width;
match raster {
png_pong::PngRaster::Graya8(ras) => {
width = ras.width();
for (y, row) in ras.pixels().chunks(width as usize).enumerate() {
for (x, px) in row.iter().enumerate() {
let v = <u8>::from(px.one() * bright);
matrix.0[y][x] = v;
}
}
}
_ => return Err(AnimeError::Format),
};
Ok(matrix)
}
}
impl From<&AnimeDiagonal> for AnimeDataBuffer {
/// Do conversion from the nested Vec in AnimeMatrix to the two required
/// packets suitable for sending over USB
#[inline]
fn from(anime: &AnimeDiagonal) -> Self {
let mut buf = vec![0u8; ANIME_DATA_LEN];
buf[1..=32].copy_from_slice(&anime.get_row(0, 3, 32));
buf[34..=66].copy_from_slice(&anime.get_row(0, 2, 33));
buf[69..=101].copy_from_slice(&anime.get_row(1, 2, 33)); // ?!
buf[102..=134].copy_from_slice(&anime.get_row(1, 1, 33));
buf[137..=169].copy_from_slice(&anime.get_row(2, 1, 33));
buf[170..=202].copy_from_slice(&anime.get_row(2, 0, 33));
buf[204..=236].copy_from_slice(&anime.get_row(3, 0, 33));
buf[237..=268].copy_from_slice(&anime.get_row(4, 0, 32));
buf[270..=301].copy_from_slice(&anime.get_row(5, 0, 32));
buf[302..=332].copy_from_slice(&anime.get_row(6, 0, 31));
buf[334..=364].copy_from_slice(&anime.get_row(7, 0, 31));
buf[365..=394].copy_from_slice(&anime.get_row(8, 0, 30));
buf[396..=425].copy_from_slice(&anime.get_row(9, 0, 30));
buf[426..=454].copy_from_slice(&anime.get_row(10, 0, 29));
buf[456..=484].copy_from_slice(&anime.get_row(11, 0, 29));
buf[485..=512].copy_from_slice(&anime.get_row(12, 0, 28));
buf[514..=541].copy_from_slice(&anime.get_row(13, 0, 28));
buf[542..=568].copy_from_slice(&anime.get_row(14, 0, 27));
buf[570..=596].copy_from_slice(&anime.get_row(15, 0, 27));
buf[597..=622].copy_from_slice(&anime.get_row(16, 0, 26));
buf[624..=649].copy_from_slice(&anime.get_row(17, 0, 26));
buf[650..=674].copy_from_slice(&anime.get_row(18, 0, 25));
buf[676..=700].copy_from_slice(&anime.get_row(19, 0, 25));
buf[701..=724].copy_from_slice(&anime.get_row(20, 0, 24));
buf[726..=749].copy_from_slice(&anime.get_row(21, 0, 24));
buf[750..=772].copy_from_slice(&anime.get_row(22, 0, 23));
buf[774..=796].copy_from_slice(&anime.get_row(23, 0, 23));
buf[797..=818].copy_from_slice(&anime.get_row(24, 0, 22));
buf[820..=841].copy_from_slice(&anime.get_row(25, 0, 22));
buf[842..=862].copy_from_slice(&anime.get_row(26, 0, 21));
buf[864..=884].copy_from_slice(&anime.get_row(27, 0, 21));
buf[885..=904].copy_from_slice(&anime.get_row(28, 0, 20));
buf[906..=925].copy_from_slice(&anime.get_row(29, 0, 20));
buf[926..=944].copy_from_slice(&anime.get_row(30, 0, 19));
buf[946..=964].copy_from_slice(&anime.get_row(31, 0, 19));
buf[965..=982].copy_from_slice(&anime.get_row(32, 0, 18));
buf[984..=1001].copy_from_slice(&anime.get_row(33, 0, 18));
buf[1002..=1018].copy_from_slice(&anime.get_row(34, 0, 17));
buf[1020..=1036].copy_from_slice(&anime.get_row(35, 0, 17));
buf[1037..=1052].copy_from_slice(&anime.get_row(36, 0, 16));
buf[1054..=1069].copy_from_slice(&anime.get_row(37, 0, 16));
buf[1070..=1084].copy_from_slice(&anime.get_row(38, 0, 15));
buf[1086..=1100].copy_from_slice(&anime.get_row(39, 0, 15));
buf[1101..=1114].copy_from_slice(&anime.get_row(40, 0, 14));
buf[1116..=1129].copy_from_slice(&anime.get_row(41, 0, 14));
buf[1130..=1142].copy_from_slice(&anime.get_row(42, 0, 13));
buf[1144..=1156].copy_from_slice(&anime.get_row(43, 0, 13));
buf[1157..=1168].copy_from_slice(&anime.get_row(44, 0, 12));
buf[1170..=1181].copy_from_slice(&anime.get_row(45, 0, 12));
buf[1182..=1192].copy_from_slice(&anime.get_row(46, 0, 11));
buf[1194..=1204].copy_from_slice(&anime.get_row(47, 0, 11));
buf[1205..=1214].copy_from_slice(&anime.get_row(48, 0, 10));
buf[1216..=1225].copy_from_slice(&anime.get_row(49, 0, 10));
buf[1226..=1234].copy_from_slice(&anime.get_row(50, 0, 9));
buf[1236..=1244].copy_from_slice(&anime.get_row(51, 0, 9));
AnimeDataBuffer::from_vec(buf)
}
}

71
rog-anime/src/error.rs Normal file
View File

@@ -0,0 +1,71 @@
use gif::DecodingError;
use png_pong::decode::Error as PngError;
use std::error::Error;
use std::fmt;
#[cfg(feature = "dbus")]
use zbus::fdo;
#[derive(Debug)]
pub enum AnimeError {
NoFrames,
Io(std::io::Error),
Png(PngError),
Gif(DecodingError),
Format,
/// The input was incorrect size, expected size is `IncorrectSize(width, height)`
IncorrectSize(u32, u32),
#[cfg(feature = "dbus")]
Zbus(fdo::Error)
}
impl fmt::Display for AnimeError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AnimeError::NoFrames => write!(f, "No frames in PNG"),
AnimeError::Io(e) => write!(f, "Could not open: {}", e),
AnimeError::Png(e) => write!(f, "PNG error: {}", e),
AnimeError::Gif(e) => write!(f, "GIF error: {}", e),
AnimeError::Format => write!(f, "PNG file is not 8bit greyscale"),
AnimeError::IncorrectSize(width, height) => write!(
f,
"The input image size is incorrect, expected {}x{}",
width, height
),
#[cfg(feature = "dbus")]
AnimeError::Zbus(e) => write!(f, "ZBUS error: {}", e),
}
}
}
impl Error for AnimeError {}
impl From<std::io::Error> for AnimeError {
#[inline]
fn from(err: std::io::Error) -> Self {
AnimeError::Io(err)
}
}
impl From<PngError> for AnimeError {
#[inline]
fn from(err: PngError) -> Self {
AnimeError::Png(err)
}
}
impl From<DecodingError> for AnimeError {
#[inline]
fn from(err: DecodingError) -> Self {
AnimeError::Gif(err)
}
}
#[cfg(feature = "dbus")]
impl From<AnimeError> for fdo::Error {
#[inline]
fn from(err: AnimeError) -> Self {
fdo::Error::Failed(format!("{}", err))
}
}

176
rog-anime/src/gif.rs Normal file
View File

@@ -0,0 +1,176 @@
use glam::Vec2;
use serde_derive::{Deserialize, Serialize};
use std::{fs::File, path::Path, time::Duration};
use crate::{error::AnimeError, AnimeDataBuffer, AnimeDiagonal, AnimeImage, Pixel};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AnimeFrame {
/// Precomputed data for the frame. This can be transferred directly to the
/// the `asusd` daemon over dbus or converted to USB packet with `AnimePacketType::from(buffer)`
data: AnimeDataBuffer,
delay: Duration,
}
impl AnimeFrame {
/// Get the inner data buffer of the gif frame
#[inline]
pub fn frame(&self) -> &AnimeDataBuffer {
&self.data
}
/// Get the `Duration` of the delay for this frame
#[inline]
pub fn delay(&self) -> Duration {
self.delay
}
}
/// Defines the time or animation cycle count to use for a gif
#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
pub enum AnimTime {
/// Time in milliseconds for animation to run
Time(Duration),
/// How many full animation loops to run
Cycles(u32),
/// Run for infinite time
Infinite,
}
impl Default for AnimTime {
#[inline]
fn default() -> Self {
Self::Infinite
}
}
/// A gif animation. This is a collection of frames from the gif, and a duration
/// that the animation should be shown for.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AnimeGif(Vec<AnimeFrame>, AnimTime);
impl AnimeGif {
/// Create an animation using the 74x36 ASUS gif format
#[inline]
pub fn create_diagonal_gif(
file_name: &Path,
duration: AnimTime,
brightness: f32,
) -> Result<Self, AnimeError> {
let mut matrix = AnimeDiagonal::new(None);
let mut decoder = gif::DecodeOptions::new();
// Configure the decoder such that it will expand the image to RGBA.
decoder.set_color_output(gif::ColorOutput::RGBA);
// Read the file header
let file = File::open(file_name)?;
let mut decoder = decoder.read_info(file)?;
let mut frames = Vec::with_capacity(decoder.buffer_size());
while let Some(frame) = decoder.read_next_frame()? {
let wait = frame.delay * 10;
if matches!(frame.dispose, gif::DisposalMethod::Background) {
frames = Vec::new();
}
for (y, row) in frame.buffer.chunks(frame.width as usize * 4).enumerate() {
for (x, px) in row.chunks(4).enumerate() {
if px[3] != 255 {
// should be t but not in some gifs? What, ASUS, what?
continue;
}
matrix.get_mut()[y + frame.top as usize][x + frame.left as usize] =
(px[0] as f32 * brightness) as u8;
}
}
frames.push(AnimeFrame {
data: <AnimeDataBuffer>::from(&matrix),
delay: Duration::from_millis(wait as u64),
});
}
Ok(Self(frames, duration))
}
/// Create an animation using a gif of any size. This method must precompute the
/// result.
#[inline]
pub fn create_png_gif(
file_name: &Path,
scale: f32,
angle: f32,
translation: Vec2,
duration: AnimTime,
brightness: f32,
) -> Result<Self, AnimeError> {
let mut frames = Vec::new();
let mut decoder = gif::DecodeOptions::new();
// Configure the decoder such that it will expand the image to RGBA.
decoder.set_color_output(gif::ColorOutput::RGBA);
// Read the file header
let file = File::open(file_name)?;
let mut decoder = decoder.read_info(file)?;
let height = decoder.height();
let width = decoder.width();
let pixels: Vec<Pixel> =
vec![Pixel::default(); (decoder.width() as u32 * decoder.height() as u32) as usize];
let mut image = AnimeImage::new(
Vec2::new(scale, scale),
angle,
translation,
brightness,
pixels,
decoder.width() as u32,
);
while let Some(frame) = decoder.read_next_frame()? {
let wait = frame.delay * 10;
if matches!(frame.dispose, gif::DisposalMethod::Background) {
let pixels: Vec<Pixel> =
vec![Pixel::default(); (width as u32 * height as u32) as usize];
image = AnimeImage::new(
Vec2::new(scale, scale),
angle,
translation,
brightness,
pixels,
width as u32,
);
}
for (y, row) in frame.buffer.chunks(frame.width as usize * 4).enumerate() {
for (x, px) in row.chunks(4).enumerate() {
if px[3] != 255 {
// should be t but not in some gifs? What, ASUS, what?
continue;
}
let pos =
(x + frame.left as usize) + ((y + frame.top as usize) * width as usize);
image.get_mut()[pos] = Pixel {
color: ((px[0] as u32 + px[1] as u32 + px[2] as u32) / 3),
alpha: 1.0,
};
}
}
image.update();
frames.push(AnimeFrame {
data: <AnimeDataBuffer>::from(&image),
delay: Duration::from_millis(wait as u64),
});
}
Ok(Self(frames, duration))
}
/// Get a slice of the frames this gif has
#[inline]
pub fn frames(&self) -> &[AnimeFrame] {
&self.0
}
/// Get the time/count for this gif
#[inline]
pub fn duration(&self) -> AnimTime {
self.1
}
}

176
rog-anime/src/grid.rs Normal file
View File

@@ -0,0 +1,176 @@
use std::time::Duration;
use crate::data::{AnimeDataBuffer, ANIME_DATA_LEN};
use crate::image::LED_IMAGE_POSITIONS;
const WIDTH: usize = 33;
const HEIGHT: usize = 55;
/// Helper structure for writing images.
///
/// See the examples for ways to write an image to `AnimeMatrix` format.
/// Width = 33
/// height = 55
///
/// **Note:** the columns in each odd row are offset by half a pixel left
#[derive(Debug, Clone)]
pub struct AnimeGrid([[u8; WIDTH]; HEIGHT], Option<Duration>);
impl Default for AnimeGrid {
#[inline]
fn default() -> Self {
Self::new(None)
}
}
impl AnimeGrid {
#[inline]
pub fn new(duration: Option<Duration>) -> Self {
AnimeGrid([[0u8; WIDTH]; HEIGHT], duration)
}
/// Set a position in the grid with a brightness value
#[inline]
pub fn set(&mut self, x: usize, y: usize, b: u8) {
self.0[y][x] = b;
}
#[inline]
pub fn get(&self) -> &[[u8; WIDTH]; HEIGHT] {
&self.0
}
#[inline]
pub fn get_mut(&mut self) -> &mut [[u8; WIDTH]; HEIGHT] {
&mut self.0
}
/// Fill the grid with a value
#[inline]
pub fn fill_with(&mut self, fill: u8) {
for row in self.0.iter_mut() {
for x in row.iter_mut() {
*x = fill;
}
}
}
// pub fn debug_print(&self) {
// // this is the index from right. It is used to progressively shorten rows
// let mut prog_row_len = WIDTH - 2;
// for (count, row) in self.0.iter().enumerate() {
// // Switch to next block (looks like )
// if count % 2 != 0 {
// // Row after 6 is only 1 less, then rows after 7 follow pattern
// if count == 7 {
// prog_row_len -= 1;
// } else {
// prog_row_len -= 2;
// }
// } else {
// prog_row_len += 1; // if count 6, 0
// }
// let index = row.len() - prog_row_len;
// if count % 2 == 0 {
// print!(" ");
// }
// for (i, n) in row.iter().enumerate() {
// if i >= index {
// print!(" {}", "XXX".color(Rgb(0, *n, 0)));
// } else {
// print!(" ");
// }
// }
// println!();
// }
// }
}
impl From<AnimeGrid> for AnimeDataBuffer {
/// Do conversion from the nested Vec in AniMeMatrix to the two required
/// packets suitable for sending over USB
#[inline]
fn from(anime: AnimeGrid) -> Self {
let mut buf = vec![0u8; ANIME_DATA_LEN];
for (idx, pos) in LED_IMAGE_POSITIONS.iter().enumerate() {
if let Some(pos) = pos {
let x = pos.x().ceil() as usize;
let y = pos.y().ceil() as usize;
buf[idx + 1] = anime.0[y][x];
}
}
AnimeDataBuffer::from_vec(buf)
}
}
#[cfg(test)]
mod tests {
use crate::grid::*;
#[test]
fn check_data_alignment() {
let mut matrix = AnimeGrid::new(None);
{
let tmp = matrix.get_mut();
for row in tmp.iter_mut() {
let idx = row.len() - 1;
row[idx] = 0xff;
}
}
let matrix = <AnimeDataBuffer>::from(matrix);
let data_cmp = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0,
0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
assert_eq!(matrix.get(), &data_cmp);
}
}

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