mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-01-22 17:33:19 +01:00
Compare commits
15 Commits
31939f41ba
...
devel-dyna
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e5cebc781 | ||
|
|
def691f9d0 | ||
|
|
80eeafd9e1 | ||
|
|
bf173300e0 | ||
|
|
07874d7452 | ||
|
|
894a0d2b11 | ||
|
|
11dc02612e | ||
|
|
fccb233e9e | ||
|
|
c13612e02c | ||
|
|
80147966a9 | ||
|
|
a1815ac40c | ||
|
|
ad051bd7b8 | ||
|
|
90676b390e | ||
|
|
974f2acafa | ||
|
|
6ae3ae5284 |
@@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Make the boot process more reliable
|
||||||
|
- tie nv_ properties to power profiles
|
||||||
|
- Better support nv_tgp
|
||||||
|
|
||||||
## [v6.1.17]
|
## [v6.1.17]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -185,6 +185,7 @@ dependencies = [
|
|||||||
"rog_scsi",
|
"rog_scsi",
|
||||||
"rog_slash",
|
"rog_slash",
|
||||||
"serde",
|
"serde",
|
||||||
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
"udev 0.8.0",
|
"udev 0.8.0",
|
||||||
"zbus",
|
"zbus",
|
||||||
|
|||||||
@@ -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")]
|
||||||
|
|||||||
@@ -1184,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().is_multiple_of(2) || 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:"
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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"]
|
||||||
|
|||||||
@@ -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,
|
||||||
@@ -146,6 +168,14 @@ impl ArmouryAttributeRegistry {
|
|||||||
self.attrs.push(attr);
|
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> {
|
pub async fn emit_limits(&self, connection: &Connection) -> Result<(), RogError> {
|
||||||
let mut last_err: Option<RogError> = None;
|
let mut last_err: Option<RogError> = None;
|
||||||
for attr in &self.attrs {
|
for attr in &self.attrs {
|
||||||
@@ -170,7 +200,7 @@ impl crate::Reloadable for AsusArmouryAttribute {
|
|||||||
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
|
||||||
@@ -196,14 +226,24 @@ impl crate::Reloadable for AsusArmouryAttribute {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(tune) = apply_value {
|
if let Some(tune) = apply_value {
|
||||||
self.attr
|
// Don't attempt writes for attributes that report min==0 and max==0
|
||||||
.set_current_value(&AttrValue::Integer(tune))
|
// (commonly means not supported in this power mode/device); skip
|
||||||
.map_err(|e| {
|
// applying stored tune in that case.
|
||||||
error!("Could not set {} value: {e:?}", self.attr.name());
|
if self.attr.base_path_exists() && !attr_unsupported(&self.attr) {
|
||||||
self.attr.base_path_exists();
|
self.attr
|
||||||
e
|
.set_current_value(&AttrValue::Integer(tune))
|
||||||
})?;
|
.map_err(|e| {
|
||||||
info!("Set {} to {:?}", self.attr.name(), tune);
|
error!("Could not set {} value: {e:?}", self.attr.name());
|
||||||
|
self.attr.base_path_exists();
|
||||||
|
e
|
||||||
|
})?;
|
||||||
|
info!("Set {} to {:?}", self.attr.name(), tune);
|
||||||
|
} else {
|
||||||
|
debug!(
|
||||||
|
"Skipping apply for {} because attribute missing or unsupported (min==max)",
|
||||||
|
self.attr.name()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Handle non-PPT attributes (boolean and other settings)
|
// Handle non-PPT attributes (boolean and other settings)
|
||||||
@@ -277,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
|
||||||
@@ -336,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
|
||||||
@@ -370,9 +410,9 @@ impl AsusArmouryAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn stored_value_for_power(&self, on_ac: bool) -> fdo::Result<i32> {
|
async fn stored_value_for_power(&self, on_ac: bool) -> fdo::Result<i32> {
|
||||||
if !self.name().is_ppt() {
|
if !(self.name().is_ppt() || self.name().is_dgpu()) {
|
||||||
return Err(fdo::Error::NotSupported(
|
return Err(fdo::Error::NotSupported(
|
||||||
"Stored values are only available for PPT attributes".to_string(),
|
"Stored values are only available for PPT/dGPU tunable attributes".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,9 +433,10 @@ impl AsusArmouryAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn set_value_for_power(&mut self, on_ac: bool, value: i32) -> fdo::Result<()> {
|
async fn set_value_for_power(&mut self, on_ac: bool, value: i32) -> fdo::Result<()> {
|
||||||
if !self.name().is_ppt() {
|
if !(self.name().is_ppt() || self.name().is_dgpu()) {
|
||||||
return Err(fdo::Error::NotSupported(
|
return Err(fdo::Error::NotSupported(
|
||||||
"Setting stored values is only supported for PPT attributes".to_string(),
|
"Setting stored values is only supported for PPT/dGPU tunable attributes"
|
||||||
|
.to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,13 +474,23 @@ impl AsusArmouryAttribute {
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
!= 0;
|
!= 0;
|
||||||
|
|
||||||
if power_plugged == on_ac {
|
// 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(value))
|
// applying stored value in that case.
|
||||||
.map_err(|e| {
|
if self.attr.base_path_exists() && !attr_unsupported(&self.attr) {
|
||||||
error!("Could not set value: {e:?}");
|
if power_plugged == on_ac {
|
||||||
e
|
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()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,7 +499,7 @@ impl AsusArmouryAttribute {
|
|||||||
|
|
||||||
#[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
|
||||||
@@ -469,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
|
||||||
@@ -515,12 +576,16 @@ 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>>,
|
||||||
|
enable_zbus: bool,
|
||||||
|
profile_override: Option<rog_platform::platform::PlatformProfile>,
|
||||||
|
power_plugged_override: Option<bool>,
|
||||||
) -> Result<ArmouryAttributeRegistry, RogError> {
|
) -> Result<ArmouryAttributeRegistry, RogError> {
|
||||||
let mut registry = ArmouryAttributeRegistry::default();
|
let mut registry = ArmouryAttributeRegistry::default();
|
||||||
for attr in attributes.attributes() {
|
for attr in attributes.attributes() {
|
||||||
@@ -533,35 +598,115 @@ pub async fn start_attributes_zbus(
|
|||||||
|
|
||||||
let registry_attr = attr.clone();
|
let registry_attr = attr.clone();
|
||||||
|
|
||||||
if let Err(e) = attr.reload().await {
|
// Only perform the full reload (which may query the platform/power sysfs)
|
||||||
error!(
|
// when zbus is enabled. Tests using the no-zbus mode skip the reload and
|
||||||
"Skipping attribute '{}' due to reload error: {e:?}",
|
// emulate the reload/apply behavior to avoid depending on udev/sysfs.
|
||||||
attr.attr.name()
|
if enable_zbus {
|
||||||
);
|
if let Err(e) = attr.reload().await {
|
||||||
// continue with others
|
error!(
|
||||||
continue;
|
"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 let Err(e) = attr.move_to_zbus(conn).await {
|
// If zbus is enabled and a connection is provided, create the SignalEmitter,
|
||||||
error!("Failed to register attribute '{attr_name}' on zbus: {e:?}");
|
// start watchers and register the object on zbus. Tests can call this function
|
||||||
continue;
|
// 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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
registry.push(registry_attr);
|
registry.push(registry_attr);
|
||||||
@@ -577,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 {
|
||||||
@@ -624,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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -96,14 +96,17 @@ pub struct AsusDevice {
|
|||||||
hid_key: Option<String>,
|
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: Arc<Mutex<HashMap<String, Arc<Mutex<HidRaw>>>>>,
|
_hid_handles: HidHandleMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceManager {
|
impl DeviceManager {
|
||||||
async fn get_or_create_hid_handle(
|
async fn get_or_create_hid_handle(
|
||||||
handles: &Arc<Mutex<HashMap<String, Arc<Mutex<HidRaw>>>>>,
|
handles: &HidHandleMap,
|
||||||
endpoint: &Device,
|
endpoint: &Device,
|
||||||
) -> Result<(Arc<Mutex<HidRaw>>, String), RogError> {
|
) -> Result<(Arc<Mutex<HidRaw>>, String), RogError> {
|
||||||
let dev_node = endpoint
|
let dev_node = endpoint
|
||||||
@@ -124,7 +127,7 @@ impl DeviceManager {
|
|||||||
async fn init_hid_devices(
|
async fn init_hid_devices(
|
||||||
connection: &Connection,
|
connection: &Connection,
|
||||||
device: Device,
|
device: Device,
|
||||||
handles: Arc<Mutex<HashMap<String, Arc<Mutex<HidRaw>>>>>,
|
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")? {
|
||||||
@@ -212,7 +215,7 @@ impl DeviceManager {
|
|||||||
/// To be called on daemon startup
|
/// To be called on daemon startup
|
||||||
async fn init_all_hid(
|
async fn init_all_hid(
|
||||||
connection: &Connection,
|
connection: &Connection,
|
||||||
handles: Arc<Mutex<HashMap<String, Arc<Mutex<HidRaw>>>>>,
|
handles: HidHandleMap,
|
||||||
) -> Result<Vec<AsusDevice>, RogError> {
|
) -> 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();
|
||||||
|
|||||||
@@ -225,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,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ pub struct CtrlPlatform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CtrlPlatform {
|
impl CtrlPlatform {
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
platform: RogPlatform,
|
platform: RogPlatform,
|
||||||
power: AsusPower,
|
power: AsusPower,
|
||||||
@@ -565,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
|
||||||
|
|||||||
@@ -77,11 +77,14 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
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();
|
||||||
let armoury_registry = match 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
|
||||||
{
|
{
|
||||||
|
|||||||
68
asusd/tests/attr_unsupported_min_eq_max.rs
Normal file
68
asusd/tests/attr_unsupported_min_eq_max.rs
Normal 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"
|
||||||
|
);
|
||||||
|
}
|
||||||
173
asusd/tests/sysfs_full_service.rs
Normal file
173
asusd/tests/sysfs_full_service.rs
Normal 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");
|
||||||
|
});
|
||||||
|
}
|
||||||
97
asusd/tests/sysfs_integration.rs
Normal file
97
asusd/tests/sysfs_integration.rs
Normal 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");
|
||||||
|
}
|
||||||
144
asusd/tests/sysfs_nv_and_ppt_acdc.rs
Normal file
144
asusd/tests/sysfs_nv_and_ppt_acdc.rs
Normal 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"
|
||||||
|
);
|
||||||
|
}
|
||||||
71
asusd/tests/sysfs_nv_tgp_acdc.rs
Normal file
71
asusd/tests/sysfs_nv_tgp_acdc.rs
Normal 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");
|
||||||
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -130,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();
|
||||||
@@ -140,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();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -615,49 +676,49 @@ pub fn setup_system_page_callbacks(ui: &MainWindow, _states: Arc<Mutex<Config>>)
|
|||||||
}
|
}
|
||||||
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);
|
setup_callback!(ppt_pl1_spl, handle, attr, i32, minmax);
|
||||||
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);
|
setup_callback!(ppt_pl2_sppt, handle, attr, i32, minmax);
|
||||||
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);
|
setup_callback!(ppt_pl3_fppt, handle, attr, i32, minmax);
|
||||||
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);
|
setup_callback!(ppt_fppt, handle, attr, i32, minmax);
|
||||||
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);
|
setup_callback!(ppt_apu_sppt, handle, attr, i32, minmax);
|
||||||
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);
|
setup_callback!(ppt_platform_sppt, handle, attr, i32, minmax);
|
||||||
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);
|
setup_callback!(nv_dynamic_boost, handle, attr, i32, minmax);
|
||||||
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);
|
setup_callback!(nv_temp_target, handle, attr, i32, minmax);
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
@@ -244,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
|
||||||
}
|
}
|
||||||
@@ -282,8 +313,8 @@ define_attribute_getters!(
|
|||||||
ppt_fppt,
|
ppt_fppt,
|
||||||
nv_dynamic_boost,
|
nv_dynamic_boost,
|
||||||
nv_temp_target,
|
nv_temp_target,
|
||||||
dgpu_base_tgp,
|
nv_base_tgp,
|
||||||
dgpu_tgp,
|
nv_tgp,
|
||||||
charge_mode,
|
charge_mode,
|
||||||
boot_sound,
|
boot_sound,
|
||||||
mcu_powersave,
|
mcu_powersave,
|
||||||
@@ -300,6 +331,7 @@ define_attribute_getters!(
|
|||||||
/// 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,
|
||||||
@@ -362,6 +394,7 @@ impl FirmwareAttribute {
|
|||||||
self,
|
self,
|
||||||
FirmwareAttribute::NvDynamicBoost
|
FirmwareAttribute::NvDynamicBoost
|
||||||
| FirmwareAttribute::NvTempTarget
|
| FirmwareAttribute::NvTempTarget
|
||||||
|
| FirmwareAttribute::DgpuBaseTgp
|
||||||
| FirmwareAttribute::DgpuTgp
|
| FirmwareAttribute::DgpuTgp
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -382,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,
|
||||||
@@ -419,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",
|
||||||
@@ -519,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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
4
rust-toolchain.toml
Normal file
4
rust-toolchain.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "stable"
|
||||||
|
components = ["rustfmt", "clippy"]
|
||||||
|
profile = "minimal"
|
||||||
@@ -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))
|
||||||
|
|||||||
Reference in New Issue
Block a user