Compare commits

..

1 Commits
4.5.8 ... 4.5.0

Author SHA1 Message Date
Luke D. Jones
2cd1ee02ee Prep release 4.5.0 2022-11-07 21:35:42 +13:00
113 changed files with 1568 additions and 3701 deletions

1
.gitignore vendored
View File

@@ -2,7 +2,6 @@
vendor.tar.xz
cargo-config
.idea
vendor
vendor-*
vendor_*
.vscode-ctags

View File

@@ -1,63 +1,29 @@
image: rust:latest
cache:
key: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
paths:
# Don't include `incremental` to save space
# Debug
- target/debug/build/
- target/debug/deps/
- target/debug/.fingerprint/
- target/debug/.cargo-lock
- target/debug/df_storyteller
# Release
- target/release/build/
- target/release/deps/
- target/release/.fingerprint/
- target/release/.cargo-lock
before_script:
- apt-get update -qq && apt-get install -y -qq libudev-dev libgtk-3-dev grep llvm clang libclang-dev
- apt-get update -qq && apt-get install -y -qq libdbus-1-dev libclang-dev libudev-dev libfontconfig1-dev
stages:
- format
- check
- test
- release
- build
format:
except:
- tags
script:
- echo "nightly" > rust-toolchain
- rustup component add rustfmt
- cargo fmt --check
check:
except:
- tags
test:
script:
- rustup component add clippy
- cargo check
# deny currently catches too much
#- cargo install cargo-deny && cargo deny
- cargo install cargo-cranky && cargo cranky
test:
except:
- tags
script:
- cargo clippy
- cargo test
release:
build:
only:
- tags
- main
script:
- make && make vendor
artifacts:
paths:
- vendor_asusctl_*.tar.xz
- vendor_asus-nb-ctrl_*.tar.xz
- cargo-config
variables:
GIT_SUBMODULE_STRATEGY: normal

View File

@@ -4,66 +4,7 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [v4.5.8]
### Changed
- Fix incorrect stop/start order of nvidia-powerd on AC plug/unplug
## [v4.5.7]
### Changed
- ROGCC: Don't notify user if changing to same mux mode
- asusd: don't block on systemd-unit change: removes all shoddy external command calls in favour of async dbus calls
## [v4.5.6]
### Changed
- Fix tasks not always running correctly on boot/sleep/wake/shutdown by finishing the move to async
- Change how the profile/fan change task monitors changes due to TUF laptops behaving slightly different
- ROGCC: Better handle the use of GPU MUX without supergfxd
- ROGCC: Track if reboot required when not using supergfxd
- Add env var for logging levels to daemon and gui (`RUST_LOG=<error|warn|info|debug|trace>`)
- ROGCC: Very basic support for running a command on AC/Battery switching, this is in config at `~/.config/rog/rog-control-center.cfg`, and for now must be edited by hand and ROGCC restarted (run ROGCC in BG to use effectively)
+ Run ROGCC from terminal to see errors of the AC/Battery command
+ Support for editing via ROGCC GUI will come in future
+ This is ideal for userspace tasks
- asusd: Very basic support for running a command on AC/Battery switching, this is in config at `/etc/asusd/asusd.conf`. A restart of asusd is not required if edited.
+ This is ideal for tasks that require root access (BE SAFE!)
- The above AC/Battery commands are probably best set to run a script for more complex tasks
- asusd: check if nvidia-powerd enabled before toggling
## [v4.5.5]
### Changed
- remove an unwrap() causing panic on main ROGCC thread
## [v4.5.4]
### Changed
- ROGCC:: Allow ROGCC to run without supergfxd
- ROGCC: Tray/notifs now reads dGPU status directly via supergfx crate (supergfxd not required)
- Add rust-toolchain to force minimum rust version
## [v4.5.3]
### Changed
- Adjust how fan graph in ROGCC works, deny incorrect graphs
- Fix to apply the fan curve change in ROGCC to the correct profile
- Support for G713RS LED modes (Author: Peter Ivanov)
- Support for G713RM LED modes (Author: maxbachmann)
- Fix VivoBook detection
- Update dependencies to get latest winit crate (fixes various small issues)
## [v4.5.2]
### Changed
- Update dependencies and bump version
## [v4.5.1]
### Added
- Support for FA506IE LED modes (Author: Herohtar)
### Changed
- Add a basic system tray with dGPU status and gpu mode switch actions
- Fixup some notifications in ROGCC
- Add config options for notifications for ROGCC
- Share states with tray process in ROGCC
- Share tates with tray process in ROGCC
## [v4.5.0]
## [Unreleased - 4.5.0]
### Added
- intofy watches on:
- `charge_control_end_threshold`

1378
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,17 +2,20 @@
members = ["asusctl", "daemon", "daemon-user", "rog-platform", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "rog-control-center"]
[workspace.package]
version = "4.5.8"
version = "4.5.0"
[workspace.dependencies]
async-trait = "^0.1"
tokio = { version = "^1.22.0", features = ["macros", "rt-multi-thread", "time", "sync"]}
tokio = { version = "^1.21.2", features = ["macros", "rt-multi-thread", "time"]}
concat-idents = "^1.1"
dirs = "^4.0"
smol = "^1.3"
smol = "^1.2"
zbus = "^3.5"
logind-zbus = { version = "^3.0.3" } #, default-features = false, features = ["non_blocking"] }
zbus = "^3.4"
zbus_macros = "^3.4"
zvariant = "^3.7"
zvariant_derive = "^3.7"
logind-zbus = { version = "^3.0" } #, default-features = false, features = ["non_blocking"] }
serde = "^1.0"
serde_derive = "^1.0"
@@ -20,19 +23,19 @@ serde_json = "^1.0"
toml = "^0.5.9"
log = "^0.4"
env_logger = "^0.10.0"
env_logger = "^0.9"
glam = { version = "^0.22", features = ["serde"] }
gumdrop = "^0.8"
udev = "^0.7"
udev = "^0.6"
rusb = "^0.9"
sysfs-class = "^0.1.3"
sysfs-class = "^0.1.2"
inotify = "^0.10.0"
png_pong = "^0.8"
pix = "^0.13"
tinybmp = "^0.4.0"
gif = "^0.12.0"
tinybmp = "^0.3"
gif = "^0.11"
notify-rust = { git = "https://github.com/flukejones/notify-rust.git", default-features = false, features = ["z"] }

View File

@@ -1,121 +0,0 @@
# https://github.com/ericseppanen/cargo-cranky
# cargo install cargo-cranky && cargo cranky
warn = [
"clippy::all",
"clippy::await_holding_lock",
"clippy::bool_to_int_with_if",
"clippy::char_lit_as_u8",
"clippy::checked_conversions",
"clippy::dbg_macro",
"clippy::debug_assert_with_mut_call",
"clippy::disallowed_methods",
"clippy::disallowed_script_idents",
"clippy::doc_link_with_quotes",
"clippy::doc_markdown",
"clippy::empty_enum",
"clippy::enum_glob_use",
"clippy::equatable_if_let",
"clippy::exit",
"clippy::expl_impl_clone_on_copy",
"clippy::explicit_deref_methods",
"clippy::explicit_into_iter_loop",
"clippy::explicit_iter_loop",
"clippy::fallible_impl_from",
"clippy::filter_map_next",
"clippy::flat_map_option",
"clippy::float_cmp_const",
"clippy::fn_params_excessive_bools",
"clippy::fn_to_numeric_cast_any",
"clippy::from_iter_instead_of_collect",
"clippy::if_let_mutex",
"clippy::implicit_clone",
"clippy::imprecise_flops",
"clippy::index_refutable_slice",
"clippy::inefficient_to_string",
"clippy::invalid_upcast_comparisons",
"clippy::iter_not_returning_iterator",
"clippy::iter_on_empty_collections",
"clippy::iter_on_single_items",
"clippy::large_digit_groups",
"clippy::large_stack_arrays",
"clippy::large_types_passed_by_value",
"clippy::let_unit_value",
"clippy::linkedlist",
"clippy::lossy_float_literal",
"clippy::macro_use_imports",
"clippy::manual_assert",
"clippy::manual_instant_elapsed",
"clippy::manual_ok_or",
"clippy::manual_string_new",
"clippy::map_err_ignore",
"clippy::map_flatten",
"clippy::map_unwrap_or",
"clippy::match_on_vec_items",
"clippy::match_same_arms",
"clippy::match_wild_err_arm",
"clippy::match_wildcard_for_single_variants",
"clippy::mem_forget",
"clippy::mismatched_target_os",
"clippy::mismatching_type_param_order",
"clippy::missing_enforced_import_renames",
# "clippy::missing_errors_doc",
"clippy::missing_safety_doc",
"clippy::mut_mut",
"clippy::mutex_integer",
"clippy::needless_borrow",
"clippy::needless_continue",
"clippy::needless_for_each",
"clippy::needless_pass_by_value",
"clippy::negative_feature_names",
"clippy::nonstandard_macro_braces",
"clippy::option_option",
"clippy::path_buf_push_overwrite",
"clippy::ptr_as_ptr",
"clippy::rc_mutex",
"clippy::ref_option_ref",
"clippy::rest_pat_in_fully_bound_structs",
"clippy::same_functions_in_if_condition",
"clippy::semicolon_if_nothing_returned",
"clippy::single_match_else",
"clippy::str_to_string",
"clippy::string_add_assign",
"clippy::string_add",
"clippy::string_lit_as_bytes",
"clippy::string_to_string",
"clippy::todo",
"clippy::trailing_empty_array",
"clippy::trait_duplication_in_bounds",
"clippy::unimplemented",
"clippy::unnecessary_wraps",
"clippy::unnested_or_patterns",
"clippy::unused_peekable",
"clippy::unused_rounding",
# "clippy::unused_self",
"clippy::useless_transmute",
"clippy::verbose_file_reads",
"clippy::zero_sized_map_values",
"elided_lifetimes_in_paths",
"future_incompatible",
"nonstandard_style",
"rust_2018_idioms",
"rust_2021_prelude_collisions",
"rustdoc::missing_crate_level_docs",
"semicolon_in_expressions_from_macros",
"trivial_numeric_casts",
"unused_extern_crates",
"unused_import_braces",
"unused_lifetimes",
]
allow = [
# TODO(emilk): enable more lints
"clippy::cloned_instead_of_copied",
"clippy::derive_partial_eq_without_eq",
"clippy::type_complexity",
"clippy::undocumented_unsafe_blocks",
"trivial_casts",
"unsafe_op_in_unsafe_fn", # `unsafe_op_in_unsafe_fn` may become the default in future Rust versions: https://github.com/rust-lang/rust/issues/71668
"unused_qualifications",
]

View File

@@ -1,4 +1,4 @@
VERSION := $(shell /usr/bin/grep -Pm1 'version = "(\d.\d.\d)"' Cargo.toml | cut -d'"' -f2)
VERSION := $(shell grep -Pm1 'version = "(\d.\d.\d)"' daemon/Cargo.toml | cut -d'"' -f2)
INSTALL = install
INSTALL_PROGRAM = ${INSTALL} -D -m 0755
@@ -57,10 +57,7 @@ install:
$(INSTALL_DATA) "./data/icons/asus_notif_yellow.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_yellow.png"
$(INSTALL_DATA) "./data/icons/asus_notif_green.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_green.png"
$(INSTALL_DATA) "./data/icons/asus_notif_blue.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_blue.png"
$(INSTALL_DATA) "./data/icons/asus_notif_red.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_red.png"
$(INSTALL_DATA) "./data/icons/asus_notif_orange.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_orange.png"
$(INSTALL_DATA) "./data/icons/asus_notif_white.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_white.png"
$(INSTALL_DATA) "./data/icons/scalable/gpu-compute.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-compute.svg"
$(INSTALL_DATA) "./data/icons/scalable/gpu-hybrid.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-hybrid.svg"

View File

@@ -2,7 +2,7 @@
[![](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/donate/?hosted_button_id=4V2DEPS7K6APC) - [Asus Linux Website](https://asus-linux.org/)
**WARNING:** Many features are developed in tandem with kernel patches. If you see a feature is missing you either need a patched kernel, or v6.1 which has all my work merged upstream.
**WARNING:** Do not run the main branch of this repo unless you have all the asus-wmi kernel patches. You should use stable kernels + tagged releases.
`asusd` is a utility for Linux to control many aspects of various ASUS laptops
but can also be used with non-asus laptops with reduced features.
@@ -81,16 +81,7 @@ Requirements are rust >= 1.57 installed from rustup.io if the distro provided ve
**fedora:**
dnf install cmake clang-devel systemd-devel gtk3-devel cargo
make
sudo make install
**openSUSE:**
Works with KDE Plasma (without GTK packages)
zypper in -t pattern devel_basis
zypper in rustup cmake clang-devel systemd-devel glib2-devel cairo-devel atkmm-devel pangomm-devel gdk-pixbuf-devel gtk3-devel
dnf install clang-devel systemd-devel cargo
make
sudo make install

View File

@@ -1,6 +1,5 @@
[package]
name = "asusctl"
license = "MPL-2.0"
authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2021"
version.workspace = true

View File

@@ -20,7 +20,7 @@ fn main() {
}
for c in (0..35).into_iter().step_by(step) {
for i in &mut matrix.get_mut()[c] {
for i in matrix.get_mut()[c].iter_mut() {
*i = 50;
}
}

View File

@@ -17,6 +17,7 @@ fn main() {
for (y, row) in tmp.iter_mut().enumerate() {
if y % 2 == 0 && i + 1 != row.len() - 1 {
i += 1;
dbg!(i);
}
row[row.len() - i] = 0x22;
if i > 5 {

View File

@@ -35,7 +35,7 @@ fn main() -> Result<(), Box<dyn Error>> {
loop {
matrix.angle += 0.05;
if matrix.angle > PI * 2.0 {
matrix.angle = 0.0;
matrix.angle = 0.0
}
matrix.update();

View File

@@ -6,17 +6,17 @@ use rog_aura::{
KeyColourArray,
};
use rog_dbus::RogDbusClientBlocking;
use std::collections::VecDeque;
use std::collections::LinkedList;
#[derive(Debug, Clone)]
struct Ball {
position: (f32, f32),
direction: (f32, f32),
trail: VecDeque<(f32, f32)>,
trail: LinkedList<(f32, f32)>,
}
impl Ball {
fn new(x: f32, y: f32, trail_len: u32) -> Self {
let mut trail = VecDeque::new();
let mut trail = LinkedList::new();
for _ in 1..=trail_len {
trail.push_back((x, y));
}

View File

@@ -105,7 +105,7 @@ impl ToString for LedBrightness {
Some(0x02) => "high",
_ => "unknown",
};
s.to_owned()
s.to_string()
}
}

View File

@@ -26,7 +26,7 @@ mod aura_cli;
mod cli_opts;
mod profiles_cli;
fn main() {
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<String> = args().skip(1).collect();
let missing_argument_k = gumdrop::Error::missing_argument(Opt::Short('k'));
@@ -44,7 +44,7 @@ fn main() {
let (dbus, _) = RogDbusClientBlocking::new()
.map_err(|e| {
print_error_help(&e, None);
print_error_help(Box::new(e), None);
std::process::exit(3);
})
.unwrap();
@@ -54,7 +54,7 @@ fn main() {
.supported()
.supported_functions()
.map_err(|e| {
print_error_help(&e, None);
print_error_help(Box::new(e), None);
std::process::exit(4);
})
.unwrap();
@@ -63,14 +63,17 @@ fn main() {
print_versions();
println!();
print_laptop_info();
return Ok(());
}
if let Err(err) = do_parsed(&parsed, &supported, &dbus) {
print_error_help(&*err, Some(&supported));
print_error_help(err, Some(&supported));
}
Ok(())
}
fn print_error_help(err: &dyn std::error::Error, supported: Option<&SupportedFunctions>) {
fn print_error_help(err: Box<dyn std::error::Error>, supported: Option<&SupportedFunctions>) {
check_service("asusd");
println!("\nError: {}\n", err);
print_versions();
@@ -123,7 +126,7 @@ fn check_service(name: &str) -> bool {
fn do_parsed(
parsed: &CliStart,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking<'_>,
dbus: &RogDbusClientBlocking,
) -> Result<(), Box<dyn std::error::Error>> {
match &parsed.command {
Some(CliCommand::LedMode(mode)) => handle_led_mode(dbus, &supported.keyboard_led, mode)?,
@@ -131,9 +134,9 @@ fn do_parsed(
Some(CliCommand::LedPow2(pow)) => handle_led_power2(dbus, &supported.keyboard_led, pow)?,
Some(CliCommand::Profile(cmd)) => handle_profile(dbus, &supported.platform_profile, cmd)?,
Some(CliCommand::FanCurve(cmd)) => {
handle_fan_curve(dbus, &supported.platform_profile, cmd)?;
handle_fan_curve(dbus, &supported.platform_profile, cmd)?
}
Some(CliCommand::Graphics(_)) => do_gfx(),
Some(CliCommand::Graphics(_)) => do_gfx()?,
Some(CliCommand::Anime(cmd)) => handle_anime(dbus, &supported.anime_ctrl, cmd)?,
Some(CliCommand::Bios(cmd)) => handle_bios_option(dbus, &supported.rog_bios_ctrl, cmd)?,
None => {
@@ -147,7 +150,7 @@ fn do_parsed(
println!("{}", CliStart::usage());
println!();
if let Some(cmdlist) = CliStart::command_list() {
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_owned()).collect();
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_string()).collect();
for command in commands.iter().filter(|command| {
if !matches!(
supported.keyboard_led.prod_id,
@@ -211,13 +214,14 @@ fn do_parsed(
Ok(())
}
fn do_gfx() {
fn do_gfx() -> Result<(), Box<dyn std::error::Error>> {
println!("Please use supergfxctl for graphics switching. supergfxctl is the result of making asusctl graphics switching generic so all laptops can use it");
println!("This command will be removed in future");
Ok(())
}
fn handle_anime(
dbus: &RogDbusClientBlocking<'_>,
dbus: &RogDbusClientBlocking,
_supported: &AnimeSupportedFunctions,
cmd: &AnimeCommand,
) -> Result<(), Box<dyn std::error::Error>> {
@@ -233,14 +237,14 @@ fn handle_anime(
}
}
if let Some(anime_turn) = cmd.enable {
dbus.proxies().anime().set_on_off(anime_turn)?;
dbus.proxies().anime().set_on_off(anime_turn)?
}
if let Some(anime_boot) = cmd.boot_enable {
dbus.proxies().anime().set_boot_on_off(anime_boot)?;
dbus.proxies().anime().set_boot_on_off(anime_boot)?
}
if let Some(bright) = cmd.brightness {
verify_brightness(bright);
dbus.proxies().anime().set_brightness(bright)?;
dbus.proxies().anime().set_brightness(bright)?
}
if cmd.clear {
let anime_type = get_anime_type()?;
@@ -377,7 +381,7 @@ fn verify_brightness(brightness: f32) {
}
fn handle_led_mode(
dbus: &RogDbusClientBlocking<'_>,
dbus: &RogDbusClientBlocking,
supported: &LedSupportedFunctions,
mode: &LedModeCommand,
) -> Result<(), Box<dyn std::error::Error>> {
@@ -434,7 +438,7 @@ fn handle_led_mode(
}
fn handle_led_power1(
dbus: &RogDbusClientBlocking<'_>,
dbus: &RogDbusClientBlocking,
supported: &LedSupportedFunctions,
power: &LedPowerCommand1,
) -> Result<(), Box<dyn std::error::Error>> {
@@ -469,7 +473,7 @@ fn handle_led_power1(
}
fn handle_led_power_1_do_1866(
dbus: &RogDbusClientBlocking<'_>,
dbus: &RogDbusClientBlocking,
power: &LedPowerCommand1,
) -> Result<(), Box<dyn std::error::Error>> {
let mut enabled: Vec<AuraDev1866> = Vec::new();
@@ -509,7 +513,7 @@ fn handle_led_power_1_do_1866(
}
fn handle_led_power_1_do_tuf(
dbus: &RogDbusClientBlocking<'_>,
dbus: &RogDbusClientBlocking,
power: &LedPowerCommand1,
) -> Result<(), Box<dyn std::error::Error>> {
let mut enabled: Vec<AuraDevTuf> = Vec::new();
@@ -535,6 +539,7 @@ fn handle_led_power_1_do_tuf(
x19b6: vec![],
tuf: enabled,
};
dbg!(&data);
dbus.proxies().led().set_leds_power(data, true)?;
let data = AuraPowerDev {
@@ -548,7 +553,7 @@ fn handle_led_power_1_do_tuf(
}
fn handle_led_power2(
dbus: &RogDbusClientBlocking<'_>,
dbus: &RogDbusClientBlocking,
supported: &LedSupportedFunctions,
power: &LedPowerCommand2,
) -> Result<(), Box<dyn std::error::Error>> {
@@ -560,8 +565,8 @@ fn handle_led_power2(
println!("Commands available");
if let Some(cmdlist) = LedPowerCommand2::command_list() {
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_owned()).collect();
for command in &commands {
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_string()).collect();
for command in commands.iter() {
println!("{}", command);
}
}
@@ -577,7 +582,7 @@ fn handle_led_power2(
}
if supported.prod_id != AuraDevice::X19B6 {
println!("This option applies only to keyboards with product ID 0x19b6");
println!("This option applies only to keyboards with product ID 0x19b6")
}
let mut enabled: Vec<AuraDev19b6> = Vec::new();
@@ -642,7 +647,7 @@ fn handle_led_power2(
}
fn handle_profile(
dbus: &RogDbusClientBlocking<'_>,
dbus: &RogDbusClientBlocking,
supported: &PlatformProfileFunctions,
cmd: &ProfileCommand,
) -> Result<(), Box<dyn std::error::Error>> {
@@ -671,9 +676,7 @@ fn handle_profile(
if cmd.list {
let res = dbus.proxies().profile().profiles()?;
for p in &res {
println!("{:?}", p);
}
res.iter().for_each(|p| println!("{:?}", p));
}
if cmd.profile_get {
@@ -685,7 +688,7 @@ fn handle_profile(
}
fn handle_fan_curve(
dbus: &RogDbusClientBlocking<'_>,
dbus: &RogDbusClientBlocking,
supported: &PlatformProfileFunctions,
cmd: &FanCurveCommand,
) -> Result<(), Box<dyn std::error::Error>> {
@@ -747,7 +750,7 @@ fn handle_fan_curve(
}
fn handle_bios_option(
dbus: &RogDbusClientBlocking<'_>,
dbus: &RogDbusClientBlocking,
supported: &RogBiosSupportedFunctions,
cmd: &BiosCommand,
) -> Result<(), Box<dyn std::error::Error>> {
@@ -762,7 +765,10 @@ fn handle_bios_option(
{
println!("Missing arg or command\n");
let usage: Vec<String> = BiosCommand::usage().lines().map(|s| s.to_owned()).collect();
let usage: Vec<String> = BiosCommand::usage()
.lines()
.map(|s| s.to_string())
.collect();
for line in usage.iter().filter(|line| {
line.contains("sound") && supported.post_sound

View File

@@ -1,6 +1,5 @@
[package]
name = "daemon-user"
license = "MPL-2.0"
version.workspace = true
authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2021"
@@ -29,3 +28,5 @@ rog_dbus = { path = "../rog-dbus" }
rog_platform = { path = "../rog-platform" }
zbus.workspace = true
zvariant.workspace = true
zvariant_derive.workspace = true

View File

@@ -11,10 +11,9 @@ use std::{
},
};
use std::{sync::Arc, thread::sleep, time::Instant};
use zbus::{
dbus_interface,
zvariant::{ObjectPath, Type},
};
use zbus::dbus_interface;
use zvariant::ObjectPath;
use zvariant_derive::Type;
use crate::user_config::ConfigLoadSave;
use crate::{error::Error, user_config::UserAnimeConfig};
@@ -103,7 +102,7 @@ impl<'a> CtrlAnimeInner<'static> {
.write(output)
.map_err(|e| AnimeError::Dbus(format!("{}", e)))
.map(|_| false)
});
})?;
}
ActionData::Image(image) => {
self.client
@@ -124,10 +123,10 @@ impl<'a> CtrlAnimeInner<'static> {
sleep(Duration::from_millis(1));
}
}
ActionData::AudioEq
| ActionData::SystemInfo
| ActionData::TimeDate
| ActionData::Matrix => {}
ActionData::AudioEq => {}
ActionData::SystemInfo => {}
ActionData::TimeDate => {}
ActionData::Matrix => {}
}
}
@@ -143,7 +142,7 @@ pub struct CtrlAnime<'a> {
inner_early_return: Arc<AtomicBool>,
}
impl CtrlAnime<'static> {
impl<'a> CtrlAnime<'static> {
pub fn new(
config: Arc<Mutex<UserAnimeConfig>>,
inner: Arc<Mutex<CtrlAnimeInner<'static>>>,
@@ -185,7 +184,7 @@ impl CtrlAnime<'static> {
pub fn insert_asus_gif(
&mut self,
index: u32,
file: &str,
file: String,
time: Timer,
brightness: f32,
) -> zbus::fdo::Result<String> {
@@ -223,7 +222,7 @@ impl CtrlAnime<'static> {
pub fn insert_image_gif(
&mut self,
index: u32,
file: &str,
file: String,
scale: f32,
angle: f32,
xy: (f32, f32),
@@ -269,7 +268,7 @@ impl CtrlAnime<'static> {
pub fn insert_image(
&mut self,
index: u32,
file: &str,
file: String,
scale: f32,
angle: f32,
xy: (f32, f32),

View File

@@ -13,7 +13,7 @@ pub enum Error {
impl fmt::Display for Error {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Io(err) => write!(f, "Failed to open: {}", err),
Error::ConfigLoadFail => write!(f, "Failed to load user config"),

View File

@@ -114,7 +114,7 @@ impl ConfigLoadSave<UserAnimeConfig> for UserAnimeConfig {
impl Default for UserAnimeConfig {
fn default() -> Self {
Self {
name: "default".to_owned(),
name: "default".to_string(),
anime: vec![
ActionLoader::AsusImage {
file: "/usr/share/asusd/anime/custom/diagonal-template.png".into(),
@@ -233,7 +233,7 @@ impl Default for UserAuraConfig {
seq.push(key);
Self {
name: "default".to_owned(),
name: "default".to_string(),
aura: seq,
}
}
@@ -251,8 +251,8 @@ pub struct UserConfig {
impl UserConfig {
pub fn new() -> Self {
Self {
active_anime: Some("anime-default".to_owned()),
active_aura: Some("aura-default".to_owned()),
active_anime: Some("anime-default".to_string()),
active_aura: Some("aura-default".to_string()),
}
}

View File

@@ -1,7 +1,7 @@
[package]
name = "daemon"
license = "MPL-2.0"
version.workspace = true
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
repository = "https://gitlab.com/asus-linux/asus-nb-ctrl"
@@ -32,6 +32,7 @@ log.workspace = true
env_logger.workspace = true
zbus.workspace = true
zvariant.workspace = true
logind-zbus.workspace = true
# serialisation
@@ -43,6 +44,4 @@ toml.workspace = true
# Device control
sysfs-class.workspace = true # used for backlight control and baord ID
concat-idents.workspace = true
systemd-zbus = "*"
concat-idents.workspace = true

View File

@@ -7,12 +7,11 @@ use std::path::PathBuf;
pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
#[derive(Deserialize, Serialize, Default)]
#[serde(default)]
pub struct Config {
/// Save charge limit for restoring on boot
pub bat_charge_limit: u8,
pub panel_od: bool,
pub ac_command: String,
pub bat_command: String,
}
impl Config {
@@ -20,8 +19,6 @@ impl Config {
Config {
bat_charge_limit: 100,
panel_od: false,
ac_command: String::new(),
bat_command: String::new(),
}
}
@@ -40,14 +37,12 @@ impl Config {
config = Self::new();
} else if let Ok(data) = serde_json::from_str(&buf) {
config = data;
} else if let Ok(data) = serde_json::from_str::<Config455>(&buf) {
config = data.into();
} else {
warn!(
"Could not deserialise {}.\nWill rename to {}-old and recreate config",
CONFIG_PATH, CONFIG_PATH
);
let cfg_old = CONFIG_PATH.to_owned() + "-old";
let cfg_old = CONFIG_PATH.to_string() + "-old";
std::fs::rename(CONFIG_PATH, cfg_old).unwrap_or_else(|err| {
panic!(
"Could not rename. Please remove {} then restart service: Error {}",
@@ -57,7 +52,7 @@ impl Config {
config = Self::new();
}
} else {
config = Self::new();
config = Self::new()
}
config.write();
config
@@ -66,7 +61,7 @@ impl Config {
pub fn read(&mut self) {
let mut file = OpenOptions::new()
.read(true)
.open(CONFIG_PATH)
.open(&CONFIG_PATH)
.unwrap_or_else(|err| panic!("Error reading {}: {}", CONFIG_PATH, err));
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {
@@ -86,22 +81,3 @@ impl Config {
.unwrap_or_else(|err| error!("Could not write config: {}", err));
}
}
#[derive(Deserialize, Serialize, Default)]
#[serde(default)]
pub struct Config455 {
/// Save charge limit for restoring on boot
pub bat_charge_limit: u8,
pub panel_od: bool,
}
impl From<Config455> for Config {
fn from(c: Config455) -> Self {
Self {
bat_charge_limit: c.bat_charge_limit,
panel_od: c.panel_od,
ac_command: String::new(),
bat_command: String::new(),
}
}
}

View File

@@ -86,25 +86,25 @@ impl AnimeConfigCached {
anime_type: AnimeType,
) -> Result<(), AnimeError> {
let mut sys = Vec::with_capacity(config.system.len());
for ani in &config.system {
for ani in config.system.iter() {
sys.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.system = sys;
let mut boot = Vec::with_capacity(config.boot.len());
for ani in &config.boot {
for ani in config.boot.iter() {
boot.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.boot = boot;
let mut wake = Vec::with_capacity(config.wake.len());
for ani in &config.wake {
for ani in config.wake.iter() {
wake.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.wake = wake;
let mut shutdown = Vec::with_capacity(config.shutdown.len());
for ani in &config.shutdown {
for ani in config.shutdown.iter() {
shutdown.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.shutdown = shutdown;
@@ -145,7 +145,7 @@ impl AnimeConfig {
.read(true)
.write(true)
.create(true)
.open(ANIME_CONFIG_PATH)
.open(&ANIME_CONFIG_PATH)
.unwrap_or_else(|_| {
panic!(
"The file {} or directory /etc/asusd/ is missing",
@@ -249,7 +249,7 @@ impl AnimeConfig {
pub fn read(&mut self) {
let mut file = OpenOptions::new()
.read(true)
.open(ANIME_CONFIG_PATH)
.open(&ANIME_CONFIG_PATH)
.unwrap_or_else(|err| panic!("Error reading {}: {}", ANIME_CONFIG_PATH, err));
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {

View File

@@ -1,5 +1,5 @@
pub mod config;
/// Implements `CtrlTask`, Reloadable, `ZbusRun`
/// Implements CtrlTask, Reloadable, ZbusRun
pub mod trait_impls;
use self::config::{AnimeConfig, AnimeConfigCached};
@@ -62,7 +62,7 @@ impl CtrlAnime {
/// one running - so the thread uses atomics to signal run/exit.
///
/// Because this also writes to the usb device, other write tries (display only) *must*
/// get the mutex lock and set the `thread_exit` atomic.
/// get the mutex lock and set the thread_exit atomic.
fn run_thread(inner: Arc<Mutex<CtrlAnime>>, actions: Vec<ActionData>, mut once: bool) {
if actions.is_empty() {
warn!("AniMe system actions was empty");
@@ -106,13 +106,13 @@ impl CtrlAnime {
'main: loop {
thread_running.store(true, Ordering::SeqCst);
for action in &actions {
for action in actions.iter() {
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
match action {
ActionData::Animation(frames) => {
rog_anime::run_animation(frames, &|frame| {
if let Err(err) = rog_anime::run_animation(frames, &|frame| {
if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: frame-loop was asked to exit");
return Ok(true); // Do safe exit
@@ -130,14 +130,15 @@ impl CtrlAnime {
.ok();
false // Don't exit yet
})
.map_or_else(
|| {
warn!("rog_anime::run_animation:callback failed");
Err(AnimeError::NoFrames)
},
Ok,
)
});
.map(Ok)
.unwrap_or_else(|| {
warn!("rog_anime::run_animation:callback failed");
Err(AnimeError::NoFrames)
})
}) {
warn!("rog_anime::run_animation:Animation {}", err);
break 'main;
};
}
ActionData::Image(image) => {
once = false;
@@ -148,10 +149,10 @@ impl CtrlAnime {
}
}
ActionData::Pause(duration) => sleep(*duration),
ActionData::AudioEq
| ActionData::SystemInfo
| ActionData::TimeDate
| ActionData::Matrix => {}
ActionData::AudioEq => {}
ActionData::SystemInfo => {}
ActionData::TimeDate => {}
ActionData::Matrix => {}
}
}
if thread_exit.load(Ordering::SeqCst) {
@@ -193,7 +194,7 @@ impl CtrlAnime {
*led = bright as u8;
}
let data = AnimePacketType::try_from(buffer)?;
for row in &data {
for row in data.iter() {
self.node.write_bytes(row)?;
}
self.node.write_bytes(&pkt_for_flush())?;

View File

@@ -47,7 +47,7 @@ impl CtrlAnimeZbus {
let mut lock = self.0.lock().await;
let mut bright = bright;
if bright < 0.0 {
bright = 0.0;
bright = 0.0
} else if bright > 1.0 {
bright = 1.0;
}
@@ -149,7 +149,7 @@ impl crate::CtrlTask for CtrlAnimeZbus {
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
let run_action =
|start: bool, lock: MutexGuard<'_, CtrlAnime>, inner: Arc<Mutex<CtrlAnime>>| {
|start: bool, lock: MutexGuard<CtrlAnime>, inner: Arc<Mutex<CtrlAnime>>| {
if start {
info!("CtrlAnimeTask running sleep animation");
CtrlAnime::run_thread(inner, lock.cache.shutdown.clone(), true);
@@ -166,32 +166,28 @@ impl crate::CtrlTask for CtrlAnimeZbus {
self.create_sys_event_tasks(
// Loop is required to try an attempt to get the mutex *without* blocking
// other threads - it is possible to end up with deadlocks otherwise.
move || {
let inner1 = inner1.clone();
async move {
let lock = inner1.lock().await;
move || loop {
if let Some(lock) = inner1.try_lock() {
run_action(true, lock, inner1.clone());
break;
}
},
move || {
let inner2 = inner2.clone();
async move {
let lock = inner2.lock().await;
run_action(true, lock, inner2.clone());
move || loop {
if let Some(lock) = inner2.try_lock() {
run_action(false, lock, inner2.clone());
break;
}
},
move || {
let inner3 = inner3.clone();
async move {
let lock = inner3.lock().await;
move || loop {
if let Some(lock) = inner3.try_lock() {
run_action(true, lock, inner3.clone());
break;
}
},
move || {
let inner4 = inner4.clone();
async move {
let lock = inner4.lock().await;
run_action(true, lock, inner4.clone());
move || loop {
if let Some(lock) = inner4.try_lock() {
run_action(false, lock, inner4.clone());
break;
}
},
)

View File

@@ -127,7 +127,7 @@ pub struct AuraConfig {
impl Default for AuraConfig {
fn default() -> Self {
let mut prod_id = AuraDevice::Unknown;
for prod in &ASUS_KEYBOARD_DEVICES {
for prod in ASUS_KEYBOARD_DEVICES.iter() {
if HidRaw::new(prod).is_ok() {
prod_id = AuraDevice::from(*prod);
break;
@@ -192,7 +192,7 @@ impl AuraConfig {
.read(true)
.write(true)
.create(true)
.open(AURA_CONFIG_PATH)
.open(&AURA_CONFIG_PATH)
.unwrap_or_else(|_| {
panic!(
"The file {} or directory /etc/asusd/ is missing",
@@ -242,7 +242,7 @@ impl AuraConfig {
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
speed: Speed::Med,
direction: Direction::Left,
});
})
}
if let Some(m) = config.multizone.as_mut() {
m.insert(*n, default);
@@ -264,7 +264,7 @@ impl AuraConfig {
pub fn read(&mut self) {
let mut file = OpenOptions::new()
.read(true)
.open(AURA_CONFIG_PATH)
.open(&AURA_CONFIG_PATH)
.unwrap_or_else(|err| panic!("Error reading {}: {}", AURA_CONFIG_PATH, err));
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {
@@ -287,31 +287,34 @@ impl AuraConfig {
/// Set the mode data, current mode, and if multizone enabled.
///
/// Multipurpose, will accept `AuraEffect` with zones and put in the correct store.
/// Multipurpose, will accept AuraEffect with zones and put in the correct store.
pub fn set_builtin(&mut self, effect: AuraEffect) {
self.current_mode = effect.mode;
if effect.zone() == AuraZone::None {
self.builtins.insert(*effect.mode(), effect);
self.multizone_on = false;
} else {
if let Some(multi) = self.multizone.as_mut() {
if let Some(fx) = multi.get_mut(effect.mode()) {
for fx in fx.iter_mut() {
if fx.zone == effect.zone {
*fx = effect;
return;
}
}
fx.push(effect);
} else {
multi.insert(*effect.mode(), vec![effect]);
}
} else {
let mut tmp = BTreeMap::new();
tmp.insert(*effect.mode(), vec![effect]);
self.multizone = Some(tmp);
match effect.zone() {
AuraZone::None => {
self.builtins.insert(*effect.mode(), effect);
self.multizone_on = false;
}
_ => {
if let Some(multi) = self.multizone.as_mut() {
if let Some(fx) = multi.get_mut(effect.mode()) {
for fx in fx.iter_mut() {
if fx.zone == effect.zone {
*fx = effect;
return;
}
}
fx.push(effect);
} else {
multi.insert(*effect.mode(), vec![effect]);
}
} else {
let mut tmp = BTreeMap::new();
tmp.insert(*effect.mode(), vec![effect]);
self.multizone = Some(tmp);
}
self.multizone_on = true;
}
self.multizone_on = true;
}
}

View File

@@ -26,7 +26,7 @@ impl GetSupported for CtrlKbdLed {
let per_key_led_mode = laptop.per_key;
let mut prod_id = AuraDevice::Unknown;
for prod in &ASUS_KEYBOARD_DEVICES {
for prod in ASUS_KEYBOARD_DEVICES.iter() {
if HidRaw::new(prod).is_ok() {
prod_id = AuraDevice::from(*prod);
break;
@@ -72,10 +72,10 @@ impl CtrlKbdLed {
pub fn new(supported_modes: LaptopLedData, config: AuraConfig) -> Result<Self, RogError> {
let mut led_prod = None;
let mut led_node = None;
for prod in &ASUS_KEYBOARD_DEVICES {
for prod in ASUS_KEYBOARD_DEVICES.iter() {
match HidRaw::new(prod) {
Ok(node) => {
led_prod = Some((*prod).to_owned());
led_prod = Some(prod.to_string());
led_node = Some(node);
info!("Looked for keyboard controller 0x{prod}: Found");
break;
@@ -335,7 +335,7 @@ impl CtrlKbdLed {
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
speed: Speed::Med,
direction: Direction::Left,
});
})
}
if default.is_empty() {
return Err(RogError::AuraEffectNotSupported);
@@ -370,7 +370,7 @@ mod tests {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::default();
let supported_modes = LaptopLedData {
prod_family: String::new(),
prod_family: "".into(),
board_names: vec![],
standard: vec![AuraModeNum::Static],
multizone: vec![],
@@ -432,7 +432,7 @@ mod tests {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::default();
let supported_modes = LaptopLedData {
prod_family: String::new(),
prod_family: "".into(),
board_names: vec![],
standard: vec![AuraModeNum::Static],
multizone: vec![],
@@ -470,7 +470,7 @@ mod tests {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::default();
let supported_modes = LaptopLedData {
prod_family: String::new(),
prod_family: "".into(),
board_names: vec![],
standard: vec![AuraModeNum::Static],
multizone: vec![AuraZone::Key1, AuraZone::Key2],

View File

@@ -237,7 +237,7 @@ impl CtrlTask for CtrlKbdLedZbus {
}
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
let load_save = |start: bool, mut lock: MutexGuard<'_, CtrlKbdLed>| {
let load_save = |start: bool, mut lock: MutexGuard<CtrlKbdLed>| {
// If waking up
if !start {
info!("CtrlKbdLedTask reloading brightness and modes");
@@ -262,32 +262,28 @@ impl CtrlTask for CtrlKbdLedZbus {
self.create_sys_event_tasks(
// Loop so that we do aquire the lock but also don't block other
// threads (prevents potential deadlocks)
move || {
let inner1 = inner1.clone();
async move {
let lock = inner1.lock().await;
move || loop {
if let Some(lock) = inner1.try_lock() {
load_save(true, lock);
break;
}
},
move || {
let inner2 = inner2.clone();
async move {
let lock = inner2.lock().await;
move || loop {
if let Some(lock) = inner2.try_lock() {
load_save(false, lock);
break;
}
},
move || {
let inner3 = inner3.clone();
async move {
let lock = inner3.lock().await;
load_save(false, lock);
move || loop {
if let Some(lock) = inner3.try_lock() {
load_save(true, lock);
break;
}
},
move || {
let inner4 = inner4.clone();
async move {
let lock = inner4.lock().await;
move || loop {
if let Some(lock) = inner4.try_lock() {
load_save(false, lock);
break;
}
},
)

View File

@@ -319,8 +319,7 @@ impl CtrlPlatform {
task_watch_item!(panel_od platform);
task_watch_item!(dgpu_disable platform);
task_watch_item!(egpu_enable platform);
// NOTE: see note further below
//task_watch_item!(gpu_mux_mode platform);
task_watch_item!(gpu_mux_mode platform);
}
#[async_trait]
@@ -333,12 +332,10 @@ impl CtrlTask for CtrlPlatform {
let platform1 = self.clone();
let platform2 = self.clone();
self.create_sys_event_tasks(
move || async { {} },
move || {},
move || {
let platform1 = platform1.clone();
async move {
info!("CtrlRogBios reloading panel_od");
let lock = platform1.config.lock().await;
info!("CtrlRogBios reloading panel_od");
if let Some(lock) = platform1.config.try_lock() {
if platform1.platform.has_panel_od() {
platform1
.set_panel_overdrive(lock.panel_od)
@@ -350,12 +347,10 @@ impl CtrlTask for CtrlPlatform {
}
}
},
move || async { {} },
move || {},
move || {
let platform2 = platform2.clone();
async move {
info!("CtrlRogBios reloading panel_od");
let lock = platform2.config.lock().await;
info!("CtrlRogBios reloading panel_od");
if let Some(lock) = platform2.config.try_lock() {
if platform2.platform.has_panel_od() {
platform2
.set_panel_overdrive(lock.panel_od)
@@ -373,9 +368,7 @@ impl CtrlTask for CtrlPlatform {
self.watch_panel_od(signal_ctxt.clone()).await?;
self.watch_dgpu_disable(signal_ctxt.clone()).await?;
self.watch_egpu_enable(signal_ctxt.clone()).await?;
// NOTE: Can't have this as a watch because on a write to it, it reverts back to booted-with value
// as it does not actually change until reboot.
//self.watch_gpu_mux_mode(signal_ctxt.clone()).await?;
self.watch_gpu_mux_mode(signal_ctxt.clone()).await?;
Ok(())
}

View File

@@ -1,13 +1,12 @@
use crate::systemd::{do_systemd_unit_action, SystemdUnitAction};
use crate::{config::Config, error::RogError, GetSupported};
use crate::{task_watch_item, CtrlTask};
use async_trait::async_trait;
use log::{error, info, warn};
use log::{info, warn};
use rog_platform::power::AsusPower;
use rog_platform::supported::ChargeSupportedFunctions;
use std::process::Command;
use std::sync::Arc;
use std::time::Duration;
use systemd_zbus::{ManagerProxy as SystemdProxy, Mode, UnitFileState};
use tokio::time::sleep;
use zbus::dbus_interface;
use zbus::export::futures_util::lock::Mutex;
@@ -153,58 +152,58 @@ impl CtrlTask for CtrlPower {
}
async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
let conn = zbus::Connection::system().await?;
let sysd1 = SystemdProxy::new(&conn).await?;
let sysd2 = sysd1.clone();
let sysd3 = sysd1.clone();
let power1 = self.clone();
let power2 = self.clone();
self.create_sys_event_tasks(
move || async {},
move || {},
move || {
let power = power1.clone();
let sysd = sysd1.clone();
async move {
info!("CtrlCharge reloading charge limit");
let lock = power.config.lock().await;
power
info!("CtrlCharge reloading charge limit");
if let Some(lock) = power1.config.try_lock() {
power1
.set(lock.bat_charge_limit)
.map_err(|err| {
warn!("CtrlCharge: set_limit {}", err);
err
})
.ok();
if let Ok(value) = power.power.get_online() {
do_nvidia_powerd_action(&sysd, value == 1).await;
}
if let Ok(value) = power1.power.get_online() {
let action = if value == 1 {
SystemdUnitAction::Restart
} else {
SystemdUnitAction::Stop
};
if do_systemd_unit_action(action, NVIDIA_POWERD).is_ok() {
info!("CtrlPower task: did {action:?} on {NVIDIA_POWERD}");
}
}
},
move || async {},
move || {},
move || {
let power = power2.clone();
let sysd = sysd2.clone();
async move {
info!("CtrlCharge reloading charge limit");
let lock = power.config.lock().await;
power
info!("CtrlCharge reloading charge limit");
if let Some(lock) = power2.config.try_lock() {
power2
.set(lock.bat_charge_limit)
.map_err(|err| {
warn!("CtrlCharge: set_limit {}", err);
err
})
.ok();
if let Ok(value) = power.power.get_online() {
do_nvidia_powerd_action(&sysd, value == 1).await;
}
if let Ok(value) = power2.power.get_online() {
let action = if value == 1 {
SystemdUnitAction::Restart
} else {
SystemdUnitAction::Stop
};
if do_systemd_unit_action(action, NVIDIA_POWERD).is_ok() {
info!("CtrlPower task: did {action:?} on {NVIDIA_POWERD}");
}
}
},
)
.await;
let config = self.config.clone();
self.watch_charge_control_end_threshold(signal_ctxt.clone())
.await?;
@@ -215,36 +214,18 @@ impl CtrlTask for CtrlPower {
if let Ok(value) = ctrl.power.get_online() {
if online != value {
online = value;
do_nvidia_powerd_action(&sysd3, value == 1).await;
let action = if value == 1 {
SystemdUnitAction::Restart
} else {
SystemdUnitAction::Stop
};
if do_systemd_unit_action(action, NVIDIA_POWERD).is_ok() {
info!("CtrlPower task: did {action:?} on {NVIDIA_POWERD}");
}
Self::notify_mains_online(&signal_ctxt, value == 1)
.await
.unwrap();
let mut config = config.lock().await;
config.read();
let mut prog: Vec<&str> = Vec::new();
if value == 1 {
// AC ONLINE
prog = config.ac_command.split_whitespace().collect();
} else if value == 0 {
// BATTERY
prog = config.bat_command.split_whitespace().collect();
}
if prog.len() > 1 {
let mut cmd = Command::new(prog[0]);
for arg in prog.iter().skip(1) {
cmd.arg(*arg);
}
if let Err(e) = cmd.spawn() {
if value == 1 {
error!("AC power command error: {e}");
} else {
error!("Battery power command error: {e}");
}
}
}
}
}
// The inotify doesn't pick up events when the kernel changes internal value
@@ -256,23 +237,3 @@ impl CtrlTask for CtrlPower {
Ok(())
}
}
async fn do_nvidia_powerd_action(proxy: &SystemdProxy<'_>, ac_on: bool) {
if let Ok(res) = proxy.get_unit_file_state(NVIDIA_POWERD).await {
if res == UnitFileState::Enabled {
if ac_on {
proxy
.start_unit(NVIDIA_POWERD, Mode::Replace)
.await
.map_err(|e| error!("Error stopping {NVIDIA_POWERD}, {e:?}"))
.ok();
} else {
proxy
.stop_unit(NVIDIA_POWERD, Mode::Replace)
.await
.map_err(|e| error!("Error stopping {NVIDIA_POWERD}, {e:?}"))
.ok();
}
}
}
}

View File

@@ -43,8 +43,7 @@ impl ProfileConfig {
"Could not deserialise {}.\nWill rename to {}-old and recreate config",
config_path, config_path
);
let mut cfg_old = config_path.clone();
cfg_old.push_str("-old");
let cfg_old = config_path.clone() + "-old";
std::fs::rename(config_path.clone(), cfg_old).unwrap_or_else(|err| {
panic!(
"Could not rename. Please remove {} then restart service: Error {}",

View File

@@ -1,4 +1,4 @@
pub mod config;
pub mod controller;
/// Implements `CtrlTask`, Reloadable, `ZbusRun`
/// Implements CtrlTask, Reloadable, ZbusRun
pub mod trait_impls;

View File

@@ -32,7 +32,7 @@ impl ProfileZbus {
return Ok(profiles);
}
Err(Error::Failed(
"Failed to get all profile details".to_owned(),
"Failed to get all profile details".to_string(),
))
}
@@ -85,9 +85,9 @@ impl ProfileZbus {
let mut ctrl = self.0.lock().await;
ctrl.config.read();
if let Some(curves) = &ctrl.config.fan_curves {
return Ok(curves.get_enabled_curve_profiles());
return Ok(curves.get_enabled_curve_profiles().to_vec());
}
Err(Error::Failed(UNSUPPORTED_MSG.to_owned()))
Err(Error::Failed(UNSUPPORTED_MSG.to_string()))
}
/// Set a profile fan curve enabled status. Will also activate a fan curve if in the
@@ -109,7 +109,7 @@ impl ProfileZbus {
ctrl.save_config();
Ok(())
} else {
Err(Error::Failed(UNSUPPORTED_MSG.to_owned()))
Err(Error::Failed(UNSUPPORTED_MSG.to_string()))
}
}
@@ -121,7 +121,7 @@ impl ProfileZbus {
let curve = curves.get_fan_curves_for(profile);
return Ok(curve.clone());
}
Err(Error::Failed(UNSUPPORTED_MSG.to_owned()))
Err(Error::Failed(UNSUPPORTED_MSG.to_string()))
}
/// Set the fan curve for the specified profile.
@@ -134,7 +134,7 @@ impl ProfileZbus {
.save_fan_curve(curve, profile)
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
} else {
return Err(Error::Failed(UNSUPPORTED_MSG.to_owned()));
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
}
ctrl.write_profile_curve_to_platform()
.map_err(|e| warn!("Profile::set_profile, {}", e))
@@ -200,36 +200,8 @@ impl CtrlTask for ProfileZbus {
}
async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
// let ctrl = self.0.clone();
// let mut watch = self.0.lock().await.platform.monitor_platform_profile()?;
// let sig_ctx = signal_ctxt.clone();
// tokio::spawn(async move {
// let mut buffer = [0; 32];
// watch
// .event_stream(&mut buffer)
// .unwrap()
// .for_each(|_| async {
// let mut lock = ctrl.lock().await;
// let new_profile = Profile::get_active_profile().unwrap();
// if new_profile != lock.config.active_profile {
// lock.config.active_profile = new_profile;
// lock.write_profile_curve_to_platform().unwrap();
// lock.save_config();
// }
// Self::notify_profile(&sig_ctx, lock.config.active_profile)
// .await
// .ok();
// })
// .await;
// });
let ctrl = self.0.clone();
let mut watch = self
.0
.lock()
.await
.platform
.monitor_throttle_thermal_policy()?;
let mut watch = self.0.lock().await.platform.monitor_platform_profile()?;
tokio::spawn(async move {
let mut buffer = [0; 32];
@@ -238,15 +210,13 @@ impl CtrlTask for ProfileZbus {
.unwrap()
.for_each(|_| async {
let mut lock = ctrl.lock().await;
let new_thermal = lock.platform.get_throttle_thermal_policy().unwrap();
let new_profile = Profile::from_throttle_thermal_policy(new_thermal);
let new_profile = Profile::get_active_profile().unwrap();
if new_profile != lock.config.active_profile {
lock.config.active_profile = new_profile;
lock.write_profile_curve_to_platform().unwrap();
lock.save_config();
Profile::set_profile(lock.config.active_profile).unwrap();
}
Self::notify_profile(&signal_ctxt, lock.config.active_profile)
Self::notify_profile(&signal_ctxt.clone(), lock.config.active_profile)
.await
.ok();
})

View File

@@ -1,6 +1,8 @@
use async_trait::async_trait;
use serde_derive::{Deserialize, Serialize};
use zbus::{dbus_interface, zvariant::Type, Connection};
use zbus::dbus_interface;
use zbus::Connection;
use zvariant::Type;
use crate::{
ctrl_anime::CtrlAnime, ctrl_aura::controller::CtrlKbdLed, ctrl_platform::CtrlPlatform,

View File

@@ -7,6 +7,7 @@ use std::time::Duration;
use ::zbus::export::futures_util::lock::Mutex;
use ::zbus::Connection;
use daemon::ctrl_anime::CtrlAnime;
use log::LevelFilter;
use log::{error, info, warn};
use tokio::time::sleep;
use zbus::SignalContext;
@@ -32,9 +33,9 @@ static PROFILE_CONFIG_PATH: &str = "/etc/asusd/profile.conf";
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut logger = env_logger::Builder::new();
logger
.parse_default_env()
.target(env_logger::Target::Stdout)
.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()))
.filter(None, LevelFilter::Info)
.init();
let is_service = match env::var_os("IS_SERVICE") {

View File

@@ -37,7 +37,7 @@ pub enum RogError {
impl fmt::Display for RogError {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
RogError::ParseVendor => write!(f, "Parse gfx vendor error"),
RogError::ParseLed => write!(f, "Parse LED error"),
@@ -51,7 +51,7 @@ impl fmt::Display for RogError {
RogError::DoTask(deets) => write!(f, "Task error: {}", deets),
RogError::MissingFunction(deets) => write!(f, "Missing functionality: {}", deets),
RogError::MissingLedBrightNode(path, error) => write!(f, "Led node at {} is missing, please check you have the required patch or dkms module installed: {}", path, error),
RogError::ReloadFail(deets) => write!(f, "Reload error: {}", deets),
RogError::ReloadFail(deets) => write!(f, "Task error: {}", deets),
RogError::Profiles(deets) => write!(f, "Profile error: {}", deets),
RogError::Initramfs(detail) => write!(f, "Initiramfs error: {}", detail),
RogError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail),

View File

@@ -71,7 +71,7 @@ impl LaptopLedData {
}
impl LedSupportFile {
/// Consumes the `LEDModes`
/// Consumes the LEDModes
fn matcher(self, prod_family: &str, board_name: &str) -> Option<LaptopLedData> {
for config in self.led_data {
if prod_family.contains(&config.prod_family) {
@@ -90,7 +90,7 @@ impl LedSupportFile {
let mut loaded = false;
let mut data = LedSupportFile::default();
// Load user configs first so they are first to be checked
if let Ok(mut file) = OpenOptions::new().read(true).open(ASUS_LED_MODE_USER_CONF) {
if let Ok(mut file) = OpenOptions::new().read(true).open(&ASUS_LED_MODE_USER_CONF) {
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {
if l == 0 {
@@ -107,7 +107,7 @@ impl LedSupportFile {
}
}
// Load and append the default LED support data
if let Ok(mut file) = OpenOptions::new().read(true).open(ASUS_LED_MODE_CONF) {
if let Ok(mut file) = OpenOptions::new().read(true).open(&ASUS_LED_MODE_CONF) {
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {
if l == 0 {

View File

@@ -1,7 +1,7 @@
#![deny(unused_must_use)]
/// Configuration loading, saving
pub mod config;
/// Control of anime matrix display
/// Control of AniMe matrix display
pub mod ctrl_anime;
/// Keyboard LED brightness control, RGB, and LED display modes
pub mod ctrl_aura;
@@ -14,18 +14,19 @@ pub mod ctrl_profiles;
/// Laptop matching to determine capabilities
pub mod laptops;
pub mod systemd;
/// Fetch all supported functions for the laptop
pub mod ctrl_supported;
pub mod error;
use std::future::Future;
use crate::error::RogError;
use async_trait::async_trait;
use log::{debug, warn};
use log::warn;
use logind_zbus::manager::ManagerProxy;
use zbus::{export::futures_util::StreamExt, zvariant::ObjectPath, Connection, SignalContext};
use zbus::{export::futures_util::StreamExt, Connection, SignalContext};
use zvariant::ObjectPath;
/// This macro adds a function which spawns an `inotify` task on the passed in `Executor`.
///
@@ -63,6 +64,7 @@ macro_rules! task_watch_item {
let mut buffer = [0; 32];
watch.event_stream(&mut buffer).unwrap().for_each(|_| async {
let value = ctrl.$name();
dbg!(&value);
concat_idents::concat_idents!(notif_fn = notify_, $name {
Self::notif_fn(&signal_ctxt, value).await.ok();
});
@@ -134,31 +136,13 @@ pub trait CtrlTask {
///
/// The closures can potentially block, so execution time should be the minimal possible
/// such as save a variable.
async fn create_sys_event_tasks<
Fut1,
Fut2,
Fut3,
Fut4,
F1: Send + 'static,
F2: Send + 'static,
F3: Send + 'static,
F4: Send + 'static,
>(
async fn create_sys_event_tasks(
&self,
mut on_sleep: F1,
mut on_wake: F2,
mut on_shutdown: F3,
mut on_boot: F4,
) where
F1: FnMut() -> Fut1,
F2: FnMut() -> Fut2,
F3: FnMut() -> Fut3,
F4: FnMut() -> Fut4,
Fut1: Future<Output = ()> + Send,
Fut2: Future<Output = ()> + Send,
Fut3: Future<Output = ()> + Send,
Fut4: Future<Output = ()> + Send,
{
mut on_sleep: impl FnMut() + Send + 'static,
mut on_wake: impl FnMut() + Send + 'static,
mut on_shutdown: impl FnMut() + Send + 'static,
mut on_boot: impl FnMut() + Send + 'static,
) {
let connection = Connection::system()
.await
.expect("Controller could not create dbus connection");
@@ -172,11 +156,9 @@ pub trait CtrlTask {
while let Some(event) = notif.next().await {
if let Ok(args) = event.args() {
if args.start {
debug!("Doing on_sleep()");
on_sleep().await;
on_sleep();
} else if !args.start() {
debug!("Doing on_wake()");
on_wake().await;
on_wake();
}
}
}
@@ -192,11 +174,9 @@ pub trait CtrlTask {
while let Some(event) = notif.next().await {
if let Ok(args) = event.args() {
if args.start {
debug!("Doing on_shutdown()");
on_shutdown().await;
on_shutdown();
} else if !args.start() {
debug!("Doing on_boot()");
on_boot().await;
on_boot();
}
}
}

90
daemon/src/systemd.rs Normal file
View File

@@ -0,0 +1,90 @@
use std::process::Command;
use crate::error::RogError;
/// An action for `systemctl`
#[derive(Debug, Copy, Clone)]
pub enum SystemdUnitAction {
Stop,
Start,
Restart,
}
impl From<SystemdUnitAction> for &str {
fn from(s: SystemdUnitAction) -> Self {
match s {
SystemdUnitAction::Stop => "stop",
SystemdUnitAction::Start => "start",
SystemdUnitAction::Restart => "restart",
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum SystemdUnitState {
Active,
Inactive,
}
impl From<SystemdUnitState> for &str {
fn from(s: SystemdUnitState) -> Self {
match s {
SystemdUnitState::Active => "active",
SystemdUnitState::Inactive => "inactive",
}
}
}
/// Change the state of a systemd unit. Blocks while running command.
pub fn do_systemd_unit_action(action: SystemdUnitAction, unit: &str) -> Result<(), RogError> {
let mut cmd = Command::new("systemctl");
cmd.arg(<&str>::from(action));
cmd.arg(unit);
let status = cmd
.status()
.map_err(|err| RogError::Command(format!("{:?}", cmd), err))?;
if !status.success() {
let msg = format!("systemctl {action:?} {unit} failed: {status:?}",);
return Err(RogError::SystemdUnitAction(msg));
}
Ok(())
}
/// Get systemd unit state. Blocks while command is run.
pub fn is_systemd_unit_state(state: SystemdUnitState, unit: &str) -> Result<bool, RogError> {
let mut cmd = Command::new("systemctl");
cmd.arg("is-active");
cmd.arg(unit);
let output = cmd
.output()
.map_err(|err| RogError::Command(format!("{:?}", cmd), err))?;
if output.stdout.starts_with(<&str>::from(state).as_bytes()) {
return Ok(true);
}
Ok(false)
}
/// Wait for a systemd unit to change to `state`. Checks state every 250ms for 3 seconds. Blocks while running wait.
pub fn wait_systemd_unit_state(state: SystemdUnitState, unit: &str) -> Result<(), RogError> {
let mut cmd = Command::new("systemctl");
cmd.arg("is-active");
cmd.arg(unit);
let mut count = 0;
while count <= (4 * 3) {
// 3 seconds max
let output = cmd
.output()
.map_err(|err| RogError::Command(format!("{:?}", cmd), err))?;
if output.stdout.starts_with(<&str>::from(state).as_bytes()) {
return Ok(());
}
// fine to block here, nobody doing shit now
std::thread::sleep(std::time::Duration::from_millis(250));
count += 1;
}
Err(RogError::SystemdUnitWaitTimeout(<&str>::from(state).into()))
}

View File

@@ -1,10 +1,3 @@
[[led_data]]
prod_family = "ASUS TUF Gaming F15"
board_names = ["FX506HC"]
standard = ["Static", "Breathe", "Strobe", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "TUF"
board_names = ["FA507"]
@@ -19,13 +12,6 @@ standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "ASUS TUF Gaming A15"
board_names = ["FA506I"]
standard = ["Static", "Breathe", "Strobe", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "Zephyrus S"
board_names = ["GX502", "GX701", "G531", "GL531", "G532"]
@@ -85,7 +71,7 @@ per_key = true
[[led_data]]
prod_family = "ROG Strix"
board_names = ["G513QE", "GX531", "G512LV", "G712LV", "G712LW", "G513IH", "G513QY", "G713QM", "G512", "G713RM", "G713RW"]
board_names = ["G513QE", "GX531", "G512LV", "G712LV", "G712LW", "G513IH", "G513QY", "G713QM", "G512", "G713RW"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = ["Key1", "Key2", "Key3", "Key4"]
per_key = false
@@ -191,7 +177,7 @@ per_key = false
[[led_data]]
prod_family = "ROG Strix"
board_names = ["G513IC", "G513RC", "G513RM"]
board_names = ["G513IC", "G513RC"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = []
per_key = false

View File

@@ -9,7 +9,6 @@ ENV{DMI_FAMILY}=="*TUF*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*ROG*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Zephyrus*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Strix*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Vivo*ook*", GOTO="asusd_start"
# No match so
GOTO="asusd_end"
@@ -17,4 +16,4 @@ LABEL="asusd_start"
ACTION=="add|change", DRIVER=="asus-nb-wmi", TAG+="systemd", ENV{SYSTEMD_WANTS}="asusd.service"
ACTION=="add|remove", DRIVER=="asus-nb-wmi", TAG+="systemd", RUN+="systemctl restart asusd.service"
LABEL="asusd_end"
LABEL="asusd_end"

View File

@@ -6,7 +6,6 @@ Before=multi-user.target
[Service]
Environment=IS_SERVICE=1
Environment=RUST_LOG="info"
ExecStartPre=/bin/sleep 2
ExecStart=/usr/bin/asusd
Restart=on-failure

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 197 KiB

View File

@@ -1,69 +0,0 @@
# https://embarkstudios.github.io/cargo-deny/
targets = [
{ triple = "aarch64-apple-darwin" },
{ triple = "aarch64-linux-android" },
{ triple = "wasm32-unknown-unknown" },
{ triple = "x86_64-apple-darwin" },
{ triple = "x86_64-pc-windows-msvc" },
{ triple = "x86_64-unknown-linux-gnu" },
{ triple = "x86_64-unknown-linux-musl" },
]
[advisories]
vulnerability = "deny"
unmaintained = "warn"
yanked = "deny"
ignore = [
"RUSTSEC-2020-0071", # https://rustsec.org/advisories/RUSTSEC-2020-0071 - chrono/time: Potential segfault in the time crate
"RUSTSEC-2020-0159", # https://rustsec.org/advisories/RUSTSEC-2020-0159 - chrono/time: Potential segfault in localtime_r invocations
"RUSTSEC-2021-0127", # https://rustsec.org/advisories/RUSTSEC-2021-0127 - https://github.com/bheisler/criterion.rs/issues/534
]
[bans]
multiple-versions = "deny"
wildcards = "allow" # at least until https://github.com/EmbarkStudios/cargo-deny/issues/241 is fixed
deny = [
{ name = "openssl" }, # prefer rustls
{ name = "openssl-sys" }, # prefer rustls
]
skip-tree = [
{ name = "criterion" }, # dev-dependency
{ name = "glium" }, # legacy crate, lots of old dependencies
{ name = "rfd" }, # example dependency
{ name = "three-d" }, # example dependency
]
[licenses]
unlicensed = "deny"
allow-osi-fsf-free = "neither"
confidence-threshold = 0.92 # We want really high confidence when inferring licenses from text
copyleft = "deny"
allow = [
"Apache-2.0", # https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)
"BSD-2-Clause", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd)
"BSD-3-Clause", # https://tldrlegal.com/license/bsd-3-clause-license-(revised)
"BSL-1.0", # https://tldrlegal.com/license/boost-software-license-1.0-explained
"CC0-1.0", # https://creativecommons.org/publicdomain/zero/1.0/
"ISC", # https://tldrlegal.com/license/-isc-license
"LicenseRef-UFL-1.0", # https://tldrlegal.com/license/ubuntu-font-license,-1.0 - no official SPDX, see https://github.com/emilk/egui/issues/2321
"MIT", # https://tldrlegal.com/license/mit-license
"MPL-2.0", # https://www.mozilla.org/en-US/MPL/2.0/FAQ/ - see Q11
"OFL-1.1", # https://spdx.org/licenses/OFL-1.1.html
"OpenSSL", # https://www.openssl.org/source/license.html
"Unicode-DFS-2016", # https://spdx.org/licenses/Unicode-DFS-2016.html
"Zlib", # https://tldrlegal.com/license/zlib-libpng-license-(zlib)
]
[[licenses.clarify]]
name = "webpki"
expression = "ISC"
license-files = [{ path = "LICENSE", hash = 0x001c7e6c }]
[[licenses.clarify]]
name = "ring"
expression = "MIT AND ISC AND OpenSSL"
license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }]

View File

@@ -1,7 +1,7 @@
[package]
name = "rog_anime"
license = "MPL-2.0"
version.workspace = true
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
repository = "https://gitlab.com/asus-linux/asus-nb-ctrl"
@@ -14,7 +14,7 @@ exclude = ["data"]
[features]
default = ["dbus", "detect"]
dbus = ["zbus"]
dbus = ["zvariant", "zbus"]
detect = ["sysfs-class"]
[dependencies]
@@ -28,8 +28,7 @@ serde_derive.workspace = true
glam.workspace = true
zvariant = { workspace = true, optional = true }
zbus = { workspace = true, optional = true }
sysfs-class = { workspace = true, optional = true }
uhid-virt = "^0.0.5"
sysfs-class = { workspace = true, optional = true }

View File

@@ -7,7 +7,7 @@ use std::{
use log::info;
use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "dbus")]
use zbus::zvariant::Type;
use zvariant::Type;
use crate::{
error::{AnimeError, Result},
@@ -47,7 +47,8 @@ impl AnimeType {
/// The width of diagonal images
pub fn width(&self) -> usize {
match self {
AnimeType::GA401 | AnimeType::GA402 => 74,
AnimeType::GA401 => 74,
AnimeType::GA402 => 74,
}
}
@@ -102,8 +103,8 @@ impl AnimeDataBuffer {
/// Create from a vector of bytes
///
/// # Errors
/// Will error if the vector length is not `ANIME_DATA_LEN`
/// # Panics
/// Will panic if the vector length is not `ANIME_DATA_LEN`
#[inline]
pub fn from_vec(anime: AnimeType, data: Vec<u8>) -> Result<Self> {
if data.len() != anime.data_length() {
@@ -146,7 +147,10 @@ impl TryFrom<AnimeDataBuffer> for AnimePacketType {
/// This runs the animations as a blocking loop by using the `callback` to write data
///
/// If `callback` is `Ok(true)` then `run_animation` will exit the animation loop early.
pub fn run_animation(frames: &AnimeGif, callback: &dyn Fn(AnimeDataBuffer) -> Result<bool>) {
pub fn run_animation(
frames: &AnimeGif,
callback: &dyn Fn(AnimeDataBuffer) -> Result<bool>,
) -> Result<()> {
let mut count = 0;
let start = Instant::now();
@@ -211,10 +215,9 @@ pub fn run_animation(frames: &AnimeGif, callback: &dyn Fn(AnimeDataBuffer) -> Re
}
}
// TODO: Log this error
if matches!(callback(output), Ok(true)) {
info!("rog-anime: frame-loop callback asked to exit early");
return;
return Ok(());
}
if timed && Instant::now().duration_since(start) > run_time {
@@ -229,4 +232,5 @@ pub fn run_animation(frames: &AnimeGif, callback: &dyn Fn(AnimeDataBuffer) -> Re
}
}
}
Ok(())
}

View File

@@ -52,43 +52,39 @@ impl AnimeDiagonal {
let mut matrix = AnimeDiagonal::new(anime_type, duration);
match &raster {
match raster {
png_pong::PngRaster::Gray8(ras) => {
Self::pixels_from_8bit(ras, &mut matrix, bright, true);
Self::pixels_from_8bit(ras, &mut matrix, bright, true)
}
png_pong::PngRaster::Graya8(ras) => {
Self::pixels_from_8bit(ras, &mut matrix, bright, true);
Self::pixels_from_8bit(ras, &mut matrix, bright, true)
}
png_pong::PngRaster::Rgb8(ras) => {
Self::pixels_from_8bit(ras, &mut matrix, bright, false);
Self::pixels_from_8bit(ras, &mut matrix, bright, false)
}
png_pong::PngRaster::Rgba8(ras) => {
Self::pixels_from_8bit(ras, &mut matrix, bright, false);
Self::pixels_from_8bit(ras, &mut matrix, bright, false)
}
png_pong::PngRaster::Gray16(ras) => {
Self::pixels_from_16bit(ras, &mut matrix, bright, true);
Self::pixels_from_16bit(ras, &mut matrix, bright, true)
}
png_pong::PngRaster::Rgb16(ras) => {
Self::pixels_from_16bit(ras, &mut matrix, bright, false);
Self::pixels_from_16bit(ras, &mut matrix, bright, false)
}
png_pong::PngRaster::Graya16(ras) => {
Self::pixels_from_16bit(ras, &mut matrix, bright, true);
Self::pixels_from_16bit(ras, &mut matrix, bright, true)
}
png_pong::PngRaster::Rgba16(ras) => {
Self::pixels_from_16bit(ras, &mut matrix, bright, false);
Self::pixels_from_16bit(ras, &mut matrix, bright, false)
}
png_pong::PngRaster::Palette(..) => return Err(AnimeError::Format),
_ => return Err(AnimeError::Format),
};
Ok(matrix)
}
fn pixels_from_8bit<P>(
ras: &pix::Raster<P>,
matrix: &mut AnimeDiagonal,
bright: f32,
grey: bool,
) where
fn pixels_from_8bit<P>(ras: pix::Raster<P>, matrix: &mut AnimeDiagonal, bright: f32, grey: bool)
where
P: pix::el::Pixel<Chan = pix::chan::Ch8>,
{
let width = ras.width();
@@ -109,7 +105,7 @@ impl AnimeDiagonal {
}
fn pixels_from_16bit<P>(
ras: &pix::Raster<P>,
ras: pix::Raster<P>,
matrix: &mut AnimeDiagonal,
bright: f32,
grey: bool,
@@ -140,7 +136,7 @@ impl AnimeDiagonal {
}
}
/// Do conversion from the nested Vec in `AnimeMatrix` to the two required
/// Do conversion from the nested Vec in AnimeMatrix to the two required
/// packets suitable for sending over USB
fn to_ga401_packets(&self) -> Result<AnimeDataBuffer> {
let mut buf = vec![0u8; AnimeType::GA401.data_length()];

View File

@@ -26,7 +26,7 @@ pub enum AnimeError {
impl fmt::Display for AnimeError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AnimeError::NoFrames => write!(f, "No frames in PNG"),
AnimeError::Io(e) => write!(f, "Could not open: {}", e),

View File

@@ -93,7 +93,7 @@ impl AnimeGrid {
impl TryFrom<AnimeGrid> for AnimeDataBuffer {
type Error = AnimeError;
/// Do conversion from the nested Vec in anime matrix to the two required
/// Do conversion from the nested Vec in AniMeMatrix to the two required
/// packets suitable for sending over USB
fn try_from(anime: AnimeGrid) -> Result<Self> {
let mut buf = vec![0u8; anime.anime_type.data_length()];

View File

@@ -30,7 +30,7 @@ impl Default for Pixel {
/// is to be used to sample an image and set the LED brightness.
///
/// The position of the Led in `LedPositions` determines the placement in the final
/// data packets when written to the `AniMe`.
/// data packets when written to the AniMe.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Led(f32, f32, u8);
@@ -58,7 +58,7 @@ impl Led {
/// Container of `Led`, each of which specifies a position within the image
/// The main use of this is to position and sample colours for the final image
/// to show on `AniMe`
/// to show on AniMe
pub struct AnimeImage {
pub scale: Vec2,
/// Angle in radians
@@ -294,8 +294,8 @@ impl AnimeImage {
let x0 = led_from_px.mul_vec3(pos + Vec3::new(0.0, -0.5, 0.0));
const GROUP: [f32; 4] = [0.0, 0.5, 1.0, 1.5];
for u in &GROUP {
for v in &GROUP {
for u in GROUP.iter() {
for v in GROUP.iter() {
let sample = x0 + *u * du + *v * dv;
let x = sample.x as i32;
@@ -399,7 +399,7 @@ impl AnimeImage {
let png_pong::Step { raster, delay: _ } = decoder.last().ok_or(AnimeError::NoFrames)??;
let width;
let pixels = match &raster {
let pixels = match raster {
png_pong::PngRaster::Gray8(ras) => {
width = ras.width();
Self::pixels_from_8bit(ras, true)
@@ -432,7 +432,7 @@ impl AnimeImage {
width = ras.width();
Self::pixels_from_16bit(ras, false)
}
png_pong::PngRaster::Palette(..) => return Err(AnimeError::Format),
_ => return Err(AnimeError::Format),
};
let mut matrix = AnimeImage::new(
@@ -449,7 +449,7 @@ impl AnimeImage {
Ok(matrix)
}
fn pixels_from_8bit<P>(ras: &pix::Raster<P>, grey: bool) -> Vec<Pixel>
fn pixels_from_8bit<P>(ras: pix::Raster<P>, grey: bool) -> Vec<Pixel>
where
P: pix::el::Pixel<Chan = pix::chan::Ch8>,
{
@@ -468,7 +468,7 @@ impl AnimeImage {
.collect()
}
fn pixels_from_16bit<P>(ras: &pix::Raster<P>, grey: bool) -> Vec<Pixel>
fn pixels_from_16bit<P>(ras: pix::Raster<P>, grey: bool) -> Vec<Pixel>
where
P: pix::el::Pixel<Chan = pix::chan::Ch16>,
{
@@ -491,7 +491,7 @@ impl AnimeImage {
impl TryFrom<&AnimeImage> for AnimeDataBuffer {
type Error = AnimeError;
/// Do conversion from the nested Vec in `AnimeDataBuffer` to the two required
/// Do conversion from the nested Vec in AnimeDataBuffer to the two required
/// packets suitable for sending over USB
fn try_from(leds: &AnimeImage) -> Result<Self> {
let mut l: Vec<u8> = leds

View File

@@ -7,11 +7,11 @@ pub use data::*;
mod grid;
pub use grid::*;
/// Transform a PNG image for displaying on `AniMe` matrix display
/// Transform a PNG image for displaying on AniMe matrix display
mod image;
pub use image::*;
/// A grid of data that is intended to be read out and displayed on the `AniMe` as
/// A grid of data that is intended to be read out and displayed on the ANiMe as
/// a diagonal
mod diagonal;
pub use diagonal::*;

View File

@@ -7,7 +7,7 @@ use crate::{
error::Result, AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType,
};
/// All the possible `AniMe` actions that can be used. This enum is intended to be
/// All the possible AniMe actions that can be used. This enum is intended to be
/// a helper for loading up `ActionData`.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum ActionLoader {
@@ -44,7 +44,7 @@ pub enum ActionLoader {
Pause(Duration),
}
/// All the possible `AniMe` actions that can be used. The enum is intended to be
/// All the possible AniMe actions that can be used. The enum is intended to be
/// used in a array allowing the user to cycle through a series of actions.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum ActionData {
@@ -194,7 +194,7 @@ impl Sequences {
None
}
pub fn iter(&self) -> ActionIterator<'_> {
pub fn iter(&self) -> ActionIterator {
ActionIterator {
actions: self,
next_idx: 0,

View File

@@ -1,4 +1,4 @@
//! Utils for writing to the `AniMe` USB device
//! Utils for writing to the AniMe USB device
//!
//! Use of the device requires a few steps:
//! 1. Initialise the device by writing the two packets from `get_init_packets()`
@@ -63,7 +63,7 @@ pub const fn pkt_for_flush() -> [u8; PACKET_SIZE] {
}
/// Get the packet required for setting the device to on, on boot. Requires
/// `pkt_for_apply()` to be written after.
/// pkt_for_apply()` to be written after.
#[inline]
pub const fn pkt_for_set_boot(status: bool) -> [u8; PACKET_SIZE] {
let mut pkt = [0; PACKET_SIZE];

View File

@@ -1,7 +1,7 @@
[package]
name = "rog_aura"
license = "MPL-2.0"
version.workspace = true
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
repository = "https://gitlab.com/asus-linux/asusctl"
@@ -14,13 +14,13 @@ exclude = ["data"]
[features]
default = ["dbus", "toml"]
dbus = ["zbus"]
dbus = ["zvariant"]
[dependencies]
serde.workspace = true
serde_derive.workspace = true
toml = { workspace = true, optional = true }
zbus = { workspace = true, optional = true }
zvariant = { workspace = true, optional = true }
[dev-dependencies]
serde_json.workspace = true
serde_json.workspace = true

View File

@@ -5,9 +5,9 @@ pub const LED_INIT4: &str = "^ASUS Tech.Inc."; // ^ == 0x5e
pub const LED_INIT5: [u8; 6] = [0x5e, 0x05, 0x20, 0x31, 0, 0x08];
use serde_derive::{Deserialize, Serialize};
use std::{fmt::Display, str::FromStr};
use std::str::FromStr;
#[cfg(feature = "dbus")]
use zbus::zvariant::Type;
use zvariant::Type;
use crate::{error::Error, LED_MSG_LEN};
@@ -25,6 +25,7 @@ impl From<u32> for LedBrightness {
match bright {
0 => LedBrightness::Off,
1 => LedBrightness::Low,
2 => LedBrightness::Med,
3 => LedBrightness::High,
_ => LedBrightness::Med,
}
@@ -170,15 +171,23 @@ pub enum AuraModeNum {
Flash = 12,
}
impl Display for AuraModeNum {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", <&str>::from(self))
}
}
impl From<AuraModeNum> for String {
fn from(mode: AuraModeNum) -> Self {
<&str>::from(&mode).to_owned()
match mode {
AuraModeNum::Static => "Static",
AuraModeNum::Breathe => "Breathe",
AuraModeNum::Strobe => "Strobe",
AuraModeNum::Rainbow => "Rainbow",
AuraModeNum::Star => "Stars",
AuraModeNum::Rain => "Rain",
AuraModeNum::Highlight => "Highlight",
AuraModeNum::Laser => "Laser",
AuraModeNum::Ripple => "Ripple",
AuraModeNum::Pulse => "Pulse",
AuraModeNum::Comet => "Comet",
AuraModeNum::Flash => "Flash",
}
.to_string()
}
}
@@ -203,6 +212,7 @@ impl From<&AuraModeNum> for &str {
impl From<&str> for AuraModeNum {
fn from(mode: &str) -> Self {
match mode {
"Static" => AuraModeNum::Static,
"Breathe" => AuraModeNum::Breathe,
"Strobe" => AuraModeNum::Strobe,
"Rainbow" => AuraModeNum::Rainbow,
@@ -222,6 +232,7 @@ impl From<&str> for AuraModeNum {
impl From<u8> for AuraModeNum {
fn from(mode: u8) -> Self {
match mode {
0 => AuraModeNum::Static,
1 => AuraModeNum::Breathe,
2 => AuraModeNum::Strobe,
3 => AuraModeNum::Rainbow,
@@ -267,14 +278,22 @@ impl FromStr for AuraZone {
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
match s.to_ascii_lowercase().as_str() {
"0" | "none" => Ok(AuraZone::None),
"1" | "one" => Ok(AuraZone::Key1),
"2" | "two" => Ok(AuraZone::Key2),
"3" | "three" => Ok(AuraZone::Key3),
"4" | "four" => Ok(AuraZone::Key4),
"5" | "logo" => Ok(AuraZone::Logo),
"6" | "lightbar-left" => Ok(AuraZone::BarLeft),
"7" | "lightbar-right" => Ok(AuraZone::BarRight),
"0" => Ok(AuraZone::None),
"none" => Ok(AuraZone::None),
"1" => Ok(AuraZone::Key1),
"one" => Ok(AuraZone::Key1),
"2" => Ok(AuraZone::Key2),
"two" => Ok(AuraZone::Key2),
"3" => Ok(AuraZone::Key3),
"three" => Ok(AuraZone::Key3),
"4" => Ok(AuraZone::Key4),
"four" => Ok(AuraZone::Key4),
"5" => Ok(AuraZone::Logo),
"logo" => Ok(AuraZone::Logo),
"6" => Ok(AuraZone::BarLeft),
"lightbar-left" => Ok(AuraZone::BarLeft),
"7" => Ok(AuraZone::BarRight),
"lightbar-right" => Ok(AuraZone::BarRight),
_ => Err(Error::ParseSpeed),
}
}
@@ -370,20 +389,18 @@ impl AuraEffect {
/// factory mode accepts only one colour.
pub const fn allowed_parameters(mode: AuraModeNum) -> AuraParameters {
match mode {
AuraModeNum::Static
| AuraModeNum::Highlight
| AuraModeNum::Pulse
| AuraModeNum::Comet
| AuraModeNum::Flash => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Static => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Breathe => AuraParameters::new(true, true, true, true, false),
AuraModeNum::Strobe | AuraModeNum::Rain => {
AuraParameters::new(true, false, false, true, false)
}
AuraModeNum::Strobe => AuraParameters::new(true, false, false, true, false),
AuraModeNum::Rainbow => AuraParameters::new(true, false, false, true, true),
AuraModeNum::Star => AuraParameters::new(true, true, true, true, true),
AuraModeNum::Laser | AuraModeNum::Ripple => {
AuraParameters::new(true, true, false, true, false)
}
AuraModeNum::Rain => AuraParameters::new(true, false, false, true, false),
AuraModeNum::Highlight => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Laser => AuraParameters::new(true, true, false, true, false),
AuraModeNum::Ripple => AuraParameters::new(true, true, false, true, false),
AuraModeNum::Pulse => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Comet => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Flash => AuraParameters::new(true, true, false, false, false),
}
}
}

View File

@@ -13,7 +13,7 @@ pub enum Error {
impl fmt::Display for Error {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::ParseColour => write!(f, "Could not parse colour"),
Error::ParseSpeed => write!(f, "Could not parse speed"),

View File

@@ -139,17 +139,17 @@ impl From<&Key> for &str {
Key::MediaStop => "Media Stop",
Key::MediaNext => "Media Next",
Key::MediaPrev => "Media Previous",
Key::NormalBlank
| Key::NormalSpacer
| Key::FuncBlank
| Key::FuncSpacer
| Key::ArrowBlank
| Key::ArrowSpacer
| Key::ArrowRegularBlank
| Key::ArrowRegularSpacer
| Key::ArrowSplitBlank
| Key::ArrowSplitSpacer
| Key::RowEndSpacer => "",
Key::NormalBlank => "",
Key::NormalSpacer => "",
Key::FuncBlank => "",
Key::FuncSpacer => "",
Key::ArrowBlank => "",
Key::ArrowSpacer => "",
Key::ArrowRegularBlank => "",
Key::ArrowRegularSpacer => "",
Key::ArrowSplitBlank => "",
Key::ArrowSplitSpacer => "",
Key::RowEndSpacer => "",
}
}
}

View File

@@ -203,22 +203,17 @@ pub enum KeyShape {
impl KeyShape {
pub const fn width(&self) -> f32 {
match self {
Self::Tilde | Self::Arrow => 0.8,
Self::Normal
| Self::NormalBlank
| Self::NormalSpacer
| Self::Func
| Self::FuncBlank
| Self::Space5
| Self::ArrowBlank
| Self::ArrowSpacer
| Self::ArrowSplit
| Self::ArrowSplitBlank
| Self::ArrowSplitSpacer => 1.0,
Self::Tilde => 0.8,
Self::Normal => 1.0,
Self::NormalBlank => 1.0,
Self::NormalSpacer => 1.0,
Self::Func => 1.0,
Self::FuncBlank => 1.0,
Self::FuncSpacer => 0.6,
Self::Space => 5.0,
Self::Space5 => 1.0,
Self::LCtrlMed => 1.1,
Self::LShift | Self::Backspace => 2.0,
Self::LShift => 2.0,
Self::LShift3 => 0.67,
Self::RShift => 2.8,
Self::RshiftSmall => 1.8,
@@ -227,9 +222,12 @@ impl KeyShape {
Self::Return3 => 0.7333,
Self::Tab => 1.4,
Self::Caps => 1.6,
Self::Backspace => 2.0,
Self::Backspace3 => 0.666,
Self::ArrowRegularBlank | Self::ArrowRegularSpacer => 0.7,
Self::Arrow => 0.8,
Self::ArrowBlank | Self::ArrowSpacer => 1.0,
Self::ArrowSplit | Self::ArrowSplitBlank | Self::ArrowSplitSpacer => 1.0,
Self::RowEndSpacer => 0.1,
}
}

View File

@@ -6,7 +6,7 @@ impl KeyLayout {
pub fn g513_layout() -> Self {
Self {
matches: vec!["G513".into()],
locale: "US".to_owned(),
locale: "US".to_string(),
rows: vec![
KeyRow::new(
0.8,

View File

@@ -5,7 +5,7 @@ impl KeyLayout {
pub fn ga401_layout() -> Self {
Self {
matches: vec!["GA401".into(), "GA402".into()],
locale: "US".to_owned(),
locale: "US".to_string(),
rows: vec![
KeyRow::new(
0.8,

View File

@@ -5,7 +5,7 @@ impl KeyLayout {
pub fn gx502_layout() -> Self {
Self {
matches: vec!["GX502".into(), "GU502".into()],
locale: "US".to_owned(),
locale: "US".to_string(),
rows: vec![
KeyRow::new(
0.8,

View File

@@ -43,7 +43,7 @@ impl KeyLayout {
pub fn matches(&self, board_name: &str) -> bool {
let board = board_name.to_ascii_uppercase();
for tmp in &self.matches {
for tmp in self.matches.iter() {
if board.contains(tmp.as_str()) {
return true;
}
@@ -91,7 +91,7 @@ impl KeyRow {
Self { height, row }
}
pub fn row(&self) -> Iter<'_, Key> {
pub fn row(&self) -> Iter<Key> {
self.row.iter()
}

View File

@@ -1,7 +1,7 @@
use crate::keys::Key;
use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "dbus")]
use zbus::zvariant::Type;
use zvariant::Type;
/// Represents the per-key raw USB packets
pub type PerKeyRaw = Vec<Vec<u8>>;

View File

@@ -1,6 +1,6 @@
use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "dbus")]
use zbus::zvariant::Type;
use zvariant::Type;
/// Represents the zoned raw USB packets
pub type ZonedRaw = Vec<u8>;
@@ -43,7 +43,8 @@ impl ZonedColourArray {
pub fn rgb_for_zone(&mut self, zone: PerZone) -> &mut [u8] {
match zone {
PerZone::None | PerZone::KeyboardLeft => &mut self.0[9..=11],
PerZone::None => &mut self.0[9..=11],
PerZone::KeyboardLeft => &mut self.0[9..=11],
PerZone::KeyboardCenterLeft => &mut self.0[12..=14],
PerZone::KeyboardCenterRight => &mut self.0[15..=17],
PerZone::KeyboardRight => &mut self.0[18..=20],

View File

@@ -9,7 +9,7 @@ use serde_derive::{Deserialize, Serialize};
// static mut RNDINDEX: usize = 0;
static mut PRNDINDEX: usize = 0;
/// Pseudo random table ripped straight out of room4doom
/// Pseudo random table ripped straight out of Room4Doom
pub const RNDTABLE: [i32; 256] = [
0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66, 74, 21, 211, 47, 80, 242, 154,
27, 205, 128, 161, 89, 77, 36, 95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188,
@@ -88,7 +88,7 @@ impl Sequences {
}
pub fn next_state(&mut self, layout: &KeyLayout) {
for effect in &mut self.0 {
for effect in self.0.iter_mut() {
effect.next_state(layout);
}
}
@@ -97,7 +97,7 @@ impl Sequences {
let mut keys = KeyColourArray::new();
let mut zones = ZonedColourArray::new();
let mut is_per_key = false;
for effect in &self.0 {
for effect in self.0.iter() {
match effect.get_led_type() {
LedType::Key(key) => {
is_per_key = true;

View File

@@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use std::ops::{BitAnd, BitOr};
#[cfg(feature = "dbus")]
use zbus::zvariant::Type;
use zvariant::Type;
pub const LED_INIT1: [u8; 2] = [0x5d, 0xb9];
pub const LED_INIT2: &str = "]ASUS Tech.Inc."; // ] == 0x5d
@@ -36,10 +36,14 @@ impl From<&str> for AuraDevice {
fn from(s: &str) -> Self {
match s.to_lowercase().as_str() {
"tuf" => AuraDevice::Tuf,
"1866" | "0x1866" => AuraDevice::X1866,
"1869" | "0x1869" => AuraDevice::X1869,
"1854" | "0x1854" => AuraDevice::X1854,
"19b6" | "0x19b6" => AuraDevice::X19B6,
"1866" => AuraDevice::X1866,
"1869" => AuraDevice::X1869,
"1854" => AuraDevice::X1854,
"19b6" => AuraDevice::X19B6,
"0x1866" => AuraDevice::X1866,
"0x1869" => AuraDevice::X1869,
"0x1854" => AuraDevice::X1854,
"0x19b6" => AuraDevice::X19B6,
_ => AuraDevice::Unknown,
}
}
@@ -104,9 +108,9 @@ impl From<AuraDev1866> for u32 {
impl AuraDev1866 {
pub fn to_bytes(control: &[Self]) -> [u8; 3] {
let mut a: u32 = 0;
for n in control {
control.iter().for_each(|n| {
a |= *n as u32;
}
});
[
((a & 0xff0000) >> 16) as u8,
((a & 0xff00) >> 8) as u8,
@@ -193,9 +197,9 @@ impl From<AuraDev19b6> for u32 {
impl AuraDev19b6 {
pub fn to_bytes(control: &[Self]) -> [u8; 3] {
let mut a: u32 = 0;
for n in control {
control.iter().for_each(|n| {
a |= *n as u32;
}
});
[
(a & 0xff) as u8,
((a & 0xff00) >> 8) as u8,

View File

@@ -1,6 +1,5 @@
[package]
name = "rog-control-center"
license = "MPL-2.0"
version.workspace = true
authors = ["Luke D. Jones <luke@ljones.dev>"]
edition = "2021"
@@ -9,11 +8,9 @@ edition = "2021"
mocking = []
[dependencies]
egui = { git = "https://github.com/flukejones/egui", branch = "wayland_dark_theme" }
eframe = { git = "https://github.com/flukejones/egui", branch = "wayland_dark_theme" }
libappindicator = "0.7" # Tray icon
gtk = "0.15.5"
egui = { git = "https://github.com/flukejones/egui" }
eframe= { git = "https://github.com/flukejones/egui" }
#eframe= { git = "https://github.com/emilk/egui", default-features = false, features = ["dark-light", "default_fonts", "wgpu"] }
daemon = { path = "../daemon" }
rog_anime = { path = "../rog-anime" }
@@ -22,9 +19,7 @@ rog_aura = { path = "../rog-aura" }
rog_profiles = { path = "../rog-profiles" }
rog_platform = { path = "../rog-platform" }
supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", default-features = false }
log.workspace = true
env_logger.workspace = true
#supergfxctl = { path = "../../supergfxctl", default-features = false }
tokio.workspace = true
serde.workspace = true
@@ -35,7 +30,5 @@ zbus.workspace = true
dirs.workspace = true
notify-rust.workspace = true
png_pong.workspace = true
nix = "^0.26.1"
tempfile = "3.3.0"
nix = "^0.20.0"
tempfile = "3.2.0"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -2,7 +2,7 @@ use std::{
f64::consts::PI,
sync::{
atomic::{AtomicBool, AtomicU8, Ordering},
Arc, Mutex,
Arc,
},
time::{Duration, Instant},
};
@@ -11,15 +11,16 @@ use egui::{Button, RichText};
use rog_platform::supported::SupportedFunctions;
use crate::{
config::Config, error::Result, system_state::SystemState, Page, RogDbusClientBlocking,
config::Config, error::Result, page_states::PageDataStates, Page, RogDbusClientBlocking,
};
pub struct RogApp {
pub struct RogApp<'a> {
pub page: Page,
pub states: Arc<Mutex<SystemState>>,
pub states: PageDataStates,
pub supported: SupportedFunctions,
// TODO: can probably just open and read whenever
pub config: Config,
pub asus_dbus: RogDbusClientBlocking<'a>,
/// Oscillator in percentage
pub oscillator1: Arc<AtomicU8>,
pub oscillator2: Arc<AtomicU8>,
@@ -30,11 +31,11 @@ pub struct RogApp {
pub oscillator_toggle: Arc<AtomicBool>,
}
impl RogApp {
impl<'a> RogApp<'a> {
/// Called once before the first frame.
pub fn new(
config: Config,
states: Arc<Mutex<SystemState>>,
states: PageDataStates,
_cc: &eframe::CreationContext<'_>,
) -> Result<Self> {
let (dbus, _) = RogDbusClientBlocking::new()?;
@@ -54,7 +55,6 @@ impl RogApp {
let oscillator_freq1 = oscillator_freq.clone();
let oscillator_toggle = Arc::new(AtomicBool::new(false));
let oscillator_toggle1 = oscillator_toggle.clone();
std::thread::spawn(move || {
let started = Instant::now();
let mut toggled = false;
@@ -83,7 +83,6 @@ impl RogApp {
oscillator1_1.store(tmp1, Ordering::SeqCst);
oscillator1_2.store(tmp2, Ordering::SeqCst);
oscillator1_3.store(tmp3, Ordering::SeqCst);
std::thread::sleep(Duration::from_millis(33));
}
});
@@ -93,6 +92,7 @@ impl RogApp {
states,
page: Page::System,
config,
asus_dbus: dbus,
oscillator1,
oscillator2,
oscillator3,
@@ -102,63 +102,60 @@ impl RogApp {
}
}
impl eframe::App for RogApp {
impl<'a> eframe::App for RogApp<'a> {
/// Called each time the UI needs repainting, which may be many times per second.
/// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
let states = self.states.clone();
if let Ok(mut states) = states.try_lock() {
if states.app_should_update {
states.app_should_update = false;
ctx.request_repaint();
}
}
let Self {
supported,
asus_dbus: dbus,
states,
..
} = self;
states
.refresh_if_notfied(supported, dbus)
.map(|repaint| {
if repaint {
ctx.request_repaint();
}
})
.map_err(|e| self.states.error = Some(e.to_string()))
.ok();
let page = self.page;
self.top_bar(ctx, frame);
self.side_panel(ctx);
let mut was_error = false;
if let Some(err) = self.states.error.clone() {
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading(RichText::new("Error!").size(28.0));
if let Ok(mut states) = states.try_lock() {
if let Some(err) = states.error.clone() {
was_error = true;
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading(RichText::new("Error!").size(28.0));
ui.centered_and_justified(|ui| {
ui.label(RichText::new(format!("The error was: {:?}", err)).size(22.0));
ui.centered_and_justified(|ui| {
ui.label(RichText::new(format!("The error was: {:?}", err)).size(22.0));
});
});
egui::TopBottomPanel::bottom("error_bar")
.default_height(26.0)
.show(ctx, |ui| {
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
if ui
.add(Button::new(RichText::new("Okay").size(20.0)))
.clicked()
{
self.states.error = None;
}
});
});
egui::TopBottomPanel::bottom("error_bar")
.default_height(26.0)
.show(ctx, |ui| {
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
if ui
.add(Button::new(RichText::new("Okay").size(20.0)))
.clicked()
{
states.error = None;
}
});
});
}
}
if !was_error {
if let Ok(mut states) = states.try_lock() {
if page == Page::System {
self.system_page(&mut states, ctx);
} else if page == Page::AuraEffects {
self.aura_page(&mut states, ctx);
// TODO: Anime page is not complete
// } else if page == Page::AnimeMatrix {
// self.anime_page(ctx);
} else if page == Page::FanCurves {
self.fan_curve_page(&mut states, ctx);
}
}
} else if page == Page::System {
self.system_page(ctx);
} else if page == Page::AuraEffects {
self.aura_page(ctx);
// TODO: Anime page is not complete
// } else if page == Page::AnimeMatrix {
// self.anime_page(ctx);
} else if page == Page::FanCurves {
self.fan_curve_page(ctx);
}
}
}

View File

@@ -1,24 +1,22 @@
use log::{error, info, warn};
use serde_derive::{Deserialize, Serialize};
use std::{
fs::{create_dir, OpenOptions},
io::{Read, Write},
};
use crate::{error::Error, update_and_notify::EnabledNotifications};
use serde_derive::{Deserialize, Serialize};
//use log::{error, info, warn};
use crate::error::Error;
const CFG_DIR: &str = "rog";
const CFG_FILE_NAME: &str = "rog-control-center.cfg";
#[derive(Debug, Clone, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize)]
#[serde(default)]
pub struct Config {
pub run_in_background: bool,
pub startup_in_background: bool,
pub ac_command: String,
pub bat_command: String,
pub enable_notifications: bool,
// This field must be last
pub enabled_notifications: EnabledNotifications,
}
impl Default for Config {
@@ -27,9 +25,6 @@ impl Default for Config {
run_in_background: true,
startup_in_background: false,
enable_notifications: true,
enabled_notifications: EnabledNotifications::default(),
ac_command: String::new(),
bat_command: String::new(),
}
}
}
@@ -37,17 +32,14 @@ impl Default for Config {
impl Config {
pub fn load() -> Result<Config, Error> {
let mut path = if let Some(dir) = dirs::config_dir() {
info!("Found XDG config dir {dir:?}");
dir
} else {
error!("Could not get XDG config dir");
return Err(Error::XdgVars);
};
path.push(CFG_DIR);
if !path.exists() {
create_dir(path.clone())?;
info!("Created {path:?}");
}
path.push(CFG_FILE_NAME);
@@ -62,23 +54,18 @@ impl Config {
if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len == 0 {
warn!("Zero len read of Config file");
let default = Config::default();
let t = toml::to_string_pretty(&default).unwrap();
file.write_all(t.as_bytes())?;
return Ok(default);
} else if let Ok(data) = toml::from_str::<Config>(&buf) {
info!("Loaded config file {path:?}");
return Ok(data);
} else if let Ok(data) = toml::from_str::<Config455>(&buf) {
info!("Loaded old v4.5.5 config file {path:?}");
return Ok(data.into());
}
}
Err(Error::ConfigLoadFail)
}
pub fn save(&mut self, enabled_notifications: &EnabledNotifications) -> Result<(), Error> {
pub fn save(&self) -> Result<(), Error> {
let mut path = if let Some(dir) = dirs::config_dir() {
dir
} else {
@@ -88,7 +75,6 @@ impl Config {
path.push(CFG_DIR);
if !path.exists() {
create_dir(path.clone())?;
info!("Created {path:?}");
}
path.push(CFG_FILE_NAME);
@@ -99,31 +85,8 @@ impl Config {
.truncate(true)
.open(&path)?;
self.enabled_notifications = enabled_notifications.clone();
let t = toml::to_string_pretty(&self).unwrap();
file.write_all(t.as_bytes())?;
info!("Saved config file {path:?}");
Ok(())
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Config455 {
pub run_in_background: bool,
pub startup_in_background: bool,
pub enable_notifications: bool,
pub enabled_notifications: EnabledNotifications,
}
impl From<Config455> for Config {
fn from(c: Config455) -> Self {
Self {
run_in_background: c.run_in_background,
startup_in_background: c.startup_in_background,
enable_notifications: c.enable_notifications,
enabled_notifications: c.enabled_notifications,
ac_command: String::new(),
bat_command: String::new(),
}
}
}

View File

@@ -15,7 +15,7 @@ pub enum Error {
impl fmt::Display for Error {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Io(err) => write!(f, "Failed to open: {}", err),
Error::Nix(err) => write!(f, "Error: {}", err),

View File

@@ -13,11 +13,10 @@ pub mod config;
pub mod error;
#[cfg(feature = "mocking")]
pub mod mocking;
pub mod notify;
pub mod page_states;
pub mod pages;
pub mod startup_error;
pub mod system_state;
pub mod tray;
pub mod update_and_notify;
pub mod widgets;
#[cfg(feature = "mocking")]
@@ -97,7 +96,7 @@ pub fn get_ipc_file() -> Result<File, crate::error::Error> {
let tmp_dir = std::env::temp_dir().join("rog-gui");
let fifo_path = tmp_dir.join("ipc.pipe");
if let Err(e) = unistd::mkfifo(&fifo_path, stat::Mode::S_IRWXU) {
if !matches!(e, nix::errno::Errno::EEXIST) {
if !matches!(e, nix::Error::Sys(nix::errno::Errno::EEXIST)) {
return Err(e)?;
}
}

View File

@@ -1,38 +1,28 @@
use eframe::{IconData, NativeOptions};
use log::{error, info};
use eframe::NativeOptions;
use rog_aura::layouts::KeyLayout;
use rog_control_center::tray::init_tray;
use rog_control_center::update_and_notify::EnabledNotifications;
use rog_control_center::{
config::Config, error::Result, get_ipc_file, on_tmp_dir_exists, print_versions,
startup_error::AppErrorShow, system_state::SystemState, update_and_notify::start_notifications,
RogApp, RogDbusClientBlocking, SHOWING_GUI, SHOW_GUI,
config::Config, error::Result, get_ipc_file, notify::start_notifications, on_tmp_dir_exists,
page_states::PageDataStates, print_versions, startup_error::AppErrorShow, RogApp,
RogDbusClientBlocking, SHOWING_GUI, SHOW_GUI,
};
use rog_platform::supported::SupportedFunctions;
use std::sync::Mutex;
use tokio::runtime::Runtime;
use std::{
fs::OpenOptions,
io::{Read, Write},
path::PathBuf,
sync::Arc,
sync::{atomic::AtomicBool, Arc},
};
use tokio::runtime::Runtime;
#[cfg(not(feature = "mocking"))]
const DATA_DIR: &str = "/usr/share/rog-gui/";
#[cfg(feature = "mocking")]
const DATA_DIR: &str = env!("CARGO_MANIFEST_DIR");
const BOARD_NAME: &str = "/sys/class/dmi/id/board_name";
const APP_ICON_PATH: &str = "/usr/share/icons/hicolor/512x512/apps/rog-control-center.png";
fn main() -> Result<()> {
print_versions();
let mut logger = env_logger::Builder::new();
logger
.parse_default_env()
.target(env_logger::Target::Stdout)
.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()))
.init();
// start tokio
let rt = Runtime::new().expect("Unable to create Runtime");
@@ -46,7 +36,6 @@ fn main() -> Result<()> {
min_window_size: Some(egui::vec2(840.0, 600.0)),
max_window_size: Some(egui::vec2(840.0, 600.0)),
run_and_return: true,
icon_data: Some(load_icon()),
..Default::default()
};
@@ -60,28 +49,14 @@ fn main() -> Result<()> {
})
.unwrap();
let supported = match dbus.proxies().supported().supported_functions() {
Ok(s) => s,
Err(e) => {
eframe::run_native(
"ROG Control Center",
native_options.clone(),
Box::new(move |_| Box::new(AppErrorShow::new(e.to_string()))),
);
SupportedFunctions::default()
}
};
// Startup
let mut config = Config::load()?;
let mut start_closed = config.startup_in_background;
if config.startup_in_background {
config.run_in_background = true;
let tmp = config.enabled_notifications.clone(); // ends up being a double clone, oh well.
config.save(&tmp)?;
config.save()?;
}
let enabled_notifications = EnabledNotifications::tokio_mutex(&config);
// Find and load a matching layout for laptop
let mut file = OpenOptions::new()
@@ -118,16 +93,16 @@ fn main() -> Result<()> {
Err(_) => on_tmp_dir_exists().unwrap(),
};
let states = setup_page_state_and_notifs(layout, enabled_notifications, &config, &supported)?;
init_tray(supported, states.clone());
let states =
setup_page_state_and_notifs(layout.clone(), &config, native_options.clone(), &dbus)
.unwrap();
loop {
if !start_closed {
start_app(states.clone(), native_options.clone())?;
}
let config = Config::load()?;
let config = Config::load().unwrap();
if !config.run_in_background {
break;
}
@@ -136,7 +111,7 @@ fn main() -> Result<()> {
let mut buf = [0u8; 4];
// blocks until it is read, typically the read will happen after a second
// process writes to the IPC (so there is data to actually read)
if get_ipc_file()?.read(&mut buf).is_ok() && buf[0] == SHOW_GUI {
if get_ipc_file().unwrap().read(&mut buf).is_ok() && buf[0] == SHOW_GUI {
start_closed = false;
continue;
}
@@ -152,24 +127,59 @@ fn main() -> Result<()> {
fn setup_page_state_and_notifs(
keyboard_layout: KeyLayout,
enabled_notifications: Arc<Mutex<EnabledNotifications>>,
config: &Config,
supported: &SupportedFunctions,
) -> Result<Arc<Mutex<SystemState>>> {
let page_states = Arc::new(Mutex::new(SystemState::new(
native_options: NativeOptions,
dbus: &RogDbusClientBlocking,
) -> Result<PageDataStates> {
// Cheap method to alert to notifications rather than spinning a thread for each
// This is quite different when done in a retained mode app
let charge_notified = Arc::new(AtomicBool::new(false));
let bios_notified = Arc::new(AtomicBool::new(false));
let aura_notified = Arc::new(AtomicBool::new(false));
let anime_notified = Arc::new(AtomicBool::new(false));
let profiles_notified = Arc::new(AtomicBool::new(false));
let fans_notified = Arc::new(AtomicBool::new(false));
let notifs_enabled = Arc::new(AtomicBool::new(config.enable_notifications));
start_notifications(
charge_notified.clone(),
bios_notified.clone(),
aura_notified.clone(),
anime_notified.clone(),
profiles_notified.clone(),
fans_notified.clone(),
notifs_enabled.clone(),
)?;
let supported = match dbus.proxies().supported().supported_functions() {
Ok(s) => s,
Err(e) => {
eframe::run_native(
"ROG Control Center",
native_options,
Box::new(move |_| Box::new(AppErrorShow::new(e.to_string()))),
);
SupportedFunctions::default()
}
};
PageDataStates::new(
keyboard_layout,
enabled_notifications.clone(),
supported,
)?));
start_notifications(config, page_states.clone(), enabled_notifications)?;
Ok(page_states)
notifs_enabled.clone(),
charge_notified.clone(),
bios_notified.clone(),
aura_notified.clone(),
anime_notified.clone(),
profiles_notified.clone(),
fans_notified.clone(),
&supported,
&dbus,
)
}
fn start_app(states: Arc<Mutex<SystemState>>, native_options: NativeOptions) -> Result<()> {
let mut ipc_file = get_ipc_file()?;
ipc_file.write_all(&[SHOWING_GUI])?;
fn start_app(states: PageDataStates, native_options: NativeOptions) -> Result<()> {
let mut ipc_file = get_ipc_file().unwrap();
ipc_file.write_all(&[SHOWING_GUI]).unwrap();
eframe::run_native(
"ROG Control Center",
native_options,
@@ -177,42 +187,3 @@ fn start_app(states: Arc<Mutex<SystemState>>, native_options: NativeOptions) ->
);
Ok(())
}
/// Bah.. the icon dosn't work on wayland anyway, but we'll leave it in for now.
fn load_icon() -> IconData {
let path = PathBuf::from(APP_ICON_PATH);
let mut buf = Vec::new();
let mut rgba = Vec::new();
let mut height = 512;
let mut width = 512;
if path.exists() {
if let Ok(mut file) = OpenOptions::new()
.read(true)
.open(path)
.map_err(|e| error!("Error opening app icon: {e:?}"))
{
file.read_to_end(&mut buf)
.map_err(|e| error!("Error reading app icon: {e:?}"))
.ok();
let data = std::io::Cursor::new(buf);
let decoder = png_pong::Decoder::new(data).unwrap().into_steps();
let png_pong::Step { raster, delay: _ } = decoder.last().unwrap().unwrap();
if let png_pong::PngRaster::Rgba8(ras) = raster {
rgba = ras.as_u8_slice().to_vec();
width = ras.width();
height = ras.height();
info!("Loaded app icon. Not actually supported in Wayland yet");
}
}
} else {
error!("Missing {APP_ICON_PATH}");
}
IconData {
height,
width,
rgba,
}
}

View File

@@ -0,0 +1,290 @@
use crate::error::Result;
use notify_rust::{Hint, Notification, NotificationHandle};
use rog_dbus::{
zbus_anime::AnimeProxy, zbus_led::LedProxy, zbus_platform::RogBiosProxy,
zbus_power::PowerProxy, zbus_profile::ProfileProxy,
};
use rog_platform::platform::GpuMode;
use rog_profiles::Profile;
use std::{
fmt::Display,
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
},
};
use supergfxctl::pci_device::GfxPower;
use zbus::export::futures_util::{future, StreamExt};
const NOTIF_HEADER: &str = "ROG Control";
macro_rules! notify {
($notifier:expr, $last_notif:ident) => {
if let Some(notif) = $last_notif.take() {
notif.close();
}
if let Ok(x) = $notifier {
$last_notif.replace(x);
}
};
}
macro_rules! recv_notif {
($proxy:ident,
$signal:ident,
$was_notified:ident,
$last_notif:ident,
$notif_enabled:ident,
[$($out_arg:ident)+],
$msg:literal,
$notifier:ident) => {
let last_notif = $last_notif.clone();
let notifs_enabled1 = $notif_enabled.clone();
let notified = $was_notified.clone();
// TODO: make a macro or generic function or something...
tokio::spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = $proxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.$signal().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if notifs_enabled1.load(Ordering::SeqCst) {
if let Ok(ref mut lock) = last_notif.try_lock() {
notify!($notifier($msg, &out$(.$out_arg)+()), lock);
}
}
notified.store(true, Ordering::SeqCst);
}
future::ready(())
})
.await;
};
});
};
}
type SharedHandle = Arc<Mutex<Option<NotificationHandle>>>;
pub fn start_notifications(
charge_notified: Arc<AtomicBool>,
bios_notified: Arc<AtomicBool>,
aura_notified: Arc<AtomicBool>,
anime_notified: Arc<AtomicBool>,
profiles_notified: Arc<AtomicBool>,
_fans_notified: Arc<AtomicBool>,
notifs_enabled: Arc<AtomicBool>,
) -> Result<()> {
let last_notification: SharedHandle = Arc::new(Mutex::new(None));
// BIOS notif
recv_notif!(
RogBiosProxy,
receive_notify_post_boot_sound,
bios_notified,
last_notification,
notifs_enabled,
[on],
"BIOS Post sound",
do_notification
);
recv_notif!(
RogBiosProxy,
receive_notify_panel_od,
bios_notified,
last_notification,
notifs_enabled,
[overdrive],
"Panel Overdrive enabled:",
do_notification
);
recv_notif!(
RogBiosProxy,
receive_notify_dgpu_disable,
bios_notified,
last_notification,
notifs_enabled,
[disable],
"BIOS dGPU disabled",
do_notification
);
recv_notif!(
RogBiosProxy,
receive_notify_egpu_enable,
bios_notified,
last_notification,
notifs_enabled,
[enable],
"BIOS eGPU enabled",
do_notification
);
recv_notif!(
RogBiosProxy,
receive_notify_gpu_mux_mode,
bios_notified,
last_notification,
notifs_enabled,
[mode],
"BIOS GPU MUX mode (reboot required)",
mux_notification
);
// Charge notif
recv_notif!(
PowerProxy,
receive_notify_charge_control_end_threshold,
charge_notified,
last_notification,
notifs_enabled,
[limit],
"Battery charge limit changed to",
do_notification
);
recv_notif!(
PowerProxy,
receive_notify_mains_online,
bios_notified,
last_notification,
notifs_enabled,
[on],
"AC Power power is",
ac_power_notification
);
// Profile notif
recv_notif!(
ProfileProxy,
receive_notify_profile,
profiles_notified,
last_notification,
notifs_enabled,
[profile],
"Profile changed to",
do_thermal_notif
);
// notify!(do_thermal_notif(&out.profile), lock);
// LED notif
recv_notif!(
LedProxy,
receive_notify_led,
aura_notified,
last_notification,
notifs_enabled,
[data mode_name],
"Keyboard LED mode changed to",
do_notification
);
tokio::spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = LedProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_all_signals().await {
p.for_each(|_| {
aura_notified.store(true, Ordering::SeqCst);
future::ready(())
})
.await;
};
});
tokio::spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = AnimeProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_power_states().await {
p.for_each(|_| {
anime_notified.store(true, Ordering::SeqCst);
future::ready(())
})
.await;
};
});
let notifs_enabled1 = notifs_enabled.clone();
let last_notif = last_notification.clone();
let bios_notified1 = bios_notified.clone();
tokio::spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = supergfxctl::zbus_proxy::DaemonProxy::new(&conn)
.await
.unwrap();
if let Ok(p) = proxy.receive_notify_gfx_status().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if notifs_enabled1.load(Ordering::SeqCst) {
let status = out.status();
if *status != GfxPower::Unknown {
// Required check because status cycles through active/unknown/suspended
if let Ok(ref mut lock) = last_notif.try_lock() {
notify!(
do_notification(
"dGPU status changed:",
&format!("{status:?}",)
),
lock
);
}
}
}
}
bios_notified1.store(true, Ordering::SeqCst);
future::ready(())
})
.await;
};
});
Ok(())
}
fn base_notification<T>(message: &str, data: &T) -> Notification
where
T: Display,
{
let mut notif = Notification::new();
notif
.summary(NOTIF_HEADER)
.body(&format!("{message} {data}"))
.timeout(2000)
//.hint(Hint::Resident(true))
.hint(Hint::Category("device".into()));
notif
}
fn do_notification<T>(message: &str, data: &T) -> Result<NotificationHandle>
where
T: Display,
{
Ok(base_notification(message, data).show()?)
}
fn ac_power_notification(message: &str, on: &bool) -> Result<NotificationHandle> {
let data = if *on {
"plugged".to_string()
} else {
"unplugged".to_string()
};
Ok(base_notification(message, &data).show()?)
}
/// Actual GpuMode unused as data is never correct until switched by reboot
fn mux_notification(message: &str, _: &GpuMode) -> Result<NotificationHandle> {
Ok(base_notification(message, &"").show()?)
}
fn do_thermal_notif(message: &str, profile: &Profile) -> Result<NotificationHandle> {
let icon = match profile {
Profile::Balanced => "asus_notif_yellow",
Profile::Performance => "asus_notif_red",
Profile::Quiet => "asus_notif_green",
};
let profile: &str = (*profile).into();
let mut notif = base_notification(message, &profile.to_uppercase());
Ok(notif.icon(icon).show()?)
}

View File

@@ -1,25 +1,24 @@
use std::{
collections::{BTreeMap, HashSet},
sync::{Arc, Mutex},
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
};
use egui::Vec2;
use rog_aura::{layouts::KeyLayout, usb::AuraPowerDev, AuraEffect, AuraModeNum};
use rog_platform::{platform::GpuMode, supported::SupportedFunctions};
use rog_profiles::{fan_curve_set::FanCurveSet, FanCurvePU, Profile};
use supergfxctl::{
pci_device::{GfxMode, GfxPower},
zbus_proxy::DaemonProxyBlocking as GfxProxyBlocking,
};
use crate::{error::Result, update_and_notify::EnabledNotifications, RogDbusClientBlocking};
use log::error;
use crate::{error::Result, RogDbusClientBlocking};
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug)]
pub struct BiosState {
/// To be shared to a thread that checks notifications.
/// It's a bit general in that it won't provide *what* was
/// updated, so the full state needs refresh
pub was_notified: Arc<AtomicBool>,
pub post_sound: bool,
pub dedicated_gfx: GpuMode,
pub panel_overdrive: bool,
@@ -28,8 +27,13 @@ pub struct BiosState {
}
impl BiosState {
pub fn new(supported: &SupportedFunctions, dbus: &RogDbusClientBlocking<'_>) -> Result<Self> {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<Self> {
Ok(Self {
was_notified,
post_sound: if supported.rog_bios_ctrl.post_sound {
dbus.proxies().rog_bios().post_boot_sound()? != 0
} else {
@@ -52,15 +56,21 @@ impl BiosState {
}
}
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug)]
pub struct ProfilesState {
pub was_notified: Arc<AtomicBool>,
pub list: Vec<Profile>,
pub current: Profile,
}
impl ProfilesState {
pub fn new(supported: &SupportedFunctions, dbus: &RogDbusClientBlocking<'_>) -> Result<Self> {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<Self> {
Ok(Self {
was_notified,
list: if supported.platform_profile.platform_profile {
let mut list = dbus.proxies().profile().profiles()?;
list.sort();
@@ -77,8 +87,9 @@ impl ProfilesState {
}
}
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug)]
pub struct FanCurvesState {
pub was_notified: Arc<AtomicBool>,
pub show_curve: Profile,
pub show_graph: FanCurvePU,
pub enabled: HashSet<Profile>,
@@ -87,25 +98,30 @@ pub struct FanCurvesState {
}
impl FanCurvesState {
pub fn new(supported: &SupportedFunctions, dbus: &RogDbusClientBlocking<'_>) -> Result<Self> {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<Self> {
let profiles = if supported.platform_profile.platform_profile {
dbus.proxies().profile().profiles()?
} else {
vec![Profile::Balanced, Profile::Quiet, Profile::Performance]
};
let enabled = if supported.platform_profile.fan_curves {
dbus.proxies()
.profile()
.enabled_fan_profiles()?
.iter()
.cloned()
.collect::<HashSet<_>>()
HashSet::from_iter(
dbus.proxies()
.profile()
.enabled_fan_profiles()?
.iter()
.cloned(),
)
} else {
HashSet::from([Profile::Balanced, Profile::Quiet, Profile::Performance])
};
let mut curves: BTreeMap<Profile, FanCurveSet> = BTreeMap::new();
for p in &profiles {
profiles.iter().for_each(|p| {
if supported.platform_profile.fan_curves {
if let Ok(curve) = dbus.proxies().profile().fan_curve_data(*p) {
curves.insert(*p, curve);
@@ -118,7 +134,7 @@ impl FanCurvesState {
curve.gpu.temp = [20, 30, 40, 50, 70, 80, 90, 100];
curves.insert(*p, curve);
}
}
});
let show_curve = if supported.platform_profile.fan_curves {
dbus.proxies().profile().active_profile()?
@@ -127,6 +143,7 @@ impl FanCurvesState {
};
Ok(Self {
was_notified,
show_curve,
show_graph: FanCurvePU::CPU,
enabled,
@@ -136,8 +153,9 @@ impl FanCurvesState {
}
}
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug)]
pub struct AuraState {
pub was_notified: Arc<AtomicBool>,
pub current_mode: AuraModeNum,
pub modes: BTreeMap<AuraModeNum, AuraEffect>,
pub enabled: AuraPowerDev,
@@ -149,8 +167,13 @@ pub struct AuraState {
}
impl AuraState {
pub fn new(supported: &SupportedFunctions, dbus: &RogDbusClientBlocking<'_>) -> Result<Self> {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<Self> {
Ok(Self {
was_notified,
current_mode: if !supported.keyboard_led.stock_led_modes.is_empty() {
dbus.proxies().led().led_mode().unwrap_or_default()
} else {
@@ -189,8 +212,9 @@ impl AuraState {
}
}
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug)]
pub struct AnimeState {
pub was_notified: Arc<AtomicBool>,
pub bright: u8,
pub boot: bool,
pub awake: bool,
@@ -198,8 +222,13 @@ pub struct AnimeState {
}
impl AnimeState {
pub fn new(supported: &SupportedFunctions, dbus: &RogDbusClientBlocking<'_>) -> Result<Self> {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<Self> {
Ok(Self {
was_notified,
boot: if supported.anime_ctrl.0 {
dbus.proxies().anime().boot_enabled()?
} else {
@@ -217,53 +246,11 @@ impl AnimeState {
}
}
#[derive(Clone, Debug)]
pub struct GfxState {
pub has_supergfx: bool,
pub mode: GfxMode,
pub power_status: GfxPower,
}
impl GfxState {
pub fn new(_supported: &SupportedFunctions, dbus: &GfxProxyBlocking<'_>) -> Result<Self> {
Ok(Self {
has_supergfx: dbus.mode().is_ok(),
mode: dbus.mode().unwrap_or(GfxMode::None),
power_status: dbus.power().unwrap_or(GfxPower::Unknown),
})
}
}
impl Default for GfxState {
fn default() -> Self {
Self {
has_supergfx: false,
mode: GfxMode::None,
power_status: GfxPower::Unknown,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct PowerState {
pub charge_limit: u8,
pub ac_power: bool,
}
impl PowerState {
pub fn new(_supported: &SupportedFunctions, dbus: &RogDbusClientBlocking<'_>) -> Result<Self> {
Ok(Self {
charge_limit: dbus.proxies().charge().charge_control_end_threshold()?,
ac_power: dbus.proxies().charge().mains_online()?,
})
}
}
/// State stored from system daemons. This is shared with: tray, zbus notifications thread
/// and the GUI app thread.
pub struct SystemState {
#[derive(Debug, Clone)]
pub struct PageDataStates {
pub keyboard_layout: KeyLayout,
pub enabled_notifications: Arc<Mutex<EnabledNotifications>>,
pub notifs_enabled: Arc<AtomicBool>,
pub was_notified: Arc<AtomicBool>,
/// Because much of the app state here is the same as `RogBiosSupportedFunctions`
/// we can re-use that structure.
pub bios: BiosState,
@@ -271,102 +258,87 @@ pub struct SystemState {
pub anime: AnimeState,
pub profiles: ProfilesState,
pub fan_curves: FanCurvesState,
pub gfx_state: GfxState,
pub power_state: PowerState,
pub charge_limit: u8,
pub error: Option<String>,
/// Specific field for the tray only so that we can know when it does need update.
/// The tray should set this to false when done.
pub tray_should_update: bool,
pub app_should_update: bool,
pub asus_dbus: RogDbusClientBlocking<'static>,
pub gfx_dbus: GfxProxyBlocking<'static>,
}
impl SystemState {
/// Creates self, including the relevant dbus connections and proixies for internal use
#[allow(clippy::too_many_arguments)]
impl PageDataStates {
pub fn new(
keyboard_layout: KeyLayout,
enabled_notifications: Arc<Mutex<EnabledNotifications>>,
notifs_enabled: Arc<AtomicBool>,
charge_notified: Arc<AtomicBool>,
bios_notified: Arc<AtomicBool>,
aura_notified: Arc<AtomicBool>,
anime_notified: Arc<AtomicBool>,
profiles_notified: Arc<AtomicBool>,
fans_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<Self> {
let (asus_dbus, conn) = RogDbusClientBlocking::new()?;
let mut error = None;
let gfx_dbus = GfxProxyBlocking::new(&conn).expect("Couldn't connect to supergfxd");
Ok(Self {
keyboard_layout,
enabled_notifications,
power_state: PowerState::new(supported, &asus_dbus)
.map_err(|e| {
let e = format!("Could not get PowerState state: {e}");
error!("{e}");
error = Some(e);
})
.unwrap_or_default(),
bios: BiosState::new(supported, &asus_dbus)
.map_err(|e| {
let e = format!("Could not get BiosState state: {e}");
error!("{e}");
error = Some(e);
})
.unwrap_or_default(),
aura: AuraState::new(supported, &asus_dbus)
.map_err(|e| {
let e = format!("Could not get AuraState state: {e}");
error!("{e}");
error = Some(e);
})
.unwrap_or_default(),
anime: AnimeState::new(supported, &asus_dbus)
.map_err(|e| {
let e = format!("Could not get AanimeState state: {e}");
error!("{e}");
error = Some(e);
})
.unwrap_or_default(),
profiles: ProfilesState::new(supported, &asus_dbus)
.map_err(|e| {
let e = format!("Could not get ProfilesState state: {e}");
error!("{e}");
error = Some(e);
})
.unwrap_or_default(),
fan_curves: FanCurvesState::new(supported, &asus_dbus)
.map_err(|e| {
let e = format!("Could not get FanCurvesState state: {e}");
error!("{e}");
error = Some(e);
})
.unwrap_or_default(),
gfx_state: GfxState::new(supported, &gfx_dbus)
.map_err(|e| {
let e = format!("Could not get supergfxd state: {e}");
error!("{e}");
error = Some(e);
})
.unwrap_or_default(),
error,
tray_should_update: true,
app_should_update: true,
asus_dbus,
gfx_dbus,
notifs_enabled,
was_notified: charge_notified,
charge_limit: dbus.proxies().charge().charge_control_end_threshold()?,
bios: BiosState::new(bios_notified, supported, dbus)?,
aura: AuraState::new(aura_notified, supported, dbus)?,
anime: AnimeState::new(anime_notified, supported, dbus)?,
profiles: ProfilesState::new(profiles_notified, supported, dbus)?,
fan_curves: FanCurvesState::new(fans_notified, supported, dbus)?,
error: None,
})
}
pub fn set_notified(&mut self) {
self.tray_should_update = true;
self.app_should_update = true;
pub fn refresh_if_notfied(
&mut self,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<bool> {
let mut notified = false;
if self.was_notified.load(Ordering::SeqCst) {
self.charge_limit = dbus.proxies().charge().charge_control_end_threshold()?;
self.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
if self.aura.was_notified.load(Ordering::SeqCst) {
self.aura = AuraState::new(self.aura.was_notified.clone(), supported, dbus)?;
self.aura.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
if self.bios.was_notified.load(Ordering::SeqCst) {
self.bios = BiosState::new(self.bios.was_notified.clone(), supported, dbus)?;
self.bios.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
if self.profiles.was_notified.load(Ordering::SeqCst) {
self.profiles =
ProfilesState::new(self.profiles.was_notified.clone(), supported, dbus)?;
self.profiles.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
if self.fan_curves.was_notified.load(Ordering::SeqCst) {
self.fan_curves =
FanCurvesState::new(self.fan_curves.was_notified.clone(), supported, dbus)?;
self.fan_curves.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
Ok(notified)
}
}
impl Default for SystemState {
impl Default for PageDataStates {
fn default() -> Self {
let (asus_dbus, conn) = RogDbusClientBlocking::new().expect("Couldn't connect to asusd");
let gfx_dbus = GfxProxyBlocking::new(&conn).expect("Couldn't connect to supergfxd");
Self {
keyboard_layout: KeyLayout::ga401_layout(),
enabled_notifications: Default::default(),
notifs_enabled: Default::default(),
was_notified: Default::default(),
bios: BiosState {
was_notified: Default::default(),
post_sound: Default::default(),
dedicated_gfx: GpuMode::NotSupported,
panel_overdrive: Default::default(),
@@ -374,6 +346,7 @@ impl Default for SystemState {
egpu_enable: Default::default(),
},
aura: AuraState {
was_notified: Default::default(),
current_mode: AuraModeNum::Static,
modes: Default::default(),
enabled: AuraPowerDev {
@@ -387,36 +360,27 @@ impl Default for SystemState {
wave_blue: Default::default(),
},
anime: AnimeState {
was_notified: Default::default(),
bright: Default::default(),
boot: Default::default(),
awake: Default::default(),
sleep: Default::default(),
},
profiles: ProfilesState {
was_notified: Default::default(),
list: Default::default(),
current: Default::default(),
},
fan_curves: FanCurvesState {
was_notified: Default::default(),
show_curve: Default::default(),
show_graph: Default::default(),
enabled: Default::default(),
curves: Default::default(),
drag_delta: Default::default(),
},
gfx_state: GfxState {
has_supergfx: false,
mode: GfxMode::None,
power_status: GfxPower::Unknown,
},
power_state: PowerState {
charge_limit: 99,
ac_power: false,
},
charge_limit: Default::default(),
error: Default::default(),
tray_should_update: true,
app_should_update: true,
asus_dbus,
gfx_dbus,
}
}
}

View File

@@ -1,6 +1,6 @@
use crate::RogApp;
impl RogApp {
impl<'a> RogApp<'a> {
pub fn anime_page(&mut self, ctx: &egui::Context) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.label("In progress");

View File

@@ -4,15 +4,16 @@ use egui::Color32;
use rog_aura::{AuraEffect, AuraModeNum};
use crate::{
system_state::SystemState,
widgets::{aura_modes_group, keyboard},
RogApp,
};
impl RogApp {
pub fn aura_page(&mut self, states: &mut SystemState, ctx: &egui::Context) {
impl<'a> RogApp<'a> {
pub fn aura_page(&mut self, ctx: &egui::Context) {
let Self {
supported,
states,
asus_dbus: dbus,
oscillator1,
oscillator2,
oscillator3,
@@ -25,6 +26,7 @@ impl RogApp {
let blue = oscillator3.load(Ordering::SeqCst) as u32;
states.aura.nudge_wave(red as u8, green as u8, blue as u8);
// let osc = c.0 * 255 / osc;
// dbg!(osc);
let c1 = states
.aura
.modes
@@ -70,7 +72,7 @@ impl RogApp {
// TODO: animation of colour changes/periods/blending
egui::CentralPanel::default().show(ctx, |ui| {
aura_modes_group(supported, states, oscillator_freq, ui);
aura_modes_group(supported, states, oscillator_freq, dbus, ui);
keyboard(ui, &states.keyboard_layout, &mut states.aura, colour);
});

View File

@@ -1,5 +1,5 @@
use crate::{
system_state::{FanCurvesState, ProfilesState, SystemState},
page_states::{FanCurvesState, ProfilesState},
widgets::fan_graphs,
RogApp, RogDbusClientBlocking,
};
@@ -7,9 +7,14 @@ use egui::Ui;
use rog_platform::supported::SupportedFunctions;
use rog_profiles::Profile;
impl RogApp {
pub fn fan_curve_page(&mut self, states: &mut SystemState, ctx: &egui::Context) {
let Self { supported, .. } = self;
impl<'a> RogApp<'a> {
pub fn fan_curve_page(&mut self, ctx: &egui::Context) {
let Self {
supported,
states,
asus_dbus: dbus,
..
} = self;
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("Custom fan curves");
@@ -18,11 +23,11 @@ impl RogApp {
supported,
&mut states.profiles,
&mut states.fan_curves,
&states.asus_dbus, &mut states.error,
dbus, &mut states.error,
ui,
);
fan_graphs(supported, &mut states.fan_curves, &states.asus_dbus, &mut states.error, ui);
fan_graphs(supported, &mut states.profiles, &mut states.fan_curves, dbus, &mut states.error, ui);
});
}
@@ -30,7 +35,7 @@ impl RogApp {
supported: &SupportedFunctions,
profiles: &mut ProfilesState,
curves: &mut FanCurvesState,
dbus: &RogDbusClientBlocking<'_>,
dbus: &RogDbusClientBlocking,
do_error: &mut Option<String>,
ui: &mut Ui,
) {
@@ -62,7 +67,7 @@ impl RogApp {
};
profiles.list.sort();
for f in &profiles.list {
for f in profiles.list.iter() {
item(*f, curves, curves.enabled.contains(f));
}
});
@@ -71,7 +76,8 @@ impl RogApp {
let selected_profile = curves.show_curve;
let selected_pu = curves.show_graph;
match FanCurvesState::new(supported, dbus) {
let notif = curves.was_notified.clone();
match FanCurvesState::new(notif, supported, dbus) {
Ok(f) => *curves = f,
Err(e) => *do_error = Some(e.to_string()),
}

View File

@@ -1,15 +1,18 @@
use crate::{
system_state::SystemState,
widgets::{
anime_power_group, app_settings, aura_power_group, platform_profile, rog_bios_group,
},
RogApp,
};
impl RogApp {
pub fn system_page(&mut self, states: &mut SystemState, ctx: &egui::Context) {
impl<'a> RogApp<'a> {
pub fn system_page(&mut self, ctx: &egui::Context) {
let Self {
config, supported, ..
config,
supported,
states,
asus_dbus: dbus,
..
} = self;
egui::CentralPanel::default().show(ctx, |ui| {
@@ -24,24 +27,26 @@ impl RogApp {
/******************************************************/
ui.vertical(|ui| {
ui.separator();
if supported.platform_profile.platform_profile {
platform_profile(states, ui);
}
app_settings(config, states, ui);
});
ui.vertical(|ui| {
ui.separator();
aura_power_group(supported, states, ui);
if supported.platform_profile.platform_profile {
platform_profile(states, dbus, ui);
}
});
ui.end_row();
/******************************************************/
ui.vertical(|ui| {
ui.separator();
app_settings(config, states, ui);
aura_power_group(supported, states, dbus, ui);
});
ui.vertical(|ui| {
ui.separator();
rog_bios_group(supported, states, ui);
rog_bios_group(supported, states, dbus, ui);
});
ui.end_row();
@@ -49,7 +54,7 @@ impl RogApp {
ui.vertical(|ui| {
ui.separator();
if supported.anime_ctrl.0 {
anime_power_group(supported, states, ui);
anime_power_group(supported, states, dbus, ui);
}
});
ui.vertical(|ui| {

View File

@@ -1,558 +0,0 @@
//! A seld-contained tray icon with menus. The control of app<->tray is done via
//! commands over an MPSC channel.
use std::{
io::Write,
sync::{
atomic::{AtomicBool, Ordering},
mpsc::{channel, Receiver},
Arc, Mutex,
},
time::Duration,
};
use gtk::{gio::Icon, prelude::*};
use rog_dbus::zbus_platform::RogBiosProxyBlocking;
use rog_platform::{platform::GpuMode, supported::SupportedFunctions};
use crate::{error::Result, get_ipc_file, system_state::SystemState, SHOW_GUI};
use libappindicator::{AppIndicator, AppIndicatorStatus};
use supergfxctl::{
pci_device::{GfxMode, GfxPower},
zbus_proxy::DaemonProxyBlocking as GfxProxyBlocking,
};
use log::{debug, error, info, trace, warn};
const TRAY_APP_ICON: &str = "rog-control-center";
const TRAY_LABEL: &str = "ROG Control Center";
pub enum AppToTray {
DgpuStatus(GfxPower),
}
pub enum TrayToApp {
Open,
Quit,
}
pub struct RadioGroup(Vec<gtk::RadioMenuItem>);
impl RadioGroup {
/// Add new radio menu item. `set_no_show_all()` is true until added to menu
/// to prevent teh callback from running
pub fn new<F>(first_label: &str, cb: F) -> Self
where
F: Fn(&gtk::RadioMenuItem) + Send + 'static,
{
let item = gtk::RadioMenuItem::with_label(first_label);
item.set_active(false);
item.set_no_show_all(true);
item.connect_activate(move |this| {
if this.is_active() && !this.is_no_show_all() {
cb(this);
}
});
Self(vec![item])
}
/// Add new radio menu item. `set_no_show_all()` is true until added to menu
/// to prevent teh callback from running
pub fn add<F>(&mut self, label: &str, cb: F)
where
F: Fn(&gtk::RadioMenuItem) + Send + 'static,
{
debug_assert!(!self.0.is_empty());
let group = self.0[0].group();
let item = gtk::RadioMenuItem::with_label_from_widget(&group[0], Some(label));
item.set_active(false);
item.set_no_show_all(true);
item.connect_activate(move |this| {
if this.is_active() && !this.is_no_show_all() {
cb(this);
}
});
self.0.push(item);
}
}
pub struct ROGTray {
tray: AppIndicator,
menu: gtk::Menu,
icon: &'static str,
bios_proxy: RogBiosProxyBlocking<'static>,
gfx_proxy: GfxProxyBlocking<'static>,
}
impl ROGTray {
pub fn new() -> Result<Self> {
let conn = zbus::blocking::Connection::system().map_err(|e| {
error!("ROGTray: {e}");
e
})?;
let rog_tray = Self {
tray: AppIndicator::new(TRAY_LABEL, TRAY_APP_ICON),
menu: gtk::Menu::new(),
icon: TRAY_APP_ICON,
bios_proxy: RogBiosProxyBlocking::new(&conn).map_err(|e| {
error!("ROGTray: {e}");
e
})?,
gfx_proxy: GfxProxyBlocking::new(&conn).map_err(|e| {
error!("ROGTray: {e}");
e
})?,
};
Ok(rog_tray)
}
pub fn set_icon(&mut self, icon: &'static str) {
self.icon = icon;
self.tray.set_icon(self.icon);
self.tray.set_status(AppIndicatorStatus::Active);
}
/// Add a non-interactive label
fn add_inactive_label(&mut self, label: &str) {
let item = gtk::MenuItem::with_label(label);
item.set_sensitive(false);
self.menu.append(&item);
self.menu.show_all();
}
/// Add a separator
fn add_separator(&mut self) {
let item = gtk::SeparatorMenuItem::new();
item.set_sensitive(false);
self.menu.append(&item);
self.menu.show_all();
}
fn add_radio_sub_menu(
&mut self,
header_label: &str,
active_label: &str,
sub_menu: &RadioGroup,
) {
let header_item = gtk::MenuItem::with_label(header_label);
header_item.show_all();
self.menu.add(&header_item);
let menu = gtk::Menu::new();
for item in &sub_menu.0 {
if let Some(label) = item.label() {
item.set_active(label == active_label);
} else {
item.set_active(false);
}
item.set_no_show_all(false);
item.show_all();
menu.add(item);
}
menu.show_all();
header_item.set_submenu(Some(&menu));
}
fn _add_menu_item<F>(&mut self, label: &str, cb: F)
where
F: Fn() + Send + 'static,
{
let item = gtk::MenuItem::with_label(label);
item.connect_activate(move |_| {
cb();
});
self.menu.append(&item);
self.menu.show_all();
}
fn add_check_menu_item<F>(&mut self, label: &str, is_active: bool, cb: F)
where
F: Fn(&gtk::CheckMenuItem) + Send + 'static,
{
let item = gtk::CheckMenuItem::with_label(label);
item.set_active(is_active);
item.connect_activate(move |this| {
cb(this);
});
self.menu.append(&item);
self.menu.show_all();
}
/// Add a menu item with an icon to the right
fn add_icon_menu_item<F>(&mut self, label: &str, icon: &str, cb: F)
where
F: Fn() + Send + 'static,
{
let g_box = gtk::Box::new(gtk::Orientation::Horizontal, 6);
let icon = gtk::Image::from_gicon(&Icon::for_string(icon).unwrap(), gtk::IconSize::Menu);
let label = gtk::Label::new(Some(label));
let item = gtk::MenuItem::new();
g_box.add(&icon);
g_box.add(&label);
item.add(&g_box);
item.show_all();
item.connect_activate(move |_| {
cb();
});
self.menu.append(&item);
self.menu.show_all();
self.tray.set_menu(&mut self.menu);
}
fn _set_status(&mut self, status: AppIndicatorStatus) {
self.tray.set_status(status);
}
fn menu_add_base(&mut self) {
self.add_icon_menu_item("Open app", "asus_notif_red", move || {
if let Ok(mut ipc) = get_ipc_file().map_err(|e| {
error!("ROGTray: get_ipc_file: {}", e);
}) {
ipc.write_all(&[SHOW_GUI]).ok();
}
});
self.add_separator();
debug!("ROGTray: built base menu");
}
fn menu_add_charge_limit(&mut self, supported: &SupportedFunctions, limit: u8) {
if supported.charge_ctrl.charge_level_set {
self.add_inactive_label(&format!("Charge limit: {limit}"));
debug!("ROGTray: appended charge limit menu");
}
}
fn menu_add_panel_od(&mut self, supported: &SupportedFunctions, panel_od: bool) {
if supported.rog_bios_ctrl.panel_overdrive {
let bios = self.bios_proxy.clone();
self.add_check_menu_item("Panel Overdrive", panel_od, move |this| {
bios.set_panel_od(this.is_active())
.map_err(|e| {
error!("ROGTray: set_panel_od: {e}");
e
})
.ok();
});
debug!("ROGTray: appended panel overdrive menu");
}
}
fn menu_add_gpu(&mut self, supported: &SupportedFunctions, current_mode: GfxMode) {
let set_mux_off = Arc::new(AtomicBool::new(false));
let gfx_dbus = self.gfx_proxy.clone();
let set_mux_off1 = set_mux_off.clone();
let mut gpu_menu = RadioGroup::new("Integrated", move |_| {
let mode = gfx_dbus
.mode()
.map_err(|e| {
error!("ROGTray: mode: {e}");
e
})
.unwrap_or(GfxMode::None);
if mode != GfxMode::Integrated {
gfx_dbus
.set_mode(&GfxMode::Integrated)
.map_err(|e| {
error!("ROGTray: srt_mode: {e}");
e
})
.ok();
}
set_mux_off1.store(true, Ordering::Relaxed);
});
let gfx_dbus = self.gfx_proxy.clone();
let set_mux_off1 = set_mux_off.clone();
gpu_menu.add("Hybrid", move |_| {
let mode = gfx_dbus
.mode()
.map_err(|e| {
error!("ROGTray: mode: {e}");
e
})
.unwrap_or(GfxMode::None);
if mode != GfxMode::Hybrid {
gfx_dbus
.set_mode(&GfxMode::Hybrid)
.map_err(|e| {
error!("ROGTray: set_mode: {e}");
e
})
.ok();
}
set_mux_off1.store(true, Ordering::Relaxed);
});
if supported.rog_bios_ctrl.egpu_enable {
let set_mux_off1 = set_mux_off.clone();
let gfx_dbus = self.gfx_proxy.clone();
gpu_menu.add("eGPU", move |_| {
let mode = gfx_dbus
.mode()
.map_err(|e| {
error!("ROGTray: mode: {e}");
e
})
.unwrap_or(GfxMode::None);
if mode != GfxMode::Egpu {
gfx_dbus
.set_mode(&GfxMode::Egpu)
.map_err(|e| {
error!("ROGTray: set_mode: {e}");
e
})
.ok();
}
set_mux_off1.store(true, Ordering::Relaxed);
});
}
let mut reboot_required = false;
if supported.rog_bios_ctrl.gpu_mux {
let gfx_dbus = self.bios_proxy.clone();
gpu_menu.add("Ultimate (Reboot required)", move |_| {
let mode = gfx_dbus
.gpu_mux_mode()
.map_err(|e| {
error!("ROGTray: mode: {e}");
e
})
.unwrap_or(GpuMode::Error);
if mode != GpuMode::Discrete {
gfx_dbus
.set_gpu_mux_mode(GpuMode::Discrete)
.map_err(|e| {
error!("ROGTray: set_mode: {e}");
e
})
.ok();
}
});
if set_mux_off.load(Ordering::Relaxed) {
warn!("Selected non-dgpu mode, must set MUX to optimus");
self.bios_proxy
.set_gpu_mux_mode(GpuMode::Optimus)
.map_err(|e| {
error!("ROGTray: set_mode: {e}");
e
})
.ok();
}
if let Ok(mode) = self.bios_proxy.gpu_mux_mode() {
let mode = match mode {
GpuMode::Discrete => GfxMode::AsusMuxDiscreet,
_ => GfxMode::Hybrid,
};
reboot_required = mode != current_mode;
}
}
let active = match current_mode {
GfxMode::AsusMuxDiscreet => "Discreet".to_owned(),
_ => current_mode.to_string(),
};
let reboot_required = if reboot_required {
"(Reboot required)"
} else {
""
};
self.add_radio_sub_menu(
&format!("GPU Mode: {active} {reboot_required}"),
active.as_str(),
&gpu_menu,
);
debug!("ROGTray: appended gpu menu");
}
fn menu_add_mux(&mut self, current_mode: GfxMode) {
let gfx_dbus = self.bios_proxy.clone();
let mut reboot_required = false;
if let Ok(mode) = gfx_dbus.gpu_mux_mode() {
let mode = match mode {
GpuMode::Discrete => GfxMode::AsusMuxDiscreet,
_ => GfxMode::Hybrid,
};
reboot_required = mode != current_mode;
}
let mut gpu_menu = RadioGroup::new("Optimus", move |_| {
gfx_dbus
.set_gpu_mux_mode(GpuMode::Optimus)
.map_err(|e| {
error!("ROGTray: set_mode: {e}");
e
})
.ok();
debug!("Setting GPU mode: {}", GpuMode::Optimus);
});
let gfx_dbus = self.bios_proxy.clone();
gpu_menu.add("Ultimate", move |_| {
gfx_dbus
.set_gpu_mux_mode(GpuMode::Discrete)
.map_err(|e| {
error!("ROGTray: set_mode: {e}");
e
})
.ok();
debug!("Setting GPU mode: {}", GpuMode::Discrete);
});
let active = match current_mode {
GfxMode::AsusMuxDiscreet => "Ultimate".to_owned(),
GfxMode::Hybrid => "Optimus".to_owned(),
_ => current_mode.to_string(),
};
debug!("Current active GPU mode: {}", active);
let reboot_required = if reboot_required {
"(Reboot required)"
} else {
""
};
self.add_radio_sub_menu(
&format!("GPU Mode: {active} {reboot_required}"),
active.as_str(),
&gpu_menu,
);
debug!("ROGTray: appended gpu menu");
}
fn menu_clear(&mut self) {
self.menu = gtk::Menu::new();
debug!("ROGTray: cleared self");
}
/// Reset GTK menu to internal state, this can be called after clearing and rebuilding the menu too.
fn menu_update(&mut self) {
self.tray.set_menu(&mut self.menu);
self.set_icon(self.icon);
}
/// Do a flush, build, and update of the tray menu
fn rebuild_and_update(
&mut self,
supported: &SupportedFunctions,
has_supergfx: bool,
current_gfx_mode: GfxMode,
charge_limit: u8,
panel_od: bool,
) {
self.menu_clear();
self.menu_add_base();
self.menu_add_charge_limit(supported, charge_limit);
self.menu_add_panel_od(supported, panel_od);
if has_supergfx {
self.menu_add_gpu(supported, current_gfx_mode);
} else if supported.rog_bios_ctrl.gpu_mux {
self.menu_add_mux(current_gfx_mode);
}
self.menu_update();
}
}
pub fn init_tray(
supported: SupportedFunctions,
states: Arc<Mutex<SystemState>>,
) -> Receiver<TrayToApp> {
let (send, recv) = channel();
let _send = Arc::new(Mutex::new(send));
let has_supergfx = if let Ok(lock) = states.try_lock() {
lock.gfx_state.has_supergfx
} else {
false
};
std::thread::spawn(move || {
if gtk::init()
.map_err(|e| {
error!("ROGTray: gtk init {e}");
e
})
.is_err()
{
return;
} // Make this the main thread for gtk
debug!("init_tray gtk");
let mut tray = match ROGTray::new() {
Ok(t) => {
info!("init_tray: built menus");
t
}
Err(e) => {
error!("ROGTray: tray init {e}");
if let Ok(mut states) = states.lock() {
states.error = Some(format!("Could not start tray: {e}"));
}
return;
}
};
tray.rebuild_and_update(&supported, has_supergfx, GfxMode::Hybrid, 100, false);
tray.set_icon(TRAY_APP_ICON);
info!("Started ROGTray");
loop {
if let Ok(mut lock) = states.lock() {
if lock.tray_should_update {
// Supergfx ends up adding some complexity to handle if it isn't available
let current_gpu_mode = if lock.gfx_state.has_supergfx {
lock.gfx_state.mode
} else {
match lock.bios.dedicated_gfx {
GpuMode::Discrete => GfxMode::AsusMuxDiscreet,
_ => GfxMode::Hybrid,
}
};
tray.rebuild_and_update(
&supported,
has_supergfx,
current_gpu_mode,
lock.power_state.charge_limit,
lock.bios.panel_overdrive,
);
lock.tray_should_update = false;
debug!("ROGTray: rebuilt menus due to state change");
match lock.gfx_state.power_status {
GfxPower::Suspended => tray.set_icon("asus_notif_blue"),
GfxPower::Off => tray.set_icon("asus_notif_green"),
GfxPower::AsusDisabled => tray.set_icon("asus_notif_white"),
GfxPower::AsusMuxDiscreet | GfxPower::Active => {
tray.set_icon("asus_notif_red");
}
GfxPower::Unknown => {
if has_supergfx {
tray.set_icon("gpu-integrated");
} else {
tray.set_icon("asus_notif_red");
}
}
};
}
}
if gtk::events_pending() {
// This is blocking until any events are available
gtk::main_iteration();
continue;
}
// Don't spool at max speed if no gtk events
std::thread::sleep(Duration::from_millis(300));
trace!("Tray loop ticked");
}
});
recv
}

View File

@@ -1,555 +0,0 @@
//! `update_and_notify` is responsible for both notifications *and* updating stored statuses
//! about the system state. This is done through either direct, intoify, zbus notifications
//! or similar methods.
use crate::{config::Config, error::Result, system_state::SystemState};
use log::{error, info, trace, warn};
use notify_rust::{Hint, Notification, NotificationHandle, Urgency};
use rog_dbus::{
zbus_anime::AnimeProxy, zbus_led::LedProxy, zbus_platform::RogBiosProxy,
zbus_power::PowerProxy, zbus_profile::ProfileProxy,
};
use rog_platform::platform::GpuMode;
use rog_profiles::Profile;
use serde::{Deserialize, Serialize};
use std::{
fmt::Display,
process::Command,
sync::{Arc, Mutex},
time::Duration,
};
use supergfxctl::{pci_device::GfxPower, zbus_proxy::DaemonProxy as SuperProxy};
use tokio::time::sleep;
use zbus::export::futures_util::{future, StreamExt};
const NOTIF_HEADER: &str = "ROG Control";
static mut POWER_AC_CMD: Option<Command> = None;
static mut POWER_BAT_CMD: Option<Command> = None;
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(default)]
pub struct EnabledNotifications {
pub receive_notify_post_boot_sound: bool,
pub receive_notify_panel_od: bool,
pub receive_notify_dgpu_disable: bool,
pub receive_notify_egpu_enable: bool,
pub receive_notify_gpu_mux_mode: bool,
pub receive_notify_charge_control_end_threshold: bool,
pub receive_notify_mains_online: bool,
pub receive_notify_profile: bool,
pub receive_notify_led: bool,
/// Anime
pub receive_power_states: bool,
pub receive_notify_gfx: bool,
pub receive_notify_gfx_status: bool,
pub all_enabled: bool,
}
impl Default for EnabledNotifications {
fn default() -> Self {
Self {
receive_notify_post_boot_sound: false,
receive_notify_panel_od: true,
receive_notify_dgpu_disable: true,
receive_notify_egpu_enable: true,
receive_notify_gpu_mux_mode: true,
receive_notify_charge_control_end_threshold: true,
receive_notify_mains_online: false,
receive_notify_profile: true,
receive_notify_led: true,
receive_power_states: false,
receive_notify_gfx: false,
receive_notify_gfx_status: false,
all_enabled: false,
}
}
}
impl EnabledNotifications {
pub fn tokio_mutex(config: &Config) -> Arc<Mutex<Self>> {
Arc::new(Mutex::new(config.enabled_notifications.clone()))
}
}
macro_rules! notify {
($notifier:expr, $last_notif:ident) => {
if let Some(notif) = $last_notif.take() {
notif.close();
}
if let Ok(x) = $notifier {
$last_notif.replace(x);
}
};
}
// TODO: drop the macro and use generics plus closure
macro_rules! recv_notif {
($proxy:ident,
$signal:ident,
$last_notif:ident,
$notif_enabled:ident,
$page_states:ident,
($($args: tt)*),
($($out_arg:tt)+),
$msg:literal,
$notifier:ident) => {
let last_notif = $last_notif.clone();
let notifs_enabled1 = $notif_enabled.clone();
let page_states1 = $page_states.clone();
tokio::spawn(async move {
let conn = zbus::Connection::system().await.map_err(|e| {
log::error!("zbus signal: {}: {e}", stringify!($signal));
e
}).unwrap();
let proxy = $proxy::new(&conn).await.map_err(|e| {
log::error!("zbus signal: {}: {e}", stringify!($signal));
e
}).unwrap();
if let Ok(mut p) = proxy.$signal().await {
info!("Started zbus signal thread: {}", stringify!($signal));
while let Some(e) = p.next().await {
if let Ok(out) = e.args() {
if let Ok(config) = notifs_enabled1.lock() {
if config.all_enabled && config.$signal {
if let Ok(ref mut lock) = last_notif.lock() {
trace!("zbus signal {} locked last_notif", stringify!($signal));
notify!($notifier($msg, &out.$($out_arg)+()), lock);
}
}
}
if let Ok(mut lock) = page_states1.lock() {
lock.$($args)+ = *out.$($out_arg)+();
lock.set_notified();
}
}
}
};
});
};
}
type SharedHandle = Arc<Mutex<Option<NotificationHandle>>>;
pub fn start_notifications(
config: &Config,
page_states: Arc<Mutex<SystemState>>,
enabled_notifications: Arc<Mutex<EnabledNotifications>>,
) -> Result<()> {
let last_notification: SharedHandle = Arc::new(Mutex::new(None));
// Setup the AC/BAT commands that will run on poweer status change
unsafe {
let prog: Vec<&str> = config.ac_command.split_whitespace().collect();
if prog.len() > 1 {
let mut cmd = Command::new(prog[0]);
for arg in prog.iter().skip(1) {
cmd.arg(*arg);
}
POWER_AC_CMD = Some(cmd);
}
}
unsafe {
let prog: Vec<&str> = config.bat_command.split_whitespace().collect();
if prog.len() > 1 {
let mut cmd = Command::new(prog[0]);
for arg in prog.iter().skip(1) {
cmd.arg(*arg);
}
POWER_BAT_CMD = Some(cmd);
}
}
// BIOS notif
recv_notif!(
RogBiosProxy,
receive_notify_post_boot_sound,
last_notification,
enabled_notifications,
page_states,
(bios.post_sound),
(on),
"BIOS Post sound",
do_notification
);
recv_notif!(
RogBiosProxy,
receive_notify_panel_od,
last_notification,
enabled_notifications,
page_states,
(bios.panel_overdrive),
(overdrive),
"Panel Overdrive enabled:",
do_notification
);
recv_notif!(
RogBiosProxy,
receive_notify_dgpu_disable,
last_notification,
enabled_notifications,
page_states,
(bios.dgpu_disable),
(disable),
"BIOS dGPU disabled",
do_notification
);
recv_notif!(
RogBiosProxy,
receive_notify_egpu_enable,
last_notification,
enabled_notifications,
page_states,
(bios.egpu_enable),
(enable),
"BIOS eGPU enabled",
do_notification
);
// Charge notif
recv_notif!(
PowerProxy,
receive_notify_charge_control_end_threshold,
last_notification,
enabled_notifications,
page_states,
(power_state.charge_limit),
(limit),
"Battery charge limit changed to",
do_notification
);
recv_notif!(
PowerProxy,
receive_notify_mains_online,
last_notification,
enabled_notifications,
page_states,
(power_state.ac_power),
(on),
"AC Power power is",
ac_power_notification
);
// Profile notif
recv_notif!(
ProfileProxy,
receive_notify_profile,
last_notification,
enabled_notifications,
page_states,
(profiles.current),
(profile),
"Profile changed to",
do_thermal_notif
);
// notify!(do_thermal_notif(&out.profile), lock);
// LED notif
recv_notif!(
LedProxy,
receive_notify_led,
last_notification,
enabled_notifications,
page_states,
(aura.current_mode),
(data.mode),
"Keyboard LED mode changed to",
do_notification
);
let page_states1 = page_states.clone();
tokio::spawn(async move {
let conn = zbus::Connection::system()
.await
.map_err(|e| {
error!("zbus signal: receive_power_states: {e}");
e
})
.unwrap();
let proxy = AnimeProxy::new(&conn)
.await
.map_err(|e| {
error!("zbus signal: receive_power_states: {e}");
e
})
.unwrap();
if let Ok(p) = proxy.receive_power_states().await {
info!("Started zbus signal thread: receive_power_states");
p.for_each(|_| {
if let Ok(_lock) = page_states1.lock() {
// TODO: lock.anime.
}
future::ready(())
})
.await;
};
});
let page_states1 = page_states.clone();
let last_notification1 = last_notification.clone();
tokio::spawn(async move {
let conn = zbus::Connection::system()
.await
.map_err(|e| {
error!("zbus signal: receive_notify_gpu_mux_mode: {e}");
e
})
.unwrap();
let proxy = RogBiosProxy::new(&conn)
.await
.map_err(|e| {
error!("zbus signal: receive_notify_gpu_mux_mode: {e}");
e
})
.unwrap();
let mut actual_mux_mode = GpuMode::Error;
if let Ok(mode) = proxy.gpu_mux_mode().await {
actual_mux_mode = mode;
}
if let Ok(mut p) = proxy.receive_notify_gpu_mux_mode().await {
info!("Started zbus signal thread: receive_power_states");
while let Some(e) = p.next().await {
if let Ok(out) = e.args() {
if out.mode == actual_mux_mode {
continue;
}
if let Ok(mut lock) = page_states1.lock() {
lock.bios.dedicated_gfx = out.mode;
lock.set_notified();
}
if let Ok(ref mut lock) = last_notification1.lock() {
if let Some(notif) = lock.take() {
notif.close();
}
}
do_mux_notification("Reboot required. BIOS GPU MUX mode set to", &out.mode)
.ok();
}
}
};
});
if let Ok(lock) = page_states.try_lock() {
use supergfxctl::pci_device::Device;
let dev = Device::find().unwrap_or_default();
let mut found_dgpu = false; // just for logging
for dev in dev {
if dev.is_dgpu() {
let notifs_enabled1 = enabled_notifications.clone();
let last_notif = last_notification.clone();
let page_states1 = page_states.clone();
// Plain old thread is perfectly fine since most of this is potentially blocking
tokio::spawn(async move {
let mut last_status = GfxPower::Unknown;
loop {
if let Ok(status) = dev.get_runtime_status() {
if status != GfxPower::Unknown && status != last_status {
if let Ok(config) = notifs_enabled1.lock() {
if config.all_enabled && config.receive_notify_gfx_status {
// Required check because status cycles through active/unknown/suspended
if let Ok(ref mut lock) = last_notif.lock() {
notify!(
do_gpu_status_notif(
"dGPU status changed:",
&status
),
lock
);
}
}
}
if let Ok(mut lock) = page_states1.lock() {
lock.gfx_state.power_status = status;
lock.set_notified();
}
last_status = status;
}
}
sleep(Duration::from_millis(500)).await;
}
});
found_dgpu = true;
break;
}
}
if !found_dgpu {
warn!("Did not find a dGPU on this system, dGPU status won't be avilable");
}
if lock.gfx_state.has_supergfx {
recv_notif!(
SuperProxy,
receive_notify_gfx,
last_notification,
enabled_notifications,
page_states,
(gfx_state.mode),
(mode),
"Gfx mode changed to",
do_notification
);
tokio::spawn(async move {
let conn = zbus::Connection::system()
.await
.map_err(|e| {
error!("zbus signal: receive_notify_action: {e}");
e
})
.unwrap();
let proxy = SuperProxy::new(&conn)
.await
.map_err(|e| {
error!("zbus signal: receive_notify_action: {e}");
e
})
.unwrap();
if let Ok(mut p) = proxy.receive_notify_action().await {
info!("Started zbus signal thread: receive_notify_action");
while let Some(e) = p.next().await {
if let Ok(out) = e.args() {
let action = out.action();
do_gfx_action_notif(
"Gfx mode change requires",
&format!("{action:?}",),
)
.map_err(|e| {
error!("zbus signal: do_gfx_action_notif: {e}");
e
})
.unwrap();
}
}
};
});
}
}
Ok(())
}
fn base_notification<T>(message: &str, data: &T) -> Notification
where
T: Display,
{
let mut notif = Notification::new();
notif
.summary(NOTIF_HEADER)
.body(&format!("{message} {data}"))
.timeout(2000)
//.hint(Hint::Resident(true))
.hint(Hint::Category("device".into()));
notif
}
fn do_notification<T>(message: &str, data: &T) -> Result<NotificationHandle>
where
T: Display,
{
Ok(base_notification(message, data).show()?)
}
fn ac_power_notification(message: &str, on: &bool) -> Result<NotificationHandle> {
let data = if *on {
unsafe {
if let Some(cmd) = POWER_AC_CMD.as_mut() {
if let Err(e) = cmd.spawn() {
error!("AC power command error: {e}");
}
}
}
"plugged".to_owned()
} else {
unsafe {
if let Some(cmd) = POWER_BAT_CMD.as_mut() {
if let Err(e) = cmd.spawn() {
error!("Battery power command error: {e}");
}
}
}
"unplugged".to_owned()
};
Ok(base_notification(message, &data).show()?)
}
fn do_thermal_notif(message: &str, profile: &Profile) -> Result<NotificationHandle> {
let icon = match profile {
Profile::Balanced => "asus_notif_yellow",
Profile::Performance => "asus_notif_red",
Profile::Quiet => "asus_notif_green",
};
let profile: &str = (*profile).into();
let mut notif = base_notification(message, &profile.to_uppercase());
Ok(notif.icon(icon).show()?)
}
fn do_gpu_status_notif(message: &str, data: &GfxPower) -> Result<NotificationHandle> {
// eww
let mut notif = base_notification(message, &<&str>::from(data).to_owned());
let icon = match data {
GfxPower::Suspended => "asus_notif_blue",
GfxPower::Off => "asus_notif_green",
GfxPower::AsusDisabled => "asus_notif_white",
GfxPower::AsusMuxDiscreet | GfxPower::Active => "asus_notif_red",
GfxPower::Unknown => "gpu-integrated",
};
notif.icon(icon);
Ok(Notification::show(&notif)?)
}
fn do_gfx_action_notif<T>(message: &str, data: &T) -> Result<()>
where
T: Display,
{
let mut notif = base_notification(message, data);
notif.action("gnome-session-quit", "Logout");
notif.urgency(Urgency::Critical);
notif.timeout(3000);
notif.icon("dialog-warning");
notif.hint(Hint::Transient(true));
let handle = notif.show()?;
handle.wait_for_action(|id| {
if id == "gnome-session-quit" {
let mut cmd = Command::new("gnome-session-quit");
cmd.spawn().ok();
} else if id == "__closed" {
// TODO: cancel the switching
}
});
Ok(())
}
/// Actual `GpuMode` unused as data is never correct until switched by reboot
fn do_mux_notification(message: &str, m: &GpuMode) -> Result<()> {
let mut notif = base_notification(message, &m.to_string());
notif.action("gnome-session-quit", "Reboot");
notif.urgency(Urgency::Critical);
notif.icon("system-reboot-symbolic");
notif.hint(Hint::Transient(true));
let handle = notif.show()?;
std::thread::spawn(|| {
handle.wait_for_action(|id| {
if id == "gnome-session-quit" {
let mut cmd = Command::new("gnome-session-quit");
cmd.arg("--reboot");
cmd.spawn().ok();
} else if id == "__closed" {
// TODO: cancel the switching
}
})
});
Ok(())
}

View File

@@ -1,9 +1,14 @@
use egui::{RichText, Ui};
use rog_platform::supported::SupportedFunctions;
use crate::system_state::SystemState;
use crate::{page_states::PageDataStates, RogDbusClientBlocking};
pub fn anime_power_group(_supported: &SupportedFunctions, states: &mut SystemState, ui: &mut Ui) {
pub fn anime_power_group(
_supported: &SupportedFunctions,
states: &mut PageDataStates,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
ui.heading("AniMe Matrix Settings");
ui.label("Options are incomplete. Awake + Boot should work");
@@ -38,9 +43,7 @@ pub fn anime_power_group(_supported: &SupportedFunctions, states: &mut SystemSta
});
ui.horizontal_wrapped(|ui| {
if ui.checkbox(&mut states.anime.boot, "Enable").changed() {
states
.asus_dbus
.proxies()
dbus.proxies()
.anime()
.set_boot_on_off(states.anime.boot)
.map_err(|err| {
@@ -51,9 +54,7 @@ pub fn anime_power_group(_supported: &SupportedFunctions, states: &mut SystemSta
});
ui.horizontal_wrapped(|ui| {
if ui.checkbox(&mut states.anime.awake, "Enable").changed() {
states
.asus_dbus
.proxies()
dbus.proxies()
.anime()
.set_on_off(states.anime.awake)
.map_err(|err| {

View File

@@ -1,98 +1,31 @@
use std::sync::atomic::Ordering;
use egui::Ui;
use crate::{config::Config, system_state::SystemState};
use crate::{config::Config, page_states::PageDataStates};
pub fn app_settings(config: &mut Config, states: &mut SystemState, ui: &mut Ui) {
pub fn app_settings(config: &mut Config, states: &mut PageDataStates, ui: &mut Ui) {
ui.heading("ROG GUI Settings");
// ui.label("Options are incomplete. Awake + Boot should work");
let mut enabled_notifications = if let Ok(lock) = states.enabled_notifications.lock() {
lock.clone()
} else {
Default::default()
};
ui.label("Application settings");
let app_changed = ui
if ui
.checkbox(&mut config.run_in_background, "Run in Background")
.clicked()
|| ui
.checkbox(&mut config.startup_in_background, "Startup Hidden")
.clicked()
|| ui
.checkbox(
&mut enabled_notifications.all_enabled,
"Enable Notifications",
)
.clicked();
ui.label("Notification settings");
let notif_changed = ui
.checkbox(
&mut enabled_notifications.receive_notify_gfx_status,
"Enable dGPU status notification",
)
.clicked()
|| ui
.checkbox(
&mut enabled_notifications.receive_notify_led,
"Enable LED mode change notification",
)
.checkbox(&mut config.enable_notifications, "Enable Notifications")
.clicked()
|| ui
.checkbox(
&mut enabled_notifications.receive_notify_dgpu_disable,
"Enable dGPU disablement notification",
)
.clicked()
|| ui
.checkbox(
&mut enabled_notifications.receive_notify_egpu_enable,
"Enable eGPU enablement notification",
)
.clicked()
|| ui
.checkbox(
&mut enabled_notifications.receive_notify_mains_online,
"Enable mains (AC) power notification",
)
.clicked()
|| ui
.checkbox(
&mut enabled_notifications.receive_notify_charge_control_end_threshold,
"Enable charge threshold notification",
)
.clicked()
|| ui
.checkbox(
&mut enabled_notifications.receive_notify_profile,
"Enable profile change notification",
)
.clicked()
|| ui
.checkbox(
&mut enabled_notifications.receive_notify_panel_od,
"Enable panel overdrive notification",
)
.clicked()
|| ui
.checkbox(
&mut enabled_notifications.receive_notify_post_boot_sound,
"Enable BIOS post sound notification",
)
.clicked();
if app_changed || notif_changed {
if let Ok(mut lock) = states.enabled_notifications.lock() {
// Replace inner content before save
*lock = enabled_notifications;
config
.save(&lock)
.map_err(|err| {
states.error = Some(err.to_string());
})
.ok();
}
{
states
.notifs_enabled
.store(config.enable_notifications, Ordering::SeqCst);
config
.save()
.map_err(|err| {
states.error = Some(err.to_string());
})
.ok();
}
}

View File

@@ -7,12 +7,16 @@ use egui::{RichText, Ui};
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour, Speed};
use rog_platform::supported::SupportedFunctions;
use crate::system_state::{AuraState, SystemState};
use crate::{
page_states::{AuraState, PageDataStates},
RogDbusClientBlocking,
};
pub fn aura_modes_group(
supported: &SupportedFunctions,
states: &mut SystemState,
states: &mut PageDataStates,
freq: &mut Arc<AtomicU8>,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
let mut changed = false;
@@ -168,7 +172,8 @@ pub fn aura_modes_group(
ui.separator();
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
if ui.add(egui::Button::new("Cancel")).clicked() {
match AuraState::new(supported, &states.asus_dbus) {
let notif = states.aura.was_notified.clone();
match AuraState::new(notif, supported, dbus) {
Ok(a) => states.aura.modes = a.modes,
Err(e) => states.error = Some(e.to_string()),
}
@@ -197,9 +202,7 @@ pub fn aura_modes_group(
if changed {
states.aura.current_mode = selected;
states
.asus_dbus
.proxies()
dbus.proxies()
.led()
.set_led_mode(states.aura.modes.get(&selected).unwrap())
.map_err(|err| {

View File

@@ -5,22 +5,32 @@ use rog_aura::{
};
use rog_platform::supported::SupportedFunctions;
use crate::system_state::SystemState;
use crate::{page_states::PageDataStates, RogDbusClientBlocking};
pub fn aura_power_group(supported: &SupportedFunctions, states: &mut SystemState, ui: &mut Ui) {
pub fn aura_power_group(
supported: &SupportedFunctions,
states: &mut PageDataStates,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
ui.heading("LED settings");
match supported.keyboard_led.prod_id {
AuraDevice::X1854 | AuraDevice::X1869 | AuraDevice::X1866 => {
aura_power1(supported, states, ui);
aura_power1(supported, states, dbus, ui)
}
AuraDevice::X19B6 => aura_power2(supported, states, ui),
AuraDevice::Tuf => aura_power1(supported, states, ui),
AuraDevice::X19B6 => aura_power2(supported, states, dbus, ui),
AuraDevice::Tuf => aura_power1(supported, states, dbus, ui),
AuraDevice::Unknown => {}
}
}
fn aura_power1(supported: &SupportedFunctions, states: &mut SystemState, ui: &mut Ui) {
fn aura_power1(
supported: &SupportedFunctions,
states: &mut PageDataStates,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
let enabled_states = &mut states.aura.enabled;
let mut boot = enabled_states.x1866.contains(&AuraDev1866::Boot);
let mut sleep = enabled_states.x1866.contains(&AuraDev1866::Sleep);
@@ -134,9 +144,7 @@ fn aura_power1(supported: &SupportedFunctions, states: &mut SystemState, ui: &mu
x19b6: vec![],
};
// build data to send
states
.asus_dbus
.proxies()
dbus.proxies()
.led()
.set_leds_power(options, enable)
.map_err(|err| {
@@ -185,9 +193,7 @@ fn aura_power1(supported: &SupportedFunctions, states: &mut SystemState, ui: &mu
x19b6: vec![],
};
// build data to send
states
.asus_dbus
.proxies()
dbus.proxies()
.led()
.set_leds_power(options, enable)
.map_err(|err| {
@@ -201,7 +207,12 @@ fn aura_power1(supported: &SupportedFunctions, states: &mut SystemState, ui: &mu
}
}
fn aura_power2(supported: &SupportedFunctions, states: &mut SystemState, ui: &mut Ui) {
fn aura_power2(
supported: &SupportedFunctions,
states: &mut PageDataStates,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
let enabled_states = &mut states.aura.enabled;
let has_logo = supported
.keyboard_led
@@ -320,9 +331,7 @@ fn aura_power2(supported: &SupportedFunctions, states: &mut SystemState, ui: &mu
x19b6: data,
};
// build data to send
states
.asus_dbus
.proxies()
dbus.proxies()
.led()
.set_leds_power(options, enable)
.map_err(|err| {

View File

@@ -2,12 +2,16 @@ use egui::{plot::Points, Ui};
use rog_platform::supported::SupportedFunctions;
use rog_profiles::{FanCurvePU, Profile};
use crate::{system_state::FanCurvesState, RogDbusClientBlocking};
use crate::{
page_states::{FanCurvesState, ProfilesState},
RogDbusClientBlocking,
};
pub fn fan_graphs(
supported: &SupportedFunctions,
profiles: &mut ProfilesState,
curves: &mut FanCurvesState,
dbus: &RogDbusClientBlocking<'_>,
dbus: &RogDbusClientBlocking,
do_error: &mut Option<String>,
ui: &mut Ui,
) {
@@ -32,14 +36,14 @@ pub fn fan_graphs(
};
ui.horizontal_wrapped(|ui| {
for a in &curves.curves {
for a in curves.curves.iter() {
item(*a.0, ui);
}
});
let curve = curves.curves.get_mut(&curves.show_curve).unwrap();
use egui::plot::{Line, Plot};
use egui::plot::{Line, Plot, PlotPoints};
let data = if curves.show_graph == FanCurvePU::CPU {
&mut curve.cpu
@@ -47,40 +51,14 @@ pub fn fan_graphs(
&mut curve.gpu
};
let mut points: Vec<[f64; 2]> = data
.temp
.iter()
.enumerate()
.map(|(idx, x)| {
let x = *x as f64;
let y = ((data.pwm[idx] as u32) * 100 / 255) as f64;
[x, y]
})
.collect();
let points = data.temp.iter().enumerate().map(|(idx, x)| {
let x = *x as f64;
let y = ((data.pwm[idx] as u32) * 100 / 255) as f64;
[x, y]
});
for i in 0..points.len() - 1 {
if i > 0 && i < points.len() - 1 {
if points[i][0] < points[i - 1][0] {
points[i][0] = points[i - 1][0] + 1.0;
data.temp[i] = points[i - 1][0] as u8;
}
if points[i][0] >= points[i + 1][0] {
points[i + 1][0] = points[i][0] + 1.0;
data.temp[i + 1] = points[i][0] as u8;
}
if points[i][1] < points[i - 1][1] {
points[i][1] = points[i - 1][1] + 1.0;
data.pwm[i] = (points[i - 1][1] * 255.0 / 100.0 + 1.0).floor() as u8;
}
if points[i][1] >= points[i + 1][1] {
points[i + 1][1] = points[i][1] + 1.0;
data.pwm[i + 1] = (points[i][1] * 255.0 / 100.0 + 1.0).floor() as u8;
}
}
}
let line = Line::new(points.clone()).width(2.0);
let points = Points::new(points).radius(3.0);
let line = Line::new(PlotPoints::from_iter(points.clone())).width(2.0);
let points = Points::new(PlotPoints::from_iter(points)).radius(3.0);
Plot::new("fan_curves")
.view_aspect(1.666)
@@ -107,7 +85,7 @@ pub fn fan_graphs(
let mut idx = 0;
if let Some(point) = plot_ui.pointer_coordinate() {
let mut x: i32 = point.x as i32;
let mut x: i32 = 255;
for (i, n) in data.temp.iter().enumerate() {
let tmp = x.min((point.x as i32 - *n as i32).abs());
if tmp < x {
@@ -129,53 +107,37 @@ pub fn fan_graphs(
}
}
plot_ui.line(line);
plot_ui.points(points);
plot_ui.points(points)
});
let mut set = false;
let mut clear = false;
let mut reset = false;
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
set = ui.add(egui::Button::new("Apply Profile")).clicked();
clear = ui.add(egui::Button::new("Clear Profile Changes")).clicked();
reset = ui.add(egui::Button::new("Factory Reset Profile")).clicked();
set = ui.add(egui::Button::new("Apply Fan-curve")).clicked();
reset = ui.add(egui::Button::new("Reset Profile")).clicked();
});
if set {
dbus.proxies()
.profile()
.set_fan_curve(curves.show_curve, data.clone())
.set_fan_curve(profiles.current, data.clone())
.map_err(|err| {
*do_error = Some(err.to_string());
})
.ok();
}
if clear {
if let Ok(curve) = dbus
.proxies()
.profile()
.fan_curve_data(curves.show_curve)
.map_err(|err| {
*do_error = Some(err.to_string());
})
{
if let Some(value) = curves.curves.get_mut(&curves.show_curve) {
*value = curve;
}
}
}
if reset {
dbus.proxies()
.profile()
.reset_profile_curves(curves.show_curve)
.reset_profile_curves(profiles.current)
.map_err(|err| {
*do_error = Some(err.to_string());
})
.ok();
match FanCurvesState::new(supported, dbus) {
let notif = curves.was_notified.clone();
match FanCurvesState::new(notif, supported, dbus) {
Ok(f) => *curves = f,
Err(e) => *do_error = Some(e.to_string()),
}

View File

@@ -1,7 +1,7 @@
use egui::{Align, Color32, Vec2};
use rog_aura::{keys::KeyShape, layouts::KeyLayout, AuraModeNum};
use crate::system_state::AuraState;
use crate::page_states::AuraState;
pub fn keyboard(
ui: &mut egui::Ui,

View File

@@ -1,9 +1,9 @@
use crate::system_state::SystemState;
use crate::{page_states::PageDataStates, RogDbusClientBlocking};
use egui::Ui;
use rog_platform::{platform::GpuMode, supported::SupportedFunctions};
use rog_profiles::Profile;
pub fn platform_profile(states: &mut SystemState, ui: &mut Ui) {
pub fn platform_profile(states: &mut PageDataStates, dbus: &RogDbusClientBlocking, ui: &mut Ui) {
ui.heading("Platform profile");
let mut changed = false;
@@ -17,15 +17,13 @@ pub fn platform_profile(states: &mut SystemState, ui: &mut Ui) {
};
ui.horizontal_wrapped(|ui| {
for a in &states.profiles.list {
for a in states.profiles.list.iter() {
item(*a, ui);
}
});
if changed {
states
.asus_dbus
.proxies()
dbus.proxies()
.profile()
.set_active_profile(states.profiles.current)
.map_err(|err| {
@@ -35,18 +33,21 @@ pub fn platform_profile(states: &mut SystemState, ui: &mut Ui) {
};
}
pub fn rog_bios_group(supported: &SupportedFunctions, states: &mut SystemState, ui: &mut Ui) {
pub fn rog_bios_group(
supported: &SupportedFunctions,
states: &mut PageDataStates,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
ui.heading("Bios options");
let slider = egui::Slider::new(&mut states.power_state.charge_limit, 20..=100)
let slider = egui::Slider::new(&mut states.charge_limit, 20..=100)
.text("Charging limit")
.step_by(1.0);
if ui.add(slider).drag_released() {
states
.asus_dbus
.proxies()
dbus.proxies()
.charge()
.set_charge_control_end_threshold(states.power_state.charge_limit)
.set_charge_control_end_threshold(states.charge_limit as u8)
.map_err(|err| {
states.error = Some(err.to_string());
})
@@ -61,9 +62,7 @@ pub fn rog_bios_group(supported: &SupportedFunctions, states: &mut SystemState,
))
.changed()
{
states
.asus_dbus
.proxies()
dbus.proxies()
.rog_bios()
.set_post_boot_sound(states.bios.post_sound)
.map_err(|err| {
@@ -80,9 +79,7 @@ pub fn rog_bios_group(supported: &SupportedFunctions, states: &mut SystemState,
))
.changed()
{
states
.asus_dbus
.proxies()
dbus.proxies()
.rog_bios()
.set_panel_od(states.bios.panel_overdrive)
.map_err(|err| {
@@ -93,13 +90,6 @@ pub fn rog_bios_group(supported: &SupportedFunctions, states: &mut SystemState,
if supported.rog_bios_ctrl.gpu_mux {
let mut changed = false;
let mut dedicated_gfx = states.bios.dedicated_gfx;
let mut reboot_required = false;
if let Ok(mode) = states.asus_dbus.proxies().rog_bios().gpu_mux_mode() {
reboot_required = mode != states.bios.dedicated_gfx;
}
ui.group(|ui| {
ui.vertical(|ui| {
ui.horizontal_wrapped(|ui| ui.label("GPU MUX mode"));
@@ -107,32 +97,26 @@ pub fn rog_bios_group(supported: &SupportedFunctions, states: &mut SystemState,
ui.horizontal_wrapped(|ui| {
changed = ui
.selectable_value(
&mut dedicated_gfx,
&mut states.bios.dedicated_gfx,
GpuMode::Discrete,
"Dedicated (Ultimate)",
)
.clicked()
|| ui
.selectable_value(
&mut dedicated_gfx,
&mut states.bios.dedicated_gfx,
GpuMode::Optimus,
"Optimus (Hybrid)",
)
.clicked();
});
if reboot_required {
ui.horizontal_wrapped(|ui| ui.heading("REBOOT REQUIRED"));
}
});
});
if changed {
states
.asus_dbus
.proxies()
dbus.proxies()
.rog_bios()
.set_gpu_mux_mode(dedicated_gfx)
.set_gpu_mux_mode(states.bios.dedicated_gfx)
.map_err(|err| {
states.error = Some(err.to_string());
})

View File

@@ -1,6 +1,6 @@
use crate::{Page, RogApp};
impl RogApp {
impl<'a> RogApp<'a> {
pub fn side_panel(&mut self, ctx: &egui::Context) {
egui::SidePanel::left("side_panel")
.resizable(false)

View File

@@ -2,7 +2,7 @@ use egui::{vec2, Align2, FontId, Id, Sense};
use crate::{RogApp, VERSION};
impl RogApp {
impl<'a> RogApp<'a> {
pub fn top_bar(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
// The top panel is often a good place for a menu bar:

View File

@@ -15,3 +15,5 @@ rog_aura = { path = "../rog-aura" }
rog_profiles = { path = "../rog-profiles" }
rog_platform = { path = "../rog-platform" }
zbus.workspace = true
zbus_macros.workspace = true
zvariant.workspace = true

View File

@@ -81,7 +81,7 @@ impl<'a> RogDbusClientBlocking<'a> {
Ok((RogDbusClientBlocking { proxies }, conn))
}
pub fn proxies(&self) -> &DbusProxiesBlocking<'_> {
pub fn proxies(&self) -> &DbusProxiesBlocking {
&self.proxies
}
}
@@ -150,7 +150,7 @@ impl<'a> RogDbusClient<'a> {
Ok((RogDbusClient { proxies }, conn))
}
pub fn proxies(&self) -> &DbusProxies<'_> {
pub fn proxies(&self) -> &DbusProxies {
&self.proxies
}
}

View File

@@ -1,5 +1,5 @@
use rog_anime::{AnimeDataBuffer, AnimePowerStates};
use zbus::dbus_proxy;
use zbus_macros::dbus_proxy;
#[dbus_proxy(
interface = "org.asuslinux.Daemon",
@@ -7,26 +7,33 @@ use zbus::dbus_proxy;
)]
trait Anime {
/// Set whether the AniMe will show boot, suspend, or off animations
#[inline]
fn set_boot_on_off(&self, status: bool) -> zbus::Result<()>;
/// Set the global AniMe brightness
#[inline]
fn set_brightness(&self, bright: f32) -> zbus::Result<()>;
/// Set whether the AniMe is displaying images/data
#[inline]
fn set_on_off(&self, status: bool) -> zbus::Result<()>;
/// Writes a data stream of length. Will force system thread to exit until it is restarted
#[inline]
fn write(&self, input: AnimeDataBuffer) -> zbus::Result<()>;
/// Get status of if the AniMe LEDs are on
#[inline]
#[dbus_proxy(property)]
fn awake_enabled(&self) -> zbus::Result<bool>;
/// Get the status of if factory system-status animations are enabled
#[inline]
#[dbus_proxy(property)]
fn boot_enabled(&self) -> zbus::Result<bool>;
/// Notify listeners of the status of AniMe LED power and factory system-status animations
#[inline]
#[dbus_proxy(signal)]
fn power_states(&self, data: AnimePowerStates) -> zbus::Result<()>;
}

View File

@@ -21,8 +21,8 @@
use std::collections::BTreeMap;
use zbus::dbus_proxy;
use zbus::{blocking::Connection, Result};
use zbus_macros::dbus_proxy;
use rog_aura::{usb::AuraPowerDev, AuraEffect, AuraModeNum, LedBrightness, PerKeyRaw};
@@ -34,46 +34,60 @@ const BLOCKING_TIME: u64 = 40; // 100ms = 10 FPS, max 50ms = 20 FPS, 40ms = 25 F
)]
trait Led {
/// NextLedMode method
#[inline]
fn next_led_mode(&self) -> zbus::Result<()>;
/// PrevLedMode method
#[inline]
fn prev_led_mode(&self) -> zbus::Result<()>;
/// Toggle to next led brightness
#[inline]
fn next_led_brightness(&self) -> zbus::Result<()>;
/// Toggle to previous led brightness
#[inline]
fn prev_led_brightness(&self) -> zbus::Result<()>;
/// SetBrightness method
#[inline]
fn set_brightness(&self, brightness: LedBrightness) -> zbus::Result<()>;
/// SetLedMode method
#[inline]
fn set_led_mode(&self, effect: &AuraEffect) -> zbus::Result<()>;
#[inline]
fn set_leds_power(&self, options: AuraPowerDev, enabled: bool) -> zbus::Result<()>;
#[inline]
fn per_key_raw(&self, data: PerKeyRaw) -> zbus::fdo::Result<()>;
/// NotifyLed signal
#[inline]
#[dbus_proxy(signal)]
fn notify_led(&self, data: AuraEffect) -> zbus::Result<()>;
#[dbus_proxy(signal)]
#[inline]
fn notify_power_states(&self, data: AuraPowerDev) -> zbus::Result<()>;
/// LedBrightness property
#[inline]
#[dbus_proxy(property)]
fn led_brightness(&self) -> zbus::Result<i16>;
/// LedMode property
#[inline]
fn led_mode(&self) -> zbus::Result<AuraModeNum>;
/// LedModes property
#[inline]
fn led_modes(&self) -> zbus::Result<BTreeMap<AuraModeNum, AuraEffect>>;
// As property doesn't work for AuraPowerDev (complexity of serialization?)
// #[dbus_proxy(property)]
#[inline]
fn leds_enabled(&self) -> zbus::Result<AuraPowerDev>;
}

View File

@@ -20,7 +20,7 @@
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
use rog_platform::platform::GpuMode;
use zbus::dbus_proxy;
use zbus_macros::dbus_proxy;
#[dbus_proxy(
interface = "org.asuslinux.Daemon",
@@ -28,48 +28,62 @@ use zbus::dbus_proxy;
)]
trait RogBios {
/// DgpuDisable method
#[inline]
fn dgpu_disable(&self) -> zbus::Result<bool>;
/// EgpuEnable method
#[inline]
fn egpu_enable(&self) -> zbus::Result<bool>;
/// GpuMuxMode method
#[inline]
fn gpu_mux_mode(&self) -> zbus::Result<GpuMode>;
/// PanelOd method
#[inline]
fn panel_od(&self) -> zbus::Result<bool>;
/// PostBootSound method
#[inline]
fn post_boot_sound(&self) -> zbus::Result<i16>;
/// SetDgpuDisable method
#[inline]
fn set_dgpu_disable(&self, disable: bool) -> zbus::Result<()>;
/// SetEgpuEnable method
#[inline]
fn set_egpu_enable(&self, enable: bool) -> zbus::Result<()>;
/// SetGpuMuxMode method
#[inline]
fn set_gpu_mux_mode(&self, mode: GpuMode) -> zbus::Result<()>;
/// SetPanelOd method
#[inline]
fn set_panel_od(&self, overdrive: bool) -> zbus::Result<()>;
/// SetPostBootSound method
#[inline]
fn set_post_boot_sound(&self, on: bool) -> zbus::Result<()>;
/// NotifyDgpuDisable signal
#[inline]
#[dbus_proxy(signal)]
fn notify_dgpu_disable(&self, disable: bool) -> zbus::Result<()>;
/// NotifyEgpuEnable signal
#[inline]
#[dbus_proxy(signal)]
fn notify_egpu_enable(&self, enable: bool) -> zbus::Result<()>;
/// NotifyGpuMuxMode signal
#[inline]
#[dbus_proxy(signal)]
fn notify_gpu_mux_mode(&self, mode: GpuMode) -> zbus::Result<()>;
/// NotifyPanelOd signal
#[inline]
#[dbus_proxy(signal)]
fn notify_panel_od(&self, overdrive: bool) -> zbus::Result<()>;

View File

@@ -19,7 +19,7 @@
//!
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
use zbus::dbus_proxy;
use zbus_macros::dbus_proxy;
#[dbus_proxy(
interface = "org.asuslinux.Daemon",
@@ -27,19 +27,24 @@ use zbus::dbus_proxy;
)]
trait Power {
/// charge_control_end_threshold method
#[inline]
fn charge_control_end_threshold(&self) -> zbus::Result<u8>;
/// MainsOnline method
#[inline]
fn mains_online(&self) -> zbus::Result<bool>;
/// set_charge_control_end_threshold method
#[inline]
fn set_charge_control_end_threshold(&self, limit: u8) -> zbus::Result<()>;
/// NotifyCharge signal
#[inline]
#[dbus_proxy(signal)]
fn notify_charge_control_end_threshold(&self, limit: u8) -> zbus::Result<u8>;
/// NotifyMainsOnline signal
#[inline]
#[dbus_proxy(signal)]
fn notify_mains_online(&self, on: bool) -> zbus::Result<()>;
}

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