Compare commits

..

2 Commits

Author SHA1 Message Date
Luke Jones 2b396f35c2 Merge branch 'main' into 'main'
Update Discord Icon in Readme

See merge request asus-linux/asusctl!181
2024-05-07 01:59:22 +00:00
Ashwanth Balakrishnan 6c503541bb Update Discord Icon to Readme 2024-04-23 16:19:28 +00:00
160 changed files with 7759 additions and 15198 deletions
+2 -2
View File
@@ -12,7 +12,7 @@ echo '+cargo clippy --all -- -D warnings'
cargo clippy --all -- -D warnings
echo '+cargo test --all'
cargo test --all -- --test-threads=1
cargo test --all
echo '+cargo cranky'
cargo cranky
cargo cranky
+2 -2
View File
@@ -17,7 +17,7 @@ image: rust:latest
- target/release/.cargo-lock
before_script:
- apt-get update -qq && apt-get install -y -qq libudev-dev libgtk-3-dev grep llvm clang libclang-dev libsdl2-dev libsdl2-gfx-dev
- apt-get update -qq && apt-get install -y -qq libinput-dev libseat-dev libudev-dev libgtk-3-dev grep llvm clang libclang-dev libsdl2-dev libsdl2-gfx-dev
stages:
- format
@@ -52,7 +52,7 @@ test:
<<: *rust_cache
script:
- mkdir -p .git/hooks > /dev/null
- cargo test --all -- --test-threads=1
- cargo test --all
release:
only:
+5 -214
View File
@@ -1,221 +1,12 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [v6.1.5]
### Changed
- Update dependencies
- Fix fan-curve proxy type signatures
## [v6.1.4]
### Changed
- Fix git doing me a dirty
## [v6.1.3]
### Changed
- Many small bugfixes such as for platform profile switching
## [v6.1.2]
### Changed
- Try a slightly different tact to fix charge control slider
## [v6.1.1]
### Changed
- Fix aura data matching
- Fix charge control slider
## [v6.1.0]
### Changed
- Update deps
- Add support for G513RC RGB modes
- Many UI fixes
- Fixes to PPT settings in UI
## [v6.1.0-rc7]
- Refactor PPT handling more:
1. Per profile, per-ac/dc
2. Do not apply unless group is enabled
3. Better reset/disable handling
4. Selecting a profile defaults PPT to off/disabled
- Bugfix: prevent an AniMe thread async deadlock
## [v6.1.0-rc6]
### Changed
- Two small fixes, one for `low-power` profile name, and one for base gpu tdp
- Move to using platform_profile api only (no throttle_thermal_policy)
## [v6.1.0-rc5]
### Changed
- Per-AC/DC, per-profile tunings enabled (Battery vs AC power + platform profile)
- Add ability to restore PPT defaults
- Add PPT help dialogue to UI
- Bug fix: correctly set charge limit from UI
## [v6.1.0-rc4]
### Changed
- Bug fix: UI was setting incorrect value for FPPT
- Bug fix: Re-add callbacks for the throttle and epp settings in UI
- Bug fix: Fix UI settigns for AniMe Matrix display
- Bug fix: better handle missing tray (for example gnome)
- Strip out all outdated and unsafe tuning stuff
- Allow each performance profile to have different PPT settings
## [v6.1.0-rc3]
### Changed
- Bug fixes
- Partial support for per-profile CPU tunings (WIP)
## [v6.1.0-rc2]
### Added
- asus-armoury driver support. WIP, will be adjusted/changed further
- More "Slash" display controls
## [v6.1.0-rc1]
### Added
- ROG Arion external driver LED support
- Add GA605W LED layout
- Add GA605 + GU605 Slash support
### Changed
- Fix attribute writes. At some point the kernel API seems to have changed.
- Extremely large refactor of Aura device handling. Should enable easy add of different kinds now.
- Rename CLI args for aura related properties. This will likely change further as more devices are added
## [v6.0.12]
### Changed
- Add Ally X aura config
- Fixes to Ally led power configs
- Fix CLI led modes
- Add FX517Z to aura_support
- Add G614JJ Aura support
- Add G614JZ (2023 Strix G16) Aura support
## [v6.0.11]
### Changed
- Renamed `Strobe` effect to `RainbowCycle` to prevent confusion over what it is
- Ranamed `Rainbow` effect to `RainbowWave`
- Cleaned up serde crate deps
- Fixed AniMe on GA402XZ
- Update some of the G713 device aura features
- Update some of the G513 device aura features
## [v6.0.10]
### Added
- Add the GA401I model to aura_support.
### Changed
- Aura support return a default aura definition instead of nothing
- Minor updates in aura controller to ensure configs are updated if the support file changes
- Don't panic if -ENODEV on fan_curve enable
- Adjust the G513Q support to match what is on the asus website.
- Adjust init sequence of anime to prevent accidental use of Slash as Anime
- Enable notifs and tray icon without supergfx
## [v6.0.9]
### Added
- Add G513RS to laptop DB.
- Add G512LI to laptop DB.
### Changed
- Rename and recreate the default Anime config if cache setup fails
### Fixed
- Nuke the issue of GUI taking 100% of a CPU core
## [v6.0.8]
### Added
- Add G512L laptop DB entry
### Changed
- Add more tests to verify things
### Fix
- asusctl incorrectly assumes fan-curves unsupported. Now fixed.
- try to fix ROGCC using CPU time.
## [v6.0.7]
### Changed
- Add a config option to set if throttle policy is changed on ac/bat change (UI only)
- Allow X11 GUI. This is *not* supported. Please see readme.
- Fixes to some GUI widget layouts and sizing
- Do a backup HID raw write fro brightness if the read-back value does not match. This is a temporary solve for some G14 and G16 until the kernel patch is ready.
- Reimplement the older 0x1866 MCU keyboard control power bits plus UI control for it. If you had a keyboard affected by Lightbar issues and it is older than a couple of years this should help. If not, please file a bug.
## [v6.0.6]
### Added
- Add GX650R laptop to aura DB
### Changed
- Further tweaks to aura init
- More logging
- Fix TUF laptop led power
- Sanitize the dbus path for aura devices (remove `.` chars)
- Even more error handling (gracefully)
- Better checking for dbus interfaces
- Remove dbus `supported_interfaces`
- dbus ObjectManager is now at root `/`
## [v6.0.5]
### Changed
- Better more robust error handling in ROGCC
- Try to better handle pre-2021 laptops with lightbar
- Add more logging to try and trace the charge_control_end_threshold issue
- Make `kbd_brightness` optional to work around issues on some laptops that seem to lack it. Likely this will all need a refactor *again* if work proceeds in kernel for better RGB support.
## [v6.0.4]
### Changed
- Remove some `todo()` in rogcc
- Add missing `#[zbus(property)]` derive to Slash dbus properties
- Match G533Q laptop explicitly to the 0x8166 PID
## [v6.0.3]
### NOTE
- Xorg is not supported any longer. All major desktops and distros are Wayland.
### Changed
- Add a check to prevent non-TUF laptops with screwed up method return from TUF acpi methods from trying to add a TUF aura device without actually being a TUF laptop.
- Make the G834JZ entry in aura db generic for all G834J
## [v6.0.2]
### Changed
- Clean up code
- Don't panic if no aura device in ROGCC
- Try to prevent an errorenous tuf keyboard detection
## [v6.0.1]
### Added
Generated
+1512 -3571
View File
File diff suppressed because it is too large Load Diff
+46 -41
View File
@@ -1,6 +1,32 @@
[workspace]
members = [
"asusctl",
"asusd",
"asusd-user",
"config-traits",
"cpuctl",
"dmi-id",
"rog-platform",
"rog-dbus",
"rog-anime",
"rog-aura",
"rog-profiles",
"rog-control-center",
"rog-slash",
"simulators",
]
default-members = [
"asusctl",
"asusd",
"asusd-user",
"cpuctl",
"rog-control-center",
]
resolver = "2"
[workspace.package]
version = "6.1.5"
rust-version = "1.82"
version = "6.0.1"
rust-version = "1.77"
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
@@ -9,46 +35,25 @@ homepage = "https://gitlab.com/asus-linux/asusctl"
description = "Laptop feature control for ASUS ROG laptops and others"
edition = "2021"
[workspace]
resolver = "2"
members = [
"asusctl",
"asusd",
"asusd-user",
"config-traits",
"dmi-id",
"rog-platform",
"rog-dbus",
"rog-anime",
"rog-aura",
"rog-profiles",
"rog-control-center",
"rog-slash",
"simulators",
"rog-scsi",
]
default-members = ["asusctl", "asusd", "asusd-user", "rog-control-center"]
[workspace.dependencies]
tokio = { version = "^1.39.0", default-features = false, features = [
"macros",
"sync",
"time",
"rt",
"rt-multi-thread",
tokio = { version = "^1.36.0", default-features = false, features = [
"macros",
"sync",
"time",
"rt-multi-thread",
] }
concat-idents = "^1.1"
dirs = "^4.0"
smol = "^2.0"
smol = "^1.3"
mio = "0.8.11"
futures-util = "0.3.31"
zbus = "5.5.0"
logind-zbus = { version = "5.2.0" } #, default-features = false, features = ["non_blocking"] }
zbus = "4.1"
logind-zbus = { version = "4.0.2" } #, default-features = false, features = ["non_blocking"] }
serde = { version = "^1.0", features = ["serde_derive"] }
serde = "^1.0"
serde_derive = "^1.0"
ron = "*"
typeshare = "1.0.0"
log = "^0.4"
env_logger = "^0.10.0"
@@ -66,26 +71,26 @@ gif = "^0.12.0"
versions = "6.2"
notify-rust = { version = "4.11.5", features = ["z", "async"] }
sg = { git = "https://github.com/flukejones/sg-rs.git" }
notify-rust = { git = "https://github.com/flukejones/notify-rust.git", rev = "54176413b81189a3e4edbdc20a0b4f7e2e35c063", default-features = false, features = [
"z",
] }
[profile.release]
# thin = 57s, asusd = 9.0M
# fat = 72s, asusd = 6.4M
lto = "thin"
lto = "fat"
debug = false
opt-level = 3
panic = "abort"
# codegen-units = 1
codegen-units = 1
[profile.dev]
opt-level = 1
# codegen-units = 1
codegen-units = 16
[profile.dev.package."*"]
opt-level = 1
# codegen-units = 1
codegen-units = 16
[profile.bench]
debug = false
+3 -3
View File
@@ -48,7 +48,7 @@ The LED controller (e.g, aura) enables setting many of the factory modes availab
#### Supported laptops
There are over 80 supported laptops as of 01-01-2023. Please see [the rog-aura crate readme for further details](/rog-aura/README.md).
There are over 60 supported laptops as of 01-01-2023. Please see [the rog-aura crate readme for further details](/rog-aura/README.md).
### Charge control
@@ -420,13 +420,13 @@ To switch to next/previous Aura modes you will need to bind both the aura keys (
**Next**
```
asusctl aura -n
asusctl led-mode -n
```
**Previous**
```
asusctl aura -p
asusctl led-mode -p
```
To switch Fan/Thermal profiles you need to bind the Fn+F5 key to `asusctl profile -n`.
+9 -8
View File
@@ -1,4 +1,4 @@
VERSION := $(shell /usr/bin/grep -Pm1 'version = "(\d+.\d+.\d+.*)"' Cargo.toml | cut -d'"' -f2)
VERSION := $(shell /usr/bin/grep -Pm1 'version = "(\d.\d.\d)"' Cargo.toml | cut -d'"' -f2)
INSTALL = install
INSTALL_PROGRAM = ${INSTALL} -D -m 0755
@@ -30,11 +30,6 @@ else
TARGET = debug
endif
X11 ?= 0
ifeq ($(X11),1)
ARGS += --features "rog-control-center/x11"
endif
VENDORED ?= 0
ifeq ($(VENDORED),1)
ARGS += --frozen
@@ -118,10 +113,16 @@ vendor:
mv .cargo/config ./cargo-config
rm -rf .cargo
rm -rf vendor
cargo vendor-filterer --all-features --platform x86_64-unknown-linux-gnu vendor
cargo vendor-filterer --platform x86_64-unknown-linux-gnu vendor
tar pcfJ vendor_asusctl_$(VERSION).tar.xz vendor
rm -rf vendor
bindings:
typeshare ./rog-anime/src/ --lang=typescript --output-file=bindings/ts/anime.ts
typeshare ./rog-aura/src/ --lang=typescript --output-file=bindings/ts/aura.ts
typeshare ./rog-profiles/src/ --lang=typescript --output-file=bindings/ts/profiles.ts
typeshare ./rog-platform/src/ --lang=typescript --output-file=bindings/ts/platform.ts
translate:
find -name \*.slint | xargs slint-tr-extractor -o rog-control-center/translations/en/rog-control-center.po
@@ -132,7 +133,7 @@ ifeq ($(VENDORED),1)
tar pxf vendor_asusctl_$(VERSION).tar.xz
endif
cargo build $(ARGS)
ifeq ($(STRIP_BINARIES),1)
ifneq ($(STRIP_BINARIES),0)
strip -s ./target/$(TARGET)/$(BIN_C)
strip -s ./target/$(TARGET)/$(BIN_D)
strip -s ./target/$(TARGET)/$(BIN_U)
+9 -27
View File
@@ -1,6 +1,6 @@
# `asusctl` for ASUS ROG
[![Become a Patron!](https://github.com/codebard/patron-button-and-widgets-by-codebard/blob/master/images/become_a_patron_button.png?raw=true)](https://www.patreon.com/bePatron?u=7602281) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/V7V5CLU67) - [Asus Linux Website](https://asus-linux.org/)
[Become a Patron!](https://www.patreon.com/bePatron?u=7602281) - [Asus Linux Website](https://asus-linux.org/)
**WARNING:** Many features are developed in tandem with kernel patches. If you see a feature is missing you either need a patched kernel or latest release.
@@ -11,15 +11,7 @@ Now includes a GUI, `rog-control-center`.
## Kernel support
Due to on-going driver work the minimum suggested kernel version is always **the latest*, as improvements and fixes are continuous.
Support for some new features is not avilable unless you run a patched kernel with the work I am doing [in this github repo](https://github.com/flukejones/linux/tree/wip/ally-6.13). Use the linked branch, or `wip/ally-6.12`. Everything that is done here is upstreamed eventually (a long process).
Z13 devices will need [these](https://lore.kernel.org/linux-input/20240416090402.31057-1-luke@ljones.dev/T/#t)
## X11 support
X11 is not supported at all, as in I will not help you with X11 issues if there are any due to limited time and it being unmaintained itself. You can however build `rog-control-center` with it enabled `cargo build --features "rog-control-center/x11"`.
**The minimum supported kernel version is 6.10**, which will contain the patches from [here](https://lore.kernel.org/platform-driver-x86/20240404001652.86207-1-luke@ljones.dev/). Z13 devices will need [these](https://lore.kernel.org/linux-input/20240416090402.31057-1-luke@ljones.dev/T/#t)
## Goals
@@ -37,10 +29,6 @@ The level of support for laptops is dependent on folks submitting data to includ
See the [rog-aura readme](./rog-aura/README.md) for more details.
## Discord
[![Discord](https://img.shields.io/badge/Discord-7289DA?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/B8GftRW2Hd)
## SUPPORTED LAPTOPS
Most ASUS gaming laptops that have a USB keyboard. If `lsusb` shows something similar
@@ -78,15 +66,14 @@ The list is a bit outdated as many features have been enabled in the Linux kerne
A gui is now in the repo - ROG Control Center. At this time it is still a WIP, but it has almost all features in place already.
**NOTE**: Xorg is not supported.
# BUILDING
Rust and cargo are required, they can be installed from [rustup.rs](https://rustup.rs/) or from the distro repos if newer than 1.75.
**fedora:**
dnf install cmake clang-devel libxkbcommon-devel systemd-devel expat-devel pcre2-devel libzstd-devel gtk3-devel
dnf install cmake clang-devel libinput-devel libseat-devel libgbm-devel libxkbcommon-devel systemd-devel \
libdrm-devel expat-devel pcre2-devel libzstd-devel libappindicator-gtk3
make
sudo make install
@@ -95,16 +82,7 @@ Rust and cargo are required, they can be installed from [rustup.rs](https://rust
Works with KDE Plasma (without GTK packages)
zypper in -t pattern devel_basis
zypper in rustup make cmake clang-devel libxkbcommon-devel systemd-devel expat-devel pcre2-devel libzstd-devel gtk3-devel
make
sudo make install
**Debian(unsuported):**
officially unsuported,but you can still try and test it by yourself(some features may not be available).
sudo apt install libclang-dev libudev-dev libfontconfig-dev build-essential cmake libxkbcommon-dev
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
zypper in rustup make cmake libinput-devel libseat-devel libgbm-devel systemd-devel clang-devel llvm-devel gdk-pixbuf-devel cairo-devel pango-devel freetype-devel libexpat-devel libayatana-indicator3-7
make
sudo make install
@@ -144,6 +122,10 @@ Generation of the bindings with `make bindings` requires `typeshare` to be insta
Dbus introsepction XML requires with `make introspection` requires `anime_sim` to be running before starting `asusd`.
# Join the Community
[![Discord](https://img.shields.io/badge/Discord-7289DA?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/z8y99XqPb7)
# OTHER
## AniMe Matrix simulator
+3 -4
View File
@@ -10,20 +10,19 @@ edition.workspace = true
[dependencies]
rog_anime = { path = "../rog-anime" }
rog_scsi = { path = "../rog-scsi" }
rog_slash = { path = "../rog-slash" }
rog_aura = { path = "../rog-aura" }
rog_dbus = { path = "../rog-dbus" }
rog_profiles = { path = "../rog-profiles" }
rog_platform = { path = "../rog-platform" }
asusd = { path = "../asusd" }
dmi_id = { path = "../dmi-id" }
log.workspace = true
env_logger.workspace = true
ron.workspace = true
gumdrop.workspace = true
zbus.workspace = true
[dev-dependencies]
rog_dbus = { path = "../rog-dbus" }
cargo-husky.workspace = true
+1 -1
View File
@@ -26,7 +26,7 @@ fn main() -> Result<(), Box<dyn Error>> {
AnimeType::GA401,
)?;
let anime_type = get_anime_type();
let anime_type = get_anime_type()?;
proxy.write(matrix.into_data_buffer(anime_type)?).unwrap();
+1 -1
View File
@@ -29,7 +29,7 @@ fn main() {
}
}
let anime_type = get_anime_type();
let anime_type = get_anime_type().unwrap();
proxy
.write(matrix.into_data_buffer(anime_type).unwrap())
.unwrap();
+9 -6
View File
@@ -19,13 +19,16 @@ fn main() {
let path = Path::new(&args[1]);
let brightness = args[2].parse::<f32>().unwrap();
let anime_type = get_anime_type();
let anime_type = get_anime_type().unwrap();
let mut seq = Sequences::new(anime_type);
seq.insert(0, &ActionLoader::AsusAnimation {
file: path.into(),
time: rog_anime::AnimTime::Infinite,
brightness,
})
seq.insert(
0,
&ActionLoader::AsusAnimation {
file: path.into(),
time: rog_anime::AnimTime::Infinite,
brightness,
},
)
.unwrap();
loop {
+1 -1
View File
@@ -14,7 +14,7 @@ fn main() {
let conn = Connection::system().unwrap();
let proxy = AnimeProxyBlocking::new(&conn).unwrap();
let anime_type = get_anime_type();
let anime_type = get_anime_type().unwrap();
let mut matrix = AnimeGrid::new(anime_type);
let tmp = matrix.get_mut();
+1 -1
View File
@@ -9,7 +9,7 @@ use zbus::blocking::Connection;
fn main() {
let conn = Connection::system().unwrap();
let proxy = AnimeProxyBlocking::new(&conn).unwrap();
let anime_type = get_anime_type();
let anime_type = get_anime_type().unwrap();
let mut matrix = AnimeDataBuffer::new(anime_type);
matrix.data_mut()[1] = 100; // start = 1
for n in matrix.data_mut()[2..32].iter_mut() {
+1 -1
View File
@@ -20,7 +20,7 @@ fn main() -> Result<(), Box<dyn Error>> {
exit(-1);
}
let anime_type = get_anime_type();
let anime_type = get_anime_type()?;
let matrix = AnimeImage::from_png(
Path::new(&args[1]),
args[2].parse::<f32>().unwrap(),
+1 -1
View File
@@ -23,7 +23,7 @@ fn main() -> Result<(), Box<dyn Error>> {
exit(-1);
}
let anime_type = get_anime_type();
let anime_type = get_anime_type()?;
let mut matrix = AnimeImage::from_png(
Path::new(&args[1]),
args[2].parse::<f32>().unwrap(),
+6 -8
View File
@@ -41,8 +41,6 @@ pub enum SetAuraZoneEnabled {
Lid(AuraPowerStates),
#[options(help = "")]
RearGlow(AuraPowerStates),
#[options(help = "")]
Ally(AuraPowerStates),
}
#[derive(Debug, Clone, Options)]
@@ -221,9 +219,9 @@ pub enum SetAuraBuiltin {
#[options(help = "pulse between one or two colours")]
Breathe(TwoColourSpeed), // 1
#[options(help = "strobe through all colours")]
RainbowCycle(SingleSpeed), // 2
Strobe(SingleSpeed), // 2
#[options(help = "rainbow cycling in one of four directions")]
RainbowWave(SingleSpeedDirection), // 3
Rainbow(SingleSpeedDirection), // 3
#[options(help = "rain pattern mimicking raindrops")]
Stars(TwoColourSpeed), // 4
#[options(help = "rain pattern of three preset colours")]
@@ -314,14 +312,14 @@ impl From<&SetAuraBuiltin> for AuraEffect {
data.mode = AuraModeNum::Breathe;
data
}
SetAuraBuiltin::RainbowCycle(x) => {
SetAuraBuiltin::Strobe(x) => {
let mut data: AuraEffect = x.into();
data.mode = AuraModeNum::RainbowCycle;
data.mode = AuraModeNum::Strobe;
data
}
SetAuraBuiltin::RainbowWave(x) => {
SetAuraBuiltin::Rainbow(x) => {
let mut data: AuraEffect = x.into();
data.mode = AuraModeNum::RainbowWave;
data.mode = AuraModeNum::Rainbow;
data
}
SetAuraBuiltin::Stars(x) => {
+33 -19
View File
@@ -1,10 +1,9 @@
use gumdrop::Options;
use rog_platform::platform::PlatformProfile;
use rog_platform::platform::ThrottlePolicy;
use crate::anime_cli::AnimeCommand;
use crate::aura_cli::{LedBrightness, LedPowerCommand1, LedPowerCommand2, SetAuraBuiltin};
use crate::fan_curve_cli::FanCurveCommand;
use crate::scsi_cli::ScsiCommand;
use crate::slash_cli::SlashCommand;
#[derive(Default, Options)]
@@ -23,8 +22,6 @@ pub struct CliStart {
pub prev_kbd_bright: bool,
#[options(meta = "", help = "Set your battery charge limit <20-100>")]
pub chg_limit: Option<u8>,
#[options(help = "Toggle one-shot battery charge to 100%")]
pub one_shot_chg: bool,
#[options(command)]
pub command: Option<CliCommand>,
}
@@ -32,11 +29,11 @@ pub struct CliStart {
#[derive(Options)]
pub enum CliCommand {
#[options(help = "Set the keyboard lighting from built-in modes")]
Aura(LedModeCommand),
LedMode(LedModeCommand),
#[options(help = "Set the LED power states")]
AuraPowerOld(LedPowerCommand1),
LedPow1(LedPowerCommand1),
#[options(help = "Set the LED power states")]
AuraPower(LedPowerCommand2),
LedPow2(LedPowerCommand2),
#[options(help = "Set or select platform_profile")]
Profile(ProfileCommand),
#[options(help = "Set, select, or modify fan curves if supported")]
@@ -47,13 +44,8 @@ pub enum CliCommand {
Anime(AnimeCommand),
#[options(name = "slash", help = "Manage Slash Ledbar")]
Slash(SlashCommand),
#[options(name = "scsi", help = "Manage SCSI external drive")]
Scsi(ScsiCommand),
#[options(
help = "Change platform settings. This is a new interface exposed by the asus-armoury \
driver, some of the settings will be the same as the older platform interface"
)]
Armoury(ArmouryCommand),
#[options(help = "Change bios settings")]
Bios(BiosCommand),
}
#[derive(Debug, Clone, Options)]
@@ -71,7 +63,7 @@ pub struct ProfileCommand {
pub profile_get: bool,
#[options(meta = "", help = "set the active profile")]
pub profile_set: Option<PlatformProfile>,
pub profile_set: Option<ThrottlePolicy>,
}
#[derive(Options)]
@@ -93,12 +85,34 @@ pub struct GraphicsCommand {
}
#[derive(Options, Debug)]
pub struct ArmouryCommand {
pub struct BiosCommand {
#[options(help = "print help message")]
pub help: bool,
#[options(
free,
help = "append each value name followed by the value to set. `-1` sets to default"
meta = "",
short = "S",
no_long,
help = "set bios POST sound: asusctl -S <true/false>"
)]
pub free: Vec<String>,
pub post_sound_set: Option<bool>,
#[options(no_long, short = "s", help = "read bios POST sound")]
pub post_sound_get: bool,
#[options(
meta = "",
short = "D",
no_long,
help = "Switch GPU MUX mode: 0 = Discrete, 1 = Optimus, reboot required"
)]
pub gpu_mux_mode_set: Option<u8>,
#[options(no_long, short = "d", help = "get GPU mode")]
pub gpu_mux_mode_get: bool,
#[options(
meta = "",
short = "O",
no_long,
help = "Set device panel overdrive <true/false>"
)]
pub panel_overdrive_set: Option<bool>,
#[options(no_long, short = "o", help = "get panel overdrive")]
pub panel_overdrive_get: bool,
}
+2 -2
View File
@@ -1,5 +1,5 @@
use gumdrop::Options;
use rog_platform::platform::PlatformProfile;
use rog_platform::platform::ThrottlePolicy;
use rog_profiles::fan_curve_set::CurveData;
use rog_profiles::FanCurvePU;
@@ -18,7 +18,7 @@ pub struct FanCurveCommand {
meta = "",
help = "profile to modify fan-curve for. Shows data if no options provided"
)]
pub mod_profile: Option<PlatformProfile>,
pub mod_profile: Option<ThrottlePolicy>,
#[options(
meta = "",
+316 -520
View File
File diff suppressed because it is too large Load Diff
-35
View File
@@ -1,35 +0,0 @@
use gumdrop::Options;
use rog_scsi::{AuraMode, Colour, Direction, Speed};
#[derive(Options)]
pub struct ScsiCommand {
#[options(help = "print help message")]
pub help: bool,
#[options(help = "Enable the SCSI drive LEDs")]
pub enable: Option<bool>,
#[options(meta = "", help = "Set LED mode (so 'list' for all options)")]
pub mode: Option<AuraMode>,
#[options(
meta = "",
help = "Set LED mode speed <slowest, slow, med, fast, fastest> (does not apply to all)"
)]
pub speed: Option<Speed>,
#[options(
meta = "",
help = "Set LED mode direction <forward, reverse> (does not apply to all)"
)]
pub direction: Option<Direction>,
#[options(
meta = "",
help = "Set LED colours <hex>, specify up to 4 with repeated arg"
)]
pub colours: Vec<Colour>,
#[options(help = "list available animations")]
pub list: bool,
}
+5 -20
View File
@@ -7,29 +7,14 @@ pub struct SlashCommand {
pub help: bool,
#[options(help = "Enable the Slash Ledbar")]
pub enable: bool,
#[options(help = "Disable the Slash Ledbar")]
#[options(help = "Ddisable the Slash Ledbar")]
pub disable: bool,
#[options(short = "l", meta = "", help = "Set brightness value <0-255>")]
#[options(meta = "", help = "Set brightness value <0-255>")]
pub brightness: Option<u8>,
#[options(meta = "", help = "Set interval value <0-5>")]
#[options(meta = "", help = "Set interval value <0-255>")]
pub interval: Option<u8>,
#[options(meta = "", help = "Set SlashMode (so 'list' for all options)")]
pub mode: Option<SlashMode>,
#[options(help = "Set SlashMode (so 'list' for all options)")]
pub slash_mode: Option<SlashMode>,
#[options(help = "list available animations")]
pub list: bool,
#[options(short = "B", meta = "", help = "Show the animation on boot")]
pub show_on_boot: Option<bool>,
#[options(short = "S", meta = "", help = "Show the animation on shutdown")]
pub show_on_shutdown: Option<bool>,
#[options(short = "s", meta = "", help = "Show the animation on sleep")]
pub show_on_sleep: Option<bool>,
#[options(short = "b", meta = "", help = "Show the animation on battery")]
pub show_on_battery: Option<bool>,
#[options(
short = "w",
meta = "",
help = "Show the low-battery warning animation"
)]
pub show_battery_warning: Option<bool>,
}
+7 -4
View File
@@ -12,16 +12,13 @@ edition.workspace = true
name = "asusd-user"
path = "src/daemon.rs"
[features]
default = []
local_data = []
[dependencies]
dirs.workspace = true
smol.workspace = true
# serialisation
serde.workspace = true
serde_derive.workspace = true
ron.workspace = true
rog_anime = { path = "../rog-anime" }
@@ -32,3 +29,9 @@ config-traits = { path = "../config-traits" }
zbus.workspace = true
env_logger.workspace = true
[dev-dependencies]
cargo-husky.workspace = true
[package.metadata.cargo-machete]
ignored = ["serde"]
+1 -1
View File
@@ -6,7 +6,7 @@ use rog_anime::{ActionLoader, AnimTime, AnimeType, Fade, Sequences as AnimeSeque
use rog_aura::effects::{AdvancedEffects as AuraSequences, Breathe, DoomFlicker, Effect, Static};
use rog_aura::keyboard::LedCode;
use rog_aura::{Colour, Speed};
use serde::{Deserialize, Serialize};
use serde_derive::{Deserialize, Serialize};
use crate::error::Error;
+8 -5
View File
@@ -9,7 +9,7 @@ use rog_anime::error::AnimeError;
use rog_anime::{ActionData, ActionLoader, AnimTime, Fade, Sequences, Vec2};
use rog_dbus::zbus_anime::AnimeProxyBlocking;
use ron::ser::PrettyConfig;
use serde::{Deserialize, Serialize};
use serde_derive::{Deserialize, Serialize};
use zbus::interface;
use zbus::zvariant::{ObjectPath, Type};
@@ -66,7 +66,7 @@ pub struct CtrlAnimeInner<'a> {
do_early_return: Arc<AtomicBool>,
}
impl CtrlAnimeInner<'static> {
impl<'a> CtrlAnimeInner<'static> {
pub fn new(
sequences: Sequences,
client: AnimeProxyBlocking<'static>,
@@ -81,7 +81,7 @@ impl CtrlAnimeInner<'static> {
/// To be called on each main loop iteration to pump out commands to the
/// anime
pub fn run(&self) -> Result<(), Error> {
pub fn run(&'a self) -> Result<(), Error> {
if self.do_early_return.load(Ordering::SeqCst) {
return Ok(());
}
@@ -151,7 +151,10 @@ impl CtrlAnime<'static> {
pub async fn add_to_server(self, server: &mut zbus::Connection) {
server
.object_server()
.at(&ObjectPath::from_str_unchecked("/xyz/ljones/Anime"), self)
.at(
&ObjectPath::from_str_unchecked("/org/asuslinux/Anime"),
self,
)
.await
.map_err(|err| {
println!("CtrlAnime: add_to_server {}", err);
@@ -167,7 +170,7 @@ impl CtrlAnime<'static> {
// - Do actions
// - Write config if required
// - Unset inner_early_return
#[interface(name = "xyz.ljones.Asusd")]
#[interface(name = "org.asuslinux.Daemon")]
impl CtrlAnime<'static> {
pub fn insert_asus_gif(
&mut self,
+9 -4
View File
@@ -11,7 +11,8 @@ use rog_aura::aura_detection::LedSupportData;
use rog_aura::keyboard::KeyLayout;
use rog_dbus::zbus_anime::AnimeProxyBlocking;
use rog_dbus::zbus_aura::AuraProxyBlocking;
use rog_dbus::{list_iface_blocking, DBUS_NAME};
use rog_dbus::zbus_platform::PlatformProxyBlocking;
use rog_dbus::DBUS_NAME;
use smol::Executor;
use zbus::Connection;
@@ -35,16 +36,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("rog-platform v{}", rog_platform::VERSION);
let conn = zbus::blocking::Connection::system().unwrap();
let platform_proxy = PlatformProxyBlocking::new(&conn).unwrap();
let supported = list_iface_blocking()?;
let supported = platform_proxy
.supported_interfaces()
.unwrap_or_default()
.contains(&"Anime".to_string());
let config = ConfigBase::new().load();
let executor = Executor::new();
let early_return = Arc::new(AtomicBool::new(false));
// Set up the anime data and run loop/thread
if supported.contains(&"xyz.ljones.Anime".to_string()) {
if supported {
if let Some(cfg) = config.active_anime {
let anime_type = get_anime_type();
let anime_type = get_anime_type()?;
let anime_config = ConfigAnime::new().set_name(cfg).load();
let anime = anime_config.create(anime_type)?;
let anime_config = Arc::new(Mutex::new(anime_config));
+7 -4
View File
@@ -1,8 +1,8 @@
//! # `DBus` interface proxy for: `xyz.ljones.Asusd`
//! # `DBus` interface proxy for: `org.asuslinux.Daemon`
//!
//! This code was generated by `zbus-xmlgen` `1.0.0` from `DBus` introspection
//! data. Source: `Interface '/xyz/ljones/Anime' from service
//! 'xyz.ljones.Asusd' on session bus`.
//! 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.
//!
@@ -23,7 +23,10 @@
use zbus::proxy;
#[proxy(interface = "xyz.ljones.Asusd", default_path = "/xyz/ljones/Anime")]
#[proxy(
interface = "org.asuslinux.Daemon",
default_path = "/org/asuslinux/Anime"
)]
trait Daemon {
/// InsertAsusGif method
fn insert_asus_gif(
+4 -2
View File
@@ -18,7 +18,6 @@ config-traits = { path = "../config-traits" }
rog_anime = { path = "../rog-anime", features = ["dbus"] }
rog_slash = { path = "../rog-slash", features = ["dbus"] }
rog_aura = { path = "../rog-aura", features = ["dbus"] }
rog_scsi = { path = "../rog-scsi", features = ["dbus"] }
rog_platform = { path = "../rog-platform" }
rog_profiles = { path = "../rog-profiles" }
dmi_id = { path = "../dmi-id" }
@@ -34,14 +33,17 @@ tokio.workspace = true
log.workspace = true
env_logger.workspace = true
futures-util.workspace = true
zbus.workspace = true
logind-zbus.workspace = true
# serialisation
serde.workspace = true
serde_derive.workspace = true
concat-idents.workspace = true
[dev-dependencies]
cargo-husky.workspace = true
[package.metadata.cargo-machete]
ignored = ["serde"]
-425
View File
@@ -1,425 +0,0 @@
use std::sync::Arc;
use config_traits::StdConfig;
use futures_util::lock::Mutex;
use log::{debug, error, info};
use rog_platform::asus_armoury::{AttrValue, Attribute, FirmwareAttribute, FirmwareAttributes};
use rog_platform::platform::{PlatformProfile, RogPlatform};
use rog_platform::power::AsusPower;
use serde::{Deserialize, Serialize};
use zbus::object_server::SignalEmitter;
use zbus::zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Type, Value};
use zbus::{fdo, interface, Connection};
use crate::config::Config;
use crate::error::RogError;
use crate::{Reloadable, ASUS_ZBUS_PATH};
const MOD_NAME: &str = "asus_armoury";
#[derive(Debug, Default, Clone, Deserialize, Serialize, Type, Value, OwnedValue)]
pub struct PossibleValues {
strings: Vec<String>,
nums: Vec<i32>,
}
fn dbus_path_for_attr(attr_name: &str) -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{attr_name}")).into()
}
#[derive(Clone)]
pub struct AsusArmouryAttribute {
attr: Attribute,
config: Arc<Mutex<Config>>,
/// platform control required here for access to PPD or Throttle profile
platform: RogPlatform,
power: AsusPower,
}
impl AsusArmouryAttribute {
pub fn new(
attr: Attribute,
platform: RogPlatform,
power: AsusPower,
config: Arc<Mutex<Config>>,
) -> Self {
Self {
attr,
config,
platform,
power,
}
}
pub async fn move_to_zbus(self, connection: &Connection) -> Result<(), RogError> {
let path = dbus_path_for_attr(self.attr.name());
connection
.object_server()
.at(path.clone(), self)
.await
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
.ok();
Ok(())
}
async fn watch_and_notify(
&mut self,
signal_ctxt: SignalEmitter<'static>,
) -> Result<(), RogError> {
use futures_util::StreamExt;
let name = self.name();
macro_rules! watch_value_notify {
($attr_str:expr, $fn_prop_changed:ident) => {
match self.attr.get_watcher($attr_str) {
Ok(watch) => {
let name = <&str>::from(name);
let ctrl = self.clone();
let sig = signal_ctxt.clone();
tokio::spawn(async move {
let mut buffer = [0; 32];
watch
.into_event_stream(&mut buffer)
.unwrap()
.for_each(|_| async {
debug!("{} changed", name);
ctrl.$fn_prop_changed(&sig).await.ok();
})
.await;
});
}
Err(e) => info!(
"inotify watch failed: {}. You can ignore this if your device does not \
support the feature",
e
),
}
};
}
// "current_value", "default_value", "min_value", "max_value"
watch_value_notify!("current_value", current_value_changed);
watch_value_notify!("default_value", default_value_changed);
watch_value_notify!("min_value", min_value_changed);
watch_value_notify!("max_value", max_value_changed);
Ok(())
}
}
impl crate::Reloadable for AsusArmouryAttribute {
async fn reload(&mut self) -> Result<(), RogError> {
info!("Reloading {}", self.attr.name());
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self
.power
.get_online()
.map_err(|e| {
error!("Could not get power status: {e:?}");
e
})
.unwrap_or_default();
let config = if power_plugged == 1 {
&self.config.lock().await.ac_profile_tunings
} else {
&self.config.lock().await.dc_profile_tunings
};
if let Some(tuning) = config.get(&profile) {
if tuning.enabled {
if let Some(tune) = tuning.group.get(&self.name()) {
self.attr
.set_current_value(&AttrValue::Integer(*tune))
.map_err(|e| {
error!("Could not set {} value: {e:?}", self.attr.name());
self.attr.base_path_exists();
e
})?;
info!("Set {} to {:?}", self.attr.name(), tune);
}
}
}
Ok(())
}
}
/// If return is `-1` on a property then there is avilable value for that
/// property
#[interface(name = "xyz.ljones.AsusArmoury")]
impl AsusArmouryAttribute {
#[zbus(property)]
fn name(&self) -> FirmwareAttribute {
self.attr.name().into()
}
#[zbus(property)]
async fn available_attrs(&self) -> Vec<String> {
let mut attrs = Vec::new();
if !matches!(self.attr.default_value(), AttrValue::None) {
attrs.push("default_value".to_string());
}
if !matches!(self.attr.min_value(), AttrValue::None) {
attrs.push("min_value".to_string());
}
if !matches!(self.attr.max_value(), AttrValue::None) {
attrs.push("max_value".to_string());
}
if !matches!(self.attr.scalar_increment(), AttrValue::None) {
attrs.push("scalar_increment".to_string());
}
if !matches!(self.attr.possible_values(), AttrValue::None) {
attrs.push("possible_values".to_string());
}
// TODO: Don't unwrap, use error
if let Ok(value) = self.attr.current_value().map_err(|e| {
error!("Failed to read: {e:?}");
e
}) {
if !matches!(value, AttrValue::None) {
attrs.push("current_value".to_string());
}
}
attrs
}
/// If return is `-1` then there is no default value
#[zbus(property)]
async fn default_value(&self) -> i32 {
match self.attr.default_value() {
AttrValue::Integer(i) => *i,
_ => -1,
}
}
async fn restore_default(&self) -> fdo::Result<()> {
self.attr.restore_default()?;
if self.name().is_ppt() {
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self
.power
.get_online()
.map_err(|e| {
error!("Could not get power status: {e:?}");
e
})
.unwrap_or_default();
let mut config = self.config.lock().await;
let tuning = config.select_tunings(power_plugged == 1, profile);
if let Some(tune) = tuning.group.get_mut(&self.name()) {
if let AttrValue::Integer(i) = self.attr.default_value() {
*tune = *i;
}
}
if tuning.enabled {
self.attr
.set_current_value(self.attr.default_value())
.map_err(|e| {
error!("Could not set value: {e:?}");
e
})?;
}
config.write();
}
Ok(())
}
#[zbus(property)]
async fn min_value(&self) -> i32 {
match self.attr.min_value() {
AttrValue::Integer(i) => *i,
_ => -1,
}
}
#[zbus(property)]
async fn max_value(&self) -> i32 {
match self.attr.max_value() {
AttrValue::Integer(i) => *i,
_ => -1,
}
}
#[zbus(property)]
async fn scalar_increment(&self) -> i32 {
match self.attr.scalar_increment() {
AttrValue::Integer(i) => *i,
_ => -1,
}
}
#[zbus(property)]
async fn possible_values(&self) -> Vec<i32> {
match self.attr.possible_values() {
AttrValue::EnumInt(i) => i.clone(),
_ => Vec::default(),
}
}
#[zbus(property)]
async fn current_value(&self) -> fdo::Result<i32> {
if self.name().is_ppt() {
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self
.power
.get_online()
.map_err(|e| {
error!("Could not get power status: {e:?}");
e
})
.unwrap_or_default();
let mut config = self.config.lock().await;
let tuning = config.select_tunings(power_plugged == 1, profile);
if let Some(tune) = tuning.group.get(&self.name()) {
return Ok(*tune);
} else if let AttrValue::Integer(i) = self.attr.default_value() {
return Ok(*i);
}
return Err(fdo::Error::Failed(
"Could not read current value".to_string(),
));
}
if let Ok(AttrValue::Integer(i)) = self.attr.current_value() {
return Ok(i);
}
Err(fdo::Error::Failed(
"Could not read current value".to_string(),
))
}
#[zbus(property)]
async fn set_current_value(&mut self, value: i32) -> fdo::Result<()> {
if self.name().is_ppt() {
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self
.power
.get_online()
.map_err(|e| {
error!("Could not get power status: {e:?}");
e
})
.unwrap_or_default();
let mut config = self.config.lock().await;
let tuning = config.select_tunings(power_plugged == 1, profile);
if let Some(tune) = tuning.group.get_mut(&self.name()) {
*tune = value;
} else {
tuning.group.insert(self.name(), value);
debug!("Store tuning config for {} = {:?}", self.attr.name(), value);
}
if tuning.enabled {
self.attr
.set_current_value(&AttrValue::Integer(value))
.map_err(|e| {
error!("Could not set value: {e:?}");
e
})?;
}
} else {
self.attr
.set_current_value(&AttrValue::Integer(value))
.map_err(|e| {
error!("Could not set value: {e:?}");
e
})?;
let has_attr = self
.config
.lock()
.await
.armoury_settings
.contains_key(&self.name());
if has_attr {
if let Some(setting) = self
.config
.lock()
.await
.armoury_settings
.get_mut(&self.name())
{
*setting = value
}
} else {
debug!("Adding config for {}", self.attr.name());
self.config
.lock()
.await
.armoury_settings
.insert(self.name(), value);
debug!("Set config for {} = {:?}", self.attr.name(), value);
}
}
self.config.lock().await.write();
Ok(())
}
}
pub async fn start_attributes_zbus(
conn: &Connection,
platform: RogPlatform,
power: AsusPower,
attributes: FirmwareAttributes,
config: Arc<Mutex<Config>>,
) -> Result<(), RogError> {
for attr in attributes.attributes() {
let mut attr = AsusArmouryAttribute::new(
attr.clone(),
platform.clone(),
power.clone(),
config.clone(),
);
attr.reload().await?;
let path = dbus_path_for_attr(attr.attr.name());
let sig = zbus::object_server::SignalEmitter::new(conn, path)?;
attr.watch_and_notify(sig).await?;
attr.move_to_zbus(conn).await?;
}
Ok(())
}
pub async fn set_config_or_default(
attrs: &FirmwareAttributes,
config: &mut Config,
power_plugged: bool,
profile: PlatformProfile,
) {
for attr in attrs.attributes().iter() {
let name: FirmwareAttribute = attr.name().into();
if name.is_ppt() {
let tuning = config.select_tunings(power_plugged, profile);
if !tuning.enabled {
debug!("Tuning group is not enabled, skipping");
return;
}
if let Some(tune) = tuning.group.get(&name) {
attr.set_current_value(&AttrValue::Integer(*tune))
.map_err(|e| {
error!("Failed to set {}: {e}", <&str>::from(name));
})
.ok();
} else {
let default = attr.default_value();
attr.set_current_value(default)
.map_err(|e| {
error!("Failed to set {}: {e}", <&str>::from(name));
})
.ok();
if let AttrValue::Integer(i) = default {
tuning.group.insert(name, *i);
info!(
"Set default tuning config for {} = {:?}",
<&str>::from(name),
i
);
// config.write();
}
}
}
}
}
-248
View File
@@ -1,248 +0,0 @@
pub mod config;
/// Implements `CtrlTask`, Reloadable, `ZbusRun`
pub mod trait_impls;
use std::convert::TryFrom;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::sleep;
use config_traits::StdConfig;
use futures_util::lock::Mutex;
use log::{debug, error, info, warn};
use rog_anime::usb::{
pkt_flush, pkt_set_brightness, pkt_set_enable_display, pkt_set_enable_powersave_anim,
pkts_for_init, Brightness,
};
use rog_anime::{ActionData, AnimeDataBuffer, AnimePacketType};
use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw;
use self::config::{AniMeConfig, AniMeConfigCached};
use crate::error::RogError;
#[derive(Debug, Clone)]
pub struct AniMe {
hid: Option<Arc<Mutex<HidRaw>>>,
usb: Option<Arc<Mutex<USBRaw>>>,
config: Arc<Mutex<AniMeConfig>>,
cache: AniMeConfigCached,
// set to force thread to exit
thread_exit: Arc<AtomicBool>,
// Set to false when the thread exits
thread_running: Arc<AtomicBool>,
}
impl AniMe {
pub fn new(
hid: Option<Arc<Mutex<HidRaw>>>,
usb: Option<Arc<Mutex<USBRaw>>>,
config: Arc<Mutex<AniMeConfig>>,
) -> Self {
Self {
hid,
usb,
config,
cache: AniMeConfigCached::default(),
thread_exit: Arc::new(AtomicBool::new(false)),
thread_running: Arc::new(AtomicBool::new(false)),
}
}
/// Will fail if something is already holding the config lock
async fn do_init_cache(&mut self) {
if let Some(mut config) = self.config.try_lock() {
if let Err(e) = self.cache.init_from_config(&config, config.anime_type) {
error!(
"Trying to cache the Anime Config failed, will reset to default config: {e:?}"
);
config.rename_file_old();
*config = AniMeConfig::new();
config.write();
} else {
debug!("Initialised AniMe cache");
}
} else {
error!("AniMe Matrix could not init cache")
}
}
/// Initialise the device if required.
pub async fn do_initialization(&mut self) -> Result<(), RogError> {
self.do_init_cache().await;
let pkts = pkts_for_init();
self.write_bytes(&pkts[0]).await?;
self.write_bytes(&pkts[1]).await?;
debug!("Succesfully initialised AniMe matrix display");
Ok(())
}
pub async fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
if let Some(hid) = &self.hid {
hid.lock().await.write_bytes(message)?;
} else if let Some(usb) = &self.usb {
usb.lock().await.write_bytes(message)?;
}
Ok(())
}
/// Write only a data packet. This will modify the leds brightness using the
/// global brightness set in config.
async fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) -> Result<(), RogError> {
for led in buffer.data_mut().iter_mut() {
let mut bright = *led as f32;
if bright > 254.0 {
bright = 254.0;
}
*led = bright as u8;
}
let data = AnimePacketType::try_from(buffer)?;
for row in &data {
self.write_bytes(row).await?;
}
self.write_bytes(&pkt_flush()).await
}
pub async fn set_builtins_enabled(
&self,
enabled: bool,
bright: Brightness,
) -> Result<(), RogError> {
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
.await?;
self.write_bytes(&pkt_set_enable_display(enabled)).await?;
self.write_bytes(&pkt_set_brightness(bright)).await?;
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
.await
}
/// Start an action thread. This is classed as a singleton and there should
/// be only one running - so the thread uses atomics to signal run/exit.
///
/// Because this also writes to the usb device, other write tries (display
/// only) *must* get the mutex lock and set the `thread_exit` atomic.
async fn run_thread(&self, actions: Vec<ActionData>, mut once: bool) {
if actions.is_empty() {
warn!("AniMe system actions was empty");
return;
}
self.write_bytes(&pkt_set_enable_powersave_anim(false))
.await
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
let thread_exit = self.thread_exit.clone();
let thread_running = self.thread_running.clone();
let anime_type = self.config.lock().await.anime_type;
let inner = self.clone();
// Loop rules:
// - Lock the mutex **only when required**. That is, the lock must be held for
// the shortest duration possible.
// - An AtomicBool used for thread exit should be checked in every loop,
// including nested
// The only reason for this outer thread is to prevent blocking while waiting
// for the next spawned thread to exit
// TODO: turn this in to async task (maybe? COuld still risk blocking main
// thread)
tokio::spawn(async move {
info!("AniMe new system thread started");
// First two loops are to ensure we *do* aquire a lock on the mutex
// The reason the loop is required is because the USB writes can block
// for up to 10ms. We can't fail to get the atomics.
while thread_running.load(Ordering::SeqCst) {
// Make any running loop exit first
thread_exit.store(true, Ordering::SeqCst);
}
info!("AniMe no previous system thread running (now)");
thread_exit.store(false, Ordering::SeqCst);
thread_running.store(true, Ordering::SeqCst);
'main: loop {
for action in &actions {
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
match action {
ActionData::Animation(frames) => {
// TODO: sort all this out
rog_anime::run_animation(frames, &|frame| {
if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: animation sub-loop was asked to exit");
return Ok(true); // Do safe exit
}
let inner = inner.clone();
tokio::task::spawn(async move {
inner
.write_data_buffer(frame)
.await
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
});
Ok(false) // Don't exit yet
});
if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: sub-loop exited and main loop exiting now");
break 'main;
}
}
ActionData::Image(image) => {
once = false;
inner
.write_data_buffer(image.as_ref().clone())
.await
.map_err(|e| error!("{}", e))
.ok();
}
ActionData::Pause(duration) => sleep(*duration),
ActionData::AudioEq
| ActionData::SystemInfo
| ActionData::TimeDate
| ActionData::Matrix => {}
}
}
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
if once || actions.is_empty() {
break 'main;
}
}
// Clear the display on exit
if let Ok(data) =
AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()])
.map_err(|e| error!("{}", e))
{
inner
.write_data_buffer(data)
.await
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
}
// A write can block for many milliseconds so lets not hold the config lock for
// the same period
let enabled = inner.config.lock().await.builtin_anims_enabled;
inner
.write_bytes(&pkt_set_enable_powersave_anim(enabled))
.await
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
// Loop ended, set the atmonics
thread_running.store(false, Ordering::SeqCst);
info!("AniMe system thread exited");
})
.await
.map(|err| info!("AniMe system thread: {:?}", err))
.ok();
}
}
-229
View File
@@ -1,229 +0,0 @@
use std::sync::Arc;
use config::AuraConfig;
use config_traits::StdConfig;
use futures_util::lock::{Mutex, MutexGuard};
use log::info;
use rog_aura::keyboard::{AuraLaptopUsbPackets, LedUsbPackets};
use rog_aura::usb::{AURA_LAPTOP_LED_APPLY, AURA_LAPTOP_LED_SET};
use rog_aura::{AuraDeviceType, AuraEffect, LedBrightness, PowerZones, AURA_LAPTOP_LED_MSG_LEN};
use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardBacklight;
use crate::error::RogError;
pub mod config;
pub mod trait_impls;
#[derive(Debug, Clone)]
pub struct Aura {
pub hid: Option<Arc<Mutex<HidRaw>>>,
pub backlight: Option<Arc<Mutex<KeyboardBacklight>>>,
pub config: Arc<Mutex<AuraConfig>>,
}
impl Aura {
/// Initialise the device if required.
pub async fn do_initialization(&self) -> Result<(), RogError> {
Ok(())
}
pub async fn lock_config(&self) -> MutexGuard<AuraConfig> {
self.config.lock().await
}
/// Will lock the internal config and update. If anything else has locked
/// this in scope then a deadlock can occur.
pub async fn update_config(&self) -> Result<(), RogError> {
let mut config = self.config.lock().await;
let bright = if let Some(bl) = self.backlight.as_ref() {
bl.lock().await.get_brightness().unwrap_or_default()
} else {
config.brightness.into()
};
config.read();
config.brightness = bright.into();
config.write();
Ok(())
}
pub async fn write_current_config_mode(&self, config: &mut AuraConfig) -> Result<(), RogError> {
if config.multizone_on {
let mode = config.current_mode;
let mut create = false;
// There is no multizone config for this mode so create one here
// using the colours of rainbow if it exists, or first available
// mode, or random
if config.multizone.is_none() {
create = true;
} else if let Some(multizones) = config.multizone.as_ref() {
if !multizones.contains_key(&mode) {
create = true;
}
}
if create {
info!("No user-set config for zone founding, attempting a default");
config.create_multizone_default()?;
}
if let Some(multizones) = config.multizone.as_mut() {
if let Some(set) = multizones.get(&mode) {
for mode in set.clone() {
self.write_effect_and_apply(config.led_type, &mode).await?;
}
}
}
} else {
let mode = config.current_mode;
if let Some(effect) = config.builtins.get(&mode).cloned() {
self.write_effect_and_apply(config.led_type, &effect)
.await?;
}
}
Ok(())
}
/// Write the AuraEffect to the device. Will lock `backlight` or `hid`.
///
/// If per-key or software-mode is active it must be marked as disabled in
/// config.
pub async fn write_effect_and_apply(
&self,
dev_type: AuraDeviceType,
mode: &AuraEffect,
) -> Result<(), RogError> {
if matches!(dev_type, AuraDeviceType::LaptopKeyboardTuf) {
if let Some(platform) = &self.backlight {
let buf = [
1, mode.mode as u8, mode.colour1.r, mode.colour1.g, mode.colour1.b,
mode.speed as u8,
];
platform.lock().await.set_kbd_rgb_mode(&buf)?;
}
} else if let Some(hid_raw) = &self.hid {
let bytes: [u8; AURA_LAPTOP_LED_MSG_LEN] = mode.into();
let hid_raw = hid_raw.lock().await;
hid_raw.write_bytes(&bytes)?;
hid_raw.write_bytes(&AURA_LAPTOP_LED_SET)?;
// Changes won't persist unless apply is set
hid_raw.write_bytes(&AURA_LAPTOP_LED_APPLY)?;
} else {
return Err(RogError::NoAuraKeyboard);
}
Ok(())
}
pub async fn set_brightness(&self, value: u8) -> Result<(), RogError> {
if let Some(backlight) = &self.backlight {
backlight.lock().await.set_brightness(value)?;
return Ok(());
}
Err(RogError::MissingFunction(
"No LED backlight control available".to_string(),
))
}
/// Set combination state for boot animation/sleep animation/all leds/keys
/// leds/side leds LED active
pub async fn set_power_states(&self, config: &AuraConfig) -> Result<(), RogError> {
if matches!(config.led_type, rog_aura::AuraDeviceType::LaptopKeyboardTuf) {
if let Some(backlight) = &self.backlight {
// TODO: tuf bool array
let buf = config.enabled.to_bytes(config.led_type);
backlight.lock().await.set_kbd_rgb_state(&buf)?;
}
} else if let Some(hid_raw) = &self.hid {
let hid_raw = hid_raw.lock().await;
if let Some(p) = config.enabled.states.first() {
if p.zone == PowerZones::Ally {
let msg = [
0x5d,
0xd1,
0x09,
0x01,
p.new_to_byte() as u8,
0x0,
0x0,
];
hid_raw.write_bytes(&msg)?;
return Ok(());
}
}
let bytes = config.enabled.to_bytes(config.led_type);
let msg = [
0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2], bytes[3],
];
hid_raw.write_bytes(&msg)?;
}
Ok(())
}
/// Write an effect block. This is for per-key, but can be repurposed to
/// write the raw factory mode packets - when doing this it is expected that
/// only the first `Vec` (`effect[0]`) is valid.
pub async fn write_effect_block(
&self,
config: &mut AuraConfig,
effect: &AuraLaptopUsbPackets,
) -> Result<(), RogError> {
if config.brightness == LedBrightness::Off {
config.brightness = LedBrightness::Med;
config.write();
}
let pkt_type = effect[0][1];
const PER_KEY_TYPE: u8 = 0xbc;
if let Some(hid_raw) = &self.hid {
let hid_raw = hid_raw.lock().await;
if pkt_type != PER_KEY_TYPE {
config.per_key_mode_active = false;
hid_raw.write_bytes(&effect[0])?;
hid_raw.write_bytes(&AURA_LAPTOP_LED_SET)?;
// hid_raw.write_bytes(&LED_APPLY)?;
} else {
if !config.per_key_mode_active {
let init = LedUsbPackets::get_init_msg();
hid_raw.write_bytes(&init)?;
config.per_key_mode_active = true;
}
for row in effect.iter() {
hid_raw.write_bytes(row)?;
}
}
} else if matches!(config.led_type, rog_aura::AuraDeviceType::LaptopKeyboardTuf) {
if let Some(tuf) = &self.backlight {
for row in effect.iter() {
let r = row[9];
let g = row[10];
let b = row[11];
tuf.lock().await.set_kbd_rgb_mode(&[
0, 0, r, g, b, 0,
])?;
}
}
}
Ok(())
}
pub async fn fix_ally_power(&mut self) -> Result<(), RogError> {
if self.config.lock().await.led_type == AuraDeviceType::Ally {
if let Some(hid_raw) = &self.hid {
let mut config = self.config.lock().await;
if config.ally_fix.is_none() {
let msg = [
0x5d, 0xbd, 0x01, 0xff, 0xff, 0xff, 0xff,
];
hid_raw.lock().await.write_bytes(&msg)?;
info!("Reset Ally power settings to base");
config.ally_fix = Some(true);
}
config.write();
}
}
Ok(())
}
}
-343
View File
@@ -1,343 +0,0 @@
use std::collections::BTreeMap;
use config_traits::StdConfig;
use log::{debug, error, info, warn};
use rog_aura::keyboard::{AuraLaptopUsbPackets, LaptopAuraPower};
use rog_aura::{AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, LedBrightness, PowerZones};
use zbus::fdo::Error as ZbErr;
use zbus::object_server::SignalEmitter;
use zbus::zvariant::OwnedObjectPath;
use zbus::{interface, Connection};
use super::Aura;
use crate::error::RogError;
use crate::{CtrlTask, Reloadable};
pub const AURA_ZBUS_NAME: &str = "Aura";
pub const AURA_ZBUS_PATH: &str = "/xyz/ljones";
#[derive(Clone)]
pub struct AuraZbus(Aura);
impl AuraZbus {
pub fn new(aura: Aura) -> Self {
Self(aura)
}
pub async fn start_tasks(
mut self,
connection: &Connection,
// _signal_ctx: SignalEmitter<'static>,
path: OwnedObjectPath,
) -> Result<(), RogError> {
// let task = zbus.clone();
// let signal_ctx = signal_ctx.clone();
self.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
connection
.object_server()
.at(path.clone(), self)
.await
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
.ok();
// TODO: skip this until we keep handles to tasks so they can be killed
// task.create_tasks(signal_ctx).await
Ok(())
}
}
/// The main interface for changing, reading, or notfying
///
/// LED commands are split between Brightness, Modes, Per-Key
#[interface(name = "xyz.ljones.Aura")]
impl AuraZbus {
/// Return the device type for this Aura keyboard
#[zbus(property)]
async fn device_type(&self) -> AuraDeviceType {
self.0.config.lock().await.led_type
}
/// Return the current LED brightness
#[zbus(property)]
async fn brightness(&self) -> Result<LedBrightness, ZbErr> {
if let Some(bl) = self.0.backlight.as_ref() {
return Ok(bl.lock().await.get_brightness().map(|n| n.into())?);
}
Err(ZbErr::Failed("No sysfs brightness control".to_string()))
}
/// Set the keyboard brightness level (0-3)
#[zbus(property)]
async fn set_brightness(&mut self, brightness: LedBrightness) -> Result<(), ZbErr> {
if let Some(bl) = self.0.backlight.as_ref() {
return Ok(bl.lock().await.set_brightness(brightness.into())?);
}
Err(ZbErr::Failed("No sysfs brightness control".to_string()))
}
/// Total levels of brightness available
#[zbus(property)]
async fn supported_brightness(&self) -> Vec<LedBrightness> {
vec![
LedBrightness::Off,
LedBrightness::Low,
LedBrightness::Med,
LedBrightness::High,
]
}
/// The total available modes
#[zbus(property)]
async fn supported_basic_modes(&self) -> Result<Vec<AuraModeNum>, ZbErr> {
let config = self.0.config.lock().await;
Ok(config.builtins.keys().cloned().collect())
}
#[zbus(property)]
async fn supported_basic_zones(&self) -> Result<Vec<AuraZone>, ZbErr> {
let config = self.0.config.lock().await;
Ok(config.support_data.basic_zones.clone())
}
#[zbus(property)]
async fn supported_power_zones(&self) -> Result<Vec<PowerZones>, ZbErr> {
let config = self.0.config.lock().await;
Ok(config.support_data.power_zones.clone())
}
/// The current mode data
#[zbus(property)]
async fn led_mode(&self) -> Result<AuraModeNum, ZbErr> {
// entirely possible to deadlock here, so use try instead of lock()
// let ctrl = self.0.lock().await;
// Ok(config.current_mode)
if let Some(config) = self.0.config.try_lock() {
Ok(config.current_mode)
} else {
Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
}
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode(&mut self, num: AuraModeNum) -> Result<(), ZbErr> {
let mut config = self.0.config.lock().await;
config.current_mode = num;
self.0.write_current_config_mode(&mut config).await?;
if config.brightness == LedBrightness::Off {
config.brightness = LedBrightness::Med;
}
self.0.set_brightness(config.brightness.into()).await?;
config.write();
Ok(())
}
/// The current mode data
#[zbus(property)]
async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> {
// entirely possible to deadlock here, so use try instead of lock()
if let Some(config) = self.0.config.try_lock() {
let mode = config.current_mode;
match config.builtins.get(&mode) {
Some(effect) => Ok(effect.clone()),
None => Err(ZbErr::Failed("Could not get the current effect".into())),
}
} else {
Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
}
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode_data(&mut self, effect: AuraEffect) -> Result<(), ZbErr> {
let mut config = self.0.config.lock().await;
if !config.support_data.basic_modes.contains(&effect.mode)
|| effect.zone != AuraZone::None
&& !config.support_data.basic_zones.contains(&effect.zone)
{
return Err(ZbErr::NotSupported(format!(
"The Aura effect is not supported: {effect:?}"
)));
}
self.0
.write_effect_and_apply(config.led_type, &effect)
.await?;
if config.brightness == LedBrightness::Off {
config.brightness = LedBrightness::Med;
}
self.0.set_brightness(config.brightness.into()).await?;
config.set_builtin(effect);
config.write();
Ok(())
}
/// Get the data set for every mode available
async fn all_mode_data(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
let config = self.0.config.lock().await;
config.builtins.clone()
}
// As property doesn't work for AuraPowerDev (complexity of serialization?)
#[zbus(property)]
async fn led_power(&self) -> LaptopAuraPower {
let config = self.0.config.lock().await;
config.enabled.clone()
}
/// Set a variety of states, input is array of enum.
/// `enabled` sets if the sent array should be disabled or enabled
///
/// For Modern ROG devices the "enabled" flag is ignored.
#[zbus(property)]
async fn set_led_power(&mut self, options: LaptopAuraPower) -> Result<(), ZbErr> {
let mut config = self.0.config.lock().await;
for opt in options.states {
let zone = opt.zone;
for config in config.enabled.states.iter_mut() {
if config.zone == zone {
*config = opt;
}
}
}
config.write();
Ok(self.0.set_power_states(&config).await.map_err(|e| {
warn!("{}", e);
e
})?)
}
/// On machine that have some form of either per-key keyboard or per-zone
/// this can be used to write custom effects over dbus. The input is a
/// nested `Vec<Vec<8>>` where `Vec<u8>` is a raw USB packet
async fn direct_addressing_raw(&self, data: AuraLaptopUsbPackets) -> Result<(), ZbErr> {
let mut config = self.0.config.lock().await;
self.0.write_effect_block(&mut config, &data).await?;
Ok(())
}
}
impl CtrlTask for AuraZbus {
fn zbus_path() -> &'static str {
"/xyz/ljones"
}
async fn create_tasks(&self, _: SignalEmitter<'static>) -> Result<(), RogError> {
let inner1 = self.0.clone();
let inner3 = self.0.clone();
self.create_sys_event_tasks(
move |sleeping| {
let inner1 = inner1.clone();
// unwrap as we want to bomb out of the task
async move {
if !sleeping {
info!("CtrlKbdLedTask reloading brightness and modes");
if let Some(backlight) = &inner1.backlight {
backlight
.lock()
.await
.set_brightness(inner1.config.lock().await.brightness.into())
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})
.unwrap();
}
let mut config = inner1.config.lock().await;
inner1
.write_current_config_mode(&mut config)
.await
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})
.unwrap();
} else if sleeping {
inner1
.update_config()
.await
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})
.unwrap();
}
}
},
move |_shutting_down| {
let inner3 = inner3.clone();
async move {
info!("CtrlKbdLedTask reloading brightness and modes");
if let Some(backlight) = &inner3.backlight {
// unwrap as we want to bomb out of the task
backlight
.lock()
.await
.set_brightness(inner3.config.lock().await.brightness.into())
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})
.unwrap();
}
}
},
move |_lid_closed| {
// on lid change
async move {}
},
move |_power_plugged| {
// power change
async move {}
},
)
.await;
// let ctrl2 = self.0.clone();
// let ctrl = self.0.lock().await;
// if ctrl.led_node.has_brightness_control() {
// let watch = ctrl.led_node.monitor_brightness()?;
// tokio::spawn(async move {
// let mut buffer = [0; 32];
// watch
// .into_event_stream(&mut buffer)
// .unwrap()
// .for_each(|_| async {
// if let Some(lock) = ctrl2.try_lock() {
// load_save(true, lock).unwrap(); // unwrap as we want
// // to
// // bomb out of the
// // task
// }
// })
// .await;
// });
// }
Ok(())
}
}
impl Reloadable for AuraZbus {
async fn reload(&mut self) -> Result<(), RogError> {
self.0.fix_ally_power().await?;
debug!("reloading keyboard mode");
let mut config = self.0.lock_config().await;
self.0.write_current_config_mode(&mut config).await?;
debug!("reloading power states");
self.0
.set_power_states(&config)
.await
.map_err(|err| warn!("{err}"))
.ok();
Ok(())
}
}
-541
View File
@@ -1,541 +0,0 @@
// Plan:
// - Manager has udev monitor on USB looking for ROG devices
// - If a device is found, add it to watch
// - Add it to Zbus server
// - If udev sees device removed then remove the zbus path
use std::sync::Arc;
use dmi_id::DMIID;
use futures_lite::future::block_on;
use futures_util::lock::Mutex;
use log::{debug, error, info, warn};
use mio::{Events, Interest, Poll, Token};
use rog_platform::error::PlatformError;
use rog_platform::hid_raw::HidRaw;
use udev::{Device, MonitorBuilder};
use zbus::zvariant::{ObjectPath, OwnedObjectPath};
use zbus::Connection;
use crate::aura_anime::trait_impls::AniMeZbus;
use crate::aura_laptop::trait_impls::AuraZbus;
use crate::aura_scsi::trait_impls::ScsiZbus;
use crate::aura_slash::trait_impls::SlashZbus;
use crate::aura_types::DeviceHandle;
use crate::error::RogError;
use crate::ASUS_ZBUS_PATH;
const MOD_NAME: &str = "aura";
/// Returns only the Device details concatenated in a form usable for
/// adding/appending to a filename
pub fn filename_partial(parent: &Device) -> Option<OwnedObjectPath> {
if let Some(id_product) = parent.attribute_value("idProduct") {
let id_product = id_product.to_string_lossy();
let mut path = if let Some(devnum) = parent.attribute_value("devnum") {
let devnum = devnum.to_string_lossy();
if let Some(devpath) = parent.attribute_value("devpath") {
let devpath = devpath.to_string_lossy();
format!("{id_product}_{devnum}_{devpath}")
} else {
format!("{id_product}_{devnum}")
}
} else {
format!("{id_product}")
};
if path.contains('.') {
warn!("dbus path for {id_product} contains `.`, removing");
path.replace('.', "").clone_into(&mut path);
}
return Some(ObjectPath::from_str_unchecked(&path).into());
}
None
}
fn dbus_path_for_dev(parent: &Device) -> Option<OwnedObjectPath> {
if let Some(filename) = filename_partial(parent) {
return Some(
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{filename}"))
.into(),
);
}
None
}
fn dbus_path_for_tuf() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/tuf")).into()
}
fn dbus_path_for_slash() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/slash")).into()
}
fn dbus_path_for_anime() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/anime")).into()
}
fn dbus_path_for_scsi(prod_id: &str) -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{prod_id}_scsi")).into()
}
fn dev_prop_matches(dev: &Device, prop: &str, value: &str) -> bool {
if let Some(p) = dev.property_value(prop) {
return p == value;
}
false
}
/// A device.
///
/// Each controller within should track its dbus path so it can be removed if
/// required.
pub struct AsusDevice {
device: DeviceHandle,
dbus_path: OwnedObjectPath,
}
pub struct DeviceManager {
_dbus_connection: Connection,
}
impl DeviceManager {
async fn init_hid_devices(
connection: &Connection,
device: Device,
) -> Result<Vec<AsusDevice>, RogError> {
let mut devices = Vec::new();
if let Some(usb_device) = device.parent_with_subsystem_devtype("usb", "usb_device")? {
if let Some(usb_id) = usb_device.attribute_value("idProduct") {
if let Some(vendor_id) = usb_device.attribute_value("idVendor") {
if vendor_id != "0b05" {
debug!("Not ASUS vendor ID");
return Ok(devices);
}
// Almost all devices are identified by the productId.
// So let's see what we have and:
// 1. Generate an interface path
// 2. Create the device
// Use the top-level endpoint, not the parent
if let Ok(hidraw) = HidRaw::from_device(device) {
debug!("Testing device {usb_id:?}");
let dev = Arc::new(Mutex::new(hidraw));
// SLASH DEVICE
if let Ok(dev_type) = DeviceHandle::new_slash_hid(
dev.clone(),
usb_id.to_str().unwrap_or_default(),
)
.await
{
if let DeviceHandle::Slash(slash) = dev_type.clone() {
let path =
dbus_path_for_dev(&usb_device).unwrap_or(dbus_path_for_slash());
let ctrl = SlashZbus::new(slash);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path,
});
}
}
// ANIME MATRIX DEVICE
if let Ok(dev_type) = DeviceHandle::maybe_anime_hid(
dev.clone(),
usb_id.to_str().unwrap_or_default(),
)
.await
{
if let DeviceHandle::AniMe(anime) = dev_type.clone() {
let path =
dbus_path_for_dev(&usb_device).unwrap_or(dbus_path_for_anime());
let ctrl = AniMeZbus::new(anime);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path,
});
}
}
// AURA LAPTOP DEVICE
if let Ok(dev_type) = DeviceHandle::maybe_laptop_aura(
Some(dev),
usb_id.to_str().unwrap_or_default(),
)
.await
{
if let DeviceHandle::Aura(aura) = dev_type.clone() {
let path =
dbus_path_for_dev(&usb_device).unwrap_or(dbus_path_for_tuf());
let ctrl = AuraZbus::new(aura);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path,
});
}
}
}
}
}
}
Ok(devices)
}
/// To be called on daemon startup
async fn init_all_hid(connection: &Connection) -> Result<Vec<AsusDevice>, RogError> {
// track and ensure we use only one hidraw per prod_id
// let mut interfaces = HashSet::new();
let mut devices: Vec<AsusDevice> = Vec::new();
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
PlatformError::Udev("enumerator failed".into(), err)
})?;
enumerator.match_subsystem("hidraw").map_err(|err| {
warn!("{}", err);
PlatformError::Udev("match_subsystem failed".into(), err)
})?;
for device in enumerator
.scan_devices()
.map_err(|e| PlatformError::IoPath("enumerator".to_owned(), e))?
{
devices.append(&mut Self::init_hid_devices(connection, device).await?);
}
Ok(devices)
}
async fn init_scsi(
connection: &Connection,
device: &Device,
path: OwnedObjectPath,
) -> Option<AsusDevice> {
// "ID_MODEL_ID" "1932"
// "ID_VENDOR_ID" "0b05"
if dev_prop_matches(device, "ID_VENDOR_ID", "0b05") {
if let Some(dev_node) = device.devnode() {
let prod_id = device
.property_value("ID_MODEL_ID")
.unwrap_or_default()
.to_string_lossy();
if let Ok(dev_type) =
DeviceHandle::maybe_scsi(dev_node.as_os_str().to_str().unwrap(), &prod_id).await
{
if let DeviceHandle::Scsi(scsi) = dev_type.clone() {
let ctrl = ScsiZbus::new(scsi);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
return Some(AsusDevice {
device: dev_type,
dbus_path: path,
});
}
}
}
}
None
}
async fn init_all_scsi(connection: &Connection) -> Result<Vec<AsusDevice>, RogError> {
// track and ensure we use only one hidraw per prod_id
// let mut interfaces = HashSet::new();
let mut devices: Vec<AsusDevice> = Vec::new();
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
PlatformError::Udev("enumerator failed".into(), err)
})?;
enumerator.match_subsystem("block").map_err(|err| {
warn!("{}", err);
PlatformError::Udev("match_subsystem failed".into(), err)
})?;
let mut found = Vec::new();
for device in enumerator
.scan_devices()
.map_err(|e| PlatformError::IoPath("enumerator".to_owned(), e))?
{
if let Some(serial) = device.property_value("ID_SERIAL_SHORT") {
let serial = serial.to_string_lossy().to_string();
let path = dbus_path_for_scsi(&serial);
if found.contains(&path) {
continue;
}
if let Some(dev) = Self::init_scsi(connection, &device, path.clone()).await {
devices.push(dev);
found.push(path);
}
} else {
debug!("No serial for SCSI device: {:?}", device.devpath());
}
}
Ok(devices)
}
pub async fn find_all_devices(connection: &Connection) -> Vec<AsusDevice> {
let mut devices: Vec<AsusDevice> = Vec::new();
// HID first, always
if let Ok(devs) = &mut Self::init_all_hid(connection).await {
devices.append(devs);
}
// USB after, need to check if HID picked something up and if so, skip it
let mut do_anime = true;
let mut do_slash = true;
let mut do_kb_backlight = true;
for dev in devices.iter() {
if matches!(dev.device, DeviceHandle::Slash(_)) {
do_slash = false;
}
if matches!(dev.device, DeviceHandle::AniMe(_)) {
do_anime = false;
}
if matches!(dev.device, DeviceHandle::Aura(_) | DeviceHandle::OldAura(_)) {
do_kb_backlight = false;
}
}
if do_slash {
if let Ok(dev_type) = DeviceHandle::new_slash_usb().await {
if let DeviceHandle::Slash(slash) = dev_type.clone() {
let path = dbus_path_for_slash();
let ctrl = SlashZbus::new(slash);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path,
});
}
} else {
info!("Tested device was not Slash");
}
}
if do_anime {
if let Ok(dev_type) = DeviceHandle::maybe_anime_usb().await {
// TODO: this is copy/pasted
if let DeviceHandle::AniMe(anime) = dev_type.clone() {
let path = dbus_path_for_anime();
let ctrl = AniMeZbus::new(anime);
if ctrl
.start_tasks(connection, path.clone())
.await
.map_err(|e| error!("Failed to start tasks: {e:?}, not adding this device"))
.is_ok()
{
devices.push(AsusDevice {
device: dev_type,
dbus_path: path,
});
}
}
} else {
info!("Tested device was not AniMe Matrix");
}
}
if do_kb_backlight {
// TUF AURA LAPTOP DEVICE
// product_name = ASUS TUF Gaming F15 FX507ZE_FX507ZE
// product_family = ASUS TUF Gaming F15
let product_name = DMIID::new().unwrap_or_default().product_name;
let product_family = DMIID::new().unwrap_or_default().product_family;
info!(
"No USB keyboard aura, system is {product_name}, try using sysfs backlight control"
);
if product_name.contains("TUF") || product_family.contains("TUF") {
info!("TUF laptop, try using sysfs backlight control");
if let Ok(dev_type) = DeviceHandle::maybe_laptop_aura(None, "tuf").await {
if let DeviceHandle::Aura(aura) = dev_type.clone() {
let path = dbus_path_for_tuf();
let ctrl = AuraZbus::new(aura);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path,
});
}
}
}
}
if let Ok(devs) = &mut Self::init_all_scsi(connection).await {
devices.append(devs);
}
devices
}
pub async fn new(connection: Connection) -> Result<Self, RogError> {
let conn_copy = connection.clone();
let devices = Self::find_all_devices(&conn_copy).await;
info!("Found {} valid devices on startup", devices.len());
let devices = Arc::new(Mutex::new(devices));
let manager = Self {
_dbus_connection: connection,
};
// TODO: The /sysfs/ LEDs don't cause events, so they need to be manually
// checked for and added
std::thread::spawn(move || {
let mut monitor = MonitorBuilder::new()?.listen()?;
let mut poll = Poll::new()?;
let mut events = Events::with_capacity(1024);
poll.registry()
.register(&mut monitor, Token(0), Interest::READABLE)?;
let rt = tokio::runtime::Runtime::new().expect("Unable to create Runtime");
let _enter = rt.enter();
loop {
if poll.poll(&mut events, None).is_err() {
continue;
}
for event in monitor.iter() {
let action = event
.action()
.unwrap_or_default()
.to_string_lossy()
.to_string();
let subsys = if let Some(subsys) = event.subsystem() {
subsys.to_string_lossy().to_string()
} else {
continue;
};
let devices = devices.clone();
let conn_copy = conn_copy.clone();
block_on(async move {
// SCSCI devs
if subsys == "block" {
if action == "remove" {
if let Some(serial) =
event.device().property_value("ID_SERIAL_SHORT")
{
let serial = serial.to_string_lossy().to_string();
let path = dbus_path_for_scsi(&serial);
let index = if let Some(index) = devices
.lock()
.await
.iter()
.position(|dev| dev.dbus_path == path)
{
index
} else {
if dev_prop_matches(&event.device(), "ID_VENDOR_ID", "0b05")
{
warn!("No device for dbus path: {path:?}");
}
return Ok(());
};
info!("removing: {path:?}");
let dev = devices.lock().await.remove(index);
let path = path.clone();
if let DeviceHandle::Scsi(_) = dev.device {
conn_copy
.object_server()
.remove::<ScsiZbus, _>(&path)
.await?;
}
}
} else if action == "add" {
let evdev = event.device();
if let Some(serial) = evdev.property_value("ID_SERIAL_SHORT") {
let serial = serial.to_string_lossy().to_string();
let path = dbus_path_for_scsi(&serial);
if let Some(new_devs) =
Self::init_scsi(&conn_copy, &evdev, path).await
{
devices.lock().await.append(&mut vec![new_devs]);
}
}
};
}
if subsys == "hidraw" {
if let Some(parent) =
event.parent_with_subsystem_devtype("usb", "usb_device")?
{
if action == "remove" {
if let Some(path) = dbus_path_for_dev(&parent) {
// Find the indexs of devices matching the path
let removals: Vec<usize> = devices
.lock()
.await
.iter()
.enumerate()
.filter_map(|(i, dev)| {
if dev.dbus_path == path {
Some(i)
} else {
None
}
})
.collect();
if removals.is_empty() {
return Ok(());
}
info!("removing: {path:?}");
// Iter in reverse so as to not screw up indexing
for index in removals.iter().rev() {
let dev = devices.lock().await.remove(*index);
let path = path.clone();
let res = match dev.device {
DeviceHandle::Aura(_) => {
conn_copy
.object_server()
.remove::<AuraZbus, _>(&path)
.await?
}
DeviceHandle::Slash(_) => {
conn_copy
.object_server()
.remove::<SlashZbus, _>(&path)
.await?
}
DeviceHandle::AniMe(_) => {
conn_copy
.object_server()
.remove::<AniMeZbus, _>(&path)
.await?
}
DeviceHandle::Scsi(_) => {
conn_copy
.object_server()
.remove::<ScsiZbus, _>(&path)
.await?
}
_ => todo!(),
};
info!("AuraManager removed: {path:?}, {res}");
}
}
} else if action == "add" {
let evdev = event.device();
if let Ok(mut new_devs) =
Self::init_hid_devices(&conn_copy, evdev)
.await
.map_err(|e| error!("Couldn't add new device: {e:?}"))
{
devices.lock().await.append(&mut new_devs);
}
};
}
}
Ok::<(), RogError>(())
})
.map_err(|e| error!("{e:?}"))
.ok();
}
}
// Required for return type on spawn
#[allow(unreachable_code)]
Ok::<(), RogError>(())
});
Ok(manager)
}
}
-114
View File
@@ -1,114 +0,0 @@
use std::collections::BTreeMap;
use config_traits::{StdConfig, StdConfigLoad};
use rog_aura::AuraDeviceType;
use rog_scsi::{AuraEffect, AuraMode};
use serde::{Deserialize, Serialize};
const CONFIG_FILE: &str = "scsi.ron";
/// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug)]
pub struct ScsiConfig {
#[serde(skip)]
pub dev_type: AuraDeviceType,
pub enabled: bool,
pub current_mode: AuraMode,
pub modes: BTreeMap<AuraMode, AuraEffect>,
}
impl ScsiConfig {
pub fn get_effect(&mut self, mode: AuraMode) -> Option<&AuraEffect> {
self.modes.get(&mode)
}
pub fn save_effect(&mut self, effect: AuraEffect) {
self.current_mode = effect.mode;
self.modes.insert(*effect.mode(), effect);
}
}
impl Default for ScsiConfig {
fn default() -> Self {
ScsiConfig {
enabled: true,
current_mode: AuraMode::Static,
dev_type: AuraDeviceType::ScsiExtDisk,
modes: BTreeMap::from([
(AuraMode::Off, AuraEffect::default_with_mode(AuraMode::Off)),
(
AuraMode::Static,
AuraEffect::default_with_mode(AuraMode::Static),
),
(
AuraMode::Breathe,
AuraEffect::default_with_mode(AuraMode::Breathe),
),
(
AuraMode::Flashing,
AuraEffect::default_with_mode(AuraMode::Flashing),
),
(
AuraMode::RainbowCycle,
AuraEffect::default_with_mode(AuraMode::RainbowCycle),
),
(
AuraMode::RainbowWave,
AuraEffect::default_with_mode(AuraMode::RainbowWave),
),
(
AuraMode::RainbowCycleBreathe,
AuraEffect::default_with_mode(AuraMode::RainbowCycleBreathe),
),
(
AuraMode::ChaseFade,
AuraEffect::default_with_mode(AuraMode::ChaseFade),
),
(
AuraMode::RainbowCycleChaseFade,
AuraEffect::default_with_mode(AuraMode::RainbowCycleChaseFade),
),
(
AuraMode::Chase,
AuraEffect::default_with_mode(AuraMode::Chase),
),
(
AuraMode::RainbowCycleChase,
AuraEffect::default_with_mode(AuraMode::RainbowCycleChase),
),
(
AuraMode::RainbowCycleWave,
AuraEffect::default_with_mode(AuraMode::RainbowCycleWave),
),
(
AuraMode::RainbowPulseChase,
AuraEffect::default_with_mode(AuraMode::RainbowPulseChase),
),
(
AuraMode::RandomFlicker,
AuraEffect::default_with_mode(AuraMode::RandomFlicker),
),
(
AuraMode::DoubleFade,
AuraEffect::default_with_mode(AuraMode::DoubleFade),
),
]),
}
}
}
impl StdConfig for ScsiConfig {
fn new() -> Self {
Self::default()
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
fn config_dir() -> std::path::PathBuf {
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
}
}
impl StdConfigLoad for ScsiConfig {}
-45
View File
@@ -1,45 +0,0 @@
use std::sync::Arc;
use config::ScsiConfig;
use futures_util::lock::{Mutex, MutexGuard};
use rog_scsi::{AuraEffect, Device, Task};
use crate::error::RogError;
pub mod config;
pub mod trait_impls;
#[derive(Clone)]
pub struct ScsiAura {
device: Arc<Mutex<Device>>,
config: Arc<Mutex<ScsiConfig>>,
}
impl ScsiAura {
pub fn new(device: Arc<Mutex<Device>>, config: Arc<Mutex<ScsiConfig>>) -> Self {
Self { device, config }
}
pub async fn lock_config(&self) -> MutexGuard<ScsiConfig> {
self.config.lock().await
}
pub async fn write_effect(&self, effect: &AuraEffect) -> Result<(), RogError> {
let tasks: Vec<Task> = effect.into();
for task in &tasks {
self.device.lock().await.perform(task).ok();
}
Ok(())
}
/// Initialise the device if required. Locks the internal config so be wary
/// of deadlocks.
pub async fn do_initialization(&self) -> Result<(), RogError> {
let config = self.config.lock().await;
let mode = config.current_mode;
if let Some(effect) = config.modes.get(&mode) {
self.write_effect(effect).await?;
}
Ok(())
}
}
-116
View File
@@ -1,116 +0,0 @@
use std::collections::BTreeMap;
use config_traits::StdConfig;
use log::error;
use rog_aura::AuraDeviceType;
use rog_scsi::{AuraEffect, AuraMode};
use zbus::fdo::Error as ZbErr;
use zbus::zvariant::OwnedObjectPath;
use zbus::{interface, Connection};
use super::ScsiAura;
use crate::error::RogError;
#[derive(Clone)]
pub struct ScsiZbus(ScsiAura);
impl ScsiZbus {
pub fn new(scsi: ScsiAura) -> Self {
Self(scsi)
}
pub async fn start_tasks(
self,
connection: &Connection,
path: OwnedObjectPath,
) -> Result<(), RogError> {
connection
.object_server()
.at(path.clone(), self)
.await
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
.ok();
Ok(())
}
}
#[interface(name = "xyz.ljones.ScsiAura")]
impl ScsiZbus {
/// Return the device type for this Aura keyboard
#[zbus(property)]
async fn device_type(&self) -> AuraDeviceType {
self.0.config.lock().await.dev_type
}
/// Get enabled or not
#[zbus(property)]
async fn enabled(&self) -> bool {
let lock = self.0.lock_config().await;
lock.enabled
}
/// Set enabled true or false
#[zbus(property)]
async fn set_enabled(&self, enabled: bool) {
let mut config = self.0.lock_config().await;
config.enabled = enabled;
config.write();
}
#[zbus(property)]
async fn led_mode(&self) -> u8 {
let config = self.0.lock_config().await;
config.current_mode as u8
}
#[zbus(property)]
async fn set_led_mode(&self, mode: AuraMode) -> Result<(), zbus::Error> {
let mut config = self.0.lock_config().await;
if let Some(effect) = config.get_effect(mode) {
self.0
.write_effect(effect)
.await
.map_err(|e| zbus::Error::Failure(format!("{e:?}")))?;
} else {
return Err(zbus::Error::Failure("Mode data does not exist".to_string()));
}
config.current_mode = mode;
config.write();
Ok(())
}
/// The current mode data
#[zbus(property)]
async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> {
// entirely possible to deadlock here, so use try instead of lock()
if let Some(config) = self.0.config.try_lock() {
let mode = config.current_mode;
match config.modes.get(&mode) {
Some(effect) => Ok(effect.clone()),
None => Err(ZbErr::Failed("Could not get the current effect".into())),
}
} else {
Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
}
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode_data(&mut self, effect: AuraEffect) -> Result<(), ZbErr> {
self.0.write_effect(&effect).await?;
let mut config = self.0.config.lock().await;
config.save_effect(effect);
config.write();
Ok(())
}
/// Get the data set for every mode available
async fn all_mode_data(&self) -> BTreeMap<AuraMode, AuraEffect> {
let config = self.0.config.lock().await;
config.modes.clone()
}
}
-64
View File
@@ -1,64 +0,0 @@
use config_traits::{StdConfig, StdConfigLoad};
use rog_slash::{DeviceState, SlashMode, SlashType};
use serde::{Deserialize, Serialize};
const CONFIG_FILE: &str = "slash.ron";
/// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug)]
pub struct SlashConfig {
#[serde(skip)]
pub slash_type: SlashType,
pub enabled: bool,
pub brightness: u8,
pub display_interval: u8,
pub display_mode: SlashMode,
pub show_on_boot: bool,
pub show_on_shutdown: bool,
pub show_on_sleep: bool,
pub show_on_battery: bool,
pub show_battery_warning: bool,
}
impl Default for SlashConfig {
fn default() -> Self {
SlashConfig {
enabled: true,
brightness: 255,
display_interval: 0,
display_mode: SlashMode::Bounce,
slash_type: SlashType::Unsupported,
show_on_boot: true,
show_on_shutdown: true,
show_on_sleep: true,
show_on_battery: true,
show_battery_warning: true,
}
}
}
impl StdConfig for SlashConfig {
fn new() -> Self {
Self::default()
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
fn config_dir() -> std::path::PathBuf {
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
}
}
impl StdConfigLoad for SlashConfig {}
impl From<&SlashConfig> for DeviceState {
fn from(config: &SlashConfig) -> Self {
DeviceState {
slash_enabled: config.enabled,
slash_brightness: config.brightness,
slash_interval: config.display_interval,
slash_mode: config.display_mode,
}
}
}
-70
View File
@@ -1,70 +0,0 @@
use std::sync::Arc;
use config::SlashConfig;
use futures_util::lock::{Mutex, MutexGuard};
use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw;
use rog_slash::usb::{get_options_packet, pkt_set_mode, pkts_for_init};
use rog_slash::SlashType;
use crate::error::RogError;
pub mod config;
pub mod trait_impls;
#[derive(Debug, Clone)]
pub struct Slash {
hid: Option<Arc<Mutex<HidRaw>>>,
usb: Option<Arc<Mutex<USBRaw>>>,
config: Arc<Mutex<SlashConfig>>,
}
impl Slash {
pub fn new(
hid: Option<Arc<Mutex<HidRaw>>>,
usb: Option<Arc<Mutex<USBRaw>>>,
config: Arc<Mutex<SlashConfig>>,
) -> Self {
Self { hid, usb, config }
}
pub async fn lock_config(&self) -> MutexGuard<SlashConfig> {
self.config.lock().await
}
pub async fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
if let Some(hid) = &self.hid {
hid.lock().await.write_bytes(message)?;
} else if let Some(usb) = &self.usb {
usb.lock().await.write_bytes(message)?;
}
Ok(())
}
/// Initialise the device if required. Locks the internal config so be wary
/// of deadlocks.
pub async fn do_initialization(&self) -> Result<(), RogError> {
// Don't try to initialise these models as the asus drivers already did
let config = self.config.lock().await;
if !matches!(config.slash_type, SlashType::GA605 | SlashType::GU605) {
for pkt in &pkts_for_init(config.slash_type) {
self.write_bytes(pkt).await?;
}
}
// Apply config upon initialization
let option_packets = get_options_packet(
config.slash_type,
config.enabled,
config.brightness,
config.display_interval,
);
self.write_bytes(&option_packets).await?;
let mode_packets = pkt_set_mode(config.slash_type, config.display_mode);
// self.node.write_bytes(&mode_packets[0])?;
self.write_bytes(&mode_packets[1]).await?;
Ok(())
}
}
-287
View File
@@ -1,287 +0,0 @@
use config_traits::StdConfig;
use log::{debug, error, warn};
use rog_slash::usb::{
get_battery_saver_packet, get_boot_packet, get_low_battery_packet, get_options_packet,
get_shutdown_packet, get_sleep_packet, pkt_save, pkt_set_mode,
};
use rog_slash::{DeviceState, SlashMode};
use zbus::zvariant::OwnedObjectPath;
use zbus::{interface, Connection};
use super::Slash;
use crate::error::RogError;
use crate::Reloadable;
#[derive(Clone)]
pub struct SlashZbus(Slash);
impl SlashZbus {
pub fn new(slash: Slash) -> Self {
Self(slash)
}
pub async fn start_tasks(
mut self,
connection: &Connection,
path: OwnedObjectPath,
) -> Result<(), RogError> {
// let task = zbus.clone();
self.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
connection
.object_server()
.at(path.clone(), self)
.await
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
.ok();
Ok(())
}
}
#[interface(name = "xyz.ljones.Slash")]
impl SlashZbus {
/// Get enabled or not
#[zbus(property)]
async fn enabled(&self) -> bool {
let lock = self.0.lock_config().await;
lock.enabled
}
/// Set enabled true or false
#[zbus(property)]
async fn set_enabled(&self, enabled: bool) {
let mut config = self.0.lock_config().await;
let brightness = if enabled && config.brightness == 0 {
0x88
} else {
config.brightness
};
self.0
.write_bytes(&get_options_packet(
config.slash_type,
enabled,
brightness,
config.display_interval,
))
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
config.enabled = enabled;
config.brightness = brightness;
config.write();
}
/// Get brightness level
#[zbus(property)]
async fn brightness(&self) -> u8 {
let config = self.0.lock_config().await;
config.brightness
}
/// Set brightness level
#[zbus(property)]
async fn set_brightness(&self, brightness: u8) {
let mut config = self.0.lock_config().await;
let enabled = brightness > 0;
self.0
.write_bytes(&get_options_packet(
config.slash_type,
enabled,
brightness,
config.display_interval,
))
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
config.enabled = enabled;
config.brightness = brightness;
config.write();
}
#[zbus(property)]
async fn interval(&self) -> u8 {
let config = self.0.lock_config().await;
config.display_interval
}
/// Set interval between slash animations (0-255)
#[zbus(property)]
async fn set_interval(&self, interval: u8) {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&get_options_packet(
config.slash_type, config.enabled, config.brightness, interval,
))
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
config.display_interval = interval;
config.write();
}
#[zbus(property)]
async fn mode(&self) -> zbus::fdo::Result<u8> {
let config = self.0.lock_config().await;
Ok(config.display_interval)
}
/// Set interval between slash animations (0-255)
#[zbus(property)]
async fn set_mode(&self, mode: SlashMode) -> zbus::Result<()> {
let mut config = self.0.lock_config().await;
let command_packets = pkt_set_mode(config.slash_type, mode);
// self.node.write_bytes(&command_packets[0])?;
self.0.write_bytes(&command_packets[1]).await?;
self.0.write_bytes(&pkt_save(config.slash_type)).await?;
config.display_mode = mode;
config.write();
Ok(())
}
/// Get the device state as stored by asusd
// #[zbus(property)]
async fn device_state(&self) -> DeviceState {
let config = self.0.lock_config().await;
DeviceState::from(&*config)
}
#[zbus(property)]
async fn show_on_boot(&self) -> zbus::fdo::Result<bool> {
let config = self.0.lock_config().await;
Ok(config.show_on_boot)
}
#[zbus(property)]
async fn set_show_on_boot(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&get_boot_packet(config.slash_type, enable))
.await?;
config.show_on_boot = enable;
config.write();
Ok(())
}
#[zbus(property)]
async fn show_on_sleep(&self) -> zbus::fdo::Result<bool> {
let config = self.0.lock_config().await;
Ok(config.show_on_sleep)
}
#[zbus(property)]
async fn set_show_on_sleep(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&get_sleep_packet(config.slash_type, enable))
.await?;
config.show_on_sleep = enable;
config.write();
Ok(())
}
#[zbus(property)]
async fn show_on_shutdown(&self) -> zbus::fdo::Result<bool> {
let config = self.0.lock_config().await;
Ok(config.show_on_shutdown)
}
#[zbus(property)]
async fn set_show_on_shutdown(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&get_shutdown_packet(config.slash_type, enable))
.await?;
config.show_on_shutdown = enable;
config.write();
Ok(())
}
#[zbus(property)]
async fn show_on_battery(&self) -> zbus::fdo::Result<bool> {
let config = self.0.lock_config().await;
Ok(config.show_on_battery)
}
#[zbus(property)]
async fn set_show_on_battery(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&get_battery_saver_packet(config.slash_type, enable))
.await?;
config.show_on_battery = enable;
config.write();
Ok(())
}
#[zbus(property)]
async fn show_battery_warning(&self) -> zbus::fdo::Result<bool> {
let config = self.0.lock_config().await;
Ok(config.show_battery_warning)
}
#[zbus(property)]
async fn set_show_battery_warning(&self, enable: bool) -> zbus::Result<()> {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&get_low_battery_packet(config.slash_type, enable))
.await?;
config.show_battery_warning = enable;
config.write();
Ok(())
}
}
impl Reloadable for SlashZbus {
async fn reload(&mut self) -> Result<(), RogError> {
debug!("reloading slash settings");
let config = self.0.lock_config().await;
self.0
.write_bytes(&get_options_packet(
config.slash_type,
config.enabled,
config.brightness,
config.display_interval,
))
.await
.map_err(|err| {
warn!("set_options {}", err);
})
.ok();
macro_rules! write_bytes_with_warning {
($packet_fn:expr, $cfg:ident, $warn_msg:expr) => {
self.0
.write_bytes(&$packet_fn(config.slash_type, config.$cfg))
.await
.map_err(|err| {
warn!("{} {}", $warn_msg, err);
})
.ok();
};
}
write_bytes_with_warning!(get_boot_packet, show_on_boot, "show_on_boot");
write_bytes_with_warning!(get_sleep_packet, show_on_sleep, "show_on_sleep");
write_bytes_with_warning!(get_shutdown_packet, show_on_shutdown, "show_on_shutdown");
write_bytes_with_warning!(get_battery_saver_packet, show_on_battery, "show_on_battery");
write_bytes_with_warning!(
get_low_battery_packet,
show_battery_warning,
"show_battery_warning"
);
Ok(())
}
}
-209
View File
@@ -1,209 +0,0 @@
use std::sync::Arc;
use config_traits::{StdConfig, StdConfigLoad};
use futures_util::lock::Mutex;
use log::{debug, error, info};
use rog_anime::error::AnimeError;
use rog_anime::usb::get_anime_type;
use rog_anime::AnimeType;
use rog_aura::AuraDeviceType;
use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardBacklight;
use rog_platform::usb_raw::USBRaw;
use rog_scsi::{open_device, ScsiType};
use rog_slash::error::SlashError;
use rog_slash::SlashType;
use crate::aura_anime::config::AniMeConfig;
use crate::aura_anime::AniMe;
use crate::aura_laptop::config::AuraConfig;
use crate::aura_laptop::Aura;
use crate::aura_scsi::config::ScsiConfig;
use crate::aura_scsi::ScsiAura;
use crate::aura_slash::config::SlashConfig;
use crate::aura_slash::Slash;
use crate::error::RogError;
pub enum _DeviceHandle {
/// The AniMe devices require USBRaw as they are not HID devices
Usb(USBRaw),
HidRaw(HidRaw),
LedClass(KeyboardBacklight),
/// TODO
MulticolourLed,
None,
}
#[derive(Clone)]
pub enum DeviceHandle {
Aura(Aura),
Slash(Slash),
/// The AniMe devices require USBRaw as they are not HID devices
AniMe(AniMe),
Scsi(ScsiAura),
Ally(Arc<Mutex<HidRaw>>),
OldAura(Arc<Mutex<HidRaw>>),
/// TUF laptops have an aditional set of attributes added to the LED /sysfs/
TufLedClass(Arc<Mutex<HidRaw>>),
/// TODO
MulticolourLed,
None,
}
impl DeviceHandle {
/// Try Slash HID. If one exists it is initialsed and returned.
pub async fn new_slash_hid(
device: Arc<Mutex<HidRaw>>,
prod_id: &str,
) -> Result<Self, RogError> {
debug!("Testing for HIDRAW Slash");
let slash_type = SlashType::from_dmi();
if matches!(slash_type, SlashType::Unsupported)
|| slash_type
.prod_id_str()
.to_lowercase()
.trim_start_matches("0x")
!= prod_id
{
log::info!("Unknown or invalid slash: {prod_id:?}, skipping");
return Err(RogError::NotFound("No slash device".to_string()));
}
info!("Found slash type {slash_type:?}: {prod_id}");
let mut config = SlashConfig::new().load();
config.slash_type = slash_type;
let slash = Slash::new(Some(device), None, Arc::new(Mutex::new(config)));
slash.do_initialization().await?;
Ok(Self::Slash(slash))
}
/// Try Slash USB. If one exists it is initialsed and returned.
pub async fn new_slash_usb() -> Result<Self, RogError> {
debug!("Testing for USB Slash");
let slash_type = SlashType::from_dmi();
if matches!(slash_type, SlashType::Unsupported) {
return Err(RogError::Slash(SlashError::NoDevice));
}
if let Ok(usb) = USBRaw::new(slash_type.prod_id()) {
info!("Found Slash USB {slash_type:?}");
let mut config = SlashConfig::new().load();
config.slash_type = slash_type;
let slash = Slash::new(
None,
Some(Arc::new(Mutex::new(usb))),
Arc::new(Mutex::new(config)),
);
slash.do_initialization().await?;
Ok(Self::Slash(slash))
} else {
Err(RogError::NotFound("No slash device found".to_string()))
}
}
/// Try AniMe Matrix HID. If one exists it is initialsed and returned.
pub async fn maybe_anime_hid(
_device: Arc<Mutex<HidRaw>>,
_prod_id: &str,
) -> Result<Self, RogError> {
// TODO: can't use HIDRAW for anime at the moment
Err(RogError::NotFound(
"Can't use anime over hidraw yet. Skip.".to_string(),
))
// debug!("Testing for HIDRAW AniMe");
// let anime_type = AnimeType::from_dmi();
// dbg!(prod_id);
// if matches!(anime_type, AnimeType::Unsupported) || prod_id != "193b"
// { log::info!("Unknown or invalid AniMe: {prod_id:?},
// skipping"); return Err(RogError::NotFound("No
// anime-matrix device".to_string())); }
// info!("Found AniMe Matrix HIDRAW {anime_type:?}: {prod_id}");
// let mut config = AniMeConfig::new().load();
// config.anime_type = anime_type;
// let mut anime = AniMe::new(Some(device), None,
// Arc::new(Mutex::new(config))); anime.do_initialization().
// await?; Ok(Self::AniMe(anime))
}
pub async fn maybe_anime_usb() -> Result<Self, RogError> {
debug!("Testing for USB AniMe");
let anime_type = get_anime_type();
if matches!(anime_type, AnimeType::Unsupported) {
info!("No Anime Matrix capable laptop found");
return Err(RogError::Anime(AnimeError::NoDevice));
}
if let Ok(usb) = USBRaw::new(0x193b) {
info!("Found AniMe Matrix USB {anime_type:?}");
let mut config = AniMeConfig::new().load();
config.anime_type = anime_type;
let mut anime = AniMe::new(
None,
Some(Arc::new(Mutex::new(usb))),
Arc::new(Mutex::new(config)),
);
anime.do_initialization().await?;
Ok(Self::AniMe(anime))
} else {
Err(RogError::NotFound(
"No AnimeMatrix device found".to_string(),
))
}
}
pub async fn maybe_scsi(dev_node: &str, prod_id: &str) -> Result<Self, RogError> {
debug!("Testing for SCSI");
let prod_id = ScsiType::from(prod_id);
if prod_id == ScsiType::Unsupported {
log::info!("Unknown or invalid SCSI: {prod_id:?}, skipping");
return Err(RogError::NotFound("No SCSI device".to_string()));
}
info!("Found SCSI device {prod_id:?} on {dev_node}");
let mut config = ScsiConfig::new().load();
config.dev_type = AuraDeviceType::ScsiExtDisk;
let dev = Arc::new(Mutex::new(open_device(dev_node)?));
let scsi = ScsiAura::new(dev, Arc::new(Mutex::new(config)));
scsi.do_initialization().await?;
Ok(Self::Scsi(scsi))
}
pub async fn maybe_laptop_aura(
device: Option<Arc<Mutex<HidRaw>>>,
prod_id: &str,
) -> Result<Self, RogError> {
debug!("Testing for laptop aura");
let aura_type = AuraDeviceType::from(prod_id);
if !matches!(
aura_type,
AuraDeviceType::LaptopKeyboard2021
| AuraDeviceType::LaptopKeyboardPre2021
| AuraDeviceType::LaptopKeyboardTuf
) {
log::info!("Unknown or invalid laptop aura: {prod_id:?}, skipping");
return Err(RogError::NotFound("No laptop aura device".to_string()));
}
info!("Found laptop aura type {prod_id:?}");
let backlight = KeyboardBacklight::new()
.map_err(|e| error!("Keyboard backlight error: {e:?}"))
.map_or(None, |k| {
info!("Found sysfs backlight control");
Some(Arc::new(Mutex::new(k)))
});
let mut config = AuraConfig::load_and_update_config(prod_id);
config.led_type = aura_type;
let aura = Aura {
hid: device,
backlight,
config: Arc::new(Mutex::new(config)),
};
aura.do_initialization().await?;
Ok(Self::Aura(aura))
}
}
+161 -102
View File
@@ -1,87 +1,84 @@
use std::collections::HashMap;
use config_traits::{StdConfig, StdConfigLoad1};
use rog_platform::asus_armoury::FirmwareAttribute;
use config_traits::{StdConfig, StdConfigLoad3};
use rog_platform::cpu::CPUEPP;
use rog_platform::platform::PlatformProfile;
use serde::{Deserialize, Serialize};
use rog_platform::platform::ThrottlePolicy;
use serde_derive::{Deserialize, Serialize};
const CONFIG_FILE: &str = "asusd.ron";
#[derive(Default, Clone, Deserialize, Serialize, PartialEq)]
pub struct Tuning {
pub enabled: bool,
pub group: HashMap<FirmwareAttribute, i32>,
}
type Tunings = HashMap<PlatformProfile, Tuning>;
#[derive(Deserialize, Serialize, PartialEq)]
#[derive(Deserialize, Serialize, Debug, PartialEq, PartialOrd)]
pub struct Config {
// The current charge limit applied
/// Save charge limit for restoring on boot/resume
pub charge_control_end_threshold: u8,
/// Save charge limit for restoring
#[serde(skip)]
pub base_charge_control_end_threshold: u8,
pub panel_od: bool,
pub boot_sound: bool,
pub mini_led_mode: bool,
pub disable_nvidia_powerd_on_battery: bool,
/// An optional command/script to run when power is changed to AC
pub ac_command: String,
/// An optional command/script to run when power is changed to battery
pub bat_command: String,
/// Set true if energy_performance_preference should be set if the
/// platform profile is changed
pub platform_profile_linked_epp: bool,
/// Which platform profile to use on battery power
pub platform_profile_on_battery: PlatformProfile,
/// Should the throttle policy be set on bat/ac change?
pub change_platform_profile_on_battery: bool,
/// Which platform profile to use on AC power
pub platform_profile_on_ac: PlatformProfile,
/// Should the platform profile be set on bat/ac change?
pub change_platform_profile_on_ac: bool,
/// The energy_performance_preference for this platform profile
pub profile_quiet_epp: CPUEPP,
/// The energy_performance_preference for this platform profile
pub profile_balanced_epp: CPUEPP,
/// The energy_performance_preference for this platform profile
pub profile_performance_epp: CPUEPP,
pub ac_profile_tunings: Tunings,
pub dc_profile_tunings: Tunings,
pub armoury_settings: HashMap<FirmwareAttribute, i32>,
/// throttle/platform profile is changed
pub throttle_policy_linked_epp: bool,
/// Which throttle/profile to use on battery power
pub throttle_policy_on_battery: ThrottlePolicy,
/// Which throttle/profile to use on AC power
pub throttle_policy_on_ac: ThrottlePolicy,
/// The energy_performance_preference for this throttle/platform profile
pub throttle_quiet_epp: CPUEPP,
/// The energy_performance_preference for this throttle/platform profile
pub throttle_balanced_epp: CPUEPP,
/// The energy_performance_preference for this throttle/platform profile
pub throttle_performance_epp: CPUEPP,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_pl1_spl: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_pl2_sppt: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_fppt: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_apu_sppt: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_platform_sppt: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_dynamic_boost: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_temp_target: Option<u8>,
/// Temporary state for AC/Batt
#[serde(skip)]
pub last_power_plugged: u8,
}
impl Config {
pub fn select_tunings(&mut self, power_plugged: bool, profile: PlatformProfile) -> &mut Tuning {
let config = if power_plugged {
&mut self.ac_profile_tunings
} else {
&mut self.dc_profile_tunings
};
config.entry(profile).or_insert_with(Tuning::default)
}
}
impl Default for Config {
fn default() -> Self {
Self {
charge_control_end_threshold: 100,
base_charge_control_end_threshold: 100,
panel_od: false,
boot_sound: false,
mini_led_mode: false,
disable_nvidia_powerd_on_battery: true,
ac_command: Default::default(),
bat_command: Default::default(),
platform_profile_linked_epp: true,
platform_profile_on_battery: PlatformProfile::Quiet,
change_platform_profile_on_battery: true,
platform_profile_on_ac: PlatformProfile::Performance,
change_platform_profile_on_ac: true,
profile_quiet_epp: CPUEPP::Power,
profile_balanced_epp: CPUEPP::BalancePower,
profile_performance_epp: CPUEPP::Performance,
ac_profile_tunings: HashMap::default(),
dc_profile_tunings: HashMap::default(),
armoury_settings: HashMap::default(),
throttle_policy_linked_epp: true,
throttle_policy_on_battery: ThrottlePolicy::Quiet,
throttle_policy_on_ac: ThrottlePolicy::Performance,
throttle_quiet_epp: CPUEPP::Power,
throttle_balanced_epp: CPUEPP::BalancePower,
throttle_performance_epp: CPUEPP::Performance,
ppt_pl1_spl: Default::default(),
ppt_pl2_sppt: Default::default(),
ppt_fppt: Default::default(),
ppt_apu_sppt: Default::default(),
ppt_platform_sppt: Default::default(),
nv_dynamic_boost: Default::default(),
nv_temp_target: Default::default(),
last_power_plugged: Default::default(),
}
}
@@ -92,8 +89,8 @@ impl StdConfig for Config {
Config {
charge_control_end_threshold: 100,
disable_nvidia_powerd_on_battery: true,
platform_profile_on_battery: PlatformProfile::Quiet,
platform_profile_on_ac: PlatformProfile::Performance,
throttle_policy_on_battery: ThrottlePolicy::Quiet,
throttle_policy_on_ac: ThrottlePolicy::Performance,
ac_command: String::new(),
bat_command: String::new(),
..Default::default()
@@ -109,68 +106,130 @@ impl StdConfig for Config {
}
}
impl StdConfigLoad1<Config601> for Config {}
impl StdConfigLoad3<Config472, Config506, Config507> for Config {}
#[derive(Deserialize, Serialize)]
pub struct Config601 {
pub struct Config507 {
/// Save charge limit for restoring on boot
pub charge_control_end_threshold: u8,
#[serde(skip)]
pub base_charge_control_end_threshold: u8,
pub panel_od: bool,
pub boot_sound: bool,
pub mini_led_mode: bool,
pub disable_nvidia_powerd_on_battery: bool,
pub ac_command: String,
pub bat_command: String,
pub platform_profile_linked_epp: bool,
pub platform_profile_on_battery: PlatformProfile,
pub change_platform_profile_on_battery: bool,
pub platform_profile_on_ac: PlatformProfile,
pub change_platform_profile_on_ac: bool,
pub profile_quiet_epp: CPUEPP,
pub profile_balanced_epp: CPUEPP,
pub profile_performance_epp: CPUEPP,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub platform_policy_linked_epp: bool,
pub platform_policy_on_battery: ThrottlePolicy,
pub platform_policy_on_ac: ThrottlePolicy,
//
pub ppt_pl1_spl: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_pl2_sppt: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_pl3_fppt: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_fppt: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_apu_sppt: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_platform_sppt: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_dynamic_boost: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_temp_target: Option<u8>,
#[serde(skip)]
pub last_power_plugged: u8,
}
impl From<Config601> for Config {
fn from(c: Config601) -> Self {
impl From<Config507> for Config {
fn from(c: Config507) -> Self {
Self {
// Restore the base charge limit
charge_control_end_threshold: c.charge_control_end_threshold,
base_charge_control_end_threshold: c.charge_control_end_threshold,
panel_od: c.panel_od,
boot_sound: false,
disable_nvidia_powerd_on_battery: c.disable_nvidia_powerd_on_battery,
ac_command: c.ac_command,
bat_command: c.bat_command,
platform_profile_linked_epp: c.platform_profile_linked_epp,
platform_profile_on_battery: c.platform_profile_on_battery,
change_platform_profile_on_battery: c.change_platform_profile_on_battery,
platform_profile_on_ac: c.platform_profile_on_ac,
change_platform_profile_on_ac: c.change_platform_profile_on_ac,
profile_quiet_epp: c.profile_quiet_epp,
profile_balanced_epp: c.profile_balanced_epp,
profile_performance_epp: c.profile_performance_epp,
last_power_plugged: c.last_power_plugged,
ac_profile_tunings: HashMap::default(),
dc_profile_tunings: HashMap::default(),
armoury_settings: HashMap::default(),
mini_led_mode: c.mini_led_mode,
throttle_policy_linked_epp: true,
throttle_policy_on_battery: c.platform_policy_on_battery,
throttle_policy_on_ac: c.platform_policy_on_ac,
throttle_quiet_epp: CPUEPP::Power,
throttle_balanced_epp: CPUEPP::BalancePower,
throttle_performance_epp: CPUEPP::Performance,
ppt_pl1_spl: c.ppt_pl1_spl,
ppt_pl2_sppt: c.ppt_pl2_sppt,
ppt_fppt: c.ppt_fppt,
ppt_apu_sppt: c.ppt_apu_sppt,
ppt_platform_sppt: c.ppt_platform_sppt,
nv_dynamic_boost: c.nv_dynamic_boost,
nv_temp_target: c.nv_temp_target,
last_power_plugged: 0,
}
}
}
#[derive(Deserialize, Serialize)]
pub struct Config506 {
/// Save charge limit for restoring on boot
pub charge_control_end_threshold: u8,
pub panel_od: bool,
pub mini_led_mode: bool,
pub disable_nvidia_powerd_on_battery: bool,
pub ac_command: String,
pub bat_command: String,
/// Restored on boot as well as when power is plugged
#[serde(skip)]
pub platform_policy_to_restore: ThrottlePolicy,
pub platform_policy_on_battery: ThrottlePolicy,
pub platform_policy_on_ac: ThrottlePolicy,
//
pub ppt_pl1_spl: Option<u8>,
pub ppt_pl2_sppt: Option<u8>,
pub ppt_fppt: Option<u8>,
pub ppt_apu_sppt: Option<u8>,
pub ppt_platform_sppt: Option<u8>,
pub nv_dynamic_boost: Option<u8>,
pub nv_temp_target: Option<u8>,
}
impl From<Config506> for Config {
fn from(c: Config506) -> Self {
Self {
charge_control_end_threshold: c.charge_control_end_threshold,
panel_od: c.panel_od,
boot_sound: false,
disable_nvidia_powerd_on_battery: c.disable_nvidia_powerd_on_battery,
ac_command: c.ac_command,
bat_command: c.bat_command,
mini_led_mode: c.mini_led_mode,
throttle_policy_linked_epp: true,
throttle_policy_on_battery: c.platform_policy_on_battery,
throttle_policy_on_ac: c.platform_policy_on_ac,
throttle_quiet_epp: CPUEPP::Power,
throttle_balanced_epp: CPUEPP::BalancePower,
throttle_performance_epp: CPUEPP::Performance,
ppt_pl1_spl: c.ppt_pl1_spl,
ppt_pl2_sppt: c.ppt_pl2_sppt,
ppt_fppt: c.ppt_fppt,
ppt_apu_sppt: c.ppt_apu_sppt,
ppt_platform_sppt: c.ppt_platform_sppt,
nv_dynamic_boost: c.nv_dynamic_boost,
nv_temp_target: c.nv_temp_target,
last_power_plugged: 0,
}
}
}
#[derive(Deserialize, Serialize)]
pub struct Config472 {
/// Save charge limit for restoring on boot
pub bat_charge_limit: u8,
pub panel_od: bool,
pub mini_led_mode: bool,
pub disable_nvidia_powerd_on_battery: bool,
pub ac_command: String,
pub bat_command: String,
}
impl From<Config472> for Config {
fn from(c: Config472) -> Self {
Self {
charge_control_end_threshold: c.bat_charge_limit,
panel_od: c.panel_od,
disable_nvidia_powerd_on_battery: true,
ac_command: c.ac_command,
bat_command: c.bat_command,
..Default::default()
}
}
}
@@ -1,27 +1,81 @@
use std::time::Duration;
use config_traits::{StdConfig, StdConfigLoad};
use config_traits::{StdConfig, StdConfigLoad2};
use rog_anime::error::AnimeError;
use rog_anime::usb::Brightness;
use rog_anime::{
ActionData, ActionLoader, AnimTime, Animations, AnimeType, DeviceState, Fade, Vec2,
};
use serde::{Deserialize, Serialize};
use serde_derive::{Deserialize, Serialize};
const CONFIG_FILE: &str = "anime.ron";
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
pub struct AniMeConfigCached {
#[derive(Deserialize, Serialize)]
pub struct AnimeConfigV460 {
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
pub sleep: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>,
pub brightness: f32,
}
impl From<AnimeConfigV460> for AnimeConfig {
fn from(c: AnimeConfigV460) -> AnimeConfig {
AnimeConfig {
system: c.system,
boot: c.boot,
wake: c.wake,
shutdown: c.shutdown,
..Default::default()
}
}
}
#[derive(Deserialize, Serialize, Debug)]
pub struct AnimeConfigV472 {
pub model_override: Option<AnimeType>,
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
pub sleep: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>,
pub brightness: f32,
pub display_enabled: bool,
pub display_brightness: Brightness,
pub builtin_anims_enabled: bool,
pub builtin_anims: Animations,
}
impl From<AnimeConfigV472> for AnimeConfig {
fn from(c: AnimeConfigV472) -> AnimeConfig {
AnimeConfig {
system: c.system,
boot: c.boot,
wake: c.wake,
shutdown: c.shutdown,
model_override: c.model_override,
display_enabled: c.display_enabled,
display_brightness: c.display_brightness,
builtin_anims_enabled: c.builtin_anims_enabled,
builtin_anims: c.builtin_anims,
..Default::default()
}
}
}
#[derive(Deserialize, Serialize, Default)]
pub struct AnimeConfigCached {
pub system: Vec<ActionData>,
pub boot: Vec<ActionData>,
pub wake: Vec<ActionData>,
pub shutdown: Vec<ActionData>,
}
impl AniMeConfigCached {
impl AnimeConfigCached {
pub fn init_from_config(
&mut self,
config: &AniMeConfig,
config: &AnimeConfig,
anime_type: AnimeType,
) -> Result<(), AnimeError> {
let mut sys = Vec::with_capacity(config.system.len());
@@ -53,9 +107,8 @@ impl AniMeConfigCached {
/// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct AniMeConfig {
#[serde(skip)]
pub anime_type: AnimeType,
pub struct AnimeConfig {
pub model_override: Option<AnimeType>,
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
@@ -71,10 +124,10 @@ pub struct AniMeConfig {
pub builtin_anims: Animations,
}
impl Default for AniMeConfig {
impl Default for AnimeConfig {
fn default() -> Self {
AniMeConfig {
anime_type: AnimeType::GA402,
AnimeConfig {
model_override: None,
system: Vec::new(),
boot: Vec::new(),
wake: Vec::new(),
@@ -92,7 +145,7 @@ impl Default for AniMeConfig {
}
}
impl StdConfig for AniMeConfig {
impl StdConfig for AnimeConfig {
fn new() -> Self {
Self::create_default()
}
@@ -106,10 +159,10 @@ impl StdConfig for AniMeConfig {
}
}
impl StdConfigLoad for AniMeConfig {}
impl StdConfigLoad2<AnimeConfigV460, AnimeConfigV472> for AnimeConfig {}
impl From<&AniMeConfig> for DeviceState {
fn from(config: &AniMeConfig) -> Self {
impl From<&AnimeConfig> for DeviceState {
fn from(config: &AnimeConfig) -> Self {
DeviceState {
display_enabled: config.display_enabled,
display_brightness: config.display_brightness,
@@ -123,7 +176,7 @@ impl From<&AniMeConfig> for DeviceState {
}
}
impl AniMeConfig {
impl AnimeConfig {
// fn clamp_config_brightness(mut config: &mut AnimeConfig) {
// if config.brightness < 0.0 || config.brightness > 1.0 {
// warn!(
@@ -136,46 +189,40 @@ impl AniMeConfig {
fn create_default() -> Self {
// create a default config here
AniMeConfig {
AnimeConfig {
system: vec![],
boot: vec![
ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
brightness: 1.0,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(2)),
Duration::from_secs(2),
)),
},
],
wake: vec![
ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
brightness: 1.0,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(2)),
Duration::from_secs(2),
)),
},
],
shutdown: vec![
ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
scale: 0.9,
angle: 0.0,
translation: Vec2::new(3.0, 2.0),
brightness: 1.0,
time: AnimTime::Infinite,
},
],
boot: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
brightness: 1.0,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(2)),
Duration::from_secs(2),
)),
}],
wake: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
brightness: 1.0,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(2)),
Duration::from_secs(2),
)),
}],
shutdown: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
scale: 0.9,
angle: 0.0,
translation: Vec2::new(3.0, 2.0),
brightness: 1.0,
time: AnimTime::Infinite,
}],
..Default::default()
}
}
+290
View File
@@ -0,0 +1,290 @@
pub mod config;
/// Implements `CtrlTask`, Reloadable, `ZbusRun`
pub mod trait_impls;
use std::convert::TryFrom;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::sleep;
use ::zbus::export::futures_util::lock::Mutex;
use log::{error, info, warn};
use rog_anime::error::AnimeError;
use rog_anime::usb::{
get_anime_type, pkt_flush, pkt_set_brightness, pkt_set_enable_display,
pkt_set_enable_powersave_anim, pkts_for_init, Brightness,
};
use rog_anime::{ActionData, AnimeDataBuffer, AnimePacketType, AnimeType};
use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw;
use self::config::{AnimeConfig, AnimeConfigCached};
use crate::error::RogError;
enum Node {
Usb(USBRaw),
Hid(HidRaw),
}
impl Node {
pub fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
// TODO: map and pass on errors
match self {
Node::Usb(u) => {
u.write_bytes(message).ok();
}
Node::Hid(h) => {
h.write_bytes(message).ok();
}
}
Ok(())
}
pub fn set_builtins_enabled(&self, enabled: bool, bright: Brightness) -> Result<(), RogError> {
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))?;
self.write_bytes(&pkt_set_enable_display(enabled))?;
self.write_bytes(&pkt_set_brightness(bright))?;
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
}
}
pub struct CtrlAnime {
// node: HidRaw,
node: Node,
anime_type: AnimeType,
cache: AnimeConfigCached,
config: AnimeConfig,
// set to force thread to exit
thread_exit: Arc<AtomicBool>,
// Set to false when the thread exits
thread_running: Arc<AtomicBool>,
}
impl CtrlAnime {
#[inline]
pub fn new(config: AnimeConfig) -> Result<CtrlAnime, RogError> {
let usb = USBRaw::new(0x193b).ok();
let hid = HidRaw::new("193b").ok();
let node = if usb.is_some() {
info!("Anime using the USB interface");
unsafe { Node::Usb(usb.unwrap_unchecked()) }
} else if hid.is_some() {
info!("Anime using the HID interface");
unsafe { Node::Hid(hid.unwrap_unchecked()) }
} else {
return Err(RogError::Anime(AnimeError::NoDevice));
};
// TODO: something better to set wakeups disabled
// if matches!(node, Node::Usb(_)) {
// if let Ok(mut enumerator) = udev::Enumerator::new() {
// enumerator.match_subsystem("usb").ok();
// enumerator.match_attribute("idProduct", "193b").ok();
// if let Ok(mut enumer) = enumerator.scan_devices() {
// if let Some(mut dev) = enumer.next() {
// dev.set_attribute_value("power/wakeup", "disabled").ok();
// }
// }
// }
// }
let mut anime_type = get_anime_type()?;
if let AnimeType::Unknown = anime_type {
if let Some(model) = config.model_override {
warn!("Overriding the Animatrix type as {model:?}");
anime_type = model;
}
}
info!("Device has an AniMe Matrix display: {anime_type:?}");
let mut cache = AnimeConfigCached::default();
cache.init_from_config(&config, anime_type)?;
let ctrl = CtrlAnime {
node,
anime_type,
cache,
config,
thread_exit: Arc::new(AtomicBool::new(false)),
thread_running: Arc::new(AtomicBool::new(false)),
};
ctrl.do_initialization()?;
Ok(ctrl)
}
// let device = CtrlAnime::get_device(0x0b05, 0x193b)?;
/// Start an action thread. This is classed as a singleton and there should
/// be only one running - so the thread uses atomics to signal run/exit.
///
/// Because this also writes to the usb device, other write tries (display
/// only) *must* get the mutex lock and set the `thread_exit` atomic.
async fn run_thread(inner: Arc<Mutex<CtrlAnime>>, actions: Vec<ActionData>, mut once: bool) {
if actions.is_empty() {
warn!("AniMe system actions was empty");
return;
}
if let Some(lock) = inner.try_lock() {
lock.node
.write_bytes(&pkt_set_enable_powersave_anim(false))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
}
// Loop rules:
// - Lock the mutex **only when required**. That is, the lock must be held for
// the shortest duration possible.
// - An AtomicBool used for thread exit should be checked in every loop,
// including nested
// The only reason for this outer thread is to prevent blocking while waiting
// for the next spawned thread to exit
// TODO: turn this in to async task (maybe? COuld still risk blocking main
// thread)
std::thread::Builder::new()
.name("AniMe system thread start".into())
.spawn(move || {
info!("AniMe new system thread started");
// Getting copies of these Atomics is done *in* the thread to ensure
// we don't block other threads/main
let thread_exit;
let thread_running;
let anime_type;
loop {
if let Some(lock) = inner.try_lock() {
thread_exit = lock.thread_exit.clone();
thread_running = lock.thread_running.clone();
anime_type = lock.anime_type;
break;
}
}
// First two loops are to ensure we *do* aquire a lock on the mutex
// The reason the loop is required is because the USB writes can block
// for up to 10ms. We can't fail to get the atomics.
while thread_running.load(Ordering::SeqCst) {
// Make any running loop exit first
thread_exit.store(true, Ordering::SeqCst);
}
info!("AniMe no previous system thread running (now)");
thread_exit.store(false, Ordering::SeqCst);
thread_running.store(true, Ordering::SeqCst);
'main: loop {
for action in &actions {
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
match action {
ActionData::Animation(frames) => {
rog_anime::run_animation(frames, &|frame| {
if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: animation sub-loop was asked to exit");
return Ok(true); // Do safe exit
}
inner
.try_lock()
.map(|lock| {
lock.write_data_buffer(frame)
.map_err(|err| {
warn!(
"rog_anime::run_animation:callback {}",
err
);
})
.ok();
false // Don't exit yet
})
.map_or_else(
|| {
warn!("rog_anime::run_animation:callback failed");
Err(AnimeError::NoFrames)
},
Ok,
)
});
if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: sub-loop exited and main loop exiting now");
break 'main;
}
}
ActionData::Image(image) => {
once = false;
if let Some(lock) = inner.try_lock() {
lock.write_data_buffer(image.as_ref().clone())
.map_err(|e| error!("{}", e))
.ok();
}
}
ActionData::Pause(duration) => sleep(*duration),
ActionData::AudioEq
| ActionData::SystemInfo
| ActionData::TimeDate
| ActionData::Matrix => {}
}
}
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
if once || actions.is_empty() {
break 'main;
}
}
// Clear the display on exit
if let Some(lock) = inner.try_lock() {
if let Ok(data) =
AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()])
.map_err(|e| error!("{}", e))
{
lock.write_data_buffer(data)
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
}
lock.node
.write_bytes(&pkt_set_enable_powersave_anim(
lock.config.builtin_anims_enabled,
))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
}
// Loop ended, set the atmonics
thread_running.store(false, Ordering::SeqCst);
info!("AniMe system thread exited");
})
.map(|err| info!("AniMe system thread: {:?}", err))
.ok();
}
/// Write only a data packet. This will modify the leds brightness using the
/// global brightness set in config.
fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) -> Result<(), RogError> {
for led in buffer.data_mut().iter_mut() {
let mut bright = *led as f32;
if bright > 254.0 {
bright = 254.0;
}
*led = bright as u8;
}
let data = AnimePacketType::try_from(buffer)?;
for row in &data {
self.node.write_bytes(row)?;
}
self.node.write_bytes(&pkt_flush())?;
Ok(())
}
fn do_initialization(&self) -> Result<(), RogError> {
let pkts = pkts_for_init();
self.node.write_bytes(&pkts[0])?;
self.node.write_bytes(&pkts[1])?;
Ok(())
}
}
@@ -1,22 +1,23 @@
use std::sync::atomic::Ordering;
use std::sync::Arc;
use config_traits::StdConfig;
use log::{debug, error, warn};
use log::warn;
use logind_zbus::manager::ManagerProxy;
use rog_anime::usb::{
pkt_set_brightness, pkt_set_builtin_animations, pkt_set_enable_display,
pkt_set_enable_powersave_anim, Brightness,
};
use rog_anime::{Animations, AnimeDataBuffer, DeviceState};
use zbus::object_server::SignalEmitter;
use zbus::proxy::CacheProperties;
use zbus::zvariant::OwnedObjectPath;
use zbus::{interface, Connection};
use zbus::export::futures_util::lock::Mutex;
use zbus::{interface, CacheProperties, Connection, SignalContext};
use super::config::AniMeConfig;
use super::AniMe;
use super::config::AnimeConfig;
use super::CtrlAnime;
use crate::error::RogError;
use crate::Reloadable;
pub const ANIME_ZBUS_NAME: &str = "Anime";
pub const ANIME_ZBUS_PATH: &str = "/org/asuslinux";
async fn get_logind_manager<'a>() -> ManagerProxy<'a> {
let connection = Connection::system()
@@ -31,117 +32,104 @@ async fn get_logind_manager<'a>() -> ManagerProxy<'a> {
}
#[derive(Clone)]
pub struct AniMeZbus(AniMe);
pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>);
impl AniMeZbus {
pub fn new(anime: AniMe) -> Self {
Self(anime)
}
pub async fn start_tasks(
mut self,
connection: &Connection,
path: OwnedObjectPath,
) -> Result<(), RogError> {
// let task = zbus.clone();
self.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
connection
.object_server()
.at(path.clone(), self)
.await
.map_err(|e| {
error!("Couldn't add server at path: {path}, {e:?}");
e
})?;
debug!("start_tasks was successful");
Ok(())
/// The struct with the main dbus methods requires this trait
impl crate::ZbusRun for CtrlAnimeZbus {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, ANIME_ZBUS_PATH, server).await;
}
}
// None of these calls can be guarnateed to succeed unless we loop until okay
// If the try_lock *does* succeed then any other thread trying to lock will not
// grab it until we finish.
#[interface(name = "xyz.ljones.Anime")]
impl AniMeZbus {
#[interface(name = "org.asuslinux.Anime")]
impl CtrlAnimeZbus {
/// Writes a data stream of length. Will force system thread to exit until
/// it is restarted
async fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> {
let bright = self.0.config.lock().await.display_brightness;
self.0.set_builtins_enabled(false, bright).await?;
self.0.thread_exit.store(true, Ordering::SeqCst);
self.0.write_data_buffer(input).await.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
err
})?;
self.0
.lock()
.await
.thread_exit
.store(true, Ordering::SeqCst);
self.0
.lock()
.await
.write_data_buffer(input)
.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
err
})?;
Ok(())
}
/// Set base brightness level
#[zbus(property)]
async fn brightness(&self) -> Brightness {
if let Some(config) = self.0.config.try_lock() {
return config.display_brightness;
}
Brightness::Off
self.0.lock().await.config.display_brightness
}
/// Set base brightness level
#[zbus(property)]
async fn set_brightness(&self, brightness: Brightness) {
self.0
.write_bytes(&pkt_set_brightness(brightness))
.lock()
.await
.node
.write_bytes(&pkt_set_brightness(brightness))
.map_err(|err| {
warn!("ctrl_anime::set_brightness {}", err);
})
.ok();
self.0
.write_bytes(&pkt_set_enable_display(brightness != Brightness::Off))
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(brightness != Brightness::Off))
.map_err(|err| {
warn!("ctrl_anime::set_brightness {}", err);
})
.ok();
let mut config = self.0.config.lock().await;
config.display_enabled = brightness != Brightness::Off;
config.display_brightness = brightness;
config.write();
self.0.lock().await.config.display_enabled = brightness != Brightness::Off;
self.0.lock().await.config.display_brightness = brightness;
self.0.lock().await.config.write();
}
#[zbus(property)]
async fn builtins_enabled(&self) -> bool {
if let Some(config) = self.0.config.try_lock() {
return config.builtin_anims_enabled;
}
false
let lock = self.0.lock().await;
lock.config.builtin_anims_enabled
}
/// Enable the builtin animations or not. This is quivalent to "Powersave
/// animations" in Armory crate
#[zbus(property)]
async fn set_builtins_enabled(&self, enabled: bool) {
let mut config = self.0.config.lock().await;
let brightness = config.display_brightness;
let brightness = self.0.lock().await.config.display_brightness;
self.0
.set_builtins_enabled(enabled, brightness)
.lock()
.await
.node
.set_builtins_enabled(enabled, brightness)
.map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err);
})
.ok();
if !enabled {
let anime_type = config.anime_type;
let anime_type = self.0.lock().await.anime_type;
let data = vec![255u8; anime_type.data_length()];
if let Ok(tmp) = AnimeDataBuffer::from_vec(anime_type, data).map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err);
}) {
self.0
.write_bytes(tmp.data())
.lock()
.await
.node
.write_bytes(tmp.data())
.map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err);
})
@@ -149,75 +137,77 @@ impl AniMeZbus {
}
}
config.builtin_anims_enabled = enabled;
config.write();
self.0.lock().await.config.builtin_anims_enabled = enabled;
self.0.lock().await.config.write();
if enabled {
self.0.thread_exit.store(true, Ordering::Release);
self.0
.lock()
.await
.thread_exit
.store(true, Ordering::Release);
}
}
#[zbus(property)]
async fn builtin_animations(&self) -> Animations {
if let Some(config) = self.0.config.try_lock() {
return config.builtin_anims;
}
Animations::default()
self.0.lock().await.config.builtin_anims
}
/// Set which builtin animation is used for each stage
#[zbus(property)]
async fn set_builtin_animations(&self, settings: Animations) {
self.0
.write_bytes(&pkt_set_builtin_animations(
settings.boot, settings.awake, settings.sleep, settings.shutdown,
))
.lock()
.await
.node
.write_bytes(&pkt_set_builtin_animations(
settings.boot,
settings.awake,
settings.sleep,
settings.shutdown,
))
.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
})
.ok();
self.0
.write_bytes(&pkt_set_enable_powersave_anim(true))
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(true))
.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
})
.ok();
let mut config = self.0.config.lock().await;
config.display_enabled = true;
config.builtin_anims = settings;
config.write();
self.0.lock().await.config.display_enabled = true;
self.0.lock().await.config.builtin_anims = settings;
self.0.lock().await.config.write();
}
#[zbus(property)]
async fn enable_display(&self) -> bool {
if let Some(config) = self.0.config.try_lock() {
return config.display_enabled;
}
false
self.0.lock().await.config.display_enabled
}
/// Set whether the AniMe is enabled at all
#[zbus(property)]
async fn set_enable_display(&self, enabled: bool) {
self.0
.write_bytes(&pkt_set_enable_display(enabled))
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(enabled))
.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
})
.ok();
let mut config = self.0.config.lock().await;
config.display_enabled = enabled;
config.write();
self.0.lock().await.config.display_enabled = enabled;
self.0.lock().await.config.write();
}
#[zbus(property)]
async fn off_when_unplugged(&self) -> bool {
if let Some(config) = self.0.config.try_lock() {
return config.off_when_unplugged;
}
false
self.0.lock().await.config.off_when_unplugged
}
/// Set if to turn the AniMe Matrix off when external power is unplugged
@@ -227,40 +217,34 @@ impl AniMeZbus {
let pow = manager.on_external_power().await.unwrap_or_default();
self.0
.write_bytes(&pkt_set_enable_display(!pow && !enabled))
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(!pow && !enabled))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
})
.ok();
let mut config = self.0.config.lock().await;
config.off_when_unplugged = enabled;
config.write();
self.0.lock().await.config.off_when_unplugged = enabled;
self.0.lock().await.config.write();
}
#[zbus(property)]
async fn off_when_suspended(&self) -> bool {
if let Some(config) = self.0.config.try_lock() {
return config.off_when_suspended;
}
false
self.0.lock().await.config.off_when_suspended
}
/// Set if to turn the AniMe Matrix off when the laptop is suspended
#[zbus(property)]
async fn set_off_when_suspended(&self, enabled: bool) {
let mut config = self.0.config.lock().await;
config.off_when_suspended = enabled;
config.write();
self.0.lock().await.config.off_when_suspended = enabled;
self.0.lock().await.config.write();
}
#[zbus(property)]
async fn off_when_lid_closed(&self) -> bool {
if let Some(config) = self.0.config.try_lock() {
return config.off_when_lid_closed;
}
false
self.0.lock().await.config.off_when_lid_closed
}
/// Set if to turn the AniMe Matrix off when the lid is closed
@@ -270,40 +254,50 @@ impl AniMeZbus {
let lid = manager.lid_closed().await.unwrap_or_default();
self.0
.write_bytes(&pkt_set_enable_display(lid && !enabled))
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(lid && !enabled))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
})
.ok();
let mut config = self.0.config.lock().await;
config.off_when_lid_closed = enabled;
config.write();
self.0.lock().await.config.off_when_lid_closed = enabled;
self.0.lock().await.config.write();
}
/// The main loop is the base system set action if the user isn't running
/// the user daemon
async fn run_main_loop(&self, start: bool) {
if start {
self.0.thread_exit.store(true, Ordering::SeqCst);
self.0.run_thread(self.0.cache.system.clone(), false).await;
self.0
.lock()
.await
.thread_exit
.store(true, Ordering::SeqCst);
CtrlAnime::run_thread(
self.0.clone(),
self.0.lock().await.cache.system.clone(),
false,
)
.await;
}
}
/// Get the device state as stored by asusd
// #[zbus(property)]
async fn device_state(&self) -> DeviceState {
DeviceState::from(&*self.0.config.lock().await)
DeviceState::from(&self.0.lock().await.config)
}
}
impl crate::CtrlTask for AniMeZbus {
impl crate::CtrlTask for CtrlAnimeZbus {
fn zbus_path() -> &'static str {
"ANIME_ZBUS_PATH"
ANIME_ZBUS_PATH
}
async fn create_tasks(&self, _: SignalEmitter<'static>) -> Result<(), RogError> {
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
let inner1 = self.0.clone();
let inner2 = self.0.clone();
let inner3 = self.0.clone();
@@ -313,15 +307,21 @@ impl crate::CtrlTask for AniMeZbus {
// on_sleep
let inner = inner1.clone();
async move {
let config = inner.config.lock().await.clone();
let config = inner.lock().await.config.clone();
if config.display_enabled {
inner.thread_exit.store(true, Ordering::Release); // ensure clean slate
inner
.lock()
.await
.thread_exit
.store(true, Ordering::Release); // ensure clean slate
inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(
!(sleeping && config.off_when_suspended),
))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err);
})
@@ -329,10 +329,12 @@ impl crate::CtrlTask for AniMeZbus {
if config.builtin_anims_enabled {
inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(
!(sleeping && config.off_when_suspended),
))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err);
})
@@ -340,11 +342,18 @@ impl crate::CtrlTask for AniMeZbus {
} else if !sleeping && !config.builtin_anims_enabled {
// Run custom wake animation
inner
.write_bytes(&pkt_set_enable_powersave_anim(false))
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(false))
.ok(); // ensure builtins are disabled
inner.run_thread(inner.cache.wake.clone(), true).await;
CtrlAnime::run_thread(
inner.clone(),
inner.lock().await.cache.wake.clone(),
true,
)
.await;
}
}
}
@@ -353,16 +362,26 @@ impl crate::CtrlTask for AniMeZbus {
// on_shutdown
let inner = inner2.clone();
async move {
let AniMeConfig {
let AnimeConfig {
display_enabled,
builtin_anims_enabled,
..
} = *inner.config.lock().await;
} = inner.lock().await.config;
if display_enabled && !builtin_anims_enabled {
if shutting_down {
inner.run_thread(inner.cache.shutdown.clone(), true).await;
CtrlAnime::run_thread(
inner.clone(),
inner.lock().await.cache.shutdown.clone(),
true,
)
.await;
} else {
inner.run_thread(inner.cache.boot.clone(), true).await;
CtrlAnime::run_thread(
inner.clone(),
inner.lock().await.cache.boot.clone(),
true,
)
.await;
}
}
}
@@ -371,24 +390,28 @@ impl crate::CtrlTask for AniMeZbus {
let inner = inner3.clone();
// on lid change
async move {
let AniMeConfig {
let AnimeConfig {
off_when_lid_closed,
builtin_anims_enabled,
..
} = *inner.config.lock().await;
} = inner.lock().await.config;
if off_when_lid_closed {
if builtin_anims_enabled {
inner
.write_bytes(&pkt_set_enable_powersave_anim(!lid_closed))
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(!lid_closed))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err);
})
.ok();
}
inner
.write_bytes(&pkt_set_enable_display(!lid_closed))
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(!lid_closed))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
})
@@ -400,33 +423,39 @@ impl crate::CtrlTask for AniMeZbus {
let inner = inner4.clone();
// on power change
async move {
let AniMeConfig {
let AnimeConfig {
off_when_unplugged,
builtin_anims_enabled,
brightness_on_battery,
..
} = *inner.config.lock().await;
} = inner.lock().await.config;
if off_when_unplugged {
if builtin_anims_enabled {
inner
.write_bytes(&pkt_set_enable_powersave_anim(power_plugged))
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(power_plugged))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err);
})
.ok();
}
inner
.write_bytes(&pkt_set_enable_display(power_plugged))
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(power_plugged))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_unplugged {}", err);
})
.ok();
} else {
inner
.write_bytes(&pkt_set_brightness(brightness_on_battery))
.lock()
.await
.node
.write_bytes(&pkt_set_brightness(brightness_on_battery))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_unplugged {}", err);
})
@@ -441,62 +470,52 @@ impl crate::CtrlTask for AniMeZbus {
}
}
impl crate::Reloadable for AniMeZbus {
impl crate::Reloadable for CtrlAnimeZbus {
async fn reload(&mut self) -> Result<(), RogError> {
let AniMeConfig {
builtin_anims_enabled,
builtin_anims,
display_enabled,
display_brightness,
off_when_lid_closed,
off_when_unplugged,
..
} = *self.0.config.lock().await;
if let Some(lock) = self.0.try_lock() {
let anim = &lock.config.builtin_anims;
// Set builtins
if lock.config.builtin_anims_enabled {
lock.node.write_bytes(&pkt_set_builtin_animations(
anim.boot,
anim.awake,
anim.sleep,
anim.shutdown,
))?;
}
// Builtins enabled or na?
lock.node.set_builtins_enabled(
lock.config.builtin_anims_enabled,
lock.config.display_brightness,
)?;
// Set builtins
if builtin_anims_enabled {
self.0
.write_bytes(&pkt_set_builtin_animations(
builtin_anims.boot,
builtin_anims.awake,
builtin_anims.sleep,
builtin_anims.shutdown,
))
.await?;
}
// Builtins enabled or na?
self.0
.set_builtins_enabled(builtin_anims_enabled, display_brightness)
.await?;
let manager = get_logind_manager().await;
let lid_closed = manager.lid_closed().await.unwrap_or_default();
let power_plugged = manager.on_external_power().await.unwrap_or_default();
let manager = get_logind_manager().await;
let lid_closed = manager.lid_closed().await.unwrap_or_default();
let power_plugged = manager.on_external_power().await.unwrap_or_default();
let turn_off =
(lid_closed && off_when_lid_closed) || (!power_plugged && off_when_unplugged);
self.0
.write_bytes(&pkt_set_enable_display(!turn_off))
.await
.map_err(|err| {
warn!("create_sys_event_tasks::reload {}", err);
})
.ok();
if turn_off || !display_enabled {
self.0.write_bytes(&pkt_set_enable_display(false)).await?;
// early return so we don't run animation thread
return Ok(());
}
if !builtin_anims_enabled && !self.0.cache.boot.is_empty() {
self.0
.write_bytes(&pkt_set_enable_powersave_anim(false))
.await
let turn_off = (lid_closed && lock.config.off_when_lid_closed)
|| (!power_plugged && lock.config.off_when_unplugged);
lock.node
.write_bytes(&pkt_set_enable_display(!turn_off))
.map_err(|err| {
warn!("create_sys_event_tasks::reload {}", err);
})
.ok();
let action = self.0.cache.boot.clone();
self.0.run_thread(action, true).await;
if turn_off || !lock.config.display_enabled {
lock.node.write_bytes(&pkt_set_enable_display(false))?;
// early return so we don't run animation thread
return Ok(());
}
if !lock.config.builtin_anims_enabled && !lock.cache.boot.is_empty() {
lock.node
.write_bytes(&pkt_set_enable_powersave_anim(false))
.ok();
let action = lock.cache.boot.clone();
CtrlAnime::run_thread(self.0.clone(), action, true).await;
}
}
Ok(())
}
@@ -7,20 +7,12 @@ use rog_aura::keyboard::LaptopAuraPower;
use rog_aura::{
AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, Direction, LedBrightness, Speed, GRADIENT,
};
use serde::{Deserialize, Serialize};
use crate::error::RogError;
use serde_derive::{Deserialize, Serialize};
#[derive(Deserialize, Serialize, Default, Debug, Clone)]
// #[serde(default)]
pub struct AuraConfig {
#[serde(skip)]
pub led_type: AuraDeviceType,
#[serde(skip)]
pub support_data: LedSupportData,
pub config_name: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ally_fix: Option<bool>,
pub brightness: LedBrightness,
pub current_mode: AuraModeNum,
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
@@ -28,8 +20,6 @@ pub struct AuraConfig {
pub multizone: Option<BTreeMap<AuraModeNum, Vec<AuraEffect>>>,
pub multizone_on: bool,
pub enabled: LaptopAuraPower,
#[serde(skip)]
pub per_key_mode_active: bool,
}
impl StdConfig for AuraConfig {
@@ -64,28 +54,24 @@ impl AuraConfig {
let support_data = LedSupportData::get_data(prod_id);
let enabled = LaptopAuraPower::new(device_type, &support_data);
let mut config = AuraConfig {
led_type: device_type,
support_data,
config_name: format!("aura_{prod_id}.ron"),
ally_fix: None,
brightness: LedBrightness::Med,
current_mode: AuraModeNum::Static,
builtins: BTreeMap::new(),
multizone: None,
multizone_on: false,
enabled,
per_key_mode_active: false,
};
for n in &config.support_data.basic_modes {
for n in &support_data.basic_modes {
debug!("creating default for {n}");
config
.builtins
.insert(*n, AuraEffect::default_with_mode(*n));
if !config.support_data.basic_zones.is_empty() {
if !support_data.basic_zones.is_empty() {
let mut default = vec![];
for (i, tmp) in config.support_data.basic_zones.iter().enumerate() {
for (i, tmp) in support_data.basic_zones.iter().enumerate() {
default.push(AuraEffect {
mode: *n,
zone: *tmp,
@@ -144,102 +130,16 @@ impl AuraConfig {
}
None
}
/// Create a default for the `current_mode` if multizone and no config
/// exists.
pub fn create_multizone_default(&mut self) -> Result<(), RogError> {
let mut default = vec![];
for (i, tmp) in self.support_data.basic_zones.iter().enumerate() {
default.push(AuraEffect {
mode: self.current_mode,
zone: *tmp,
colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]),
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
speed: Speed::Med,
direction: Direction::Left,
});
}
if default.is_empty() {
return Err(RogError::AuraEffectNotSupported);
}
if let Some(multizones) = self.multizone.as_mut() {
multizones.insert(self.current_mode, default);
} else {
let mut tmp = BTreeMap::new();
tmp.insert(self.current_mode, default);
self.multizone = Some(tmp);
}
Ok(())
}
/// Reload the config from disk then verify and update it if required.
/// Always rewrites the file to disk.
pub fn load_and_update_config(prod_id: &str) -> AuraConfig {
// New loads data from the DB also
let mut config_init = AuraConfig::new(prod_id);
// config_init.set_filename(prod_id);
let mut config_loaded = config_init.clone().load();
// update the initialised data with what we loaded from disk
for mode_init in &mut config_init.builtins {
// update init values from loaded values if they exist
if let Some(loaded) = config_loaded.builtins.get(mode_init.0) {
*mode_init.1 = loaded.clone();
}
}
// Then replace just incase the initialised data contains new modes added
config_loaded.builtins = config_init.builtins;
config_loaded.support_data = config_init.support_data;
config_loaded.led_type = config_init.led_type;
config_loaded.ally_fix = config_init.ally_fix;
for enabled_init in &mut config_init.enabled.states {
for enabled in &mut config_loaded.enabled.states {
if enabled.zone == enabled_init.zone {
*enabled_init = *enabled;
break;
}
}
}
config_loaded.enabled = config_init.enabled;
if let (Some(mut multizone_init), Some(multizone_loaded)) =
(config_init.multizone, config_loaded.multizone.as_mut())
{
for mode in multizone_init.iter_mut() {
// update init values from loaded values if they exist
if let Some(loaded) = multizone_loaded.get(mode.0) {
let mut new_set = Vec::new();
let data = LedSupportData::get_data(prod_id);
// only reuse a zone mode if the mode is supported
for mode in loaded {
if data.basic_modes.contains(&mode.mode) {
new_set.push(mode.clone());
}
}
*mode.1 = new_set;
}
}
*multizone_loaded = multizone_init;
}
config_loaded.write();
config_loaded
}
}
#[cfg(test)]
mod tests {
use rog_aura::keyboard::AuraPowerState;
use rog_aura::{
AuraEffect, AuraModeNum, AuraZone, Colour, Direction, LedBrightness, PowerZones, Speed,
};
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour};
use super::AuraConfig;
#[test]
fn set_multizone_4key_config() {
std::env::set_var("BOARD_NAME", "");
let mut config = AuraConfig::new("19b6");
let effect = AuraEffect {
@@ -294,31 +194,42 @@ mod tests {
let res = config.multizone.unwrap();
let sta = res.get(&AuraModeNum::Static).unwrap();
assert_eq!(sta.len(), 4);
assert_eq!(sta[0].colour1, Colour {
r: 0xff,
g: 0x00,
b: 0xff
});
assert_eq!(sta[1].colour1, Colour {
r: 0x00,
g: 0xff,
b: 0xff
});
assert_eq!(sta[2].colour1, Colour {
r: 0xff,
g: 0xff,
b: 0x00
});
assert_eq!(sta[3].colour1, Colour {
r: 0x00,
g: 0xff,
b: 0x00
});
assert_eq!(
sta[0].colour1,
Colour {
r: 0xff,
g: 0x00,
b: 0xff
}
);
assert_eq!(
sta[1].colour1,
Colour {
r: 0x00,
g: 0xff,
b: 0xff
}
);
assert_eq!(
sta[2].colour1,
Colour {
r: 0xff,
g: 0xff,
b: 0x00
}
);
assert_eq!(
sta[3].colour1,
Colour {
r: 0x00,
g: 0xff,
b: 0x00
}
);
}
#[test]
fn set_multizone_multimode_config() {
std::env::set_var("BOARD_NAME", "");
let mut config = AuraConfig::new("19b6");
let effect = AuraEffect {
@@ -363,54 +274,4 @@ mod tests {
let sta = res.get(&AuraModeNum::Pulse).unwrap();
assert_eq!(sta.len(), 1);
}
#[test]
fn verify_0x1866_g531i() {
std::env::set_var("BOARD_NAME", "G513I");
let mut config = AuraConfig::new("1866");
assert_eq!(config.brightness, LedBrightness::Med);
assert_eq!(config.builtins.len(), 5);
assert_eq!(config.builtins.first_entry().unwrap().get(), &AuraEffect {
mode: AuraModeNum::Static,
zone: AuraZone::None,
colour1: Colour { r: 166, g: 0, b: 0 },
colour2: Colour { r: 0, g: 0, b: 0 },
speed: Speed::Med,
direction: Direction::Right
});
assert_eq!(config.enabled.states.len(), 1);
assert_eq!(config.enabled.states[0], AuraPowerState {
zone: PowerZones::KeyboardAndLightbar,
boot: true,
awake: true,
sleep: true,
shutdown: true
});
}
#[test]
fn verify_0x19b6_g634j() {
std::env::set_var("BOARD_NAME", "G634J");
let mut config = AuraConfig::new("19b6");
assert_eq!(config.brightness, LedBrightness::Med);
assert_eq!(config.builtins.len(), 12);
assert_eq!(config.builtins.first_entry().unwrap().get(), &AuraEffect {
mode: AuraModeNum::Static,
zone: AuraZone::None,
colour1: Colour { r: 166, g: 0, b: 0 },
colour2: Colour { r: 0, g: 0, b: 0 },
speed: Speed::Med,
direction: Direction::Right
});
assert_eq!(config.enabled.states.len(), 4);
assert_eq!(config.enabled.states[0], AuraPowerState {
zone: PowerZones::Keyboard,
boot: true,
awake: true,
sleep: true,
shutdown: true
});
}
}
+458
View File
@@ -0,0 +1,458 @@
use std::collections::{BTreeMap, HashSet};
use config_traits::{StdConfig, StdConfigLoad};
use inotify::Inotify;
use log::{debug, info, warn};
use rog_aura::aura_detection::LedSupportData;
use rog_aura::keyboard::{LedUsbPackets, UsbPackets};
use rog_aura::usb::{LED_APPLY, LED_SET};
use rog_aura::{
AuraDeviceType, AuraEffect, Direction, LedBrightness, Speed, GRADIENT, LED_MSG_LEN,
};
use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardLed;
use zbus::zvariant::OwnedObjectPath;
use super::config::AuraConfig;
use crate::ctrl_aura::manager::{dbus_path_for_dev, dbus_path_for_tuf};
use crate::error::RogError;
#[derive(Debug)]
pub enum LEDNode {
/// Brightness and/or TUF RGB controls
KbdLed(KeyboardLed),
/// Raw HID handle
Rog(KeyboardLed, HidRaw),
}
impl LEDNode {
// TODO: move various methods upwards to this
pub fn set_brightness(&self, value: u8) -> Result<(), RogError> {
match self {
LEDNode::KbdLed(k) => k.set_brightness(value)?,
LEDNode::Rog(k, _) => k.set_brightness(value)?,
}
Ok(())
}
pub fn get_brightness(&self) -> Result<u8, RogError> {
Ok(match self {
LEDNode::KbdLed(k) => k.get_brightness()?,
LEDNode::Rog(k, _) => k.get_brightness()?,
})
}
pub fn monitor_brightness(&self) -> Result<Inotify, RogError> {
Ok(match self {
LEDNode::KbdLed(k) => k.monitor_brightness()?,
LEDNode::Rog(k, _) => k.monitor_brightness()?,
})
}
}
/// Individual controller for one Aura device
pub struct CtrlKbdLed {
pub led_type: AuraDeviceType,
pub led_node: LEDNode,
pub supported_data: LedSupportData, // TODO: is storing this really required?
pub per_key_mode_active: bool,
pub config: AuraConfig,
pub dbus_path: OwnedObjectPath,
}
impl CtrlKbdLed {
pub fn find_all() -> Result<Vec<Self>, RogError> {
info!("Searching for all Aura devices");
let mut devices = Vec::new();
let mut found = HashSet::new(); // track and ensure we use only one hidraw per prod_id
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
err
})?;
enumerator.match_subsystem("hidraw").map_err(|err| {
warn!("{}", err);
err
})?;
for end_point in enumerator.scan_devices()? {
// usb_device gives us a product and vendor ID
if let Some(usb_device) =
end_point.parent_with_subsystem_devtype("usb", "usb_device")?
{
// The asus_wmi driver latches MCU that controls the USB endpoints
if let Some(parent) = end_point.parent() {
if let Some(driver) = parent.driver() {
// There is a tree of devices added so filter by driver
if driver != "asus" {
continue;
}
} else {
continue;
}
}
// Device is something like 002, while its parent is the MCU
// Think of it like the device is an endpoint of the USB device attached
let mut prod_id = String::new();
if let Some(usb_id) = usb_device.attribute_value("idProduct") {
prod_id = usb_id.to_string_lossy().to_string();
let aura_dev = AuraDeviceType::from(prod_id.as_str());
if aura_dev == AuraDeviceType::Unknown || found.contains(&aura_dev) {
log::debug!("Unknown or invalid device: {usb_id:?}, skipping");
continue;
}
found.insert(aura_dev);
}
let dev_node = if let Some(dev_node) = usb_device.devnode() {
dev_node
} else {
debug!("Device has no devnode, skipping");
continue;
};
info!("AuraControl found device at: {:?}", dev_node);
let dbus_path = dbus_path_for_dev(&usb_device).unwrap_or_default();
let dev = HidRaw::from_device(end_point)?;
let mut dev = Self::from_hidraw(dev, dbus_path)?;
dev.config = Self::init_config(&prod_id);
devices.push(dev);
}
}
// Check for a TUF laptop LED. Assume there is only ever one.
if let Ok(tuf_kbd) = KeyboardLed::new() {
if tuf_kbd.has_kbd_rgb_mode() {
info!("AuraControl found a TUF laptop keyboard");
let ctrl = CtrlKbdLed {
led_type: AuraDeviceType::LaptopTuf,
led_node: LEDNode::KbdLed(tuf_kbd),
supported_data: LedSupportData::get_data("tuf"),
per_key_mode_active: false,
config: Self::init_config("tuf"),
dbus_path: dbus_path_for_tuf(),
};
devices.push(ctrl);
}
}
info!("Found {} Aura devices", devices.len());
Ok(devices)
}
/// The generated data from this function has a default config. This config
/// should be overwritten. The reason for the default config is because
/// of async issues between this and udev/hidraw
pub fn from_hidraw(device: HidRaw, dbus_path: OwnedObjectPath) -> Result<Self, RogError> {
let rgb_led = KeyboardLed::new()?;
let prod_id = AuraDeviceType::from(device.prod_id());
if prod_id == AuraDeviceType::Unknown {
log::error!("{} is AuraDevice::Unknown", device.prod_id());
return Err(RogError::NoAuraNode);
}
// New loads data from the DB also
// let config = Self::init_config(prod_id, data);
let data = LedSupportData::get_data(device.prod_id());
let ctrl = CtrlKbdLed {
led_type: prod_id,
led_node: LEDNode::Rog(rgb_led, device),
supported_data: data.clone(),
per_key_mode_active: false,
config: AuraConfig::default(),
dbus_path,
};
Ok(ctrl)
}
pub fn init_config(prod_id: &str) -> AuraConfig {
// New loads data from the DB also
let mut config_init = AuraConfig::new(prod_id);
// config_init.set_filename(prod_id);
let mut config_loaded = config_init.clone().load();
// update the initialised data with what we loaded from disk
for mode in &mut config_init.builtins {
// update init values from loaded values if they exist
if let Some(loaded) = config_loaded.builtins.get(mode.0) {
*mode.1 = loaded.clone();
}
}
// Then replace just incase the initialised data contains new modes added
config_loaded.builtins = config_init.builtins;
if let (Some(mut multizone_init), Some(multizone_loaded)) =
(config_init.multizone, config_loaded.multizone.as_mut())
{
for mode in multizone_init.iter_mut() {
// update init values from loaded values if they exist
if let Some(loaded) = multizone_loaded.get(mode.0) {
let mut new_set = Vec::new();
let data = LedSupportData::get_data(prod_id);
// only reuse a zone mode if the mode is supported
for mode in loaded {
if data.basic_modes.contains(&mode.mode) {
new_set.push(mode.clone());
}
}
*mode.1 = new_set;
}
}
*multizone_loaded = multizone_init;
}
config_loaded
}
/// Set combination state for boot animation/sleep animation/all leds/keys
/// leds/side leds LED active
pub(super) fn set_power_states(&mut self) -> Result<(), RogError> {
if let LEDNode::KbdLed(_platform) = &mut self.led_node {
// TODO: tuf bool array
// if let Some(pwr) =
// AuraPowerConfig::to_tuf_bool_array(&self.config.enabled) {
// let buf = [1, pwr[1] as u8, pwr[2] as u8, pwr[3] as u8,
// pwr[4] as u8]; platform.set_kbd_rgb_state(&buf)?;
// }
} else if let LEDNode::Rog(_, hid_raw) = &self.led_node {
let bytes = self.config.enabled.to_bytes(self.led_type);
let message = [0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2], bytes[3]];
hid_raw.write_bytes(&message)?;
hid_raw.write_bytes(&LED_SET)?;
// Changes won't persist unless apply is set
hid_raw.write_bytes(&LED_APPLY)?;
}
Ok(())
}
/// Write an effect block. This is for per-key, but can be repurposed to
/// write the raw factory mode packets - when doing this it is expected that
/// only the first `Vec` (`effect[0]`) is valid.
pub fn write_effect_block(&mut self, effect: &UsbPackets) -> Result<(), RogError> {
if self.config.brightness == LedBrightness::Off {
self.config.brightness = LedBrightness::Med;
self.config.write();
}
let pkt_type = effect[0][1];
const PER_KEY_TYPE: u8 = 0xbc;
if pkt_type != PER_KEY_TYPE {
self.per_key_mode_active = false;
if let LEDNode::Rog(_, hid_raw) = &self.led_node {
hid_raw.write_bytes(&effect[0])?;
hid_raw.write_bytes(&LED_SET)?;
// hid_raw.write_bytes(&LED_APPLY)?;
}
} else {
if !self.per_key_mode_active {
if let LEDNode::Rog(_, hid_raw) = &self.led_node {
let init = LedUsbPackets::get_init_msg();
hid_raw.write_bytes(&init)?;
}
self.per_key_mode_active = true;
}
if let LEDNode::Rog(_, hid_raw) = &self.led_node {
for row in effect.iter() {
hid_raw.write_bytes(row)?;
}
} else if let LEDNode::KbdLed(tuf) = &self.led_node {
for row in effect.iter() {
let r = row[9];
let g = row[10];
let b = row[11];
tuf.set_kbd_rgb_mode(&[0, 0, r, g, b, 0])?;
}
}
}
Ok(())
}
pub fn write_mode(&mut self, mode: &AuraEffect) -> Result<(), RogError> {
if let LEDNode::KbdLed(platform) = &self.led_node {
let buf = [
1,
mode.mode as u8,
mode.colour1.r,
mode.colour1.g,
mode.colour1.b,
mode.speed as u8,
];
platform.set_kbd_rgb_mode(&buf)?;
} else if let LEDNode::Rog(_, hid_raw) = &self.led_node {
let bytes: [u8; LED_MSG_LEN] = mode.into();
hid_raw.write_bytes(&bytes)?;
hid_raw.write_bytes(&LED_SET)?;
// Changes won't persist unless apply is set
hid_raw.write_bytes(&LED_APPLY)?;
} else {
return Err(RogError::NoAuraKeyboard);
}
self.per_key_mode_active = false;
Ok(())
}
pub(super) fn write_current_config_mode(&mut self) -> Result<(), RogError> {
if self.config.multizone_on {
let mode = self.config.current_mode;
let mut create = false;
// There is no multizone config for this mode so create one here
// using the colours of rainbow if it exists, or first available
// mode, or random
if self.config.multizone.is_none() {
create = true;
} else if let Some(multizones) = self.config.multizone.as_ref() {
if !multizones.contains_key(&mode) {
create = true;
}
}
if create {
info!("No user-set config for zone founding, attempting a default");
self.create_multizone_default()?;
}
if let Some(multizones) = self.config.multizone.as_mut() {
if let Some(set) = multizones.get(&mode) {
for mode in set.clone() {
self.write_mode(&mode)?;
}
}
}
} else {
let mode = self.config.current_mode;
if let Some(effect) = self.config.builtins.get(&mode).cloned() {
self.write_mode(&effect)?;
}
}
Ok(())
}
/// Create a default for the `current_mode` if multizone and no config
/// exists.
fn create_multizone_default(&mut self) -> Result<(), RogError> {
let mut default = vec![];
for (i, tmp) in self.supported_data.basic_zones.iter().enumerate() {
default.push(AuraEffect {
mode: self.config.current_mode,
zone: *tmp,
colour1: *GRADIENT.get(i).unwrap_or(&GRADIENT[0]),
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
speed: Speed::Med,
direction: Direction::Left,
});
}
if default.is_empty() {
return Err(RogError::AuraEffectNotSupported);
}
if let Some(multizones) = self.config.multizone.as_mut() {
multizones.insert(self.config.current_mode, default);
} else {
let mut tmp = BTreeMap::new();
tmp.insert(self.config.current_mode, default);
self.config.multizone = Some(tmp);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use rog_aura::aura_detection::LedSupportData;
use rog_aura::{AuraDeviceType, AuraModeNum, AuraZone, PowerZones};
use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardLed;
use zbus::zvariant::OwnedObjectPath;
use super::CtrlKbdLed;
use crate::ctrl_aura::config::AuraConfig;
use crate::ctrl_aura::controller::LEDNode;
#[test]
#[ignore = "Unable to run in CI as the HIDRAW device is required"]
fn create_multizone_if_no_config() {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::new("19b6");
let supported_basic_modes = LedSupportData {
device_name: String::new(),
product_id: String::new(),
layout_name: "ga401".to_owned(),
basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![],
advanced_type: rog_aura::keyboard::AdvancedAuraType::None,
power_zones: vec![PowerZones::Keyboard, PowerZones::RearGlow],
};
let mut controller = CtrlKbdLed {
led_type: AuraDeviceType::LaptopPost2021,
led_node: LEDNode::Rog(KeyboardLed::default(), HidRaw::new("19b6").unwrap()),
supported_data: supported_basic_modes,
per_key_mode_active: false,
config,
dbus_path: OwnedObjectPath::default(),
};
assert!(controller.config.multizone.is_none());
assert!(controller.create_multizone_default().is_err());
assert!(controller.config.multizone.is_none());
controller.supported_data.basic_zones.push(AuraZone::Key1);
controller.supported_data.basic_zones.push(AuraZone::Key2);
assert!(controller.create_multizone_default().is_ok());
assert!(controller.config.multizone.is_some());
let m = controller.config.multizone.unwrap();
assert!(m.contains_key(&AuraModeNum::Static));
let e = m.get(&AuraModeNum::Static).unwrap();
assert_eq!(e.len(), 2);
assert_eq!(e[0].zone, AuraZone::Key1);
assert_eq!(e[1].zone, AuraZone::Key2);
}
#[test]
#[ignore = "Unable to run in CI as the HIDRAW device is required"]
// TODO: use sim device
fn next_mode_create_multizone_if_no_config() {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::new("19b6");
let supported_basic_modes = LedSupportData {
device_name: String::new(),
product_id: String::new(),
layout_name: "ga401".to_owned(),
basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![AuraZone::Key1, AuraZone::Key2],
advanced_type: rog_aura::keyboard::AdvancedAuraType::None,
power_zones: vec![PowerZones::Keyboard, PowerZones::RearGlow],
};
let mut controller = CtrlKbdLed {
led_type: AuraDeviceType::LaptopPost2021,
led_node: LEDNode::Rog(KeyboardLed::default(), HidRaw::new("19b6").unwrap()),
supported_data: supported_basic_modes,
per_key_mode_active: false,
config,
dbus_path: OwnedObjectPath::default(),
};
assert!(controller.config.multizone.is_none());
controller.config.multizone_on = true;
// This is called in toggle_mode. It will error here because we have no
// keyboard node in tests.
assert_eq!(
controller
.write_current_config_mode()
.unwrap_err()
.to_string(),
"No supported Aura keyboard"
);
assert!(controller.config.multizone.is_some());
let m = controller.config.multizone.unwrap();
assert!(m.contains_key(&AuraModeNum::Static));
let e = m.get(&AuraModeNum::Static).unwrap();
assert_eq!(e.len(), 2);
assert_eq!(e[0].zone, AuraZone::Key1);
assert_eq!(e[1].zone, AuraZone::Key2);
}
}
+187
View File
@@ -0,0 +1,187 @@
// Plan:
// - Manager has udev monitor on USB looking for ROG devices
// - If a device is found, add it to watch
// - Add it to Zbus server
// - If udev sees device removed then remove the zbus path
use std::collections::HashSet;
use log::{debug, error, info, warn};
use mio::{Events, Interest, Poll, Token};
use rog_aura::AuraDeviceType;
use rog_platform::hid_raw::HidRaw;
use tokio::task::spawn_blocking;
use udev::{Device, MonitorBuilder};
use zbus::object_server::SignalContext;
use zbus::zvariant::{ObjectPath, OwnedObjectPath};
use zbus::Connection;
use crate::ctrl_aura::controller::CtrlKbdLed;
use crate::ctrl_aura::trait_impls::{CtrlAuraZbus, AURA_ZBUS_PATH};
use crate::error::RogError;
use crate::{CtrlTask, Reloadable};
pub struct AuraManager {
_connection: Connection,
}
impl AuraManager {
pub async fn new(connection: Connection) -> Result<Self, RogError> {
let conn_copy = connection.clone();
let mut interfaces = HashSet::new();
// Do the initial keyboard detection:
let all = CtrlKbdLed::find_all()?;
for ctrl in all {
let path = ctrl.dbus_path.clone();
interfaces.insert(path.clone()); // ensure we record the initial stuff
let sig_ctx = CtrlAuraZbus::signal_context(&connection)?;
let sig_ctx2 = sig_ctx.clone();
let zbus = CtrlAuraZbus::new(ctrl, sig_ctx);
start_tasks(zbus, connection.clone(), sig_ctx2, path).await?;
}
let manager = Self {
_connection: connection,
};
// detect all plugged in aura devices (eventually)
// only USB devices are detected for here
spawn_blocking(move || {
let mut monitor = MonitorBuilder::new()?.match_subsystem("hidraw")?.listen()?;
let mut poll = Poll::new()?;
let mut events = Events::with_capacity(1024);
poll.registry()
.register(&mut monitor, Token(0), Interest::READABLE)?;
loop {
if poll.poll(&mut events, None).is_err() {
continue;
}
for event in monitor.iter() {
let parent = if let Some(parent) =
event.parent_with_subsystem_devtype("usb", "usb_device")?
{
parent
} else {
continue;
};
let action = if let Some(action) = event.action() {
action
} else {
continue;
};
let id_product = if let Some(id_product) = parent.attribute_value("idProduct") {
id_product.to_string_lossy()
} else {
continue;
};
let path = if let Some(path) = dbus_path_for_dev(&parent) {
path
} else {
continue;
};
let aura_device = AuraDeviceType::from(&*id_product);
if aura_device == AuraDeviceType::Unknown {
warn!("idProduct:{id_product:?} is unknown, not using");
continue;
}
if action == "remove" {
if interfaces.remove(&path) {
info!("AuraManager removing: {path:?}");
let conn_copy = conn_copy.clone();
tokio::spawn(async move {
let res = conn_copy
.object_server()
.remove::<CtrlAuraZbus, _>(&path)
.await
.map_err(|e| {
error!("Failed to remove {path:?}, {e:?}");
e
})?;
info!("AuraManager removed: {path:?}, {res}");
Ok::<(), RogError>(())
});
}
} else if action == "add" {
if interfaces.contains(&path) {
debug!("Already a ctrl at {path:?}");
continue;
}
// Need to check the driver is asus to prevent using hid_generic
if let Some(p2) = event.parent() {
if let Some(driver) = p2.driver() {
// There is a tree of devices added so filter by driver
if driver != "asus" {
debug!("{id_product:?} driver was not asus, skipping");
continue;
}
} else {
continue;
}
}
if let Some(dev_node) = event.devnode() {
if let Ok(raw) = HidRaw::from_device(event.device())
.map_err(|e| error!("device path error: {e:?}"))
{
if let Ok(mut ctrl) = CtrlKbdLed::from_hidraw(raw, path.clone()) {
ctrl.config = CtrlKbdLed::init_config(&id_product);
interfaces.insert(path.clone());
info!("AuraManager starting device at: {dev_node:?}, {path:?}");
let sig_ctx = CtrlAuraZbus::signal_context(&conn_copy)?;
let zbus = CtrlAuraZbus::new(ctrl, sig_ctx);
let sig_ctx = CtrlAuraZbus::signal_context(&conn_copy)?;
let conn_copy = conn_copy.clone();
tokio::spawn(async move {
start_tasks(zbus, conn_copy.clone(), sig_ctx, path).await
});
}
}
}
};
}
}
// Required for return type on spawn
#[allow(unreachable_code)]
Ok::<(), RogError>(())
});
Ok(manager)
}
}
pub(crate) fn dbus_path_for_dev(parent: &Device) -> Option<OwnedObjectPath> {
if let Some(filename) = super::filename_partial(parent) {
return Some(
ObjectPath::from_str_unchecked(&format!("{AURA_ZBUS_PATH}/{filename}")).into(),
);
}
None
}
pub(crate) fn dbus_path_for_tuf() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{AURA_ZBUS_PATH}/tuf")).into()
}
async fn start_tasks(
mut zbus: CtrlAuraZbus,
connection: Connection,
_signal_ctx: SignalContext<'static>,
path: OwnedObjectPath,
) -> Result<(), RogError> {
// let task = zbus.clone();
// let signal_ctx = signal_ctx.clone();
zbus.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
connection.object_server().at(path, zbus).await.unwrap();
// TODO: skip this until we keep handles to tasks so they can be killed
// task.create_tasks(signal_ctx).await
Ok(())
}
+29
View File
@@ -0,0 +1,29 @@
use udev::Device;
use zbus::zvariant::{ObjectPath, OwnedObjectPath};
pub mod config;
pub mod controller;
pub mod manager;
/// Implements `CtrlTask`, `Reloadable`, `ZbusRun`
pub mod trait_impls;
/// Returns only the Device details concatenated in a form usable for
/// adding/appending to a filename
pub(super) fn filename_partial(parent: &Device) -> Option<OwnedObjectPath> {
if let Some(id_product) = parent.attribute_value("idProduct") {
let id_product = id_product.to_string_lossy();
let path = if let Some(devnum) = parent.attribute_value("devnum") {
let devnum = devnum.to_string_lossy();
if let Some(devpath) = parent.attribute_value("devpath") {
let devpath = devpath.to_string_lossy();
format!("{id_product}_{devnum}_{devpath}")
} else {
format!("{id_product}_{devnum}")
}
} else {
format!("{id_product}")
};
return Some(ObjectPath::from_str_unchecked(&path).into());
}
None
}
+295
View File
@@ -0,0 +1,295 @@
use std::collections::BTreeMap;
use std::sync::Arc;
use config_traits::StdConfig;
use log::{debug, error, info, warn};
use rog_aura::keyboard::{LaptopAuraPower, UsbPackets};
use rog_aura::{AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, LedBrightness, PowerZones};
use zbus::export::futures_util::lock::{Mutex, MutexGuard};
use zbus::export::futures_util::StreamExt;
use zbus::fdo::Error as ZbErr;
use zbus::{interface, SignalContext};
use super::controller::CtrlKbdLed;
use crate::error::RogError;
use crate::CtrlTask;
pub const AURA_ZBUS_NAME: &str = "Aura";
pub const AURA_ZBUS_PATH: &str = "/org/asuslinux";
#[derive(Clone)]
pub struct CtrlAuraZbus(Arc<Mutex<CtrlKbdLed>>, SignalContext<'static>);
impl CtrlAuraZbus {
pub fn new(controller: CtrlKbdLed, signal: SignalContext<'static>) -> Self {
Self(Arc::new(Mutex::new(controller)), signal)
}
fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> {
let bright = lock.led_node.get_brightness()?;
lock.config.read();
lock.config.brightness = bright.into();
lock.config.write();
Ok(())
}
}
/// The main interface for changing, reading, or notfying
///
/// LED commands are split between Brightness, Modes, Per-Key
#[interface(name = "org.asuslinux.Aura")]
impl CtrlAuraZbus {
/// Return the device type for this Aura keyboard
#[zbus(property)]
async fn device_type(&self) -> AuraDeviceType {
let ctrl = self.0.lock().await;
ctrl.led_type
}
/// Return the current LED brightness
#[zbus(property)]
async fn brightness(&self) -> Result<LedBrightness, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.led_node.get_brightness().map(|n| n.into())?)
}
/// Set the keyboard brightness level (0-3)
#[zbus(property)]
async fn set_brightness(&mut self, brightness: LedBrightness) -> Result<(), ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.led_node.set_brightness(brightness.into())?)
}
/// Total levels of brightness available
#[zbus(property)]
async fn supported_brightness(&self) -> Vec<LedBrightness> {
vec![
LedBrightness::Off,
LedBrightness::Low,
LedBrightness::Med,
LedBrightness::High,
]
}
/// The total available modes
#[zbus(property)]
async fn supported_basic_modes(&self) -> Result<Vec<AuraModeNum>, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.config.builtins.keys().cloned().collect())
}
#[zbus(property)]
async fn supported_basic_zones(&self) -> Result<Vec<AuraZone>, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.supported_data.basic_zones.clone())
}
#[zbus(property)]
async fn supported_power_zones(&self) -> Result<Vec<PowerZones>, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.supported_data.power_zones.clone())
}
/// The current mode data
#[zbus(property)]
async fn led_mode(&self) -> Result<AuraModeNum, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.config.current_mode)
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode(&mut self, num: AuraModeNum) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
ctrl.config.current_mode = num;
ctrl.write_current_config_mode()?;
if ctrl.config.brightness == LedBrightness::Off {
ctrl.config.brightness = LedBrightness::Med;
}
ctrl.led_node
.set_brightness(ctrl.config.brightness.into())?;
ctrl.config.write();
self.led_mode_data_invalidate(&self.1).await.ok();
Ok(())
}
/// The current mode data
#[zbus(property)]
async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> {
let ctrl = self.0.lock().await;
let mode = ctrl.config.current_mode;
match ctrl.config.builtins.get(&mode) {
Some(effect) => Ok(effect.clone()),
None => Err(ZbErr::Failed("Could not get the current effect".into())),
}
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode_data(&mut self, effect: AuraEffect) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
if !ctrl.supported_data.basic_modes.contains(&effect.mode)
|| effect.zone != AuraZone::None
&& !ctrl.supported_data.basic_zones.contains(&effect.zone)
{
return Err(ZbErr::NotSupported(format!(
"The Aura effect is not supported: {effect:?}"
)));
}
ctrl.write_mode(&effect)?;
if ctrl.config.brightness == LedBrightness::Off {
ctrl.config.brightness = LedBrightness::Med;
}
ctrl.led_node
.set_brightness(ctrl.config.brightness.into())?;
ctrl.config.set_builtin(effect);
ctrl.config.write();
self.led_mode_invalidate(&self.1).await.ok();
Ok(())
}
/// Get the data set for every mode available
async fn all_mode_data(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
let ctrl = self.0.lock().await;
ctrl.config.builtins.clone()
}
// As property doesn't work for AuraPowerDev (complexity of serialization?)
#[zbus(property)]
async fn led_power(&self) -> LaptopAuraPower {
let ctrl = self.0.lock().await;
ctrl.config.enabled.clone()
}
/// Set a variety of states, input is array of enum.
/// `enabled` sets if the sent array should be disabled or enabled
///
/// For Modern ROG devices the "enabled" flag is ignored.
#[zbus(property)]
async fn set_led_power(&mut self, options: LaptopAuraPower) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
for opt in options.states {
let zone = opt.zone;
for config in ctrl.config.enabled.states.iter_mut() {
if config.zone == zone {
*config = opt;
}
}
}
ctrl.config.write();
Ok(ctrl.set_power_states().map_err(|e| {
warn!("{}", e);
e
})?)
}
/// On machine that have some form of either per-key keyboard or per-zone
/// this can be used to write custom effects over dbus. The input is a
/// nested `Vec<Vec<8>>` where `Vec<u8>` is a raw USB packet
async fn direct_addressing_raw(&self, data: UsbPackets) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
ctrl.write_effect_block(&data)?;
Ok(())
}
}
impl CtrlTask for CtrlAuraZbus {
fn zbus_path() -> &'static str {
"/org/asuslinux"
}
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
let load_save =
|start: bool, mut lock: MutexGuard<'_, CtrlKbdLed>| -> Result<(), RogError> {
// If waking up
if !start {
info!("CtrlKbdLedTask reloading brightness and modes");
lock.led_node
.set_brightness(lock.config.brightness.into())
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})?;
lock.write_current_config_mode().map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})?;
} else if start {
Self::update_config(&mut lock).map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})?;
}
Ok(())
};
let inner1 = self.0.clone();
let inner3 = self.0.clone();
self.create_sys_event_tasks(
move |sleeping| {
let inner1 = inner1.clone();
async move {
let lock = inner1.lock().await;
load_save(sleeping, lock).unwrap(); // unwrap as we want to
// bomb out of the task
}
},
move |_shutting_down| {
let inner3 = inner3.clone();
async move {
let lock = inner3.lock().await;
load_save(false, lock).unwrap(); // unwrap as we want to
// bomb out of the task
}
},
move |_lid_closed| {
// on lid change
async move {}
},
move |_power_plugged| {
// power change
async move {}
},
)
.await;
let ctrl2 = self.0.clone();
let ctrl = self.0.lock().await;
let watch = ctrl.led_node.monitor_brightness()?;
tokio::spawn(async move {
let mut buffer = [0; 32];
watch
.into_event_stream(&mut buffer)
.unwrap()
.for_each(|_| async {
if let Some(lock) = ctrl2.try_lock() {
load_save(true, lock).unwrap(); // unwrap as we want to
// bomb out of the task
}
})
.await;
});
Ok(())
}
}
impl crate::Reloadable for CtrlAuraZbus {
async fn reload(&mut self) -> Result<(), RogError> {
let mut ctrl = self.0.lock().await;
debug!("reloading keyboard mode");
ctrl.write_current_config_mode()?;
debug!("reloading power states");
ctrl.set_power_states().map_err(|err| warn!("{err}")).ok();
Ok(())
}
}
+37 -43
View File
@@ -3,27 +3,26 @@ use std::sync::Arc;
use config_traits::{StdConfig, StdConfigLoad};
use futures_lite::StreamExt;
use futures_util::lock::Mutex;
use log::{debug, error, info, warn};
use rog_platform::platform::{PlatformProfile, RogPlatform};
use rog_platform::platform::{RogPlatform, ThrottlePolicy};
use rog_profiles::error::ProfileError;
use rog_profiles::fan_curve_set::CurveData;
use rog_profiles::{find_fan_curve_node, FanCurvePU, FanCurveProfiles};
use serde::{Deserialize, Serialize};
use zbus::object_server::SignalEmitter;
use zbus::{interface, Connection};
use serde_derive::{Deserialize, Serialize};
use tokio::sync::Mutex;
use zbus::{interface, Connection, SignalContext};
use crate::error::RogError;
use crate::{CtrlTask, CONFIG_PATH_BASE};
pub const FAN_CURVE_ZBUS_NAME: &str = "FanCurves";
pub const FAN_CURVE_ZBUS_PATH: &str = "/xyz/ljones";
pub const FAN_CURVE_ZBUS_PATH: &str = "/org/asuslinux";
#[derive(Deserialize, Serialize, Debug, Default)]
pub struct FanCurveConfig {
pub profiles: FanCurveProfiles,
#[serde(skip)]
pub current: PlatformProfile,
pub current: u8,
}
impl StdConfig for FanCurveConfig {
@@ -54,7 +53,7 @@ pub struct CtrlFanCurveZbus {
impl CtrlFanCurveZbus {
pub fn new() -> Result<Self, RogError> {
let platform = RogPlatform::new()?;
if platform.has_platform_profile() {
if platform.has_throttle_thermal_policy() {
info!("Device has profile control available");
find_fan_curve_node()?;
info!("Device has fan curves available");
@@ -65,16 +64,16 @@ impl CtrlFanCurveZbus {
if config.profiles.balanced.is_empty() || !config.file_path().exists() {
info!("Fetching default fan curves");
let current = platform.get_platform_profile()?;
let current = platform.get_throttle_thermal_policy()?;
for this in [
PlatformProfile::Balanced,
PlatformProfile::Performance,
PlatformProfile::Quiet,
ThrottlePolicy::Balanced,
ThrottlePolicy::Performance,
ThrottlePolicy::Quiet,
] {
// For each profile we need to switch to it before we
// can read the existing values from hardware. The ACPI method used
// for this is what limits us.
platform.set_platform_profile(this.into())?;
platform.set_throttle_thermal_policy(this.into())?;
let mut dev = find_fan_curve_node()?;
fan_curves.set_active_curve_to_defaults(this, &mut dev)?;
@@ -83,7 +82,7 @@ impl CtrlFanCurveZbus {
info!("{}", String::from(curve));
}
}
platform.set_platform_profile(current.as_str())?;
platform.set_throttle_thermal_policy(current)?;
config.profiles = fan_curves;
config.write();
} else {
@@ -101,13 +100,13 @@ impl CtrlFanCurveZbus {
}
}
#[interface(name = "xyz.ljones.FanCurves")]
#[interface(name = "org.asuslinux.FanCurves")]
impl CtrlFanCurveZbus {
/// Set all fan curves for a profile to enabled status. Will also activate a
/// fan curve if in the same profile mode
async fn set_fan_curves_enabled(
&mut self,
profile: PlatformProfile,
profile: ThrottlePolicy,
enabled: bool,
) -> zbus::fdo::Result<()> {
self.config
@@ -128,7 +127,7 @@ impl CtrlFanCurveZbus {
/// activate a fan curve if in the same profile mode
async fn set_profile_fan_curve_enabled(
&mut self,
profile: PlatformProfile,
profile: ThrottlePolicy,
fan: FanCurvePU,
enabled: bool,
) -> zbus::fdo::Result<()> {
@@ -149,7 +148,7 @@ impl CtrlFanCurveZbus {
/// Get the fan-curve data for the currently active ThrottlePolicy
async fn fan_curve_data(
&mut self,
profile: PlatformProfile,
profile: ThrottlePolicy,
) -> zbus::fdo::Result<Vec<CurveData>> {
let curve = self
.config
@@ -165,7 +164,7 @@ impl CtrlFanCurveZbus {
/// Will also activate the fan curve if the user is in the same mode.
async fn set_fan_curve(
&mut self,
profile: PlatformProfile,
profile: ThrottlePolicy,
curve: CurveData,
) -> zbus::fdo::Result<()> {
self.config
@@ -173,7 +172,7 @@ impl CtrlFanCurveZbus {
.await
.profiles
.save_fan_curve(curve, profile)?;
let active: PlatformProfile = self.platform.get_platform_profile()?.into();
let active: ThrottlePolicy = self.platform.get_throttle_thermal_policy()?.into();
if active == profile {
self.config
.lock()
@@ -190,15 +189,15 @@ impl CtrlFanCurveZbus {
///
/// Each platform_profile has a different default and the default can be
/// read only for the currently active profile.
async fn set_curves_to_defaults(&mut self, profile: PlatformProfile) -> zbus::fdo::Result<()> {
let active = self.platform.get_platform_profile()?;
self.platform.set_platform_profile(profile.into())?;
async fn set_curves_to_defaults(&mut self, profile: ThrottlePolicy) -> zbus::fdo::Result<()> {
let active = self.platform.get_throttle_thermal_policy()?;
self.platform.set_throttle_thermal_policy(profile.into())?;
self.config
.lock()
.await
.profiles
.set_active_curve_to_defaults(profile, &mut find_fan_curve_node()?)?;
self.platform.set_platform_profile(active.as_str())?;
self.platform.set_throttle_thermal_policy(active)?;
self.config.lock().await.write();
Ok(())
}
@@ -208,16 +207,16 @@ impl CtrlFanCurveZbus {
///
/// Each platform_profile has a different default and the defualt can be
/// read only for the currently active profile.
async fn reset_profile_curves(&self, profile: PlatformProfile) -> zbus::fdo::Result<()> {
let active = self.platform.get_platform_profile()?;
async fn reset_profile_curves(&self, profile: ThrottlePolicy) -> zbus::fdo::Result<()> {
let active = self.platform.get_throttle_thermal_policy()?;
self.platform.set_platform_profile(profile.into())?;
self.platform.set_throttle_thermal_policy(profile.into())?;
self.config
.lock()
.await
.profiles
.set_active_curve_to_defaults(active.as_str().into(), &mut find_fan_curve_node()?)?;
self.platform.set_platform_profile(active.as_str())?;
.set_active_curve_to_defaults(active.into(), &mut find_fan_curve_node()?)?;
self.platform.set_throttle_thermal_policy(active)?;
self.config.lock().await.write();
Ok(())
@@ -235,32 +234,27 @@ impl CtrlTask for CtrlFanCurveZbus {
FAN_CURVE_ZBUS_PATH
}
async fn create_tasks(&self, _signal_ctxt: SignalEmitter<'static>) -> Result<(), RogError> {
let watch_platform_profile = self.platform.monitor_platform_profile()?;
async fn create_tasks(&self, _signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
let watch_throttle_thermal_policy = self.platform.monitor_throttle_thermal_policy()?;
let platform = self.platform.clone();
let config = self.config.clone();
let fan_curves = self.config.clone();
tokio::spawn(async move {
let mut buffer = [0; 32];
if let Ok(mut stream) = watch_platform_profile.into_event_stream(&mut buffer) {
if let Ok(mut stream) = watch_throttle_thermal_policy.into_event_stream(&mut buffer) {
while (stream.next().await).is_some() {
debug!("watch_platform_profile changed");
if let Ok(profile) =
platform
.get_platform_profile()
.map(|p| p.into())
.map_err(|e| {
error!("get_platform_profile error: {e}");
})
{
debug!("watch_throttle_thermal_policy changed");
if let Ok(profile) = platform.get_throttle_thermal_policy().map_err(|e| {
error!("get_throttle_thermal_policy error: {e}");
}) {
if profile != config.lock().await.current {
fan_curves
.lock()
.await
.profiles
.write_profile_curve_to_platform(
profile,
profile.into(),
&mut find_fan_curve_node().unwrap(),
)
.map_err(|e| warn!("write_profile_curve_to_platform, {}", e))
@@ -279,7 +273,7 @@ impl CtrlTask for CtrlFanCurveZbus {
impl crate::Reloadable for CtrlFanCurveZbus {
/// Fetch the active profile and use that to set all related components up
async fn reload(&mut self) -> Result<(), RogError> {
let active = self.platform.get_platform_profile()?.into();
let active = self.platform.get_throttle_thermal_policy()?.into();
let mut config = self.config.lock().await;
if let Ok(mut device) = find_fan_curve_node() {
config
+526 -375
View File
File diff suppressed because it is too large Load Diff
+51
View File
@@ -0,0 +1,51 @@
use config_traits::{StdConfig, StdConfigLoad};
use rog_slash::{DeviceState, SlashMode};
use serde_derive::{Deserialize, Serialize};
const CONFIG_FILE: &str = "slash.ron";
/// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug)]
pub struct SlashConfig {
pub slash_enabled: bool,
pub slash_brightness: u8,
pub slash_interval: u8,
pub slash_mode: SlashMode,
}
impl Default for SlashConfig {
fn default() -> Self {
SlashConfig {
slash_enabled: true,
slash_brightness: 255,
slash_interval: 0,
slash_mode: SlashMode::Bounce,
}
}
}
impl StdConfig for SlashConfig {
fn new() -> Self {
Self::default()
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
fn config_dir() -> std::path::PathBuf {
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
}
}
impl StdConfigLoad for SlashConfig {}
impl From<&SlashConfig> for DeviceState {
fn from(config: &SlashConfig) -> Self {
DeviceState {
slash_enabled: config.slash_enabled,
slash_brightness: config.slash_brightness,
slash_interval: config.slash_interval,
slash_mode: config.slash_mode,
}
}
}
+106
View File
@@ -0,0 +1,106 @@
pub mod config;
pub mod trait_impls;
use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw;
use rog_slash::error::SlashError;
use rog_slash::usb::{get_slash_type, pkt_set_mode, pkt_set_options, pkts_for_init};
use rog_slash::{SlashMode, SlashType};
use crate::ctrl_slash::config::SlashConfig;
use crate::error::RogError;
enum Node {
Usb(USBRaw),
Hid(HidRaw),
}
impl Node {
pub fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
// TODO: map and pass on errors
match self {
Node::Usb(u) => {
u.write_bytes(message).ok();
}
Node::Hid(h) => {
h.write_bytes(message).ok();
}
}
Ok(())
}
}
pub struct CtrlSlash {
// node: HidRaw,
node: Node,
config: SlashConfig,
// slash_type: SlashType,
// // set to force thread to exit
// thread_exit: Arc<AtomicBool>,
// // Set to false when the thread exits
// thread_running: Arc<AtomicBool>,
}
impl CtrlSlash {
#[inline]
pub fn new(config: SlashConfig) -> Result<CtrlSlash, RogError> {
let slash_type = get_slash_type()?;
if matches!(slash_type, SlashType::Unknown | SlashType::Unsupported) {
return Err(RogError::Slash(SlashError::NoDevice));
}
let usb = USBRaw::new(rog_slash::usb::PROD_ID).ok();
let hid = HidRaw::new(rog_slash::usb::PROD_ID_STR).ok();
let node = if usb.is_some() {
unsafe { Node::Usb(usb.unwrap_unchecked()) }
} else if hid.is_some() {
unsafe { Node::Hid(hid.unwrap_unchecked()) }
} else {
return Err(RogError::NotSupported);
};
let ctrl = CtrlSlash {
node,
config,
// slash_type,
// thread_exit: Arc::new(AtomicBool::new(false)),
// thread_running: Arc::new(AtomicBool::new(false)),
};
ctrl.do_initialization()?;
Ok(ctrl)
}
fn do_initialization(&self) -> Result<(), RogError> {
let init_packets = pkts_for_init();
self.node.write_bytes(&init_packets[0])?;
self.node.write_bytes(&init_packets[1])?;
// Apply config upon initialization
let option_packets = pkt_set_options(
self.config.slash_enabled,
self.config.slash_brightness,
self.config.slash_interval,
);
self.node.write_bytes(&option_packets)?;
let mode_packets = pkt_set_mode(self.config.slash_mode);
self.node.write_bytes(&mode_packets[0])?;
self.node.write_bytes(&mode_packets[1])?;
Ok(())
}
pub fn set_options(&self, enabled: bool, brightness: u8, interval: u8) -> Result<(), RogError> {
let command_packets = pkt_set_options(enabled, brightness, interval);
self.node.write_bytes(&command_packets)?;
Ok(())
}
pub fn set_slash_mode(&self, slash_mode: SlashMode) -> Result<(), RogError> {
let command_packets = pkt_set_mode(slash_mode);
self.node.write_bytes(&command_packets[0])?;
self.node.write_bytes(&command_packets[1])?;
Ok(())
}
}
+161
View File
@@ -0,0 +1,161 @@
use std::sync::Arc;
use config_traits::StdConfig;
use log::warn;
use rog_slash::usb::{pkt_set_mode, pkt_set_options};
use rog_slash::{DeviceState, SlashMode};
use zbus::export::futures_util::lock::Mutex;
use zbus::{interface, Connection, SignalContext};
use crate::ctrl_slash::CtrlSlash;
use crate::error::RogError;
pub const SLASH_ZBUS_NAME: &str = "Slash";
pub const SLASH_ZBUS_PATH: &str = "/org/asuslinux";
#[derive(Clone)]
pub struct CtrlSlashZbus(pub Arc<Mutex<CtrlSlash>>);
/// The struct with the main dbus methods requires this trait
impl crate::ZbusRun for CtrlSlashZbus {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, SLASH_ZBUS_PATH, server).await;
}
}
#[interface(name = "org.asuslinux.Slash")]
impl CtrlSlashZbus {
/// Get enabled or not
#[zbus(property)]
async fn enabled(&self) -> bool {
let lock = self.0.lock().await;
lock.config.slash_enabled
}
/// Set enabled true or false
async fn set_enabled(&self, enabled: bool) {
let mut lock = self.0.lock().await;
let brightness = if enabled && lock.config.slash_brightness == 0 {
0x88
} else {
lock.config.slash_brightness
};
lock.node
.write_bytes(&pkt_set_options(
enabled,
brightness,
lock.config.slash_interval,
))
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_enabled = enabled;
lock.config.slash_brightness = brightness;
lock.config.write();
}
/// Get brightness level
#[zbus(property)]
async fn brightness(&self) -> u8 {
let lock = self.0.lock().await;
lock.config.slash_brightness
}
/// Set brightness level
async fn set_brightness(&self, brightness: u8) {
let mut lock = self.0.lock().await;
let enabled = brightness > 0;
lock.node
.write_bytes(&pkt_set_options(
enabled,
brightness,
lock.config.slash_interval,
))
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_enabled = enabled;
lock.config.slash_brightness = brightness;
lock.config.write();
}
#[zbus(property)]
async fn interval(&self) -> u8 {
let lock = self.0.lock().await;
lock.config.slash_interval
}
/// Set interval between slash animations (0-255)
async fn set_interval(&self, interval: u8) {
let mut lock = self.0.lock().await;
lock.node
.write_bytes(&pkt_set_options(
lock.config.slash_enabled,
lock.config.slash_brightness,
interval,
))
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_interval = interval;
lock.config.write();
}
#[zbus(property)]
async fn slash_mode(&self) -> u8 {
let lock = self.0.lock().await;
lock.config.slash_interval
}
/// Set interval between slash animations (0-255)
async fn set_slash_mode(&self, slash_mode: SlashMode) {
let mut lock = self.0.lock().await;
let command_packets = pkt_set_mode(slash_mode);
lock.node
.write_bytes(&command_packets[0])
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.node
.write_bytes(&command_packets[1])
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_mode = slash_mode;
lock.config.write();
}
/// Get the device state as stored by asusd
// #[zbus(property)]
async fn device_state(&self) -> DeviceState {
let lock = self.0.lock().await;
DeviceState::from(&lock.config)
}
}
impl crate::CtrlTask for CtrlSlashZbus {
fn zbus_path() -> &'static str {
SLASH_ZBUS_PATH
}
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
Ok(())
}
}
impl crate::Reloadable for CtrlSlashZbus {
async fn reload(&mut self) -> Result<(), RogError> {
Ok(())
}
}
+50 -34
View File
@@ -2,19 +2,21 @@ use std::env;
use std::error::Error;
use std::sync::Arc;
use ::zbus::export::futures_util::lock::Mutex;
use ::zbus::Connection;
use asusd::asus_armoury::start_attributes_zbus;
use asusd::aura_manager::DeviceManager;
use asusd::config::Config;
use asusd::ctrl_anime::config::AnimeConfig;
use asusd::ctrl_anime::trait_impls::CtrlAnimeZbus;
use asusd::ctrl_anime::CtrlAnime;
use asusd::ctrl_aura::manager::AuraManager;
use asusd::ctrl_fancurves::CtrlFanCurveZbus;
use asusd::ctrl_platform::CtrlPlatform;
use asusd::ctrl_slash::config::SlashConfig;
use asusd::ctrl_slash::trait_impls::CtrlSlashZbus;
use asusd::ctrl_slash::CtrlSlash;
use asusd::{print_board_info, start_tasks, CtrlTask, DBUS_NAME};
use config_traits::{StdConfig, StdConfigLoad1};
use futures_util::lock::Mutex;
use config_traits::{StdConfig, StdConfigLoad, StdConfigLoad2, StdConfigLoad3};
use log::{error, info};
use rog_platform::asus_armoury::FirmwareAttributes;
use rog_platform::platform::RogPlatform;
use rog_platform::power::AsusPower;
use zbus::fdo::ObjectManager;
#[tokio::main]
@@ -25,12 +27,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.parse_default_env()
.target(env_logger::Target::Stdout)
.format_timestamp(None)
.filter_level(log::LevelFilter::Debug)
.init();
let is_service = match env::var_os("IS_SERVICE") {
Some(val) => val == "1",
None => true,
None => false,
};
if !is_service {
@@ -60,30 +61,23 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
// println!("{:?}", supported.supported_functions());
// Start zbus server
let mut server = Connection::system().await?;
server.object_server().at("/", ObjectManager).await.unwrap();
let mut connection = Connection::system().await?;
connection
.object_server()
.at("/org/asuslinux", ObjectManager)
.await
.unwrap();
let config = Config::new().load();
let cfg_path = config.file_path();
let config = Arc::new(Mutex::new(config));
// supported.add_to_server(&mut connection).await;
let platform = RogPlatform::new()?; // TODO: maybe needs async mutex?
let power = AsusPower::new()?; // TODO: maybe needs async mutex?
let attributes = FirmwareAttributes::new();
start_attributes_zbus(
&server,
platform.clone(),
power.clone(),
attributes.clone(),
config.clone(),
)
.await?;
match CtrlFanCurveZbus::new() {
Ok(ctrl) => {
let sig_ctx = CtrlFanCurveZbus::signal_context(&server)?;
start_tasks(ctrl, &mut server, sig_ctx).await?;
let sig_ctx = CtrlFanCurveZbus::signal_context(&connection)?;
start_tasks(ctrl, &mut connection, sig_ctx).await?;
}
Err(err) => {
error!("FanCurves: {}", err);
@@ -91,30 +85,52 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
}
match CtrlPlatform::new(
platform,
power,
attributes,
config.clone(),
&cfg_path,
CtrlPlatform::signal_context(&server)?,
CtrlPlatform::signal_context(&connection)?,
) {
Ok(ctrl) => {
let sig_ctx = CtrlPlatform::signal_context(&server)?;
start_tasks(ctrl, &mut server, sig_ctx).await?;
let sig_ctx = CtrlPlatform::signal_context(&connection)?;
start_tasks(ctrl, &mut connection, sig_ctx).await?;
}
Err(err) => {
error!("CtrlPlatform: {}", err);
}
}
let _ = DeviceManager::new(server.clone()).await?;
match CtrlAnime::new(AnimeConfig::new().load()) {
Ok(ctrl) => {
let zbus = CtrlAnimeZbus(Arc::new(Mutex::new(ctrl)));
let sig_ctx = CtrlAnimeZbus::signal_context(&connection)?;
start_tasks(zbus, &mut connection, sig_ctx).await?;
}
Err(err) => {
info!("AniMe control: {}", err);
}
}
match CtrlSlash::new(SlashConfig::new().load()) {
Ok(ctrl) => {
let zbus = CtrlSlashZbus(Arc::new(Mutex::new(ctrl)));
// Currently, the Slash has no need for a loop watching power events, however,
// it could be cool to have the slash do some power-on/off animation
// (It has a built-in power on animation which plays when u plug in the power
// supply)
let sig_ctx = CtrlSlashZbus::signal_context(&connection)?;
start_tasks(zbus, &mut connection, sig_ctx).await?;
}
Err(err) => {
info!("AniMe control: {}", err);
}
}
let _ = AuraManager::new(connection.clone()).await?;
// Request dbus name after finishing initalizing all functions
server.request_name(DBUS_NAME).await?;
connection.request_name(DBUS_NAME).await?;
info!("Startup success, begining dbus server loop");
loop {
// This is just a blocker to idle and ensure the reator reacts
server.executor().tick().await;
connection.executor().tick().await;
}
}
-7
View File
@@ -142,10 +142,3 @@ impl From<RogError> for zbus::fdo::Error {
zbus::fdo::Error::Failed(format!("{}", err))
}
}
impl From<RogError> for zbus::Error {
#[inline]
fn from(err: RogError) -> Self {
zbus::Error::Failure(format!("{}", err))
}
}
+28 -36
View File
@@ -1,18 +1,17 @@
#![deny(unused_must_use)]
/// Configuration loading, saving
pub mod config;
/// Control of anime matrix display
pub mod ctrl_anime;
/// Keyboard LED brightness control, RGB, and LED display modes
pub mod ctrl_aura;
/// Control platform profiles + fan-curves if available
pub mod ctrl_fancurves;
/// Control ASUS bios function such as boot sound, Optimus/Dedicated gfx mode
pub mod ctrl_platform;
/// Control of Slash led bar
pub mod ctrl_slash;
pub mod asus_armoury;
pub mod aura_anime;
pub mod aura_laptop;
pub mod aura_manager;
pub mod aura_scsi;
pub mod aura_slash;
pub mod aura_types;
pub mod error;
use std::future::Future;
@@ -23,19 +22,15 @@ use futures_lite::stream::StreamExt;
use log::{debug, info, warn};
use logind_zbus::manager::ManagerProxy;
use tokio::time::sleep;
use zbus::object_server::{Interface, SignalEmitter};
use zbus::proxy::CacheProperties;
use zbus::zvariant::ObjectPath;
use zbus::Connection;
use zbus::{CacheProperties, Connection, SignalContext};
use crate::error::RogError;
const CONFIG_PATH_BASE: &str = "/etc/asusd/";
pub const ASUS_ZBUS_PATH: &str = "/xyz/ljones";
pub static DBUS_NAME: &str = "xyz.ljones.Asusd";
pub static DBUS_PATH: &str = "/xyz/ljones/Daemon";
pub static DBUS_IFACE: &str = "xyz.ljones.Asusd";
pub static DBUS_NAME: &str = "org.asuslinux.Daemon";
pub static DBUS_PATH: &str = "/org/asuslinux/Daemon";
pub static DBUS_IFACE: &str = "org.asuslinux.Daemon";
/// This macro adds a function which spawns an `inotify` task on the passed in
/// `Executor`.
@@ -44,7 +39,7 @@ pub static DBUS_IFACE: &str = "xyz.ljones.Asusd";
/// methods to be available:
/// - `<name>() -> SomeValue`, functionally is a getter, but is allowed to have
/// side effects.
/// - `notify_<name>(SignalEmitter, SomeValue)`
/// - `notify_<name>(SignalContext, SomeValue)`
///
/// In most cases if `SomeValue` is stored in a config then `<name>()` getter is
/// expected to update it. The getter should *never* write back to the path or
@@ -61,13 +56,13 @@ pub static DBUS_IFACE: &str = "xyz.ljones.Asusd";
/// // TODO: this is kind of useless if it can't trigger some action
#[macro_export]
macro_rules! task_watch_item {
($name:ident $name_str:literal $self_inner:ident) => {
($name:ident $self_inner:ident) => {
concat_idents::concat_idents!(fn_name = watch_, $name {
async fn fn_name(
&self,
signal_ctxt: SignalEmitter<'static>,
signal_ctxt: SignalContext<'static>,
) -> Result<(), RogError> {
use futures_util::StreamExt;
use zbus::export::futures_util::StreamExt;
let ctrl = self.clone();
concat_idents::concat_idents!(watch_fn = monitor_, $name {
@@ -77,15 +72,12 @@ macro_rules! task_watch_item {
let mut buffer = [0; 32];
watch.into_event_stream(&mut buffer).unwrap().for_each(|_| async {
if let Ok(value) = ctrl.$name() { // get new value from zbus method
if ctrl.config.lock().await.$name != value {
log::debug!("{} was changed to {} externally", $name_str, value);
concat_idents::concat_idents!(notif_fn = $name, _changed {
ctrl.notif_fn(&signal_ctxt).await.ok();
});
let mut lock = ctrl.config.lock().await;
lock.$name = value;
lock.write();
}
concat_idents::concat_idents!(notif_fn = $name, _changed {
ctrl.notif_fn(&signal_ctxt).await.ok();
});
let mut lock = ctrl.config.lock().await;
lock.$name = value;
lock.write();
}
}).await;
});
@@ -105,9 +97,9 @@ macro_rules! task_watch_item_notify {
concat_idents::concat_idents!(fn_name = watch_, $name {
async fn fn_name(
&self,
signal_ctxt: SignalEmitter<'static>,
signal_ctxt: SignalContext<'static>,
) -> Result<(), RogError> {
use futures_util::StreamExt;
use zbus::export::futures_util::StreamExt;
let ctrl = self.clone();
concat_idents::concat_idents!(watch_fn = monitor_, $name {
@@ -148,7 +140,7 @@ pub trait ReloadAndNotify {
fn reload_and_notify(
&mut self,
signal_context: &SignalEmitter<'static>,
signal_context: &SignalContext<'static>,
data: Self::Data,
) -> impl Future<Output = Result<(), RogError>> + Send;
}
@@ -157,7 +149,7 @@ pub trait ZbusRun {
fn add_to_server(self, server: &mut Connection) -> impl Future<Output = ()> + Send;
fn add_to_server_helper(
iface: impl Interface,
iface: impl zbus::Interface,
path: &str,
server: &mut Connection,
) -> impl Future<Output = ()> + Send {
@@ -179,8 +171,8 @@ pub trait ZbusRun {
pub trait CtrlTask {
fn zbus_path() -> &'static str;
fn signal_context(connection: &Connection) -> Result<SignalEmitter<'static>, zbus::Error> {
SignalEmitter::new(connection, Self::zbus_path())
fn signal_context(connection: &Connection) -> Result<SignalContext<'static>, zbus::Error> {
SignalContext::new(connection, Self::zbus_path())
}
/// Implement to set up various tasks that may be required, using the
@@ -188,7 +180,7 @@ pub trait CtrlTask {
/// separate thread.
fn create_tasks(
&self,
signal: SignalEmitter<'static>,
signal: SignalContext<'static>,
) -> impl Future<Output = Result<(), RogError>> + Send;
// /// Create a timed repeating task
@@ -302,7 +294,7 @@ pub trait GetSupported {
pub async fn start_tasks<T>(
mut zbus: T,
connection: &mut Connection,
signal_ctx: SignalEmitter<'static>,
signal_ctx: SignalContext<'static>,
) -> Result<(), RogError>
where
T: ZbusRun + Reloadable + CtrlTask + Clone,
+3
View File
@@ -13,3 +13,6 @@ serde.workspace = true
ron.workspace = true
log.workspace = true
[dev-dependencies]
cargo-husky.workspace = true
+6 -6
View File
@@ -146,7 +146,11 @@ where
/// Renames the existing file to `<file>-old`
fn rename_file_old(&self) {
warn!("Renaming {} to {}-old", self.file_name(), self.file_name());
warn!(
"Renaming {} to {}-old and recreating config",
self.file_name(),
self.file_name()
);
let mut cfg_old = self.file_path().to_string_lossy().to_string();
cfg_old.push_str("-old");
std::fs::rename(self.file_path(), cfg_old).unwrap_or_else(|err| {
@@ -203,7 +207,7 @@ macro_rules! std_config_load {
/// new one created
pub trait $trait_name<$($generic),*>
where
Self: $crate::StdConfig + DeserializeOwned + Serialize,
Self: $crate::StdConfig +std::fmt::Debug + DeserializeOwned + Serialize,
$($generic: DeserializeOwned + Into<Self>),*
{
fn load(mut self) -> Self {
@@ -270,8 +274,6 @@ mod tests {
}
}
let _ = Test {};
impl crate::StdConfigLoad1<Old1> for Test {}
}
@@ -321,8 +323,6 @@ mod tests {
}
}
let _ = Test {};
impl crate::StdConfigLoad3<Old1, Old2, Old3> for Test {}
}
}
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "cpuctl"
license.workspace = true
version.workspace = true
readme.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
edition.workspace = true
[dependencies]
+14
View File
@@ -0,0 +1,14 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
+11 -11
View File
@@ -3,24 +3,24 @@
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy group="adm">
<allow send_destination="xyz.ljones.Asusd"/>
<allow receive_sender="xyz.ljones.Asusd"/>
<allow send_destination="org.asuslinux.Daemon"/>
<allow receive_sender="org.asuslinux.Daemon"/>
</policy>
<policy group="sudo">
<allow send_destination="xyz.ljones.Asusd"/>
<allow receive_sender="xyz.ljones.Asusd"/>
<allow send_destination="org.asuslinux.Daemon"/>
<allow receive_sender="org.asuslinux.Daemon"/>
</policy>
<policy group="users">
<allow send_destination="xyz.ljones.Asusd"/>
<allow receive_sender="xyz.ljones.Asusd"/>
<allow send_destination="org.asuslinux.Daemon"/>
<allow receive_sender="org.asuslinux.Daemon"/>
</policy>
<policy group="wheel">
<allow send_destination="xyz.ljones.Asusd"/>
<allow receive_sender="xyz.ljones.Asusd"/>
<allow send_destination="org.asuslinux.Daemon"/>
<allow receive_sender="org.asuslinux.Daemon"/>
</policy>
<policy user="root">
<allow own="xyz.ljones.Asusd"/>
<allow send_destination="xyz.ljones.Asusd"/>
<allow receive_sender="xyz.ljones.Asusd"/>
<allow own="org.asuslinux.Daemon"/>
<allow send_destination="org.asuslinux.Daemon"/>
<allow receive_sender="org.asuslinux.Daemon"/>
</policy>
</busconfig>
-2
View File
@@ -7,8 +7,6 @@ ENV{DMI_FAMILY}=="*ROG*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Zephyrus*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Strix*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Vivo*ook*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Zenbook*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*ProArt*", GOTO="asusd_start"
# No match so
GOTO="asusd_end"
+2 -3
View File
@@ -6,14 +6,13 @@ After=nvidia-powerd.service systemd-udevd.service
[Service]
Environment=IS_SERVICE=1
Environment=RUST_LOG="debug"
Environment=RUST_LOG="info"
# required to prevent init issues with hid_asus and MCU
ExecStartPre=/bin/sleep 1
ExecStart=/usr/bin/asusd
Restart=on-failure
RestartSec=1
Type=dbus
BusName=xyz.ljones.Asusd
BusName=org.asuslinux.Daemon
SELinuxContext=system_u:system_r:unconfined_t:s0
#SELinuxContext=system_u:object_r:modules_object_t:s0
TimeoutSec=10
Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 81 KiB

+1 -1
View File
@@ -135,7 +135,7 @@ impl crate::ZbusAdd for CtrlAnimeZbus {
}
}
#[dbus_interface(name = "xyz.ljones.Asusd")]
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlAnimeZbus {
async fn <zbus method>() {
let lock = self.inner.lock().await;
+9 -15
View File
@@ -20,14 +20,13 @@
%global debug_package %{nil}
%endif
%define specrelease %{?dist}
%define pkg_release 3%{specrelease}
%global rpm_dkms_opt 1
# Use hardening ldflags.
%global rustflags -Clink-arg=-Wl,-z,relro,-z,now
Name: asusctl
Version: 6.0.7
Release: %{pkg_release}
Version: 4.7.0
Release: 2
Summary: Control fan speeds, LEDs, graphics modes, and charge levels for ASUS notebooks
License: MPLv2
@@ -35,8 +34,8 @@ Group: System Environment/Kernel
URL: https://gitlab.com/asus-linux/asusctl
Source: %{name}-%{version}.tar.gz
Source1: vendor_%{name}_%{version}.tar.xz
Source2: cargo-config
Source1: vendor-%{name}-%{version}.tar.gz
Source2: cargo_config
BuildRequires: cargo
BuildRequires: rust-packaging
@@ -45,16 +44,12 @@ BuildRequires: clang-devel
BuildRequires: cmake
BuildRequires: rust
BuildRequires: rust-std-static
BuildRequires: pkgconfig(expat)
BuildRequires: pkgconfig(dbus-1)
BuildRequires: pkgconfig(libudev)
BuildRequires: pkgconfig(xkbcommon)
BuildRequires: pkgconfig(libzstd)
BuildRequires: pkgconfig(gtk+-3.0)
BuildRequires: pkgconfig(gdk-3.0)
BuildRequires: desktop-file-utils
# expat-devel pcre2-devel
Requires: libappindicator-gtk3
%description
asus-nb-ctrl is a utility for Linux to control many aspects of various
@@ -72,10 +67,9 @@ A one-stop-shop GUI tool for asusd/asusctl. It aims to provide most controls,
a notification service, and ability to run in the background.
%prep
# %setup -D -T -a 1 -c -n %{name}-%{version}/vendor
# %setup -D -T -a 0 -c
%autosetup
%setup -D -T -a 1
%setup -D -T -a 1 -c -n %{name}-%{version}/vendor
cd ..
mv Cargo.lock{,.bak}
%cargo_prep
@@ -92,7 +86,7 @@ export RUSTFLAGS="%{rustflags}"
export RUSTFLAGS="%{rustflags}"
mkdir -p "%{buildroot}/%{_bindir}" "%{buildroot}%{_docdir}"
%make_install
install -D -m 0644 README.md %{buildroot}/%{_docdir}/%{name}/README.md
install -D -m 0644 rog-anime/README.md %{buildroot}/%{_docdir}/%{name}/README-anime.md
install -D -m 0644 rog-anime/data/diagonal-template.png %{buildroot}/%{_docdir}/%{name}/diagonal-template.png
+3 -1
View File
@@ -1,4 +1,4 @@
use log::warn;
use log::{info, warn};
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Clone)]
pub struct DMIID {
@@ -33,6 +33,8 @@ impl DMIID {
})?;
if let Some(device) = (result).next() {
info!("Found dmi ID info at {:?}", device.sysname());
return Ok(Self {
id_model: device
.property_value("ID_MODEL")
+8
View File
@@ -28,9 +28,17 @@ gif.workspace = true
log.workspace = true
serde.workspace = true
serde_derive.workspace = true
glam.workspace = true
typeshare.workspace = true
zbus = { workspace = true, optional = true }
dmi_id = { path = "../dmi-id", optional = true }
[dev-dependencies]
cargo-husky.workspace = true
[package.metadata.cargo-machete]
ignored = ["serde"]
+15 -30
View File
@@ -3,9 +3,9 @@ use std::str::FromStr;
use std::thread::sleep;
use std::time::{Duration, Instant};
use dmi_id::DMIID;
use log::info;
use serde::{Deserialize, Serialize};
use serde_derive::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value};
@@ -22,19 +22,15 @@ const BLOCK_END: usize = 634;
const PANE_LEN: usize = BLOCK_END - BLOCK_START;
/// First packet is for GA401 + GA402
pub const USB_PREFIX1: [u8; 7] = [
0x5e, 0xc0, 0x02, 0x01, 0x00, 0x73, 0x02,
];
pub const USB_PREFIX1: [u8; 7] = [0x5e, 0xc0, 0x02, 0x01, 0x00, 0x73, 0x02];
/// Second packet is for GA401 + GA402
pub const USB_PREFIX2: [u8; 7] = [
0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02,
];
pub const USB_PREFIX2: [u8; 7] = [0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02];
/// Third packet is for GA402 matrix
pub const USB_PREFIX3: [u8; 7] = [
0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02,
];
pub const USB_PREFIX3: [u8; 7] = [0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02];
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[typeshare]
#[derive(Default, Deserialize, PartialEq, Eq, Clone, Copy, Serialize, Debug)]
pub struct Animations {
pub boot: AnimBooting,
@@ -44,7 +40,9 @@ pub struct Animations {
}
// TODO: move this out
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type))]
#[typeshare]
#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub struct DeviceState {
pub display_enabled: bool,
@@ -57,14 +55,14 @@ pub struct DeviceState {
pub brightness_on_battery: Brightness,
}
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type), zvariant(signature = "s"))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize, Default)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub enum AnimeType {
GA401,
GA402,
GU604,
#[default]
Unsupported,
Unknown,
}
impl FromStr for AnimeType {
@@ -75,25 +73,12 @@ impl FromStr for AnimeType {
"ga401" | "GA401" => Self::GA401,
"ga402" | "GA402" => Self::GA402,
"gu604" | "GU604" => Self::GU604,
_ => Self::Unsupported,
_ => Self::Unknown,
})
}
}
impl AnimeType {
pub fn from_dmi() -> Self {
let board_name = DMIID::new().unwrap_or_default().board_name.to_uppercase();
if board_name.contains("GA401I") || board_name.contains("GA401Q") {
AnimeType::GA401
} else if board_name.contains("GA402R") || board_name.contains("GA402X") {
AnimeType::GA402
} else if board_name.contains("GU604V") {
AnimeType::GU604
} else {
AnimeType::Unsupported
}
}
/// The width of diagonal images
pub fn width(&self) -> usize {
match self {
@@ -180,7 +165,7 @@ impl TryFrom<AnimeDataBuffer> for AnimePacketType {
let mut buffers = match anime.anime {
AnimeType::GA401 => vec![[0; 640]; 2],
AnimeType::GA402 | AnimeType::GU604 | AnimeType::Unsupported => vec![[0; 640]; 3],
AnimeType::GA402 | AnimeType::GU604 | AnimeType::Unknown => vec![[0; 640]; 3],
};
for (idx, chunk) in anime.data.as_slice().chunks(PANE_LEN).enumerate() {
@@ -191,7 +176,7 @@ impl TryFrom<AnimeDataBuffer> for AnimePacketType {
if matches!(
anime.anime,
AnimeType::GA402 | AnimeType::GU604 | AnimeType::Unsupported
AnimeType::GA402 | AnimeType::GU604 | AnimeType::Unknown
) {
buffers[2][..7].copy_from_slice(&USB_PREFIX3);
}
+1 -6
View File
@@ -3,8 +3,6 @@
use std::path::Path;
use std::time::Duration;
use log::error;
use crate::data::AnimeDataBuffer;
use crate::error::{AnimeError, Result};
use crate::AnimeType;
@@ -51,10 +49,7 @@ impl AnimeDiagonal {
bright: f32,
anime_type: AnimeType,
) -> Result<Self> {
let data = std::fs::read(path).map_err(|e| {
error!("Could not open {path:?}: {e:?}");
e
})?;
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)??;
+4 -10
View File
@@ -4,8 +4,7 @@ use std::path::Path;
use std::time::Duration;
use glam::Vec2;
use log::error;
use serde::{Deserialize, Serialize};
use serde_derive::{Deserialize, Serialize};
use crate::error::{AnimeError, Result};
use crate::{AnimeDataBuffer, AnimeDiagonal, AnimeImage, AnimeType, Pixel};
@@ -108,10 +107,7 @@ impl AnimeGif {
// 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).map_err(|e| {
error!("Could not open {file_name:?}: {e:?}");
e
})?;
let file = File::open(file_name)?;
let mut decoder = decoder.read_info(file)?;
let mut frames = Vec::default();
@@ -190,14 +186,12 @@ impl AnimeGif {
anime_type: AnimeType,
) -> Result<Self> {
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).map_err(|e| {
error!("Could not open {file_name:?}: {e:?}");
e
})?;
let file = File::open(file_name)?;
let mut decoder = decoder.read_info(file)?;
let height = decoder.height();
+2 -8
View File
@@ -3,7 +3,6 @@ use std::path::Path;
pub use glam::Vec2;
use glam::{Mat3, Vec3};
use log::error;
use crate::data::AnimeDataBuffer;
use crate::error::{AnimeError, Result};
@@ -321,9 +320,7 @@ impl AnimeImage {
let pos = Vec3::new(led.x(), led.y(), 1.0);
let x0 = led_from_px.mul_vec3(pos + Vec3::new(0.0, -0.5, 0.0));
const GROUP: [f32; 4] = [
0.0, 0.5, 1.0, 1.5,
];
const GROUP: [f32; 4] = [0.0, 0.5, 1.0, 1.5];
for u in &GROUP {
for v in &GROUP {
let sample = x0 + *u * du + *v * dv;
@@ -424,10 +421,7 @@ impl AnimeImage {
bright: f32,
anime_type: AnimeType,
) -> Result<Self> {
let data = std::fs::read(path).map_err(|e| {
error!("Could not open {path:?}: {e:?}");
e
})?;
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)??;
+36 -7
View File
@@ -3,7 +3,7 @@ use std::path::PathBuf;
use std::time::Duration;
use glam::Vec2;
use serde::{Deserialize, Serialize};
use serde_derive::{Deserialize, Serialize};
use crate::error::Result;
use crate::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType};
@@ -75,7 +75,10 @@ impl ActionData {
time,
brightness,
} => ActionData::Animation(AnimeGif::from_diagonal_gif(
file, *time, *brightness, anime_type,
file,
*time,
*brightness,
anime_type,
)?),
ActionLoader::AsusImage {
file,
@@ -88,7 +91,10 @@ impl ActionData {
ActionData::Image(Box::new(data))
}
_ => ActionData::Animation(AnimeGif::from_diagonal_png(
file, anime_type, *time, *brightness,
file,
anime_type,
*time,
*brightness,
)?),
},
ActionLoader::ImageAnimation {
@@ -102,12 +108,24 @@ impl ActionData {
if let Some(ext) = file.extension() {
if ext.to_string_lossy().to_lowercase() == "png" {
return Ok(ActionData::Animation(AnimeGif::from_png(
file, *scale, *angle, *translation, *time, *brightness, anime_type,
file,
*scale,
*angle,
*translation,
*time,
*brightness,
anime_type,
)?));
}
}
ActionData::Animation(AnimeGif::from_gif(
file, *scale, *angle, *translation, *time, *brightness, anime_type,
file,
*scale,
*angle,
*translation,
*time,
*brightness,
anime_type,
)?)
}
ActionLoader::Image {
@@ -122,13 +140,24 @@ impl ActionData {
AnimTime::Infinite => {
// If no time then create a plain static image
let image = AnimeImage::from_png(
file, *scale, *angle, *translation, *brightness, anime_type,
file,
*scale,
*angle,
*translation,
*brightness,
anime_type,
)?;
let data = <AnimeDataBuffer>::try_from(&image)?;
ActionData::Image(Box::new(data))
}
_ => ActionData::Animation(AnimeGif::from_png(
file, *scale, *angle, *translation, *time, *brightness, anime_type,
file,
*scale,
*angle,
*translation,
*time,
*brightness,
anime_type,
)?),
}
}
+18 -12
View File
@@ -11,7 +11,8 @@
use std::str::FromStr;
use dmi_id::DMIID;
use serde::{Deserialize, Serialize};
use serde_derive::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value};
@@ -28,6 +29,7 @@ pub const PROD_ID: u16 = 0x193b;
derive(Type, Value, OwnedValue),
zvariant(signature = "u")
)]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
/// Base LED brightness of the display
pub enum Brightness {
@@ -80,6 +82,7 @@ impl From<Brightness> for i32 {
derive(Type, Value, OwnedValue),
zvariant(signature = "s")
)]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub enum AnimBooting {
#[default]
@@ -120,6 +123,7 @@ impl From<AnimBooting> for i32 {
derive(Type, Value, OwnedValue),
zvariant(signature = "s")
)]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub enum AnimAwake {
#[default]
@@ -160,6 +164,7 @@ impl From<AnimAwake> for i32 {
derive(Type, Value, OwnedValue),
zvariant(signature = "s")
)]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub enum AnimSleeping {
#[default]
@@ -200,6 +205,7 @@ impl From<AnimSleeping> for i32 {
derive(Type, Value, OwnedValue),
zvariant(signature = "s")
)]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub enum AnimShutdown {
#[default]
@@ -235,25 +241,25 @@ impl From<AnimShutdown> for i32 {
}
}
/// `get_maybe_anime_type` is very broad, matching on part of the laptop board
/// name only. For this reason `find_node()` must be used also to verify if the
/// USB device is available.
/// `get_anime_type` is very broad, matching on part of the laptop board name
/// only. For this reason `find_node()` must be used also to verify if the USB
/// device is available.
///
/// The currently known USB device is `19b6`.
#[inline]
pub fn get_anime_type() -> AnimeType {
let dmi = DMIID::new().unwrap_or_default();
pub fn get_anime_type() -> Result<AnimeType, AnimeError> {
let dmi = DMIID::new().map_err(|_| AnimeError::NoDevice)?; // TODO: better error
let board_name = dmi.board_name;
if board_name.contains("GA401I") || board_name.contains("GA401Q") {
AnimeType::GA401
} else if board_name.contains("GA402R") || board_name.contains("GA402X") {
AnimeType::GA402
return Ok(AnimeType::GA401);
} else if board_name.contains("GA402R") {
return Ok(AnimeType::GA402);
} else if board_name.contains("GU604V") {
AnimeType::GU604
} else {
AnimeType::Unsupported
return Ok(AnimeType::GU604);
}
log::warn!("AniMe Matrix device found but not yet supported, will default to a GA402 layout");
Ok(AnimeType::Unknown)
}
/// Get the two device initialization packets. These are required for device
+5
View File
@@ -18,10 +18,15 @@ dbus = ["zbus"]
[dependencies]
serde.workspace = true
serde_derive.workspace = true
zbus = { workspace = true, optional = true }
dmi_id = { path = "../dmi-id" }
# cli and logging
log.workspace = true
typeshare.workspace = true
ron = { version = "*", optional = true }
[dev-dependencies]
cargo-husky.workspace = true
+245 -227
View File
@@ -3,16 +3,7 @@
device_name: "FA506I",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "FA506N",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle],
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -21,7 +12,7 @@
device_name: "FA506Q",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -30,34 +21,43 @@
device_name: "FA507",
product_id: "",
layout_name: "fa507",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "FA617NS",
device_name: "FX505D",
product_id: "",
layout_name: "fx505d",
basic_modes: [Static, Breathe, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "FX505",
device_name: "FX505G",
product_id: "",
layout_name: "fx505d",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "FX506",
device_name: "FX506H",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "FX506L",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -66,7 +66,7 @@
device_name: "FX507Z",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -75,25 +75,7 @@
device_name: "FX516P",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "FX517Z",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "FX617X",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Pulse],
basic_modes: [Static, Breathe, Strobe],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -102,62 +84,89 @@
device_name: "FX705D",
product_id: "",
layout_name: "fx505d",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "G512L",
device_name: "G512",
product_id: "",
layout_name: "g512",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
power_zones: [Keyboard],
),
(
device_name: "G512LV",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "G513I",
product_id: "",
layout_name: "g513i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]),
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G513Q",
device_name: "G513QE",
product_id: "",
layout_name: "g513i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
power_zones: [Keyboard],
),
(
device_name: "G513QM",
product_id: "",
layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "G513QR",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
power_zones: [Keyboard],
),
(
device_name: "G513R",
device_name: "G513QY",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
power_zones: [Keyboard],
),
(
device_name: "G513RC",
product_id: "",
layout_name: "g513i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]),
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G513RM",
product_id: "",
layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]),
power_zones: [Keyboard, Lightbar],
),
@@ -165,16 +174,61 @@
device_name: "G513RW",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
power_zones: [Keyboard],
),
(
device_name: "G531G",
device_name: "G531",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "G531",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "G531GD",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "G531GT",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "G531GU",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "G531GV",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -183,7 +237,7 @@
device_name: "G531GW",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -192,34 +246,25 @@
device_name: "G532",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "G533Q",
product_id: "1866",
product_id: "",
layout_name: "g533q-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G533QS",
product_id: "18c6",
layout_name: "g533q-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
power_zones: [Keyboard],
),
(
device_name: "G533Z",
product_id: "",
layout_name: "g533q-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
@@ -228,34 +273,16 @@
device_name: "G614J",
product_id: "",
layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Pulse, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G614JJ",
product_id: "",
layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G614JZ",
product_id: "",
layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G634J",
product_id: "",
layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar, Logo, RearGlow],
@@ -264,7 +291,7 @@
device_name: "G712LI",
product_id: "",
layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -273,7 +300,7 @@
device_name: "G712LV",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -282,7 +309,7 @@
device_name: "G712LW",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -291,61 +318,43 @@
device_name: "G713IC",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "G713P",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G713QC",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G713QE",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
power_zones: [Keyboard],
),
(
device_name: "G713QM",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "G713QR",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
power_zones: [Keyboard],
),
(
device_name: "G713RC",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]),
power_zones: [Keyboard, Lightbar],
@@ -354,7 +363,7 @@
device_name: "G713RM",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -363,7 +372,7 @@
device_name: "G713RS",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
@@ -372,25 +381,34 @@
device_name: "G713RW",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4, BarLeft, BarRight],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
power_zones: [Keyboard],
),
(
device_name: "G731",
product_id: "",
layout_name: "g533q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "G731G",
device_name: "G731GT",
product_id: "",
layout_name: "g533q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "G731GU",
product_id: "",
layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -399,7 +417,7 @@
device_name: "G731GV",
product_id: "",
layout_name: "g533q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -408,7 +426,7 @@
device_name: "G731GW",
product_id: "",
layout_name: "g533q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -417,16 +435,16 @@
device_name: "G733C",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [Logo, BarLeft, BarRight],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar, Logo, Lid],
power_zones: [Keyboard],
),
(
device_name: "G733P",
device_name: "G733PZ",
product_id: "",
layout_name: "g733pz-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
@@ -435,7 +453,7 @@
device_name: "G733Q",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
@@ -444,38 +462,38 @@
device_name: "G733Z",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar, Logo, Lid],
power_zones: [Keyboard],
),
(
device_name: "G814J",
device_name: "G814JI",
product_id: "",
layout_name: "g814ji-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G834J",
device_name: "G814JZ",
product_id: "",
layout_name: "g814ji-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G834JZ",
product_id: "",
layout_name: "g814ji-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar, Logo, RearGlow],
),
(
device_name: "GA401I",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GA401Q",
product_id: "",
@@ -489,16 +507,7 @@
device_name: "GA402N",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GA402NU-0002",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_modes: [Static, Breathe, Pulse, Rainbow, Strobe],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -507,7 +516,7 @@
device_name: "GA402R",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Pulse, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -516,13 +525,13 @@
device_name: "GA402X",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Pulse, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GA402XV-NC012",
device_name: "GA402XV",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
@@ -531,7 +540,7 @@
power_zones: [Keyboard],
),
(
device_name: "GA403U",
device_name: "GA403UI",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
@@ -543,7 +552,7 @@
device_name: "GA503Q",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Pulse, Rainbow, Strobe],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -561,25 +570,16 @@
device_name: "GA503R",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Pulse, Rainbow, Strobe],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GA605W",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
),
(
device_name: "GL503",
product_id: "",
layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -588,7 +588,7 @@
device_name: "GL503V",
product_id: "",
layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -597,7 +597,7 @@
device_name: "GL504G",
product_id: "",
layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4, Logo, BarLeft, BarRight],
advanced_type: None,
power_zones: [Keyboard],
@@ -606,7 +606,7 @@
device_name: "GL531",
product_id: "",
layout_name: "g512",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
@@ -615,7 +615,7 @@
device_name: "GL553V",
product_id: "",
layout_name: "g533q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -624,7 +624,7 @@
device_name: "GL703G",
product_id: "",
layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -633,34 +633,61 @@
device_name: "GM501G",
product_id: "",
layout_name: "fa507",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GU502",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "GU502G",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "GU502L",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "GU502LU",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GU603H",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
),
(
device_name: "GU603V",
device_name: "GU603VV",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
@@ -669,7 +696,7 @@
device_name: "GU603Z",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
@@ -678,7 +705,7 @@
device_name: "GU604V",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
@@ -687,7 +714,7 @@
device_name: "GU605M",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
@@ -723,7 +750,7 @@
device_name: "GV601R",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -741,7 +768,7 @@
device_name: "GV604V",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -750,7 +777,7 @@
device_name: "GX502",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
@@ -759,7 +786,7 @@
device_name: "GX531",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
@@ -768,7 +795,7 @@
device_name: "GX550L",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
@@ -777,7 +804,7 @@
device_name: "GX551Q",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
@@ -786,16 +813,7 @@
device_name: "GX650P",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "GX650R",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
@@ -804,7 +822,7 @@
device_name: "GX701",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
@@ -813,7 +831,7 @@
device_name: "GX703H",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
@@ -836,6 +854,15 @@
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GZ301Z",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GZ301Z",
product_id: "18c6",
@@ -858,18 +885,9 @@
device_name: "RC71L",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Ally],
),
(
device_name: "RC72L",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Ally],
power_zones: [Keyboard],
),
])
+13 -68
View File
@@ -1,8 +1,6 @@
use std::env;
use dmi_id::DMIID;
use log::{error, info, warn};
use serde::{Deserialize, Serialize};
use serde_derive::{Deserialize, Serialize};
use crate::keyboard::AdvancedAuraType;
use crate::{AuraModeNum, AuraZone, PowerZones};
@@ -62,20 +60,17 @@ impl LedSupportData {
/// matches against laptops first, then will proceed with matching the
/// `device_name` if there are no DMI matches.
pub fn get_data(product_id: &str) -> Self {
let mut dmi = DMIID::new().unwrap_or_default();
if let Ok(board_name) = env::var("BOARD_NAME") {
dmi.board_name = board_name;
}
let dmi = DMIID::new().unwrap_or_default();
// let prod_family = dmi.product_family().expect("Could not get
// product_family");
if let Some(data) = LedSupportFile::load_from_supoprt_db() {
return data.match_device(&dmi.board_name, product_id);
if let Some(data) = data.match_device(&dmi.board_name, product_id) {
return data;
}
}
info!("Using generic LED control for keyboard brightness only. No aura_support file found");
let mut data = LedSupportData::default();
data.power_zones.push(PowerZones::Keyboard);
data
info!("Using generic LED control for keyboard brightness only");
LedSupportData::default()
}
}
@@ -89,7 +84,7 @@ impl LedSupportFile {
/// The list is stored in ordered format, so the iterator must be reversed
/// to ensure we match to *whole names* first before doing a glob match
fn match_device(&self, device_name: &str, product_id: &str) -> LedSupportData {
fn match_device(&self, device_name: &str, product_id: &str) -> Option<LedSupportData> {
for config in self.0.iter().rev() {
if device_name.contains(&config.device_name) {
info!("Matched to {}", config.device_name);
@@ -97,27 +92,15 @@ impl LedSupportFile {
info!("Checking product ID");
if config.product_id == product_id {
info!("Matched to {}", config.product_id);
return config.clone();
return Some(config.clone());
} else {
continue;
}
}
return config.clone();
return Some(config.clone());
}
}
warn!(
"the aura_support.ron file has no entry for this model: {device_name}, {product_id}. \
Using a default"
);
LedSupportData {
device_name: device_name.to_owned(),
product_id: product_id.to_owned(),
layout_name: "Default".to_owned(),
basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![],
advanced_type: AdvancedAuraType::None,
power_zones: vec![PowerZones::Keyboard],
}
None
}
/// Load `LedSupportFile` from the `aura_support.ron` file at
@@ -171,7 +154,6 @@ impl LedSupportFile {
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::PathBuf;
@@ -191,16 +173,9 @@ mod tests {
product_id: String::new(),
layout_name: "ga401".to_owned(),
basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![
AuraZone::Key1,
AuraZone::Logo,
AuraZone::BarLeft,
],
basic_zones: vec![AuraZone::Key1, AuraZone::Logo, AuraZone::BarLeft],
advanced_type: AdvancedAuraType::Zoned(vec![LedCode::LightbarRight]),
power_zones: vec![
PowerZones::Keyboard,
PowerZones::RearGlow,
],
power_zones: vec![PowerZones::Keyboard, PowerZones::RearGlow],
};
assert!(ron::to_string(&led).is_ok());
@@ -220,9 +195,6 @@ mod tests {
let mut tmp_sort = tmp.clone();
tmp_sort.0.sort_by(|a, b| a.product_id.cmp(&b.product_id));
tmp_sort.0.sort_by(|a, b| a.device_name.cmp(&b.device_name));
for model in tmp_sort.0.iter_mut() {
model.basic_modes.sort_by_key(|a| *a as u8);
}
if tmp != tmp_sort {
let sorted =
ron::ser::to_string_pretty(&tmp_sort, PrettyConfig::new().depth_limit(2)).unwrap();
@@ -245,31 +217,4 @@ mod tests {
ron::ser::to_string_pretty(&tmp, my_config).unwrap()
);
}
#[test]
fn find_data_file_groups() {
let mut data = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
data.push("data/aura_support.ron");
let buf = std::fs::read_to_string(&data).unwrap();
let tmp = ron::from_str::<LedSupportFile>(&buf).unwrap();
let mut modes: HashMap<Vec<AuraModeNum>, Vec<String>> = HashMap::new();
for entry in tmp.0 {
if let Some(modes) = modes.get_mut(&entry.basic_modes) {
modes.push(entry.device_name);
} else {
modes.insert(entry.basic_modes, vec![entry.device_name]);
}
}
dbg!(modes);
// let my_config = PrettyConfig::new().depth_limit(2);
// println!(
// "RON: {}",
// ron::ser::to_string_pretty(&tmp, my_config).unwrap()
// );
}
}
+85 -59
View File
@@ -1,13 +1,15 @@
use std::fmt::Display;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use serde_derive::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value};
use crate::error::Error;
use crate::AURA_LAPTOP_LED_MSG_LEN;
use crate::LED_MSG_LEN;
#[typeshare]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[cfg_attr(
feature = "dbus",
@@ -77,6 +79,7 @@ impl From<i32> for LedBrightness {
}
}
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Debug, Clone, PartialEq, Eq, Copy, Deserialize, Serialize)]
pub struct Colour {
@@ -117,11 +120,7 @@ impl From<&[f32; 3]> for Colour {
impl From<Colour> for [f32; 3] {
fn from(c: Colour) -> Self {
[
c.r as f32 / 255.0,
c.g as f32 / 255.0,
c.b as f32 / 255.0,
]
[c.r as f32 / 255.0, c.g as f32 / 255.0, c.b as f32 / 255.0]
}
}
@@ -137,12 +136,11 @@ impl From<&[u8; 3]> for Colour {
impl From<Colour> for [u8; 3] {
fn from(c: Colour) -> Self {
[
c.r, c.g, c.b,
]
[c.r, c.g, c.b]
}
}
#[typeshare]
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
@@ -202,6 +200,7 @@ impl From<Speed> for u8 {
/// Used for Rainbow mode.
///
/// Enum corresponds to the required integer value
#[typeshare]
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
@@ -249,6 +248,7 @@ impl From<Direction> for i32 {
}
/// Enum of modes that convert to the actual number required by a USB HID packet
#[typeshare]
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
@@ -261,8 +261,8 @@ pub enum AuraModeNum {
#[default]
Static = 0,
Breathe = 1,
RainbowCycle = 2,
RainbowWave = 3,
Strobe = 2,
Rainbow = 3,
Star = 4,
Rain = 5,
Highlight = 6,
@@ -290,8 +290,8 @@ impl From<&AuraModeNum> for &str {
match mode {
AuraModeNum::Static => "Static",
AuraModeNum::Breathe => "Breathe",
AuraModeNum::RainbowCycle => "RainbowCycle",
AuraModeNum::RainbowWave => "RainbowWave",
AuraModeNum::Strobe => "Strobe",
AuraModeNum::Rainbow => "Rainbow",
AuraModeNum::Star => "Stars",
AuraModeNum::Rain => "Rain",
AuraModeNum::Highlight => "Highlight",
@@ -307,8 +307,8 @@ impl From<&str> for AuraModeNum {
fn from(mode: &str) -> Self {
match mode {
"Breathe" => AuraModeNum::Breathe,
"RainbowCycle" => AuraModeNum::RainbowCycle,
"RainbowWave" => AuraModeNum::RainbowWave,
"Strobe" => AuraModeNum::Strobe,
"Rainbow" => AuraModeNum::Rainbow,
"Stars" => AuraModeNum::Star,
"Rain" => AuraModeNum::Rain,
"Highlight" => AuraModeNum::Highlight,
@@ -326,8 +326,8 @@ impl From<u8> for AuraModeNum {
fn from(mode: u8) -> Self {
match mode {
1 => AuraModeNum::Breathe,
2 => AuraModeNum::RainbowCycle,
3 => AuraModeNum::RainbowWave,
2 => AuraModeNum::Strobe,
3 => AuraModeNum::Rainbow,
4 => AuraModeNum::Star,
5 => AuraModeNum::Rain,
6 => AuraModeNum::Highlight,
@@ -360,6 +360,7 @@ impl From<AuraEffect> for AuraModeNum {
}
/// Base effects have no zoning, while multizone is 1-4
#[typeshare]
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
@@ -431,8 +432,9 @@ impl From<AuraZone> for i32 {
/// ```rust
/// // let bytes: [u8; LED_MSG_LEN] = mode.into();
/// ```
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AuraEffect {
/// The effect type
pub mode: AuraModeNum,
@@ -492,6 +494,56 @@ impl Display for AuraEffect {
}
}
pub struct AuraParameters {
pub zone: bool,
pub colour1: bool,
pub colour2: bool,
pub speed: bool,
pub direction: bool,
}
#[allow(clippy::fn_params_excessive_bools)]
impl AuraParameters {
pub const fn new(
zone: bool,
colour1: bool,
colour2: bool,
speed: bool,
direction: bool,
) -> Self {
Self {
zone,
colour1,
colour2,
speed,
direction,
}
}
}
impl AuraEffect {
/// A helper to provide detail on what effects have which parameters, e.g
/// the static factory mode accepts only one colour.
pub const fn allowed_parameters(mode: AuraModeNum) -> AuraParameters {
match mode {
AuraModeNum::Static
| AuraModeNum::Highlight
| AuraModeNum::Pulse
| AuraModeNum::Comet
| AuraModeNum::Flash => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Breathe => AuraParameters::new(true, true, true, true, false),
AuraModeNum::Strobe | AuraModeNum::Rain => {
AuraParameters::new(true, false, false, true, false)
}
AuraModeNum::Rainbow => AuraParameters::new(true, false, false, true, true),
AuraModeNum::Star => AuraParameters::new(true, true, true, true, true),
AuraModeNum::Laser | AuraModeNum::Ripple => {
AuraParameters::new(true, true, false, true, false)
}
}
}
}
/// Parses `AuraEffect` in to packet data for writing to the USB interface
///
/// Byte structure where colour is RGB, one byte per R, G, B:
@@ -500,9 +552,9 @@ impl Display for AuraEffect {
/// |---|---|-----|-----|---------|------|----------|---|-----------|
/// |5d |b3 |Zone |Mode |Colour 1 |Speed |Direction |00 |Colour 2 |
/// ```
impl From<&AuraEffect> for [u8; AURA_LAPTOP_LED_MSG_LEN] {
impl From<&AuraEffect> for [u8; LED_MSG_LEN] {
fn from(aura: &AuraEffect) -> Self {
let mut msg = [0u8; AURA_LAPTOP_LED_MSG_LEN];
let mut msg = [0u8; LED_MSG_LEN];
msg[0] = 0x5d;
msg[1] = 0xb3;
msg[2] = aura.zone as u8;
@@ -521,7 +573,7 @@ impl From<&AuraEffect> for [u8; AURA_LAPTOP_LED_MSG_LEN] {
impl From<&AuraEffect> for Vec<u8> {
fn from(aura: &AuraEffect) -> Self {
let mut msg = vec![0u8; AURA_LAPTOP_LED_MSG_LEN];
let mut msg = vec![0u8; LED_MSG_LEN];
msg[0] = 0x5d;
msg[1] = 0xb3;
msg[2] = aura.zone as u8;
@@ -540,9 +592,7 @@ impl From<&AuraEffect> for Vec<u8> {
#[cfg(test)]
mod tests {
use crate::{
AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed, AURA_LAPTOP_LED_MSG_LEN,
};
use crate::{AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed, LED_MSG_LEN};
#[test]
fn check_led_static_packet() {
@@ -558,7 +608,7 @@ mod tests {
speed: Speed::Med,
direction: Direction::Right,
};
let ar = <[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st);
let ar = <[u8; LED_MSG_LEN]>::from(&st);
println!("{:02x?}", ar);
let check = [
@@ -586,10 +636,7 @@ mod tests {
0x5d, 0xb3, 0x01, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
];
assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.zone = AuraZone::Key2;
st.colour1 = Colour {
@@ -601,10 +648,7 @@ mod tests {
0x5d, 0xb3, 0x02, 0x00, 0xff, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
];
assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.zone = AuraZone::Key3;
st.colour1 = Colour {
@@ -616,10 +660,7 @@ mod tests {
0x5d, 0xb3, 0x03, 0x00, 0x00, 0xff, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
];
assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.zone = AuraZone::Key4;
st.colour1 = Colour {
@@ -631,10 +672,7 @@ mod tests {
0x5d, 0xb3, 0x04, 0x00, 0xff, 0x00, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
];
assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.zone = AuraZone::Logo;
st.colour1 = Colour {
@@ -646,10 +684,7 @@ mod tests {
0x5d, 0xb3, 0x05, 0x00, 0x2c, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
];
assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.zone = AuraZone::BarLeft;
st.colour1 = Colour {
@@ -661,10 +696,7 @@ mod tests {
0x5d, 0xb3, 0x06, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
];
assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.zone = AuraZone::BarRight;
st.colour1 = Colour {
@@ -676,19 +708,13 @@ mod tests {
0x5d, 0xb3, 0x07, 0x00, 0xff, 0x00, 0xcd, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
];
assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.mode = AuraModeNum::RainbowWave;
st.mode = AuraModeNum::Rainbow;
let capture = [
0x5d, 0xb3, 0x07, 0x03, 0xff, 0x00, 0xcd, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
];
assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
}
}
-1
View File
@@ -2,7 +2,6 @@ use super::{EffectState, InputForEffect};
use crate::keyboard::{KeyLayout, LedCode};
use crate::Colour;
#[allow(dead_code)]
pub struct InputBased {
led: LedCode,
colour: Colour,
+8 -6
View File
@@ -1,4 +1,4 @@
use serde::{Deserialize, Serialize};
use serde_derive::{Deserialize, Serialize};
mod doom;
pub use doom::*;
@@ -12,7 +12,7 @@ pub use breathe::*;
mod static_;
pub use static_::*;
use crate::keyboard::{AuraLaptopUsbPackets, KeyLayout, LedCode, LedUsbPackets};
use crate::keyboard::{KeyLayout, LedCode, LedUsbPackets, UsbPackets};
use crate::Colour;
// static mut RNDINDEX: usize = 0;
@@ -106,7 +106,7 @@ impl AdvancedEffects {
}
}
pub fn create_packets(&self) -> AuraLaptopUsbPackets {
pub fn create_packets(&self) -> UsbPackets {
let mut usb_packets = if self.zoned {
// TODO: figure out if that single byte difference for multizone actually
// matters
@@ -207,12 +207,14 @@ mod tests {
fn single_key_next_state_then_create() {
let layout = KeyLayout::default_layout();
let mut seq = AdvancedEffects::new(false);
seq.effects
.push(Effect::Static(Static::new(LedCode::F, Colour {
seq.effects.push(Effect::Static(Static::new(
LedCode::F,
Colour {
r: 255,
g: 127,
b: 0,
})));
},
)));
seq.next_state(&layout);
let packets = seq.create_packets();
+14 -11
View File
@@ -1,5 +1,6 @@
use log::warn;
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")]
use zbus::zvariant::Type;
@@ -193,7 +194,8 @@ impl LedCode {
}
/// Represents the per-key raw USB packets
pub type AuraLaptopUsbPackets = Vec<Vec<u8>>;
#[typeshare]
pub type UsbPackets = Vec<Vec<u8>>;
/// A `UsbPackets` contains all data to change the full set of keyboard
/// key colours individually.
@@ -202,11 +204,12 @@ pub type AuraLaptopUsbPackets = Vec<Vec<u8>>;
/// to the keyboard EC. One row controls one group of keys, these keys are not
/// necessarily all on the same row of the keyboard, with some splitting between
/// two rows.
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct LedUsbPackets {
/// The packet data used to send data to the USB keyboard
usb_packets: AuraLaptopUsbPackets,
usb_packets: UsbPackets,
/// Wether or not this packet collection is zoned. The determines which
/// starting bytes are used and what the indexing is for lightbar RGB
/// colours
@@ -469,22 +472,22 @@ impl LedUsbPackets {
}
#[inline]
pub fn get(&self) -> AuraLaptopUsbPackets {
pub fn get(&self) -> UsbPackets {
self.usb_packets.clone()
}
#[inline]
pub fn get_ref(&self) -> &AuraLaptopUsbPackets {
pub fn get_ref(&self) -> &UsbPackets {
&self.usb_packets
}
#[inline]
pub fn get_mut(&mut self) -> &mut AuraLaptopUsbPackets {
pub fn get_mut(&mut self) -> &mut UsbPackets {
&mut self.usb_packets
}
}
impl From<LedUsbPackets> for AuraLaptopUsbPackets {
impl From<LedUsbPackets> for UsbPackets {
fn from(k: LedUsbPackets) -> Self {
k.usb_packets
}
@@ -640,7 +643,7 @@ impl From<&LedCode> for &str {
#[cfg(test)]
mod tests {
use crate::keyboard::{AuraLaptopUsbPackets, LedCode, LedUsbPackets};
use crate::keyboard::{LedCode, LedUsbPackets, UsbPackets};
macro_rules! colour_check_zoned {
($zone:expr, $pkt_idx_start:expr) => {
@@ -650,7 +653,7 @@ mod tests {
c[1] = 255;
c[2] = 255;
let pkt: AuraLaptopUsbPackets = zone.into();
let pkt: UsbPackets = zone.into();
assert_eq!(pkt[0][$pkt_idx_start], 0xff);
assert_eq!(pkt[0][$pkt_idx_start + 1], 0xff);
assert_eq!(pkt[0][$pkt_idx_start + 2], 0xff);
@@ -660,7 +663,7 @@ mod tests {
#[test]
fn zone_to_packet_check() {
let zone = LedUsbPackets::new_zoned(true);
let pkt: AuraLaptopUsbPackets = zone.into();
let pkt: UsbPackets = zone.into();
assert_eq!(pkt[0][0], 0x5d);
assert_eq!(pkt[0][1], 0xbc);
assert_eq!(pkt[0][2], 0x01);
@@ -683,7 +686,7 @@ mod tests {
#[test]
fn perkey_to_packet_check() {
let per_key = LedUsbPackets::new_per_key();
let pkt: AuraLaptopUsbPackets = per_key.into();
let pkt: UsbPackets = per_key.into();
assert_eq!(pkt[0][0], 0x5d);
assert_eq!(pkt[0][1], 0xbc);
assert_eq!(pkt[0][2], 0x00);
@@ -709,7 +712,7 @@ mod tests {
c[1] = 255;
c[2] = 255;
let pkt: AuraLaptopUsbPackets = per_key.into();
let pkt: UsbPackets = per_key.into();
assert_eq!(pkt[5][30], 0xff); // D, red
assert_eq!(pkt[5][31], 0xff); // D
assert_eq!(pkt[5][32], 0xff); // D
+112 -88
View File
@@ -335,93 +335,117 @@ impl KeyLayout {
KeyShape::new_led(1.0, 1.0, 0.1, 0.1, 0.1, 0.1),
)]),
key_rows: vec![
KeyRow::new(0.1, 0.1, vec![
(LedCode::Esc, "regular".to_owned()),
(LedCode::F1, "regular".to_owned()),
(LedCode::F2, "regular".to_owned()),
(LedCode::F3, "regular".to_owned()),
(LedCode::F4, "regular".to_owned()),
// not sure which key to put here
(LedCode::F5, "regular".to_owned()),
(LedCode::F6, "regular".to_owned()),
(LedCode::F7, "regular".to_owned()),
(LedCode::F8, "regular".to_owned()),
(LedCode::F9, "regular".to_owned()),
(LedCode::F10, "regular".to_owned()),
(LedCode::F11, "regular".to_owned()),
(LedCode::F12, "regular".to_owned()),
]),
KeyRow::new(0.1, 0.1, vec![
(LedCode::Tilde, "regular".to_owned()),
(LedCode::N1, "regular".to_owned()),
(LedCode::N2, "regular".to_owned()),
(LedCode::N3, "regular".to_owned()),
(LedCode::N4, "regular".to_owned()),
(LedCode::N5, "regular".to_owned()),
(LedCode::N6, "regular".to_owned()),
(LedCode::N7, "regular".to_owned()),
(LedCode::N8, "regular".to_owned()),
(LedCode::N9, "regular".to_owned()),
(LedCode::N0, "regular".to_owned()),
(LedCode::Hyphen, "regular".to_owned()),
(LedCode::Equals, "regular".to_owned()),
(LedCode::Backspace, "regular".to_owned()),
]),
KeyRow::new(0.1, 0.1, vec![
(LedCode::Tab, "regular".to_owned()),
(LedCode::Q, "regular".to_owned()),
(LedCode::W, "regular".to_owned()),
(LedCode::E, "regular".to_owned()),
(LedCode::R, "regular".to_owned()),
(LedCode::T, "regular".to_owned()),
(LedCode::Y, "regular".to_owned()),
(LedCode::U, "regular".to_owned()),
(LedCode::I, "regular".to_owned()),
(LedCode::O, "regular".to_owned()),
(LedCode::P, "regular".to_owned()),
(LedCode::LBracket, "regular".to_owned()),
(LedCode::RBracket, "regular".to_owned()),
(LedCode::BackSlash, "regular".to_owned()),
]),
KeyRow::new(0.1, 0.1, vec![
(LedCode::Caps, "regular".to_owned()),
(LedCode::A, "regular".to_owned()),
(LedCode::S, "regular".to_owned()),
(LedCode::D, "regular".to_owned()),
(LedCode::F, "regular".to_owned()),
(LedCode::G, "regular".to_owned()),
(LedCode::H, "regular".to_owned()),
(LedCode::J, "regular".to_owned()),
(LedCode::K, "regular".to_owned()),
(LedCode::L, "regular".to_owned()),
(LedCode::SemiColon, "regular".to_owned()),
(LedCode::Quote, "regular".to_owned()),
(LedCode::Return, "regular".to_owned()),
]),
KeyRow::new(0.1, 0.1, vec![
(LedCode::LShift, "regular".to_owned()),
(LedCode::Z, "regular".to_owned()),
(LedCode::X, "regular".to_owned()),
(LedCode::C, "regular".to_owned()),
(LedCode::V, "regular".to_owned()),
(LedCode::B, "regular".to_owned()),
(LedCode::N, "regular".to_owned()),
(LedCode::M, "regular".to_owned()),
(LedCode::Comma, "regular".to_owned()),
(LedCode::Period, "regular".to_owned()),
(LedCode::FwdSlash, "regular".to_owned()),
(LedCode::Rshift, "regular".to_owned()),
]),
KeyRow::new(0.1, 0.1, vec![
(LedCode::LCtrl, "regular".to_owned()),
(LedCode::LFn, "regular".to_owned()),
(LedCode::Meta, "regular".to_owned()),
(LedCode::LAlt, "regular".to_owned()),
(LedCode::Spacebar, "regular".to_owned()),
(LedCode::RAlt, "regular".to_owned()),
(LedCode::PrtSc, "regular".to_owned()),
(LedCode::RCtrl, "regular".to_owned()),
]),
KeyRow::new(
0.1,
0.1,
vec![
(LedCode::Esc, "regular".to_owned()),
(LedCode::F1, "regular".to_owned()),
(LedCode::F2, "regular".to_owned()),
(LedCode::F3, "regular".to_owned()),
(LedCode::F4, "regular".to_owned()),
// not sure which key to put here
(LedCode::F5, "regular".to_owned()),
(LedCode::F6, "regular".to_owned()),
(LedCode::F7, "regular".to_owned()),
(LedCode::F8, "regular".to_owned()),
(LedCode::F9, "regular".to_owned()),
(LedCode::F10, "regular".to_owned()),
(LedCode::F11, "regular".to_owned()),
(LedCode::F12, "regular".to_owned()),
],
),
KeyRow::new(
0.1,
0.1,
vec![
(LedCode::Tilde, "regular".to_owned()),
(LedCode::N1, "regular".to_owned()),
(LedCode::N2, "regular".to_owned()),
(LedCode::N3, "regular".to_owned()),
(LedCode::N4, "regular".to_owned()),
(LedCode::N5, "regular".to_owned()),
(LedCode::N6, "regular".to_owned()),
(LedCode::N7, "regular".to_owned()),
(LedCode::N8, "regular".to_owned()),
(LedCode::N9, "regular".to_owned()),
(LedCode::N0, "regular".to_owned()),
(LedCode::Hyphen, "regular".to_owned()),
(LedCode::Equals, "regular".to_owned()),
(LedCode::Backspace, "regular".to_owned()),
],
),
KeyRow::new(
0.1,
0.1,
vec![
(LedCode::Tab, "regular".to_owned()),
(LedCode::Q, "regular".to_owned()),
(LedCode::W, "regular".to_owned()),
(LedCode::E, "regular".to_owned()),
(LedCode::R, "regular".to_owned()),
(LedCode::T, "regular".to_owned()),
(LedCode::Y, "regular".to_owned()),
(LedCode::U, "regular".to_owned()),
(LedCode::I, "regular".to_owned()),
(LedCode::O, "regular".to_owned()),
(LedCode::P, "regular".to_owned()),
(LedCode::LBracket, "regular".to_owned()),
(LedCode::RBracket, "regular".to_owned()),
(LedCode::BackSlash, "regular".to_owned()),
],
),
KeyRow::new(
0.1,
0.1,
vec![
(LedCode::Caps, "regular".to_owned()),
(LedCode::A, "regular".to_owned()),
(LedCode::S, "regular".to_owned()),
(LedCode::D, "regular".to_owned()),
(LedCode::F, "regular".to_owned()),
(LedCode::G, "regular".to_owned()),
(LedCode::H, "regular".to_owned()),
(LedCode::J, "regular".to_owned()),
(LedCode::K, "regular".to_owned()),
(LedCode::L, "regular".to_owned()),
(LedCode::SemiColon, "regular".to_owned()),
(LedCode::Quote, "regular".to_owned()),
(LedCode::Return, "regular".to_owned()),
],
),
KeyRow::new(
0.1,
0.1,
vec![
(LedCode::LShift, "regular".to_owned()),
(LedCode::Z, "regular".to_owned()),
(LedCode::X, "regular".to_owned()),
(LedCode::C, "regular".to_owned()),
(LedCode::V, "regular".to_owned()),
(LedCode::B, "regular".to_owned()),
(LedCode::N, "regular".to_owned()),
(LedCode::M, "regular".to_owned()),
(LedCode::Comma, "regular".to_owned()),
(LedCode::Period, "regular".to_owned()),
(LedCode::FwdSlash, "regular".to_owned()),
(LedCode::Rshift, "regular".to_owned()),
],
),
KeyRow::new(
0.1,
0.1,
vec![
(LedCode::LCtrl, "regular".to_owned()),
(LedCode::LFn, "regular".to_owned()),
(LedCode::Meta, "regular".to_owned()),
(LedCode::LAlt, "regular".to_owned()),
(LedCode::Spacebar, "regular".to_owned()),
(LedCode::RAlt, "regular".to_owned()),
(LedCode::PrtSc, "regular".to_owned()),
(LedCode::RCtrl, "regular".to_owned()),
],
),
],
}
}
@@ -464,7 +488,7 @@ mod tests {
let rows = &data.key_rows;
for row in rows {
for k in &row.row {
if data.key_shapes.contains_key(&k.1) {
if data.key_shapes.get(&k.1).is_some() {
unused.remove(&k.1);
} else {
panic!("Key {:?} was missing matching shape {}", k.0, k.1);
+250 -329
View File
@@ -5,6 +5,7 @@ use std::ops::{BitAnd, BitOr};
use log::warn;
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value};
@@ -15,6 +16,7 @@ use crate::{AuraDeviceType, PowerZones};
/// - 2021+, the struct is a single zone with 4 states
/// - pre-2021, the struct is 1 or 2 zones and 3 states
/// - Tuf, the struct is 1 zone and 3 states
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuraPowerState {
@@ -51,10 +53,7 @@ impl AuraPowerState {
}
fn tuf_to_bytes(&self) -> Vec<u8> {
// &cmd, &boot, &awake, &sleep, &keyboard
vec![
1, self.boot as u8, self.awake as u8, self.sleep as u8, 1,
]
todo!("0s and 1s for bool array")
}
/// # Bits for older 0x1866 keyboard model
@@ -102,49 +101,44 @@ impl AuraPowerState {
]
}
pub fn new_to_byte(&self) -> u32 {
fn new_to_byte(&self) -> u32 {
match self.zone {
PowerZones::Logo => {
self.boot as u32
| ((self.awake as u32) << 2)
| ((self.sleep as u32) << 4)
| ((self.shutdown as u32) << 6)
}
PowerZones::Ally => {
(self.boot as u32)
| ((self.awake as u32) << 1)
| ((self.sleep as u32) << 2)
| ((self.shutdown as u32) << 3)
| (self.awake as u32) << 2
| (self.sleep as u32) << 4
| (self.shutdown as u32) << 6
}
PowerZones::Keyboard => {
((self.boot as u32) << 1)
| ((self.awake as u32) << 3)
| ((self.sleep as u32) << 5)
| ((self.shutdown as u32) << 7)
(self.boot as u32) << 1
| (self.awake as u32) << 3
| (self.sleep as u32) << 5
| (self.shutdown as u32) << 7
}
PowerZones::Lightbar => {
((self.boot as u32) << (7 + 2))
| ((self.awake as u32) << (7 + 3))
| ((self.sleep as u32) << (7 + 4))
| ((self.shutdown as u32) << (7 + 5))
(self.boot as u32) << (7 + 2)
| (self.awake as u32) << (7 + 3)
| (self.sleep as u32) << (7 + 4)
| (self.shutdown as u32) << (7 + 5)
}
PowerZones::Lid => {
((self.boot as u32) << (15 + 1))
| ((self.awake as u32) << (15 + 2))
| ((self.sleep as u32) << (15 + 3))
| ((self.shutdown as u32) << (15 + 4))
(self.boot as u32) << (15 + 1)
| (self.awake as u32) << (15 + 2)
| (self.sleep as u32) << (15 + 3)
| (self.shutdown as u32) << (15 + 4)
}
PowerZones::RearGlow => {
((self.boot as u32) << (23 + 1))
| ((self.awake as u32) << (23 + 2))
| ((self.sleep as u32) << (23 + 3))
| ((self.shutdown as u32) << (23 + 4))
(self.boot as u32) << (23 + 1)
| (self.awake as u32) << (23 + 2)
| (self.sleep as u32) << (23 + 3)
| (self.shutdown as u32) << (23 + 4)
}
PowerZones::None | PowerZones::KeyboardAndLightbar => 0,
PowerZones::KeyboardAndLightbar | PowerZones::None => 0,
}
}
}
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct LaptopAuraPower {
@@ -192,18 +186,14 @@ impl LaptopAuraPower {
// TODO: use support data to setup correct zones
pub fn new(aura_type: AuraDeviceType, support_data: &LedSupportData) -> Self {
match aura_type {
AuraDeviceType::Unknown | AuraDeviceType::Ally | AuraDeviceType::LaptopKeyboard2021 => {
AuraDeviceType::Unknown | AuraDeviceType::LaptopPost2021 => {
let mut states = Vec::new();
for zone in support_data.power_zones.iter() {
states.push(AuraPowerState::default_for(*zone))
}
Self { states }
}
AuraDeviceType::LaptopKeyboardPre2021 => {
// The older devices are tri-state if have lightbar:
// 1. Keyboard
// 2. Lightbar
// 3. KeyboardAndLightbar
AuraDeviceType::LaptopPre2021 => {
if support_data.power_zones.contains(&PowerZones::Lightbar) {
Self {
states: vec![AuraPowerState::default_for(PowerZones::KeyboardAndLightbar)],
@@ -214,48 +204,23 @@ impl LaptopAuraPower {
}
}
}
AuraDeviceType::LaptopKeyboardTuf => Self {
AuraDeviceType::LaptopTuf => Self {
states: vec![AuraPowerState::default_for(PowerZones::Keyboard)],
},
AuraDeviceType::ScsiExtDisk => todo!(),
AuraDeviceType::AnimeOrSlash => todo!(),
}
}
pub fn to_bytes(&self, aura_type: AuraDeviceType) -> Vec<u8> {
if let Some(stuff) = self.states.first() {
if stuff.zone == PowerZones::Ally {
return vec![
0x5d,
0xd1,
0x09,
0x01,
stuff.new_to_byte() as u8,
];
}
}
match aura_type {
AuraDeviceType::LaptopKeyboard2021 | AuraDeviceType::Ally => self.new_to_bytes(),
AuraDeviceType::LaptopKeyboardPre2021 => {
if self.states.len() == 1 {
self.states
.first()
.cloned()
.unwrap_or_default()
.old_to_bytes()
} else {
let mut bytes: Vec<Vec<u8>> =
self.states.iter().map(|s| s.old_to_bytes()).collect();
let mut b = bytes.pop().unwrap();
for i in bytes {
for (i, n) in i.iter().enumerate() {
b[i] |= n;
}
}
b
}
}
AuraDeviceType::LaptopKeyboardTuf => self
AuraDeviceType::LaptopPost2021 => self.new_to_bytes(),
AuraDeviceType::LaptopPre2021 => self
.states
.first()
.cloned()
.unwrap_or_default()
.old_to_bytes(),
AuraDeviceType::LaptopTuf => self
.states
.first()
.cloned()
@@ -265,8 +230,7 @@ impl LaptopAuraPower {
warn!("Trying to create bytes for an unknown device");
self.new_to_bytes()
}
AuraDeviceType::ScsiExtDisk => todo!("scsi disk not implemented yet"),
AuraDeviceType::AnimeOrSlash => todo!("anime/slash not implemented yet"),
AuraDeviceType::ScsiExtDisk => todo!(),
}
}
}
@@ -315,297 +279,254 @@ mod test {
use crate::keyboard::{AuraPowerState, LaptopAuraPower};
use crate::{AuraDeviceType, PowerZones};
fn to_binary_string_post2021(power: &LaptopAuraPower) -> String {
let bytes = power.to_bytes(AuraDeviceType::LaptopKeyboard2021);
format!(
"{:08b}, {:08b}, {:08b}, {:08b}",
bytes[0], bytes[1], bytes[2], bytes[3]
)
}
#[test]
fn check_0x1866_control_bytes() {
let power = LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Keyboard,
boot: false,
awake: true,
sleep: false,
shutdown: false,
},
],
let state = AuraPowerState {
zone: PowerZones::Keyboard,
awake: true,
boot: false,
sleep: false,
shutdown: false,
};
let bytes = power.to_bytes(AuraDeviceType::LaptopKeyboardPre2021);
let bytes = state.old_to_bytes();
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x08, 0x00, 0x02, 0x00]);
let power = LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Lightbar,
boot: false,
awake: true,
sleep: false,
shutdown: false,
},
],
let state = AuraPowerState {
zone: PowerZones::Lightbar,
awake: true,
boot: false,
sleep: false,
shutdown: false,
};
let bytes = power.to_bytes(AuraDeviceType::LaptopKeyboardPre2021);
let bytes = state.old_to_bytes();
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x04, 0x05, 0x02, 0x00]);
// let bytes = [
// OldAuraPower::Keyboard,
// OldAuraPower::Lightbar,
// OldAuraPower::Awake,
// OldAuraPower::Sleep,
// OldAuraPower::Boot,
// ];
let power = LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Keyboard,
boot: true,
awake: true,
sleep: true,
shutdown: false,
},
AuraPowerState {
zone: PowerZones::Lightbar,
boot: true,
awake: true,
sleep: true,
shutdown: false,
},
],
let bytes = AuraPowerState {
zone: PowerZones::None,
awake: false,
boot: false,
sleep: true,
shutdown: false,
};
let bytes = power.to_bytes(AuraDeviceType::LaptopKeyboardPre2021);
let bytes = bytes.old_to_bytes();
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x30, 0x08, 0x04, 0x00]);
let bytes = AuraPowerState {
zone: PowerZones::None,
awake: false,
boot: true,
sleep: false,
shutdown: false,
};
let bytes = bytes.old_to_bytes();
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0xc3, 0x12, 0x09, 0x00]);
let power = AuraPowerState {
zone: PowerZones::KeyboardAndLightbar,
awake: true,
boot: true,
sleep: true,
shutdown: false,
};
let bytes = power.old_to_bytes();
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0xff, 0x1f, 0x000f, 0x00]);
}
#[test]
fn check_0x19b6_control_bytes_binary_rep() {
let boot_logo_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Logo,
boot: true,
awake: false,
sleep: false,
shutdown: false,
},
],
fn to_binary_string(power: &LaptopAuraPower) -> String {
let bytes = power.to_bytes(AuraDeviceType::LaptopPost2021);
format!(
"{:08b}, {:08b}, {:08b}, {:08b}",
bytes[0], bytes[1], bytes[2], bytes[3]
)
}
let boot_logo_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Logo,
boot: true,
awake: false,
sleep: false,
shutdown: false,
}],
});
let boot_keyb_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Keyboard,
boot: true,
awake: false,
sleep: false,
shutdown: false,
},
],
let boot_keyb_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Keyboard,
boot: true,
awake: false,
sleep: false,
shutdown: false,
}],
});
let sleep_logo = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Logo,
boot: false,
awake: false,
sleep: true,
shutdown: false,
},
],
let sleep_logo = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Logo,
boot: false,
awake: false,
sleep: true,
shutdown: false,
}],
});
let sleep_keyb = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Keyboard,
boot: false,
awake: false,
sleep: true,
shutdown: false,
},
],
let sleep_keyb = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Keyboard,
boot: false,
awake: false,
sleep: true,
shutdown: false,
}],
});
let awake_logo = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Logo,
boot: false,
awake: true,
sleep: false,
shutdown: false,
},
],
let awake_logo = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Logo,
boot: false,
awake: true,
sleep: false,
shutdown: false,
}],
});
let awake_keyb = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Keyboard,
boot: false,
awake: true,
sleep: false,
shutdown: false,
},
],
let awake_keyb = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Keyboard,
boot: false,
awake: true,
sleep: false,
shutdown: false,
}],
});
let shut_logo_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Logo,
boot: false,
awake: false,
sleep: false,
shutdown: true,
},
],
let shut_logo_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Logo,
boot: false,
awake: false,
sleep: false,
shutdown: true,
}],
});
let shut_keyb_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Keyboard,
boot: false,
awake: false,
sleep: false,
shutdown: true,
},
],
let shut_keyb_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Keyboard,
boot: false,
awake: false,
sleep: false,
shutdown: true,
}],
});
let boot_bar__ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Lightbar,
boot: true,
awake: false,
sleep: false,
shutdown: false,
},
],
let boot_bar__ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lightbar,
boot: true,
awake: false,
sleep: false,
shutdown: false,
}],
});
let awake_bar_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Lightbar,
boot: false,
awake: true,
sleep: false,
shutdown: false,
},
],
let awake_bar_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lightbar,
boot: false,
awake: true,
sleep: false,
shutdown: false,
}],
});
let sleep_bar_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Lightbar,
boot: false,
awake: false,
sleep: true,
shutdown: false,
},
],
let sleep_bar_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lightbar,
boot: false,
awake: false,
sleep: true,
shutdown: false,
}],
});
let shut_bar__ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Lightbar,
boot: false,
awake: false,
sleep: false,
shutdown: true,
},
],
let shut_bar__ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lightbar,
boot: false,
awake: false,
sleep: false,
shutdown: true,
}],
});
let boot_lid__ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Lid,
boot: true,
awake: false,
sleep: false,
shutdown: false,
},
],
let boot_lid__ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lid,
boot: true,
awake: false,
sleep: false,
shutdown: false,
}],
});
let awake_lid_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Lid,
boot: false,
awake: true,
sleep: false,
shutdown: false,
},
],
let awake_lid_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lid,
boot: false,
awake: true,
sleep: false,
shutdown: false,
}],
});
let sleep_lid_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Lid,
boot: false,
awake: false,
sleep: true,
shutdown: false,
},
],
let sleep_lid_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lid,
boot: false,
awake: false,
sleep: true,
shutdown: false,
}],
});
let shut_lid__ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Lid,
boot: false,
awake: false,
sleep: false,
shutdown: true,
},
],
let shut_lid__ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lid,
boot: false,
awake: false,
sleep: false,
shutdown: true,
}],
});
let boot_rear_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::RearGlow,
boot: true,
awake: false,
sleep: false,
shutdown: false,
},
],
let boot_rear_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::RearGlow,
boot: true,
awake: false,
sleep: false,
shutdown: false,
}],
});
let awake_rear = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::RearGlow,
boot: false,
awake: true,
sleep: false,
shutdown: false,
},
],
let awake_rear = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::RearGlow,
boot: false,
awake: true,
sleep: false,
shutdown: false,
}],
});
let sleep_rear = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::RearGlow,
boot: false,
awake: false,
sleep: true,
shutdown: false,
},
],
let sleep_rear = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::RearGlow,
boot: false,
awake: false,
sleep: true,
shutdown: false,
}],
});
let shut_rear_ = to_binary_string_post2021(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::RearGlow,
boot: false,
awake: false,
sleep: false,
shutdown: true,
},
],
let shut_rear_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::RearGlow,
boot: false,
awake: false,
sleep: false,
shutdown: true,
}],
});
assert_eq!(boot_logo_, "00000001, 00000000, 00000000, 00000000");
@@ -633,7 +554,7 @@ mod test {
assert_eq!(shut_rear_, "00000000, 00000000, 00000000, 00001000");
// All on
let byte1 = to_binary_string_post2021(&LaptopAuraPower {
let byte1 = to_binary_string(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Keyboard,
+15 -25
View File
@@ -6,6 +6,7 @@
use std::fmt::Debug;
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value};
@@ -23,7 +24,7 @@ pub mod usb;
pub mod keyboard;
pub const AURA_LAPTOP_LED_MSG_LEN: usize = 17;
pub const LED_MSG_LEN: usize = 17;
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub const RED: Colour = Colour {
@@ -61,39 +62,32 @@ pub const ORANGE: Colour = Colour {
g: 0xa4,
b: 0x00,
};
pub const GRADIENT: [Colour; 7] = [
RED, VIOLET, BLUE, TEAL, GREEN, YELLOW, ORANGE,
];
pub const GRADIENT: [Colour; 7] = [RED, VIOLET, BLUE, TEAL, GREEN, YELLOW, ORANGE];
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum AuraDeviceType {
/// Most new laptops
#[default]
LaptopKeyboard2021 = 0,
LaptopKeyboardPre2021 = 1,
LaptopKeyboardTuf = 2,
LaptopPost2021 = 0,
LaptopPre2021 = 1,
LaptopTuf = 2,
ScsiExtDisk = 3,
Ally = 4,
AnimeOrSlash = 5,
Unknown = 255,
}
impl AuraDeviceType {
pub fn is_old_laptop(&self) -> bool {
*self == Self::LaptopKeyboardPre2021
*self == Self::LaptopPre2021
}
pub fn is_tuf_laptop(&self) -> bool {
*self == Self::LaptopKeyboardTuf
*self == Self::LaptopTuf
}
pub fn is_new_laptop(&self) -> bool {
*self == Self::LaptopKeyboard2021
}
pub fn is_ally(&self) -> bool {
*self == Self::Ally
*self == Self::LaptopPost2021
}
pub fn is_scsi(&self) -> bool {
@@ -104,18 +98,16 @@ impl AuraDeviceType {
impl From<&str> for AuraDeviceType {
fn from(s: &str) -> Self {
match s.to_lowercase().trim_start_matches("0x") {
"tuf" => AuraDeviceType::LaptopKeyboardTuf,
"tuf" => AuraDeviceType::LaptopTuf,
"1932" => AuraDeviceType::ScsiExtDisk,
"1866" | "18c6" | "1869" | "1854" => Self::LaptopKeyboardPre2021,
"1abe" | "1b4c" => Self::Ally,
"19b3" | "193b" => Self::AnimeOrSlash,
"19b6" => Self::LaptopKeyboard2021,
_ => Self::Unknown,
"1866" | "18c6" | "1869" | "1854" => Self::LaptopPre2021,
_ => Self::LaptopPost2021,
}
}
}
/// The powerr zones this laptop supports
#[typeshare]
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
@@ -134,9 +126,7 @@ pub enum PowerZones {
Lid = 3,
/// The led strip on the rear of some laptops
RearGlow = 4,
/// Exists for the older 0x1866 models
/// On pre-2021 laptops there is either 1 or 2 zones used
KeyboardAndLightbar = 5,
/// Ally specific for creating correct packet
Ally = 6,
None = 255,
}
+9 -6
View File
@@ -1,7 +1,10 @@
// Only these two packets must be 17 bytes
pub const AURA_LAPTOP_LED_APPLY: [u8; 17] = [
0x5d, 0xb4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
pub const AURA_LAPTOP_LED_SET: [u8; 17] = [
0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
pub const LED_APPLY: [u8; 17] = [0x5d, 0xb4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
pub const LED_SET: [u8; 17] = [0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
/// Writes out the correct byte string for brightness
pub const fn aura_brightness_bytes(brightness: u8) -> [u8; 17] {
[
0x5a, 0xba, 0xc5, 0xc4, brightness, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]
}
+14 -15
View File
@@ -9,17 +9,13 @@ homepage.workspace = true
edition.workspace = true
[features]
default = []
mocking = []
x11 = ["slint/backend-winit-x11"]
# Requires RUSTFLAGS="--cfg tokio_unstable"
tokio-debug = ["console-subscriber"]
#default = ["mocking"]
#mocking = []
[dependencies]
console-subscriber = { version = "^0.4", optional = true }
ksni = { version = "0.3", default-features = false, features = ["async-io"] }
image = "0.25.5"
nix = { version = "^0.28.0", features = ["fs"] }
tempfile = "3.3.0"
betrayer = { version = "0.2.0" }
asusd = { path = "../asusd" }
config-traits = { path = "../config-traits" }
@@ -28,7 +24,7 @@ rog_dbus = { path = "../rog-dbus" }
rog_aura = { path = "../rog-aura" }
rog_profiles = { path = "../rog-profiles" }
rog_platform = { path = "../rog-platform" }
supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", default-features = false }
supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", rev = "4eb6e97c22b68ae8d1e80500709b0c0580776ad3", default-features = false }
dmi_id = { path = "../dmi-id" }
gumdrop.workspace = true
@@ -37,11 +33,11 @@ env_logger.workspace = true
tokio.workspace = true
serde.workspace = true
serde_derive.workspace = true
zbus.workspace = true
dirs.workspace = true
notify-rust.workspace = true
concat-idents.workspace = true
futures-util.workspace = true
versions.workspace = true
@@ -49,13 +45,16 @@ versions.workspace = true
git = "https://github.com/slint-ui/slint.git"
default-features = false
features = [
"compat-1-2",
"gettext",
"accessibility",
"compat-1-2",
"backend-linuxkms",
"backend-winit-wayland",
# "renderer-femtovg",
"renderer-skia-opengl",
"renderer-winit-femtovg",
# "renderer-skia-opengl",
]
[build-dependencies.slint-build]
git = "https://github.com/slint-ui/slint.git"
[dev-dependencies]
cargo-husky.workspace = true
+1 -5
View File
@@ -1,9 +1,5 @@
# ROGALOG
## X11 support
X11 is not supported at all, as in I will not help you with X11 issues if there are any due to limited time and it being unmaintained itself. You can however build `rog-control-center` with it enabled `cargo build --features x11`.
### Translations
You can help with translations by following https://slint.dev/releases/1.1.0/docs/slint/src/concepts/translations#translate-the-strings
@@ -12,4 +8,4 @@ Begin by copying `rog-control-center/translations/en/rog-control-center.po` to `
Run `msgfmt rog-control-center/translations/<YOUR LOCALE>/rog-control-center.po -o rog-control-center/translations/<YOUR LOCALE>/LC_MESSAGES/rog-control-center.mo` to make the binary formatted translation where `<YOUR LOCALE>` is changed to your translation locale.
To test you local translations run `RUST_TRANSLATIONS=1 rog-control-center`.
To test you local translations run `RUST_TRANSLATIONS=1 rog-control-center`.
+3 -1
View File
@@ -5,6 +5,7 @@ use slint_build::CompilerConfiguration;
fn main() {
// write_locales();
let root = env!("CARGO_MANIFEST_DIR");
let mut main = PathBuf::from_str(root).unwrap();
main.push("ui/main_window.slint");
@@ -13,12 +14,13 @@ fn main() {
include.push("ui");
slint_build::print_rustc_flags().unwrap();
// slint_build::compile("ui/main_window.slint").unwrap();
slint_build::compile_with_config(
main,
CompilerConfiguration::new()
// .embed_resources(EmbedResourcesKind::EmbedFiles)
.with_include_paths(vec![include])
.with_style("fluent".into()),
.with_style("fluent-dark".into()),
)
.unwrap();
}
@@ -1,6 +1,7 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=ROG Control Center
Comment=Make your ASUS ROG Laptop go Brrrrr!
Categories=Settings
+1 -1
View File
@@ -1,7 +1,7 @@
use std::fs::create_dir;
use config_traits::{StdConfig, StdConfigLoad1};
use serde::{Deserialize, Serialize};
use serde_derive::{Deserialize, Serialize};
use crate::notify::EnabledNotifications;
+8
View File
@@ -5,6 +5,7 @@ pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
Io(std::io::Error),
Nix(nix::Error),
ConfigLoadFail,
ConfigLockFail,
XdgVars,
@@ -17,6 +18,7 @@ impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Io(err) => write!(f, "Failed to open: {}", err),
Error::Nix(err) => write!(f, "Error: {}", 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"),
@@ -34,6 +36,12 @@ impl From<std::io::Error> for Error {
}
}
impl From<nix::Error> for Error {
fn from(err: nix::Error) -> Self {
Error::Nix(err)
}
}
impl From<zbus::Error> for Error {
fn from(err: zbus::Error) -> Self {
Error::Zbus(err)
+73 -3
View File
@@ -2,8 +2,14 @@
#![allow(clippy::redundant_clone, clippy::cmp_owned)]
slint::include_modules!();
/// Intentionally reexport slint so that GUI consumers don't need to add to
/// `Cargo.toml`
// Intentionally reexport slint so that GUI consumers don't need to add to
// `Cargo.toml`
use std::fs::{remove_dir_all, File, OpenOptions};
use std::io::{Read, Write};
use std::process::exit;
use std::thread::sleep;
use std::time::Duration;
pub use slint;
pub mod cli_options;
@@ -15,7 +21,11 @@ pub mod notify;
pub mod tray;
pub mod types;
pub mod ui;
pub mod zbus_proxies;
use nix::sys::stat;
use nix::unistd;
use tempfile::TempDir;
// use log::{error, info, warn};
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub const APP_ICON_PATH: &str = "/usr/share/icons/hicolor/512x512/apps/rog-control-center.png";
@@ -32,6 +42,10 @@ pub fn print_versions() {
println!("rog-platform v{}", rog_platform::VERSION);
}
pub const SHOWING_GUI: u8 = 1;
pub const SHOW_GUI: u8 = 2;
pub const QUIT_APP: u8 = 3;
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum Page {
AppSettings,
@@ -40,3 +54,59 @@ pub enum Page {
AnimeMatrix,
FanCurves,
}
/// Either exit the process, or return with a refreshed tmp-dir
pub fn on_tmp_dir_exists() -> Result<TempDir, std::io::Error> {
let mut buf = [0u8; 2];
let path = std::env::temp_dir().join("rog-gui");
if path.read_dir()?.next().is_none() {
std::fs::remove_dir_all(path)?;
return tempfile::Builder::new()
.prefix("rog-gui")
.rand_bytes(0)
.tempdir();
}
let mut ipc_file = OpenOptions::new()
.read(true)
.write(true)
.create(false)
.open(path.join("ipc.pipe"))?;
// If the app is running this ends up stacked on top of SHOWING_GUI
ipc_file.write_all(&[SHOW_GUI, 0])?;
// tiny sleep to give the app a chance to respond
sleep(Duration::from_millis(10));
ipc_file.read_exact(&mut buf).ok();
// First entry is the actual state
if buf[0] == SHOWING_GUI {
ipc_file.write_all(&[SHOWING_GUI, 0])?; // Store state again as we drained the fifo
// Early exit is not an error and we don't want to pass back a dir
#[allow(clippy::exit)]
exit(0);
} else if buf[0] == SHOW_GUI {
remove_dir_all(&path)?;
return tempfile::Builder::new()
.prefix("rog-gui")
.rand_bytes(0)
.tempdir();
}
panic!("Invalid exit or app state");
}
pub fn get_ipc_file() -> Result<File, crate::error::Error> {
let tmp_dir = std::env::temp_dir().join("rog-gui");
let fifo_path = tmp_dir.join("ipc.pipe");
if let Err(e) = unistd::mkfifo(&fifo_path, stat::Mode::S_IRWXU) {
if !matches!(e, nix::errno::Errno::EEXIST) {
Err(e)?
}
}
Ok(OpenOptions::new()
.read(true)
.write(true)
// .truncate(true)
.open(&fifo_path)?)
}
+129 -123
View File
@@ -1,4 +1,6 @@
use std::env::{self, args};
use std::borrow::BorrowMut;
use std::env::args;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::process::exit;
use std::sync::{Arc, Mutex};
@@ -8,7 +10,7 @@ use std::time::Duration;
use config_traits::{StdConfig, StdConfigLoad1};
use dmi_id::DMIID;
use gumdrop::Options;
use log::{info, warn, LevelFilter};
use log::{info, LevelFilter};
use rog_control_center::cli_options::CliStart;
use rog_control_center::config::Config;
use rog_control_center::error::Result;
@@ -16,83 +18,39 @@ use rog_control_center::notify::start_notifications;
use rog_control_center::slint::ComponentHandle;
use rog_control_center::tray::init_tray;
use rog_control_center::ui::setup_window;
use rog_control_center::zbus_proxies::{
AppState, ROGCCZbus, ROGCCZbusProxyBlocking, ZBUS_IFACE, ZBUS_PATH,
use rog_control_center::{
get_ipc_file, on_tmp_dir_exists, print_versions, MainWindow, QUIT_APP, SHOWING_GUI, SHOW_GUI,
};
use rog_control_center::{print_versions, MainWindow};
use tokio::runtime::Runtime;
// use winit::monitor::VideoMode;
// use winit::window::{Fullscreen, WindowLevel};
#[tokio::main]
async fn main() -> Result<()> {
let mut logger = env_logger::Builder::new();
logger
.filter_level(LevelFilter::Warn)
.parse_default_env()
.target(env_logger::Target::Stdout)
.format_timestamp(None)
.init();
// If we're running under gamescope we have to set WAYLAND_DISPLAY for winit to
// use
if let Ok(gamescope) = env::var("GAMESCOPE_WAYLAND_DISPLAY") {
if !gamescope.is_empty() {
env::set_var("WAYLAND_DISPLAY", gamescope);
}
}
// Try to open a proxy and check for app state first
{
let user_con = zbus::blocking::Connection::session()?;
if let Ok(proxy) = ROGCCZbusProxyBlocking::new(&user_con) {
if let Ok(state) = proxy.state() {
info!("App is already running: {state:?}, opening the window");
// if there is a proxy connection assume the app is already running
proxy.set_state(AppState::MainWindowShouldOpen)?;
std::process::exit(0);
}
}
}
// version checks
let self_version = env!("CARGO_PKG_VERSION");
let zbus_con = zbus::blocking::Connection::system()?;
let platform_proxy = rog_dbus::zbus_platform::PlatformProxyBlocking::new(&zbus_con)?;
let asusd_version = platform_proxy
.version()
.map_err(|e| {
println!("Could not get asusd version: {e:?}\nIs asusd.service running?");
})
.unwrap();
let conn = zbus::blocking::Connection::system()?;
let proxy = rog_dbus::zbus_platform::PlatformProxyBlocking::new(&conn)?;
let asusd_version = proxy.version().unwrap();
if asusd_version != self_version {
println!("Version mismatch: asusctl = {self_version}, asusd = {asusd_version}");
return Ok(());
}
// start tokio
let rt = Runtime::new().expect("Unable to create Runtime");
// Enter the runtime so that `tokio::spawn` is available immediately.
let _enter = rt.enter();
#[cfg(feature = "tokio-debug")]
console_subscriber::init();
let state_zbus = ROGCCZbus::new();
let app_state = state_zbus.clone_state();
let _conn = zbus::connection::Builder::session()?
.name(ZBUS_IFACE)?
.serve_at(ZBUS_PATH, state_zbus)?
.build()
.await
.map_err(|err| {
warn!("{}: add_to_server {}", ZBUS_PATH, err);
err
})?;
let dmi = DMIID::new().unwrap_or_default();
let board_name = dmi.board_name;
let prod_family = dmi.product_family;
info!("Running on {board_name}, product: {prod_family}");
let is_rog_ally = prod_family == "RC71L" || prod_family == "RC72L";
let is_rog_ally = prod_family == "RC71L";
// tmp-dir must live to the end of program life
let _tmp_dir = match tempfile::Builder::new()
.prefix("rog-gui")
.rand_bytes(0)
.tempdir()
{
Ok(tmp) => tmp,
Err(_) => on_tmp_dir_exists().unwrap(),
};
let args: Vec<String> = args().skip(1).collect();
@@ -107,7 +65,26 @@ async fn main() -> Result<()> {
return Ok(());
}
let supported_properties = platform_proxy.supported_properties().unwrap_or_default();
let mut logger = env_logger::Builder::new();
logger
.filter_level(LevelFilter::Warn)
.parse_default_env()
.target(env_logger::Target::Stdout)
.format_timestamp(None)
.init();
// start tokio
let rt = Runtime::new().expect("Unable to create Runtime");
// Enter the runtime so that `tokio::spawn` is available immediately.
let _enter = rt.enter();
let supported_properties = match proxy.supported_properties() {
Ok(s) => s,
Err(_e) => {
// TODO: show an error window
Vec::default()
}
};
// Startup
let mut config = Config::new().load();
@@ -128,16 +105,19 @@ async fn main() -> Result<()> {
config.enable_tray_icon = false;
config.run_in_background = false;
config.startup_in_background = false;
config.start_fullscreen = true;
}
if config.startup_in_background {
config.run_in_background = true;
} else {
get_ipc_file().unwrap().write_all(&[SHOW_GUI, 0]).unwrap();
}
config.write();
let enable_tray_icon = config.enable_tray_icon;
let startup_in_background = config.startup_in_background;
let config = Arc::new(Mutex::new(config));
start_notifications(config.clone(), &rt)?;
start_notifications(config.clone())?;
if enable_tray_icon {
init_tray(supported_properties, config.clone());
}
@@ -145,11 +125,7 @@ async fn main() -> Result<()> {
thread_local! { pub static UI: std::cell::RefCell<Option<MainWindow>> = Default::default()};
// i_slint_backend_selector::with_platform(|_| Ok(())).unwrap();
if !startup_in_background {
if let Ok(mut app_state) = app_state.lock() {
*app_state = AppState::MainWindowShouldOpen;
}
}
let mut do_once = !startup_in_background;
if std::env::var("RUST_TRANSLATIONS").is_ok() {
// don't care about content
@@ -161,86 +137,116 @@ async fn main() -> Result<()> {
}
thread::spawn(move || {
let mut state = AppState::StartingUp;
let mut buf = [0u8; 2];
// blocks until it is read, typically the read will happen after a second
// process writes to the IPC (so there is data to actually read)
loop {
// save as a var, don't hold the lock the entire time or deadlocks happen
if let Ok(app_state) = app_state.lock() {
state = *app_state;
if do_once {
buf[0] = SHOW_GUI;
do_once = false;
} else {
get_ipc_file().unwrap().read_exact(&mut buf).unwrap();
}
// This sleep is required to give the event loop time to react
sleep(Duration::from_millis(300));
if state == AppState::MainWindowShouldOpen {
if let Ok(mut app_state) = app_state.lock() {
*app_state = AppState::MainWindowOpen;
}
if buf[0] == SHOW_GUI {
// There's a balancing act with read/write timing of IPC, there needs to be a
// small sleep after this to give any other process a chance to
// read the IPC before looping
get_ipc_file()
.unwrap()
.write_all(&[SHOWING_GUI, 0])
.unwrap();
sleep(Duration::from_millis(50));
let config_copy = config.clone();
let app_state_copy = app_state.clone();
slint::invoke_from_event_loop(move || {
UI.with(|ui| {
let app_state_copy = app_state_copy.clone();
let mut ui = ui.borrow_mut();
if let Some(ui) = ui.as_mut() {
ui.window().show().unwrap();
ui.window().on_close_requested(move || {
if let Ok(mut app_state) = app_state_copy.lock() {
*app_state = AppState::MainWindowClosed;
}
ui.window().on_close_requested(|| {
get_ipc_file().unwrap().write_all(&[0, 0]).unwrap();
slint::CloseRequestResponse::HideWindow
});
} else {
let config_copy_2 = config_copy.clone();
let newui = setup_window(config_copy);
newui.window().on_close_requested(move || {
if let Ok(mut app_state) = app_state_copy.lock() {
*app_state = AppState::MainWindowClosed;
}
newui.window().show().unwrap();
newui.window().on_close_requested(|| {
get_ipc_file().unwrap().write_all(&[0, 0]).unwrap();
slint::CloseRequestResponse::HideWindow
});
let ui_copy = newui.as_weak();
newui
.window()
.set_rendering_notifier(move |s, _| {
if let slint::RenderingState::RenderingSetup = s {
let config = config_copy_2.clone();
ui_copy
.upgrade_in_event_loop(move |w| {
let fullscreen =
config.lock().is_ok_and(|c| c.start_fullscreen);
if fullscreen && !w.window().is_fullscreen() {
w.window().set_fullscreen(fullscreen);
}
})
.ok();
}
})
.ok();
ui.replace(newui);
}
});
})
.unwrap();
} else if state == AppState::QuitApp {
slint::quit_event_loop().unwrap();
exit(0);
} else if state != AppState::MainWindowOpen {
if let Ok(config) = config.lock() {
if !config.run_in_background {
slint::quit_event_loop().unwrap();
exit(0);
} else {
if buf[1] == QUIT_APP {
slint::quit_event_loop().unwrap();
exit(0);
}
if buf[0] != SHOWING_GUI {
if let Ok(lock) = config.lock() {
if !lock.run_in_background {
slint::quit_event_loop().unwrap();
exit(0);
}
}
slint::invoke_from_event_loop(move || {
UI.with(|ui| {
let mut ui = ui.take();
if let Some(ui) = ui.borrow_mut() {
ui.window().hide().unwrap();
}
});
})
.unwrap();
}
}
}
});
slint::run_event_loop_until_quit().unwrap();
rt.shutdown_background();
Ok(())
}
// /// Bah.. the icon dosn't work on wayland anyway, but we'll leave it in for
// now. fn load_icon() -> IconData {
// let path = PathBuf::from(APP_ICON_PATH);
// let mut rgba = Vec::new();
// let mut height = 512;
// let mut width = 512;
// if path.exists() {
// if let Ok(data) = std::fs::read(path)
// .map_err(|e| error!("Error reading app icon: {e:?}"))
// .map_err(|e| error!("Error opening app icon: {e:?}"))
// {
// let data = std::io::Cursor::new(data);
// let decoder = png_pong::Decoder::new(data).unwrap().into_steps();
// let png_pong::Step { raster, delay: _ } =
// decoder.last().unwrap().unwrap();
// if let png_pong::PngRaster::Rgba8(ras) = raster {
// rgba = ras.as_u8_slice().to_vec();
// width = ras.width();
// height = ras.height();
// info!("Loaded app icon. Not actually supported in Wayland
// yet"); }
// }
// } else {
// error!("Missing {APP_ICON_PATH}");
// }
// IconData {
// height,
// width,
// rgba
//
//
// / }
// }
fn do_cli_help(parsed: &CliStart) -> bool {
if parsed.help {
println!("{}", CliStart::usage());
+4 -12
View File
@@ -125,18 +125,10 @@ impl Profile {
pub fn fan_curve_data(&self, _p: rog_profiles::Profile) -> Result<FanCurveSet> {
let mut curve = FanCurveSet::default();
curve.cpu.pwm = [
30, 40, 60, 100, 140, 180, 200, 250,
];
curve.cpu.temp = [
20, 30, 40, 50, 70, 80, 90, 100,
];
curve.gpu.pwm = [
40, 80, 100, 140, 170, 200, 230, 250,
];
curve.gpu.temp = [
20, 30, 40, 50, 70, 80, 90, 100,
];
curve.cpu.pwm = [30, 40, 60, 100, 140, 180, 200, 250];
curve.cpu.temp = [20, 30, 40, 50, 70, 80, 90, 100];
curve.gpu.pwm = [40, 80, 100, 140, 170, 200, 230, 250];
curve.gpu.temp = [20, 30, 40, 50, 70, 80, 90, 100];
Ok(curve)
}
+119 -156
View File
@@ -9,17 +9,17 @@ use std::process::Command;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use futures_util::StreamExt;
use log::{debug, error, info, warn};
use notify_rust::{Hint, Notification, Timeout, Urgency};
use log::{error, info, warn};
use notify_rust::{Hint, Notification, NotificationHandle, Urgency};
use rog_dbus::zbus_platform::PlatformProxy;
use rog_platform::platform::GpuMode;
use rog_platform::power::AsusPower;
use serde::{Deserialize, Serialize};
use supergfxctl::actions::UserActionRequired as GfxUserAction;
use supergfxctl::pci_device::{GfxMode, GfxPower};
use supergfxctl::zbus_proxy::DaemonProxy as SuperProxy;
use tokio::runtime::Runtime;
use tokio::task::JoinHandle;
use tokio::time::sleep;
use zbus::export::futures_util::StreamExt;
use crate::config::Config;
use crate::error::Result;
@@ -44,59 +44,10 @@ impl Default for EnabledNotifications {
}
}
fn start_dpu_status_mon(config: Arc<Mutex<Config>>) {
use supergfxctl::pci_device::Device;
let dev = Device::find().unwrap_or_default();
let mut found_dgpu = false; // just for logging
for dev in dev {
if dev.is_dgpu() {
info!(
"Found dGPU: {}, starting status notifications",
dev.pci_id()
);
let enabled_notifications_copy = config.clone();
// Plain old thread is perfectly fine since most of this is potentially blocking
std::thread::spawn(move || {
let mut last_status = GfxPower::Unknown;
loop {
std::thread::sleep(Duration::from_millis(1500));
if let Ok(status) = dev.get_runtime_status() {
if status != GfxPower::Unknown && status != last_status {
if let Ok(config) = enabled_notifications_copy.lock() {
if !config.notifications.receive_notify_gfx_status
|| !config.notifications.enabled
{
continue;
}
}
// Required check because status cycles through
// active/unknown/suspended
do_gpu_status_notif("dGPU status changed:", &status)
.show()
.unwrap()
.on_close(|_| ());
debug!("dGPU status changed: {:?}", &status);
}
last_status = status;
}
}
});
found_dgpu = true;
break;
}
}
if !found_dgpu {
warn!("Did not find a dGPU on this system, dGPU status won't be avilable");
}
}
pub fn start_notifications(
config: Arc<Mutex<Config>>,
rt: &Runtime,
) -> Result<Vec<JoinHandle<()>>> {
pub fn start_notifications(config: Arc<Mutex<Config>>) -> Result<()> {
// Setup the AC/BAT commands that will run on power status change
let config_copy = config.clone();
let blocking = rt.spawn_blocking(move || {
tokio::task::spawn_blocking(move || {
let power = AsusPower::new()
.map_err(|e| {
error!("AsusPower: {e}");
@@ -145,66 +96,106 @@ pub fn start_notifications(
}
});
let enabled_notifications_copy = config.clone();
let no_supergfx = move |e: &zbus::Error| {
error!("zbus signal: receive_notify_gfx_status: {e}");
warn!("Attempting to start plain dgpu status monitor");
start_dpu_status_mon(enabled_notifications_copy.clone());
};
// GPU MUX Mode notif
// TODO: need to get armoury attrs and iter to find
// let enabled_notifications_copy = config.clone();
// tokio::spawn(async move {
// let conn = zbus::Connection::system().await.map_err(|e| {
// error!("zbus signal: receive_notify_gpu_mux_mode: {e}");
// e
// })?;
// let proxy = PlatformProxy::new(&conn).await.map_err(|e| {
// error!("zbus signal: receive_notify_gpu_mux_mode: {e}");
// e
// })?;
// let mut actual_mux_mode = GpuMode::Error;
// if let Ok(mode) = proxy.gpu_mux_mode().await {
// actual_mux_mode = GpuMode::from(mode);
// }
// info!("Started zbus signal thread: receive_notify_gpu_mux_mode");
// while let Some(e) =
// proxy.receive_gpu_mux_mode_changed().await.next().await { if let
// Ok(config) = enabled_notifications_copy.lock() { if
// !config.notifications.enabled || !config.notifications.receive_notify_gfx {
// continue;
// }
// }
// if let Ok(out) = e.get().await {
// let mode = GpuMode::from(out);
// if mode == actual_mux_mode {
// continue;
// }
// do_mux_notification("Reboot required. BIOS GPU MUX mode set to",
// &mode).ok(); }
// }
// Ok::<(), zbus::Error>(())
// });
let enabled_notifications_copy = config.clone();
tokio::spawn(async move {
let conn = zbus::Connection::system()
.await
.map_err(|e| {
error!("zbus signal: receive_notify_gpu_mux_mode: {e}");
e
})
.unwrap();
let proxy = PlatformProxy::new(&conn)
.await
.map_err(|e| {
error!("zbus signal: receive_notify_gpu_mux_mode: {e}");
e
})
.unwrap();
let mut actual_mux_mode = GpuMode::Error;
if let Ok(mode) = proxy.gpu_mux_mode().await {
actual_mux_mode = GpuMode::from(mode);
}
info!("Started zbus signal thread: receive_notify_gpu_mux_mode");
while let Some(e) = proxy.receive_gpu_mux_mode_changed().await.next().await {
if let Ok(config) = enabled_notifications_copy.lock() {
if !config.notifications.enabled || !config.notifications.receive_notify_gfx {
continue;
}
}
if let Ok(out) = e.get().await {
let mode = GpuMode::from(out);
if mode == actual_mux_mode {
continue;
}
do_mux_notification("Reboot required. BIOS GPU MUX mode set to", &mode).ok();
}
}
});
use supergfxctl::pci_device::Device;
let dev = Device::find().unwrap_or_default();
let mut found_dgpu = false; // just for logging
for dev in dev {
if dev.is_dgpu() {
let enabled_notifications_copy = config.clone();
// Plain old thread is perfectly fine since most of this is potentially blocking
tokio::spawn(async move {
let mut last_status = GfxPower::Unknown;
loop {
if let Ok(status) = dev.get_runtime_status() {
if status != GfxPower::Unknown && status != last_status {
if let Ok(config) = enabled_notifications_copy.lock() {
if !config.notifications.receive_notify_gfx_status
|| !config.notifications.enabled
{
continue;
}
}
// Required check because status cycles through
// active/unknown/suspended
do_gpu_status_notif("dGPU status changed:", &status).ok();
}
last_status = status;
}
sleep(Duration::from_millis(500)).await;
}
});
found_dgpu = true;
break;
}
}
if !found_dgpu {
warn!("Did not find a dGPU on this system, dGPU status won't be avilable");
}
// GPU Mode change/action notif
tokio::spawn(async move {
let conn = zbus::Connection::system().await.inspect_err(|e| {
no_supergfx(e);
})?;
let proxy = SuperProxy::builder(&conn).build().await.inspect_err(|e| {
no_supergfx(e);
})?;
let _ = proxy.mode().await.inspect_err(|e| {
no_supergfx(e);
})?;
let conn = zbus::Connection::system()
.await
.map_err(|e| {
error!("zbus signal: receive_notify_action: {e}");
e
})
.unwrap();
let proxy = SuperProxy::builder(&conn)
.build()
.await
.map_err(|e| {
error!("zbus signal: receive_notify_action: {e}");
e
})
.unwrap();
let proxy_copy = proxy.clone();
let mut p = proxy.receive_notify_action().await?;
tokio::spawn(async move {
if proxy.mode().await.is_err() {
info!("supergfxd not running or not responding");
return;
}
if let Ok(mut p) = proxy.receive_notify_action().await {
info!("Started zbus signal thread: receive_notify_action");
while let Some(e) = p.next().await {
if let Ok(out) = e.args() {
@@ -223,39 +214,10 @@ pub fn start_notifications(
.ok();
}
}
});
let mut p = proxy_copy.receive_notify_gfx_status().await?;
tokio::spawn(async move {
info!("Started zbus signal thread: receive_notify_gfx_status");
let mut last_status = GfxPower::Unknown;
while let Some(e) = p.next().await {
if let Ok(out) = e.args() {
let status = out.status;
if status != GfxPower::Unknown && status != last_status {
if let Ok(config) = enabled_notifications_copy.lock() {
if !config.notifications.receive_notify_gfx_status
|| !config.notifications.enabled
{
continue;
}
}
// Required check because status cycles through
// active/unknown/suspended
do_gpu_status_notif("dGPU status changed:", &status)
.show_async()
.await
.unwrap()
.on_close(|_| ());
}
last_status = status;
}
}
});
Ok::<(), zbus::Error>(())
};
});
Ok(vec![blocking])
Ok(())
}
fn convert_gfx_mode(gfx: GfxMode) -> GpuMode {
@@ -275,15 +237,19 @@ where
T: Display,
{
let mut notif = Notification::new();
notif
.appname(NOTIF_HEADER)
.summary(&format!("{message} {data}"))
.timeout(Timeout::Milliseconds(3000))
.summary(NOTIF_HEADER)
.body(&format!("{message} {data}"))
.timeout(-1)
//.hint(Hint::Resident(true))
.hint(Hint::Category("device".into()));
notif
}
fn do_gpu_status_notif(message: &str, data: &GfxPower) -> Notification {
fn do_gpu_status_notif(message: &str, data: &GfxPower) -> Result<NotificationHandle> {
// eww
let mut notif = base_notification(message, &<&str>::from(data).to_owned());
let icon = match data {
GfxPower::Suspended => "asus_notif_blue",
@@ -293,7 +259,7 @@ fn do_gpu_status_notif(message: &str, data: &GfxPower) -> Notification {
GfxPower::Unknown => "gpu-integrated",
};
notif.icon(icon);
notif
Ok(Notification::show(&notif)?)
}
fn do_gfx_action_notif(message: &str, action: GfxUserAction, mode: GpuMode) -> Result<()> {
@@ -304,12 +270,13 @@ fn do_gfx_action_notif(message: &str, action: GfxUserAction, mode: GpuMode) -> R
let mut notif = Notification::new();
notif
.appname(NOTIF_HEADER)
.summary(&format!("Changing to {mode}. {message}"))
.summary(NOTIF_HEADER)
.body(&format!("Changing to {mode}. {message}"))
.timeout(2000)
//.hint(Hint::Resident(true))
.hint(Hint::Category("device".into()))
.urgency(Urgency::Critical)
.timeout(Timeout::Never)
.timeout(-1)
.icon("dialog-warning")
.hint(Hint::Transient(true));
@@ -330,9 +297,7 @@ fn do_gfx_action_notif(message: &str, action: GfxUserAction, mode: GpuMode) -> R
handle.wait_for_action(|id| {
if id == "gfx-mode-session-action" {
let mut cmd = Command::new("qdbus");
cmd.args([
"org.kde.ksmserver", "/KSMServer", "logout", "1", "0", "0",
]);
cmd.args(["org.kde.ksmserver", "/KSMServer", "logout", "1", "0", "0"]);
cmd.spawn().ok();
} else if id == "__closed" {
// TODO: cancel the switching
@@ -374,9 +339,7 @@ fn do_mux_notification(message: &str, m: &GpuMode) -> Result<()> {
handle.wait_for_action(|id| {
if id == "gfx-mode-session-action" {
let mut cmd = Command::new("qdbus");
cmd.args([
"org.kde.ksmserver", "/KSMServer", "logout", "1", "1", "0",
]);
cmd.args(["org.kde.ksmserver", "/KSMServer", "logout", "1", "1", "0"]);
cmd.spawn().ok();
} else if id == "__closed" {
// TODO: cancel the switching
+100 -164
View File
@@ -1,20 +1,23 @@
//! A self-contained tray icon with menus.
//! A seld-contained tray icon with menus. The control of app<->tray is done via
//! commands over an MPSC channel.
use std::fs::OpenOptions;
use std::io::Read;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::process::exit;
use std::sync::{Arc, Mutex, OnceLock};
use std::thread::sleep;
use std::time::Duration;
use ksni::{Handle, Icon, TrayMethods};
use log::{info, warn};
use betrayer::{Icon, Menu, MenuItem, TrayEvent, TrayIcon, TrayIconBuilder};
use log::{debug, error, info, warn};
use rog_platform::platform::Properties;
use supergfxctl::pci_device::{Device, GfxMode, GfxPower};
use supergfxctl::zbus_proxy::DaemonProxy as GfxProxy;
use supergfxctl::pci_device::{GfxMode, GfxPower};
use supergfxctl::zbus_proxy::DaemonProxyBlocking as GfxProxy;
use versions::Versioning;
use crate::config::Config;
use crate::zbus_proxies::{AppState, ROGCCZbusProxyBlocking};
use crate::{get_ipc_file, QUIT_APP, SHOW_GUI};
const TRAY_LABEL: &str = "ROG Control Center";
const TRAY_ICON_PATH: &str = "/usr/share/icons/hicolor/512x512/apps/";
@@ -29,83 +32,65 @@ struct Icons {
static ICONS: OnceLock<Icons> = OnceLock::new();
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
enum TrayAction {
Open,
Quit,
}
fn open_app() {
if let Ok(mut ipc) = get_ipc_file().map_err(|e| {
error!("ROGTray: get_ipc_file: {}", e);
}) {
debug!("Tray told app to show self");
ipc.write_all(&[SHOW_GUI, 0]).ok();
}
}
fn quit_app() {
if let Ok(mut ipc) = get_ipc_file().map_err(|e| {
error!("ROGTray: get_ipc_file: {}", e);
}) {
debug!("Tray told app to show self");
ipc.write_all(&[QUIT_APP, 0]).ok();
}
}
fn read_icon(file: &Path) -> Icon {
let mut path = PathBuf::from(TRAY_ICON_PATH);
path.push(file);
let mut file = OpenOptions::new()
.read(true)
.open(&path)
.unwrap_or_else(|_| panic!("Missing icon: {:?}", path));
let mut file = OpenOptions::new().read(true).open(path).unwrap();
let mut bytes = Vec::new();
file.read_to_end(&mut bytes).unwrap();
let mut img = image::load_from_memory_with_format(&bytes, image::ImageFormat::Png)
.expect("icon not found")
.to_rgba8();
for image::Rgba(pixel) in img.pixels_mut() {
// (╯°□°)╯︵ ┻━┻
*pixel = u32::from_be_bytes(*pixel).rotate_right(8).to_be_bytes();
}
let (width, height) = img.dimensions();
Icon {
width: width as i32,
height: height as i32,
data: img.into_raw(),
}
Icon::from_png_bytes(&bytes)
.unwrap_or(Icon::from_rgba(vec![255u8; 32 * 32 * 4], 32, 32).unwrap())
}
struct AsusTray {
current_title: String,
current_icon: Icon,
proxy: ROGCCZbusProxyBlocking<'static>,
fn build_menu() -> Menu<TrayAction> {
Menu::new([
MenuItem::separator(),
MenuItem::button("Open", TrayAction::Open),
MenuItem::button("Quit", TrayAction::Quit),
])
}
impl ksni::Tray for AsusTray {
fn id(&self) -> String {
TRAY_LABEL.into()
}
fn icon_pixmap(&self) -> Vec<ksni::Icon> {
vec![self.current_icon.clone()]
}
fn title(&self) -> String {
self.current_title.clone()
}
fn status(&self) -> ksni::Status {
ksni::Status::Active
}
fn menu(&self) -> Vec<ksni::MenuItem<Self>> {
use ksni::menu::*;
vec![
StandardItem {
label: "Open ROGCC".into(),
icon_name: "rog-control-center".into(),
activate: Box::new(move |s: &mut AsusTray| {
s.proxy.set_state(AppState::MainWindowShouldOpen).ok();
}),
..Default::default()
fn do_action(event: TrayEvent<TrayAction>) {
if let TrayEvent::Menu(action) = event {
match action {
TrayAction::Open => open_app(),
TrayAction::Quit => {
quit_app();
exit(0);
}
.into(),
MenuItem::Separator,
StandardItem {
label: "Quit ROGCC".into(),
icon_name: "application-exit".into(),
activate: Box::new(|_| std::process::exit(0)),
..Default::default()
}
.into(),
]
}
}
}
async fn set_tray_icon_and_tip(
fn set_tray_icon_and_tip(
mode: GfxMode,
power: GfxPower,
tray: &mut Handle<AsusTray>,
tray: &mut TrayIcon<TrayAction>,
supergfx_active: bool,
) {
if let Some(icons) = ICONS.get() {
@@ -128,124 +113,75 @@ async fn set_tray_icon_and_tip(
}
}
};
// *tray = TrayIconBuilder::<TrayAction>::new()
// .with_icon(icon)
// .with_tooltip(format!("ROG: gpu mode = {mode:?}, gpu power = {power:?}"))
// .with_menu(build_menu())
// .build(do_action)
// .map_err(|e| log::error!("Tray unable to be initialised: {e:?}"))
// .unwrap();
tray.update(|tray: &mut AsusTray| {
tray.current_icon = icon;
tray.current_title = format!(
"ROG: gpu mode = {mode:?}, gpu power =
{power:?}"
);
})
.await;
tray.set_icon(Some(icon));
tray.set_tooltip(format!("ROG: gpu mode = {mode:?}, gpu power = {power:?}"));
}
}
fn find_dgpu() -> Option<Device> {
use supergfxctl::pci_device::Device;
let dev = Device::find().unwrap_or_default();
for dev in dev {
if dev.is_dgpu() {
info!("Found dGPU: {}", dev.pci_id());
// Plain old thread is perfectly fine since most of this is potentially blocking
return Some(dev);
}
}
warn!("Did not find a dGPU on this system, dGPU status won't be avilable");
None
}
/// The tray is controlled somewhat by `Arc<Mutex<SystemState>>`
pub fn init_tray(_supported_properties: Vec<Properties>, config: Arc<Mutex<Config>>) {
tokio::spawn(async move {
let user_con = zbus::blocking::Connection::session().unwrap();
let proxy = ROGCCZbusProxyBlocking::new(&user_con).unwrap();
std::thread::spawn(move || {
let rog_red = read_icon(&PathBuf::from("asus_notif_red.png"));
let tray_init = AsusTray {
current_title: TRAY_LABEL.to_string(),
current_icon: rog_red.clone(),
proxy,
};
if let Ok(mut tray) = TrayIconBuilder::<TrayAction>::new()
.with_icon(rog_red.clone())
.with_tooltip(TRAY_LABEL)
.with_menu(build_menu())
.build(do_action)
.map_err(|e| log::error!("Tray unable to be initialised: {e:?}"))
{
info!("Tray started");
let rog_blue = read_icon(&PathBuf::from("asus_notif_blue.png"));
let rog_green = read_icon(&PathBuf::from("asus_notif_green.png"));
let rog_white = read_icon(&PathBuf::from("asus_notif_white.png"));
let gpu_integrated = read_icon(&PathBuf::from("rog-control-center.png"));
ICONS.get_or_init(|| Icons {
rog_blue,
rog_red: rog_red.clone(),
rog_green,
rog_white,
gpu_integrated,
});
// TODO: return an error to the UI
let mut tray;
match tray_init.spawn_without_dbus_name().await {
Ok(t) => tray = t,
Err(e) => {
log::error!(
"Tray unable to be initialised: {e:?}. Do you have a system tray enabled?"
);
return;
}
}
info!("Tray started");
let rog_blue = read_icon(&PathBuf::from("asus_notif_blue.png"));
let rog_green = read_icon(&PathBuf::from("asus_notif_green.png"));
let rog_white = read_icon(&PathBuf::from("asus_notif_white.png"));
let gpu_integrated = read_icon(&PathBuf::from("rog-control-center.png"));
ICONS.get_or_init(|| Icons {
rog_blue,
rog_red: rog_red.clone(),
rog_green,
rog_white,
gpu_integrated,
});
let mut has_supergfx = false;
let conn = zbus::Connection::system().await.unwrap();
if let Ok(gfx_proxy) = GfxProxy::new(&conn).await {
match gfx_proxy.mode().await {
Ok(_) => {
has_supergfx = true;
if let Ok(version) = gfx_proxy.version().await {
if let Some(version) = Versioning::new(&version) {
let curr_gfx = Versioning::new("5.2.0").unwrap();
warn!("supergfxd version = {version}");
if version < curr_gfx {
// Don't allow mode changing if too old a version
warn!("supergfxd found but is too old to use");
has_supergfx = false;
}
let conn = zbus::blocking::Connection::system().unwrap();
let gfx_proxy = GfxProxy::new(&conn).unwrap();
let mut supergfx_active = false;
if gfx_proxy.mode().is_ok() {
supergfx_active = true;
if let Ok(version) = gfx_proxy.version() {
if let Some(version) = Versioning::new(&version) {
let curr_gfx = Versioning::new("5.0.3-RC4").unwrap();
warn!("supergfxd version = {version}");
if version < curr_gfx {
// Don't allow mode changing if too old a version
warn!("supergfxd found but is too old to use");
// tray.gfx_proxy_is_active = false;
}
}
}
Err(e) => match e {
zbus::Error::MethodError(_, _, message) => {
warn!(
"Couldn't get mode from supergfxd: {message:?}, the supergfxd service \
may not be running or installed"
)
}
_ => warn!("Couldn't get mode from supergfxd: {e:?}"),
},
}
};
info!("Started ROGTray");
let mut last_power = GfxPower::Unknown;
let dev = find_dgpu();
loop {
tokio::time::sleep(Duration::from_millis(1000)).await;
sleep(Duration::from_millis(1000));
if let Ok(lock) = config.try_lock() {
if !lock.enable_tray_icon {
return;
}
}
if has_supergfx {
if let Ok(mode) = gfx_proxy.mode().await {
if let Ok(power) = gfx_proxy.power().await {
if last_power != power {
set_tray_icon_and_tip(mode, power, &mut tray, has_supergfx).await;
last_power = power;
}
}
}
} else if let Some(dev) = dev.as_ref() {
if let Ok(power) = dev.get_runtime_status() {
if let Ok(mode) = gfx_proxy.mode() {
if let Ok(power) = gfx_proxy.power() {
if last_power != power {
set_tray_icon_and_tip(GfxMode::Hybrid, power, &mut tray, has_supergfx)
.await;
set_tray_icon_and_tip(mode, power, &mut tray, supergfx_active);
last_power = power;
}
}
+4 -35
View File
@@ -1,6 +1,5 @@
use crate::slint_generatedMainWindow::{
AuraDevType as SlintDeviceType, AuraPowerState as SlintAuraPowerState,
LaptopAuraPower as SlintLaptopAuraPower,
AuraPowerState as SlintAuraPowerState, LaptopAuraPower as SlintLaptopAuraPower,
};
impl From<rog_aura::AuraEffect> for crate::slint_generatedMainWindow::AuraEffect {
@@ -52,7 +51,7 @@ impl From<crate::slint_generatedMainWindow::AuraEffect> for rog_aura::AuraEffect
}
use rog_aura::keyboard::{AuraPowerState, LaptopAuraPower};
use rog_aura::{AuraDeviceType, PowerZones};
use rog_aura::PowerZones;
use slint::{Model, ModelRc, RgbaColor};
use crate::slint_generatedMainWindow::PowerZones as SlintPowerZones;
@@ -64,9 +63,8 @@ impl From<PowerZones> for SlintPowerZones {
PowerZones::Lightbar => SlintPowerZones::Lightbar,
PowerZones::Lid => SlintPowerZones::Lid,
PowerZones::RearGlow => SlintPowerZones::RearGlow,
PowerZones::KeyboardAndLightbar => SlintPowerZones::KeyboardAndLightbar,
PowerZones::Ally => SlintPowerZones::Ally,
PowerZones::None => SlintPowerZones::Keyboard,
PowerZones::KeyboardAndLightbar => todo!(),
PowerZones::None => todo!(),
}
}
}
@@ -80,7 +78,6 @@ impl From<SlintPowerZones> for PowerZones {
SlintPowerZones::Lid => PowerZones::Lid,
SlintPowerZones::RearGlow => PowerZones::RearGlow,
SlintPowerZones::KeyboardAndLightbar => PowerZones::KeyboardAndLightbar,
SlintPowerZones::Ally => PowerZones::Ally,
}
}
}
@@ -144,31 +141,3 @@ impl From<LaptopAuraPower> for SlintLaptopAuraPower {
}
}
}
impl From<SlintDeviceType> for AuraDeviceType {
fn from(value: SlintDeviceType) -> Self {
match value {
SlintDeviceType::New => Self::LaptopKeyboard2021,
SlintDeviceType::Old => Self::LaptopKeyboardPre2021,
SlintDeviceType::Tuf => Self::LaptopKeyboardTuf,
SlintDeviceType::ScsiExtDisk => Self::ScsiExtDisk,
SlintDeviceType::Unknown => Self::Unknown,
SlintDeviceType::Ally => Self::Ally,
SlintDeviceType::AnimeOrSlash => Self::AnimeOrSlash,
}
}
}
impl From<AuraDeviceType> for SlintDeviceType {
fn from(value: AuraDeviceType) -> Self {
match value {
AuraDeviceType::LaptopKeyboard2021 => SlintDeviceType::New,
AuraDeviceType::LaptopKeyboardPre2021 => SlintDeviceType::Old,
AuraDeviceType::LaptopKeyboardTuf => SlintDeviceType::Tuf,
AuraDeviceType::ScsiExtDisk => SlintDeviceType::ScsiExtDisk,
AuraDeviceType::Unknown => SlintDeviceType::Unknown,
AuraDeviceType::Ally => SlintDeviceType::Ally,
AuraDeviceType::AnimeOrSlash => SlintDeviceType::AnimeOrSlash,
}
}
}
+10 -12
View File
@@ -1,26 +1,24 @@
use rog_platform::platform::PlatformProfile;
use rog_platform::platform::ThrottlePolicy;
use rog_profiles::FanCurvePU;
use crate::{FanType, Profile};
impl From<Profile> for PlatformProfile {
impl From<Profile> for ThrottlePolicy {
fn from(value: Profile) -> Self {
match value {
Profile::Balanced => PlatformProfile::Balanced,
Profile::Performance => PlatformProfile::Performance,
Profile::Quiet => PlatformProfile::Quiet,
Profile::LowPower => PlatformProfile::LowPower,
Profile::Balanced => ThrottlePolicy::Balanced,
Profile::Performance => ThrottlePolicy::Performance,
Profile::Quiet => ThrottlePolicy::Quiet,
}
}
}
impl From<PlatformProfile> for Profile {
fn from(value: PlatformProfile) -> Self {
impl From<ThrottlePolicy> for Profile {
fn from(value: ThrottlePolicy) -> Self {
match value {
PlatformProfile::Balanced => Profile::Balanced,
PlatformProfile::Performance => Profile::Performance,
PlatformProfile::Quiet => Profile::Quiet,
PlatformProfile::LowPower => Profile::LowPower,
ThrottlePolicy::Balanced => Profile::Balanced,
ThrottlePolicy::Performance => Profile::Performance,
ThrottlePolicy::Quiet => Profile::Quiet,
}
}
}
+44 -29
View File
@@ -6,17 +6,31 @@ pub mod setup_system;
use std::sync::{Arc, Mutex};
use config_traits::StdConfig;
use log::warn;
use rog_dbus::list_iface_blocking;
use slint::{ComponentHandle, SharedString, Weak};
use rog_dbus::zbus_platform::PlatformProxyBlocking;
use slint::{ComponentHandle, PhysicalSize, SharedString, Weak};
use crate::config::Config;
use crate::ui::setup_anime::setup_anime_page;
use crate::ui::setup_aura::setup_aura_page;
use crate::ui::setup_aura::{has_aura_iface_blocking, setup_aura_page};
use crate::ui::setup_fans::setup_fan_curve_page;
use crate::ui::setup_system::{setup_system_page, setup_system_page_callbacks};
use crate::{AppSettingsPageData, MainWindow};
// This macro expects are consistent naming between proxy calls and slint
// globals
#[macro_export]
macro_rules! set_ui_props_async {
($ui:ident, $proxy:ident, $global:ident, $proxy_fn:ident) => {
if let Ok(value) = $proxy.$proxy_fn().await {
$ui.upgrade_in_event_loop(move |handle| {
concat_idents::concat_idents!(set = set_, $proxy_fn {
handle.global::<$global>().set(value.into());
});
}).ok();
}
};
}
// this macro sets up:
// - a link from UI callback -> dbus proxy property
// - a link from dbus property signal -> UI state
@@ -27,7 +41,7 @@ macro_rules! set_ui_callbacks {
let handle_copy = $handle.as_weak();
let proxy_copy = $proxy.clone();
let data = $handle.global::<$data>();
concat_idents::concat_idents!(on_set = on_cb_, $proxy_fn {
concat_idents::concat_idents!(on_set = on_set_, $proxy_fn {
data.on_set(move |value| {
let proxy_copy = proxy_copy.clone();
let handle_copy = handle_copy.clone();
@@ -50,7 +64,7 @@ macro_rules! set_ui_callbacks {
tokio::spawn(async move {
let mut x = proxy_copy.receive().await;
concat_idents::concat_idents!(set = set_, $proxy_fn {
use futures_util::StreamExt;
use zbus::export::futures_util::StreamExt;
while let Some(e) = x.next().await {
if let Ok(out) = e.get().await {
handle_copy.upgrade_in_event_loop(move |handle| {
@@ -83,20 +97,30 @@ pub fn show_toast(
}
pub fn setup_window(config: Arc<Mutex<Config>>) -> MainWindow {
slint::set_xdg_app_id("rog-control-center")
.map_err(|e| warn!("Couldn't set application ID: {e:?}"))
.ok();
let ui = MainWindow::new().unwrap();
ui.window().show().unwrap();
if let Ok(lock) = config.try_lock() {
let fullscreen = lock.start_fullscreen;
let width = lock.fullscreen_width;
let height = lock.fullscreen_height;
if fullscreen {
ui.window().set_fullscreen(fullscreen);
ui.window().set_size(PhysicalSize { width, height });
}
};
let available = list_iface_blocking().unwrap_or_default();
let conn = zbus::blocking::Connection::system().unwrap();
let platform = PlatformProxyBlocking::new(&conn).unwrap();
let interfaces = platform.supported_interfaces().unwrap();
log::debug!("Available interfaces: {interfaces:?}");
// "Anime", "Aura", "FanCurves", "Platform"
ui.set_sidebar_items_avilable(
[
// Needs to match the order of slint sidebar items
available.contains(&"xyz.ljones.Platform".to_string()),
available.contains(&"xyz.ljones.Aura".to_string()),
available.contains(&"xyz.ljones.Anime".to_string()),
available.contains(&"xyz.ljones.FanCurves".to_string()),
interfaces.contains(&"Platform".into()),
has_aura_iface_blocking().unwrap_or(false),
interfaces.contains(&"Anime".into()),
interfaces.contains(&"FanCurves".into()),
true,
true,
]
@@ -108,20 +132,11 @@ pub fn setup_window(config: Arc<Mutex<Config>>) -> MainWindow {
});
setup_app_settings_page(&ui, config.clone());
if available.contains(&"xyz.ljones.Platform".to_string()) {
setup_system_page(&ui, config.clone());
setup_system_page_callbacks(&ui, config.clone());
}
if available.contains(&"xyz.ljones.Aura".to_string()) {
setup_aura_page(&ui, config.clone());
}
if available.contains(&"xyz.ljones.Anime".to_string()) {
setup_anime_page(&ui, config.clone());
}
if available.contains(&"xyz.ljones.FanCurves".to_string()) {
setup_fan_curve_page(&ui, config);
}
setup_system_page(&ui, config.clone());
setup_system_page_callbacks(&ui, config.clone());
setup_aura_page(&ui, config.clone());
setup_anime_page(&ui, config.clone());
setup_fan_curve_page(&ui, config);
ui
}

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