Compare commits

...

56 Commits

Author SHA1 Message Date
Denis Benato
5e5cebc781 Revert "Fix: continue -> return in set_config_or_default"
This reverts commit ad051bd7b8.
2025-11-06 22:53:21 +01:00
Denis Benato
def691f9d0 Fix: sliders not updating the value 2025-11-06 22:53:09 +01:00
Denis Benato
80eeafd9e1 Feat: config reload and do not apply properties if max==min 2025-11-06 21:06:44 +01:00
Denis Benato
bf173300e0 Feat: start in tray mode 2025-11-06 17:29:12 +01:00
Denis Benato
07874d7452 tests: load bundled aura_support.ron when system files missing to make unit tests reliable in CI 2025-11-06 17:24:01 +01:00
Denis Benato
894a0d2b11 Chore: update CHANGELOG.md 2025-11-06 16:12:44 +01:00
Denis Benato
11dc02612e Fix clippy warnings: alias complex types, allow some clippy lints, collapse else-if, avoid is_multiple_of usage 2025-11-06 16:05:38 +01:00
Denis Benato
fccb233e9e Fix: stop rust from silly complains about is_multiple_of 2025-11-06 15:58:30 +01:00
Denis Benato
c13612e02c Fix: simplify an if 2025-11-06 15:52:43 +01:00
Denis Benato
80147966a9 Chore: use stable 2025-11-06 15:49:05 +01:00
Denis Benato
a1815ac40c Chore: improve tests 2025-11-06 15:34:30 +01:00
Denis Benato
ad051bd7b8 Fix: continue -> return in set_config_or_default 2025-11-06 15:07:31 +01:00
Denis Benato
90676b390e Chore: update CHANGELOG.md 2025-11-06 15:04:59 +01:00
Denis Benato
974f2acafa Feat: better handling of nv_ properties 2025-11-06 14:53:57 +01:00
Denis Benato
6ae3ae5284 Feat: make nvidia dGPU tunables power-profile dependant 2025-11-06 14:26:03 +01:00
Denis Benato
34699a7021 Fix: notifications not respecting settings 2025-11-06 02:57:20 +01:00
Denis Benato
ef311689ec Fix: do not stall the boot process 2025-11-06 00:55:20 +01:00
Denis Benato
8ee0281b4f Fix: fedora build 2025-11-05 22:40:58 +01:00
Denis Benato
d8504b5430 Changelog for 6.1.17 2025-11-05 21:14:21 +01:00
Denis Benato
5c4d833fbd Prepare for 6.1.17 2025-11-05 21:09:18 +01:00
Denis Benato
698999e828 cargo fmt and README.md restyle 2025-11-05 20:02:43 +01:00
Denis Benato
0eae9e55c6 Merge branch 'devel' 2025-11-05 19:54:21 +01:00
Denis Benato
07171888a1 Merge branch 'main' into 'main'
feat: Allow setting default profile for ac and battery

See merge request asus-linux/asusctl!229
2025-11-05 18:53:53 +00:00
matszwe02
9321fde6af feat: Allow setting default profile for ac and battery 2025-11-05 01:44:31 +01:00
Denis Benato
f90d0a6673 Merge branch 'devel' into 'devel'
Add install-data-asusd_user to install-data in Makefile

See merge request asus-linux/asusctl!228
2025-10-29 18:49:50 +00:00
Synby
bbd03c128d Edit Makefile
add install-data-asusd_user to install-data
2025-10-29 17:23:29 +00:00
Denis Benato
132a2f3665 Chore: complete the switch back to stable 2025-10-23 14:50:40 +02:00
Denis Benato
180566e5f1 Fix: share a single HID device
Avoid opening multiple handles to the same device whenever possible.
2025-10-22 22:05:17 +02:00
Denis Benato
c9e76f3273 Chore: prepare for 6.1.16 release 2025-10-20 19:15:36 +02:00
Denis Benato
2997ae83ff Merge branch 'devel' 2025-10-20 04:08:20 +02:00
Denis Benato
151d681e16 feat: expose MCU Powersave toggle on rogcc 2025-10-20 03:47:09 +02:00
Denis Benato
90b3f43a36 Chore: add more debugging 2025-10-20 02:49:47 +02:00
Denis Benato
a345b09ce1 Chore: type abttery -> battery 2025-10-20 02:46:07 +02:00
Denis Benato
f26b0d8de5 Merge branch 'profile-match' into 'main'
Allow flexible profile cli input for LowPower

See merge request asus-linux/asusctl!226
2025-10-20 00:40:54 +00:00
yanganto
9366b0ec04 Allow flexible profile cli input for LowPower 2025-10-20 00:40:54 +00:00
Denis Benato
415712143b Feat: add screen_auto_brightness 2025-10-20 02:08:59 +02:00
Denis Benato
60fce30a06 Chore: flush out requirement for nightly 2025-10-20 00:39:17 +02:00
Denis Benato
d8f06230fa Chore: Added support for FX607J 2025-10-19 23:02:45 +02:00
Denis Benato
834464a527 Fix: fixed ac_command/bat_command 2025-10-19 23:00:23 +02:00
Denis Benato
4faa96298a Chore: Added supprot for G614JIR 2025-10-19 22:32:05 +02:00
Denis Benato
78c574b761 chore_ add supprot for GU605CR 2025-10-19 21:27:33 +02:00
Denis Benato
9785eafd53 Modified to README.md to not mislead users attempting to compile it 2025-10-19 20:45:49 +02:00
Denis Benato
51cad9ea7e chore: make the startup more verbose 2025-10-16 15:17:27 +02:00
Denis Benato
319373faea chore: Prepare for 6.1.15 release 2025-10-14 01:57:03 +02:00
Denis Benato
f6aa3e3d01 Merge branch 'devel' 2025-10-14 01:50:20 +02:00
Denis Benato
d11fc20bab feat: apply the proper configuration depending on the plug status 2025-10-13 21:36:01 +02:00
Denis Benato
b0e1b21e4b chore: cargo clippy fix 2025-10-12 22:34:20 +02:00
Denis Benato
1c1daaa6d2 chore: reimplement default for FanCurveCPU 2025-10-12 19:47:26 +02:00
HiFiPhile
c3b5de843f Add TX Air rule 2025-10-12 19:44:30 +02:00
Denis Benato
09dcfb4065 Fix multiple warnings 2025-10-12 19:43:07 +02:00
Denis Benato
b2e7211bbe chore: cargo fmt 2025-10-12 19:43:05 +02:00
Denis Benato
1ab1adf937 Merge branch 'tx_air' into 'main'
Add TX Air (TUF Chinese version) udev rule

See merge request asus-linux/asusctl!227
2025-10-12 17:42:09 +00:00
HiFiPhile
a21bf779b0 Add TX Air rule 2025-10-12 13:20:07 +02:00
Denis Benato
0dba22529c feat: change limits dynamically 2025-10-11 20:51:57 +02:00
Denis Benato
180d63620b Fix multiple warnings 2025-10-08 01:19:38 +02:00
Denis Benato
daea1f538c chore: cargo fmt 2025-10-08 01:01:43 +02:00
56 changed files with 2570 additions and 1047 deletions

View File

@@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
set -e set -e
echo '+cargo +nightly fmt --all -- --check' echo '+cargo fmt --all -- --check'
cargo +nightly fmt --all -- --check cargo fmt --all -- --check
git add -u git add -u

View File

@@ -2,8 +2,8 @@
set -e set -e
echo '+cargo +nightly fmt --all -- --check' echo '+cargo fmt --all -- --check'
cargo +nightly fmt --all -- --check cargo fmt --all -- --check
echo '+cargo clippy --all -- -D warnings' echo '+cargo clippy --all -- -D warnings'
cargo clippy --all -- -D warnings cargo clippy --all -- -D warnings
echo '+cargo cranky' echo '+cargo cranky'

View File

@@ -31,8 +31,7 @@ format:
- tags - tags
<<: *rust_cache <<: *rust_cache
script: script:
- echo "nightly" > rust-toolchain - rustup component add rustfmt || true
- rustup component add rustfmt
- cargo fmt --check - cargo fmt --check
check: check:
@@ -40,8 +39,8 @@ check:
- tags - tags
<<: *rust_cache <<: *rust_cache
script: script:
- rustup component add clippy - rustup component add clippy || true
- cargo check - cargo check --locked --workspace
# deny currently catches too much # deny currently catches too much
#- cargo install cargo-deny && cargo deny #- cargo install cargo-deny && cargo deny
- cargo install cargo-cranky && cargo cranky - cargo install cargo-cranky && cargo cranky
@@ -52,7 +51,7 @@ test:
<<: *rust_cache <<: *rust_cache
script: script:
- mkdir -p .git/hooks > /dev/null - mkdir -p .git/hooks > /dev/null
- cargo test --all -- --test-threads=1 - cargo test --locked --all
release: release:
only: only:
@@ -60,7 +59,7 @@ release:
<<: *rust_cache <<: *rust_cache
script: script:
- cargo install cargo-vendor-filterer - cargo install cargo-vendor-filterer
- make && make vendor - make FROZEN=1 && make vendor
artifacts: artifacts:
paths: paths:
- vendor_asusctl*.tar.xz - vendor_asusctl*.tar.xz
@@ -72,7 +71,7 @@ pages:
- tags - tags
<<: *rust_cache <<: *rust_cache
script: script:
- cargo doc --document-private-items --no-deps --workspace - cargo doc --locked --document-private-items --no-deps --workspace
- rm -rf public - rm -rf public
- mkdir public - mkdir public
- cp -R target/doc/* public - cp -R target/doc/* public

View File

@@ -2,6 +2,28 @@
## [Unreleased] ## [Unreleased]
### Changed
- Make the boot process more reliable
- tie nv_ properties to power profiles
- Better support nv_tgp
## [v6.1.17]
### Changed
- Fix Makefile
- Share a single HID device
## [v6.1.16]
### Changed
- Expose more properties via rog-control-center
- Add support for a few more models
## [v6.1.15]
### Changed
- Reflect the current asus-armoury status on AC plug connection status change
## [v6.1.14] ## [v6.1.14]
### Changed ### Changed

1241
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
[workspace.package] [workspace.package]
version = "6.1.14" version = "6.1.17"
rust-version = "1.82" rust-version = "1.82"
license = "MPL-2.0" license = "MPL-2.0"
readme = "README.md" readme = "README.md"

View File

@@ -37,6 +37,15 @@ ifeq ($(X11),1)
ARGS += --features "rog-control-center/x11" ARGS += --features "rog-control-center/x11"
endif endif
# Always use the versions in Cargo.lock by default
ARGS += --locked
# Allow optionally freezing the build to avoid any network access and enforce Cargo.lock strictly
FROZEN ?= 0
ifeq ($(FROZEN),1)
ARGS += --frozen
endif
VENDORED ?= 0 VENDORED ?= 0
ifeq ($(VENDORED),1) ifeq ($(VENDORED),1)
ARGS += --frozen ARGS += --frozen
@@ -104,7 +113,7 @@ install-data-asusd_user:
.PHONY: install-data-asusd install-data-asusd_user .PHONY: install-data-asusd install-data-asusd_user
install-data: install-data-asusd install-data-rog_gui install-data: install-data-asusd install-data-asusd_user install-data-rog_gui
install: install-program install-data install: install-program install-data
$(INSTALL_DATA) "./LICENSE" "$(DESTDIR)$(datarootdir)/asusctl/LICENSE" $(INSTALL_DATA) "./LICENSE" "$(DESTDIR)$(datarootdir)/asusctl/LICENSE"

View File

@@ -46,13 +46,13 @@ See the [rog-aura readme](./rog-aura/README.md) for more details.
Most ASUS gaming laptops that have a USB keyboard. If `lsusb` shows something similar Most ASUS gaming laptops that have a USB keyboard. If `lsusb` shows something similar
to this: to this:
``` ```plain
Bus 001 Device 002: ID 0b05:1866 ASUSTek Computer, Inc. N-KEY Device Bus 001 Device 002: ID 0b05:1866 ASUSTek Computer, Inc. N-KEY Device
``` ```
or or
``` ```plain
Bus 003 Device 002: ID 0b05:19b6 ASUSTek Computer, Inc. [unknown] Bus 003 Device 002: ID 0b05:19b6 ASUSTek Computer, Inc. [unknown]
``` ```
@@ -74,39 +74,47 @@ The list is a bit outdated as many features have been enabled in the Linux kerne
- [x] Toggle bios setting for boot/POST sound - [x] Toggle bios setting for boot/POST sound
- [x] Toggle GPU MUX (g-sync, or called MUX on 2022+ laptops) - [x] Toggle GPU MUX (g-sync, or called MUX on 2022+ laptops)
# GUI ## GUI
A gui is now in the repo - ROG Control Center. At this time it is still a WIP, but it has almost all features in place already. A gui is now in the repo - ROG Control Center. At this time it is still a WIP, but it has almost all features in place already.
**NOTE**: Xorg is not supported. **NOTE**: Xorg is not supported.
# BUILDING ## BUILDING
Rust and cargo are required, they can be installed from [rustup.rs](https://rustup.rs/) or from the distro repos if newer than 1.75. Rust and cargo are required, they can be installed from [rustup.rs](https://rustup.rs/).
Distro packaging should work with the stable toolchain. If your distro does not provide a recent Rust toolchain, install rustup and use the stable toolchain.
**fedora:** **fedora:**
dnf install cmake clang-devel libxkbcommon-devel systemd-devel expat-devel pcre2-devel libzstd-devel gtk3-devel ```sh
make dnf install cmake clang-devel libxkbcommon-devel systemd-devel expat-devel pcre2-devel libzstd-devel gtk3-devel
sudo make install make
sudo make install
```
**openSUSE:** **openSUSE:**
Works with KDE Plasma (without GTK packages) Works with KDE Plasma (without GTK packages)
zypper in -t pattern devel_basis ```sh
zypper in rustup make cmake clang-devel libxkbcommon-devel systemd-devel expat-devel pcre2-devel libzstd-devel gtk3-devel zypper in -t pattern devel_basis
make zypper in rustup make cmake clang-devel libxkbcommon-devel systemd-devel expat-devel pcre2-devel libzstd-devel gtk3-devel
sudo make install make
sudo make install
```
**Debian(unsuported):** **Debian(unsuported):**
officially unsuported,but you can still try and test it by yourself(some features may not be available). officially unsuported,but you can still try and test it by yourself(some features may not be available).
sudo apt install libclang-dev libudev-dev libfontconfig-dev build-essential cmake libxkbcommon-dev ```sh
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh sudo apt install libclang-dev libudev-dev libfontconfig-dev build-essential cmake libxkbcommon-dev
make curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
sudo make install make
sudo make install
```
**Ubuntu, Popos (unsuported):** **Ubuntu, Popos (unsuported):**
@@ -133,15 +141,15 @@ You may also need to activate the service for debian install. If running Pop!\_O
If you are upgrading from a previous installed version, you will need to restart the service or reboot. If you are upgrading from a previous installed version, you will need to restart the service or reboot.
``` ```sh
$ systemctl daemon-reload && systemctl restart asusd systemctl daemon-reload && systemctl restart asusd
``` ```
## Uninstalling ## Uninstalling
Run `sudo make uninstall` in the source repo, and remove `/etc/asusd/`. Run `sudo make uninstall` in the source repo, and remove `/etc/asusd/`.
# Contributing ## Contributing
See `CONTRIBUTING.md`. Additionally, also do `cargo clean` and `cargo test` on first checkout to ensure the commit hooks are used (via `cargo-husky`). See `CONTRIBUTING.md`. Additionally, also do `cargo clean` and `cargo test` on first checkout to ensure the commit hooks are used (via `cargo-husky`).
@@ -149,17 +157,17 @@ Generation of the bindings with `make bindings` requires `typeshare` to be insta
Dbus introsepction XML requires with `make introspection` requires `anime_sim` to be running before starting `asusd`. Dbus introsepction XML requires with `make introspection` requires `anime_sim` to be running before starting `asusd`.
# OTHER ## OTHER
## AniMe Matrix simulator ### AniMe Matrix simulator
A simulator using SDL2 can be built using `cargo build --package rog_simulators` and run with `./target/debug/anime_sim`. Once started `asusd` will need restarting to pick it up. If running this sim on a laptop _with_ the display, the simulated display will be used instead of the physical display. A simulator using SDL2 can be built using `cargo build --package rog_simulators` and run with `./target/debug/anime_sim`. Once started `asusd` will need restarting to pick it up. If running this sim on a laptop _with_ the display, the simulated display will be used instead of the physical display.
## Supporting more laptops ### Supporting more laptops
Please file a support request. Please file a support request.
# License & Trademarks ## License & Trademarks
Mozilla Public License 2 (MPL-2.0) Mozilla Public License 2 (MPL-2.0)

View File

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

View File

@@ -74,6 +74,16 @@ pub struct ProfileCommand {
#[options(meta = "", help = "set the active profile")] #[options(meta = "", help = "set the active profile")]
pub profile_set: Option<PlatformProfile>, pub profile_set: Option<PlatformProfile>,
#[options(short = "a", meta = "", help = "set the profile to use on AC power")]
pub profile_set_ac: Option<PlatformProfile>,
#[options(
short = "b",
meta = "",
help = "set the profile to use on battery power"
)]
pub profile_set_bat: Option<PlatformProfile>,
} }
#[derive(Options)] #[derive(Options)]

View File

@@ -979,7 +979,13 @@ fn handle_throttle_profile(
return Err(ProfileError::NotSupported.into()); return Err(ProfileError::NotSupported.into());
} }
if !cmd.next && !cmd.list && cmd.profile_set.is_none() && !cmd.profile_get { if !cmd.next
&& !cmd.list
&& cmd.profile_set.is_none()
&& !cmd.profile_get
&& cmd.profile_set_ac.is_none()
&& cmd.profile_set_bat.is_none()
{
if !cmd.help { if !cmd.help {
println!("Missing arg or command\n"); println!("Missing arg or command\n");
} }
@@ -999,6 +1005,10 @@ fn handle_throttle_profile(
proxy.set_platform_profile(PlatformProfile::next(current, &choices))?; proxy.set_platform_profile(PlatformProfile::next(current, &choices))?;
} else if let Some(profile) = cmd.profile_set { } else if let Some(profile) = cmd.profile_set {
proxy.set_platform_profile(profile)?; proxy.set_platform_profile(profile)?;
} else if let Some(profile) = cmd.profile_set_ac {
proxy.set_platform_profile_on_ac(profile)?;
} else if let Some(profile) = cmd.profile_set_bat {
proxy.set_platform_profile_on_battery(profile)?;
} }
if cmd.list { if cmd.list {
@@ -1009,6 +1019,11 @@ fn handle_throttle_profile(
if cmd.profile_get { if cmd.profile_get {
println!("Active profile is {current:?}"); println!("Active profile is {current:?}");
println!("Profile on AC is {:?}", proxy.platform_profile_on_ac()?);
println!(
"Profile on Battery is {:?}",
proxy.platform_profile_on_battery()?
);
} }
Ok(()) Ok(())
@@ -1169,11 +1184,15 @@ fn print_firmware_attr(attr: &AsusArmouryProxyBlocking) -> Result<(), Box<dyn st
Ok(()) Ok(())
} }
#[allow(clippy::manual_is_multiple_of)]
fn handle_armoury_command(cmd: &ArmouryCommand) -> Result<(), Box<dyn std::error::Error>> { fn handle_armoury_command(cmd: &ArmouryCommand) -> Result<(), Box<dyn std::error::Error>> {
{ {
if cmd.free.is_empty() || cmd.free.len() % 2 != 0 || cmd.help { // 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"; const USAGE: &str = "Usage: asusctl platform panel_overdrive 1 nv_dynamic_boost 5";
if cmd.free.len() % 2 != 0 { if odd_len {
println!( println!(
"Incorrect number of args, each attribute label must be paired with a setting:" "Incorrect number of args, each attribute label must be paired with a setting:"
); );

View File

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

View File

@@ -1,12 +1,12 @@
use std::sync::Arc; use std::sync::Arc;
use config_traits::StdConfig; use config_traits::StdConfig;
use futures_util::lock::Mutex;
use log::{debug, error, info}; use log::{debug, error, info};
use rog_platform::asus_armoury::{AttrValue, Attribute, FirmwareAttribute, FirmwareAttributes}; use rog_platform::asus_armoury::{AttrValue, Attribute, FirmwareAttribute, FirmwareAttributes};
use rog_platform::platform::{PlatformProfile, RogPlatform}; use rog_platform::platform::{PlatformProfile, RogPlatform};
use rog_platform::power::AsusPower; use rog_platform::power::AsusPower;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::sync::Mutex;
use zbus::object_server::SignalEmitter; use zbus::object_server::SignalEmitter;
use zbus::zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Type, Value}; use zbus::zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Type, Value};
use zbus::{fdo, interface, Connection}; use zbus::{fdo, interface, Connection};
@@ -27,6 +27,28 @@ fn dbus_path_for_attr(attr_name: &str) -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{MOD_NAME}/{attr_name}")).into() 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)] #[derive(Clone)]
pub struct AsusArmouryAttribute { pub struct AsusArmouryAttribute {
attr: Attribute, attr: Attribute,
@@ -55,6 +77,25 @@ impl AsusArmouryAttribute {
String::from(self.attr.name()) String::from(self.attr.name())
} }
fn resolve_i32_value(refreshed: Option<i32>, cached: &AttrValue) -> i32 {
refreshed
.or(match cached {
AttrValue::Integer(i) => Some(*i),
_ => None,
})
.unwrap_or(-1)
}
pub async fn emit_limits(&self, connection: &Connection) -> Result<(), RogError> {
let path = dbus_path_for_attr(self.attr.name());
let signal = SignalEmitter::new(connection, path)?;
self.min_value_changed(&signal).await?;
self.max_value_changed(&signal).await?;
self.scalar_increment_changed(&signal).await?;
self.current_value_changed(&signal).await?;
Ok(())
}
pub async fn move_to_zbus(self, connection: &Connection) -> Result<(), RogError> { pub async fn move_to_zbus(self, connection: &Connection) -> Result<(), RogError> {
let path = dbus_path_for_attr(self.attr.name()); let path = dbus_path_for_attr(self.attr.name());
connection connection
@@ -91,7 +132,8 @@ impl AsusArmouryAttribute {
.await; .await;
} else { } else {
info!( info!(
"inotify event stream failed for {} ({}). You can ignore this if unsupported", "inotify event stream failed for {} ({}). You can ignore this \
if unsupported",
name, $attr_str name, $attr_str
); );
} }
@@ -116,12 +158,49 @@ impl AsusArmouryAttribute {
} }
} }
#[derive(Clone, Default)]
pub struct ArmouryAttributeRegistry {
attrs: Vec<AsusArmouryAttribute>,
}
impl ArmouryAttributeRegistry {
pub fn push(&mut self, attr: AsusArmouryAttribute) {
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 {
if let Err(e) = attr.emit_limits(connection).await {
error!(
"Failed to emit updated limits for attribute '{}': {e:?}",
attr.attribute_name()
);
last_err = Some(e);
}
}
if let Some(err) = last_err {
Err(err)
} else {
Ok(())
}
}
}
impl crate::Reloadable for AsusArmouryAttribute { impl crate::Reloadable for AsusArmouryAttribute {
async fn reload(&mut self) -> Result<(), RogError> { async fn reload(&mut self) -> Result<(), RogError> {
info!("Reloading {}", self.attr.name()); info!("Reloading {}", self.attr.name());
let name: FirmwareAttribute = self.attr.name().into(); let name: FirmwareAttribute = self.attr.name().into();
if name.is_ppt() { if name.is_ppt() || name.is_dgpu() {
let profile: PlatformProfile = self.platform.get_platform_profile()?.into(); let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self let power_plugged = self
.power .power
@@ -130,24 +209,40 @@ impl crate::Reloadable for AsusArmouryAttribute {
error!("Could not get power status: {e:?}"); error!("Could not get power status: {e:?}");
e e
}) })
.unwrap_or_default(); .unwrap_or_default()
let config = if power_plugged == 1 { == 1;
&self.config.lock().await.ac_profile_tunings
} else { let apply_value = {
&self.config.lock().await.dc_profile_tunings let config = self.config.lock().await;
config
.select_tunings_ref(power_plugged, profile)
.and_then(|tuning| {
if tuning.enabled {
tuning.group.get(&self.name()).copied()
} else {
None
}
})
}; };
if let Some(tuning) = config.get(&profile) {
if tuning.enabled { if let Some(tune) = apply_value {
if let Some(tune) = tuning.group.get(&self.name()) { // Don't attempt writes for attributes that report min==0 and max==0
self.attr // (commonly means not supported in this power mode/device); skip
.set_current_value(&AttrValue::Integer(*tune)) // applying stored tune in that case.
.map_err(|e| { if self.attr.base_path_exists() && !attr_unsupported(&self.attr) {
error!("Could not set {} value: {e:?}", self.attr.name()); self.attr
self.attr.base_path_exists(); .set_current_value(&AttrValue::Integer(tune))
e .map_err(|e| {
})?; error!("Could not set {} value: {e:?}", self.attr.name());
info!("Set {} to {:?}", self.attr.name(), tune); 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()
);
} }
} }
} else { } else {
@@ -222,7 +317,7 @@ impl AsusArmouryAttribute {
async fn restore_default(&self) -> fdo::Result<()> { async fn restore_default(&self) -> fdo::Result<()> {
self.attr.restore_default()?; self.attr.restore_default()?;
if self.name().is_ppt() { if self.name().is_ppt() || self.name().is_dgpu() {
let profile: PlatformProfile = self.platform.get_platform_profile()?.into(); let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self let power_plugged = self
.power .power
@@ -255,26 +350,20 @@ impl AsusArmouryAttribute {
#[zbus(property)] #[zbus(property)]
async fn min_value(&self) -> i32 { async fn min_value(&self) -> i32 {
match self.attr.min_value() { Self::resolve_i32_value(self.attr.refresh_min_value(), self.attr.min_value())
AttrValue::Integer(i) => *i,
_ => -1,
}
} }
#[zbus(property)] #[zbus(property)]
async fn max_value(&self) -> i32 { async fn max_value(&self) -> i32 {
match self.attr.max_value() { Self::resolve_i32_value(self.attr.refresh_max_value(), self.attr.max_value())
AttrValue::Integer(i) => *i,
_ => -1,
}
} }
#[zbus(property)] #[zbus(property)]
async fn scalar_increment(&self) -> i32 { async fn scalar_increment(&self) -> i32 {
match self.attr.scalar_increment() { Self::resolve_i32_value(
AttrValue::Integer(i) => *i, self.attr.refresh_scalar_increment(),
_ => -1, self.attr.scalar_increment(),
} )
} }
#[zbus(property)] #[zbus(property)]
@@ -287,7 +376,7 @@ impl AsusArmouryAttribute {
#[zbus(property)] #[zbus(property)]
async fn current_value(&self) -> fdo::Result<i32> { async fn current_value(&self) -> fdo::Result<i32> {
if self.name().is_ppt() { if self.name().is_ppt() || self.name().is_dgpu() {
let profile: PlatformProfile = self.platform.get_platform_profile()?.into(); let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self let power_plugged = self
.power .power
@@ -296,12 +385,15 @@ impl AsusArmouryAttribute {
error!("Could not get power status: {e:?}"); error!("Could not get power status: {e:?}");
e e
}) })
.unwrap_or_default(); .unwrap_or_default()
let mut config = self.config.lock().await; == 1;
let tuning = config.select_tunings(power_plugged == 1, profile); let config = self.config.lock().await;
if let Some(tune) = tuning.group.get(&self.name()) { if let Some(tuning) = config.select_tunings_ref(power_plugged, profile) {
return Ok(*tune); if let Some(tune) = tuning.group.get(&self.name()) {
} else if let AttrValue::Integer(i) = self.attr.default_value() { return Ok(*tune);
}
}
if let AttrValue::Integer(i) = self.attr.default_value() {
return Ok(*i); return Ok(*i);
} }
return Err(fdo::Error::Failed( return Err(fdo::Error::Failed(
@@ -317,9 +409,97 @@ 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)] #[zbus(property)]
async fn set_current_value(&mut self, value: i32) -> fdo::Result<()> { async fn set_current_value(&mut self, value: i32) -> fdo::Result<()> {
if self.name().is_ppt() { if self.name().is_ppt() || self.name().is_dgpu() {
let profile: PlatformProfile = self.platform.get_platform_profile()?.into(); let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
let power_plugged = self let power_plugged = self
.power .power
@@ -340,12 +520,22 @@ impl AsusArmouryAttribute {
debug!("Store tuning config for {} = {:?}", self.attr.name(), value); debug!("Store tuning config for {} = {:?}", self.attr.name(), value);
} }
if tuning.enabled { if tuning.enabled {
self.attr // Don't attempt writes for attributes that report min==0 and max==0
.set_current_value(&AttrValue::Integer(value)) // (commonly means not supported in this power mode/device); skip
.map_err(|e| { // applying stored value in that case.
error!("Could not set value: {e:?}"); if self.attr.base_path_exists() && !attr_unsupported(&self.attr) {
e 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()
);
}
} }
} else { } else {
self.attr self.attr
@@ -386,13 +576,18 @@ impl AsusArmouryAttribute {
} }
} }
#[allow(clippy::too_many_arguments)]
pub async fn start_attributes_zbus( pub async fn start_attributes_zbus(
conn: &Connection, conn: Option<&Connection>,
platform: RogPlatform, platform: RogPlatform,
power: AsusPower, power: AsusPower,
attributes: FirmwareAttributes, attributes: FirmwareAttributes,
config: Arc<Mutex<Config>>, config: Arc<Mutex<Config>>,
) -> Result<(), RogError> { 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() { for attr in attributes.attributes() {
let mut attr = AsusArmouryAttribute::new( let mut attr = AsusArmouryAttribute::new(
attr.clone(), attr.clone(),
@@ -401,37 +596,122 @@ pub async fn start_attributes_zbus(
config.clone(), config.clone(),
); );
if let Err(e) = attr.reload().await { let registry_attr = attr.clone();
error!(
"Skipping attribute '{}' due to reload error: {e:?}", // Only perform the full reload (which may query the platform/power sysfs)
attr.attr.name() // 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.
// continue with others if enable_zbus {
continue; if let Err(e) = attr.reload().await {
error!(
"Skipping attribute '{}' due to reload error: {e:?}",
attr.attr.name()
);
// continue with others
continue;
}
} }
let attr_name = attr.attribute_name(); let attr_name = attr.attribute_name();
let path = dbus_path_for_attr(attr_name.as_str()); // If zbus is enabled and a connection is provided, create the SignalEmitter,
match zbus::object_server::SignalEmitter::new(conn, path) { // start watchers and register the object on zbus. Tests can call this function
Ok(sig) => { // with enable_zbus=false and conn=None to skip DBus registration and watchers.
if let Err(e) = attr.watch_and_notify(sig).await { if !enable_zbus {
error!("Failed to start watcher for '{}': {e:?}", attr.attr.name()); // 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
})?;
} }
} } else if let Some(saved_value) = attr.config.lock().await.armoury_settings.get(&name) {
Err(e) => { attr.attr
error!( .set_current_value(&AttrValue::Integer(*saved_value))
"Failed to create SignalEmitter for '{}': {e:?}", .map_err(|e| {
attr.attr.name() error!("Could not set {} value: {e:?}", attr.attr.name());
e
})?;
info!(
"Restored armoury setting {} to {:?}",
attr.attr.name(),
saved_value
); );
} }
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 { registry.push(registry_attr);
error!("Failed to register attribute '{attr_name}' on zbus: {e:?}");
}
} }
Ok(()) Ok(registry)
} }
pub async fn set_config_or_default( pub async fn set_config_or_default(
@@ -442,34 +722,50 @@ pub async fn set_config_or_default(
) { ) {
for attr in attrs.attributes().iter() { for attr in attrs.attributes().iter() {
let name: FirmwareAttribute = attr.name().into(); let name: FirmwareAttribute = attr.name().into();
if name.is_ppt() { if name.is_ppt() || name.is_dgpu() {
let tuning = config.select_tunings(power_plugged, profile); let tuning = config.select_tunings(power_plugged, profile);
if !tuning.enabled { if !tuning.enabled {
debug!("Tuning group is not enabled, skipping"); debug!("Tuning group is not enabled, skipping");
return; continue;
} }
// 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 let Some(tune) = tuning.group.get(&name) {
attr.set_current_value(&AttrValue::Integer(*tune)) if supported {
.map_err(|e| { attr.set_current_value(&AttrValue::Integer(*tune))
error!("Failed to set {}: {e}", <&str>::from(name)); .map_err(|e| {
}) error!("Failed to set {}: {e}", <&str>::from(name));
.ok(); })
} else { .ok();
let default = attr.default_value(); } else {
attr.set_current_value(default) debug!(
.map_err(|e| { "Skipping apply for {} in set_config_or_default because attribute missing or unsupported",
error!("Failed to set {}: {e}", <&str>::from(name)); <&str>::from(name)
}) );
.ok(); }
if let AttrValue::Integer(i) = default { } else {
tuning.group.insert(name, *i); // Only attempt to apply defaults when the attribute supports a range
info!( if supported {
"Set default tuning config for {} = {:?}", let default = attr.default_value();
<&str>::from(name), attr.set_current_value(default)
i .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)
); );
// config.write();
} }
} }
} else { } else {
@@ -489,3 +785,69 @@ 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

@@ -8,7 +8,6 @@ use std::sync::Arc;
use std::thread::sleep; use std::thread::sleep;
use config_traits::StdConfig; use config_traits::StdConfig;
use futures_util::lock::Mutex;
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use rog_anime::usb::{ use rog_anime::usb::{
pkt_flush, pkt_set_brightness, pkt_set_enable_display, pkt_set_enable_powersave_anim, pkt_flush, pkt_set_brightness, pkt_set_enable_display, pkt_set_enable_powersave_anim,
@@ -17,6 +16,7 @@ use rog_anime::usb::{
use rog_anime::{ActionData, AnimeDataBuffer, AnimePacketType}; use rog_anime::{ActionData, AnimeDataBuffer, AnimePacketType};
use rog_platform::hid_raw::HidRaw; use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw; use rog_platform::usb_raw::USBRaw;
use tokio::sync::Mutex;
use self::config::{AniMeConfig, AniMeConfigCached}; use self::config::{AniMeConfig, AniMeConfigCached};
use crate::error::RogError; use crate::error::RogError;
@@ -51,7 +51,7 @@ impl AniMe {
/// Will fail if something is already holding the config lock /// Will fail if something is already holding the config lock
async fn do_init_cache(&mut self) { async fn do_init_cache(&mut self) {
if let Some(mut config) = self.config.try_lock() { if let Ok(mut config) = self.config.try_lock() {
if let Err(e) = self.cache.init_from_config(&config, config.anime_type) { if let Err(e) = self.cache.init_from_config(&config, config.anime_type) {
error!( error!(
"Trying to cache the Anime Config failed, will reset to default config: {e:?}" "Trying to cache the Anime Config failed, will reset to default config: {e:?}"

View File

@@ -85,7 +85,7 @@ impl AniMeZbus {
/// Set base brightness level /// Set base brightness level
#[zbus(property)] #[zbus(property)]
async fn brightness(&self) -> Brightness { async fn brightness(&self) -> Brightness {
if let Some(config) = self.0.config.try_lock() { if let Ok(config) = self.0.config.try_lock() {
return config.display_brightness; return config.display_brightness;
} }
Brightness::Off Brightness::Off
@@ -117,7 +117,7 @@ impl AniMeZbus {
#[zbus(property)] #[zbus(property)]
async fn builtins_enabled(&self) -> bool { async fn builtins_enabled(&self) -> bool {
if let Some(config) = self.0.config.try_lock() { if let Ok(config) = self.0.config.try_lock() {
return config.builtin_anims_enabled; return config.builtin_anims_enabled;
} }
false false
@@ -162,7 +162,7 @@ impl AniMeZbus {
#[zbus(property)] #[zbus(property)]
async fn builtin_animations(&self) -> Animations { async fn builtin_animations(&self) -> Animations {
if let Some(config) = self.0.config.try_lock() { if let Ok(config) = self.0.config.try_lock() {
return config.builtin_anims; return config.builtin_anims;
} }
Animations::default() Animations::default()
@@ -195,7 +195,7 @@ impl AniMeZbus {
#[zbus(property)] #[zbus(property)]
async fn enable_display(&self) -> bool { async fn enable_display(&self) -> bool {
if let Some(config) = self.0.config.try_lock() { if let Ok(config) = self.0.config.try_lock() {
return config.display_enabled; return config.display_enabled;
} }
false false
@@ -218,7 +218,7 @@ impl AniMeZbus {
#[zbus(property)] #[zbus(property)]
async fn off_when_unplugged(&self) -> bool { async fn off_when_unplugged(&self) -> bool {
if let Some(config) = self.0.config.try_lock() { if let Ok(config) = self.0.config.try_lock() {
return config.off_when_unplugged; return config.off_when_unplugged;
} }
false false
@@ -245,7 +245,7 @@ impl AniMeZbus {
#[zbus(property)] #[zbus(property)]
async fn off_when_suspended(&self) -> bool { async fn off_when_suspended(&self) -> bool {
if let Some(config) = self.0.config.try_lock() { if let Ok(config) = self.0.config.try_lock() {
return config.off_when_suspended; return config.off_when_suspended;
} }
false false
@@ -261,7 +261,7 @@ impl AniMeZbus {
#[zbus(property)] #[zbus(property)]
async fn off_when_lid_closed(&self) -> bool { async fn off_when_lid_closed(&self) -> bool {
if let Some(config) = self.0.config.try_lock() { if let Ok(config) = self.0.config.try_lock() {
return config.off_when_lid_closed; return config.off_when_lid_closed;
} }
false false

View File

@@ -231,6 +231,8 @@ impl AuraConfig {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::sync::{Mutex, MutexGuard, OnceLock};
use rog_aura::keyboard::AuraPowerState; use rog_aura::keyboard::AuraPowerState;
use rog_aura::{ use rog_aura::{
AuraEffect, AuraModeNum, AuraZone, Colour, Direction, LedBrightness, PowerZones, Speed, AuraEffect, AuraModeNum, AuraZone, Colour, Direction, LedBrightness, PowerZones, Speed,
@@ -238,8 +240,20 @@ mod tests {
use super::AuraConfig; use super::AuraConfig;
// Global mutex to serialize tests that rely on process-wide environment
// variables
static TEST_MUTEX: OnceLock<Mutex<()>> = OnceLock::new();
fn test_lock() -> MutexGuard<'static, ()> {
TEST_MUTEX
.get_or_init(|| Mutex::new(()))
.lock()
.expect("TEST_MUTEX poisoned")
}
#[test] #[test]
fn set_multizone_4key_config() { fn set_multizone_4key_config() {
let _guard = test_lock();
std::env::set_var("BOARD_NAME", ""); std::env::set_var("BOARD_NAME", "");
let mut config = AuraConfig::new("19b6"); let mut config = AuraConfig::new("19b6");
@@ -331,6 +345,7 @@ mod tests {
#[test] #[test]
fn set_multizone_multimode_config() { fn set_multizone_multimode_config() {
let _guard = test_lock();
std::env::set_var("BOARD_NAME", ""); std::env::set_var("BOARD_NAME", "");
let mut config = AuraConfig::new("19b6"); let mut config = AuraConfig::new("19b6");
@@ -379,6 +394,7 @@ mod tests {
#[test] #[test]
fn verify_0x1866_g531i() { fn verify_0x1866_g531i() {
let _guard = test_lock();
std::env::set_var("BOARD_NAME", "G513I"); std::env::set_var("BOARD_NAME", "G513I");
let mut config = AuraConfig::new("1866"); let mut config = AuraConfig::new("1866");
@@ -410,6 +426,7 @@ mod tests {
#[test] #[test]
fn verify_0x19b6_g634j() { fn verify_0x19b6_g634j() {
let _guard = test_lock();
std::env::set_var("BOARD_NAME", "G634J"); std::env::set_var("BOARD_NAME", "G634J");
let mut config = AuraConfig::new("19b6"); let mut config = AuraConfig::new("19b6");

View File

@@ -2,13 +2,13 @@ use std::sync::Arc;
use config::AuraConfig; use config::AuraConfig;
use config_traits::StdConfig; use config_traits::StdConfig;
use futures_util::lock::{Mutex, MutexGuard};
use log::info; use log::info;
use rog_aura::keyboard::{AuraLaptopUsbPackets, LedUsbPackets}; use rog_aura::keyboard::{AuraLaptopUsbPackets, LedUsbPackets};
use rog_aura::usb::{AURA_LAPTOP_LED_APPLY, AURA_LAPTOP_LED_SET}; use rog_aura::usb::{AURA_LAPTOP_LED_APPLY, AURA_LAPTOP_LED_SET};
use rog_aura::{AuraDeviceType, AuraEffect, LedBrightness, PowerZones, AURA_LAPTOP_LED_MSG_LEN}; use rog_aura::{AuraDeviceType, AuraEffect, LedBrightness, PowerZones, AURA_LAPTOP_LED_MSG_LEN};
use rog_platform::hid_raw::HidRaw; use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardBacklight; use rog_platform::keyboard_led::KeyboardBacklight;
use tokio::sync::{Mutex, MutexGuard};
use crate::error::RogError; use crate::error::RogError;
@@ -28,7 +28,7 @@ impl Aura {
Ok(()) Ok(())
} }
pub async fn lock_config(&self) -> MutexGuard<AuraConfig> { pub async fn lock_config(&self) -> MutexGuard<'_, AuraConfig> {
self.config.lock().await self.config.lock().await
} }

View File

@@ -112,7 +112,7 @@ impl AuraZbus {
// entirely possible to deadlock here, so use try instead of lock() // entirely possible to deadlock here, so use try instead of lock()
// let ctrl = self.0.lock().await; // let ctrl = self.0.lock().await;
// Ok(config.current_mode) // Ok(config.current_mode)
if let Some(config) = self.0.config.try_lock() { if let Ok(config) = self.0.config.try_lock() {
Ok(config.current_mode) Ok(config.current_mode)
} else { } else {
Err(ZbErr::Failed("Aura control couldn't lock self".to_string())) Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
@@ -140,7 +140,7 @@ impl AuraZbus {
#[zbus(property)] #[zbus(property)]
async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> { async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> {
// entirely possible to deadlock here, so use try instead of lock() // entirely possible to deadlock here, so use try instead of lock()
if let Some(config) = self.0.config.try_lock() { if let Ok(config) = self.0.config.try_lock() {
let mode = config.current_mode; let mode = config.current_mode;
match config.builtins.get(&mode) { match config.builtins.get(&mode) {
Some(effect) => Ok(effect.clone()), Some(effect) => Ok(effect.clone()),

View File

@@ -4,15 +4,16 @@
// - Add it to Zbus server // - Add it to Zbus server
// - If udev sees device removed then remove the zbus path // - If udev sees device removed then remove the zbus path
use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use dmi_id::DMIID; use dmi_id::DMIID;
use futures_lite::future::block_on; use futures_lite::future::block_on;
use futures_util::lock::Mutex;
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use mio::{Events, Interest, Poll, Token}; use mio::{Events, Interest, Poll, Token};
use rog_platform::error::PlatformError; use rog_platform::error::PlatformError;
use rog_platform::hid_raw::HidRaw; use rog_platform::hid_raw::HidRaw;
use tokio::sync::Mutex;
use udev::{Device, MonitorBuilder}; use udev::{Device, MonitorBuilder};
use zbus::zvariant::{ObjectPath, OwnedObjectPath}; use zbus::zvariant::{ObjectPath, OwnedObjectPath};
use zbus::Connection; use zbus::Connection;
@@ -92,16 +93,41 @@ fn dev_prop_matches(dev: &Device, prop: &str, value: &str) -> bool {
pub struct AsusDevice { pub struct AsusDevice {
device: DeviceHandle, device: DeviceHandle,
dbus_path: OwnedObjectPath, dbus_path: OwnedObjectPath,
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 { pub struct DeviceManager {
_dbus_connection: Connection, _dbus_connection: Connection,
_hid_handles: HidHandleMap,
} }
impl DeviceManager { impl DeviceManager {
async fn get_or_create_hid_handle(
handles: &HidHandleMap,
endpoint: &Device,
) -> Result<(Arc<Mutex<HidRaw>>, String), RogError> {
let dev_node = endpoint
.devnode()
.ok_or_else(|| RogError::MissingFunction("hidraw devnode missing".to_string()))?;
let key = dev_node.to_string_lossy().to_string();
if let Some(existing) = handles.lock().await.get(&key).cloned() {
return Ok((existing, key));
}
let hidraw = HidRaw::from_device(endpoint.clone())?;
let handle = Arc::new(Mutex::new(hidraw));
handles.lock().await.insert(key.clone(), handle.clone());
Ok((handle, key))
}
async fn init_hid_devices( async fn init_hid_devices(
connection: &Connection, connection: &Connection,
device: Device, device: Device,
handles: HidHandleMap,
) -> Result<Vec<AsusDevice>, RogError> { ) -> Result<Vec<AsusDevice>, RogError> {
let mut devices = Vec::new(); let mut devices = Vec::new();
if let Some(usb_device) = device.parent_with_subsystem_devtype("usb", "usb_device")? { if let Some(usb_device) = device.parent_with_subsystem_devtype("usb", "usb_device")? {
@@ -116,9 +142,10 @@ impl DeviceManager {
// 1. Generate an interface path // 1. Generate an interface path
// 2. Create the device // 2. Create the device
// Use the top-level endpoint, not the parent // Use the top-level endpoint, not the parent
if let Ok(hidraw) = HidRaw::from_device(device) { if let Ok((dev, hid_key)) =
Self::get_or_create_hid_handle(&handles, &device).await
{
debug!("Testing device {usb_id:?}"); debug!("Testing device {usb_id:?}");
let dev = Arc::new(Mutex::new(hidraw));
// SLASH DEVICE // SLASH DEVICE
if let Ok(dev_type) = DeviceHandle::new_slash_hid( if let Ok(dev_type) = DeviceHandle::new_slash_hid(
dev.clone(), dev.clone(),
@@ -134,6 +161,7 @@ impl DeviceManager {
devices.push(AsusDevice { devices.push(AsusDevice {
device: dev_type, device: dev_type,
dbus_path: path, dbus_path: path,
hid_key: Some(hid_key.clone()),
}); });
} }
} }
@@ -152,6 +180,7 @@ impl DeviceManager {
devices.push(AsusDevice { devices.push(AsusDevice {
device: dev_type, device: dev_type,
dbus_path: path, dbus_path: path,
hid_key: Some(hid_key.clone()),
}); });
} }
} }
@@ -170,9 +199,12 @@ impl DeviceManager {
devices.push(AsusDevice { devices.push(AsusDevice {
device: dev_type, device: dev_type,
dbus_path: path, dbus_path: path,
hid_key: Some(hid_key),
}); });
} }
} }
} else {
warn!("Failed to initialise shared hid handle for {usb_id:?}");
} }
} }
} }
@@ -181,7 +213,10 @@ impl DeviceManager {
} }
/// To be called on daemon startup /// To be called on daemon startup
async fn init_all_hid(connection: &Connection) -> Result<Vec<AsusDevice>, RogError> { async fn init_all_hid(
connection: &Connection,
handles: HidHandleMap,
) -> Result<Vec<AsusDevice>, RogError> {
// track and ensure we use only one hidraw per prod_id // track and ensure we use only one hidraw per prod_id
// let mut interfaces = HashSet::new(); // let mut interfaces = HashSet::new();
let mut devices: Vec<AsusDevice> = Vec::new(); let mut devices: Vec<AsusDevice> = Vec::new();
@@ -200,7 +235,7 @@ impl DeviceManager {
.scan_devices() .scan_devices()
.map_err(|e| PlatformError::IoPath("enumerator".to_owned(), e))? .map_err(|e| PlatformError::IoPath("enumerator".to_owned(), e))?
{ {
devices.append(&mut Self::init_hid_devices(connection, device).await?); devices.append(&mut Self::init_hid_devices(connection, device, handles.clone()).await?);
} }
Ok(devices) Ok(devices)
@@ -228,6 +263,7 @@ impl DeviceManager {
return Some(AsusDevice { return Some(AsusDevice {
device: dev_type, device: dev_type,
dbus_path: path, dbus_path: path,
hid_key: None,
}); });
} }
} }
@@ -275,10 +311,13 @@ impl DeviceManager {
Ok(devices) Ok(devices)
} }
pub async fn find_all_devices(connection: &Connection) -> Vec<AsusDevice> { pub async fn find_all_devices(
connection: &Connection,
handles: Arc<Mutex<HashMap<String, Arc<Mutex<HidRaw>>>>>,
) -> Vec<AsusDevice> {
let mut devices: Vec<AsusDevice> = Vec::new(); let mut devices: Vec<AsusDevice> = Vec::new();
// HID first, always // HID first, always
if let Ok(devs) = &mut Self::init_all_hid(connection).await { if let Ok(devs) = &mut Self::init_all_hid(connection, handles.clone()).await {
devices.append(devs); devices.append(devs);
} }
// USB after, need to check if HID picked something up and if so, skip it // USB after, need to check if HID picked something up and if so, skip it
@@ -306,6 +345,7 @@ impl DeviceManager {
devices.push(AsusDevice { devices.push(AsusDevice {
device: dev_type, device: dev_type,
dbus_path: path, dbus_path: path,
hid_key: None,
}); });
} }
} else { } else {
@@ -328,6 +368,7 @@ impl DeviceManager {
devices.push(AsusDevice { devices.push(AsusDevice {
device: dev_type, device: dev_type,
dbus_path: path, dbus_path: path,
hid_key: None,
}); });
} }
} }
@@ -355,6 +396,7 @@ impl DeviceManager {
devices.push(AsusDevice { devices.push(AsusDevice {
device: dev_type, device: dev_type,
dbus_path: path, dbus_path: path,
hid_key: None,
}); });
} }
} }
@@ -370,16 +412,19 @@ impl DeviceManager {
pub async fn new(connection: Connection) -> Result<Self, RogError> { pub async fn new(connection: Connection) -> Result<Self, RogError> {
let conn_copy = connection.clone(); let conn_copy = connection.clone();
let devices = Self::find_all_devices(&conn_copy).await; let hid_handles = Arc::new(Mutex::new(HashMap::new()));
let devices = Self::find_all_devices(&conn_copy, hid_handles.clone()).await;
info!("Found {} valid devices on startup", devices.len()); info!("Found {} valid devices on startup", devices.len());
let devices = Arc::new(Mutex::new(devices)); let devices = Arc::new(Mutex::new(devices));
let manager = Self { let manager = Self {
_dbus_connection: connection, _dbus_connection: connection,
_hid_handles: hid_handles.clone(),
}; };
// TODO: The /sysfs/ LEDs don't cause events, so they need to be manually // TODO: The /sysfs/ LEDs don't cause events, so they need to be manually
// checked for and added // checked for and added
let hid_handles_thread = hid_handles.clone();
std::thread::spawn(move || { std::thread::spawn(move || {
let mut monitor = MonitorBuilder::new()?.listen()?; let mut monitor = MonitorBuilder::new()?.listen()?;
let mut poll = Poll::new()?; let mut poll = Poll::new()?;
@@ -408,6 +453,7 @@ impl DeviceManager {
let devices = devices.clone(); let devices = devices.clone();
let conn_copy = conn_copy.clone(); let conn_copy = conn_copy.clone();
let hid_handles = hid_handles_thread.clone();
block_on(async move { block_on(async move {
// SCSCI devs // SCSCI devs
if subsys == "block" { if subsys == "block" {
@@ -483,6 +529,7 @@ impl DeviceManager {
// Iter in reverse so as to not screw up indexing // Iter in reverse so as to not screw up indexing
for index in removals.iter().rev() { for index in removals.iter().rev() {
let dev = devices.lock().await.remove(*index); let dev = devices.lock().await.remove(*index);
let hid_key = dev.hid_key.clone();
let path = path.clone(); let path = path.clone();
let res = match dev.device { let res = match dev.device {
DeviceHandle::Aura(_) => { DeviceHandle::Aura(_) => {
@@ -512,14 +559,20 @@ impl DeviceManager {
_ => todo!(), _ => todo!(),
}; };
info!("AuraManager removed: {path:?}, {res}"); info!("AuraManager removed: {path:?}, {res}");
if let Some(key) = hid_key {
hid_handles.lock().await.remove(&key);
}
} }
} }
} else if action == "add" { } else if action == "add" {
let evdev = event.device(); let evdev = event.device();
if let Ok(mut new_devs) = if let Ok(mut new_devs) = Self::init_hid_devices(
Self::init_hid_devices(&conn_copy, evdev) &conn_copy,
.await evdev,
.map_err(|e| error!("Couldn't add new device: {e:?}")) hid_handles.clone(),
)
.await
.map_err(|e| error!("Couldn't add new device: {e:?}"))
{ {
devices.lock().await.append(&mut new_devs); devices.lock().await.append(&mut new_devs);
} }

View File

@@ -1,8 +1,8 @@
use std::sync::Arc; use std::sync::Arc;
use config::ScsiConfig; use config::ScsiConfig;
use futures_util::lock::{Mutex, MutexGuard};
use rog_scsi::{AuraEffect, Device, Task}; use rog_scsi::{AuraEffect, Device, Task};
use tokio::sync::{Mutex, MutexGuard};
use crate::error::RogError; use crate::error::RogError;
@@ -20,7 +20,7 @@ impl ScsiAura {
Self { device, config } Self { device, config }
} }
pub async fn lock_config(&self) -> MutexGuard<ScsiConfig> { pub async fn lock_config(&self) -> MutexGuard<'_, ScsiConfig> {
self.config.lock().await self.config.lock().await
} }

View File

@@ -83,7 +83,7 @@ impl ScsiZbus {
#[zbus(property)] #[zbus(property)]
async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> { async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> {
// entirely possible to deadlock here, so use try instead of lock() // entirely possible to deadlock here, so use try instead of lock()
if let Some(config) = self.0.config.try_lock() { if let Ok(config) = self.0.config.try_lock() {
let mode = config.current_mode; let mode = config.current_mode;
match config.modes.get(&mode) { match config.modes.get(&mode) {
Some(effect) => Ok(effect.clone()), Some(effect) => Ok(effect.clone()),

View File

@@ -1,10 +1,10 @@
use std::sync::Arc; use std::sync::Arc;
use config::SlashConfig; use config::SlashConfig;
use futures_util::lock::{Mutex, MutexGuard};
use rog_platform::hid_raw::HidRaw; use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw; use rog_platform::usb_raw::USBRaw;
use rog_slash::usb::{slash_pkt_enable, slash_pkt_init, slash_pkt_options, slash_pkt_set_mode}; use rog_slash::usb::{slash_pkt_enable, slash_pkt_init, slash_pkt_options, slash_pkt_set_mode};
use tokio::sync::{Mutex, MutexGuard};
use crate::error::RogError; use crate::error::RogError;
@@ -27,7 +27,7 @@ impl Slash {
Self { hid, usb, config } Self { hid, usb, config }
} }
pub async fn lock_config(&self) -> MutexGuard<SlashConfig> { pub async fn lock_config(&self) -> MutexGuard<'_, SlashConfig> {
self.config.lock().await self.config.lock().await
} }

View File

@@ -1,7 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use config_traits::{StdConfig, StdConfigLoad}; use config_traits::{StdConfig, StdConfigLoad};
use futures_util::lock::Mutex;
use log::{debug, error, info}; use log::{debug, error, info};
use rog_anime::error::AnimeError; use rog_anime::error::AnimeError;
use rog_anime::usb::get_anime_type; use rog_anime::usb::get_anime_type;
@@ -13,6 +12,7 @@ use rog_platform::usb_raw::USBRaw;
use rog_scsi::{open_device, ScsiType}; use rog_scsi::{open_device, ScsiType};
use rog_slash::error::SlashError; use rog_slash::error::SlashError;
use rog_slash::SlashType; use rog_slash::SlashType;
use tokio::sync::Mutex;
use crate::aura_anime::config::AniMeConfig; use crate::aura_anime::config::AniMeConfig;
use crate::aura_anime::AniMe; use crate::aura_anime::AniMe;

View File

@@ -67,6 +67,19 @@ impl Config {
}; };
config.entry(profile).or_insert_with(Tuning::default) config.entry(profile).or_insert_with(Tuning::default)
} }
pub fn select_tunings_ref(
&self,
power_plugged: bool,
profile: PlatformProfile,
) -> Option<&Tuning> {
let config = if power_plugged {
&self.ac_profile_tunings
} else {
&self.dc_profile_tunings
};
config.get(&profile)
}
} }
impl Default for Config { impl Default for Config {
@@ -146,7 +159,7 @@ pub struct Config611 {
impl From<Config611> for Config { impl From<Config611> for Config {
fn from(c: Config611) -> Self { fn from(c: Config611) -> Self {
Self { let mut config = Self {
// Restore the base charge limit // Restore the base charge limit
charge_control_end_threshold: c.charge_control_end_threshold, charge_control_end_threshold: c.charge_control_end_threshold,
base_charge_control_end_threshold: c.charge_control_end_threshold, base_charge_control_end_threshold: c.charge_control_end_threshold,
@@ -168,7 +181,12 @@ impl From<Config611> for Config {
armoury_settings: HashMap::default(), armoury_settings: HashMap::default(),
screenpad_gamma: None, screenpad_gamma: None,
screenpad_sync_primary: Default::default(), screenpad_sync_primary: Default::default(),
} };
config.ac_profile_tunings = c.ac_profile_tunings;
config.dc_profile_tunings = c.dc_profile_tunings;
config.armoury_settings = c.armoury_settings;
config
} }
} }
@@ -207,6 +225,8 @@ pub struct Config601 {
pub nv_dynamic_boost: Option<u8>, pub nv_dynamic_boost: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)] #[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_temp_target: Option<u8>, pub nv_temp_target: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_tgp: Option<u8>,
#[serde(skip)] #[serde(skip)]
pub last_power_plugged: u8, pub last_power_plugged: u8,
} }

View File

@@ -2,9 +2,9 @@ use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use config_traits::StdConfig; use config_traits::StdConfig;
use futures_util::lock::Mutex;
use log::{info, warn}; use log::{info, warn};
use rog_platform::backlight::{Backlight, BacklightType}; use rog_platform::backlight::{Backlight, BacklightType};
use tokio::sync::Mutex;
use zbus::fdo::Error as FdoErr; use zbus::fdo::Error as FdoErr;
use zbus::object_server::SignalEmitter; use zbus::object_server::SignalEmitter;
use zbus::{interface, Connection}; use zbus::{interface, Connection};
@@ -13,7 +13,7 @@ use crate::config::Config;
use crate::error::RogError; use crate::error::RogError;
use crate::ASUS_ZBUS_PATH; use crate::ASUS_ZBUS_PATH;
#[derive(Debug, Clone)] #[derive(Clone)]
pub struct CtrlBacklight { pub struct CtrlBacklight {
backlights: Vec<Backlight>, backlights: Vec<Backlight>,
config: Arc<Mutex<Config>>, config: Arc<Mutex<Config>>,

View File

@@ -3,13 +3,13 @@ use std::sync::Arc;
use config_traits::{StdConfig, StdConfigLoad}; use config_traits::{StdConfig, StdConfigLoad};
use futures_lite::StreamExt; use futures_lite::StreamExt;
use futures_util::lock::Mutex;
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use rog_platform::platform::{PlatformProfile, RogPlatform}; use rog_platform::platform::{PlatformProfile, RogPlatform};
use rog_profiles::error::ProfileError; use rog_profiles::error::ProfileError;
use rog_profiles::fan_curve_set::CurveData; use rog_profiles::fan_curve_set::CurveData;
use rog_profiles::{find_fan_curve_node, FanCurvePU, FanCurveProfiles}; use rog_profiles::{find_fan_curve_node, FanCurvePU, FanCurveProfiles};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::sync::Mutex;
use zbus::object_server::SignalEmitter; use zbus::object_server::SignalEmitter;
use zbus::{interface, Connection}; use zbus::{interface, Connection};

View File

@@ -3,17 +3,17 @@ use std::process::Command;
use std::sync::Arc; use std::sync::Arc;
use config_traits::StdConfig; use config_traits::StdConfig;
use futures_util::lock::Mutex;
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use rog_platform::asus_armoury::{AttrValue, FirmwareAttribute, FirmwareAttributes}; use rog_platform::asus_armoury::{AttrValue, FirmwareAttribute, FirmwareAttributes};
use rog_platform::cpu::{CPUControl, CPUGovernor, CPUEPP}; use rog_platform::cpu::{CPUControl, CPUGovernor, CPUEPP};
use rog_platform::platform::{PlatformProfile, Properties, RogPlatform}; use rog_platform::platform::{PlatformProfile, Properties, RogPlatform};
use rog_platform::power::AsusPower; use rog_platform::power::AsusPower;
use tokio::sync::Mutex;
use zbus::fdo::Error as FdoErr; use zbus::fdo::Error as FdoErr;
use zbus::object_server::SignalEmitter; use zbus::object_server::SignalEmitter;
use zbus::{interface, Connection}; use zbus::{interface, Connection};
use crate::asus_armoury::set_config_or_default; use crate::asus_armoury::{set_config_or_default, ArmouryAttributeRegistry};
use crate::config::Config; use crate::config::Config;
use crate::error::RogError; use crate::error::RogError;
use crate::{task_watch_item, CtrlTask, ReloadAndNotify}; use crate::{task_watch_item, CtrlTask, ReloadAndNotify};
@@ -46,9 +46,12 @@ pub struct CtrlPlatform {
attributes: FirmwareAttributes, attributes: FirmwareAttributes,
cpu_control: Option<CPUControl>, cpu_control: Option<CPUControl>,
config: Arc<Mutex<Config>>, config: Arc<Mutex<Config>>,
connection: Connection,
armoury_registry: ArmouryAttributeRegistry,
} }
impl CtrlPlatform { impl CtrlPlatform {
#[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
platform: RogPlatform, platform: RogPlatform,
power: AsusPower, power: AsusPower,
@@ -56,6 +59,8 @@ impl CtrlPlatform {
config: Arc<Mutex<Config>>, config: Arc<Mutex<Config>>,
config_path: &Path, config_path: &Path,
signal_context: SignalEmitter<'static>, signal_context: SignalEmitter<'static>,
connection: Connection,
armoury_registry: ArmouryAttributeRegistry,
) -> Result<Self, RogError> { ) -> Result<Self, RogError> {
let config1 = config.clone(); let config1 = config.clone();
let config_path = config_path.to_owned(); let config_path = config_path.to_owned();
@@ -68,6 +73,8 @@ impl CtrlPlatform {
cpu_control: CPUControl::new() cpu_control: CPUControl::new()
.map_err(|e| error!("Couldn't get CPU control sysfs: {e}")) .map_err(|e| error!("Couldn't get CPU control sysfs: {e}"))
.ok(), .ok(),
connection,
armoury_registry,
}; };
let mut inotify_self = ret_self.clone(); let mut inotify_self = ret_self.clone();
@@ -161,7 +168,7 @@ impl CtrlPlatform {
.map(|s| s.to_string()) .map(|s| s.to_string())
.collect() .collect()
}; };
if prog.len() > 1 { if (!prog.is_empty()) && (!prog[0].is_empty()) {
let mut cmd = Command::new(&prog[0]); let mut cmd = Command::new(&prog[0]);
for arg in prog.iter().skip(1) { for arg in prog.iter().skip(1) {
cmd.arg(arg); cmd.arg(arg);
@@ -559,7 +566,7 @@ impl CtrlPlatform {
for attr in self.attributes.attributes() { for attr in self.attributes.attributes() {
let name: FirmwareAttribute = attr.name().into(); let name: FirmwareAttribute = attr.name().into();
if name.is_ppt() { if name.is_ppt() || name.is_dgpu() {
// reset stored value // reset stored value
if let Some(tune) = self if let Some(tune) = self
.config .config
@@ -729,6 +736,31 @@ impl CtrlTask for CtrlPlatform {
} }
if !sleeping { if !sleeping {
platform1.run_ac_or_bat_cmd(power_plugged > 0).await; platform1.run_ac_or_bat_cmd(power_plugged > 0).await;
if let Ok(profile) =
platform1.platform.get_platform_profile().map(|p| p.into())
{
let attrs = FirmwareAttributes::new();
{
let mut cfg = platform1.config.lock().await;
set_config_or_default(
&attrs,
&mut cfg,
power_plugged > 0,
profile,
)
.await;
}
if let Err(e) = platform1
.armoury_registry
.emit_limits(&platform1.connection)
.await
{
error!(
"Failed to emit armoury updates after power change: \
{e:?}"
);
}
}
} }
platform1.config.lock().await.last_power_plugged = power_plugged; platform1.config.lock().await.last_power_plugged = power_plugged;
} }
@@ -789,13 +821,17 @@ impl CtrlTask for CtrlPlatform {
{ {
// TODO: manage this better, shouldn't need to create every time // TODO: manage this better, shouldn't need to create every time
let attrs = FirmwareAttributes::new(); let attrs = FirmwareAttributes::new();
set_config_or_default( {
&attrs, let mut cfg = platform3.config.lock().await;
&mut *platform3.config.lock().await, set_config_or_default(&attrs, &mut cfg, power_plugged, profile).await;
power_plugged, }
profile, if let Err(e) = platform3
) .armoury_registry
.await; .emit_limits(&platform3.connection)
.await
{
error!("Failed to emit armoury updates after AC/DC toggle: {e:?}");
}
platform3 platform3
.enable_ppt_group_changed(&signal_ctxt_copy) .enable_ppt_group_changed(&signal_ctxt_copy)
.await .await
@@ -852,6 +888,9 @@ impl CtrlTask for CtrlPlatform {
profile, profile,
) )
.await; .await;
if let Err(e) = ctrl.armoury_registry.emit_limits(&ctrl.connection).await {
error!("Failed to emit armoury updates after profile change: {e:?}");
}
} }
} }
} }

View File

@@ -3,7 +3,7 @@ use std::error::Error;
use std::sync::Arc; use std::sync::Arc;
use ::zbus::Connection; use ::zbus::Connection;
use asusd::asus_armoury::start_attributes_zbus; use asusd::asus_armoury::{start_attributes_zbus, ArmouryAttributeRegistry};
use asusd::aura_manager::DeviceManager; use asusd::aura_manager::DeviceManager;
use asusd::config::Config; use asusd::config::Config;
use asusd::ctrl_backlight::CtrlBacklight; use asusd::ctrl_backlight::CtrlBacklight;
@@ -11,15 +11,17 @@ use asusd::ctrl_fancurves::CtrlFanCurveZbus;
use asusd::ctrl_platform::CtrlPlatform; use asusd::ctrl_platform::CtrlPlatform;
use asusd::{print_board_info, start_tasks, CtrlTask, ZbusRun, DBUS_NAME}; use asusd::{print_board_info, start_tasks, CtrlTask, ZbusRun, DBUS_NAME};
use config_traits::{StdConfig, StdConfigLoad2}; use config_traits::{StdConfig, StdConfigLoad2};
use futures_util::lock::Mutex;
use log::{error, info}; use log::{error, info};
use rog_platform::asus_armoury::FirmwareAttributes; use rog_platform::asus_armoury::FirmwareAttributes;
use rog_platform::platform::RogPlatform; use rog_platform::platform::RogPlatform;
use rog_platform::power::AsusPower; use rog_platform::power::AsusPower;
use tokio::sync::Mutex;
use zbus::fdo::ObjectManager; use zbus::fdo::ObjectManager;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Starting asusd daemon...");
// console_subscriber::init(); // console_subscriber::init();
let mut logger = env_logger::Builder::new(); let mut logger = env_logger::Builder::new();
logger logger
@@ -74,22 +76,34 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
let platform = RogPlatform::new()?; // TODO: maybe needs async mutex? let platform = RogPlatform::new()?; // TODO: maybe needs async mutex?
let power = AsusPower::new()?; // TODO: maybe needs async mutex? let power = AsusPower::new()?; // TODO: maybe needs async mutex?
let attributes = FirmwareAttributes::new(); let attributes = FirmwareAttributes::new();
if let Err(e) = start_attributes_zbus( let armoury_registry = match start_attributes_zbus(
&server, Some(&server),
platform.clone(), platform.clone(),
power.clone(), power.clone(),
attributes.clone(), attributes.clone(),
config.clone(), config.clone(),
true,
None,
None,
) )
.await .await
{ {
error!("Failed to initialize firmware attributes over zbus: {e:?}"); Ok(registry) => {
} info!("attribute on zbus initialized");
registry
}
Err(e) => {
error!("Failed to initialize firmware attributes over zbus: {e:?}");
ArmouryAttributeRegistry::default()
}
};
match CtrlFanCurveZbus::new() { match CtrlFanCurveZbus::new() {
Ok(ctrl) => { Ok(ctrl) => {
info!("FanCurves: found supported fancurves");
let sig_ctx = CtrlFanCurveZbus::signal_context(&server)?; let sig_ctx = CtrlFanCurveZbus::signal_context(&server)?;
start_tasks(ctrl, &mut server, sig_ctx).await?; start_tasks(ctrl, &mut server, sig_ctx).await?;
info!("FanCurves: initialized");
} }
Err(err) => { Err(err) => {
error!("FanCurves: {}", err); error!("FanCurves: {}", err);
@@ -98,8 +112,10 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
match CtrlBacklight::new(config.clone()) { match CtrlBacklight::new(config.clone()) {
Ok(backlight) => { Ok(backlight) => {
info!("Backlight: found supported backlight");
backlight.start_watch_primary().await?; backlight.start_watch_primary().await?;
backlight.add_to_server(&mut server).await; backlight.add_to_server(&mut server).await;
info!("Backlight: initialized");
} }
Err(err) => { Err(err) => {
error!("Backlight: {}", err); error!("Backlight: {}", err);
@@ -113,10 +129,14 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
config.clone(), config.clone(),
&cfg_path, &cfg_path,
CtrlPlatform::signal_context(&server)?, CtrlPlatform::signal_context(&server)?,
server.clone(),
armoury_registry,
) { ) {
Ok(ctrl) => { Ok(ctrl) => {
info!("CtrlPlatform: initialized");
let sig_ctx = CtrlPlatform::signal_context(&server)?; let sig_ctx = CtrlPlatform::signal_context(&server)?;
start_tasks(ctrl, &mut server, sig_ctx).await?; start_tasks(ctrl, &mut server, sig_ctx).await?;
info!("CtrlPlatform: tasks started");
} }
Err(err) => { Err(err) => {
error!("CtrlPlatform: {}", err); error!("CtrlPlatform: {}", err);
@@ -125,10 +145,12 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
let _ = DeviceManager::new(server.clone()).await?; let _ = DeviceManager::new(server.clone()).await?;
info!("DeviceManager initialized");
// Request dbus name after finishing initalizing all functions // Request dbus name after finishing initalizing all functions
server.request_name(DBUS_NAME).await?; server.request_name(DBUS_NAME).await?;
info!("Startup success, begining dbus server loop"); info!("Startup success on dbus name {DBUS_NAME}: begining dbus server loop");
loop { loop {
// This is just a blocker to idle and ensure the reator reacts // This is just a blocker to idle and ensure the reator reacts
server.executor().tick().await; server.executor().tick().await;

View File

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

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

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

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

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

@@ -9,12 +9,13 @@ ENV{DMI_FAMILY}=="*Strix*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Vivo*ook*", GOTO="asusd_start" ENV{DMI_FAMILY}=="*Vivo*ook*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Zenbook*", GOTO="asusd_start" ENV{DMI_FAMILY}=="*Zenbook*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*ProArt*", 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}=="*TX Gaming*", GOTO="asusd_start"
# No match so # No match so
GOTO="asusd_end" GOTO="asusd_end"
LABEL="asusd_start" LABEL="asusd_start"
ACTION=="add|change", DRIVER=="asus-nb-wmi", TAG+="systemd", ENV{SYSTEMD_WANTS}="asusd.service" ACTION=="add|change", DRIVER=="asus-nb-wmi", TAG+="systemd", ENV{SYSTEMD_WANTS}+="asusd.service"
ACTION=="add|remove", DRIVER=="asus-nb-wmi", TAG+="systemd", RUN+="/usr/bin/systemctl restart asusd.service" ACTION=="add|remove", DRIVER=="asus-nb-wmi", TAG+="systemd", ENV{SYSTEMD_WANTS}+="asusd.service"
LABEL="asusd_end" LABEL="asusd_end"

View File

@@ -20,7 +20,7 @@
%global debug_package %{nil} %global debug_package %{nil}
%endif %endif
%define version 6.1.14 %define version 6.1.17
%define specrelease %{?dist} %define specrelease %{?dist}
%define pkg_release 9%{specrelease} %define pkg_release 9%{specrelease}
@@ -92,7 +92,9 @@ EOF
%build %build
export RUSTFLAGS="%{rustflags}" export RUSTFLAGS="%{rustflags}"
%if %{defined fedora} %if %{defined fedora}
%cargo_build %# 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
%else %else
/usr/bin/cargo auditable build --release /usr/bin/cargo auditable build --release
%endif %endif

View File

@@ -169,7 +169,7 @@ impl AnimeImage {
// first 5 rows for GA401 are always at X = 0 // first 5 rows for GA401 are always at X = 0
return 0; return 0;
} }
(y + 1) / 2 - 3 y.div_ceil(2) - 3
} }
AnimeType::GU604 => { AnimeType::GU604 => {
// first 9 rows start at zero // first 9 rows start at zero
@@ -185,7 +185,7 @@ impl AnimeImage {
return 0; return 0;
} }
// and then their offset grows by one every two rows // and then their offset grows by one every two rows
(y + 1) / 2 - 5 y.div_ceil(2) - 5
} }
} }
} }
@@ -213,7 +213,7 @@ impl AnimeImage {
// First 5 rows for GA401 are always 33 physical LEDs long // First 5 rows for GA401 are always 33 physical LEDs long
return 33; return 33;
} }
36 - (y + 1) / 2 36 - y.div_ceil(2)
} }
AnimeType::GU604 => { AnimeType::GU604 => {
if y <= 9 { if y <= 9 {

View File

@@ -5,7 +5,7 @@
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -14,7 +14,7 @@
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle], basic_modes: [Static, Breathe, RainbowCycle],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -23,7 +23,7 @@
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -32,7 +32,7 @@
layout_name: "fa507", layout_name: "fa507",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -41,7 +41,7 @@
layout_name: "fx505d", layout_name: "fx505d",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -50,7 +50,7 @@
layout_name: "fx505d", layout_name: "fx505d",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -59,7 +59,7 @@
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -68,7 +68,7 @@
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -77,7 +77,7 @@
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -86,7 +86,16 @@
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard],
),
(
device_name: "FX607J",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -95,7 +104,7 @@
layout_name: "fa506i", layout_name: "fa506i",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -104,7 +113,7 @@
layout_name: "fx505d", layout_name: "fx505d",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -113,7 +122,7 @@
layout_name: "fx505d", layout_name: "fx505d",
basic_modes: [Static, Breathe, RainbowCycle], basic_modes: [Static, Breathe, RainbowCycle],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -122,7 +131,7 @@
layout_name: "g512", layout_name: "g512",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -140,7 +149,7 @@
layout_name: "g513i", layout_name: "g513i",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -185,7 +194,7 @@
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -194,7 +203,7 @@
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -239,7 +248,16 @@
layout_name: "g634j-per-key", layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G614JIR",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -248,7 +266,7 @@
layout_name: "g634j-per-key", layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -257,7 +275,7 @@
layout_name: "g634j-per-key", layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -275,7 +293,7 @@
layout_name: "gl503", layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -284,7 +302,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -293,7 +311,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -302,7 +320,7 @@
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -311,7 +329,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -320,7 +338,7 @@
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -329,7 +347,7 @@
layout_name: "gx502", layout_name: "gx502",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -365,7 +383,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -383,7 +401,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4, BarLeft, BarRight], basic_zones: [Key1, Key2, Key3, Key4, BarLeft, BarRight],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
( (
@@ -401,7 +419,7 @@
layout_name: "g533q", layout_name: "g533q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -410,7 +428,7 @@
layout_name: "g533q", layout_name: "g533q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -419,7 +437,7 @@
layout_name: "g533q", layout_name: "g533q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -482,7 +500,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -491,7 +509,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -500,7 +518,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -509,7 +527,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -518,7 +536,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -527,7 +545,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -536,7 +554,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -545,7 +563,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -554,7 +572,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -563,7 +581,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -572,7 +590,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -590,7 +608,7 @@
layout_name: "gl503", layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -599,7 +617,7 @@
layout_name: "gl503", layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -608,7 +626,7 @@
layout_name: "gl503", layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4, Logo, BarLeft, BarRight], basic_zones: [Key1, Key2, Key3, Key4, Logo, BarLeft, BarRight],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -626,7 +644,7 @@
layout_name: "g533q", layout_name: "g533q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -635,7 +653,7 @@
layout_name: "gl503", layout_name: "gl503",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -644,7 +662,7 @@
layout_name: "fa507", layout_name: "fa507",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -692,6 +710,15 @@
advanced_type: Zoned([SingleZone]), advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
(
device_name: "GU605C",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
),
( (
device_name: "GU605M", device_name: "GU605M",
product_id: "", product_id: "",
@@ -707,7 +734,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -716,7 +743,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -725,7 +752,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -734,7 +761,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -752,7 +779,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -770,7 +797,7 @@
layout_name: "gx531-per-key", layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [Key1, Key2, Key3, Key4], basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -824,7 +851,7 @@
layout_name: "gx531-per-key", layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -833,7 +860,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -842,7 +869,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -851,8 +878,8 @@
layout_name: "", layout_name: "",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [None], power_zones: [r#None],
), ),
( (
device_name: "GZ301Z", device_name: "GZ301Z",
@@ -860,7 +887,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse], basic_modes: [Static, Breathe, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
( (
@@ -869,7 +896,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Ally], power_zones: [Ally],
), ),
( (
@@ -878,7 +905,7 @@
layout_name: "ga401q", layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [], basic_zones: [],
advanced_type: None, advanced_type: r#None,
power_zones: [Ally], power_zones: [Ally],
), ),
]) ])

View File

@@ -164,7 +164,29 @@ impl LedSupportFile {
return Some(data); return Some(data);
} }
warn!("Does {} exist?", ASUS_LED_MODE_USER_CONF); // 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);
data.0.sort_by(|a, b| a.device_name.cmp(&b.device_name));
info!("Loaded bundled LED support data from {}", bundled.display());
return Some(data);
} else {
warn!(
"Bundled aura_support.ron present but failed to parse: {}",
bundled.display()
);
}
} else {
warn!("Does {} exist?", ASUS_LED_MODE_USER_CONF);
}
None None
} }
} }

View File

@@ -12,7 +12,7 @@ edition.workspace = true
default = [] default = []
mocking = [] mocking = []
x11 = ["slint/backend-winit-x11"] x11 = ["slint/backend-winit-x11"]
# Requires RUSTFLAGS="--cfg tokio_unstable" # Optional tokio debug feature does not require nightly; remove RUSTFLAGS note.
tokio-debug = ["console-subscriber"] tokio-debug = ["console-subscriber"]
[dependencies] [dependencies]

View File

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

View File

@@ -140,12 +140,22 @@ async fn main() -> Result<()> {
config.startup_in_background = false; config.startup_in_background = false;
config.start_fullscreen = true; 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(); config.write();
let enable_tray_icon = config.enable_tray_icon; let enable_tray_icon = config.enable_tray_icon;
let startup_in_background = config.startup_in_background; let startup_in_background = config.startup_in_background;
let config = Arc::new(Mutex::new(config)); 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)?; start_notifications(config.clone(), &rt)?;
if enable_tray_icon { if enable_tray_icon {
@@ -170,12 +180,14 @@ async fn main() -> Result<()> {
slint::init_translations!(concat!(env!("CARGO_MANIFEST_DIR"), "/translations/")); 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 || { thread::spawn(move || {
let mut state = AppState::StartingUp; let mut state = AppState::StartingUp;
loop { loop {
if is_rog_ally { if is_rog_ally {
let config_copy_2 = config.clone(); let config_copy_2 = config_for_ui.clone();
let newui = setup_window(config.clone()); let newui = setup_window(config_for_ui.clone());
newui.window().on_close_requested(move || { newui.window().on_close_requested(move || {
exit(0); exit(0);
}); });
@@ -211,13 +223,19 @@ async fn main() -> Result<()> {
*app_state = AppState::MainWindowOpen; *app_state = AppState::MainWindowOpen;
} }
let config_copy = config.clone(); let config_copy = config_for_ui.clone();
let app_state_copy = app_state.clone(); let app_state_copy = app_state.clone();
let ui_handle_for_ui = ui_handle_for_thread.clone();
slint::invoke_from_event_loop(move || { slint::invoke_from_event_loop(move || {
let ui_handle_for_ui = ui_handle_for_ui.clone();
UI.with(|ui| { UI.with(|ui| {
let app_state_copy = app_state_copy.clone(); let app_state_copy = app_state_copy.clone();
let mut ui = ui.borrow_mut(); let mut ui = ui.borrow_mut();
if let Some(ui) = ui.as_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().show().unwrap();
ui.window().on_close_requested(move || { ui.window().on_close_requested(move || {
if let Ok(mut app_state) = app_state_copy.lock() { if let Ok(mut app_state) = app_state_copy.lock() {
@@ -228,6 +246,10 @@ async fn main() -> Result<()> {
} else { } else {
let config_copy_2 = config_copy.clone(); let config_copy_2 = config_copy.clone();
let newui = setup_window(config_copy); 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 || { newui.window().on_close_requested(move || {
if let Ok(mut app_state) = app_state_copy.lock() { if let Ok(mut app_state) = app_state_copy.lock() {
*app_state = AppState::MainWindowClosed; *app_state = AppState::MainWindowClosed;
@@ -263,8 +285,8 @@ async fn main() -> Result<()> {
slint::quit_event_loop().unwrap(); slint::quit_event_loop().unwrap();
exit(0); exit(0);
} else if state != AppState::MainWindowOpen { } else if state != AppState::MainWindowOpen {
if let Ok(config) = config.lock() { if let Ok(cfg) = config_for_ui.lock() {
if !config.run_in_background { if !cfg.run_in_background {
slint::quit_event_loop().unwrap(); slint::quit_event_loop().unwrap();
exit(0); exit(0);
} }
@@ -274,11 +296,67 @@ 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(); slint::run_event_loop_until_quit().unwrap();
rt.shutdown_background(); rt.shutdown_background();
Ok(()) 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 { fn do_cli_help(parsed: &CliStart) -> bool {
if parsed.help { if parsed.help {
println!("{}", CliStart::usage()); println!("{}", CliStart::usage());

View File

@@ -100,6 +100,31 @@ impl Bios {
pub fn set_panel_od(&self, _b: bool) -> Result<()> { pub fn set_panel_od(&self, _b: bool) -> Result<()> {
Ok(()) 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; pub struct Profile;

View File

@@ -116,26 +116,26 @@ pub fn start_notifications(
if p == 0 && p != last_state { if p == 0 && p != last_state {
let prog: Vec<&str> = bat.split_whitespace().collect(); let prog: Vec<&str> = bat.split_whitespace().collect();
if prog.len() > 1 { if (!prog.is_empty()) && (!prog[0].is_empty()) {
let mut cmd = Command::new(prog[0]); let mut cmd = Command::new(prog[0]);
for arg in prog.iter().skip(1) { for arg in prog.iter().skip(1) {
cmd.arg(*arg); cmd.arg(*arg);
} }
cmd.spawn() cmd.spawn()
.map_err(|e| error!("AC command error: {e:?}")) .map_err(|e| error!("Battery power command error: {e:?}"))
.ok(); .ok();
} }
} else if p != last_state { } else if p != last_state {
let prog: Vec<&str> = ac.split_whitespace().collect(); let prog: Vec<&str> = ac.split_whitespace().collect();
if prog.len() > 1 { if (!prog.is_empty()) && (!prog[0].is_empty()) {
let mut cmd = Command::new(prog[0]); let mut cmd = Command::new(prog[0]);
for arg in prog.iter().skip(1) { for arg in prog.iter().skip(1) {
cmd.arg(*arg); cmd.arg(*arg);
} }
cmd.spawn() cmd.spawn()
.map_err(|e| error!("AC command error: {e:?}")) .map_err(|e| error!("AC power command error: {e:?}"))
.ok(); .ok();
} }
} }
@@ -203,11 +203,19 @@ pub fn start_notifications(
})?; })?;
let proxy_copy = proxy.clone(); let proxy_copy = proxy.clone();
let enabled_notifications_copy_action = enabled_notifications_copy.clone();
let mut p = proxy.receive_notify_action().await?; let mut p = proxy.receive_notify_action().await?;
tokio::spawn(async move { tokio::spawn(async move {
info!("Started zbus signal thread: receive_notify_action"); info!("Started zbus signal thread: receive_notify_action");
while let Some(e) = p.next().await { while let Some(e) = p.next().await {
if let Ok(out) = e.args() { 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 action = out.action();
let mode = convert_gfx_mode(proxy.mode().await.unwrap_or_default()); let mode = convert_gfx_mode(proxy.mode().await.unwrap_or_default());
match action { match action {
@@ -309,7 +317,9 @@ fn do_gfx_action_notif(message: &str, action: GfxUserAction, mode: GpuMode) -> R
//.hint(Hint::Resident(true)) //.hint(Hint::Resident(true))
.hint(Hint::Category("device".into())) .hint(Hint::Category("device".into()))
.urgency(Urgency::Critical) .urgency(Urgency::Critical)
.timeout(Timeout::Never) // 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") .icon("dialog-warning")
.hint(Hint::Transient(true)); .hint(Hint::Transient(true));

View File

@@ -12,7 +12,7 @@ use slint::{ComponentHandle, Model, ModelRc, SharedString, VecModel};
use super::show_toast; use super::show_toast;
use crate::config::Config; use crate::config::Config;
use crate::zbus_proxies::find_iface_async; use crate::zbus_proxies::find_iface_async;
use crate::{set_ui_callbacks, set_ui_props_async, AttrMinMax, MainWindow, SystemPageData}; use crate::{set_ui_callbacks, AttrMinMax, MainWindow, SystemPageData};
const MINMAX: AttrMinMax = AttrMinMax { const MINMAX: AttrMinMax = AttrMinMax {
min: 0, min: 0,
@@ -40,6 +40,8 @@ pub fn setup_system_page(ui: &MainWindow, _config: Arc<Mutex<Config>>) {
ui.global::<SystemPageData>().set_platform_profile(-1); ui.global::<SystemPageData>().set_platform_profile(-1);
ui.global::<SystemPageData>().set_panel_overdrive(-1); ui.global::<SystemPageData>().set_panel_overdrive(-1);
ui.global::<SystemPageData>().set_boot_sound(-1); ui.global::<SystemPageData>().set_boot_sound(-1);
ui.global::<SystemPageData>().set_screen_auto_brightness(-1);
ui.global::<SystemPageData>().set_mcu_powersave(-1);
ui.global::<SystemPageData>().set_mini_led_mode(-1); ui.global::<SystemPageData>().set_mini_led_mode(-1);
ui.global::<SystemPageData>().set_screenpad_brightness(-1); ui.global::<SystemPageData>().set_screenpad_brightness(-1);
ui.global::<SystemPageData>().set_ppt_pl1_spl(MINMAX); ui.global::<SystemPageData>().set_ppt_pl1_spl(MINMAX);
@@ -128,6 +130,51 @@ macro_rules! init_minmax_property {
// For handling callbacks from UI value changes // For handling callbacks from UI value changes
macro_rules! setup_callback { 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) => { ($property:ident, $handle:expr, $attr:expr, $type:tt) => {
let handle_copy = $handle.as_weak(); let handle_copy = $handle.as_weak();
let proxy_copy = $attr.clone(); let proxy_copy = $attr.clone();
@@ -138,12 +185,28 @@ macro_rules! setup_callback {
let handle_copy = handle_copy.clone(); let handle_copy = handle_copy.clone();
let proxy_copy = proxy_copy.clone(); let proxy_copy = proxy_copy.clone();
tokio::spawn(async move { tokio::spawn(async move {
let res = proxy_copy
.set_current_value(convert_to_dbus!($type, v))
.await;
show_toast( show_toast(
format!("{} successfully set to {}", stringify!($property), v).into(), format!("{} successfully set to {}", stringify!($property), v).into(),
format!("Setting {} failed", stringify!($property)).into(), format!("Setting {} failed", stringify!($property)).into(),
handle_copy, handle_copy.clone(),
proxy_copy.set_current_value(convert_to_dbus!($type, v)).await, res.clone(),
); );
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();
}
}
}); });
}); });
}); });
@@ -308,6 +371,7 @@ pub fn setup_system_page_callbacks(ui: &MainWindow, _states: Arc<Mutex<Config>>)
let platform_copy = platform.clone(); let platform_copy = platform.clone();
if let Ok(mut value) = platform.platform_profile_choices().await { if let Ok(mut value) = platform.platform_profile_choices().await {
debug!("Available platform profile choices: {:?}", value);
handle handle
.upgrade_in_event_loop(move |handle| { .upgrade_in_event_loop(move |handle| {
value.sort(); value.sort();
@@ -519,7 +583,7 @@ pub fn setup_system_page_callbacks(ui: &MainWindow, _states: Arc<Mutex<Config>>)
set_ui_callbacks!(handle, set_ui_callbacks!(handle,
SystemPageData(as i32), SystemPageData(as i32),
platform_copy.platform_profile_on_battery(.into()), platform_copy.platform_profile_on_battery(.into()),
"Throttle policy on abttery set to {}", "Throttle policy on battery set to {}",
"Setting Throttle policy on battery failed" "Setting Throttle policy on battery failed"
); );
set_ui_callbacks!(handle, set_ui_callbacks!(handle,
@@ -572,120 +636,132 @@ pub fn setup_system_page_callbacks(ui: &MainWindow, _states: Arc<Mutex<Config>>)
for attr in armoury_attrs { for attr in armoury_attrs {
if let Ok(value) = attr.current_value().await { if let Ok(value) = attr.current_value().await {
let name = attr.name().await.unwrap(); if let Ok(name) = attr.name().await {
debug!("Setting up {} = {value}", <&str>::from(name)); debug!("Setting up {} = {value}", <&str>::from(name));
let platform = platform.clone(); let platform = platform.clone();
handle handle
.upgrade_in_event_loop(move |handle| match name { .upgrade_in_event_loop(move |handle| match name {
FirmwareAttribute::ApuMem => {} FirmwareAttribute::ApuMem => {}
FirmwareAttribute::CoresPerformance => {} FirmwareAttribute::CoresPerformance => {}
FirmwareAttribute::CoresEfficiency => {} FirmwareAttribute::CoresEfficiency => {}
FirmwareAttribute::PptEnabled => { FirmwareAttribute::PptEnabled => {
init_property!(ppt_enabled, handle, value, bool); init_property!(ppt_enabled, handle, value, bool);
setup_callback!(ppt_enabled, handle, attr, bool); setup_callback!(ppt_enabled, handle, attr, bool);
let handle_copy = handle.as_weak(); let handle_copy = handle.as_weak();
let proxy_copy = attr.clone(); let proxy_copy = attr.clone();
tokio::spawn(async move { tokio::spawn(async move {
let mut x = proxy_copy.receive_current_value_changed().await; let mut x = proxy_copy.receive_current_value_changed().await;
use futures_util::StreamExt; use futures_util::StreamExt;
while let Some(e) = x.next().await { while let Some(e) = x.next().await {
if let Ok(out) = e.get().await { if let Ok(out) = e.get().await {
handle_copy handle_copy
.upgrade_in_event_loop(move |handle| { .upgrade_in_event_loop(move |handle| {
handle handle
.global::<SystemPageData>() .global::<SystemPageData>()
.set_enable_ppt_group(out == 1); .set_enable_ppt_group(out == 1);
handle handle
.global::<SystemPageData>() .global::<SystemPageData>()
.set_ppt_enabled(out == 1); .set_ppt_enabled(out == 1);
}) })
.ok(); .ok();
}
} }
} });
}); handle
handle .global::<SystemPageData>()
.global::<SystemPageData>() .set_ppt_enabled_available(true);
.set_ppt_enabled_available(true); handle
handle .global::<SystemPageData>()
.global::<SystemPageData>() .set_enable_ppt_group(value == 1);
.set_enable_ppt_group(value == 1); }
} FirmwareAttribute::PptPl1Spl => {
FirmwareAttribute::PptPl1Spl => { init_minmax_property!(ppt_pl1_spl, handle, attr);
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_callback_restore_default!(ppt_pl1_spl, handle, attr); setup_minmax_external!(ppt_pl1_spl, handle, attr, platform);
setup_minmax_external!(ppt_pl1_spl, handle, attr, platform); }
} FirmwareAttribute::PptPl2Sppt => {
FirmwareAttribute::PptPl2Sppt => { init_minmax_property!(ppt_pl2_sppt, handle, attr);
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_callback_restore_default!(ppt_pl2_sppt, handle, attr); setup_minmax_external!(ppt_pl2_sppt, handle, attr, platform);
setup_minmax_external!(ppt_pl2_sppt, handle, attr, platform); }
} FirmwareAttribute::PptPl3Fppt => {
FirmwareAttribute::PptPl3Fppt => { init_minmax_property!(ppt_pl3_fppt, handle, attr);
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_callback_restore_default!(ppt_pl3_fppt, handle, attr); setup_minmax_external!(ppt_pl3_fppt, handle, attr, platform);
setup_minmax_external!(ppt_pl3_fppt, handle, attr, platform); }
} FirmwareAttribute::PptFppt => {
FirmwareAttribute::PptFppt => { init_minmax_property!(ppt_fppt, handle, attr);
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_callback_restore_default!(ppt_fppt, handle, attr); setup_minmax_external!(ppt_fppt, handle, attr, platform);
setup_minmax_external!(ppt_fppt, handle, attr, platform); }
} FirmwareAttribute::PptApuSppt => {
FirmwareAttribute::PptApuSppt => { init_minmax_property!(ppt_apu_sppt, handle, attr);
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_callback_restore_default!(ppt_apu_sppt, handle, attr); setup_minmax_external!(ppt_apu_sppt, handle, attr, platform);
setup_minmax_external!(ppt_apu_sppt, handle, attr, platform); }
} FirmwareAttribute::PptPlatformSppt => {
FirmwareAttribute::PptPlatformSppt => { init_minmax_property!(ppt_platform_sppt, handle, attr);
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_callback_restore_default!(ppt_platform_sppt, handle, attr); setup_minmax_external!(ppt_platform_sppt, handle, attr, platform);
setup_minmax_external!(ppt_platform_sppt, handle, attr, platform); }
} FirmwareAttribute::NvDynamicBoost => {
FirmwareAttribute::NvDynamicBoost => { init_minmax_property!(nv_dynamic_boost, handle, attr);
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_callback_restore_default!(nv_dynamic_boost, handle, attr); setup_minmax_external!(nv_dynamic_boost, handle, attr, platform);
setup_minmax_external!(nv_dynamic_boost, handle, attr, platform); }
} FirmwareAttribute::NvTempTarget => {
FirmwareAttribute::NvTempTarget => { init_minmax_property!(nv_temp_target, handle, attr);
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_callback_restore_default!(nv_temp_target, handle, attr); setup_minmax_external!(nv_temp_target, handle, attr, platform);
setup_minmax_external!(nv_temp_target, handle, attr, platform); }
} FirmwareAttribute::DgpuBaseTgp => {}
FirmwareAttribute::DgpuBaseTgp => {} FirmwareAttribute::DgpuTgp => {}
FirmwareAttribute::DgpuTgp => {} FirmwareAttribute::ChargeMode => {}
FirmwareAttribute::ChargeMode => {} FirmwareAttribute::BootSound => {
FirmwareAttribute::BootSound => { init_property!(boot_sound, handle, value, i32);
init_property!(boot_sound, handle, value, i32); setup_callback!(boot_sound, handle, attr, i32);
setup_callback!(boot_sound, handle, attr, i32); setup_external!(boot_sound, i32, handle, attr, value)
setup_external!(boot_sound, i32, handle, attr, value) }
} FirmwareAttribute::ScreenAutoBrightness => {
FirmwareAttribute::McuPowersave => {} init_property!(screen_auto_brightness, handle, value, i32);
FirmwareAttribute::PanelOverdrive => { setup_callback!(screen_auto_brightness, handle, attr, i32);
init_property!(panel_overdrive, handle, value, i32); setup_external!(screen_auto_brightness, i32, handle, attr, value)
setup_callback!(panel_overdrive, handle, attr, i32); }
setup_external!(panel_overdrive, i32, handle, attr, value) FirmwareAttribute::McuPowersave => {
} init_property!(mcu_powersave, handle, value, i32);
FirmwareAttribute::PanelHdMode => {} setup_callback!(mcu_powersave, handle, attr, i32);
FirmwareAttribute::EgpuConnected => {} setup_external!(mcu_powersave, i32, handle, attr, value)
FirmwareAttribute::EgpuEnable => {} }
FirmwareAttribute::DgpuDisable => {} FirmwareAttribute::PanelOverdrive => {
FirmwareAttribute::GpuMuxMode => {} init_property!(panel_overdrive, handle, value, i32);
FirmwareAttribute::MiniLedMode => { setup_callback!(panel_overdrive, handle, attr, i32);
init_property!(mini_led_mode, handle, value, i32); setup_external!(panel_overdrive, i32, handle, attr, value)
setup_callback!(mini_led_mode, handle, attr, i32); }
setup_external!(mini_led_mode, i32, handle, attr, value); FirmwareAttribute::PanelHdMode => {}
} FirmwareAttribute::EgpuConnected => {}
FirmwareAttribute::PendingReboot => {} FirmwareAttribute::EgpuEnable => {}
FirmwareAttribute::None => {} FirmwareAttribute::DgpuDisable => {}
}) FirmwareAttribute::GpuMuxMode => {}
.ok(); FirmwareAttribute::MiniLedMode => {
init_property!(mini_led_mode, handle, value, i32);
setup_callback!(mini_led_mode, handle, attr, i32);
setup_external!(mini_led_mode, i32, handle, attr, value);
}
FirmwareAttribute::PendingReboot => {}
FirmwareAttribute::None => {}
})
.ok();
} else {
error!("Attribute with no name, skipping");
}
} }
} }
handle handle

View File

@@ -190,7 +190,9 @@ export component MainWindow inherits Window {
y: 0px; y: 0px;
width: root.width; width: root.width;
height: root.height; height: root.height;
padding: 10px;
//padding only has effect on layout elements
//padding: 10px;
background: Palette.background; background: Palette.background;
border-color: Palette.border; border-color: Palette.border;

View File

@@ -37,7 +37,8 @@ export component PageAnime inherits Rectangle {
property <bool> show_builtin_advanced: false; property <bool> show_builtin_advanced: false;
clip: true; clip: true;
// TODO: slow with border-radius // TODO: slow with border-radius
padding: 8px; //padding only has effect on layout elements
//padding: 8px;
// height: parent.height - infobar.height - mainview.padding - self.padding * 2; // height: parent.height - infobar.height - mainview.padding - self.padding * 2;
// TODO: border-radius: 8px; // TODO: border-radius: 8px;
VerticalLayout { VerticalLayout {

View File

@@ -16,7 +16,9 @@ export component PageAppSettings inherits VerticalLayout {
Rectangle { Rectangle {
clip: true; clip: true;
// TODO: slow with border-radius // TODO: slow with border-radius
padding: 8px; //padding only has effect on layout elements
//padding: 8px;
// height: parent.height - infobar.height - mainview.padding - self.padding * 2; // height: parent.height - infobar.height - mainview.padding - self.padding * 2;
// TODO: border-radius: 8px; // TODO: border-radius: 8px;
mainview := VerticalLayout { mainview := VerticalLayout {

View File

@@ -113,7 +113,8 @@ export component PageAura inherits Rectangle {
min-height: 80px; min-height: 80px;
max-height: 90px; max-height: 90px;
RogItem { RogItem {
padding: 0px; //padding only has effect on layout elements
//padding: 0px;
VerticalBox { VerticalBox {
Text { Text {
text: @tr("Zone"); text: @tr("Zone");
@@ -136,7 +137,8 @@ export component PageAura inherits Rectangle {
} }
RogItem { RogItem {
padding: 0px; //padding only has effect on layout elements
//padding: 0px;
VerticalBox { VerticalBox {
Text { Text {
text: @tr("Direction"); text: @tr("Direction");
@@ -158,7 +160,8 @@ export component PageAura inherits Rectangle {
} }
RogItem { RogItem {
padding: 0px; //padding only has effect on layout elements
//padding: 0px;
VerticalBox { VerticalBox {
Text { Text {
text: @tr("Speed"); text: @tr("Speed");

View File

@@ -51,6 +51,10 @@ export global SystemPageData {
callback cb_panel_overdrive(int); callback cb_panel_overdrive(int);
in-out property <int> boot_sound; in-out property <int> boot_sound;
callback cb_boot_sound(int); callback cb_boot_sound(int);
in-out property <int> screen_auto_brightness;
callback cb_screen_auto_brightness(int);
in-out property <int> mcu_powersave;
callback cb_mcu_powersave(int);
in-out property <int> mini_led_mode; in-out property <int> mini_led_mode;
callback cb_mini_led_mode(int); callback cb_mini_led_mode(int);
@@ -140,7 +144,8 @@ export component PageSystem inherits Rectangle {
property <bool> show_fade_cover: false; property <bool> show_fade_cover: false;
property <bool> show_throttle_advanced: false; property <bool> show_throttle_advanced: false;
clip: true; clip: true;
padding: 8px; //padding only has effect on layout elements
//padding: 8px;
ScrollView { ScrollView {
VerticalLayout { VerticalLayout {
padding: 10px; padding: 10px;
@@ -299,9 +304,25 @@ export component PageSystem inherits Rectangle {
SystemPageData.cb_boot_sound(SystemPageData.boot_sound) SystemPageData.cb_boot_sound(SystemPageData.boot_sound)
} }
} }
if SystemPageData.screen_auto_brightness != -1: SystemToggleInt {
text: @tr("Screen Auto Brightness");
checked_int <=> SystemPageData.screen_auto_brightness;
toggled => {
SystemPageData.cb_screen_auto_brightness(SystemPageData.screen_auto_brightness)
}
}
if SystemPageData.mcu_powersave != -1: SystemToggleInt {
text: @tr("MCU Powersave");
checked_int <=> SystemPageData.mcu_powersave;
toggled => {
SystemPageData.cb_mcu_powersave(SystemPageData.mcu_powersave)
}
}
} }
if SystemPageData.ppt_pl1_spl.current != -1 || SystemPageData.ppt_pl2_sppt.current != -1 || SystemPageData.ppt_pl3_fppt.current != -1 || SystemPageData.ppt_fppt.current != -1 || SystemPageData.ppt_apu_sppt.current != -1 || SystemPageData.nv_temp_target.current != -1 || SystemPageData.nv_dynamic_boost.current != -1: HorizontalLayout { if (SystemPageData.ppt_pl1_spl.max > 0 && SystemPageData.ppt_pl1_spl.current != -1) || (SystemPageData.ppt_pl2_sppt.max > 0 && SystemPageData.ppt_pl2_sppt.current != -1) || (SystemPageData.ppt_pl3_fppt.max > 0 && SystemPageData.ppt_pl3_fppt.current != -1) || (SystemPageData.ppt_fppt.max > 0 && SystemPageData.ppt_fppt.current != -1) || (SystemPageData.ppt_apu_sppt.max > 0 && SystemPageData.ppt_apu_sppt.current != -1) || (SystemPageData.nv_temp_target.max > 0 && SystemPageData.nv_temp_target.current != -1) || (SystemPageData.nv_dynamic_boost.max > 0 && SystemPageData.nv_dynamic_boost.current != -1): HorizontalLayout {
padding-right: 10px; padding-right: 10px;
padding-left: 10px; padding-left: 10px;
alignment: LayoutAlignment.space-between; alignment: LayoutAlignment.space-between;
@@ -330,7 +351,7 @@ export component PageSystem inherits Rectangle {
} }
} }
if SystemPageData.ppt_pl1_spl.current != -1: SystemSlider { if SystemPageData.ppt_pl1_spl.max > 0 && SystemPageData.ppt_pl1_spl.current != -1: SystemSlider {
text: @tr("ppt_pl1_spl" => "CPU Sustained Power Limit"); text: @tr("ppt_pl1_spl" => "CPU Sustained Power Limit");
title: @tr("ppt_pl1_spl" => "CPU Sustained Power Limit"); title: @tr("ppt_pl1_spl" => "CPU Sustained Power Limit");
help_text: @tr("ppt_pl1_spl_help" => "Long-term CPU power limit that affects sustained workload performance. Higher values may increase heat and power consumption."); help_text: @tr("ppt_pl1_spl_help" => "Long-term CPU power limit that affects sustained workload performance. Higher values may increase heat and power consumption.");
@@ -348,7 +369,7 @@ export component PageSystem inherits Rectangle {
} }
} }
if SystemPageData.ppt_pl2_sppt.current != -1: SystemSlider { if SystemPageData.ppt_pl2_sppt.max > 0 && SystemPageData.ppt_pl2_sppt.current != -1: SystemSlider {
text: @tr("ppt_pl2_sppt" => "CPU Turbo Power Limit"); text: @tr("ppt_pl2_sppt" => "CPU Turbo Power Limit");
title: @tr("ppt_pl2_sppt" => "CPU Turbo Power Limit"); title: @tr("ppt_pl2_sppt" => "CPU Turbo Power Limit");
help_text: @tr("ppt_pl2_sppt_help" => "Short-term CPU power limit for boost periods. Controls maximum power during brief high-performance bursts."); help_text: @tr("ppt_pl2_sppt_help" => "Short-term CPU power limit for boost periods. Controls maximum power during brief high-performance bursts.");
@@ -366,7 +387,7 @@ export component PageSystem inherits Rectangle {
} }
} }
if SystemPageData.ppt_pl3_fppt.current != -1: SystemSlider { if SystemPageData.ppt_pl3_fppt.max > 0 && SystemPageData.ppt_pl3_fppt.current != -1: SystemSlider {
text: @tr("ppt_pl3_fppt" => "CPU Fast Burst Power Limit"); text: @tr("ppt_pl3_fppt" => "CPU Fast Burst Power Limit");
title: @tr("ppt_pl3_fppt" => "CPU Fast Burst Power Limit"); title: @tr("ppt_pl3_fppt" => "CPU Fast Burst Power Limit");
help_text: @tr("ppt_pl3_fppt_help" => "Ultra-short duration power limit for instantaneous CPU bursts. Affects responsiveness during sudden workload spikes."); help_text: @tr("ppt_pl3_fppt_help" => "Ultra-short duration power limit for instantaneous CPU bursts. Affects responsiveness during sudden workload spikes.");
@@ -383,7 +404,7 @@ export component PageSystem inherits Rectangle {
SystemPageData.cb_ppt_pl3_fppt(Math.round(value)); SystemPageData.cb_ppt_pl3_fppt(Math.round(value));
} }
} }
if SystemPageData.ppt_fppt.current != -1: SystemSlider { if SystemPageData.ppt_fppt.max > 0 && SystemPageData.ppt_fppt.current != -1: SystemSlider {
text: @tr("ppt_fppt" => "Fast Package Power Limit"); text: @tr("ppt_fppt" => "Fast Package Power Limit");
title: @tr("ppt_fppt" => "Fast Package Power Limit"); title: @tr("ppt_fppt" => "Fast Package Power Limit");
help_text: @tr("ppt_fppt_help" => "Ultra-short duration power limit for system package. Controls maximum power during millisecond-scale load spikes."); help_text: @tr("ppt_fppt_help" => "Ultra-short duration power limit for system package. Controls maximum power during millisecond-scale load spikes.");
@@ -401,7 +422,7 @@ export component PageSystem inherits Rectangle {
} }
} }
if SystemPageData.ppt_apu_sppt.current != -1: SystemSlider { if SystemPageData.ppt_apu_sppt.max > 0 && SystemPageData.ppt_apu_sppt.current != -1: SystemSlider {
text: @tr("ppt_apu_sppt" => "APU Sustained Power Limit"); text: @tr("ppt_apu_sppt" => "APU Sustained Power Limit");
title: @tr("ppt_apu_sppt" => "APU Sustained Power Limit"); title: @tr("ppt_apu_sppt" => "APU Sustained Power Limit");
help_text: @tr("ppt_apu_sppt_help" => "Long-term power limit for integrated graphics and CPU combined. Affects sustained performance of APU-based workloads."); help_text: @tr("ppt_apu_sppt_help" => "Long-term power limit for integrated graphics and CPU combined. Affects sustained performance of APU-based workloads.");
@@ -419,7 +440,7 @@ export component PageSystem inherits Rectangle {
} }
} }
if SystemPageData.ppt_platform_sppt.current != -1: SystemSlider { if SystemPageData.ppt_platform_sppt.max > 0 && SystemPageData.ppt_platform_sppt.current != -1: SystemSlider {
text: @tr("ppt_platform_sppt" => "Platform Sustained Power Limit"); text: @tr("ppt_platform_sppt" => "Platform Sustained Power Limit");
title: @tr("ppt_platform_sppt" => "Platform Sustained Power Limit"); title: @tr("ppt_platform_sppt" => "Platform Sustained Power Limit");
help_text: @tr("ppt_platform_sppt_help" => "Overall system power limit for sustained operations. Controls total platform power consumption over extended periods."); help_text: @tr("ppt_platform_sppt_help" => "Overall system power limit for sustained operations. Controls total platform power consumption over extended periods.");
@@ -437,7 +458,7 @@ export component PageSystem inherits Rectangle {
} }
} }
if SystemPageData.nv_dynamic_boost.current != -1: SystemSlider { if SystemPageData.nv_dynamic_boost.max > 0 && SystemPageData.nv_dynamic_boost.current != -1: SystemSlider {
text: @tr("nv_dynamic_boost" => "GPU Power Boost"); text: @tr("nv_dynamic_boost" => "GPU Power Boost");
title: @tr("nv_dynamic_boost" => "GPU Power Boost"); title: @tr("nv_dynamic_boost" => "GPU Power Boost");
help_text: @tr("nv_dynamic_boost_help" => "Additional power allocation for GPU dynamic boost. Higher values increase GPU performance but generate more heat."); help_text: @tr("nv_dynamic_boost_help" => "Additional power allocation for GPU dynamic boost. Higher values increase GPU performance but generate more heat.");
@@ -455,7 +476,7 @@ export component PageSystem inherits Rectangle {
} }
} }
if SystemPageData.nv_temp_target.current != -1: SystemSlider { if SystemPageData.nv_temp_target.max > 0 && SystemPageData.nv_temp_target.current != -1: SystemSlider {
text: @tr("nv_temp_target" => "GPU Temperature Limit"); text: @tr("nv_temp_target" => "GPU Temperature Limit");
title: @tr("nv_temp_target" => "GPU Temperature Limit"); title: @tr("nv_temp_target" => "GPU Temperature Limit");
help_text: @tr("nv_temp_target_help" => "Maximum GPU temperature threshold in Celsius. GPU will throttle to maintain temperature below this limit."); help_text: @tr("nv_temp_target_help" => "Maximum GPU temperature threshold in Celsius. GPU will throttle to maintain temperature below this limit.");

View File

@@ -4,7 +4,8 @@
import { Palette, HorizontalBox, VerticalBox } from "std-widgets.slint"; import { Palette, HorizontalBox, VerticalBox } from "std-widgets.slint";
component SideBarItem inherits Rectangle { component SideBarItem inherits Rectangle {
padding: 10px; // padding only has effect on layout elements
// padding: 10px;
in property <bool> selected; in property <bool> selected;
in property <bool> has-focus; in property <bool> has-focus;
in-out property <string> text <=> label.text; in-out property <string> text <=> label.text;

View File

@@ -95,7 +95,7 @@ impl Attribute {
_ => return Err(PlatformError::InvalidValue), _ => return Err(PlatformError::InvalidValue),
}; };
let mut file = OpenOptions::new().write(true).open(&path)?; let mut file = OpenOptions::new().write(true).truncate(true).open(&path)?;
file.write_all(value_str.as_bytes())?; file.write_all(value_str.as_bytes())?;
Ok(()) Ok(())
} }
@@ -124,6 +124,22 @@ impl Attribute {
&self.scalar_increment &self.scalar_increment
} }
fn read_attr_i32(&self, name: &str) -> Option<i32> {
read_i32(&self.base_path.join(name)).ok()
}
pub fn refresh_min_value(&self) -> Option<i32> {
self.read_attr_i32("min_value")
}
pub fn refresh_max_value(&self) -> Option<i32> {
self.read_attr_i32("max_value")
}
pub fn refresh_scalar_increment(&self) -> Option<i32> {
self.read_attr_i32("scalar_increment")
}
/// Read all the immutable values to struct data. These should *never* /// Read all the immutable values to struct data. These should *never*
/// change, if they do then it is possibly a driver issue - although this is /// change, if they do then it is possibly a driver issue - although this is
/// subject to `firmware_attributes` class changes in kernel. /// subject to `firmware_attributes` class changes in kernel.
@@ -228,6 +244,37 @@ impl FirmwareAttributes {
Self { attrs } 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> { pub fn attributes(&self) -> &Vec<Attribute> {
&self.attrs &self.attrs
} }
@@ -256,15 +303,35 @@ macro_rules! define_attribute_getters {
} }
define_attribute_getters!( define_attribute_getters!(
apu_mem, cores_performance, cores_efficiency, ppt_pl1_spl, ppt_pl2_sppt, ppt_apu_sppt, apu_mem,
ppt_platform_sppt, ppt_fppt, nv_dynamic_boost, nv_temp_target, dgpu_base_tgp, dgpu_tgp, cores_performance,
charge_mode, boot_sound, mcu_powersave, panel_od, panel_hd_mode, egpu_connected, egpu_enable, cores_efficiency,
dgpu_disable, gpu_mux_mode, mini_led_mode ppt_pl1_spl,
ppt_pl2_sppt,
ppt_apu_sppt,
ppt_platform_sppt,
ppt_fppt,
nv_dynamic_boost,
nv_temp_target,
nv_base_tgp,
nv_tgp,
charge_mode,
boot_sound,
mcu_powersave,
panel_od,
panel_hd_mode,
egpu_connected,
egpu_enable,
dgpu_disable,
gpu_mux_mode,
mini_led_mode,
screen_auto_brightness
); );
/// CamelCase names of the properties. Intended for use with DBUS /// CamelCase names of the properties. Intended for use with DBUS
#[repr(u8)] #[repr(u8)]
#[derive( #[derive(
Debug,
Clone, Clone,
Copy, Copy,
Serialize, Serialize,
@@ -306,6 +373,7 @@ pub enum FirmwareAttribute {
PendingReboot = 23, PendingReboot = 23,
PptEnabled = 24, PptEnabled = 24,
None = 25, None = 25,
ScreenAutoBrightness = 26,
} }
impl FirmwareAttribute { impl FirmwareAttribute {
@@ -326,6 +394,7 @@ impl FirmwareAttribute {
self, self,
FirmwareAttribute::NvDynamicBoost FirmwareAttribute::NvDynamicBoost
| FirmwareAttribute::NvTempTarget | FirmwareAttribute::NvTempTarget
| FirmwareAttribute::DgpuBaseTgp
| FirmwareAttribute::DgpuTgp | FirmwareAttribute::DgpuTgp
) )
} }
@@ -346,8 +415,11 @@ impl From<&str> for FirmwareAttribute {
"ppt_platform_sppt" => Self::PptPlatformSppt, "ppt_platform_sppt" => Self::PptPlatformSppt,
"nv_dynamic_boost" => Self::NvDynamicBoost, "nv_dynamic_boost" => Self::NvDynamicBoost,
"nv_temp_target" => Self::NvTempTarget, "nv_temp_target" => Self::NvTempTarget,
"nv_tgp" => Self::DgpuTgp,
"nv_base_tgp" => Self::DgpuBaseTgp, "nv_base_tgp" => Self::DgpuBaseTgp,
/* Backwards compatibility: some kernels expose these attributes with a dgpu_* prefix */
"dgpu_tgp" => Self::DgpuTgp, "dgpu_tgp" => Self::DgpuTgp,
"dgpu_base_tgp" => Self::DgpuBaseTgp,
"charge_mode" => Self::ChargeMode, "charge_mode" => Self::ChargeMode,
"boot_sound" => Self::BootSound, "boot_sound" => Self::BootSound,
"mcu_powersave" => Self::McuPowersave, "mcu_powersave" => Self::McuPowersave,
@@ -359,6 +431,7 @@ impl From<&str> for FirmwareAttribute {
"gpu_mux_mode" => Self::GpuMuxMode, "gpu_mux_mode" => Self::GpuMuxMode,
"mini_led_mode" => Self::MiniLedMode, "mini_led_mode" => Self::MiniLedMode,
"pending_reboot" => Self::PendingReboot, "pending_reboot" => Self::PendingReboot,
"screen_auto_brightness" => Self::ScreenAutoBrightness,
_ => { _ => {
error!("Invalid firmware attribute: {}", s); error!("Invalid firmware attribute: {}", s);
Self::None Self::None
@@ -382,8 +455,8 @@ impl From<FirmwareAttribute> for &str {
FirmwareAttribute::PptPlatformSppt => "ppt_platform_sppt", FirmwareAttribute::PptPlatformSppt => "ppt_platform_sppt",
FirmwareAttribute::NvDynamicBoost => "nv_dynamic_boost", FirmwareAttribute::NvDynamicBoost => "nv_dynamic_boost",
FirmwareAttribute::NvTempTarget => "nv_temp_target", FirmwareAttribute::NvTempTarget => "nv_temp_target",
FirmwareAttribute::DgpuBaseTgp => "dgpu_base_tgp", FirmwareAttribute::DgpuBaseTgp => "nv_base_tgp",
FirmwareAttribute::DgpuTgp => "dgpu_tgp", FirmwareAttribute::DgpuTgp => "nv_tgp",
FirmwareAttribute::ChargeMode => "charge_mode", FirmwareAttribute::ChargeMode => "charge_mode",
FirmwareAttribute::BootSound => "boot_sound", FirmwareAttribute::BootSound => "boot_sound",
FirmwareAttribute::McuPowersave => "mcu_powersave", FirmwareAttribute::McuPowersave => "mcu_powersave",
@@ -395,6 +468,7 @@ impl From<FirmwareAttribute> for &str {
FirmwareAttribute::GpuMuxMode => "gpu_mux_mode", FirmwareAttribute::GpuMuxMode => "gpu_mux_mode",
FirmwareAttribute::MiniLedMode => "mini_led_mode", FirmwareAttribute::MiniLedMode => "mini_led_mode",
FirmwareAttribute::PendingReboot => "pending_reboot", FirmwareAttribute::PendingReboot => "pending_reboot",
FirmwareAttribute::ScreenAutoBrightness => "screen_auto_brightness",
FirmwareAttribute::None => "none", FirmwareAttribute::None => "none",
} }
} }
@@ -481,3 +555,28 @@ mod tests {
attr.set_current_value(&val).unwrap(); 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

@@ -275,11 +275,16 @@ impl std::str::FromStr for PlatformProfile {
type Err = PlatformError; type Err = PlatformError;
fn from_str(profile: &str) -> Result<Self> { fn from_str(profile: &str) -> Result<Self> {
match profile.to_ascii_lowercase().trim() { match profile
.to_ascii_lowercase()
.trim()
.replace(|c| !char::is_alphabetic(c), "")
.as_str()
{
"balanced" => Ok(PlatformProfile::Balanced), "balanced" => Ok(PlatformProfile::Balanced),
"performance" => Ok(PlatformProfile::Performance), "performance" => Ok(PlatformProfile::Performance),
"quiet" => Ok(PlatformProfile::Quiet), "quiet" => Ok(PlatformProfile::Quiet),
"low-power" => Ok(PlatformProfile::LowPower), "lowpower" => Ok(PlatformProfile::LowPower),
"custom" => Ok(PlatformProfile::Custom), "custom" => Ok(PlatformProfile::Custom),
_ => Err(PlatformError::NotSupported), _ => Err(PlatformError::NotSupported),
} }
@@ -288,11 +293,16 @@ impl std::str::FromStr for PlatformProfile {
impl From<&str> for PlatformProfile { impl From<&str> for PlatformProfile {
fn from(profile: &str) -> Self { fn from(profile: &str) -> Self {
match profile.to_ascii_lowercase().trim() { match profile
.to_ascii_lowercase()
.trim()
.replace(|c| !char::is_alphabetic(c), "")
.as_str()
{
"balanced" => PlatformProfile::Balanced, "balanced" => PlatformProfile::Balanced,
"performance" => PlatformProfile::Performance, "performance" => PlatformProfile::Performance,
"quiet" => PlatformProfile::Quiet, "quiet" => PlatformProfile::Quiet,
"low-power" => PlatformProfile::LowPower, "lowpower" => PlatformProfile::LowPower,
"custom" => PlatformProfile::Custom, "custom" => PlatformProfile::Custom,
_ => { _ => {
warn!("{profile} is unknown, using ThrottlePolicy::Balanced"); warn!("{profile} is unknown, using ThrottlePolicy::Balanced");

View File

@@ -106,3 +106,13 @@ impl AsusPower {
)) ))
} }
} }
impl Default for AsusPower {
fn default() -> Self {
Self {
mains: PathBuf::from("/this_shouldNeVErr_exisid"),
battery: PathBuf::from("/this_shouldNeVErr_exisid"),
usb: None,
}
}
}

View File

@@ -37,8 +37,9 @@ pub fn find_fan_curve_node() -> Result<Device, ProfileError> {
derive(Type, Value, OwnedValue), derive(Type, Value, OwnedValue),
zvariant(signature = "s") zvariant(signature = "s")
)] )]
#[derive(Deserialize, Serialize, Debug, Hash, PartialEq, Eq, Clone, Copy)] #[derive(Default, Deserialize, Serialize, Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum FanCurvePU { pub enum FanCurvePU {
#[default]
CPU = 0, CPU = 0,
GPU = 1, GPU = 1,
MID = 2, MID = 2,
@@ -100,12 +101,6 @@ impl std::str::FromStr for FanCurvePU {
} }
} }
impl Default for FanCurvePU {
fn default() -> Self {
Self::CPU
}
}
/// Main purpose of `FanCurves` is to enable restoring state on system boot /// Main purpose of `FanCurves` is to enable restoring state on system boot
#[cfg_attr(feature = "dbus", derive(Type))] #[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Deserialize, Serialize, Debug, Default)] #[derive(Deserialize, Serialize, Debug, Default)]

4
rust-toolchain.toml Normal file
View File

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

View File

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