Compare commits

..

88 Commits

Author SHA1 Message Date
Denis Benato
e9c5315bda Feat: Remove supergfxctl notification daemon 2026-01-13 22:05:34 +01:00
Denis Benato
b521a9ffc1 Fix: avoid using deprecated functions 2026-01-13 21:49:14 +01:00
Denis Benato
5e48923db1 Feat: update dependencies 2026-01-13 21:29:58 +01:00
Denis Benato
392436808d Fix text of armoury usage 2026-01-13 20:54:53 +01:00
Denis Benato
3d9a08d7e0 Silence the server 2026-01-13 20:18:49 +01:00
Denis Benato
ff103f98af Chore: change text of an error 2026-01-13 20:14:45 +01:00
Denis Benato
d05182ae64 chore: make the settings save code prettier 2026-01-13 19:29:37 +01:00
Denis Benato
a4957a6eeb Modify changelog and cargo fmt 2026-01-10 13:58:09 +01:00
Denis Benato
dda750cf33 Merge branch 'Support_additional_ga403_2025' into 'devel'
Add slash support for additional ga403 2025 models

See merge request asus-linux/asusctl!238
2026-01-10 12:54:01 +00:00
James Lademann
0b5e04393a Add slash support for additional ga403 2025 models 2026-01-10 12:54:01 +00:00
Denis Benato
c9c9a022a4 Merge branch 'fix/one-shot-charge-persistence' into 'devel'
fix: one-shot charging loses original charge limit after restart

See merge request asus-linux/asusctl!242
2026-01-10 12:51:30 +00:00
bitr8
aa063c20fd fix: persist base_charge_control_end_threshold for one-shot charging
base_charge_control_end_threshold was marked #[serde(skip)] so it only
lived in memory. triggering one-shot charge then restarting asusd would
permanently lose the original charge limit.

- remove #[serde(skip)] so the field gets persisted
- add serde default of 0 for backwards compat (skips restore for
  upgraded configs missing the field)
- add comments explaining Default vs serde default asymmetry
2026-01-05 17:23:59 +11:00
Denis Benato
8551908452 Merge branch 'devel' into 'devel'
slashctl: Add "static" LED profile

See merge request asus-linux/asusctl!240
2025-12-24 00:09:19 +00:00
Denis Benato
5ecb174b8f Merge branch 'main' into 'devel'
feat (rog-aura): Add support to FA617NT and FA617XT and update layout_name on FA617NS and FA617XS models

See merge request asus-linux/asusctl!241
2025-12-23 23:48:32 +00:00
Denis Benato
fa266bff5b Merge branch 'main' into 'devel'
Add G614JU support

See merge request asus-linux/asusctl!239
2025-12-23 23:43:41 +00:00
Seom1177
f53f1f360f Feat: update layout_name for FA617NS and FA617XS models in aura_support.ron 2025-12-22 09:06:06 -05:00
Seom1177
da19216b78 Feat: add support for FA617NT and FA617XT models to aura_support.ron 2025-12-22 09:02:25 -05:00
Some Otters
46efd4190f slashctl: Add "static" LED profile
See https://gitlab.com/asus-linux/reverse-engineering/-/blob/master/slash-bar/packets.txt?ref_type=heads . Tested working on a GU605CR
2025-12-22 10:59:17 +00:00
Hamidreza Khorsand
ed3022e25e Add G614JU support 2025-12-19 01:21:16 +03:30
Denis Benato
7c10d6c6d9 Fix: fans control for LowPower and Quiet 2025-12-18 21:53:42 +01:00
Denis Benato
af5f3a5c71 Fix: memorize last applied brightness settings 2025-12-18 15:07:14 +01:00
Denis Benato
7d5ec5f2c7 Update CHANGELOG.md 2025-12-18 03:40:03 +01:00
Denis Benato
1c8acf6de3 Feat: implement keyboard control for TUFs via asus-armoury 2025-12-18 02:10:45 +01:00
Denis Benato
7b644e7ad6 Merge branch 'main' into 'devel'
fix: add systemd scriptlets to restart asusd on package upgrade

See merge request asus-linux/asusctl!237
2025-12-15 18:12:29 +00:00
Denis Benato
0f02fe868c Fix: re-add G835L 2025-12-15 19:11:09 +01:00
Ali Abdelaal
abd3100e30 Merge upstream main, keep systemd scriptlets 2025-12-15 15:47:18 +00:00
Ali Abdelaal
6f651c2b85 Add systemd scriptlets to restart asusd on upgrade 2025-12-15 15:36:45 +00:00
Denis Benato
93ec5d1bce Feat: improve anime matrix detection 2025-12-14 15:05:10 +01:00
Denis Benato
5aea7f51c0 Chore: fix CHANGELOG.md styling 2025-12-14 15:00:51 +01:00
Denis Benato
d03d8ce67f Release: v6.2.0 2025-12-13 14:41:12 +01:00
Denis Benato
ae3693e0d9 Feat: add aura support for G614F models 2025-12-13 14:17:49 +01:00
Denis Benato
c8b9248eda Chore: update CHANGELOG.md 2025-12-13 13:56:46 +01:00
Denis Benato
22098794fe Fix: ensure upper/lower case doesn't fail the match 2025-12-13 13:48:33 +01:00
Denis Benato
10d49f4fc8 Fix: rever silly change in anime type detection 2025-12-13 13:46:29 +01:00
Denis Benato
5aba2854b0 Feat: add support for GU605C* models 2025-12-13 13:45:55 +01:00
Denis Benato
ea32ad6e0a Feat: add support for FX607V 2025-12-13 13:36:46 +01:00
Denis Benato
ee0e612c04 Feat: add AniMe support for G835LW 2025-12-13 13:35:56 +01:00
Denis Benato
e565ce748a Feat: GA403WR -> GA403W 2025-12-13 13:21:58 +01:00
Denis Benato
7024941663 Chore: fix changelog styling 2025-12-13 13:14:37 +01:00
Denis Benato
817a0165b5 Merge branch 'ga403wr' into 'devel'
add support for ga403wr, zephyrus g14 2025

See merge request asus-linux/asusctl!235
2025-12-13 12:14:19 +00:00
Denis Benato
efcd038f40 Merge branch 'main' into 'devel'
Fix: restore spec file for fedora copr builds

See merge request asus-linux/asusctl!236
2025-12-13 12:06:36 +00:00
Ali
375d99b8fc Fix: restore spec file for fedora copr builds 2025-12-13 12:06:36 +00:00
Ali Abdelaal
3a206eb76f Add asusctl as dependency for rog-gui subpackage 2025-12-11 23:49:24 +00:00
Ali Abdelaal
4449838282 Fix: remove cargo_prep which deletes Cargo.lock 2025-12-11 22:55:49 +00:00
Ali Abdelaal
58d740f77a Fix: use direct cargo build instead of cargo_build macro 2025-12-11 22:52:46 +00:00
Ali Abdelaal
f0488d9750 Build with --locked for reproducible builds 2025-12-11 22:47:00 +00:00
Ali Abdelaal
f1b9ae6f71 Fix: rewrite install section to handle Fedora rpm target dir 2025-12-11 21:48:40 +00:00
Ali Abdelaal
db5de3b854 Fix: add asusctl dir to files, remove duplicate aura_support.ron 2025-12-11 21:17:00 +00:00
Ali Abdelaal
7a3d39b8f1 Add fontconfig BuildRequires for Fedora COPR build 2025-12-11 21:01:16 +00:00
Ali Abdelaal
7a5d6325c0 Restore distro-packaging spec file for Fedora COPR builds 2025-12-11 20:46:03 +00:00
rustysec
574b954866 add support for ga403wr, zephyrus g14 2025 2025-11-29 23:49:24 -08:00
Denis Benato
a811f20f65 Release: v6.1.22 2025-11-29 02:57:34 +01:00
Denis Benato
0c9c263be6 Chore: update CHANGELOG.md 2025-11-29 02:52:27 +01:00
Denis Benato
6571c04bfe Merge branch 'devel' 2025-11-29 02:51:29 +01:00
Denis Benato
e48acbb8a2 Chore: update CHANGELOG.md 2025-11-29 02:51:16 +01:00
Denis Benato
f33496ef68 Merge branch 'main' into 'main'
Support the ROG Strix G18 (2025) G815

See merge request asus-linux/asusctl!234
2025-11-29 01:50:33 +00:00
Denis Benato
cd3176b565 Feat: improve a debug message 2025-11-29 02:48:22 +01:00
Denis Benato
7595613d7e Fix: add EXPERTBOOK to udev rules 2025-11-29 02:00:23 +01:00
Adam Bierman
b8d0245e7a Support G815L 2025-11-27 03:20:26 +00:00
Adam Bierman
54bd2ec800 Update file aura_support.ron 2025-11-27 02:44:30 +00:00
Denis Benato
b6c8566565 Feat: Treat dGPU attributes the same as PPT attributes for power-profile 2025-11-23 17:12:55 +01:00
Denis Benato
052c096014 Fix: use the correct name for nv_tgp 2025-11-23 16:56:47 +01:00
Denis Benato
81cbe3c522 Chore(Makefile): install cargo-vendor-filterer 2025-11-20 14:04:35 +01:00
Denis Benato
09f7492bec Release: fuck fedora 2025-11-20 02:59:41 +01:00
Denis Benato
6adfb2cf48 Fix: Release process 2025-11-18 23:03:58 +01:00
Denis Benato
a2a56792a8 Chore: changelog for 6.1.20 2025-11-18 22:56:42 +01:00
Denis Benato
317daf4ed1 Release: 6.1.20 2025-11-18 22:50:35 +01:00
Denis Benato
eb0d9514b5 Merge branch 'devel' 2025-11-18 22:36:27 +01:00
LucaP
d324b1bce5 Feat: Added support for G635L* models. Lightbar, Logo, Matrix 2025-11-18 22:32:49 +01:00
Denis Benato
d1c7385146 Merge branch 'main' into 'main'
Feat: Added support for G635L* models. Lightbar, Logo, Matrix

See merge request asus-linux/asusctl!233
2025-11-18 21:31:13 +00:00
Denis Benato
48a4935fc7 Fix: don't spam the console 2025-11-18 21:36:48 +01:00
LucaP
7e917b91a5 Feat: Added support for G635L* models. Lightbar, Logo, Matrix 2025-11-17 20:12:30 +02:00
Denis Benato
45f8b8ffb0 Chore: attempt another CI fix 2025-11-16 14:43:40 +01:00
Denis Benato
031a36242b Chore: Spare memory on CI 2025-11-16 14:17:24 +01:00
Denis Benato
8ad26ad136 Fix: CI testing 2025-11-15 18:57:14 +01:00
Denis Benato
907d4694f3 Merge branch 'devel' 2025-11-15 18:17:38 +01:00
Denis Benato
1dcc4ff502 Update CHANGELOG.md 2025-11-15 18:15:55 +01:00
Denis Benato
cf232047b0 Merge branch 'patch-1' into 'main'
Add aura support for G614FR (ROG Strix G16 2025)

See merge request asus-linux/asusctl!232
2025-11-15 15:08:19 +00:00
Henry Zhang
ff99ec9a39 Add aura support for G614FR (ROG Strix G16 2025) 2025-11-15 16:13:38 +08:00
Denis Benato
8a19374a41 Merge branch 'aura-support-fa617xs' into 'main'
Add aura support for ASUS TUF Gaming A16 (FA617XS, AMD Advantage Edition)

Closes #696

See merge request asus-linux/asusctl!231
2025-11-14 06:56:21 +00:00
AB
4cec1f49b4 Add FA617XS (TUF A16 AMD Advantage Edition) support to aura_support.ron 2025-11-12 16:29:38 +05:30
Denis Benato
01aae200f3 Fix: do not crash if no battery is present 2025-11-11 22:41:54 +01:00
Denis Benato
4c6db9f0a9 Chore: ignore push-blocking features that would make older rust toolchains error 2025-11-11 22:21:32 +01:00
Denis Benato
7207d3287a Chore: avoid rebuilding after make 2025-11-11 22:03:45 +01:00
Denis Benato
1b2d8d83fc Chore: prepare for 6.1.18 release 2025-11-11 21:55:42 +01:00
Denis Benato
6d8db91583 Chore: reduce trace logging from libraries 2025-11-11 21:49:00 +01:00
Denis Benato
20c3628476 Chore: update dependencies 2025-11-11 21:44:27 +01:00
Denis Benato
4edecfce1c Merge commit '34699a7021a85e1c0ba3d6ac0876c6beb8ae394f' 2025-11-11 21:39:01 +01:00
45 changed files with 1731 additions and 2042 deletions

View File

@@ -1,22 +1,26 @@
image: rust:latest
# Use shallow clone to reduce checkout size
variables:
GIT_DEPTH: "1"
# Put cargo home and target under project dir so we can clean them easily
CARGO_HOME: "$CI_PROJECT_DIR/.cargo"
CARGO_TARGET_DIR: "$CI_PROJECT_DIR/ci-target"
GIT_SUBMODULE_STRATEGY: normal
# Cache only cargo registries/git metadata to speed dependency fetches.
# Avoid caching compiled `target` artifacts which are large and easily fill disk.
.rust_cache: &rust_cache
cache:
# key: $CI_COMMIT_REF_SLUG
key: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
paths:
# Don't include `incremental` to save space
# Debug
- target/debug/build/
- target/debug/deps/
- target/debug/.fingerprint/
- target/debug/.cargo-lock
# Release
- target/release/build/
- target/release/deps/
- target/release/.fingerprint/
- target/release/.cargo-lock
- .cargo/registry
- .cargo/git
before_script:
- df -h
- echo "Cleaning stale targets to free space if present"
- rm -rf "$CI_PROJECT_DIR/target" "$CI_PROJECT_DIR/ci-target" || true
- apt-get update -qq && apt-get install -y -qq libudev-dev libgtk-3-dev grep llvm clang libclang-dev libsdl2-dev libsdl2-gfx-dev
stages:
@@ -33,6 +37,9 @@ format:
script:
- rustup component add rustfmt || true
- cargo fmt --check
after_script:
- du -sh "$CI_PROJECT_DIR/ci-target" || true
- rm -rf "$CI_PROJECT_DIR/ci-target" || true
check:
except:
@@ -44,6 +51,8 @@ check:
# deny currently catches too much
#- cargo install cargo-deny && cargo deny
- cargo install cargo-cranky && cargo cranky
after_script:
- rm -rf "$CI_PROJECT_DIR/ci-target" || true
test:
except:
@@ -52,6 +61,8 @@ test:
script:
- mkdir -p .git/hooks > /dev/null
- cargo test --locked --all
after_script:
- rm -rf "$CI_PROJECT_DIR/ci-target" || true
release:
only:
@@ -59,11 +70,15 @@ release:
<<: *rust_cache
script:
- cargo install cargo-vendor-filterer
- cargo fetch
- make FROZEN=1 && make vendor
artifacts:
paths:
- vendor_asusctl*.tar.xz
- cargo-config
expire_in: 1 week
after_script:
- rm -rf vendor vendor_asusctl*.tar.xz "$CI_PROJECT_DIR/ci-target" || true
pages:
stage: deploy
@@ -74,11 +89,11 @@ pages:
- cargo doc --locked --document-private-items --no-deps --workspace
- rm -rf public
- mkdir public
- cp -R target/doc/* public
- cp -R ci-target/doc/* public
- cp extra/index.html public
artifacts:
paths:
- public
variables:
GIT_SUBMODULE_STRATEGY: normal
expire_in: 1 week
after_script:
- rm -rf "$CI_PROJECT_DIR/ci-target" || true

View File

@@ -3,9 +3,47 @@
## [Unreleased]
### Changed
- Make the boot process more reliable
- tie nv_ properties to power profiles
- Better support nv_tgp
- Added support for TUF keyboard powerstate control
- Improved AniMe Matrix support thanks to @Seom1177 !
- Fixed a bug with one-shot battery change, thanks @bitr8 !
## [6.2.0]
### Changed
- Added aura support for FX607V: thanks @jomp16
- Added testing support for G835LW
- Added support for GU605C models slash lighting: thanks @Otters
- Restore fedora: thanks @ali205412
- Add support to G614F models slash lighting
## [6.1.22]
### Changed
- Allow configuration of nv_tgp
- Treat dGPU attributes as power profiles
- Add EXPERTBOOK DMI match to ensure the service is loaded
- Support G815L thanks to @solost !
## [6.1.21]
### Changed
- Kill Fedora: screw your cursed cargo bullshit
- Restore CI building
## [6.1.20]
### Changed
- Addded support for G635L: thanks @luca_pisl !
- Suppress verbose output in applications too, not just daemon
## [6.1.18]
### Changed
- Add aura support for G614FR (ROG Strix G16 2025)
- all notifications now respects the timeout
- improve udev daemon-starting rule
- reduce log noise
## [v6.1.17]

1148
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
[workspace.package]
version = "6.1.17"
version = "6.2.0"
rust-version = "1.82"
license = "MPL-2.0"
readme = "README.md"
@@ -44,7 +44,7 @@ smol = "^2.0"
mio = "0.8.11"
futures-util = "0.3.31"
zbus = "5.5.0"
zbus = "5.13.1"
logind-zbus = { version = "5.2.0" } #, default-features = false, features = ["non_blocking"] }
serde = { version = "^1.0", features = ["serde_derive"] }
@@ -57,12 +57,12 @@ glam = { version = "^0.22", features = ["serde"] }
gumdrop = "^0.8"
udev = { version = "^0.8", features = ["mio"] }
rusb = "^0.9"
inotify = "^0.10.0"
inotify = "^0.10"
png_pong = "^0.8"
pix = "^0.13"
tinybmp = "^0.4.0"
gif = "^0.12.0"
tinybmp = "^0.4"
gif = "^0.12"
versions = "6.2"

View File

@@ -59,10 +59,17 @@ clean:
distclean:
rm -rf .cargo vendor vendor.tar.xz
target/$(TARGET)/$(BIN_D): build
target/$(TARGET)/$(BIN_C): build
target/$(TARGET)/$(BIN_U): build
target/$(TARGET)/$(BIN_ROG): build
target/$(TARGET)/$(BIN_D): $(SRC)
$(MAKE) build
target/$(TARGET)/$(BIN_C): $(SRC)
$(MAKE) build
target/$(TARGET)/$(BIN_U): $(SRC)
$(MAKE) build
target/$(TARGET)/$(BIN_ROG): $(SRC)
$(MAKE) build
install-asusd: target/$(TARGET)/$(BIN_D)
$(INSTALL_PROGRAM) "./target/$(TARGET)/$(BIN_D)" "$(DESTDIR)$(bindir)/$(BIN_D)"
@@ -151,6 +158,8 @@ vendor:
mv .cargo/config ./cargo-config
rm -rf .cargo
rm -rf vendor
# Ensure cargo-vendor-filterer is installed (CI installs it already)
command -v cargo-vendor-filterer >/dev/null 2>&1 || cargo install --locked cargo-vendor-filterer
cargo vendor-filterer --all-features --platform x86_64-unknown-linux-gnu vendor
tar pcfJ vendor_asusctl_$(VERSION).tar.xz vendor
rm -rf vendor

View File

@@ -180,8 +180,8 @@ pub struct TwoColourSpeed {
pub zone: AuraZone,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default, Options)]
#[allow(dead_code)]
pub struct MultiZone {
#[options(help = "print help message")]
help: bool,
@@ -195,8 +195,8 @@ pub struct MultiZone {
pub colour4: Colour,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default, Options)]
#[allow(dead_code)]
pub struct MultiColourSpeed {
#[options(help = "print help message")]
help: bool,

View File

@@ -9,7 +9,7 @@ use aura_cli::{LedPowerCommand1, LedPowerCommand2};
use dmi_id::DMIID;
use fan_curve_cli::FanCurveCommand;
use gumdrop::{Opt, Options};
use log::{error, info};
use log::{error, info, LevelFilter};
use rog_anime::usb::get_anime_type;
use rog_anime::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType, Vec2};
use rog_aura::keyboard::{AuraPowerState, LaptopAuraPower};
@@ -44,12 +44,16 @@ mod scsi_cli;
mod slash_cli;
fn main() {
// Ensure tracing spans are quiet by default unless user overrides
if std::env::var_os("RUST_LOG").is_none() {
std::env::set_var("RUST_LOG", "warn,tracing=error,zbus=error");
}
let mut logger = env_logger::Builder::new();
logger
.parse_default_env()
.target(env_logger::Target::Stdout)
.filter_level(LevelFilter::Info)
.target(env_logger::Target::Stderr)
.format_timestamp(None)
.filter_level(log::LevelFilter::Debug)
.init();
let self_version = env!("CARGO_PKG_VERSION");
@@ -1184,15 +1188,12 @@ fn print_firmware_attr(attr: &AsusArmouryProxyBlocking) -> Result<(), Box<dyn st
Ok(())
}
#[allow(clippy::manual_is_multiple_of)]
#[allow(clippy::manual_is_multiple_of, clippy::nonminimal_bool)]
fn handle_armoury_command(cmd: &ArmouryCommand) -> Result<(), Box<dyn std::error::Error>> {
{
// Avoid using `.is_multiple_of(2)` to satisfy the request. Use modulus check
// and simplify the boolean expression.
let odd_len = cmd.free.len() % 2 != 0;
if cmd.free.is_empty() || odd_len || cmd.help {
const USAGE: &str = "Usage: asusctl platform panel_overdrive 1 nv_dynamic_boost 5";
if odd_len {
if cmd.free.is_empty() || (cmd.free.len() % 2 != 0) || cmd.help {
const USAGE: &str = "Usage: asusctl armoury panel_overdrive 1 nv_dynamic_boost 5";
if cmd.free.len() % 2 != 0 {
println!(
"Incorrect number of args, each attribute label must be paired with a setting:"
);

View File

@@ -45,7 +45,6 @@ concat-idents.workspace = true
[dev-dependencies]
cargo-husky.workspace = true
tempfile = "3"
[package.metadata.deb]
license-file = ["../LICENSE", "4"]

View File

@@ -1,7 +1,7 @@
use std::sync::Arc;
use config_traits::StdConfig;
use log::{debug, error, info};
use log::{debug, error, info, warn};
use rog_platform::asus_armoury::{AttrValue, Attribute, FirmwareAttribute, FirmwareAttributes};
use rog_platform::platform::{PlatformProfile, RogPlatform};
use rog_platform::power::AsusPower;
@@ -27,28 +27,6 @@ fn dbus_path_for_attr(attr_name: &str) -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{attr_name}")).into()
}
// Helper: return true when the attribute is effectively unsupported for
// the current power mode/device. We consider an attribute unsupported when
// its resolved min and max are equal (no available range), which indicates
// writes would be invalid. When true, callers should avoid attempting writes.
fn attr_unsupported(attr: &Attribute) -> bool {
let min = attr
.refresh_min_value()
.or(match attr.min_value() {
AttrValue::Integer(i) => Some(*i),
_ => None,
})
.unwrap_or(-1);
let max = attr
.refresh_max_value()
.or(match attr.max_value() {
AttrValue::Integer(i) => Some(*i),
_ => None,
})
.unwrap_or(-2); // different default so equality is false unless both present
min == max
}
#[derive(Clone)]
pub struct AsusArmouryAttribute {
attr: Attribute,
@@ -168,14 +146,6 @@ impl ArmouryAttributeRegistry {
self.attrs.push(attr);
}
pub fn is_empty(&self) -> bool {
self.attrs.is_empty()
}
pub fn iter(&self) -> std::slice::Iter<'_, AsusArmouryAttribute> {
self.attrs.iter()
}
pub async fn emit_limits(&self, connection: &Connection) -> Result<(), RogError> {
let mut last_err: Option<RogError> = None;
for attr in &self.attrs {
@@ -200,6 +170,8 @@ impl crate::Reloadable for AsusArmouryAttribute {
info!("Reloading {}", self.attr.name());
let name: FirmwareAttribute = self.attr.name().into();
// Treat dGPU attributes the same as PPT attributes for power-profile
// behaviour so they follow AC/DC tuning groups.
if name.is_ppt() || name.is_dgpu() {
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self
@@ -226,24 +198,20 @@ impl crate::Reloadable for AsusArmouryAttribute {
};
if let Some(tune) = apply_value {
// Don't attempt writes for attributes that report min==0 and max==0
// (commonly means not supported in this power mode/device); skip
// applying stored tune in that case.
if self.attr.base_path_exists() && !attr_unsupported(&self.attr) {
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);
} else {
debug!(
"Skipping apply for {} because attribute missing or unsupported (min==max)",
self.attr.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!(
"Restored PPT armoury setting {} to {:?}",
self.attr.name(),
tune
);
} else {
info!("Ignored restoring PPT armoury setting {} as tuning group is disabled or no saved value", self.attr.name());
}
} else {
// Handle non-PPT attributes (boolean and other settings)
@@ -251,7 +219,10 @@ impl crate::Reloadable for AsusArmouryAttribute {
self.attr
.set_current_value(&AttrValue::Integer(*saved_value))
.map_err(|e| {
error!("Could not set {} value: {e:?}", self.attr.name());
error!(
"Error restoring armoury setting {}: {e:?}",
self.attr.name()
);
self.attr.base_path_exists();
e
})?;
@@ -260,6 +231,11 @@ impl crate::Reloadable for AsusArmouryAttribute {
self.attr.name(),
saved_value
);
} else {
info!(
"No saved armoury setting for {}: skipping restore",
self.attr.name()
);
}
}
@@ -409,94 +385,6 @@ impl AsusArmouryAttribute {
))
}
async fn stored_value_for_power(&self, on_ac: bool) -> fdo::Result<i32> {
if !(self.name().is_ppt() || self.name().is_dgpu()) {
return Err(fdo::Error::NotSupported(
"Stored values are only available for PPT/dGPU tunable attributes".to_string(),
));
}
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let config = self.config.lock().await;
if let Some(tuning) = config.select_tunings_ref(on_ac, profile) {
if let Some(tune) = tuning.group.get(&self.name()) {
return Ok(*tune);
}
}
if let AttrValue::Integer(i) = self.attr.default_value() {
return Ok(*i);
}
Err(fdo::Error::Failed(
"Could not read stored value".to_string(),
))
}
async fn set_value_for_power(&mut self, on_ac: bool, value: i32) -> fdo::Result<()> {
if !(self.name().is_ppt() || self.name().is_dgpu()) {
return Err(fdo::Error::NotSupported(
"Setting stored values is only supported for PPT/dGPU tunable attributes"
.to_string(),
));
}
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let apply_now;
{
let mut config = self.config.lock().await;
let tuning = config.select_tunings(on_ac, profile);
if let Some(tune) = tuning.group.get_mut(&self.name()) {
*tune = value;
} else {
tuning.group.insert(self.name(), value);
debug!(
"Store {} value for {} power = {}",
self.attr.name(),
if on_ac { "AC" } else { "DC" },
value
);
}
apply_now = tuning.enabled;
config.write();
}
if apply_now {
let power_plugged = self
.power
.get_online()
.map_err(|e| {
error!("Could not get power status: {e:?}");
e
})
.unwrap_or_default()
!= 0;
// Don't attempt writes for attributes that report min==0 and max==0
// (commonly means not supported in this power mode/device); skip
// applying stored value in that case.
if self.attr.base_path_exists() && !attr_unsupported(&self.attr) {
if power_plugged == on_ac {
self.attr
.set_current_value(&AttrValue::Integer(value))
.map_err(|e| {
error!("Could not set value: {e:?}");
e
})?;
}
} else {
debug!(
"Skipping immediate apply for {} because attribute missing or unsupported (min==max)",
self.attr.name()
);
}
}
Ok(())
}
#[zbus(property)]
async fn set_current_value(&mut self, value: i32) -> fdo::Result<()> {
if self.name().is_ppt() || self.name().is_dgpu() {
@@ -520,72 +408,58 @@ impl AsusArmouryAttribute {
debug!("Store tuning config for {} = {:?}", self.attr.name(), value);
}
if tuning.enabled {
// Don't attempt writes for attributes that report min==0 and max==0
// (commonly means not supported in this power mode/device); skip
// applying stored value in that case.
if self.attr.base_path_exists() && !attr_unsupported(&self.attr) {
self.attr
.set_current_value(&AttrValue::Integer(value))
.map_err(|e| {
error!("Could not set value: {e:?}");
e
})?;
} else {
debug!(
"Skipping apply for {} on set_current_value because attribute missing or unsupported (min==max)",
self.attr.name()
);
}
self.attr
.set_current_value(&AttrValue::Integer(value))
.map_err(|e| {
error!(
"Could not set value to PPT property {}: {e:?}",
self.attr.name()
);
e
})?;
} else {
warn!(
"Tuning group is disabled: skipping setting value to PPT property {}",
self.attr.name()
);
}
} else {
self.attr
.set_current_value(&AttrValue::Integer(value))
.map_err(|e| {
error!("Could not set value: {e:?}");
error!(
"Could not set value {value} to attribute {}: {e:?}",
self.attr.name()
);
e
})?;
let has_attr = self
.config
.lock()
.await
let mut settings = self.config.lock().await;
settings
.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);
}
.entry(self.name())
.and_modify(|setting| {
debug!("Set config for {} = {value}", self.attr.name());
*setting = value;
})
.or_insert_with(|| {
debug!("Adding config for {} = {value}", self.attr.name());
value
});
}
// write config after setting value
self.config.lock().await.write();
Ok(())
}
}
#[allow(clippy::too_many_arguments)]
pub async fn start_attributes_zbus(
conn: Option<&Connection>,
conn: &Connection,
platform: RogPlatform,
power: AsusPower,
attributes: FirmwareAttributes,
config: Arc<Mutex<Config>>,
enable_zbus: bool,
profile_override: Option<rog_platform::platform::PlatformProfile>,
power_plugged_override: Option<bool>,
) -> Result<ArmouryAttributeRegistry, RogError> {
let mut registry = ArmouryAttributeRegistry::default();
for attr in attributes.attributes() {
@@ -598,115 +472,34 @@ pub async fn start_attributes_zbus(
let registry_attr = attr.clone();
// Only perform the full reload (which may query the platform/power sysfs)
// when zbus is enabled. Tests using the no-zbus mode skip the reload and
// emulate the reload/apply behavior to avoid depending on udev/sysfs.
if enable_zbus {
if let Err(e) = attr.reload().await {
error!(
"Skipping attribute '{}' due to reload error: {e:?}",
attr.attr.name()
);
// continue with others
continue;
}
if let Err(e) = attr.reload().await {
error!(
"Skipping attribute '{}' due to reload error: {e:?}",
attr.attr.name()
);
break;
}
let attr_name = attr.attribute_name();
// If zbus is enabled and a connection is provided, create the SignalEmitter,
// start watchers and register the object on zbus. Tests can call this function
// with enable_zbus=false and conn=None to skip DBus registration and watchers.
if !enable_zbus {
// Emulate reload logic but prefer overrides when provided to avoid dependency on udev/sysfs in tests
let name: rog_platform::asus_armoury::FirmwareAttribute = attr.attr.name().into();
if name.is_ppt() || name.is_dgpu() {
// determine profile
let profile = if let Some(p) = profile_override {
p
} else {
match attr.platform.get_platform_profile() {
Ok(p) => p.into(),
Err(_) => rog_platform::platform::PlatformProfile::Balanced,
}
};
// determine power plugged
let power_plugged = if let Some(v) = power_plugged_override {
v
} else {
match attr.power.get_online() {
Ok(v) => v == 1,
Err(_) => false,
}
};
let apply_value = {
let config = attr.config.lock().await;
config
.select_tunings_ref(power_plugged, profile)
.and_then(|tuning| {
if tuning.enabled {
tuning.group.get(&attr.name()).copied()
} else {
None
}
})
};
if let Some(tune) = apply_value {
attr.attr
.set_current_value(&AttrValue::Integer(tune))
.map_err(|e| {
error!("Could not set {} value: {e:?}", attr.attr.name());
e
})?;
let path = dbus_path_for_attr(attr_name.as_str());
match zbus::object_server::SignalEmitter::new(conn, path) {
Ok(sig) => {
if let Err(e) = attr.watch_and_notify(sig).await {
error!("Failed to start watcher for '{}': {e:?}", attr.attr.name());
}
} else if let Some(saved_value) = attr.config.lock().await.armoury_settings.get(&name) {
attr.attr
.set_current_value(&AttrValue::Integer(*saved_value))
.map_err(|e| {
error!("Could not set {} value: {e:?}", attr.attr.name());
e
})?;
info!(
"Restored armoury setting {} to {:?}",
attr.attr.name(),
saved_value
}
Err(e) => {
error!(
"Failed to create SignalEmitter for '{}': {e:?}",
attr.attr.name()
);
}
registry.push(registry_attr);
continue;
}
// If zbus is enabled and a connection is provided, create the SignalEmitter,
// start watchers and register the object on zbus. Tests can call this function
// with enable_zbus=false and conn=None to skip DBus registration and watchers.
if enable_zbus {
if let Some(connection) = conn {
let path = dbus_path_for_attr(attr_name.as_str());
match zbus::object_server::SignalEmitter::new(connection, path) {
Ok(sig) => {
if let Err(e) = attr.watch_and_notify(sig).await {
error!("Failed to start watcher for '{}': {e:?}", attr.attr.name());
}
}
Err(e) => {
error!(
"Failed to create SignalEmitter for '{}': {e:?}",
attr.attr.name()
);
}
}
if let Err(e) = attr.move_to_zbus(connection).await {
error!("Failed to register attribute '{attr_name}' on zbus: {e:?}");
continue;
}
} else {
error!("zbus enabled but no Connection provided for attribute registration");
}
if let Err(e) = attr.move_to_zbus(conn).await {
error!("Failed to register attribute '{attr_name}' on zbus: {e:?}");
continue;
}
registry.push(registry_attr);
@@ -726,46 +519,30 @@ pub async fn set_config_or_default(
let tuning = config.select_tunings(power_plugged, profile);
if !tuning.enabled {
debug!("Tuning group is not enabled, skipping");
continue;
return;
}
// Determine once whether attribute is present and supports a writable range
let supported = attr.base_path_exists() && !attr_unsupported(attr);
if let Some(tune) = tuning.group.get(&name) {
if supported {
attr.set_current_value(&AttrValue::Integer(*tune))
.map_err(|e| {
error!("Failed to set {}: {e}", <&str>::from(name));
})
.ok();
} else {
debug!(
"Skipping apply for {} in set_config_or_default because attribute missing or unsupported",
<&str>::from(name)
);
}
attr.set_current_value(&AttrValue::Integer(*tune))
.map_err(|e| {
error!("Failed to set {}: {e}", <&str>::from(name));
})
.ok();
} else {
// Only attempt to apply defaults when the attribute supports a range
if supported {
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();
}
} else {
debug!(
"Skipping default apply for {} in set_config_or_default because attribute missing or unsupported",
<&str>::from(name)
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();
}
}
} else {
@@ -785,69 +562,3 @@ pub async fn set_config_or_default(
}
}
}
// Internal helper to store a tuning value into the correct per-profile, per-power map.
// This centralizes the behavior so tests can validate storage semantics.
#[allow(dead_code)]
fn insert_tuning_value(
config: &mut Config,
on_ac: bool,
profile: PlatformProfile,
name: rog_platform::asus_armoury::FirmwareAttribute,
value: i32,
) {
let tuning = config.select_tunings(on_ac, profile);
if let Some(t) = tuning.group.get_mut(&name) {
*t = value;
} else {
tuning.group.insert(name, value);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::Config;
use rog_platform::asus_armoury::FirmwareAttribute;
use rog_platform::platform::PlatformProfile;
#[test]
fn insert_nv_tuning_is_per_profile_and_power() {
let mut cfg = Config::default();
let profile = PlatformProfile::Performance;
// Insert value for AC
insert_tuning_value(
&mut cfg,
true,
profile,
FirmwareAttribute::NvDynamicBoost,
7,
);
// Value should be present in ac_profile_tunings
let t_ac = cfg.select_tunings_ref(true, profile).unwrap();
assert_eq!(t_ac.group.get(&FirmwareAttribute::NvDynamicBoost), Some(&7));
// Insert separate value for DC
insert_tuning_value(
&mut cfg,
false,
profile,
FirmwareAttribute::NvDynamicBoost,
3,
);
let t_dc = cfg.select_tunings_ref(false, profile).unwrap();
assert_eq!(t_dc.group.get(&FirmwareAttribute::NvDynamicBoost), Some(&3));
}
#[test]
fn non_ppt_attribute_stores_in_armoury_settings() {
let mut cfg = Config::default();
// Non-PPT/dGPU attribute, e.g., BootSound
let name = FirmwareAttribute::BootSound;
// Simulate setting armoury setting
cfg.armoury_settings.insert(name, 1);
assert_eq!(cfg.armoury_settings.get(&name), Some(&1));
}
}

View File

@@ -71,7 +71,13 @@ impl AuraZbus {
#[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())?);
let res = bl.lock().await.set_brightness(brightness.into());
if res.is_ok() {
let mut config = self.0.config.lock().await;
config.brightness = brightness;
config.write();
}
return Ok(res?);
}
Err(ZbErr::Failed("No sysfs brightness control".to_string()))
}

View File

@@ -96,17 +96,15 @@ pub struct AsusDevice {
hid_key: Option<String>,
}
/// Shared alias for the HidRaw handle map used throughout this module.
type HidHandleMap = Arc<Mutex<HashMap<String, Arc<Mutex<HidRaw>>>>>;
pub struct DeviceManager {
_dbus_connection: Connection,
_hid_handles: HidHandleMap,
_hid_handles: Arc<Mutex<HashMap<String, Arc<Mutex<HidRaw>>>>>,
}
impl DeviceManager {
#[allow(clippy::type_complexity)]
async fn get_or_create_hid_handle(
handles: &HidHandleMap,
handles: &Arc<Mutex<HashMap<String, Arc<Mutex<HidRaw>>>>>,
endpoint: &Device,
) -> Result<(Arc<Mutex<HidRaw>>, String), RogError> {
let dev_node = endpoint
@@ -127,14 +125,14 @@ impl DeviceManager {
async fn init_hid_devices(
connection: &Connection,
device: Device,
handles: HidHandleMap,
handles: Arc<Mutex<HashMap<String, Arc<Mutex<HidRaw>>>>>,
) -> 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");
debug!("Not ASUS vendor ID: {}", vendor_id.to_string_lossy());
return Ok(devices);
}
// Almost all devices are identified by the productId.
@@ -215,7 +213,7 @@ impl DeviceManager {
/// To be called on daemon startup
async fn init_all_hid(
connection: &Connection,
handles: HidHandleMap,
handles: Arc<Mutex<HashMap<String, Arc<Mutex<HidRaw>>>>>,
) -> Result<Vec<AsusDevice>, RogError> {
// track and ensure we use only one hidraw per prod_id
// let mut interfaces = HashSet::new();

View File

@@ -8,6 +8,12 @@ use serde::{Deserialize, Serialize};
const CONFIG_FILE: &str = "asusd.ron";
/// Default value for base_charge_control_end_threshold when not present in config.
/// Returns 0 so restore_charge_limit() skips restoration for upgraded configs.
fn default_base_charge_limit() -> u8 {
0
}
#[derive(Default, Clone, Deserialize, Serialize, PartialEq)]
pub struct Tuning {
pub enabled: bool,
@@ -19,8 +25,8 @@ type Tunings = HashMap<PlatformProfile, Tuning>;
pub struct Config {
// The current charge limit applied
pub charge_control_end_threshold: u8,
/// Save charge limit for restoring
#[serde(skip)]
/// Save charge limit for restoring after one-shot full charge
#[serde(default = "default_base_charge_limit")]
pub base_charge_control_end_threshold: u8,
pub disable_nvidia_powerd_on_battery: bool,
/// An optional command/script to run when power is changed to AC
@@ -86,6 +92,9 @@ impl Default for Config {
fn default() -> Self {
Self {
charge_control_end_threshold: 100,
// NOTE: This is intentionally 100 (not 0 like the serde default).
// New installs get 100 (no limit). Upgraded configs missing this
// field get 0 via serde, which skips restore_charge_limit().
base_charge_control_end_threshold: 100,
disable_nvidia_powerd_on_battery: true,
ac_command: Default::default(),
@@ -225,8 +234,6 @@ pub struct Config601 {
pub nv_dynamic_boost: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_temp_target: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_tgp: Option<u8>,
#[serde(skip)]
pub last_power_plugged: u8,
}

View File

@@ -295,16 +295,39 @@ impl CtrlPlatform {
#[zbus(property)]
fn charge_control_end_threshold(&self) -> Result<u8, FdoErr> {
let limit = self.power.get_charge_control_end_threshold()?;
if !self.power.has_charge_control_end_threshold() {
return Err(FdoErr::NotSupported(
"RogPlatform: charge_control_end_threshold not supported".to_owned(),
));
}
let limit = self.power.get_charge_control_end_threshold().map_err(|e| {
FdoErr::Failed(format!(
"Could not read charge_control_end_threshold: {e:?}"
))
})?;
Ok(limit)
}
#[zbus(property)]
async fn set_charge_control_end_threshold(&mut self, limit: u8) -> Result<(), FdoErr> {
if !self.power.has_charge_control_end_threshold() {
return Err(FdoErr::NotSupported(
"RogPlatform: charge_control_end_threshold not supported".to_owned(),
));
}
if !(20..=100).contains(&limit) {
return Err(RogError::ChargeLimit(limit))?;
}
self.power.set_charge_control_end_threshold(limit)?;
self.power
.set_charge_control_end_threshold(limit)
.map_err(|e| {
FdoErr::Failed(format!("Could not set charge_control_end_threshold: {e:?}"))
})?;
self.config.lock().await.charge_control_end_threshold = limit;
self.config.lock().await.base_charge_control_end_threshold = limit;
self.config.lock().await.write();
@@ -312,12 +335,22 @@ impl CtrlPlatform {
}
async fn one_shot_full_charge(&self) -> Result<(), FdoErr> {
if !self.power.has_charge_control_end_threshold() {
return Err(FdoErr::NotSupported(
"RogPlatform: charge_control_end_threshold not supported".to_owned(),
));
}
let base_limit = std::mem::replace(
&mut self.config.lock().await.charge_control_end_threshold,
100,
);
if base_limit != 100 {
self.power.set_charge_control_end_threshold(100)?;
self.power
.set_charge_control_end_threshold(100)
.map_err(|e| {
FdoErr::Failed(format!("Could not set one_shot_full_charge: {e:?}"))
})?;
self.config.lock().await.base_charge_control_end_threshold = base_limit;
self.config.lock().await.write();
}
@@ -566,7 +599,7 @@ impl CtrlPlatform {
for attr in self.attributes.attributes() {
let name: FirmwareAttribute = attr.name().into();
if name.is_ppt() || name.is_dgpu() {
if name.is_ppt() {
// reset stored value
if let Some(tune) = self
.config

View File

@@ -77,14 +77,11 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
let power = AsusPower::new()?; // TODO: maybe needs async mutex?
let attributes = FirmwareAttributes::new();
let armoury_registry = match start_attributes_zbus(
Some(&server),
&server,
platform.clone(),
power.clone(),
attributes.clone(),
config.clone(),
true,
None,
None,
)
.await
{

View File

@@ -1,68 +0,0 @@
use std::fs::{create_dir_all, File};
use std::io::Write;
use std::path::PathBuf;
use tempfile::tempdir;
use asusd::asus_armoury::set_config_or_default;
use asusd::config::Config;
use rog_platform::asus_armoury::FirmwareAttributes;
use rog_platform::platform::PlatformProfile;
fn write_attr_dir_with_min_max(base: &PathBuf, name: &str, default: &str, min: &str, max: &str) {
let attr_dir = base.join(name);
create_dir_all(&attr_dir).unwrap();
let mut f = File::create(attr_dir.join("default_value")).unwrap();
write!(f, "{}", default).unwrap();
let mut f = File::create(attr_dir.join("display_name")).unwrap();
write!(f, "{}", name).unwrap();
// create current_value file so set_current_value can open for write
let mut f = File::create(attr_dir.join("current_value")).unwrap();
write!(f, "{}", default).unwrap();
// write explicit min and max
let mut f = File::create(attr_dir.join("min_value")).unwrap();
write!(f, "{}", min).unwrap();
let mut f = File::create(attr_dir.join("max_value")).unwrap();
write!(f, "{}", max).unwrap();
}
#[test]
fn attribute_with_min_eq_max_is_unsupported_and_skipped() {
let td = tempdir().unwrap();
let base = td.path().join("attributes");
create_dir_all(&base).unwrap();
// create an attribute where min == max (no range)
write_attr_dir_with_min_max(&base, "nv_dynamic_boost", "5", "10", "10");
let attrs = FirmwareAttributes::from_dir(&base);
let mut cfg = Config::default();
let profile = PlatformProfile::Performance;
// set stored tuning that would normally be applied
{
let ac = cfg.select_tunings(true, profile);
ac.enabled = true;
ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::NvDynamicBoost,
9,
);
}
let rt = tokio::runtime::Runtime::new().unwrap();
// apply AC
rt.block_on(async {
set_config_or_default(&attrs, &mut cfg, true, profile).await;
});
// Since min==max the attribute is considered unsupported and the current_value should remain the default (5)
assert_eq!(
std::fs::read_to_string(base.join("nv_dynamic_boost").join("current_value"))
.unwrap()
.trim(),
"5"
);
}

View File

@@ -1,173 +0,0 @@
use std::fs::{create_dir_all, File};
use std::io::Write;
use std::path::PathBuf;
use std::sync::Arc;
use tempfile::tempdir;
use asusd::asus_armoury::start_attributes_zbus;
use asusd::config::Config;
use rog_platform::asus_armoury::FirmwareAttributes;
use rog_platform::platform::PlatformProfile;
use rog_platform::platform::RogPlatform;
use rog_platform::power::AsusPower;
use tokio::runtime::Runtime;
use tokio::sync::Mutex as TokioMutex;
fn write_attr_dir(base: &PathBuf, name: &str, default: &str, display: &str) {
let attr_dir = base.join(name);
create_dir_all(&attr_dir).unwrap();
let mut f = File::create(attr_dir.join("default_value")).unwrap();
write!(f, "{}", default).unwrap();
let mut f = File::create(attr_dir.join("display_name")).unwrap();
write!(f, "{}", display).unwrap();
// create current_value file so set_current_value can open for write
let mut f = File::create(attr_dir.join("current_value")).unwrap();
write!(f, "{}", default).unwrap();
}
#[test]
fn full_service_handles_boot_sound_and_nv_tgp() {
let td = tempdir().unwrap();
let base = td.path().join("attributes");
create_dir_all(&base).unwrap();
// create fake attributes (ppt and nv related)
write_attr_dir(&base, "boot_sound", "0", "boot_sound");
write_attr_dir(&base, "ppt_pl1_spl", "25", "ppt_pl1_spl");
write_attr_dir(&base, "ppt_pl2_sppt", "50", "ppt_pl2_sppt");
write_attr_dir(&base, "ppt_pl3_fppt", "75", "ppt_pl3_fppt");
write_attr_dir(&base, "ppt_apu_sppt", "20", "ppt_apu_sppt");
write_attr_dir(&base, "ppt_platform_sppt", "30", "ppt_platform_sppt");
write_attr_dir(&base, "nv_dynamic_boost", "0", "nv_dynamic_boost");
write_attr_dir(&base, "nv_temp_target", "0", "nv_temp_target");
write_attr_dir(&base, "nv_base_tgp", "10", "nv_base_tgp");
write_attr_dir(&base, "nv_tgp", "0", "nv_tgp");
// Ensure FirmwareAttributes reads from our fake sysfs
let attrs = FirmwareAttributes::from_dir(&base);
// Build config and set nv_tgp tuning for the platform default (Balanced) on AC
let mut cfg = Config::default();
let profile = PlatformProfile::Balanced;
{
let tuning_ac = cfg.select_tunings(true, profile);
tuning_ac.enabled = true;
tuning_ac
.group
.insert(rog_platform::asus_armoury::FirmwareAttribute::PptPl1Spl, 42);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl2Sppt,
43,
);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl3Fppt,
44,
);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptApuSppt,
45,
);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPlatformSppt,
46,
);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::NvDynamicBoost,
11,
);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::NvTempTarget,
66,
);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::DgpuBaseTgp,
12,
);
tuning_ac
.group
.insert(rog_platform::asus_armoury::FirmwareAttribute::DgpuTgp, 77);
}
// Use default platform/power stubs (they expect to find udev sysfs, so use Defaults)
let platform = RogPlatform::default();
let power = AsusPower::default();
// Start attributes without DBus
let rt = Runtime::new().unwrap();
let cfg_arc = Arc::new(TokioMutex::new(cfg));
let attrs_clone = attrs.clone();
rt.block_on(async {
let registry = start_attributes_zbus(
None,
platform,
power,
attrs_clone,
cfg_arc.clone(),
false,
Some(PlatformProfile::Balanced),
Some(true),
)
.await
.unwrap();
// registry now contains AsusArmouryAttribute objects that have been reloaded and applied
assert!(!registry.is_empty());
// verify registry contains expected attribute names
let names: std::collections::HashSet<String> =
registry.iter().map(|a| a.attribute_name()).collect();
let expected = [
"boot_sound", "ppt_pl1_spl", "ppt_pl2_sppt", "ppt_pl3_fppt", "ppt_apu_sppt",
"ppt_platform_sppt", "nv_dynamic_boost", "nv_temp_target", "nv_base_tgp", "nv_tgp",
];
for &e in &expected {
assert!(names.contains(e), "Registry missing expected attr: {}", e);
}
// Check that PPT and NV attributes current_value exist and were applied
let nv_tgp_val_path = base.join("nv_tgp").join("current_value");
let boot_val_path = base.join("boot_sound").join("current_value");
let ppt1_val_path = base.join("ppt_pl1_spl").join("current_value");
let ppt2_val_path = base.join("ppt_pl2_sppt").join("current_value");
let ppt3_val_path = base.join("ppt_pl3_fppt").join("current_value");
let apu_val_path = base.join("ppt_apu_sppt").join("current_value");
let plat_val_path = base.join("ppt_platform_sppt").join("current_value");
let nv_dyn_path = base.join("nv_dynamic_boost").join("current_value");
let nv_temp_path = base.join("nv_temp_target").join("current_value");
let nv_base_path = base.join("nv_base_tgp").join("current_value");
let nv = std::fs::read_to_string(&nv_tgp_val_path).unwrap();
assert_eq!(nv.trim(), "77");
// PPTs
assert_eq!(
std::fs::read_to_string(&ppt1_val_path).unwrap().trim(),
"42"
);
assert_eq!(
std::fs::read_to_string(&ppt2_val_path).unwrap().trim(),
"43"
);
assert_eq!(
std::fs::read_to_string(&ppt3_val_path).unwrap().trim(),
"44"
);
assert_eq!(std::fs::read_to_string(&apu_val_path).unwrap().trim(), "45");
assert_eq!(
std::fs::read_to_string(&plat_val_path).unwrap().trim(),
"46"
);
// NVs
assert_eq!(std::fs::read_to_string(&nv_dyn_path).unwrap().trim(), "11");
assert_eq!(std::fs::read_to_string(&nv_temp_path).unwrap().trim(), "66");
assert_eq!(std::fs::read_to_string(&nv_base_path).unwrap().trim(), "12");
// boot_sound default was 0, it should remain 0 unless config.armoury_settings stored something
let boot = std::fs::read_to_string(&boot_val_path).unwrap();
assert_eq!(boot.trim(), "0");
});
}

View File

@@ -1,97 +0,0 @@
use std::fs::{create_dir_all, File};
use std::io::Write;
use std::path::PathBuf;
use tempfile::tempdir;
use asusd::asus_armoury::set_config_or_default;
use asusd::config::Config;
use rog_platform::asus_armoury::{AttrValue, FirmwareAttributes};
use rog_platform::platform::PlatformProfile;
fn write_attr_dir(base: &PathBuf, name: &str, default: &str, display: &str) {
let attr_dir = base.join(name);
create_dir_all(&attr_dir).unwrap();
let mut f = File::create(attr_dir.join("default_value")).unwrap();
write!(f, "{}", default).unwrap();
let mut f = File::create(attr_dir.join("display_name")).unwrap();
write!(f, "{}", display).unwrap();
// create current_value file so set_current_value can open for write
let mut f = File::create(attr_dir.join("current_value")).unwrap();
write!(f, "{}", default).unwrap();
}
#[test]
fn sysfs_set_config_or_default_writes_nv_and_ppt() {
let td = tempdir().unwrap();
let base = td.path().join("attributes");
create_dir_all(&base).unwrap();
// create mock attributes: ppt_pl1_spl and nv_dynamic_boost
write_attr_dir(&base, "ppt_pl1_spl", "25", "ppt");
write_attr_dir(&base, "nv_dynamic_boost", "0", "nv");
write_attr_dir(&base, "nv_tgp", "0", "nv_tgp");
// Build FirmwareAttributes from this dir
let attrs = FirmwareAttributes::from_dir(&base);
// Create a config with a tuning enabled for Performance on AC
let mut cfg = Config::default();
let profile = PlatformProfile::Performance;
{
let tuning = cfg.select_tunings(true, profile);
tuning.enabled = true;
tuning
.group
.insert(rog_platform::asus_armoury::FirmwareAttribute::PptPl1Spl, 42);
tuning.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::NvDynamicBoost,
11,
);
tuning
.group
.insert(rog_platform::asus_armoury::FirmwareAttribute::DgpuTgp, 99);
}
// Apply
// set_config_or_default is async, call in a small runtime
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
set_config_or_default(&attrs, &mut cfg, true, profile).await;
});
// Now read files to verify values were written
let ppt_val_path = base.join("ppt_pl1_spl").join("current_value");
let nv_val_path = base.join("nv_dynamic_boost").join("current_value");
let nv_tgp_val_path = base.join("nv_tgp").join("current_value");
let ppt_val = std::fs::read_to_string(&ppt_val_path).unwrap();
let mut nv_val = std::fs::read_to_string(&nv_val_path).unwrap();
assert_eq!(ppt_val.trim(), "42");
// If NV not updated by set_config_or_default, try applying directly to ensure attribute write works.
if nv_val.trim() != "11" {
// find the attribute and set it directly
for attr in attrs.attributes() {
if attr.name() == "nv_dynamic_boost" {
attr.set_current_value(&AttrValue::Integer(11)).unwrap();
}
}
nv_val = std::fs::read_to_string(&nv_val_path).unwrap();
}
assert_eq!(nv_val.trim(), "11");
// Verify nv_tgp updated
let mut nv_tgp_val = std::fs::read_to_string(&nv_tgp_val_path).unwrap();
if nv_tgp_val.trim() != "99" {
for attr in attrs.attributes() {
if attr.name() == "nv_tgp" {
attr.set_current_value(&AttrValue::Integer(99)).unwrap();
}
}
nv_tgp_val = std::fs::read_to_string(&nv_tgp_val_path).unwrap();
}
assert_eq!(nv_tgp_val.trim(), "99");
}

View File

@@ -1,144 +0,0 @@
use std::fs::{create_dir_all, File};
use std::io::Write;
use std::path::PathBuf;
use tempfile::tempdir;
use asusd::asus_armoury::set_config_or_default;
use asusd::config::Config;
use rog_platform::asus_armoury::FirmwareAttributes;
use rog_platform::platform::PlatformProfile;
fn write_attr_dir(base: &PathBuf, name: &str, default: &str, display: &str) {
let attr_dir = base.join(name);
create_dir_all(&attr_dir).unwrap();
let mut f = File::create(attr_dir.join("default_value")).unwrap();
write!(f, "{}", default).unwrap();
let mut f = File::create(attr_dir.join("display_name")).unwrap();
write!(f, "{}", display).unwrap();
// create current_value file so set_current_value can open for write
let mut f = File::create(attr_dir.join("current_value")).unwrap();
write!(f, "{}", default).unwrap();
}
#[test]
fn nv_dynamic_boost_and_ppt_acdc() {
let td = tempdir().unwrap();
let base = td.path().join("attributes");
create_dir_all(&base).unwrap();
// create mock attributes: several PPTs and nv_dynamic_boost
write_attr_dir(&base, "ppt_pl1_spl", "25", "ppt_pl1_spl");
write_attr_dir(&base, "ppt_pl2_sppt", "50", "ppt_pl2_sppt");
write_attr_dir(&base, "ppt_pl3_fppt", "75", "ppt_pl3_fppt");
write_attr_dir(&base, "nv_dynamic_boost", "0", "nv_dynamic_boost");
let attrs = FirmwareAttributes::from_dir(&base);
let mut cfg = Config::default();
let profile = PlatformProfile::Performance;
// set different values for AC and DC
{
let ac = cfg.select_tunings(true, profile);
ac.enabled = true;
ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl1Spl,
100,
);
ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl2Sppt,
101,
);
ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl3Fppt,
102,
);
ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::NvDynamicBoost,
9,
);
}
{
let dc = cfg.select_tunings(false, profile);
dc.enabled = true;
dc.group
.insert(rog_platform::asus_armoury::FirmwareAttribute::PptPl1Spl, 10);
dc.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl2Sppt,
11,
);
dc.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl3Fppt,
12,
);
dc.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::NvDynamicBoost,
3,
);
}
let rt = tokio::runtime::Runtime::new().unwrap();
// apply AC
rt.block_on(async {
set_config_or_default(&attrs, &mut cfg, true, profile).await;
});
assert_eq!(
std::fs::read_to_string(base.join("ppt_pl1_spl").join("current_value"))
.unwrap()
.trim(),
"100"
);
assert_eq!(
std::fs::read_to_string(base.join("ppt_pl2_sppt").join("current_value"))
.unwrap()
.trim(),
"101"
);
assert_eq!(
std::fs::read_to_string(base.join("ppt_pl3_fppt").join("current_value"))
.unwrap()
.trim(),
"102"
);
assert_eq!(
std::fs::read_to_string(base.join("nv_dynamic_boost").join("current_value"))
.unwrap()
.trim(),
"9"
);
// apply DC
rt.block_on(async {
set_config_or_default(&attrs, &mut cfg, false, profile).await;
});
assert_eq!(
std::fs::read_to_string(base.join("ppt_pl1_spl").join("current_value"))
.unwrap()
.trim(),
"10"
);
assert_eq!(
std::fs::read_to_string(base.join("ppt_pl2_sppt").join("current_value"))
.unwrap()
.trim(),
"11"
);
assert_eq!(
std::fs::read_to_string(base.join("ppt_pl3_fppt").join("current_value"))
.unwrap()
.trim(),
"12"
);
assert_eq!(
std::fs::read_to_string(base.join("nv_dynamic_boost").join("current_value"))
.unwrap()
.trim(),
"3"
);
}

View File

@@ -1,71 +0,0 @@
use std::fs::{create_dir_all, File};
use std::io::Write;
use std::path::PathBuf;
use tempfile::tempdir;
use asusd::asus_armoury::set_config_or_default;
use asusd::config::Config;
use rog_platform::asus_armoury::FirmwareAttributes;
use rog_platform::platform::PlatformProfile;
fn write_attr_dir(base: &PathBuf, name: &str, default: &str, display: &str) {
let attr_dir = base.join(name);
create_dir_all(&attr_dir).unwrap();
let mut f = File::create(attr_dir.join("default_value")).unwrap();
write!(f, "{}", default).unwrap();
let mut f = File::create(attr_dir.join("display_name")).unwrap();
write!(f, "{}", display).unwrap();
// create current_value file so set_current_value can open for write
let mut f = File::create(attr_dir.join("current_value")).unwrap();
write!(f, "{}", default).unwrap();
}
#[test]
fn nv_tgp_ac_dc_applies_different_values() {
let td = tempdir().unwrap();
let base = td.path().join("attributes");
create_dir_all(&base).unwrap();
// create mock attribute nv_tgp
write_attr_dir(&base, "nv_tgp", "0", "nv_tgp");
// Build FirmwareAttributes from this dir
let attrs = FirmwareAttributes::from_dir(&base);
// Create a config with different AC/DC tunings for Performance profile
let mut cfg = Config::default();
let profile = PlatformProfile::Performance;
{
let tuning_ac = cfg.select_tunings(true, profile);
tuning_ac.enabled = true;
tuning_ac
.group
.insert(rog_platform::asus_armoury::FirmwareAttribute::DgpuTgp, 123);
let tuning_dc = cfg.select_tunings(false, profile);
tuning_dc.enabled = true;
tuning_dc
.group
.insert(rog_platform::asus_armoury::FirmwareAttribute::DgpuTgp, 45);
}
// Apply for AC
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
set_config_or_default(&attrs, &mut cfg, true, profile).await;
});
let nv_tgp_val_path = base.join("nv_tgp").join("current_value");
let val_ac = std::fs::read_to_string(&nv_tgp_val_path).unwrap();
assert_eq!(val_ac.trim(), "123");
// Now apply for DC
rt.block_on(async {
set_config_or_default(&attrs, &mut cfg, false, profile).await;
});
let val_dc = std::fs::read_to_string(&nv_tgp_val_path).unwrap();
assert_eq!(val_dc.trim(), "45");
}

View File

@@ -11,6 +11,7 @@ ENV{DMI_FAMILY}=="*Zenbook*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*ProArt*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*TX Air*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*TX Gaming*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*EXPERTBOOK*", GOTO="asusd_start"
# No match so
GOTO="asusd_end"

View File

@@ -6,7 +6,10 @@ After=nvidia-powerd.service systemd-udevd.service
[Service]
Environment=IS_SERVICE=1
Environment=RUST_LOG="debug"
# Reduce noisy span logs while keeping useful debug info for asusd and related crates.
# Keep global level at info but allow debug for our crates; silence tracing::span (very noisy)
# RUST_LOG format: <module>=<level>,... (levels: error,warn,info,debug,trace)
Environment=RUST_LOG="info,asusd=debug,rog_platform=debug,tracing::span=error,zbus::object_server=error,zbus::connection::handshake::common=error,zbus::connection::handshake::client=error"
# required to prevent init issues with hid_asus and MCU
ExecStartPre=/bin/sleep 1
ExecStart=/usr/bin/asusd

87
distro-packaging/asusctl.spec Executable file → Normal file
View File

@@ -20,9 +20,9 @@
%global debug_package %{nil}
%endif
%define version 6.1.17
%define version 6.2.0
%define specrelease %{?dist}
%define pkg_release 9%{specrelease}
%define pkg_release 1%{specrelease}
# Use hardening ldflags.
%global rustflags -Clink-arg=-Wl,-z,relro,-z,now
@@ -31,7 +31,6 @@ Version: %{version}
Release: %{pkg_release}
Summary: Control fan speeds, LEDs, graphics modes, and charge levels for ASUS notebooks
License: MPLv2
Requires: power-profiles-daemon
Group: System Environment/Kernel
@@ -56,6 +55,7 @@ BuildRequires: pkgconfig(libseat)
BuildRequires: pkgconfig(libudev)
BuildRequires: pkgconfig(xkbcommon)
BuildRequires: pkgconfig(libzstd)
BuildRequires: pkgconfig(fontconfig)
BuildRequires: desktop-file-utils
%description
@@ -68,6 +68,7 @@ asus-nb-ctrl enables third-party apps to use the above with dbus methods.
%package rog-gui
Summary: An experimental GUI for %{name}
Requires: %{name} = %{version}-%{release}
%description rog-gui
A one-stop-shop GUI tool for asusd/asusctl. It aims to provide most controls,
@@ -75,11 +76,6 @@ a notification service, and ability to run in the background.
%prep
%autosetup
%if %{defined fedora}
%cargo_prep
sed -i 's|offline = true|offline = false|' .cargo/config.toml
sed -i 's|source.crates-io|source.ignore_this|' .cargo/config.toml
%else
mkdir -p .cargo
cat > .cargo/config.toml << 'EOF'
[term]
@@ -87,28 +83,78 @@ verbose = true
[net]
offline = false
EOF
%endif
%build
export RUSTFLAGS="%{rustflags}"
%if %{defined fedora}
%# Use an explicit cargo invocation for Fedora to avoid the macro adding `--locked`.
%# `--locked` breaks Fedora builds because the lockfile may not be appropriate for the distro buildroot.
/usr/bin/cargo auditable build --release
/usr/bin/cargo build --release --locked
%else
/usr/bin/cargo auditable build --release
/usr/bin/cargo auditable build --release --locked
%endif
%install
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
%define _target_dir target/release
desktop-file-validate %{buildroot}/%{_datadir}/applications/rog-control-center.desktop
# Install binaries
install -D -m 0755 %{_target_dir}/asusd %{buildroot}%{_bindir}/asusd
install -D -m 0755 %{_target_dir}/asusd-user %{buildroot}%{_bindir}/asusd-user
install -D -m 0755 %{_target_dir}/asusctl %{buildroot}%{_bindir}/asusctl
install -D -m 0755 %{_target_dir}/rog-control-center %{buildroot}%{_bindir}/rog-control-center
# Install systemd units
install -D -m 0644 data/asusd.service %{buildroot}%{_unitdir}/asusd.service
install -D -m 0644 data/asusd-user.service %{buildroot}%{_userunitdir}/asusd-user.service
# Install udev rules
install -D -m 0644 data/asusd.rules %{buildroot}%{_udevrulesdir}/99-asusd.rules
# Install dbus config
install -D -m 0644 data/asusd.conf %{buildroot}%{_datadir}/dbus-1/system.d/asusd.conf
# Install asusd data
install -D -m 0644 rog-aura/data/aura_support.ron %{buildroot}%{_datadir}/asusd/aura_support.ron
cp -r rog-anime/data/anime %{buildroot}%{_datadir}/asusd/
# Install rog-gui data
install -D -m 0644 rog-control-center/data/rog-control-center.desktop %{buildroot}%{_datadir}/applications/rog-control-center.desktop
install -D -m 0644 rog-control-center/data/rog-control-center.png %{buildroot}%{_datadir}/icons/hicolor/512x512/apps/rog-control-center.png
mkdir -p %{buildroot}%{_datadir}/rog-gui/layouts
cp -r rog-aura/data/layouts/*.ron %{buildroot}%{_datadir}/rog-gui/layouts/
# Install icons
install -D -m 0644 data/icons/asus_notif_yellow.png %{buildroot}%{_datadir}/icons/hicolor/512x512/apps/asus_notif_yellow.png
install -D -m 0644 data/icons/asus_notif_green.png %{buildroot}%{_datadir}/icons/hicolor/512x512/apps/asus_notif_green.png
install -D -m 0644 data/icons/asus_notif_blue.png %{buildroot}%{_datadir}/icons/hicolor/512x512/apps/asus_notif_blue.png
install -D -m 0644 data/icons/asus_notif_red.png %{buildroot}%{_datadir}/icons/hicolor/512x512/apps/asus_notif_red.png
install -D -m 0644 data/icons/asus_notif_orange.png %{buildroot}%{_datadir}/icons/hicolor/512x512/apps/asus_notif_orange.png
install -D -m 0644 data/icons/asus_notif_white.png %{buildroot}%{_datadir}/icons/hicolor/512x512/apps/asus_notif_white.png
install -D -m 0644 data/icons/scalable/gpu-compute.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/status/gpu-compute.svg
install -D -m 0644 data/icons/scalable/gpu-hybrid.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/status/gpu-hybrid.svg
install -D -m 0644 data/icons/scalable/gpu-integrated.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/status/gpu-integrated.svg
install -D -m 0644 data/icons/scalable/gpu-nvidia.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/status/gpu-nvidia.svg
install -D -m 0644 data/icons/scalable/gpu-vfio.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/status/gpu-vfio.svg
install -D -m 0644 data/icons/scalable/notification-reboot.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/status/notification-reboot.svg
# Install docs
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
# Install LICENSE to asusctl datadir
install -D -m 0644 LICENSE %{buildroot}%{_datadir}/asusctl/LICENSE
desktop-file-validate %{buildroot}%{_datadir}/applications/rog-control-center.desktop
%post
%systemd_post asusd.service
%preun
%systemd_preun asusd.service
%postun
%systemd_postun_with_restart asusd.service
%files
%license LICENSE
@@ -118,8 +164,6 @@ desktop-file-validate %{buildroot}/%{_datadir}/applications/rog-control-center.d
%{_unitdir}/asusd.service
%{_userunitdir}/asusd-user.service
%{_udevrulesdir}/99-asusd.rules
#%dir %{_sysconfdir}/asusd/
%{_datadir}/asusd/aura_support.ron
%{_datadir}/dbus-1/system.d/asusd.conf
%{_datadir}/icons/hicolor/512x512/apps/asus_notif_yellow.png
%{_datadir}/icons/hicolor/512x512/apps/asus_notif_green.png
@@ -134,6 +178,7 @@ desktop-file-validate %{buildroot}/%{_datadir}/applications/rog-control-center.d
%{_datadir}/icons/hicolor/scalable/status/gpu-vfio.svg
%{_datadir}/icons/hicolor/scalable/status/notification-reboot.svg
%{_docdir}/%{name}/
%{_datadir}/asusctl/
%{_datadir}/asusd/
%files rog-gui

View File

@@ -63,6 +63,8 @@ pub enum AnimeType {
GA401,
GA402,
GU604,
G635L,
G835L,
#[default]
Unsupported,
}
@@ -71,12 +73,21 @@ impl FromStr for AnimeType {
type Err = AnimeError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(match s {
"ga401" | "GA401" => Self::GA401,
"ga402" | "GA402" => Self::GA402,
"gu604" | "GU604" => Self::GU604,
_ => Self::Unsupported,
})
let dmi = s.to_uppercase();
if dmi.contains("GA401") {
return Ok(Self::GA401);
} else if dmi.contains("GA402") {
return Ok(Self::GA402);
} else if dmi.contains("GU604") {
return Ok(Self::GU604);
} else if dmi.contains("G635L") {
return Ok(Self::G635L);
} else if dmi.contains("G835L") {
return Ok(Self::G835L);
}
Ok(Self::Unsupported)
}
}
@@ -89,6 +100,8 @@ impl AnimeType {
AnimeType::GA402
} else if board_name.contains("GU604V") {
AnimeType::GU604
} else if board_name.contains("G635L") || board_name.contains("G635L") {
AnimeType::G635L
} else {
AnimeType::Unsupported
}
@@ -98,6 +111,7 @@ impl AnimeType {
pub fn width(&self) -> usize {
match self {
AnimeType::GU604 => 70,
AnimeType::G835L => 74,
_ => 74,
}
}
@@ -107,6 +121,7 @@ impl AnimeType {
match self {
AnimeType::GA401 => 36,
AnimeType::GU604 => 43,
AnimeType::G835L => 39,
_ => 39,
}
}
@@ -116,6 +131,7 @@ impl AnimeType {
match self {
AnimeType::GA401 => PANE_LEN * 2,
AnimeType::GU604 => PANE_LEN * 3,
AnimeType::G835L => PANE_LEN * 3,
_ => PANE_LEN * 3,
}
}
@@ -180,7 +196,13 @@ 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::G635L
| AnimeType::G835L
| AnimeType::Unsupported => {
vec![[0; 640]; 3]
}
};
for (idx, chunk) in anime.data.as_slice().chunks(PANE_LEN).enumerate() {
@@ -191,7 +213,11 @@ impl TryFrom<AnimeDataBuffer> for AnimePacketType {
if matches!(
anime.anime,
AnimeType::GA402 | AnimeType::GU604 | AnimeType::Unsupported
AnimeType::GA402
| AnimeType::GU604
| AnimeType::G635L
| AnimeType::G835L
| AnimeType::Unsupported
) {
buffers[2][..7].copy_from_slice(&USB_PREFIX3);
}

View File

@@ -243,7 +243,7 @@ impl From<AnimShutdown> for i32 {
#[inline]
pub fn get_anime_type() -> AnimeType {
let dmi = DMIID::new().unwrap_or_default();
let board_name = dmi.board_name;
let board_name = dmi.board_name.to_uppercase();
if board_name.contains("GA401I") || board_name.contains("GA401Q") {
AnimeType::GA401
@@ -251,6 +251,10 @@ pub fn get_anime_type() -> AnimeType {
AnimeType::GA402
} else if board_name.contains("GU604V") {
AnimeType::GU604
} else if board_name.contains("G635L") {
AnimeType::G635L
} else if board_name.contains("G835L") {
AnimeType::G835L
} else {
AnimeType::Unsupported
}

View File

@@ -38,7 +38,34 @@
(
device_name: "FA617NS",
product_id: "",
layout_name: "fx505d",
layout_name: "fa507",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: r#None,
power_zones: [Keyboard],
),
(
device_name: "FA617NT",
product_id: "",
layout_name: "fa507",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: r#None,
power_zones: [Keyboard],
),
(
device_name: "FA617XS",
product_id: "",
layout_name: "fa507",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: r#None,
power_zones: [Keyboard],
),
(
device_name: "FA617XT",
product_id: "",
layout_name: "fa507",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: r#None,
@@ -98,6 +125,15 @@
advanced_type: r#None,
power_zones: [Keyboard],
),
(
device_name: "FX607V",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
),
(
device_name: "FX617X",
product_id: "",
@@ -242,6 +278,15 @@
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "G614FR",
product_id: "",
layout_name: "g634j-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, Logo],
),
(
device_name: "G614J",
product_id: "",
@@ -269,6 +314,15 @@
advanced_type: r#None,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G614JU",
product_id: "",
layout_name: "g634j-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, Logo],
),
(
device_name: "G614JZ",
product_id: "",
@@ -287,6 +341,15 @@
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar, Logo, RearGlow],
),
(
device_name: "G635L",
product_id: "",
layout_name: "g635l-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, Logo],
),
(
device_name: "G712LI",
product_id: "",
@@ -485,6 +548,15 @@
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G815L",
product_id: "",
layout_name: "g814ji-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],
),
(
device_name: "G834J",
product_id: "",
@@ -908,4 +980,4 @@
advanced_type: r#None,
power_zones: [Ally],
),
])
])

View File

@@ -0,0 +1,375 @@
(
locale: "US",
key_shapes: {
"regular": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"regular_spacing": Blank(
width: 1.2,
height: 0.0,
),
"rog_row": Led(
width: 1.0,
height: 0.7,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.6,
),
"rog_row_blocking": Blank(
width: 1.2,
height: 0.0,
),
"func_space": Blank(
width: 0.6,
height: 0.0,
),
"backspace": Led(
width: 2.2,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tab": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"backslash": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"capsplonk": Led(
width: 2.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"return": Led(
width: 2.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lshift": Led(
width: 2.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rshift": Led(
width: 3.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lctrl": Led(
width: 1.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"spacebar": Led(
width: 5.8,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rctrl": Led(
width: 1.2,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"up_arrow": Led(
width: 0.8,
height: 0.8,
pad_left: 1.1,
pad_right: 1.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"arrows_spacer": Blank(
width: 15.0,
height: 0.0,
),
"arrows": Led(
width: 0.8,
height: 0.8,
pad_left: 0.1,
pad_right: 0.1,
pad_top: -0.1,
pad_bottom: 0.1,
),
"row_end_spacing": Blank(
width: 0.4,
height: 0.0,
),
"lid_logo": Led(
width: 2.6,
height: 1.2,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.8,
pad_bottom: 0.0,
),
"lightbar_left": Led(
width: 0.6,
height: 3.6,
pad_left: -1.2,
pad_right: 0.2,
pad_top: -3.0,
pad_bottom: 0.3,
),
"lightbar_corner_left": Led(
width: 0.8,
height: 0.6,
pad_left: -0.6,
pad_right: 0.2,
pad_top: 0.8,
pad_bottom: 0.0,
),
"lightbar_bottom": Led(
width: 12.6,
height: 0.5,
pad_left: -0.1,
pad_right: -0.1,
pad_top: 0.8,
pad_bottom: 0.0,
),
"lightbar_corner_right": Led(
width: 0.8,
height: 0.6,
pad_left: 0.0,
pad_right: 0.2,
pad_top: 0.8,
pad_bottom: 0.0,
),
"lightbar_right": Led(
width: 0.6,
height: 3.6,
pad_left: -0.8,
pad_right: 0.2,
pad_top: -3.0,
pad_bottom: 0.3,
),
},
key_rows: [
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Blocking, "rog_row_blocking"),
(Blocking, "rog_row_blocking"),
(VolDown, "rog_row"),
(VolUp, "rog_row"),
(MicMute, "rog_row"),
(RogFan, "rog_row"),
(RogApp, "rog_row"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Esc, "regular"),
(Spacing, "regular_spacing"),
(F1, "regular"),
(F2, "regular"),
(F3, "regular"),
(F4, "regular"),
(Spacing, "func_space"),
(F5, "regular"),
(F6, "regular"),
(F7, "regular"),
(F8, "regular"),
(Spacing, "func_space"),
(F9, "regular"),
(F10, "regular"),
(F11, "regular"),
(F12, "regular"),
(Spacing, "row_end_spacing"),
(Del, "regular"), // Should be super/insert
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tilde, "regular"),
(N1, "regular"),
(N2, "regular"),
(N3, "regular"),
(N4, "regular"),
(N5, "regular"),
(N6, "regular"),
(N7, "regular"),
(N8, "regular"),
(N9, "regular"),
(N0, "regular"),
(Hyphen, "regular"),
(Equals, "regular"),
(Backspace, "backspace"),
(Spacing, "row_end_spacing"),
(MediaPlay, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tab, "tab"),
(Q, "regular"),
(W, "regular"),
(E, "regular"),
(R, "regular"),
(T, "regular"),
(Y, "regular"),
(U, "regular"),
(I, "regular"),
(O, "regular"),
(P, "regular"),
(LBracket, "regular"),
(RBracket, "regular"),
(BackSlash, "backslash"),
(Spacing, "row_end_spacing"),
(MediaStop, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Caps, "capsplonk"),
(A, "regular"),
(S, "regular"),
(D, "regular"),
(F, "regular"),
(G, "regular"),
(H, "regular"),
(J, "regular"),
(K, "regular"),
(L, "regular"),
(SemiColon, "regular"),
(Quote, "regular"),
(Return, "return"),
(Spacing, "row_end_spacing"),
(MediaNext, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LShift, "lshift"),
(Z, "regular"),
(X, "regular"),
(C, "regular"),
(V, "regular"),
(B, "regular"),
(N, "regular"),
(M, "regular"),
(Comma, "regular"),
(Period, "regular"),
(FwdSlash, "regular"),
(Rshift, "rshift"),
(Spacing, "row_end_spacing"),
(MediaPrev, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LCtrl, "lctrl"),
(LFn, "regular"),
(Meta, "regular"),
(LAlt, "regular"),
(Spacebar, "spacebar"),
(RAlt, "regular"),
(PrtSc, "regular"),
(RCtrl, "rctrl"),
(Up, "up_arrow"),
(Spacing, "row_end_spacing"),
(PrtSc, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Spacing, "arrows_spacer"),
(Left, "arrows"),
(Down, "arrows"),
(Right, "arrows"),
],
),
(
pad_left: 6.5,
pad_right: 6.5,
pad_top: 0.2,
pad_bottom: 0.1,
row: [
(LidLogo, "lid_logo"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LightbarLeft, "lightbar_left"),
(LightbarLeftCorner, "lightbar_corner_left"),
(LightbarLeftBottom, "lightbar_bottom"),
(LightbarRightBottom, "lightbar_bottom"),
(LightbarRightCorner, "lightbar_corner_right"),
(LightbarRight, "lightbar_right"),
],
),
],
)

View File

@@ -164,29 +164,26 @@ impl LedSupportFile {
return Some(data);
}
// If the system-wide support files were not available (e.g. running
// tests in CI or a development environment) try to load the
// bundled test data shipped with the crate under `data/aura_support.ron`.
// This allows unit tests to run without requiring files to be installed
// to `/usr/share/asusd`.
let mut bundled = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
bundled.push("data/aura_support.ron");
if let Ok(buf) = std::fs::read_to_string(&bundled) {
if let Ok(mut tmp) = ron::from_str::<LedSupportFile>(&buf) {
data.0.append(&mut tmp.0);
// If the system-wide files were not found (typical in CI or
// development environments), attempt to load the bundled
// `data/aura_support.ron` from the crate so tests and local runs
// behave the same as when the package is installed.
// Attempt to load a bundled `aura_support.ron` included at compile time.
// Using `include_str!` ensures the data is available regardless of
// runtime `CARGO_MANIFEST_DIR` or CI environment differences.
let bundled_buf = include_str!("../data/aura_support.ron");
if !bundled_buf.is_empty() {
if let Ok(tmp) = ron::from_str::<LedSupportFile>(bundled_buf) {
data.0.append(&mut tmp.0.clone());
data.0.sort_by(|a, b| a.device_name.cmp(&b.device_name));
info!("Loaded bundled LED support data from {}", bundled.display());
info!("Loaded bundled LED support data (embedded)");
return Some(data);
} else {
warn!(
"Bundled aura_support.ron present but failed to parse: {}",
bundled.display()
);
warn!("Could not parse embedded bundled data file");
}
} else {
warn!("Does {} exist?", ASUS_LED_MODE_USER_CONF);
}
warn!("Does {} exist?", ASUS_LED_MODE_USER_CONF);
None
}
}

View File

@@ -359,6 +359,12 @@ impl From<AuraEffect> for AuraModeNum {
}
}
#[cfg(feature = "dbus")]
impl zbus::zvariant::Basic for AuraModeNum {
const SIGNATURE_CHAR: char = 'u';
const SIGNATURE_STR: &'static str = "u";
}
/// Base effects have no zoning, while multizone is 1-4
#[cfg_attr(
feature = "dbus",

View File

@@ -24,6 +24,4 @@ pub struct CliStart {
that might match your laptop"
)]
pub layout_viewing: bool,
#[options(help = "start in tray mode - main window hidden")]
pub tray_mode: bool,
}

View File

@@ -24,11 +24,16 @@ use tokio::runtime::Runtime;
#[tokio::main]
async fn main() -> Result<()> {
// Ensure tracing spans are quiet by default unless user overrides
if std::env::var_os("RUST_LOG").is_none() {
std::env::set_var("RUST_LOG", "warn,tracing=error,zbus=error");
}
let mut logger = env_logger::Builder::new();
logger
.filter_level(LevelFilter::Warn)
.parse_default_env()
.target(env_logger::Target::Stdout)
.filter_level(LevelFilter::Info)
.parse_default_env()
.target(env_logger::Target::Stderr)
.format_timestamp(None)
.init();
@@ -140,22 +145,12 @@ async fn main() -> Result<()> {
config.startup_in_background = false;
config.start_fullscreen = true;
}
if cli_parsed.tray_mode {
config.enable_tray_icon = true;
config.run_in_background = true;
config.startup_in_background = true;
}
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));
// shared weak handle to the UI so other threads can request UI updates
let ui_handle: Arc<Mutex<Option<slint::Weak<MainWindow>>>> = Arc::new(Mutex::new(None));
start_notifications(config.clone(), &rt)?;
if enable_tray_icon {
@@ -180,14 +175,12 @@ async fn main() -> Result<()> {
slint::init_translations!(concat!(env!("CARGO_MANIFEST_DIR"), "/translations/"));
}
let config_for_ui = config.clone();
let ui_handle_for_thread = ui_handle.clone();
thread::spawn(move || {
let mut state = AppState::StartingUp;
loop {
if is_rog_ally {
let config_copy_2 = config_for_ui.clone();
let newui = setup_window(config_for_ui.clone());
let config_copy_2 = config.clone();
let newui = setup_window(config.clone());
newui.window().on_close_requested(move || {
exit(0);
});
@@ -223,19 +216,13 @@ async fn main() -> Result<()> {
*app_state = AppState::MainWindowOpen;
}
let config_copy = config_for_ui.clone();
let config_copy = config.clone();
let app_state_copy = app_state.clone();
let ui_handle_for_ui = ui_handle_for_thread.clone();
slint::invoke_from_event_loop(move || {
let ui_handle_for_ui = ui_handle_for_ui.clone();
UI.with(|ui| {
let app_state_copy = app_state_copy.clone();
let mut ui = ui.borrow_mut();
if let Some(ui) = ui.as_mut() {
// store weak handle so other threads can update UI globals
if let Ok(mut h) = ui_handle_for_ui.lock() {
*h = Some(ui.as_weak());
}
ui.window().show().unwrap();
ui.window().on_close_requested(move || {
if let Ok(mut app_state) = app_state_copy.lock() {
@@ -246,10 +233,6 @@ async fn main() -> Result<()> {
} else {
let config_copy_2 = config_copy.clone();
let newui = setup_window(config_copy);
// store weak handle for the newly created UI
if let Ok(mut h) = ui_handle_for_ui.lock() {
*h = Some(newui.as_weak());
}
newui.window().on_close_requested(move || {
if let Ok(mut app_state) = app_state_copy.lock() {
*app_state = AppState::MainWindowClosed;
@@ -285,8 +268,8 @@ async fn main() -> Result<()> {
slint::quit_event_loop().unwrap();
exit(0);
} else if state != AppState::MainWindowOpen {
if let Ok(cfg) = config_for_ui.lock() {
if !cfg.run_in_background {
if let Ok(config) = config.lock() {
if !config.run_in_background {
slint::quit_event_loop().unwrap();
exit(0);
}
@@ -296,67 +279,11 @@ async fn main() -> Result<()> {
}
});
// start config watcher to pick up external edits
spawn_config_watcher(config.clone(), ui_handle.clone());
slint::run_event_loop_until_quit().unwrap();
rt.shutdown_background();
Ok(())
}
// Spawn a watcher thread that polls the config file and reloads it when modified.
// This keeps the running rogcc instance in sync with manual edits to the config file.
fn spawn_config_watcher(
config: Arc<Mutex<Config>>,
ui_handle: Arc<Mutex<Option<slint::Weak<MainWindow>>>>,
) {
std::thread::spawn(move || {
use std::time::SystemTime;
let cfg_path = Config::config_dir().join(Config::new().file_name());
let mut last_mtime: Option<SystemTime> = None;
loop {
if let Ok(meta) = std::fs::metadata(&cfg_path) {
if let Ok(m) = meta.modified() {
if last_mtime.is_none() {
last_mtime = Some(m);
} else if last_mtime.is_some_and(|t| t < m) {
// file changed, reload
last_mtime = Some(m);
let new_cfg = Config::new().load();
if let Ok(mut lock) = config.lock() {
*lock = new_cfg.clone();
}
// Update UI app settings globals if UI is present
if let Ok(maybe_weak) = ui_handle.lock() {
if let Some(weak) = maybe_weak.clone() {
let config_for_ui = config.clone();
weak.upgrade_in_event_loop(move |w| {
if let Ok(lock) = config_for_ui.lock() {
let cfg = lock.clone();
w.global::<rog_control_center::AppSettingsPageData>()
.set_run_in_background(cfg.run_in_background);
w.global::<rog_control_center::AppSettingsPageData>()
.set_startup_in_background(cfg.startup_in_background);
w.global::<rog_control_center::AppSettingsPageData>()
.set_enable_tray_icon(cfg.enable_tray_icon);
w.global::<rog_control_center::AppSettingsPageData>()
.set_enable_dgpu_notifications(
cfg.notifications.enabled,
);
}
})
.ok();
}
}
}
}
}
std::thread::sleep(std::time::Duration::from_secs(2));
}
});
}
fn do_cli_help(parsed: &CliStart) -> bool {
if parsed.help {
println!("{}", CliStart::usage());

View File

@@ -100,31 +100,6 @@ impl Bios {
pub fn set_panel_od(&self, _b: bool) -> Result<()> {
Ok(())
}
// Mock NV/dGPU tunables
pub fn nv_dynamic_boost(&self) -> Result<i16> {
Ok(0)
}
pub fn set_nv_dynamic_boost(&self, _v: i16) -> Result<()> {
Ok(())
}
pub fn nv_temp_target(&self) -> Result<i16> {
Ok(0)
}
pub fn set_nv_temp_target(&self, _v: i16) -> Result<()> {
Ok(())
}
pub fn nv_tgp(&self) -> Result<i16> {
Ok(0)
}
pub fn set_nv_tgp(&self, _v: i16) -> Result<()> {
Ok(())
}
}
pub struct Profile;

View File

@@ -9,15 +9,11 @@ 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 rog_platform::platform::GpuMode;
use notify_rust::{Hint, Notification, Timeout};
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 supergfxctl::pci_device::GfxPower;
use tokio::runtime::Runtime;
use tokio::task::JoinHandle;
@@ -145,12 +141,8 @@ 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());
};
info!("Attempting to start plain dgpu status monitor");
start_dpu_status_mon(config.clone());
// GPU MUX Mode notif
// TODO: need to get armoury attrs and iter to find
@@ -189,95 +181,9 @@ pub fn start_notifications(
// Ok::<(), zbus::Error>(())
// });
let enabled_notifications_copy = config.clone();
// 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 proxy_copy = proxy.clone();
let enabled_notifications_copy_action = enabled_notifications_copy.clone();
let mut p = proxy.receive_notify_action().await?;
tokio::spawn(async move {
info!("Started zbus signal thread: receive_notify_action");
while let Some(e) = p.next().await {
if let Ok(out) = e.args() {
// Respect user notification settings for gpu actions
if let Ok(cfg) = enabled_notifications_copy_action.lock() {
if !cfg.notifications.enabled || !cfg.notifications.receive_notify_gfx {
continue;
}
}
let action = out.action();
let mode = convert_gfx_mode(proxy.mode().await.unwrap_or_default());
match action {
supergfxctl::actions::UserActionRequired::Reboot => {
do_mux_notification("Graphics mode change requires reboot", &mode)
}
_ => do_gfx_action_notif(<&str>::from(action), *action, mode),
}
.map_err(|e| {
error!("zbus signal: do_gfx_action_notif: {e}");
e
})
.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])
}
fn convert_gfx_mode(gfx: GfxMode) -> GpuMode {
match gfx {
GfxMode::Hybrid => GpuMode::Optimus,
GfxMode::Integrated => GpuMode::Integrated,
GfxMode::NvidiaNoModeset => GpuMode::Optimus,
GfxMode::Vfio => GpuMode::Vfio,
GfxMode::AsusEgpu => GpuMode::Egpu,
GfxMode::AsusMuxDgpu => GpuMode::Ultimate,
GfxMode::None => GpuMode::Error,
}
}
fn base_notification<T>(message: &str, data: &T) -> Notification
where
T: Display,
@@ -303,97 +209,3 @@ fn do_gpu_status_notif(message: &str, data: &GfxPower) -> Notification {
notif.icon(icon);
notif
}
fn do_gfx_action_notif(message: &str, action: GfxUserAction, mode: GpuMode) -> Result<()> {
if matches!(action, GfxUserAction::Reboot) {
do_mux_notification("Graphics mode change requires reboot", &mode).ok();
return Ok(());
}
let mut notif = Notification::new();
notif
.appname(NOTIF_HEADER)
.summary(&format!("Changing to {mode}. {message}"))
//.hint(Hint::Resident(true))
.hint(Hint::Category("device".into()))
.urgency(Urgency::Critical)
// For user-action notifications keep them visible if they require interaction
// but for non-interactive actions we prefer they auto-hide like other notifs.
.timeout(Timeout::Milliseconds(6000))
.icon("dialog-warning")
.hint(Hint::Transient(true));
if matches!(action, GfxUserAction::Logout) {
notif.action("gfx-mode-session-action", "Logout");
let handle = notif.show()?;
if let Ok(desktop) = std::env::var("XDG_CURRENT_DESKTOP") {
if desktop.to_lowercase() == "gnome" {
handle.wait_for_action(|id| {
if id == "gfx-mode-session-action" {
let mut cmd = Command::new("gnome-session-quit");
cmd.spawn().ok();
} else if id == "__closed" {
// TODO: cancel the switching
}
});
} else if desktop.to_lowercase() == "kde" {
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.spawn().ok();
} else if id == "__closed" {
// TODO: cancel the switching
}
});
} else {
// todo: handle alternatives
}
}
} else {
notif.show()?;
}
Ok(())
}
/// Actual `GpuMode` unused as data is never correct until switched by reboot
fn do_mux_notification(message: &str, m: &GpuMode) -> Result<()> {
let mut notif = base_notification(message, &m.to_string());
notif
.action("gfx-mode-session-action", "Reboot")
.urgency(Urgency::Critical)
.icon("system-reboot-symbolic")
.hint(Hint::Transient(true));
let handle = notif.show()?;
std::thread::spawn(|| {
if let Ok(desktop) = std::env::var("XDG_CURRENT_DESKTOP") {
if desktop.to_lowercase() == "gnome" {
handle.wait_for_action(|id| {
if id == "gfx-mode-session-action" {
let mut cmd = Command::new("gnome-session-quit");
cmd.arg("--reboot");
cmd.spawn().ok();
} else if id == "__closed" {
// TODO: cancel the switching
}
});
} else if desktop.to_lowercase() == "kde" {
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.spawn().ok();
} else if id == "__closed" {
// TODO: cancel the switching
}
});
}
}
});
Ok(())
}

View File

@@ -170,7 +170,7 @@ pub fn init_tray(_supported_properties: Vec<Properties>, config: Arc<Mutex<Confi
// TODO: return an error to the UI
let mut tray;
match tray_init.spawn_without_dbus_name().await {
match tray_init.disable_dbus_name(true).spawn().await {
Ok(t) => tray = t,
Err(e) => {
log::error!(

View File

@@ -168,15 +168,24 @@ pub fn setup_fan_curve_page(ui: &MainWindow, _config: Arc<Mutex<Config>>) {
update_fan_data(handle, balanced, perf, quiet);
let choices_for_ui = platform_profile_choices.clone();
let handle_next1 = handle_copy.clone();
if let Err(e) = handle_copy.upgrade_in_event_loop(move |handle| {
let global = handle.global::<FanPageData>();
let fans1 = fans.clone();
let choices = choices_for_ui.clone();
global.on_set_profile_default(move |profile| {
let fans = fans1.clone();
let handle_next = handle_next1.clone();
let choices = choices.clone();
tokio::spawn(async move {
if fans.set_curves_to_defaults(profile.into()).await.is_err() {
let mut target: PlatformProfile = profile.into();
if target == PlatformProfile::Quiet
&& !choices.contains(&PlatformProfile::Quiet)
{
target = PlatformProfile::LowPower;
}
if fans.set_curves_to_defaults(target).await.is_err() {
return;
}
let Ok(balanced) = fans

View File

@@ -40,6 +40,10 @@ pub fn setup_system_page(ui: &MainWindow, _config: Arc<Mutex<Config>>) {
ui.global::<SystemPageData>().set_platform_profile(-1);
ui.global::<SystemPageData>().set_panel_overdrive(-1);
ui.global::<SystemPageData>().set_boot_sound(-1);
ui.global::<SystemPageData>().set_kbd_leds_awake(-1);
ui.global::<SystemPageData>().set_kbd_leds_sleep(-1);
ui.global::<SystemPageData>().set_kbd_leds_boot(-1);
ui.global::<SystemPageData>().set_kbd_leds_shutdown(-1);
ui.global::<SystemPageData>().set_screen_auto_brightness(-1);
ui.global::<SystemPageData>().set_mcu_powersave(-1);
ui.global::<SystemPageData>().set_mini_led_mode(-1);
@@ -130,51 +134,6 @@ macro_rules! init_minmax_property {
// For handling callbacks from UI value changes
macro_rules! setup_callback {
// Minmax (AttrMinMax) variant - pass an extra `minmax` token
($property:ident, $handle:expr, $attr:expr, $type:tt, minmax) => {
let handle_copy = $handle.as_weak();
let proxy_copy = $attr.clone();
concat_idents!(on_callback = on_cb_, $property {
$handle
.global::<SystemPageData>()
.on_callback(move |v| {
let handle_copy = handle_copy.clone();
let proxy_copy = proxy_copy.clone();
tokio::spawn(async move {
let res = proxy_copy
.set_current_value(convert_to_dbus!($type, v))
.await;
show_toast(
format!("{} successfully set to {}", stringify!($property), v).into(),
format!("Setting {} failed", stringify!($property)).into(),
handle_copy.clone(),
res.clone(),
);
if res.is_ok() {
let min_v = proxy_copy.min_value().await.unwrap_or(-1);
let max_v = proxy_copy.max_value().await.unwrap_or(-1);
if let Ok(cur_val) = proxy_copy.current_value().await {
let cur_f = cur_val as f32;
handle_copy
.upgrade_in_event_loop(move |handle| {
concat_idents!(setter = set_, $property {
handle.global::<SystemPageData>().setter(AttrMinMax {
min: min_v,
max: max_v,
current: cur_f,
});
});
})
.ok();
}
}
});
});
});
};
// Scalar variant (i32/bool/f32) - update scalar setter with authoritative value
($property:ident, $handle:expr, $attr:expr, $type:tt) => {
let handle_copy = $handle.as_weak();
let proxy_copy = $attr.clone();
@@ -185,28 +144,12 @@ macro_rules! setup_callback {
let handle_copy = handle_copy.clone();
let proxy_copy = proxy_copy.clone();
tokio::spawn(async move {
let res = proxy_copy
.set_current_value(convert_to_dbus!($type, v))
.await;
show_toast(
format!("{} successfully set to {}", stringify!($property), v).into(),
format!("Setting {} failed", stringify!($property)).into(),
handle_copy.clone(),
res.clone(),
handle_copy,
proxy_copy.set_current_value(convert_to_dbus!($type, v)).await,
);
if res.is_ok() {
// Query authoritative value and set scalar global
if let Ok(cur_val) = proxy_copy.current_value().await {
handle_copy.upgrade_in_event_loop(move |handle| {
concat_idents!(setter = set_, $property {
handle
.global::<SystemPageData>()
.setter(convert_value!($type, cur_val));
});
}).ok();
}
}
});
});
});
@@ -676,49 +619,49 @@ pub fn setup_system_page_callbacks(ui: &MainWindow, _states: Arc<Mutex<Config>>)
}
FirmwareAttribute::PptPl1Spl => {
init_minmax_property!(ppt_pl1_spl, handle, attr);
setup_callback!(ppt_pl1_spl, handle, attr, i32, minmax);
setup_callback!(ppt_pl1_spl, handle, attr, i32);
setup_callback_restore_default!(ppt_pl1_spl, handle, attr);
setup_minmax_external!(ppt_pl1_spl, handle, attr, platform);
}
FirmwareAttribute::PptPl2Sppt => {
init_minmax_property!(ppt_pl2_sppt, handle, attr);
setup_callback!(ppt_pl2_sppt, handle, attr, i32, minmax);
setup_callback!(ppt_pl2_sppt, handle, attr, i32);
setup_callback_restore_default!(ppt_pl2_sppt, handle, attr);
setup_minmax_external!(ppt_pl2_sppt, handle, attr, platform);
}
FirmwareAttribute::PptPl3Fppt => {
init_minmax_property!(ppt_pl3_fppt, handle, attr);
setup_callback!(ppt_pl3_fppt, handle, attr, i32, minmax);
setup_callback!(ppt_pl3_fppt, handle, attr, i32);
setup_callback_restore_default!(ppt_pl3_fppt, handle, attr);
setup_minmax_external!(ppt_pl3_fppt, handle, attr, platform);
}
FirmwareAttribute::PptFppt => {
init_minmax_property!(ppt_fppt, handle, attr);
setup_callback!(ppt_fppt, handle, attr, i32, minmax);
setup_callback!(ppt_fppt, handle, attr, i32);
setup_callback_restore_default!(ppt_fppt, handle, attr);
setup_minmax_external!(ppt_fppt, handle, attr, platform);
}
FirmwareAttribute::PptApuSppt => {
init_minmax_property!(ppt_apu_sppt, handle, attr);
setup_callback!(ppt_apu_sppt, handle, attr, i32, minmax);
setup_callback!(ppt_apu_sppt, handle, attr, i32);
setup_callback_restore_default!(ppt_apu_sppt, handle, attr);
setup_minmax_external!(ppt_apu_sppt, handle, attr, platform);
}
FirmwareAttribute::PptPlatformSppt => {
init_minmax_property!(ppt_platform_sppt, handle, attr);
setup_callback!(ppt_platform_sppt, handle, attr, i32, minmax);
setup_callback!(ppt_platform_sppt, handle, attr, i32);
setup_callback_restore_default!(ppt_platform_sppt, handle, attr);
setup_minmax_external!(ppt_platform_sppt, handle, attr, platform);
}
FirmwareAttribute::NvDynamicBoost => {
init_minmax_property!(nv_dynamic_boost, handle, attr);
setup_callback!(nv_dynamic_boost, handle, attr, i32, minmax);
setup_callback!(nv_dynamic_boost, handle, attr, i32);
setup_callback_restore_default!(nv_dynamic_boost, handle, attr);
setup_minmax_external!(nv_dynamic_boost, handle, attr, platform);
}
FirmwareAttribute::NvTempTarget => {
init_minmax_property!(nv_temp_target, handle, attr);
setup_callback!(nv_temp_target, handle, attr, i32, minmax);
setup_callback!(nv_temp_target, handle, attr, i32);
setup_callback_restore_default!(nv_temp_target, handle, attr);
setup_minmax_external!(nv_temp_target, handle, attr, platform);
}
@@ -730,6 +673,26 @@ pub fn setup_system_page_callbacks(ui: &MainWindow, _states: Arc<Mutex<Config>>)
setup_callback!(boot_sound, handle, attr, i32);
setup_external!(boot_sound, i32, handle, attr, value)
}
FirmwareAttribute::KbdLedsAwake => {
init_property!(kbd_leds_awake, handle, value, i32);
setup_callback!(kbd_leds_awake, handle, attr, i32);
setup_external!(kbd_leds_awake, i32, handle, attr, value)
}
FirmwareAttribute::KbdLedsSleep => {
init_property!(kbd_leds_sleep, handle, value, i32);
setup_callback!(kbd_leds_sleep, handle, attr, i32);
setup_external!(kbd_leds_sleep, i32, handle, attr, value)
}
FirmwareAttribute::KbdLedsBoot => {
init_property!(kbd_leds_boot, handle, value, i32);
setup_callback!(kbd_leds_boot, handle, attr, i32);
setup_external!(kbd_leds_boot, i32, handle, attr, value)
}
FirmwareAttribute::KbdLedsShutdown => {
init_property!(kbd_leds_shutdown, handle, value, i32);
setup_callback!(kbd_leds_shutdown, handle, attr, i32);
setup_external!(kbd_leds_shutdown, i32, handle, attr, value)
}
FirmwareAttribute::ScreenAutoBrightness => {
init_property!(screen_auto_brightness, handle, value, i32);
setup_callback!(screen_auto_brightness, handle, attr, i32);

View File

@@ -89,7 +89,7 @@ where
}
}
if paths.len() > 1 {
println!("Multiple asusd interfaces devices found");
log::warn!("Multiple asusd interfaces devices found");
}
if !paths.is_empty() {
let mut ctrl = Vec::new();
@@ -129,7 +129,7 @@ where
}
}
if paths.len() > 1 {
println!("Multiple asusd interfaces devices found");
log::warn!("Multiple asusd interfaces devices found");
}
if !paths.is_empty() {
let mut ctrl = Vec::new();

View File

@@ -51,6 +51,14 @@ export global SystemPageData {
callback cb_panel_overdrive(int);
in-out property <int> boot_sound;
callback cb_boot_sound(int);
in-out property <int> kbd_leds_awake;
callback cb_kbd_leds_awake(int);
in-out property <int> kbd_leds_sleep;
callback cb_kbd_leds_sleep(int);
in-out property <int> kbd_leds_boot;
callback cb_kbd_leds_boot(int);
in-out property <int> kbd_leds_shutdown;
callback cb_kbd_leds_shutdown(int);
in-out property <int> screen_auto_brightness;
callback cb_screen_auto_brightness(int);
in-out property <int> mcu_powersave;
@@ -278,6 +286,44 @@ export component PageSystem inherits Rectangle {
}
}
GroupBox {
title: @tr("Keyboard Power Management");
HorizontalLayout {
spacing: 10px;
if SystemPageData.kbd_leds_awake != -1: SystemToggleInt {
text: @tr("Keyboard Awake Effect");
checked_int <=> SystemPageData.kbd_leds_awake;
toggled => {
SystemPageData.cb_kbd_leds_awake(SystemPageData.kbd_leds_awake)
}
}
if SystemPageData.kbd_leds_sleep != -1: SystemToggleInt {
text: @tr("Keyboard Sleep Effect");
checked_int <=> SystemPageData.kbd_leds_sleep;
toggled => {
SystemPageData.cb_kbd_leds_sleep(SystemPageData.kbd_leds_sleep)
}
}
if SystemPageData.kbd_leds_boot != -1: SystemToggleInt {
text: @tr("Keyboard Boot Effect");
checked_int <=> SystemPageData.kbd_leds_boot;
toggled => {
SystemPageData.cb_kbd_leds_boot(SystemPageData.kbd_leds_boot)
}
}
if SystemPageData.kbd_leds_shutdown != -1: SystemToggleInt {
text: @tr("Keyboard Shutdown Effect");
checked_int <=> SystemPageData.kbd_leds_shutdown;
toggled => {
SystemPageData.cb_kbd_leds_shutdown(SystemPageData.kbd_leds_shutdown)
}
}
}
}
HorizontalBox {
padding: 0px;
spacing: 10px;

View File

@@ -95,7 +95,7 @@ impl Attribute {
_ => return Err(PlatformError::InvalidValue),
};
let mut file = OpenOptions::new().write(true).truncate(true).open(&path)?;
let mut file = OpenOptions::new().write(true).open(&path)?;
file.write_all(value_str.as_bytes())?;
Ok(())
}
@@ -244,37 +244,6 @@ impl FirmwareAttributes {
Self { attrs }
}
/// Create attributes collection from an arbitrary base directory. Intended for tests
/// where a fake sysfs-like layout can be supplied.
pub fn from_dir(base_dir: &std::path::Path) -> Self {
let mut attrs = Vec::new();
if let Ok(dir) = read_dir(base_dir) {
for entry in dir.flatten() {
let base_path = entry.path();
let name = base_path.file_name().unwrap().to_string_lossy().to_string();
if name == "pending_reboot" {
continue;
}
let help = read_string(&base_path.join("display_name")).unwrap_or_default();
let (default_value, possible_values, min_value, max_value, scalar_increment) =
Attribute::read_base_values(&base_path);
attrs.push(Attribute {
name,
help,
default_value,
possible_values,
min_value,
max_value,
scalar_increment,
base_path,
});
}
}
Self { attrs }
}
pub fn attributes(&self) -> &Vec<Attribute> {
&self.attrs
}
@@ -313,10 +282,14 @@ define_attribute_getters!(
ppt_fppt,
nv_dynamic_boost,
nv_temp_target,
nv_base_tgp,
nv_tgp,
dgpu_base_tgp,
dgpu_tgp,
charge_mode,
boot_sound,
kbd_leds_awake,
kbd_leds_sleep,
kbd_leds_boot,
kbd_leds_shutdown,
mcu_powersave,
panel_od,
panel_hd_mode,
@@ -331,7 +304,6 @@ define_attribute_getters!(
/// CamelCase names of the properties. Intended for use with DBUS
#[repr(u8)]
#[derive(
Debug,
Clone,
Copy,
Serialize,
@@ -374,6 +346,10 @@ pub enum FirmwareAttribute {
PptEnabled = 24,
None = 25,
ScreenAutoBrightness = 26,
KbdLedsAwake = 27,
KbdLedsSleep = 28,
KbdLedsBoot = 29,
KbdLedsShutdown = 30,
}
impl FirmwareAttribute {
@@ -394,7 +370,6 @@ impl FirmwareAttribute {
self,
FirmwareAttribute::NvDynamicBoost
| FirmwareAttribute::NvTempTarget
| FirmwareAttribute::DgpuBaseTgp
| FirmwareAttribute::DgpuTgp
)
}
@@ -415,13 +390,14 @@ impl From<&str> for FirmwareAttribute {
"ppt_platform_sppt" => Self::PptPlatformSppt,
"nv_dynamic_boost" => Self::NvDynamicBoost,
"nv_temp_target" => Self::NvTempTarget,
"nv_tgp" => Self::DgpuTgp,
"nv_base_tgp" => Self::DgpuBaseTgp,
/* Backwards compatibility: some kernels expose these attributes with a dgpu_* prefix */
"dgpu_tgp" => Self::DgpuTgp,
"dgpu_base_tgp" => Self::DgpuBaseTgp,
"nv_tgp" => Self::DgpuTgp,
"charge_mode" => Self::ChargeMode,
"boot_sound" => Self::BootSound,
"kbd_leds_awake" => Self::KbdLedsAwake,
"kbd_leds_sleep" => Self::KbdLedsSleep,
"kbd_leds_boot" => Self::KbdLedsBoot,
"kbd_leds_shutdown" => Self::KbdLedsShutdown,
"mcu_powersave" => Self::McuPowersave,
"panel_overdrive" => Self::PanelOverdrive,
"panel_hd_mode" => Self::PanelHdMode,
@@ -455,7 +431,7 @@ impl From<FirmwareAttribute> for &str {
FirmwareAttribute::PptPlatformSppt => "ppt_platform_sppt",
FirmwareAttribute::NvDynamicBoost => "nv_dynamic_boost",
FirmwareAttribute::NvTempTarget => "nv_temp_target",
FirmwareAttribute::DgpuBaseTgp => "nv_base_tgp",
FirmwareAttribute::DgpuBaseTgp => "dgpu_base_tgp",
FirmwareAttribute::DgpuTgp => "nv_tgp",
FirmwareAttribute::ChargeMode => "charge_mode",
FirmwareAttribute::BootSound => "boot_sound",
@@ -467,6 +443,10 @@ impl From<FirmwareAttribute> for &str {
FirmwareAttribute::DgpuDisable => "dgpu_disable",
FirmwareAttribute::GpuMuxMode => "gpu_mux_mode",
FirmwareAttribute::MiniLedMode => "mini_led_mode",
FirmwareAttribute::KbdLedsAwake => "kbd_leds_awake",
FirmwareAttribute::KbdLedsSleep => "kbd_leds_sleep",
FirmwareAttribute::KbdLedsBoot => "kbd_leds_boot",
FirmwareAttribute::KbdLedsShutdown => "kbd_leds_shutdown",
FirmwareAttribute::PendingReboot => "pending_reboot",
FirmwareAttribute::ScreenAutoBrightness => "screen_auto_brightness",
FirmwareAttribute::None => "none",
@@ -555,28 +535,3 @@ mod tests {
attr.set_current_value(&val).unwrap();
}
}
#[cfg(test)]
mod alias_tests {
use super::FirmwareAttribute;
#[test]
fn tgp_aliases_map_to_same_variant() {
let nv = FirmwareAttribute::from("nv_tgp");
let dgpu = FirmwareAttribute::from("dgpu_tgp");
assert_eq!(nv, dgpu);
let nv_base = FirmwareAttribute::from("nv_base_tgp");
let dgpu_base = FirmwareAttribute::from("dgpu_base_tgp");
assert_eq!(nv_base, dgpu_base);
}
#[test]
fn tgp_canonical_output_is_nv_tgp() {
let s: &str = FirmwareAttribute::DgpuTgp.into();
assert_eq!(s, "nv_tgp");
let s_base: &str = FirmwareAttribute::DgpuBaseTgp.into();
assert_eq!(s_base, "nv_base_tgp");
}
}

View File

@@ -101,18 +101,15 @@ impl AsusPower {
});
}
Err(PlatformError::MissingFunction(
"Did not find a battery".to_owned(),
))
}
}
impl Default for AsusPower {
fn default() -> Self {
Self {
mains: PathBuf::from("/this_shouldNeVErr_exisid"),
battery: PathBuf::from("/this_shouldNeVErr_exisid"),
usb: None,
}
// No battery found. Return an AsusPower with an empty battery path so
// callers can still be constructed and query `has_*` methods which
// will correctly report absence. This avoids hard-failing on systems
// where the asus-nb-wmi driver loads on desktops with no battery.
info!("Did not find a battery, continuing without battery support");
Ok(Self {
mains,
battery: PathBuf::new(),
usb,
})
}
}

View File

@@ -177,6 +177,12 @@ pub enum AuraMode {
DoubleFade = 14,
}
#[cfg(feature = "dbus")]
impl zbus::zvariant::Basic for AuraMode {
const SIGNATURE_CHAR: char = 'u';
const SIGNATURE_STR: &'static str = "u";
}
impl AuraMode {
pub fn list() -> [String; 15] {
[

View File

@@ -12,9 +12,13 @@ use crate::usb::{PROD_ID1, PROD_ID1_STR, PROD_ID2, PROD_ID2_STR};
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub enum SlashType {
GA403,
GA605,
GU605,
GA403_2024,
GA403_2025,
GA605_2024,
GA605_2025,
GU605_2024,
GU605_2025,
G614_2025,
#[default]
Unsupported,
}
@@ -22,30 +26,51 @@ pub enum SlashType {
impl SlashType {
pub const fn prod_id(&self) -> u16 {
match self {
SlashType::GA403 => PROD_ID1,
SlashType::GA605 => PROD_ID2,
SlashType::GU605 => PROD_ID1,
SlashType::GA403_2025 => PROD_ID2,
SlashType::GA403_2024 => PROD_ID1,
SlashType::GA605_2025 => PROD_ID2,
SlashType::GA605_2024 => PROD_ID2,
SlashType::GU605_2025 => PROD_ID2,
SlashType::GU605_2024 => PROD_ID1,
SlashType::G614_2025 => PROD_ID2,
SlashType::Unsupported => 0,
}
}
pub const fn prod_id_str(&self) -> &str {
match self {
SlashType::GA403 => PROD_ID1_STR,
SlashType::GA605 => PROD_ID2_STR,
SlashType::GU605 => PROD_ID1_STR,
SlashType::GA403_2025 => PROD_ID2_STR,
SlashType::GA403_2024 => PROD_ID1_STR,
SlashType::GA605_2025 => PROD_ID2_STR,
SlashType::GA605_2024 => PROD_ID2_STR,
SlashType::GU605_2025 => PROD_ID2_STR,
SlashType::GU605_2024 => PROD_ID1_STR,
SlashType::G614_2025 => PROD_ID2_STR,
SlashType::Unsupported => "",
}
}
pub fn from_dmi() -> Self {
let board_name = DMIID::new().unwrap_or_default().board_name.to_uppercase();
if board_name.contains("GA403") {
SlashType::GA403
if board_name.contains("G614F") {
SlashType::G614_2025
} else if [
"GA403W", "GA403UH", "GA403UM", "GA403UP",
]
.iter()
.any(|s| board_name.contains(s))
{
SlashType::GA403_2025
} else if board_name.contains("GA403") {
SlashType::GA403_2024
} else if board_name.contains("GA605K") {
SlashType::GA605_2025
} else if board_name.contains("GA605") {
SlashType::GA605
SlashType::GA605_2024
} else if board_name.contains("GU605C") {
SlashType::GU605_2025
} else if board_name.contains("GU605") {
SlashType::GU605
SlashType::GU605_2024
} else {
SlashType::Unsupported
}
@@ -56,10 +81,14 @@ impl FromStr for SlashType {
type Err = SlashError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(match s {
"ga403" | "GA403" => Self::GA403,
"ga605" | "GA605" => Self::GA605,
"gu605" | "GU605" => Self::GU605,
Ok(match s.to_uppercase().as_str() {
"GA403_2025" => Self::GA403_2025,
"GA403_2024" => Self::GA403_2024,
"GA605_2025" => Self::GA605_2025,
"GA605_2024" => Self::GA605_2024,
"GU605_2025" => Self::GU605_2025,
"GU605_2024" => Self::GU605_2024,
"G614_2025" => Self::G614_2025,
_ => Self::Unsupported,
})
}
@@ -68,6 +97,7 @@ impl FromStr for SlashType {
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub enum SlashMode {
Static = 0x06,
Bounce = 0x10,
Slash = 0x12,
Loading = 0x13,
@@ -91,6 +121,7 @@ impl FromStr for SlashMode {
fn from_str(s: &str) -> Result<Self, SlashError> {
match s {
"Static" => Ok(SlashMode::Static),
"Bounce" => Ok(SlashMode::Bounce),
"Slash" => Ok(SlashMode::Slash),
"Loading" => Ok(SlashMode::Loading),
@@ -114,6 +145,7 @@ impl FromStr for SlashMode {
impl Display for SlashMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let str = match &self {
SlashMode::Static => String::from("Static"),
SlashMode::Bounce => String::from("Bounce"),
SlashMode::Slash => String::from("Slash"),
SlashMode::Loading => String::from("Loading"),
@@ -135,8 +167,9 @@ impl Display for SlashMode {
}
impl SlashMode {
pub fn list() -> [String; 15] {
pub fn list() -> [String; 16] {
[
SlashMode::Static.to_string(),
SlashMode::Bounce.to_string(),
SlashMode::Slash.to_string(),
SlashMode::Loading.to_string(),

View File

@@ -37,14 +37,26 @@ pub fn get_slash_type() -> SlashType {
let dmi = DMIID::new()
.map_err(|_| SlashError::NoDevice)
.unwrap_or_default();
let board_name = dmi.board_name;
if board_name.contains("GA403") {
SlashType::GA403
let board_name = dmi.board_name.to_uppercase();
if board_name.contains("G614F") {
SlashType::G614_2025
} else if [
"GA403W", "GA403UH", "GA403UM", "GA403UP",
]
.iter()
.any(|s| board_name.contains(s))
{
SlashType::GA403_2025
} else if board_name.contains("GA403") {
SlashType::GA403_2024
} else if board_name.contains("GA605K") {
SlashType::GA605_2025
} else if board_name.contains("GA605") {
SlashType::GA605
SlashType::GA605_2024
} else if board_name.contains("GU605C") {
SlashType::GU605_2025
} else if board_name.contains("GU605") {
SlashType::GU605
SlashType::GU605_2024
} else {
SlashType::Unsupported
}
@@ -52,9 +64,13 @@ pub fn get_slash_type() -> SlashType {
pub const fn report_id(slash_type: SlashType) -> u8 {
match slash_type {
SlashType::GA403 => REPORT_ID_193B,
SlashType::GA605 => REPORT_ID_19B6,
SlashType::GU605 => REPORT_ID_193B,
SlashType::GA403_2025 => REPORT_ID_19B6,
SlashType::GA403_2024 => REPORT_ID_193B,
SlashType::GA605_2025 => REPORT_ID_19B6,
SlashType::GA605_2024 => REPORT_ID_19B6,
SlashType::GU605_2025 => REPORT_ID_19B6,
SlashType::GU605_2024 => REPORT_ID_193B,
SlashType::G614_2025 => REPORT_ID_19B6,
SlashType::Unsupported => REPORT_ID_19B6,
}
}

View File

@@ -1,4 +0,0 @@
[toolchain]
channel = "stable"
components = ["rustfmt", "clippy"]
profile = "minimal"

View File

@@ -43,10 +43,12 @@ impl AniMatrix {
vertical: 2,
horizontal: 5,
},
AnimeType::GA402 | AnimeType::Unsupported => LedShape {
vertical: 2,
horizontal: 5,
},
AnimeType::GA402 | AnimeType::G635L | AnimeType::G835L | AnimeType::Unsupported => {
LedShape {
vertical: 2,
horizontal: 5,
}
}
AnimeType::GU604 => LedShape {
vertical: 2,
horizontal: 5,
@@ -56,7 +58,9 @@ impl AniMatrix {
// Do a hard mapping of each (derived from wireshardk captures)
let rows = match model {
AnimeType::GA401 => GA401.to_vec(),
AnimeType::GA402 | AnimeType::Unsupported => GA402.to_vec(),
AnimeType::GA402 | AnimeType::G635L | AnimeType::G835L | AnimeType::Unsupported => {
GA402.to_vec()
}
AnimeType::GU604 => GU604.to_vec(),
};

View File

@@ -157,14 +157,14 @@ fn main() -> Result<(), Box<dyn Error>> {
for (x_count, b) in dev.buffer[start..=end].iter().enumerate() {
canvas.set_draw_color(Color::RGB(*b, *b, *b));
let x: i32 = w + x_count as i32 * w - {
#[allow(clippy::manual_is_multiple_of)]
if (y_count + y_offset as usize) % 2 != 0 {
#[allow(clippy::manual_is_multiple_of)]
let x: i32 = w + x_count as i32 * w
- if (y_count + y_offset as usize) % 2 != 0 {
0
} else {
w / 2
}
} + row.3 * w;
+ row.3 * w;
let y = y_count as i32 * h - y_offset * h;
canvas
.fill_rect(Rect::new(x, y, w as u32, h as u32))