Compare commits

...

51 Commits
4.7.1 ... 5.0.1

Author SHA1 Message Date
Luke D. Jones
9aa332de3b New release 2023-12-15 11:50:49 +13:00
Luke D. Jones
5efd7fc6a7 Fix: Corrections to dbus signature for some keyboard power settings
Closes #423
2023-12-15 11:48:20 +13:00
Luke D. Jones
0aafe24a02 Fix: correctiosn to asusd.service
Closes #424
2023-12-15 11:48:20 +13:00
Luke D. Jones
dda6d343d9 Fix: correction to switching next fan profile
Closes #425
2023-12-15 11:48:18 +13:00
Luke D. Jones
6f39307080 remember how to tag releases 2023-12-12 22:00:37 +13:00
Luke Jones
ef63789faa Merge branch 'main' into 'main'
Added missing button, and fixed layout for Strix G18

See merge request asus-linux/asusctl!176
2023-12-12 08:58:31 +00:00
Sunehildeep Singh
c422e77ba6 Added missing button, and fixed layout for Strix G18 2023-12-12 08:58:31 +00:00
Luke D. Jones
3c234dd3c4 Release 5.0.0 2023-12-12 21:54:24 +13:00
Luke Jones
c420dd820a Merge branch '419-support-for-strix-g18-aura-g814ji-already-done-need-merge' into 'main'
Draft: Resolve "Support For Strix G18 AURA (G814JI) - Already Done, need merge"

Closes #419

See merge request asus-linux/asusctl!175
2023-12-10 20:03:55 +00:00
Luke D. Jones
a3e6fec163 Add g814ji and layout 2023-12-11 08:56:17 +13:00
Luke D. Jones
9b4e76be87 Update examples 2023-12-11 08:49:32 +13:00
Luke D. Jones
7b2125cbdf fic async deadlock in platform control 2023-12-08 20:06:22 +13:00
Luke D. Jones
694a644cc6 Revert "Fix: change epp change watch to a loop to prevent deadlock"
This reverts commit c06f78990f.
2023-12-08 20:06:22 +13:00
Luke D. Jones
922aa0c352 platform example testing 2023-12-08 17:33:27 +13:00
Luke D. Jones
c06f78990f Fix: change epp change watch to a loop to prevent deadlock 2023-12-07 20:04:21 +13:00
Luke D. Jones
5c8bb6e6ea Ensure thermal policy is set on resume and relaod 2023-12-05 19:26:49 +13:00
Luke Jones
993131d0a9 Merge branch 'fluke/dbus-refactor' into 'main'
Fluke/dbus refactor

See merge request asus-linux/asusctl!173
2023-12-03 20:44:01 +00:00
Luke Jones
0a69c23288 Fluke/dbus refactor 2023-12-03 20:44:01 +00:00
Luke D. Jones
f6e4cc0626 Cleanup after changes from Platform dbus rework 2023-11-18 22:36:55 +13:00
Luke D. Jones
1f696508e7 rog-platform: refactor all related parts 2023-11-18 21:57:46 +13:00
Luke D. Jones
fa043adc99 rog-platform: add CPU and GPU tunings
rog-platform: add tunables to supported dat

Anime: fixes to how some power options work
2023-11-17 17:16:03 +13:00
Luke D. Jones
b9c2d929b3 anime: rework sleep logic 2023-11-16 13:57:42 +13:00
Luke D. Jones
eda1e920df Update changelog 2023-11-15 18:29:35 +13:00
Luke D. Jones
0de2c9e424 Anime: remove sleep animation config 2023-11-15 18:27:58 +13:00
Luke D. Jones
e88e7be8ae Anime: expose new options in CLI 2023-11-15 17:45:52 +13:00
Luke D. Jones
e470d3acc0 Anime: add dbus methods 2023-11-15 17:23:44 +13:00
Luke D. Jones
f5b3f0bc38 Fix lints on examples 2023-11-15 17:23:35 +13:00
Luke D. Jones
19497c94e0 Anime: fix data struct 2023-11-15 16:56:50 +13:00
Luke D. Jones
670eee23e8 Anime: small cleanup 2023-11-15 16:45:30 +13:00
Luke D. Jones
26309776e9 Fix test 2023-11-15 14:47:52 +13:00
Luke D. Jones
a7d5057976 Anime: propagate property config 2023-11-15 14:46:42 +13:00
Luke D. Jones
8eb9b1d4eb Anime: refactor power stuff 2023-11-15 14:29:38 +13:00
Luke D. Jones
71ee9e43ba Fix gnome-45 extension version 2023-11-14 16:49:17 +13:00
Luke D. Jones
f1b0e1288a Add missing crates 2023-11-13 11:28:34 +13:00
Luke D. Jones
35c7fd10b3 Drop sysfs_class and create dmi_id for getting identifying info with udev 2023-11-08 14:00:35 +13:00
Luke D. Jones
4c50dc259c rog-control-center: ensure brightness slider works correctly 2023-11-08 13:59:07 +13:00
Luke D. Jones
0fd0aeff88 Support Rog Ally LED modes (basic) 2023-11-07 17:24:38 +13:00
Luke Jones
fd37f41ef1 Merge branch 'sctk-0.16.1' into 'main'
cargo update -p smithay-client-toolkit v0.16.0 -> v0.16.1

Closes #407

See merge request asus-linux/asusctl!172
2023-10-11 06:30:37 +00:00
Cole Mickens
1307997122 cargo update -p smithay-client-toolkit v0.16.0 -> v0.16.1 2023-10-11 08:15:04 +02:00
Luke Jones
85187d2d8d Merge branch 'add_laptop_G513RW' into 'main'
Added preliminary support for G513RW

See merge request asus-linux/asusctl!171
2023-09-16 05:51:15 +00:00
Edwin Clement
e29a568195 Added preliminary support for G513RW 2023-09-15 20:03:02 -04:00
Luke D. Jones
6c375a9951 Prep new release 2023-09-08 13:22:15 +12:00
Luke D. Jones
4641e19c43 Fix bad keybaord layout 2023-09-04 10:19:57 +12:00
Luke D. Jones
91f0c2ea14 Fix loading of fan curves
Closes #402

Signed-off-by: Luke D. Jones <luke@ljones.dev>
2023-09-04 10:18:48 +12:00
Luke Jones
b4a8cb9de2 Merge branch 'srivatsarog-main-patch-78688' into 'main'
Update aura_support.ron

See merge request asus-linux/asusctl!169
2023-09-03 08:45:39 +00:00
Luke Jones
67bbcdb964 Merge branch 'main' into 'srivatsarog-main-patch-78688'
# Conflicts:
#   rog-aura/data/aura_support.ron
2023-09-03 08:37:52 +00:00
Luke Jones
8acfe0a9e8 Merge branch 'main' into 'main'
aura: Support for G733PZ

See merge request asus-linux/asusctl!167
2023-09-03 08:36:20 +00:00
Luke D. Jones
5f6e6ec382 gex: rename gnome-44, add gnome-45 2023-08-31 14:36:01 +12:00
Luke D. Jones
cf92526d87 Set cargo resolver 2023-08-31 12:42:31 +12:00
gabi
b652fd15a2 Update aura_support.ron 2023-07-28 06:16:39 +00:00
Interfector18
9e65921c0a aura: Support for G733PZ 2023-07-24 17:44:59 +02:00
149 changed files with 9157 additions and 6096 deletions

9
.gitignore vendored
View File

@@ -14,7 +14,8 @@ vendor_*
node-modules
bindings/ts/*.d.ts
bindings/ts/*.js.map
desktop-extensions/gnome/dist
desktop-extensions/gnome/node_modules
desktop-extensions/gnome/schemas/gschemas.compiled
desktop-extensions/gnome/*.zip
desktop-extensions/gnome*/dist
desktop-extensions/gnome*/@types/gir-generated
desktop-extensions/gnome*/node_modules
desktop-extensions/gnome*/schemas/gschemas.compiled
desktop-extensions/gnome*/*.zip

View File

@@ -5,6 +5,51 @@ 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]
## [v5.0.1]
### Changed
- Fix setting next fan profile
- Fix the assud.service
- Fix dbus signature of some power setting types for some keyboards
## [v5.0.0]
### Added
- Gnome 45 plugin
- Support for G513RW LED modes
- Support Rog Ally LED modes (basic)
- Add on_lid_closed and on_external_power_changed events for running certain tasks
- Anime dbus: add:
- SetOffWhenUnplugged, also add asusctl CLI option
- SetOffWhenSuspended, also add asusctl CLI option
- SetOffWhenLidClosed, also add asusctl CLI option
- Anime: add brightness_on_battery config option
- Platform: add `post_animation_sound`, kernel 6.7+ requires patch
- Add changing of CPU energy perfromance preference in relation to throttle_thermal_policy. This means that the CPU correctly behaves according to throttle_thermal_policy (and platform profile use is *removed*)
- Add setting of throttle_thermal_policy on power plug/unplug
### Changed
- asusd: remove set_image_brightness for anime
- asusd: refactor how certain things like display enable/builtins are toggled
- Refactor sleep/shutdown tasks
- rog-control-center: ensure brightness slider works correctly
- Update `smithay-client-toolkit` for fix to issue #407
- Remove the "sleep" animations from Anime to stop preventing the display-off
- Anime:
- Ensure display is off when lid is closed and option is set
- Ensure display is off when on battery and option is set
- Ensure builtin animations run instead of custom animations if option is set
### Breaking
- DBUS stuff. Again. All of it.
## [v4.7.2]
### Added
- Support for G733PZ LED modes
- Support for G713RC LED modes
### Changed
- Fix loading of fan curves from stored settings
## [v4.7.1]
### Changed
- Fixes to asusctl CLI tool to show fan curves

1071
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,14 @@
[workspace]
members = ["asusctl", "asusd", "asusd-user", "config-traits", "rog-platform", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "rog-control-center", "simulators"]
default-members = ["asusctl", "asusd", "asusd-user", "rog-control-center"]
workspace.resolver = "2"
members = ["asusctl", "asusd", "asusd-user", "config-traits", "cpuctl", "dmi-id", "rog-platform", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "rog-control-center", "simulators"]
default-members = ["asusctl", "asusd", "asusd-user", "cpuctl", "rog-control-center"]
resolver = "2"
[workspace.package]
version = "4.7.1"
version = "5.0.1"
[workspace.dependencies]
async-trait = "^0.1"
tokio = { version = "^1.23.0", features = ["macros", "rt-multi-thread", "time", "sync"]}
tokio = { version = "^1.23.0", features = ["macros", "sync", "rt-multi-thread"]}
concat-idents = "^1.1"
dirs = "^4.0"
smol = "^1.3"
@@ -30,7 +30,6 @@ glam = { version = "^0.22", features = ["serde"] }
gumdrop = "^0.8"
udev = "^0.7"
rusb = "^0.9"
sysfs-class = "^0.1.3"
inotify = "^0.10.0"
png_pong = "^0.8"
@@ -49,6 +48,7 @@ lto = "fat"
debug = false
opt-level = 3
panic = "abort"
#codegen-units = 1
[profile.dev]
debug = true

View File

@@ -112,6 +112,8 @@ vendor:
echo 'directory = "vendor"' >> .cargo/config
mv .cargo/config ./cargo-config
rm -rf .cargo
rm -rf vendor
cargo vendor-filterer --platform x86_64-unknown-linux-gnu vendor
tar pcfJ vendor_asusctl_$(VERSION).tar.xz vendor
rm -rf vendor
@@ -122,18 +124,17 @@ bindings:
typeshare ./rog-platform/src/ --lang=typescript --output-file=bindings/ts/platform.ts
introspect:
gdbus introspect --system -d org.asuslinux.Daemon -o /org/asuslinux/Platform -x > bindings/dbus-xml/org-asuslinux-platform-4.xml
gdbus introspect --system -d org.asuslinux.Daemon -o /org/asuslinux/Aura -x > bindings/dbus-xml/org-asuslinux-aura-4.xml
gdbus introspect --system -d org.asuslinux.Daemon -o /org/asuslinux/Anime -x > bindings/dbus-xml/org-asuslinux-anime-4.xml
gdbus introspect --system -d org.asuslinux.Daemon -o /org/asuslinux/Platform -x > bindings/dbus-xml/org-asuslinux-platform-4.xml
gdbus introspect --system -d org.asuslinux.Daemon -o /org/asuslinux/Power -x > bindings/dbus-xml/org-asuslinux-power-4.xml
gdbus introspect --system -d org.asuslinux.Daemon -o /org/asuslinux/Profile -x > bindings/dbus-xml/org-asuslinux-profile-4.xml
gdbus introspect --system -d org.asuslinux.Daemon -o /org/asuslinux/Supported -x > bindings/dbus-xml/org-asuslinux-supported-4.xml
gdbus introspect --system -d org.asuslinux.Daemon -o /org/asuslinux/FanCurves -x > bindings/dbus-xml/org-asuslinux-fan-curves-4.xml
xmlstarlet ed -L -O -d '//interface[@name="org.freedesktop.DBus.Introspectable"]' bindings/dbus-xml/org-asuslinux-*
xmlstarlet ed -L -O -d '//interface[@name="org.freedesktop.DBus.Properties"]' bindings/dbus-xml/org-asuslinux-*
xmlstarlet ed -L -O -d '//interface[@name="org.freedesktop.DBus.Peer"]' bindings/dbus-xml/org-asuslinux-*
build:
ifeq ($(VENDORED),1)
cargo vendor
@echo "version = $(VERSION)"
tar pxf vendor_asusctl_$(VERSION).tar.xz
endif

View File

@@ -12,10 +12,10 @@ rog_dbus = { path = "../rog-dbus" }
rog_profiles = { path = "../rog-profiles" }
rog_platform = { path = "../rog-platform" }
asusd = { path = "../asusd" }
dmi_id = { path = "../dmi-id" }
gumdrop.workspace = true
toml.workspace = true
sysfs-class.workspace = true
[dev-dependencies]
gif.workspace = true

View File

@@ -10,7 +10,7 @@ use rog_dbus::RogDbusClientBlocking;
fn main() -> Result<(), Box<dyn Error>> {
let (client, _) = RogDbusClientBlocking::new().unwrap();
let args: Vec<String> = env::args().into_iter().collect();
let args: Vec<String> = env::args().collect();
if args.len() != 3 {
println!("Usage: <filepath> <brightness>");
println!("e.g, asusctl/examples/doom_large.png 0.8");

View File

@@ -15,13 +15,13 @@ fn main() {
for step in (2..50).rev() {
let mut matrix = AnimeDiagonal::new(AnimeType::GA401, None);
for c in (0..60).into_iter().step_by(step) {
for c in (0..60).step_by(step) {
for i in matrix.get_mut().iter_mut() {
i[c] = 50;
}
}
for c in (0..35).into_iter().step_by(step) {
for c in (0..35).step_by(step) {
for i in &mut matrix.get_mut()[c] {
*i = 50;
}

View File

@@ -9,7 +9,7 @@ use rog_dbus::RogDbusClientBlocking;
fn main() {
let (client, _) = RogDbusClientBlocking::new().unwrap();
let args: Vec<String> = env::args().into_iter().collect();
let args: Vec<String> = env::args().collect();
if args.len() != 3 {
println!("Please supply filepath and brightness");
return;

View File

@@ -11,7 +11,7 @@ use rog_dbus::RogDbusClientBlocking;
fn main() -> Result<(), Box<dyn Error>> {
let (client, _) = RogDbusClientBlocking::new().unwrap();
let args: Vec<String> = env::args().into_iter().collect();
let args: Vec<String> = env::args().collect();
if args.len() != 7 {
println!("Usage: <filepath> <scale> <angle> <x pos> <y pos> <brightness>");
println!("e.g, asusctl/examples/doom_large.png 0.9 0.4 0.0 0.0 0.8");

View File

@@ -14,7 +14,7 @@ use rog_dbus::RogDbusClientBlocking;
fn main() -> Result<(), Box<dyn Error>> {
let (client, _) = RogDbusClientBlocking::new().unwrap();
let args: Vec<String> = env::args().into_iter().collect();
let args: Vec<String> = env::args().collect();
if args.len() != 7 {
println!("Usage: <filepath> <scale> <angle> <x pos> <y pos> <brightness>");
println!("e.g, asusctl/examples/doom_large.png 0.9 0.4 0.0 0.0 0.8");

View File

@@ -62,7 +62,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
seq.next_state(&layout);
let packets = seq.create_packets();
client.proxies().led().direct_addressing_raw(packets)?;
client.proxies().aura().direct_addressing_raw(packets)?;
std::thread::sleep(std::time::Duration::from_millis(33));
}
}

View File

@@ -17,10 +17,28 @@ pub struct AnimeCommand {
help = "set global base brightness value <Off, Low, Med, High>"
)]
pub brightness: Option<Brightness>,
#[options(meta = "", help = "set global (image) brightness value")]
pub image_brightness: Option<f32>,
#[options(help = "clear the display")]
pub clear: bool,
#[options(
no_short,
meta = "",
help = "turn the anime off when external power is unplugged"
)]
pub off_when_unplugged: Option<bool>,
#[options(
no_short,
meta = "",
help = "turn the anime off when the laptop suspends"
)]
pub off_when_suspended: Option<bool>,
#[options(
no_short,
meta = "",
help = "turn the anime off when the lid is closed"
)]
pub off_when_lid_closed: Option<bool>,
#[options(no_short, meta = "", help = "Off with his head!!!")]
pub off_with_his_head: Option<bool>,
#[options(command)]
pub command: Option<AnimeActions>,
}

View File

@@ -59,14 +59,14 @@ pub struct AuraPowerStates {
#[derive(Options)]
pub struct LedBrightness {
level: Option<u32>,
level: Option<u8>,
}
impl LedBrightness {
pub fn new(level: Option<u32>) -> Self {
pub fn new(level: Option<u8>) -> Self {
LedBrightness { level }
}
pub fn level(&self) -> Option<u32> {
pub fn level(&self) -> Option<u8> {
self.level
}
}

View File

@@ -1,8 +1,9 @@
use gumdrop::Options;
use rog_platform::platform::PlatformPolicy;
use crate::anime_cli::AnimeCommand;
use crate::aura_cli::{LedBrightness, LedPowerCommand1, LedPowerCommand2, SetAuraBuiltin};
use crate::profiles_cli::{FanCurveCommand, ProfileCommand};
use crate::fan_curve_cli::FanCurveCommand;
#[derive(Default, Options)]
pub struct CliStart {
@@ -44,6 +45,24 @@ pub enum CliCommand {
Bios(BiosCommand),
}
#[derive(Debug, Clone, Options)]
pub struct ProfileCommand {
#[options(help = "print help message")]
pub help: bool,
#[options(help = "toggle to next profile in list")]
pub next: bool,
#[options(help = "list available profiles")]
pub list: bool,
#[options(help = "get profile")]
pub profile_get: bool,
#[options(meta = "", help = "set the active profile")]
pub profile_set: Option<PlatformPolicy>,
}
#[derive(Options)]
pub struct LedModeCommand {
#[options(help = "print help message")]

View File

@@ -1,24 +1,7 @@
use gumdrop::Options;
use rog_platform::platform::PlatformPolicy;
use rog_profiles::fan_curve_set::CurveData;
use rog_profiles::{FanCurvePU, Profile};
#[derive(Debug, Clone, Options)]
pub struct ProfileCommand {
#[options(help = "print help message")]
pub help: bool,
#[options(help = "toggle to next profile in list")]
pub next: bool,
#[options(help = "list available profiles")]
pub list: bool,
#[options(help = "get profile")]
pub profile_get: bool,
#[options(meta = "", help = "set the active profile")]
pub profile_set: Option<Profile>,
}
use rog_profiles::FanCurvePU;
#[derive(Debug, Clone, Options)]
pub struct FanCurveCommand {
@@ -35,7 +18,7 @@ pub struct FanCurveCommand {
meta = "",
help = "profile to modify fan-curve for. Shows data if no options provided"
)]
pub mod_profile: Option<Profile>,
pub mod_profile: Option<PlatformPolicy>,
#[options(
meta = "",

View File

@@ -5,17 +5,20 @@ use std::process::Command;
use std::thread::sleep;
use anime_cli::{AnimeActions, AnimeCommand};
use asusd::ctrl_aura::trait_impls::AURA_ZBUS_NAME;
use asusd::ctrl_fancurves::FAN_CURVE_ZBUS_NAME;
use aura_cli::{LedPowerCommand1, LedPowerCommand2};
use dmi_id::DMIID;
use fan_curve_cli::FanCurveCommand;
use gumdrop::{Opt, Options};
use profiles_cli::{FanCurveCommand, ProfileCommand};
use rog_anime::usb::get_anime_type;
use rog_anime::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType, Vec2};
use rog_aura::power::KbAuraPowerState;
use rog_aura::usb::{AuraDevRog1, AuraDevTuf, AuraDevice, AuraPowerDev};
use rog_aura::usb::{AuraDevRog1, AuraDevTuf, AuraPowerDev};
use rog_aura::{self, AuraEffect};
use rog_dbus::RogDbusClientBlocking;
use rog_platform::platform::GpuMode;
use rog_platform::supported::*;
use rog_platform::error::PlatformError;
use rog_platform::platform::{GpuMode, PlatformPolicy, Properties};
use rog_profiles::error::ProfileError;
use crate::aura_cli::{AuraPowerStates, LedBrightness};
@@ -24,7 +27,7 @@ use crate::cli_opts::*;
mod anime_cli;
mod aura_cli;
mod cli_opts;
mod profiles_cli;
mod fan_curve_cli;
fn main() {
let args: Vec<String> = args().skip(1).collect();
@@ -43,43 +46,42 @@ fn main() {
};
if let Ok((dbus, _)) = RogDbusClientBlocking::new().map_err(|e| {
print_error_help(&e, None);
check_service("asusd");
println!("\nError: {e}\n");
print_info();
}) {
if let Ok(supported) = dbus
.proxies()
.supported()
.supported_functions()
.map_err(|e| {
print_error_help(&e, None);
})
{
let supported_properties = dbus.proxies().platform().supported_properties().unwrap();
let supported_interfaces = dbus.proxies().platform().supported_interfaces().unwrap();
if parsed.version {
println!("asusctl v{}", env!("CARGO_PKG_VERSION"));
println!();
print_info();
}
if let Err(err) = do_parsed(&parsed, &supported, &dbus) {
print_error_help(&*err, Some(&supported));
}
if let Err(err) = do_parsed(&parsed, &supported_interfaces, &supported_properties, &dbus) {
print_error_help(&*err, &supported_interfaces, &supported_properties);
}
}
}
fn print_error_help(err: &dyn std::error::Error, supported: Option<&SupportedFunctions>) {
fn print_error_help(
err: &dyn std::error::Error,
supported_interfaces: &[String],
supported_properties: &[Properties],
) {
check_service("asusd");
println!("\nError: {}\n", err);
print_info();
if let Some(supported) = supported {
println!();
println!("Supported laptop functions:\n\n{}", supported);
}
println!("Supported interfaces:\n\n{:#?}\n", supported_interfaces);
println!("Supported properties:\n\n{:#?}\n", supported_properties);
}
fn print_info() {
let dmi = sysfs_class::DmiId::default();
let board_name = dmi.board_name().expect("Could not get board_name");
let prod_family = dmi.product_family().expect("Could not get product_family");
let dmi = DMIID::new().unwrap_or_default();
let board_name = dmi.board_name;
let prod_family = dmi.product_family;
println!("asusctl version: {}", env!("CARGO_PKG_VERSION"));
println!(" Product family: {}", prod_family.trim());
println!(" Board name: {}", board_name.trim());
@@ -104,20 +106,21 @@ fn check_service(name: &str) -> bool {
fn do_parsed(
parsed: &CliStart,
supported: &SupportedFunctions,
supported_interfaces: &[String],
supported_properties: &[Properties],
dbus: &RogDbusClientBlocking<'_>,
) -> Result<(), Box<dyn std::error::Error>> {
match &parsed.command {
Some(CliCommand::LedMode(mode)) => handle_led_mode(dbus, &supported.keyboard_led, mode)?,
Some(CliCommand::LedPow1(pow)) => handle_led_power1(dbus, &supported.keyboard_led, pow)?,
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::LedMode(mode)) => handle_led_mode(dbus, supported_interfaces, mode)?,
Some(CliCommand::LedPow1(pow)) => handle_led_power1(dbus, supported_interfaces, pow)?,
Some(CliCommand::LedPow2(pow)) => handle_led_power2(dbus, supported_interfaces, pow)?,
Some(CliCommand::Profile(cmd)) => handle_throttle_profile(dbus, supported_properties, cmd)?,
Some(CliCommand::FanCurve(cmd)) => {
handle_fan_curve(dbus, &supported.platform_profile, cmd)?;
handle_fan_curve(dbus, supported_interfaces, cmd)?;
}
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)?,
Some(CliCommand::Anime(cmd)) => handle_anime(dbus, cmd)?,
Some(CliCommand::Bios(cmd)) => handle_platform_properties(dbus, supported_properties, cmd)?,
None => {
if (!parsed.show_supported
&& parsed.kbd_bright.is_none()
@@ -131,19 +134,14 @@ fn do_parsed(
if let Some(cmdlist) = CliStart::command_list() {
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_owned()).collect();
for command in commands.iter().filter(|command| {
if !matches!(
supported.keyboard_led.dev_id,
AuraDevice::X1854
| AuraDevice::X1869
| AuraDevice::X1866
| AuraDevice::Tuf
) && command.trim().starts_with("led-pow-1")
let dev_type = dbus.proxies().aura().device_type().unwrap();
if !dev_type.is_old_style()
&& !dev_type.is_tuf_style()
&& command.trim().starts_with("led-pow-1")
{
return false;
}
if supported.keyboard_led.dev_id != AuraDevice::X19b6
&& command.trim().starts_with("led-pow-2")
{
if !dev_type.is_new_style() && command.trim().starts_with("led-pow-2") {
return false;
}
true
@@ -162,31 +160,34 @@ fn do_parsed(
if let Some(brightness) = &parsed.kbd_bright {
match brightness.level() {
None => {
let level = dbus.proxies().led().led_brightness()?;
println!("Current keyboard led brightness: {}", level);
let level = dbus.proxies().aura().brightness()?;
println!("Current keyboard led brightness: {level:?}");
}
Some(level) => dbus
.proxies()
.led()
.set_brightness(<rog_aura::LedBrightness>::from(level))?,
.aura()
.set_brightness(rog_aura::LedBrightness::from(level))?,
}
}
if parsed.next_kbd_bright {
dbus.proxies().led().next_led_brightness()?;
let brightness = dbus.proxies().aura().brightness()?;
dbus.proxies().aura().set_brightness(brightness.next())?;
}
if parsed.prev_kbd_bright {
dbus.proxies().led().prev_led_brightness()?;
let brightness = dbus.proxies().aura().brightness()?;
dbus.proxies().aura().set_brightness(brightness.prev())?;
}
if parsed.show_supported {
println!("Supported laptop functions:\n\n{}", supported);
}
// TODO:
// if parsed.show_supported {
// println!("Supported laptop functions:\n\n{}", supported);
// }
if let Some(chg_limit) = parsed.chg_limit {
dbus.proxies()
.charge()
.platform()
.set_charge_control_end_threshold(chg_limit)?;
}
@@ -203,14 +204,16 @@ fn do_gfx() {
fn handle_anime(
dbus: &RogDbusClientBlocking<'_>,
_supported: &AnimeSupportedFunctions,
cmd: &AnimeCommand,
) -> Result<(), Box<dyn std::error::Error>> {
if (cmd.command.is_none()
&& cmd.enable_display.is_none()
&& cmd.enable_powersave_anim.is_none()
&& cmd.brightness.is_none()
&& cmd.image_brightness.is_none()
&& cmd.off_when_lid_closed.is_none()
&& cmd.off_when_suspended.is_none()
&& cmd.off_when_unplugged.is_none()
&& cmd.off_with_his_head.is_none()
&& !cmd.clear)
|| cmd.help
{
@@ -228,9 +231,17 @@ fn handle_anime(
if let Some(bright) = cmd.brightness {
dbus.proxies().anime().set_brightness(bright)?;
}
if let Some(bright) = cmd.image_brightness {
verify_brightness(bright);
dbus.proxies().anime().set_image_brightness(bright)?;
if let Some(enable) = cmd.off_when_lid_closed {
dbus.proxies().anime().set_off_when_lid_closed(enable)?;
}
if let Some(enable) = cmd.off_when_suspended {
dbus.proxies().anime().set_off_when_suspended(enable)?;
}
if let Some(enable) = cmd.off_when_unplugged {
dbus.proxies().anime().set_off_when_unplugged(enable)?;
}
if cmd.off_with_his_head.is_some() {
println!("Did Alice _really_ make it back from Wonderland?");
}
let mut anime_type = get_anime_type()?;
@@ -367,12 +378,14 @@ fn handle_anime(
return Ok(());
}
dbus.proxies().anime().set_builtin_animations(
builtins.boot,
builtins.awake,
builtins.sleep,
builtins.shutdown,
)?;
dbus.proxies()
.anime()
.set_builtin_animations(rog_anime::Animations {
boot: builtins.boot,
awake: builtins.awake,
sleep: builtins.sleep,
shutdown: builtins.shutdown,
})?;
}
}
}
@@ -390,9 +403,14 @@ fn verify_brightness(brightness: f32) {
fn handle_led_mode(
dbus: &RogDbusClientBlocking<'_>,
supported: &LedSupportedFunctions,
supported: &[String],
mode: &LedModeCommand,
) -> Result<(), Box<dyn std::error::Error>> {
if !supported.contains(&AURA_ZBUS_NAME.to_string()) {
println!("This laptop does not support power options");
return Err(PlatformError::NotSupported.into());
}
if mode.command.is_none() && !mode.prev_mode && !mode.next_mode {
if !mode.help {
println!("Missing arg or command\n");
@@ -403,7 +421,8 @@ fn handle_led_mode(
if let Some(cmdlist) = LedModeCommand::command_list() {
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_owned()).collect();
for command in commands.iter().filter(|command| {
for mode in &supported.basic_modes {
let modes = dbus.proxies().aura().supported_modes().unwrap();
for mode in &modes {
if command
.trim()
.starts_with(&<&str>::from(mode).to_lowercase())
@@ -411,9 +430,10 @@ fn handle_led_mode(
return true;
}
}
if !supported.basic_zones.is_empty() && command.trim().starts_with("multi") {
return true;
}
// TODO
// if !supported.basic_zones.is_empty() && command.trim().starts_with("multi") {
// return true;
// }
false
}) {
println!("{}", command);
@@ -429,17 +449,31 @@ fn handle_led_mode(
return Ok(());
}
if mode.next_mode {
dbus.proxies().led().next_led_mode()?;
let mode = dbus.proxies().aura().led_mode()?;
let modes = dbus.proxies().aura().supported_modes()?;
let mut pos = modes.iter().position(|m| *m == mode).unwrap() + 1;
if pos >= modes.len() {
pos = 0;
}
dbus.proxies().aura().set_led_mode(modes[pos])?;
} else if mode.prev_mode {
dbus.proxies().led().prev_led_mode()?;
let mode = dbus.proxies().aura().led_mode()?;
let modes = dbus.proxies().aura().supported_modes()?;
let mut pos = modes.iter().position(|m| *m == mode).unwrap();
if pos == 0 {
pos = modes.len() - 1;
} else {
pos -= 1;
}
dbus.proxies().aura().set_led_mode(modes[pos])?;
} else if let Some(mode) = mode.command.as_ref() {
if mode.help_requested() {
println!("{}", mode.self_usage());
return Ok(());
}
dbus.proxies()
.led()
.set_led_mode(&<AuraEffect>::from(mode))?;
.aura()
.set_led_mode_data(<AuraEffect>::from(mode))?;
}
Ok(())
@@ -447,9 +481,18 @@ fn handle_led_mode(
fn handle_led_power1(
dbus: &RogDbusClientBlocking<'_>,
supported: &LedSupportedFunctions,
supported: &[String],
power: &LedPowerCommand1,
) -> Result<(), Box<dyn std::error::Error>> {
if !supported.contains(&AURA_ZBUS_NAME.to_string()) {
println!("This laptop does not support power options");
return Err(PlatformError::NotSupported.into());
}
let dev_type = dbus.proxies().aura().device_type()?;
if !dev_type.is_old_style() && !dev_type.is_tuf_style() {
println!("This option applies only to keyboards 2021+");
}
if power.awake.is_none()
&& power.sleep.is_none()
&& power.boot.is_none()
@@ -463,15 +506,12 @@ fn handle_led_power1(
return Ok(());
}
if matches!(
supported.dev_id,
AuraDevice::X1854 | AuraDevice::X1869 | AuraDevice::X1866
) {
if dev_type.is_old_style() {
handle_led_power_1_do_1866(dbus, power)?;
return Ok(());
}
if matches!(supported.dev_id, AuraDevice::Tuf) {
if dev_type.is_tuf_style() {
handle_led_power_1_do_tuf(dbus, power)?;
return Ok(());
}
@@ -507,13 +547,13 @@ fn handle_led_power_1_do_1866(
old_rog: enabled,
..Default::default()
};
dbus.proxies().led().set_led_power(data, true)?;
dbus.proxies().aura().set_led_power((data, true))?;
let data = AuraPowerDev {
old_rog: disabled,
..Default::default()
};
dbus.proxies().led().set_led_power(data, false)?;
dbus.proxies().aura().set_led_power((data, false))?;
Ok(())
}
@@ -544,22 +584,31 @@ fn handle_led_power_1_do_tuf(
tuf: enabled,
..Default::default()
};
dbus.proxies().led().set_led_power(data, true)?;
dbus.proxies().aura().set_led_power((data, true))?;
let data = AuraPowerDev {
tuf: disabled,
..Default::default()
};
dbus.proxies().led().set_led_power(data, false)?;
dbus.proxies().aura().set_led_power((data, false))?;
Ok(())
}
fn handle_led_power2(
dbus: &RogDbusClientBlocking<'_>,
supported: &LedSupportedFunctions,
supported: &[String],
power: &LedPowerCommand2,
) -> Result<(), Box<dyn std::error::Error>> {
if !supported.contains(&AURA_ZBUS_NAME.to_string()) {
println!("This laptop does not support power options");
return Err(PlatformError::NotSupported.into());
}
let dev_type = dbus.proxies().aura().device_type()?;
if !dev_type.is_new_style() {
println!("This option applies only to keyboards 2021+");
}
if power.command().is_none() {
if !power.help {
println!("Missing arg or command\n");
@@ -584,10 +633,6 @@ fn handle_led_power2(
return Ok(());
}
if supported.dev_id != AuraDevice::X19b6 {
println!("This option applies only to keyboards with product ID 0x19b6");
}
let set = |power: &mut KbAuraPowerState, set_to: &AuraPowerStates| {
power.boot = set_to.boot;
power.awake = set_to.awake;
@@ -595,7 +640,7 @@ fn handle_led_power2(
power.shutdown = set_to.shutdown;
};
let mut enabled = dbus.proxies().led().led_power()?;
let mut enabled = dbus.proxies().aura().led_power()?;
if let Some(cmd) = &power.command {
match cmd {
aura_cli::SetAuraZoneEnabled::Keyboard(k) => set(&mut enabled.rog.keyboard, k),
@@ -606,18 +651,18 @@ fn handle_led_power2(
}
}
dbus.proxies().led().set_led_power(enabled, true)?;
dbus.proxies().aura().set_led_power((enabled, true))?;
}
Ok(())
}
fn handle_profile(
fn handle_throttle_profile(
dbus: &RogDbusClientBlocking<'_>,
supported: &PlatformProfileFunctions,
supported: &[Properties],
cmd: &ProfileCommand,
) -> Result<(), Box<dyn std::error::Error>> {
if !supported.platform_profile {
if !supported.contains(&Properties::DgpuDisable) {
println!("Profiles not supported by either this kernel or by the laptop.");
return Err(ProfileError::NotSupported.into());
}
@@ -633,23 +678,27 @@ fn handle_profile(
}
return Ok(());
}
let current = dbus.proxies().platform().throttle_thermal_policy()?;
if cmd.next {
dbus.proxies().profile().next_profile()?;
dbus.proxies()
.platform()
.set_throttle_thermal_policy(current.next())?;
} else if let Some(profile) = cmd.profile_set {
dbus.proxies().profile().set_active_profile(profile)?;
dbus.proxies()
.platform()
.set_throttle_thermal_policy(profile)?;
}
if cmd.list {
let res = dbus.proxies().profile().profiles()?;
let res = PlatformPolicy::list();
for p in &res {
println!("{:?}", p);
}
}
if cmd.profile_get {
let res = dbus.proxies().profile().active_profile()?;
println!("Active profile is {:?}", res);
println!("Active profile is {current:?}");
}
Ok(())
@@ -657,12 +706,11 @@ fn handle_profile(
fn handle_fan_curve(
dbus: &RogDbusClientBlocking<'_>,
supported: &PlatformProfileFunctions,
supported: &[String],
cmd: &FanCurveCommand,
) -> Result<(), Box<dyn std::error::Error>> {
if supported.fans.is_empty() {
if !supported.contains(&FAN_CURVE_ZBUS_NAME.to_string()) {
println!("Fan-curves not supported by either this kernel or by the laptop.");
println!("This requires kernel 5.17 or the fan curve patch listed in the readme.");
return Err(ProfileError::NotSupported.into());
}
@@ -689,34 +737,34 @@ fn handle_fan_curve(
}
if cmd.get_enabled {
let profile = dbus.proxies().profile().active_profile()?;
let curves = dbus.proxies().profile().fan_curve_data(profile)?;
let profile = dbus.proxies().platform().throttle_thermal_policy()?;
let curves = dbus.proxies().fan_curves().fan_curve_data(profile)?;
for curve in curves.iter() {
println!("{}", String::from(curve));
}
}
if cmd.default {
dbus.proxies().profile().set_active_curve_to_defaults()?;
dbus.proxies().fan_curves().set_active_curve_to_defaults()?;
}
if let Some(profile) = cmd.mod_profile {
if cmd.enable_fan_curves.is_none() && cmd.data.is_none() {
let data = dbus.proxies().profile().fan_curve_data(profile)?;
let data = dbus.proxies().fan_curves().fan_curve_data(profile)?;
let data = toml::to_string(&data)?;
println!("\nFan curves for {:?}\n\n{}", profile, data);
}
if let Some(enabled) = cmd.enable_fan_curves {
dbus.proxies()
.profile()
.fan_curves()
.set_fan_curves_enabled(profile, enabled)?;
}
if let Some(enabled) = cmd.enable_fan_curve {
if let Some(fan) = cmd.fan {
dbus.proxies()
.profile()
.fan_curves()
.set_profile_fan_curve_enabled(profile, fan, enabled)?;
} else {
println!(
@@ -729,16 +777,16 @@ fn handle_fan_curve(
if let Some(mut curve) = cmd.data.clone() {
let fan = cmd.fan.unwrap_or_default();
curve.set_fan(fan);
dbus.proxies().profile().set_fan_curve(profile, curve)?;
dbus.proxies().fan_curves().set_fan_curve(profile, curve)?;
}
}
Ok(())
}
fn handle_bios_option(
fn handle_platform_properties(
dbus: &RogDbusClientBlocking<'_>,
supported: &RogBiosSupportedFunctions,
supported: &[Properties],
cmd: &BiosCommand,
) -> Result<(), Box<dyn std::error::Error>> {
{
@@ -755,26 +803,26 @@ fn handle_bios_option(
let usage: Vec<String> = BiosCommand::usage().lines().map(|s| s.to_owned()).collect();
for line in usage.iter().filter(|line| {
line.contains("sound") && supported.post_sound
|| line.contains("GPU") && supported.gpu_mux
|| line.contains("panel") && supported.panel_overdrive
line.contains("sound") && supported.contains(&Properties::PostAnimationSound)
|| line.contains("GPU") && supported.contains(&Properties::GpuMuxMode)
|| line.contains("panel") && supported.contains(&Properties::PanelOd)
}) {
println!("{}", line);
}
}
if let Some(opt) = cmd.post_sound_set {
dbus.proxies().rog_bios().set_post_boot_sound(opt)?;
dbus.proxies().platform().set_post_animation_sound(opt)?;
}
if cmd.post_sound_get {
let res = dbus.proxies().rog_bios().post_boot_sound()? == 1;
let res = dbus.proxies().platform().post_animation_sound()?;
println!("Bios POST sound on: {}", res);
}
if let Some(opt) = cmd.gpu_mux_mode_set {
println!("Rebuilding initrd to include drivers");
dbus.proxies()
.rog_bios()
.platform()
.set_gpu_mux_mode(GpuMode::from_mux(opt))?;
println!(
"The mode change is not active until you reboot, on boot the bios will make the \
@@ -782,15 +830,15 @@ fn handle_bios_option(
);
}
if cmd.gpu_mux_mode_get {
let res = dbus.proxies().rog_bios().gpu_mux_mode()?;
let res = dbus.proxies().platform().gpu_mux_mode()?;
println!("Bios GPU MUX: {:?}", res);
}
if let Some(opt) = cmd.panel_overdrive_set {
dbus.proxies().rog_bios().set_panel_od(opt)?;
dbus.proxies().platform().set_panel_od(opt)?;
}
if cmd.panel_overdrive_get {
let res = dbus.proxies().rog_bios().panel_od()?;
let res = dbus.proxies().platform().panel_od()?;
println!("Panel overdrive on: {}", res);
}
}

View File

@@ -5,12 +5,11 @@ use std::sync::{Arc, Mutex};
use asusd_user::config::*;
use asusd_user::ctrl_anime::{CtrlAnime, CtrlAnimeInner};
use asusd_user::DBUS_NAME;
use config_traits::{StdConfig, StdConfigLoad};
use rog_anime::usb::get_anime_type;
use rog_aura::aura_detection::LaptopLedData;
use rog_aura::layouts::KeyLayout;
use rog_dbus::RogDbusClientBlocking;
use rog_dbus::{RogDbusClientBlocking, DBUS_NAME};
use smol::Executor;
use zbus::Connection;
@@ -34,15 +33,18 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("rog-platform v{}", rog_platform::VERSION);
let (client, _) = RogDbusClientBlocking::new()?;
let supported = client.proxies().supported().supported_functions()?;
let supported = client
.proxies()
.platform()
.supported_interfaces()
.unwrap_or_default()
.contains(&"Anime".to_string());
let config = ConfigBase::new().load();
let executor = Executor::new();
let early_return = Arc::new(AtomicBool::new(false));
// Set up the anime data and run loop/thread
if supported.anime_ctrl.0 {
if supported {
if let Some(cfg) = config.active_anime {
let anime_type = get_anime_type()?;
let anime_config = ConfigAnime::new().set_name(cfg).load();
@@ -100,7 +102,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
client
.proxies()
.led()
.aura()
.direct_addressing_raw(packets)
.unwrap();
std::thread::sleep(std::time::Duration::from_millis(33));

View File

@@ -6,6 +6,4 @@ pub mod ctrl_anime;
pub mod zbus_anime;
pub static DBUS_NAME: &str = "org.asuslinux.Daemon";
pub static VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@@ -19,7 +19,8 @@ rog_anime = { path = "../rog-anime", features = ["dbus"] }
rog_aura = { path = "../rog-aura", features = ["dbus"] }
rog_platform = { path = "../rog-platform" }
rog_profiles = { path = "../rog-profiles" }
rog_dbus = { path = "../rog-dbus" }
dmi_id = { path = "../dmi-id" }
futures-lite = "*"
async-trait.workspace = true
tokio.workspace = true
@@ -35,9 +36,6 @@ logind-zbus.workspace = true
serde.workspace = true
serde_derive.workspace = true
# Device control
sysfs-class.workspace = true # used for backlight control and baord ID
concat-idents.workspace = true
systemd-zbus = "*"

View File

@@ -1,4 +1,5 @@
use config_traits::{StdConfig, StdConfigLoad2};
use rog_platform::platform::PlatformPolicy;
use serde_derive::{Deserialize, Serialize};
const CONFIG_FILE: &str = "asusd.ron";
@@ -6,23 +7,37 @@ const CONFIG_FILE: &str = "asusd.ron";
#[derive(Deserialize, Serialize, Default, Debug)]
pub struct Config {
/// Save charge limit for restoring on boot
pub bat_charge_limit: u8,
pub charge_control_end_threshold: u8,
pub panel_od: bool,
pub mini_led_mode: bool,
pub disable_nvidia_powerd_on_battery: bool,
pub ac_command: String,
pub bat_command: String,
/// Restored on boot as well as when power is plugged
#[serde(skip)]
pub platform_policy_to_restore: PlatformPolicy,
pub platform_policy_on_battery: PlatformPolicy,
pub platform_policy_on_ac: PlatformPolicy,
//
pub ppt_pl1_spl: Option<u8>,
pub ppt_pl2_sppt: Option<u8>,
pub ppt_fppt: Option<u8>,
pub ppt_apu_sppt: Option<u8>,
pub ppt_platform_sppt: Option<u8>,
pub nv_dynamic_boost: Option<u8>,
pub nv_temp_target: Option<u8>,
}
impl StdConfig for Config {
fn new() -> Self {
Config {
bat_charge_limit: 100,
panel_od: false,
mini_led_mode: false,
charge_control_end_threshold: 100,
disable_nvidia_powerd_on_battery: true,
platform_policy_on_battery: PlatformPolicy::Quiet,
platform_policy_on_ac: PlatformPolicy::Performance,
ac_command: String::new(),
bat_command: String::new(),
..Default::default()
}
}
@@ -35,7 +50,31 @@ impl StdConfig for Config {
}
}
impl StdConfigLoad2<Config458, Config462> for Config {}
impl StdConfigLoad2<Config462, Config472> for Config {}
#[derive(Deserialize, Serialize, Default, Debug)]
pub struct Config472 {
/// Save charge limit for restoring on boot
pub bat_charge_limit: u8,
pub panel_od: bool,
pub mini_led_mode: bool,
pub disable_nvidia_powerd_on_battery: bool,
pub ac_command: String,
pub bat_command: String,
}
impl From<Config472> for Config {
fn from(c: Config472) -> Self {
Self {
charge_control_end_threshold: c.bat_charge_limit,
panel_od: c.panel_od,
disable_nvidia_powerd_on_battery: true,
ac_command: c.ac_command,
bat_command: c.bat_command,
..Default::default()
}
}
}
#[derive(Deserialize, Serialize)]
pub struct Config462 {
@@ -50,34 +89,12 @@ pub struct Config462 {
impl From<Config462> for Config {
fn from(c: Config462) -> Self {
Self {
bat_charge_limit: c.bat_charge_limit,
charge_control_end_threshold: c.bat_charge_limit,
panel_od: c.panel_od,
mini_led_mode: false,
disable_nvidia_powerd_on_battery: true,
ac_command: String::new(),
bat_command: String::new(),
}
}
}
#[derive(Deserialize, Serialize)]
pub struct Config458 {
/// 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 From<Config458> for Config {
fn from(c: Config458) -> Self {
Self {
bat_charge_limit: c.bat_charge_limit,
panel_od: c.panel_od,
mini_led_mode: false,
disable_nvidia_powerd_on_battery: true,
ac_command: c.ac_command,
bat_command: c.bat_command,
..Default::default()
}
}
}

View File

@@ -3,7 +3,9 @@ use std::time::Duration;
use config_traits::{StdConfig, StdConfigLoad2};
use rog_anime::error::AnimeError;
use rog_anime::usb::Brightness;
use rog_anime::{ActionData, ActionLoader, AnimTime, Animations, AnimeType, Fade, Vec2};
use rog_anime::{
ActionData, ActionLoader, AnimTime, Animations, AnimeType, DeviceState, Fade, Vec2,
};
use serde_derive::{Deserialize, Serialize};
const CONFIG_FILE: &str = "anime.ron";
@@ -24,7 +26,6 @@ impl From<AnimeConfigV460> for AnimeConfig {
system: c.system,
boot: c.boot,
wake: c.wake,
sleep: c.sleep,
shutdown: c.shutdown,
..Default::default()
}
@@ -32,25 +33,32 @@ impl From<AnimeConfigV460> for AnimeConfig {
}
#[derive(Deserialize, Serialize, Debug)]
pub struct AnimeConfigV5 {
pub struct AnimeConfigV472 {
pub model_override: Option<AnimeType>,
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
pub sleep: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>,
pub brightness: f32,
pub awake_enabled: bool,
pub boot_anim_enabled: bool,
pub display_enabled: bool,
pub display_brightness: Brightness,
pub builtin_anims_enabled: bool,
pub builtin_anims: Animations,
}
impl From<AnimeConfigV5> for AnimeConfig {
fn from(c: AnimeConfigV5) -> AnimeConfig {
impl From<AnimeConfigV472> for AnimeConfig {
fn from(c: AnimeConfigV472) -> AnimeConfig {
AnimeConfig {
system: c.system,
boot: c.boot,
wake: c.wake,
sleep: c.sleep,
shutdown: c.shutdown,
model_override: c.model_override,
display_enabled: c.display_enabled,
display_brightness: c.display_brightness,
builtin_anims_enabled: c.builtin_anims_enabled,
builtin_anims: c.builtin_anims,
..Default::default()
}
}
@@ -61,7 +69,6 @@ pub struct AnimeConfigCached {
pub system: Vec<ActionData>,
pub boot: Vec<ActionData>,
pub wake: Vec<ActionData>,
pub sleep: Vec<ActionData>,
pub shutdown: Vec<ActionData>,
}
@@ -89,12 +96,6 @@ impl AnimeConfigCached {
}
self.wake = wake;
let mut sleep = Vec::with_capacity(config.sleep.len());
for ani in &config.sleep {
sleep.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.sleep = sleep;
let mut shutdown = Vec::with_capacity(config.shutdown.len());
for ani in &config.shutdown {
shutdown.push(ActionData::from_anime_action(anime_type, ani)?);
@@ -111,12 +112,15 @@ pub struct AnimeConfig {
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
pub sleep: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>,
pub brightness: f32,
// pub brightness: f32,
pub display_enabled: bool,
pub display_brightness: Brightness,
pub builtin_anims_enabled: bool,
pub off_when_unplugged: bool,
pub off_when_suspended: bool,
pub off_when_lid_closed: bool,
pub brightness_on_battery: Brightness,
pub builtin_anims: Animations,
}
@@ -127,12 +131,15 @@ impl Default for AnimeConfig {
system: Vec::new(),
boot: Vec::new(),
wake: Vec::new(),
sleep: Vec::new(),
shutdown: Vec::new(),
brightness: 1.0,
// brightness: 1.0,
display_enabled: true,
display_brightness: Brightness::Med,
builtin_anims_enabled: true,
off_when_unplugged: true,
off_when_suspended: true,
off_when_lid_closed: true,
brightness_on_battery: Brightness::Low,
builtin_anims: Animations::default(),
}
}
@@ -152,7 +159,22 @@ impl StdConfig for AnimeConfig {
}
}
impl StdConfigLoad2<AnimeConfigV460, AnimeConfigV5> for AnimeConfig {}
impl StdConfigLoad2<AnimeConfigV460, AnimeConfigV472> for AnimeConfig {}
impl From<&AnimeConfig> for DeviceState {
fn from(config: &AnimeConfig) -> Self {
DeviceState {
display_enabled: config.display_enabled,
display_brightness: config.display_brightness,
builtin_anims_enabled: config.builtin_anims_enabled,
builtin_anims: config.builtin_anims,
off_when_unplugged: config.off_when_unplugged,
off_when_suspended: config.off_when_suspended,
off_when_lid_closed: config.off_when_lid_closed,
brightness_on_battery: config.brightness_on_battery,
}
}
}
impl AnimeConfig {
// fn clamp_config_brightness(mut config: &mut AnimeConfig) {
@@ -193,14 +215,6 @@ impl AnimeConfig {
Duration::from_secs(2),
)),
}],
sleep: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
scale: 0.9,
angle: 0.0,
translation: Vec2::new(3.0, 2.0),
brightness: 1.0,
time: AnimTime::Infinite,
}],
shutdown: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
scale: 0.9,
@@ -209,7 +223,6 @@ impl AnimeConfig {
brightness: 1.0,
time: AnimTime::Infinite,
}],
brightness: 1.0,
..Default::default()
}
}

View File

@@ -10,27 +10,16 @@ use std::thread::sleep;
use ::zbus::export::futures_util::lock::Mutex;
use log::{error, info, warn};
use rog_anime::error::AnimeError;
use rog_anime::usb::{get_anime_type, pkt_flush, pkt_set_enable_powersave_anim, pkts_for_init};
use rog_anime::usb::{
get_anime_type, pkt_flush, pkt_set_brightness, pkt_set_enable_display,
pkt_set_enable_powersave_anim, pkts_for_init, Brightness,
};
use rog_anime::{ActionData, AnimeDataBuffer, AnimePacketType, AnimeType};
use rog_platform::hid_raw::HidRaw;
use rog_platform::supported::AnimeSupportedFunctions;
use rog_platform::usb_raw::USBRaw;
use self::config::{AnimeConfig, AnimeConfigCached};
use crate::error::RogError;
use crate::GetSupported;
impl GetSupported for CtrlAnime {
type A = AnimeSupportedFunctions;
fn get_supported() -> Self::A {
if USBRaw::new(0x193b).is_ok() {
AnimeSupportedFunctions(true)
} else {
AnimeSupportedFunctions(HidRaw::new("193b").is_ok())
}
}
}
enum Node {
Usb(USBRaw),
@@ -50,6 +39,13 @@ impl Node {
}
Ok(())
}
pub fn set_builtins_enabled(&self, enabled: bool, bright: Brightness) -> Result<(), RogError> {
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))?;
self.write_bytes(&pkt_set_enable_display(enabled))?;
self.write_bytes(&pkt_set_brightness(bright))?;
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
}
}
pub struct CtrlAnime {
@@ -234,6 +230,14 @@ impl CtrlAnime {
})
.ok();
}
lock.node
.write_bytes(&pkt_set_enable_powersave_anim(
lock.config.builtin_anims_enabled,
))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
}
// Loop ended, set the atmonics
thread_running.store(false, Ordering::SeqCst);
@@ -247,7 +251,7 @@ impl CtrlAnime {
/// global brightness set in config.
fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) -> Result<(), RogError> {
for led in buffer.data_mut().iter_mut() {
let mut bright = *led as f32 * self.config.brightness;
let mut bright = *led as f32;
if bright > 254.0 {
bright = 254.0;
}

View File

@@ -4,18 +4,32 @@ use std::sync::Arc;
use async_trait::async_trait;
use config_traits::StdConfig;
use log::warn;
use logind_zbus::manager::ManagerProxy;
use rog_anime::usb::{
pkt_set_brightness, pkt_set_builtin_animations, pkt_set_enable_display,
pkt_set_enable_powersave_anim, AnimAwake, AnimBooting, AnimShutdown, AnimSleeping, Brightness,
pkt_set_enable_powersave_anim, Brightness,
};
use rog_anime::{AnimeDataBuffer, DeviceState};
use rog_anime::{Animations, AnimeDataBuffer, DeviceState};
use zbus::export::futures_util::lock::Mutex;
use zbus::{dbus_interface, Connection, SignalContext};
use zbus::{dbus_interface, CacheProperties, Connection, SignalContext};
use super::CtrlAnime;
use crate::error::RogError;
pub(super) const ZBUS_PATH: &str = "/org/asuslinux/Anime";
pub const ANIME_ZBUS_NAME: &str = "Anime";
pub const ANIME_ZBUS_PATH: &str = "/org/asuslinux/Anime";
async fn get_logind_manager<'a>() -> ManagerProxy<'a> {
let connection = Connection::system()
.await
.expect("Controller could not create dbus connection");
ManagerProxy::builder(&connection)
.cache_properties(CacheProperties::No)
.build()
.await
.expect("Controller could not create ManagerProxy")
}
#[derive(Clone)]
pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>);
@@ -24,7 +38,7 @@ pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>);
#[async_trait]
impl crate::ZbusRun for CtrlAnimeZbus {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, ZBUS_PATH, server).await;
Self::add_to_server_helper(self, ANIME_ZBUS_PATH, server).await;
}
}
@@ -39,156 +53,192 @@ impl CtrlAnimeZbus {
let lock = self.0.lock().await;
lock.thread_exit.store(true, Ordering::SeqCst);
lock.write_data_buffer(input).map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
warn!("ctrl_anime::run_animation:callback {}", err);
err
})?;
Ok(())
}
/// Set the global AniMe brightness
async fn set_image_brightness(&self, bright: f32) {
let mut lock = self.0.lock().await;
let mut bright = bright;
if bright < 0.0 {
bright = 0.0;
} else if bright > 1.0 {
bright = 1.0;
}
lock.config.brightness = bright;
lock.config.write();
/// Set base brightness level
#[dbus_interface(property)]
async fn brightness(&self) -> Brightness {
let lock = self.0.lock().await;
lock.config.display_brightness
}
/// Set base brightness level
// TODO: enum for brightness
async fn set_brightness(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
brightness: Brightness,
) {
#[dbus_interface(property)]
async fn set_brightness(&self, brightness: Brightness) {
let mut lock = self.0.lock().await;
lock.node
.write_bytes(&pkt_set_brightness(brightness))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
warn!("ctrl_anime::set_brightness {}", err);
})
.ok();
lock.node
.write_bytes(&pkt_set_enable_display(brightness != Brightness::Off))
.map_err(|err| {
warn!("ctrl_anime::set_brightness {}", err);
})
.ok();
lock.config.display_enabled = brightness != Brightness::Off;
lock.config.display_brightness = brightness;
lock.config.write();
}
Self::notify_device_state(
&ctxt,
DeviceState {
display_enabled: lock.config.display_enabled,
display_brightness: lock.config.display_brightness,
builtin_anims_enabled: lock.config.builtin_anims_enabled,
builtin_anims: lock.config.builtin_anims,
},
)
.await
.ok();
#[dbus_interface(property)]
async fn builtins_enabled(&self) -> bool {
let lock = self.0.lock().await;
lock.config.builtin_anims_enabled
}
/// Enable the builtin animations or not. This is quivalent to "Powersave
/// animations" in Armory crate
async fn set_builtins_enabled(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
enabled: bool,
) {
#[dbus_interface(property)]
async fn set_builtins_enabled(&self, enabled: bool) {
let mut lock = self.0.lock().await;
lock.node
.write_bytes(&pkt_set_enable_powersave_anim(enabled))
.set_builtins_enabled(enabled, lock.config.display_brightness)
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
warn!("ctrl_anime::set_builtins_enabled {}", err);
})
.ok();
if !enabled {
let data = vec![255u8; lock.anime_type.data_length()];
if let Ok(tmp) = AnimeDataBuffer::from_vec(lock.anime_type, data).map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err);
}) {
lock.node
.write_bytes(tmp.data())
.map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err);
})
.ok();
}
}
lock.config.builtin_anims_enabled = enabled;
lock.config.write();
if enabled {
lock.thread_exit.store(true, Ordering::Release);
}
}
Self::notify_device_state(
&ctxt,
DeviceState {
display_enabled: lock.config.display_enabled,
display_brightness: lock.config.display_brightness,
builtin_anims_enabled: lock.config.builtin_anims_enabled,
builtin_anims: lock.config.builtin_anims,
},
)
.await
.ok();
#[dbus_interface(property)]
async fn builtin_animations(&self) -> Animations {
let lock = self.0.lock().await;
lock.config.builtin_anims
}
/// Set which builtin animation is used for each stage
async fn set_builtin_animations(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
boot: AnimBooting,
awake: AnimAwake,
sleep: AnimSleeping,
shutdown: AnimShutdown,
) {
#[dbus_interface(property)]
async fn set_builtin_animations(&self, settings: Animations) {
let mut lock = self.0.lock().await;
lock.node
.write_bytes(&pkt_set_builtin_animations(
settings.boot,
settings.awake,
settings.sleep,
settings.shutdown,
))
.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
})
.ok();
lock.node
.write_bytes(&pkt_set_enable_powersave_anim(true))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
warn!("ctrl_anime::run_animation:callback {}", err);
})
.ok();
lock.node
.write_bytes(&pkt_set_builtin_animations(boot, awake, sleep, shutdown))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
lock.config.builtin_anims.boot = boot;
lock.config.builtin_anims.sleep = sleep;
lock.config.builtin_anims.awake = awake;
lock.config.builtin_anims.shutdown = shutdown;
lock.config.display_enabled = true;
lock.config.builtin_anims = settings;
lock.config.write();
}
Self::notify_device_state(
&ctxt,
DeviceState {
display_enabled: lock.config.display_enabled,
display_brightness: lock.config.display_brightness,
builtin_anims_enabled: lock.config.builtin_anims_enabled,
builtin_anims: lock.config.builtin_anims,
},
)
.await
.ok();
#[dbus_interface(property)]
async fn enable_display(&self) -> bool {
let lock = self.0.lock().await;
lock.config.display_enabled
}
/// Set whether the AniMe is enabled at all
async fn set_enable_display(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
enabled: bool,
) {
#[dbus_interface(property)]
async fn set_enable_display(&self, enabled: bool) {
let mut lock = self.0.lock().await;
lock.node
.write_bytes(&pkt_set_enable_display(enabled))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
warn!("ctrl_anime::run_animation:callback {}", err);
})
.ok();
lock.config.display_enabled = enabled;
lock.config.write();
}
Self::notify_device_state(
&ctxt,
DeviceState {
display_enabled: lock.config.display_enabled,
display_brightness: lock.config.display_brightness,
builtin_anims_enabled: lock.config.builtin_anims_enabled,
builtin_anims: lock.config.builtin_anims,
},
)
.await
#[dbus_interface(property)]
async fn off_when_unplugged(&self) -> bool {
let lock = self.0.lock().await;
lock.config.off_when_unplugged
}
/// Set if to turn the AniMe Matrix off when external power is unplugged
#[dbus_interface(property)]
async fn set_off_when_unplugged(&self, enabled: bool) {
let mut lock = self.0.lock().await;
let manager = get_logind_manager().await;
let pow = manager.on_external_power().await.unwrap_or_default();
lock.node
.write_bytes(&pkt_set_enable_display(!pow && !enabled))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
})
.ok();
lock.config.off_when_unplugged = enabled;
lock.config.write();
}
#[dbus_interface(property)]
async fn off_when_suspended(&self) -> bool {
let lock = self.0.lock().await;
lock.config.off_when_suspended
}
/// Set if to turn the AniMe Matrix off when the laptop is suspended
#[dbus_interface(property)]
async fn set_off_when_suspended(&self, enabled: bool) {
let mut lock = self.0.lock().await;
lock.config.off_when_suspended = enabled;
lock.config.write();
}
#[dbus_interface(property)]
async fn off_when_lid_closed(&self) -> bool {
let lock = self.0.lock().await;
lock.config.off_when_lid_closed
}
/// Set if to turn the AniMe Matrix off when the lid is closed
#[dbus_interface(property)]
async fn set_off_when_lid_closed(&self, enabled: bool) {
let mut lock = self.0.lock().await;
let manager = get_logind_manager().await;
let lid = manager.lid_closed().await.unwrap_or_default();
lock.node
.write_bytes(&pkt_set_enable_display(lid && !enabled))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
})
.ok();
lock.config.off_when_lid_closed = enabled;
lock.config.write();
}
/// The main loop is the base system set action if the user isn't running
@@ -205,24 +255,14 @@ impl CtrlAnimeZbus {
// #[dbus_interface(property)]
async fn device_state(&self) -> DeviceState {
let lock = self.0.lock().await;
DeviceState {
display_enabled: lock.config.display_enabled,
display_brightness: lock.config.display_brightness,
builtin_anims_enabled: lock.config.builtin_anims_enabled,
builtin_anims: lock.config.builtin_anims,
DeviceState::from(&lock.config)
}
}
/// Notify listeners of the status of AniMe LED power and factory
/// system-status animations
#[dbus_interface(signal)]
async fn notify_device_state(ctxt: &SignalContext<'_>, data: DeviceState) -> zbus::Result<()>;
}
#[async_trait]
impl crate::CtrlTask for CtrlAnimeZbus {
fn zbus_path() -> &'static str {
ZBUS_PATH
ANIME_ZBUS_PATH
}
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
@@ -231,36 +271,80 @@ impl crate::CtrlTask for CtrlAnimeZbus {
let inner3 = self.0.clone();
let inner4 = self.0.clone();
self.create_sys_event_tasks(
move || {
move |sleeping| {
// on_sleep
let inner1 = inner1.clone();
let inner = inner1.clone();
async move {
let lock = inner1.lock().await;
CtrlAnime::run_thread(inner1.clone(), lock.cache.sleep.clone(), true).await;
let lock = inner.lock().await;
if lock.config.display_enabled {
lock.thread_exit.store(true, Ordering::Release); // ensure clean slate
lock.node
.write_bytes(&pkt_set_enable_display(
!(sleeping && lock.config.off_when_suspended),
))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err);
})
.ok();
if !sleeping && !lock.config.builtin_anims_enabled {
CtrlAnime::run_thread(inner.clone(), lock.cache.wake.clone(), true)
.await;
}
}
}
},
move || {
// on_wake
let inner2 = inner2.clone();
async move {
let lock = inner2.lock().await;
CtrlAnime::run_thread(inner2.clone(), lock.cache.wake.clone(), true).await;
}
},
move || {
move |shutting_down| {
// on_shutdown
let inner3 = inner3.clone();
let inner = inner2.clone();
async move {
let lock = inner3.lock().await;
CtrlAnime::run_thread(inner3.clone(), lock.cache.shutdown.clone(), true).await;
let lock = inner.lock().await;
if lock.config.display_enabled && !lock.config.builtin_anims_enabled {
if shutting_down {
CtrlAnime::run_thread(inner.clone(), lock.cache.shutdown.clone(), true)
.await;
} else {
CtrlAnime::run_thread(inner.clone(), lock.cache.boot.clone(), true)
.await;
}
}
}
},
move || {
// on_boot
let inner4 = inner4.clone();
move |lid_closed| {
let inner = inner3.clone();
// on lid change
async move {
let lock = inner4.lock().await;
CtrlAnime::run_thread(inner4.clone(), lock.cache.boot.clone(), true).await;
let lock = inner.lock().await;
if lock.config.off_when_lid_closed {
lock.node
.write_bytes(&pkt_set_enable_display(lid_closed))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
})
.ok();
}
}
},
move |power_plugged| {
let inner = inner4.clone();
// on power change
async move {
let lock = inner.lock().await;
if lock.config.off_when_unplugged {
lock.node
.write_bytes(&pkt_set_enable_display(power_plugged))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_unplugged {}", err);
})
.ok();
} else {
lock.node
.write_bytes(&pkt_set_brightness(lock.config.brightness_on_battery))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_unplugged {}", err);
})
.ok();
}
}
},
)
@@ -275,26 +359,49 @@ impl crate::Reloadable for CtrlAnimeZbus {
async fn reload(&mut self) -> Result<(), RogError> {
if let Some(lock) = self.0.try_lock() {
let anim = &lock.config.builtin_anims;
lock.node
.write_bytes(&pkt_set_enable_display(lock.config.display_enabled))?;
lock.node.write_bytes(&pkt_set_enable_powersave_anim(
lock.config.builtin_anims_enabled,
))?;
// Set builtins
if lock.config.builtin_anims_enabled {
lock.node.write_bytes(&pkt_set_builtin_animations(
anim.boot,
anim.awake,
anim.sleep,
anim.shutdown,
))?;
}
// Builtins enabled or na?
lock.node.set_builtins_enabled(
lock.config.builtin_anims_enabled,
lock.config.display_brightness,
)?;
if lock.config.builtin_anims_enabled && !lock.cache.boot.is_empty() {
let manager = get_logind_manager().await;
let lid_closed = manager.lid_closed().await.unwrap_or_default();
let power_plugged = manager.on_external_power().await.unwrap_or_default();
let turn_off = (lid_closed && lock.config.off_when_lid_closed)
|| (!power_plugged && lock.config.off_when_unplugged);
lock.node
.write_bytes(&pkt_set_enable_display(!turn_off))
.map_err(|err| {
warn!("create_sys_event_tasks::reload {}", err);
})
.ok();
if turn_off || !lock.config.display_enabled {
lock.node.write_bytes(&pkt_set_enable_display(false))?;
// early return so we don't run animation thread
return Ok(());
}
if !lock.config.builtin_anims_enabled && !lock.cache.boot.is_empty() {
lock.node
.write_bytes(&pkt_set_enable_powersave_anim(false))
.ok();
}
let action = lock.cache.boot.clone();
CtrlAnime::run_thread(self.0.clone(), action, true).await;
}
}
Ok(())
}
}

View File

@@ -143,9 +143,9 @@ impl StdConfigLoad for AuraConfig {}
impl AuraConfig {
pub fn from_default_support(prod_id: AuraDevice, support_data: &LaptopLedData) -> Self {
// create a default config here
let enabled = if prod_id == AuraDevice::X19b6 {
let enabled = if prod_id.is_new_style() {
AuraPowerConfig::AuraDevRog2(AuraPower::new_all_on())
} else if prod_id == AuraDevice::Tuf {
} else if prod_id.is_tuf_style() {
AuraPowerConfig::AuraDevTuf(HashSet::from([
AuraDevTuf::Awake,
AuraDevTuf::Boot,

View File

@@ -1,53 +1,19 @@
use std::collections::BTreeMap;
use config_traits::{StdConfig, StdConfigLoad};
use dmi_id::DMIID;
use log::{info, warn};
use rog_aura::advanced::{LedUsbPackets, UsbPackets};
use rog_aura::aura_detection::{LaptopLedData, ASUS_KEYBOARD_DEVICES};
use rog_aura::usb::{AuraDevice, LED_APPLY, LED_SET};
use rog_aura::{AuraEffect, AuraZone, Direction, LedBrightness, Speed, GRADIENT, LED_MSG_LEN};
use rog_aura::{AuraEffect, Direction, LedBrightness, Speed, GRADIENT, LED_MSG_LEN};
use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardLed;
use rog_platform::supported::LedSupportedFunctions;
use super::config::{AuraConfig, AuraPowerConfig};
use crate::error::RogError;
use crate::GetSupported;
impl GetSupported for CtrlKbdLed {
type A = LedSupportedFunctions;
fn get_supported() -> Self::A {
// let mode = <&str>::from(&<AuraModes>::from(*mode));
let laptop = LaptopLedData::get_data();
let mut prod_id = AuraDevice::Unknown;
for prod in ASUS_KEYBOARD_DEVICES {
if HidRaw::new(prod.into()).is_ok() {
prod_id = prod;
break;
}
}
let rgb = KeyboardLed::new();
if let Ok(p) = rgb.as_ref() {
if p.has_kbd_rgb_mode() {
prod_id = AuraDevice::Tuf;
}
}
LedSupportedFunctions {
dev_id: prod_id,
brightness: rgb.is_ok(),
basic_modes: laptop.basic_modes,
basic_zones: laptop.basic_zones,
advanced_type: laptop.advanced_type.into(),
power_zones: laptop.power_zones,
}
}
}
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug)]
pub enum LEDNode {
KbdLed(KeyboardLed),
Rog(HidRaw),
@@ -58,9 +24,8 @@ pub struct CtrlKbdLed {
// TODO: config stores the keyboard type as an AuraPower, use or update this
pub led_prod: AuraDevice,
pub led_node: LEDNode,
pub kd_brightness: KeyboardLed,
pub sysfs_node: KeyboardLed,
pub supported_modes: LaptopLedData,
pub flip_effect_write: bool,
pub per_key_mode_active: bool,
pub config: AuraConfig,
}
@@ -90,15 +55,13 @@ impl CtrlKbdLed {
let rgb_led = KeyboardLed::new()?;
if usb_node.is_none() && !rgb_led.has_kbd_rgb_mode() {
let dmi = sysfs_class::DmiId::default();
if let Ok(prod_family) = dmi.product_family() {
if prod_family.contains("TUF") {
let dmi = DMIID::new().unwrap_or_default();
if dmi.dmi_family.contains("TUF") {
warn!(
"kbd_rgb_mode was not found in the /sys/. You require a minimum 6.1 \
kernel and a supported TUF laptop"
"kbd_rgb_mode was not found in the /sys/. You require a minimum 6.1 kernel \
and a supported TUF laptop"
);
}
}
return Err(RogError::NoAuraKeyboard);
}
@@ -147,49 +110,14 @@ impl CtrlKbdLed {
let ctrl = CtrlKbdLed {
led_prod,
led_node, // on TUF this is the same as rgb_led / kd_brightness
kd_brightness: rgb_led, // If was none then we already returned above
sysfs_node: rgb_led, // If was none then we already returned above
supported_modes,
flip_effect_write: false,
per_key_mode_active: false,
config: config_loaded,
};
Ok(ctrl)
}
pub(super) fn get_brightness(&self) -> Result<u8, RogError> {
self.kd_brightness
.get_brightness()
.map_err(RogError::Platform)
}
pub(super) fn set_brightness(&self, brightness: LedBrightness) -> Result<(), RogError> {
self.kd_brightness
.set_brightness(brightness as u8)
.map_err(RogError::Platform)
}
pub fn next_brightness(&mut self) -> Result<(), RogError> {
let mut bright = (self.config.brightness as u32) + 1;
if bright > 3 {
bright = 0;
}
self.config.brightness = <LedBrightness>::from(bright);
self.config.write();
self.set_brightness(self.config.brightness)
}
pub fn prev_brightness(&mut self) -> Result<(), RogError> {
let mut bright = self.config.brightness as u32;
if bright == 0 {
bright = 3;
} else {
bright -= 1;
}
self.config.brightness = <LedBrightness>::from(bright);
self.config.write();
self.set_brightness(self.config.brightness)
}
/// Set combination state for boot animation/sleep animation/all leds/keys
/// leds/side leds LED active
pub(super) fn set_power_states(&mut self) -> Result<(), RogError> {
@@ -210,29 +138,6 @@ impl CtrlKbdLed {
Ok(())
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
pub(crate) fn set_effect(&mut self, effect: AuraEffect) -> Result<(), RogError> {
if !self.supported_modes.basic_modes.contains(&effect.mode)
|| effect.zone != AuraZone::None
&& !self.supported_modes.basic_zones.contains(&effect.zone)
{
return Err(RogError::AuraEffectNotSupported);
}
self.write_mode(&effect)?;
self.config.read(); // refresh config if successful
self.config.set_builtin(effect);
if self.config.brightness == LedBrightness::Off {
self.config.brightness = LedBrightness::Med;
}
self.config.write();
self.set_brightness(self.config.brightness)?;
Ok(())
}
/// Write an effect block. This is for per-key, but can be repurposed to
/// write the raw factory mode packets - when doing this it is expected that
/// only the first `Vec` (`effect[0]`) is valid.
@@ -272,47 +177,11 @@ impl CtrlKbdLed {
tuf.set_kbd_rgb_mode(&[0, 0, r, g, b, 0])?;
}
}
self.flip_effect_write = !self.flip_effect_write;
}
Ok(())
}
pub(super) fn toggle_mode(&mut self, reverse: bool) -> Result<(), RogError> {
let current = self.config.current_mode;
if let Some(idx) = self
.supported_modes
.basic_modes
.iter()
.position(|v| *v == current)
{
let mut idx = idx;
// goes past end of array
if reverse {
if idx == 0 {
idx = self.supported_modes.basic_modes.len() - 1;
} else {
idx -= 1;
}
} else {
idx += 1;
if idx == self.supported_modes.basic_modes.len() {
idx = 0;
}
}
let next = self.supported_modes.basic_modes[idx];
self.config.read();
// if self.config.builtins.contains_key(&next) {
self.config.current_mode = next;
self.write_current_config_mode()?;
// }
self.config.write();
}
Ok(())
}
fn write_mode(&mut self, mode: &AuraEffect) -> Result<(), RogError> {
pub fn write_mode(&mut self, mode: &AuraEffect) -> Result<(), RogError> {
if let LEDNode::KbdLed(platform) = &self.led_node {
let buf = [
1,
@@ -405,82 +274,13 @@ impl CtrlKbdLed {
mod tests {
use rog_aura::aura_detection::{LaptopLedData, PowerZones};
use rog_aura::usb::AuraDevice;
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour};
use rog_aura::{AuraModeNum, AuraZone};
use rog_platform::keyboard_led::KeyboardLed;
use super::CtrlKbdLed;
use crate::ctrl_aura::config::AuraConfig;
use crate::ctrl_aura::controller::LEDNode;
#[test]
// #[ignore = "Must be manually run due to detection stage"]
fn check_set_mode_errors() {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::from_default_support(AuraDevice::X19b6, &LaptopLedData::default());
let supported_modes = LaptopLedData {
board_name: String::new(),
layout_name: "ga401".to_owned(),
basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![],
advanced_type: rog_aura::AdvancedAuraType::None,
power_zones: vec![PowerZones::Keyboard, PowerZones::RearGlow],
};
let mut controller = CtrlKbdLed {
led_prod: AuraDevice::X19b6,
led_node: LEDNode::None,
kd_brightness: KeyboardLed::default(),
supported_modes,
flip_effect_write: false,
per_key_mode_active: false,
config,
};
let mut effect = AuraEffect {
colour1: Colour {
r: 0xff,
g: 0x00,
b: 0xff,
},
zone: AuraZone::None,
..Default::default()
};
// This error comes from write_bytes because we don't have a keyboard node
// stored
assert_eq!(
controller
.set_effect(effect.clone())
.unwrap_err()
.to_string(),
"No supported Aura keyboard"
);
effect.mode = AuraModeNum::Laser;
assert_eq!(
controller
.set_effect(effect.clone())
.unwrap_err()
.to_string(),
"Aura effect not supported"
);
effect.mode = AuraModeNum::Static;
effect.zone = AuraZone::Key2;
assert_eq!(
controller
.set_effect(effect.clone())
.unwrap_err()
.to_string(),
"Aura effect not supported"
);
controller.supported_modes.basic_zones.push(AuraZone::Key2);
assert_eq!(
controller.set_effect(effect).unwrap_err().to_string(),
"No supported Aura keyboard"
);
}
#[test]
fn create_multizone_if_no_config() {
// Checking to ensure set_mode errors when unsupported modes are tried
@@ -496,9 +296,8 @@ mod tests {
let mut controller = CtrlKbdLed {
led_prod: AuraDevice::X19b6,
led_node: LEDNode::None,
kd_brightness: KeyboardLed::default(),
sysfs_node: KeyboardLed::default(),
supported_modes,
flip_effect_write: false,
per_key_mode_active: false,
config,
};
@@ -535,9 +334,8 @@ mod tests {
let mut controller = CtrlKbdLed {
led_prod: AuraDevice::X19b6,
led_node: LEDNode::None,
kd_brightness: KeyboardLed::default(),
sysfs_node: KeyboardLed::default(),
supported_modes,
flip_effect_write: false,
per_key_mode_active: false,
config,
};

View File

@@ -6,34 +6,36 @@ use config_traits::StdConfig;
use log::{debug, error, info, warn};
use rog_aura::advanced::UsbPackets;
use rog_aura::usb::{AuraDevice, AuraPowerDev};
use rog_aura::{AuraEffect, AuraModeNum, LedBrightness};
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, LedBrightness};
use zbus::export::futures_util::lock::{Mutex, MutexGuard};
use zbus::export::futures_util::StreamExt;
use zbus::fdo::Error as ZbErr;
use zbus::{dbus_interface, Connection, SignalContext};
use super::controller::CtrlKbdLed;
use crate::error::RogError;
use crate::CtrlTask;
pub(super) const ZBUS_PATH: &str = "/org/asuslinux/Aura";
pub const AURA_ZBUS_NAME: &str = "Aura";
pub const AURA_ZBUS_PATH: &str = "/org/asuslinux/Aura";
#[derive(Clone)]
pub struct CtrlKbdLedZbus(pub Arc<Mutex<CtrlKbdLed>>);
pub struct CtrlAuraZbus(pub Arc<Mutex<CtrlKbdLed>>);
impl CtrlKbdLedZbus {
impl CtrlAuraZbus {
fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> {
let bright = lock.kd_brightness.get_brightness()?;
let bright = lock.sysfs_node.get_brightness()?;
lock.config.read();
lock.config.brightness = (bright as u32).into();
lock.config.brightness = bright.into();
lock.config.write();
Ok(())
}
}
#[async_trait]
impl crate::ZbusRun for CtrlKbdLedZbus {
impl crate::ZbusRun for CtrlAuraZbus {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, ZBUS_PATH, server).await;
Self::add_to_server_helper(self, AURA_ZBUS_PATH, server).await;
}
}
@@ -41,25 +43,130 @@ impl crate::ZbusRun for CtrlKbdLedZbus {
///
/// LED commands are split between Brightness, Modes, Per-Key
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlKbdLedZbus {
/// Set the keyboard brightness level (0-3)
async fn set_brightness(&mut self, brightness: LedBrightness) {
impl CtrlAuraZbus {
/// Return the device type for this Aura keyboard
#[dbus_interface(property)]
async fn device_type(&self) -> AuraDevice {
let ctrl = self.0.lock().await;
ctrl.set_brightness(brightness)
.map_err(|err| warn!("{}", err))
.ok();
ctrl.led_prod
}
/// Return the current LED brightness
#[dbus_interface(property)]
async fn brightness(&self) -> Result<LedBrightness, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.sysfs_node.get_brightness().map(|n| n.into())?)
}
/// Set the keyboard brightness level (0-3)
#[dbus_interface(property)]
async fn set_brightness(&mut self, brightness: LedBrightness) -> Result<(), ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.sysfs_node.set_brightness(brightness.into())?)
}
/// Total levels of brightness available
#[dbus_interface(property)]
async fn supported_brightness(&self) -> Vec<LedBrightness> {
vec![
LedBrightness::Off,
LedBrightness::Low,
LedBrightness::Med,
LedBrightness::High,
]
}
/// The total available modes
#[dbus_interface(property)]
async fn supported_modes(&self) -> Result<Vec<AuraModeNum>, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.config.builtins.keys().cloned().collect())
}
/// The current mode data
#[dbus_interface(property)]
async fn led_mode(&self) -> Result<AuraModeNum, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.config.current_mode)
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[dbus_interface(property)]
async fn set_led_mode(&mut self, num: AuraModeNum) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
ctrl.config.current_mode = num;
ctrl.write_current_config_mode()?;
if ctrl.config.brightness == LedBrightness::Off {
ctrl.config.brightness = LedBrightness::Med;
}
ctrl.sysfs_node
.set_brightness(ctrl.config.brightness.into())?;
ctrl.config.write();
Ok(())
}
/// The current mode data
#[dbus_interface(property)]
async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> {
let ctrl = self.0.lock().await;
let mode = ctrl.config.current_mode;
match ctrl.config.builtins.get(&mode) {
Some(effect) => Ok(effect.clone()),
None => Err(ZbErr::Failed("Could not get the current effect".into())),
}
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[dbus_interface(property)]
async fn set_led_mode_data(&mut self, effect: AuraEffect) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
if !ctrl.supported_modes.basic_modes.contains(&effect.mode)
|| effect.zone != AuraZone::None
&& !ctrl.supported_modes.basic_zones.contains(&effect.zone)
{
return Err(ZbErr::NotSupported(format!(
"The Aura effect is not supported: {effect:?}"
)));
}
ctrl.write_mode(&effect)?;
if ctrl.config.brightness == LedBrightness::Off {
ctrl.config.brightness = LedBrightness::Med;
}
ctrl.sysfs_node
.set_brightness(ctrl.config.brightness.into())?;
ctrl.config.set_builtin(effect);
ctrl.config.write();
Ok(())
}
/// Get the data set for every mode available
async fn all_mode_data(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
let ctrl = self.0.lock().await;
ctrl.config.builtins.clone()
}
// As property doesn't work for AuraPowerDev (complexity of serialization?)
#[dbus_interface(property)]
async fn led_power(&self) -> AuraPowerDev {
let ctrl = self.0.lock().await;
AuraPowerDev::from(&ctrl.config.enabled)
}
/// Set a variety of states, input is array of enum.
/// `enabled` sets if the sent array should be disabled or enabled
///
/// For Modern ROG devices the "enabled" flag is ignored.
async fn set_led_power(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
options: AuraPowerDev,
enabled: bool,
) -> zbus::fdo::Result<()> {
#[dbus_interface(property)]
async fn set_led_power(&mut self, options: (AuraPowerDev, bool)) -> Result<(), ZbErr> {
let enabled = options.1;
let options = options.0;
let mut ctrl = self.0.lock().await;
for p in options.tuf {
ctrl.config.enabled.set_tuf(p, enabled);
@@ -68,158 +175,27 @@ impl CtrlKbdLedZbus {
ctrl.config.enabled.set_0x1866(p, enabled);
}
ctrl.config.enabled.set_0x19b6(options.rog);
ctrl.config.write();
ctrl.set_power_states().map_err(|e| {
Ok(ctrl.set_power_states().map_err(|e| {
warn!("{}", e);
e
})?;
Self::notify_power_states(&ctxt, &AuraPowerDev::from(&ctrl.config.enabled))
.await
.unwrap_or_else(|err| warn!("{}", err));
Ok(())
}
async fn set_led_mode(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
effect: AuraEffect,
) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await;
ctrl.set_effect(effect).map_err(|e| {
warn!("{}", e);
e
})?;
ctrl.set_brightness(ctrl.config.brightness).map_err(|e| {
warn!("{}", e);
e
})?;
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
Self::notify_led(&ctxt, mode.clone())
.await
.unwrap_or_else(|err| warn!("{}", err));
}
Ok(())
}
async fn next_led_mode(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await;
ctrl.toggle_mode(false).map_err(|e| {
warn!("{}", e);
e
})?;
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
Self::notify_led(&ctxt, mode.clone())
.await
.unwrap_or_else(|err| warn!("{}", err));
}
Ok(())
}
async fn prev_led_mode(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await;
ctrl.toggle_mode(true).map_err(|e| {
warn!("{}", e);
e
})?;
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
Self::notify_led(&ctxt, mode.clone())
.await
.unwrap_or_else(|err| warn!("{}", err));
}
Ok(())
}
async fn next_led_brightness(&self) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await;
ctrl.next_brightness().map_err(|e| {
warn!("{}", e);
e
})?;
Ok(())
}
async fn prev_led_brightness(&self) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await;
ctrl.prev_brightness().map_err(|e| {
warn!("{}", e);
e
})?;
Ok(())
}
/// Return the device type for this Aura keyboard
async fn device_type(&self) -> AuraDevice {
let ctrl = self.0.lock().await;
ctrl.led_prod
}
// As property doesn't work for AuraPowerDev (complexity of serialization?)
// #[dbus_interface(property)]
async fn led_power(&self) -> AuraPowerDev {
let ctrl = self.0.lock().await;
AuraPowerDev::from(&ctrl.config.enabled)
}
/// Return the current mode data
async fn led_mode(&self) -> AuraModeNum {
let ctrl = self.0.lock().await;
ctrl.config.current_mode
}
/// Return a list of available modes
async fn led_modes(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
let ctrl = self.0.lock().await;
ctrl.config.builtins.clone()
})?)
}
/// On machine that have some form of either per-key keyboard or per-zone
/// this can be used to write custom effects over dbus. The input is a
/// nested `Vec<Vec<8>>` where `Vec<u8>` is a raw USB packet
async fn direct_addressing_raw(&self, data: UsbPackets) -> zbus::fdo::Result<()> {
async fn direct_addressing_raw(&self, data: UsbPackets) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
ctrl.write_effect_block(&data)?;
Ok(())
}
/// Return the current LED brightness
#[dbus_interface(property)]
async fn led_brightness(&self) -> i8 {
let ctrl = self.0.lock().await;
ctrl.get_brightness().map(|n| n as i8).unwrap_or(-1)
}
#[dbus_interface(signal)]
async fn notify_led(signal_ctxt: &SignalContext<'_>, data: AuraEffect) -> zbus::Result<()>;
#[dbus_interface(signal)]
async fn notify_power_states(
signal_ctxt: &SignalContext<'_>,
data: &AuraPowerDev,
) -> zbus::Result<()>;
}
#[async_trait]
impl CtrlTask for CtrlKbdLedZbus {
impl CtrlTask for CtrlAuraZbus {
fn zbus_path() -> &'static str {
ZBUS_PATH
AURA_ZBUS_PATH
}
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
@@ -227,7 +203,8 @@ impl CtrlTask for CtrlKbdLedZbus {
// If waking up
if !start {
info!("CtrlKbdLedTask reloading brightness and modes");
lock.set_brightness(lock.config.brightness)
lock.sysfs_node
.set_brightness(lock.config.brightness.into())
.map_err(|e| error!("CtrlKbdLedTask: {e}"))
.ok();
lock.write_current_config_mode()
@@ -241,46 +218,36 @@ impl CtrlTask for CtrlKbdLedZbus {
};
let inner1 = self.0.clone();
let inner2 = self.0.clone();
let inner3 = self.0.clone();
let inner4 = self.0.clone();
self.create_sys_event_tasks(
// Loop so that we do aquire the lock but also don't block other
// threads (prevents potential deadlocks)
move || {
move |sleeping| {
let inner1 = inner1.clone();
async move {
let lock = inner1.lock().await;
load_save(true, lock);
load_save(sleeping, lock);
}
},
move || {
let inner2 = inner2.clone();
async move {
let lock = inner2.lock().await;
load_save(false, lock);
}
},
move || {
move |_shutting_down| {
let inner3 = inner3.clone();
async move {
let lock = inner3.lock().await;
load_save(false, lock);
}
},
move || {
let inner4 = inner4.clone();
async move {
let lock = inner4.lock().await;
load_save(false, lock);
}
move |_lid_closed| {
// on lid change
async move {}
},
move |_power_plugged| {
// power change
async move {}
},
)
.await;
let ctrl2 = self.0.clone();
let ctrl = self.0.lock().await;
let watch = ctrl.kd_brightness.monitor_brightness()?;
let watch = ctrl.sysfs_node.monitor_brightness()?;
tokio::spawn(async move {
let mut buffer = [0; 32];
watch
@@ -299,7 +266,7 @@ impl CtrlTask for CtrlKbdLedZbus {
}
#[async_trait]
impl crate::Reloadable for CtrlKbdLedZbus {
impl crate::Reloadable for CtrlAuraZbus {
async fn reload(&mut self) -> Result<(), RogError> {
let mut ctrl = self.0.lock().await;
debug!("CtrlKbdLedZbus: reloading keyboard mode");

295
asusd/src/ctrl_fancurves.rs Normal file
View File

@@ -0,0 +1,295 @@
use std::path::PathBuf;
use std::sync::Arc;
use async_trait::async_trait;
use config_traits::{StdConfig, StdConfigLoad};
use futures_lite::StreamExt;
use log::{debug, error, info, warn};
use rog_platform::platform::{PlatformPolicy, RogPlatform};
use rog_profiles::error::ProfileError;
use rog_profiles::fan_curve_set::CurveData;
use rog_profiles::{find_fan_curve_node, FanCurvePU, FanCurveProfiles};
use serde_derive::{Deserialize, Serialize};
use tokio::sync::Mutex;
use zbus::{dbus_interface, Connection, SignalContext};
use crate::error::RogError;
use crate::{CtrlTask, CONFIG_PATH_BASE};
const MOD_NAME: &str = "FanCurveZbus";
pub const FAN_CURVE_ZBUS_NAME: &str = "FanCurves";
pub const FAN_CURVE_ZBUS_PATH: &str = "/org/asuslinux/FanCurves";
#[derive(Deserialize, Serialize, Debug, Default)]
pub struct FanCurveConfig {
pub balanced: Vec<CurveData>,
pub performance: Vec<CurveData>,
pub quiet: Vec<CurveData>,
#[serde(skip)]
pub current: u8,
}
impl StdConfig for FanCurveConfig {
/// Create a new config. The defaults are zeroed so the device must be read
/// to get the actual device defaults.
fn new() -> Self {
Self::default()
}
fn file_name(&self) -> String {
"fan_curves.ron".to_owned()
}
fn config_dir() -> std::path::PathBuf {
PathBuf::from(CONFIG_PATH_BASE)
}
}
impl StdConfigLoad for FanCurveConfig {}
#[derive(Debug, Clone)]
pub struct CtrlFanCurveZbus {
config: Arc<Mutex<FanCurveConfig>>,
fan_curves: Arc<Mutex<FanCurveProfiles>>,
platform: RogPlatform,
}
// Non-zbus-derive impl
impl CtrlFanCurveZbus {
pub fn new() -> Result<Self, RogError> {
let platform = RogPlatform::new()?;
if platform.has_throttle_thermal_policy() {
info!("{MOD_NAME}: Device has profile control available");
find_fan_curve_node()?;
info!("{MOD_NAME}: Device has fan curves available");
let mut config = FanCurveConfig::new();
let mut fan_curves = FanCurveProfiles::default();
// Only do defaults if the config doesn't already exist
if !config.file_path().exists() {
info!("{MOD_NAME}: Fetching default fan curves");
for this in [
PlatformPolicy::Balanced,
PlatformPolicy::Performance,
PlatformPolicy::Quiet,
] {
// For each profile we need to switch to it before we
// can read the existing values from hardware. The ACPI method used
// for this is what limits us.
let next = PlatformPolicy::get_next_profile(this);
platform.set_throttle_thermal_policy(next.into())?;
let active = platform
.get_throttle_thermal_policy()
.map_or(PlatformPolicy::Balanced, |t| t.into());
info!("{MOD_NAME}: {active:?}:");
for curve in fan_curves.get_fan_curves_for(active) {
info!("{}", String::from(curve));
}
}
config.write();
} else {
info!("{MOD_NAME}: Fan curves previously stored, loading...");
config = config.load();
fan_curves.balanced = config.balanced.clone();
fan_curves.performance = config.performance.clone();
fan_curves.quiet = config.quiet.clone();
}
return Ok(Self {
config: Arc::new(Mutex::new(config)),
fan_curves: Arc::new(Mutex::new(fan_curves)),
platform,
});
}
Err(ProfileError::NotSupported.into())
}
pub async fn update_profiles_from_config(&self) {
let mut fan_curves = self.fan_curves.lock().await;
let config = self.config.lock().await;
fan_curves.balanced = config.balanced.clone();
fan_curves.performance = config.performance.clone();
fan_curves.quiet = config.quiet.clone();
}
pub async fn update_config_from_profiles(&self) {
let fan_curves = self.fan_curves.lock().await;
let mut config = self.config.lock().await;
config.balanced = fan_curves.balanced.clone();
config.performance = fan_curves.performance.clone();
config.quiet = fan_curves.quiet.clone();
}
}
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlFanCurveZbus {
/// Set all fan curves for a profile to enabled status. Will also activate a
/// fan curve if in the same profile mode
async fn set_fan_curves_enabled(
&mut self,
profile: PlatformPolicy,
enabled: bool,
) -> zbus::fdo::Result<()> {
let mut fan_curves = self.fan_curves.lock().await;
fan_curves.set_profile_curves_enabled(profile, enabled);
fan_curves.write_profile_curve_to_platform(profile, &mut find_fan_curve_node()?)?;
self.update_config_from_profiles().await;
self.config.lock().await.write();
Ok(())
}
/// Set a single fan curve for a profile to enabled status. Will also
/// activate a fan curve if in the same profile mode
async fn set_profile_fan_curve_enabled(
&mut self,
profile: PlatformPolicy,
fan: FanCurvePU,
enabled: bool,
) -> zbus::fdo::Result<()> {
let mut fan_curves = self.fan_curves.lock().await;
fan_curves.set_profile_fan_curve_enabled(profile, fan, enabled);
fan_curves.write_profile_curve_to_platform(profile, &mut find_fan_curve_node()?)?;
self.update_config_from_profiles().await;
self.config.lock().await.write();
Ok(())
}
/// Get the fan-curve data for the currently active PlatformPolicy
async fn fan_curve_data(
&mut self,
profile: PlatformPolicy,
) -> zbus::fdo::Result<Vec<CurveData>> {
let fan_curves = self.fan_curves.lock().await;
let curve = fan_curves.get_fan_curves_for(profile);
Ok(curve.to_vec())
}
/// Set the fan curve for the specified profile.
/// Will also activate the fan curve if the user is in the same mode.
async fn set_fan_curve(
&mut self,
profile: PlatformPolicy,
curve: CurveData,
) -> zbus::fdo::Result<()> {
let mut fan_curves = self.fan_curves.lock().await;
fan_curves.save_fan_curve(curve, profile)?;
fan_curves.write_profile_curve_to_platform(profile, &mut find_fan_curve_node()?)?;
self.update_config_from_profiles().await;
self.config.lock().await.write();
Ok(())
}
/// Reset the stored (self) and device curve to the defaults of the
/// platform.
///
/// Each platform_profile has a different default and the defualt can be
/// read only for the currently active profile.
async fn set_active_curve_to_defaults(&mut self) -> zbus::fdo::Result<()> {
let mut fan_curves = self.fan_curves.lock().await;
let active = self.platform.get_throttle_thermal_policy()?;
fan_curves.set_active_curve_to_defaults(active.into(), &mut find_fan_curve_node()?)?;
self.update_config_from_profiles().await;
self.config.lock().await.write();
Ok(())
}
/// Reset the stored (self) and device curve to the defaults of the
/// platform.
///
/// Each platform_profile has a different default and the defualt can be
/// read only for the currently active profile.
async fn reset_profile_curves(&self, profile: PlatformPolicy) -> zbus::fdo::Result<()> {
let mut fan_curves = self.fan_curves.lock().await;
let active = self
.platform
.get_throttle_thermal_policy()
.unwrap_or(PlatformPolicy::Balanced.into());
self.platform.set_throttle_thermal_policy(profile.into())?;
fan_curves.set_active_curve_to_defaults(active.into(), &mut find_fan_curve_node()?)?;
self.platform.set_throttle_thermal_policy(active)?;
self.update_config_from_profiles().await;
self.config.lock().await.write();
Ok(())
}
}
#[async_trait]
impl crate::ZbusRun for CtrlFanCurveZbus {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, FAN_CURVE_ZBUS_PATH, server).await;
}
}
#[async_trait]
impl CtrlTask for CtrlFanCurveZbus {
fn zbus_path() -> &'static str {
FAN_CURVE_ZBUS_PATH
}
async fn create_tasks(&self, _signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
let watch_throttle_thermal_policy = self.platform.monitor_throttle_thermal_policy()?;
let platform = self.platform.clone();
let config = self.config.clone();
let fan_curves = self.fan_curves.clone();
tokio::spawn(async move {
let mut buffer = [0; 32];
if let Ok(mut stream) = watch_throttle_thermal_policy.into_event_stream(&mut buffer) {
while (stream.next().await).is_some() {
debug!("watch_throttle_thermal_policy changed");
if let Ok(profile) = platform.get_throttle_thermal_policy().map_err(|e| {
error!("{MOD_NAME}: get_throttle_thermal_policy error: {e}");
}) {
if profile != config.lock().await.current {
fan_curves
.lock()
.await
.write_profile_curve_to_platform(
profile.into(),
&mut find_fan_curve_node().unwrap(),
)
.map_err(|e| {
warn!("{MOD_NAME}: write_profile_curve_to_platform, {}", e)
})
.ok();
config.lock().await.current = profile;
}
}
}
dbg!("STREAM ENDED");
}
});
Ok(())
}
}
#[async_trait]
impl crate::Reloadable for CtrlFanCurveZbus {
/// Fetch the active profile and use that to set all related components up
async fn reload(&mut self) -> Result<(), RogError> {
// let active = self.platform.get_throttle_thermal_policy()?.into();
// if let Ok(mut device) = find_fan_curve_node() {
// // There is a possibility that the curve was default zeroed, so this call
// // initialises the data from system read and we need to save it
// // after
// self.fan_curves
// .lock()
// .await
// .write_profile_curve_to_platform(active, &mut device)?;
// }
Ok(())
}
}

View File

@@ -1,86 +1,137 @@
use std::fs::OpenOptions;
use std::io::{Read, Write};
use std::path::Path;
use std::process::Command;
use std::sync::Arc;
use async_trait::async_trait;
use config_traits::StdConfig;
use log::{info, warn};
use rog_platform::platform::{AsusPlatform, GpuMode};
use rog_platform::supported::RogBiosSupportedFunctions;
use log::{debug, error, info, warn};
use rog_platform::cpu::CPUControl;
use rog_platform::platform::{GpuMode, PlatformPolicy, Properties, RogPlatform};
use rog_platform::power::AsusPower;
use zbus::export::futures_util::lock::Mutex;
use zbus::{dbus_interface, Connection, SignalContext};
use zbus::fdo::Error as FdoErr;
use zbus::{dbus_interface, Connection, ObjectServer, SignalContext};
use crate::config::Config;
use crate::ctrl_anime::trait_impls::{CtrlAnimeZbus, ANIME_ZBUS_NAME, ANIME_ZBUS_PATH};
use crate::ctrl_aura::trait_impls::{CtrlAuraZbus, AURA_ZBUS_NAME, AURA_ZBUS_PATH};
use crate::ctrl_fancurves::{CtrlFanCurveZbus, FAN_CURVE_ZBUS_NAME, FAN_CURVE_ZBUS_PATH};
use crate::error::RogError;
use crate::{task_watch_item, CtrlTask, GetSupported};
use crate::{task_watch_item, task_watch_item_notify, CtrlTask};
const ZBUS_PATH: &str = "/org/asuslinux/Platform";
const ASUS_POST_LOGO_SOUND: &str =
"/sys/firmware/efi/efivars/AsusPostLogoSound-607005d5-3f75-4b2e-98f0-85ba66797a3e";
macro_rules! platform_get_value {
($self:ident, $property:tt, $prop_name:literal) => {
concat_idents::concat_idents!(has = has_, $property {
if $self.platform.has() {
concat_idents::concat_idents!(get = get_, $property {
$self.platform
.get()
.map_err(|err| {
warn!("RogPlatform: {}: {}", $prop_name, err);
FdoErr::Failed(format!("RogPlatform: {}: {}", $prop_name, err))
})
})
} else {
error!("RogPlatform: {} not supported", $prop_name);
return Err(FdoErr::NotSupported(format!("RogPlatform: {} not supported", $prop_name)));
}
})
}
}
macro_rules! platform_get_value_if_some {
($self:ident, $property:tt, $prop_name:literal, $default:expr) => {
concat_idents::concat_idents!(has = has_, $property {
if $self.platform.has() {
let lock = $self.config.lock().await;
Ok(lock.ppt_pl1_spl.unwrap_or($default))
} else {
error!("RogPlatform: {} not supported", $prop_name);
return Err(FdoErr::NotSupported(format!("RogPlatform: {} not supported", $prop_name)));
}
})
}
}
macro_rules! platform_set_bool {
($self:ident, $property:tt, $prop_name:literal, $new_value:expr) => {
concat_idents::concat_idents!(has = has_, $property {
if $self.platform.has() {
concat_idents::concat_idents!(set = set_, $property {
$self.platform.set($new_value).map_err(|err| {
error!("RogPlatform: {} {err}", $prop_name);
FdoErr::NotSupported(format!("RogPlatform: {} {err}", $prop_name))
})?;
});
let mut lock = $self.config.lock().await;
lock.$property = $new_value;
lock.write();
Ok(())
} else {
error!("RogPlatform: {} not supported", $prop_name);
Err(FdoErr::NotSupported(format!("RogPlatform: {} not supported", $prop_name)))
}
})
}
}
/// Intended only for setting platform object values where the value isn't
/// retained across boots
macro_rules! platform_set_with_min_max {
($self:ident, $property:tt, $prop_name:literal, $new_value:expr, $min_value:expr, $max_value:expr) => {
if !($min_value..=$max_value).contains(&$new_value) {
Err(FdoErr::Failed(
format!("RogPlatform: {} value not in range {}=..={}", $prop_name, $min_value, $max_value)
))
} else {
concat_idents::concat_idents!(has = has_, $property {
if $self.platform.has() {
concat_idents::concat_idents!(set = set_, $property {
$self.platform.set($new_value).map_err(|err| {
error!("RogPlatform: {} {err}", $prop_name);
FdoErr::NotSupported(format!("RogPlatform: {} {err}", $prop_name))
})?;
});
let mut lock = $self.config.lock().await;
lock.$property = Some($new_value);
lock.write();
} else {
error!("RogPlatform: {} not supported", $prop_name);
return Err(FdoErr::NotSupported(format!("RogPlatform: {} not supported", $prop_name)));
}
});
Ok(())
}
}
}
#[derive(Clone)]
pub struct CtrlPlatform {
platform: AsusPlatform,
power: AsusPower,
platform: RogPlatform,
cpu_control: Option<CPUControl>,
config: Arc<Mutex<Config>>,
}
impl GetSupported for CtrlPlatform {
type A = RogBiosSupportedFunctions;
fn get_supported() -> Self::A {
let mut panel_overdrive = false;
let mut mini_led_mode = false;
let mut dgpu_disable = false;
let mut egpu_enable = false;
let mut gpu_mux = false;
if let Ok(platform) = AsusPlatform::new() {
panel_overdrive = platform.has_panel_od();
mini_led_mode = platform.has_mini_led_mode();
dgpu_disable = platform.has_dgpu_disable();
egpu_enable = platform.has_egpu_enable();
gpu_mux = platform.has_gpu_mux_mode();
}
RogBiosSupportedFunctions {
post_sound: Path::new(ASUS_POST_LOGO_SOUND).exists(),
gpu_mux,
panel_overdrive,
mini_led_mode,
dgpu_disable,
egpu_enable,
}
}
}
impl CtrlPlatform {
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
let platform = AsusPlatform::new()?;
let platform = RogPlatform::new()?;
let power = AsusPower::new()?;
if !platform.has_gpu_mux_mode() {
info!("G-Sync Switchable Graphics or GPU MUX not detected");
info!("Standard graphics switching will still work.");
}
if Path::new(ASUS_POST_LOGO_SOUND).exists() {
CtrlPlatform::set_path_mutable(ASUS_POST_LOGO_SOUND)?;
} else {
info!("Switch for POST boot sound not detected");
}
Ok(CtrlPlatform { platform, config })
}
fn set_path_mutable(path: &str) -> Result<(), RogError> {
let output = Command::new("/usr/bin/chattr")
.arg("-i")
.arg(path)
.output()
.map_err(|err| RogError::Path(path.into(), err))?;
info!("Set {} writeable: status: {}", path, output.status);
Ok(())
Ok(CtrlPlatform {
power,
platform,
config,
cpu_control: CPUControl::new()
.map_err(|e| error!("Couldn't get CPU control sysfs: {e}"))
.ok(),
})
}
fn set_gfx_mode(&self, mode: GpuMode) -> Result<(), RogError> {
@@ -94,232 +145,362 @@ impl CtrlPlatform {
Ok(())
}
pub fn get_boot_sound() -> Result<i8, RogError> {
let data = std::fs::read(ASUS_POST_LOGO_SOUND)
.map_err(|err| RogError::Read(ASUS_POST_LOGO_SOUND.into(), err))?;
let idx = data.len() - 1;
Ok(data[idx] as i8)
}
pub(super) fn set_boot_sound(on: bool) -> Result<(), RogError> {
let path = ASUS_POST_LOGO_SOUND;
let mut file = OpenOptions::new()
.read(true)
.write(true)
.open(path)
.map_err(|err| RogError::Path(path.into(), err))?;
let mut data = Vec::new();
#[allow(clippy::verbose_file_reads)]
file.read_to_end(&mut data)
.map_err(|err| RogError::Read(path.into(), err))?;
let idx = data.len() - 1;
if on {
data[idx] = 1;
info!("Set boot POST sound on");
async fn run_ac_or_bat_cmd(&self, power_plugged: bool) {
let prog: Vec<String> = if power_plugged {
// AC ONLINE
self.config
.lock()
.await
.ac_command
.split_whitespace()
.map(|s| s.to_string())
.collect()
} else {
data[idx] = 0;
info!("Set boot POST sound off");
// BATTERY
self.config
.lock()
.await
.bat_command
.split_whitespace()
.map(|s| s.to_string())
.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 power_plugged {
error!("AC power command error: {e}");
} else {
error!("Battery power command error: {e}");
}
}
}
file.write_all(&data)
.map_err(|err| RogError::Path(path.into(), err))?;
Ok(())
}
fn set_panel_overdrive(&self, enable: bool) -> Result<(), RogError> {
self.platform.set_panel_od(enable).map_err(|err| {
warn!("CtrlRogBios: set_panel_overdrive {}", err);
err
})?;
Ok(())
async fn update_policy_ac_or_bat(&self, power_plugged: bool) {
let profile = if power_plugged {
self.config.lock().await.platform_policy_on_ac
} else {
self.config.lock().await.platform_policy_on_battery
};
self.platform
.set_throttle_thermal_policy(profile.into())
.ok();
if let Some(cpu) = self.cpu_control.as_ref() {
cpu.set_epp(profile.into()).ok();
}
}
}
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlPlatform {
async fn set_gpu_mux_mode(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
mode: GpuMode,
) {
self.set_gfx_mode(mode)
.map_err(|err| {
warn!("CtrlRogBios: set_gpu_mux_mode {}", err);
err
/// Returns a list of property names that this system supports
async fn supported_properties(&self) -> Vec<Properties> {
let mut supported = Vec::new();
macro_rules! platform_name {
($property:tt, $prop_name:ty) => {
concat_idents::concat_idents!(has = has_, $property {
if self.platform.has() {
supported.push($prop_name.to_owned());
}
})
.ok();
Self::notify_gpu_mux_mode(&ctxt, mode).await.ok();
}
fn gpu_mux_mode(&self) -> GpuMode {
match self.platform.get_gpu_mux_mode() {
Ok(m) => GpuMode::from_mux(m as u8),
Err(e) => {
warn!("CtrlRogBios: get_gfx_mode {}", e);
GpuMode::Error
}
}
}
#[dbus_interface(signal)]
async fn notify_gpu_mux_mode(
signal_ctxt: &SignalContext<'_>,
mode: GpuMode,
) -> zbus::Result<()> {
macro_rules! power_name {
($property:tt, $prop_name:ty) => {
concat_idents::concat_idents!(has = has_, $property {
if self.power.has() {
supported.push($prop_name.to_owned());
}
async fn set_post_boot_sound(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
on: bool,
) {
Self::set_boot_sound(on)
.map_err(|err| {
warn!("CtrlRogBios: set_post_boot_sound {}", err);
err
})
.ok();
Self::notify_post_boot_sound(&ctxt, on)
}
}
// TODO: automate this
power_name!(
charge_control_end_threshold,
Properties::ChargeControlEndThreshold
);
platform_name!(dgpu_disable, Properties::DgpuDisable);
platform_name!(gpu_mux_mode, Properties::GpuMuxMode);
platform_name!(post_animation_sound, Properties::PostAnimationSound);
platform_name!(panel_od, Properties::PanelOd);
platform_name!(mini_led_mode, Properties::MiniLedMode);
platform_name!(egpu_enable, Properties::EgpuEnable);
platform_name!(throttle_thermal_policy, Properties::PlatformPolicy);
platform_name!(ppt_pl1_spl, Properties::PptPl1Spl);
platform_name!(ppt_pl2_sppt, Properties::PptPl2Sppt);
platform_name!(ppt_fppt, Properties::PptFppt);
platform_name!(ppt_apu_sppt, Properties::PptApuSppt);
platform_name!(ppt_platform_sppt, Properties::PptPlatformSppt);
platform_name!(nv_dynamic_boost, Properties::NvDynamicBoost);
platform_name!(nv_temp_target, Properties::NvTempTarget);
supported
}
async fn supported_interfaces(
&self,
#[zbus(object_server)] server: &ObjectServer,
) -> Vec<String> {
let mut interfaces = Vec::default();
if server
.interface::<_, CtrlAnimeZbus>(ANIME_ZBUS_PATH)
.await
.map_err(|err| {
warn!("CtrlRogBios: set_post_boot_sound {}", err);
err
.is_ok()
{
interfaces.push(ANIME_ZBUS_NAME.to_owned());
}
if server
.interface::<_, CtrlAuraZbus>(AURA_ZBUS_PATH)
.await
.is_ok()
{
interfaces.push(AURA_ZBUS_NAME.to_owned());
}
if server
.interface::<_, CtrlFanCurveZbus>(FAN_CURVE_ZBUS_PATH)
.await
.is_ok()
{
interfaces.push(FAN_CURVE_ZBUS_NAME.to_owned());
}
interfaces
}
#[dbus_interface(property)]
fn charge_control_end_threshold(&self) -> Result<u8, FdoErr> {
let limit = self.power.get_charge_control_end_threshold()?;
Ok(limit)
}
#[dbus_interface(property)]
async fn set_charge_control_end_threshold(&mut self, limit: u8) -> Result<(), FdoErr> {
if !(20..=100).contains(&limit) {
return Err(RogError::ChargeLimit(limit))?;
}
self.power.set_charge_control_end_threshold(limit)?;
self.config.lock().await.charge_control_end_threshold = limit;
Ok(())
}
#[dbus_interface(property)]
fn gpu_mux_mode(&self) -> Result<u8, FdoErr> {
self.platform.get_gpu_mux_mode().map_err(|err| {
warn!("RogPlatform: set_gpu_mux_mode {err}");
FdoErr::NotSupported("RogPlatform: set_gpu_mux_mode not supported".to_owned())
})
.ok();
}
fn post_boot_sound(&self) -> i8 {
Self::get_boot_sound()
.map_err(|err| {
warn!("CtrlRogBios: get_boot_sound {}", err);
err
#[dbus_interface(property)]
async fn set_gpu_mux_mode(&mut self, mode: u8) -> Result<(), FdoErr> {
if self.platform.has_gpu_mux_mode() {
self.set_gfx_mode(mode.into()).map_err(|err| {
warn!("RogPlatform: set_gpu_mux_mode {}", err);
FdoErr::Failed(format!("RogPlatform: set_gpu_mux_mode: {err}"))
})
.unwrap_or(-1)
} else {
Err(FdoErr::NotSupported(
"RogPlatform: set_gpu_mux_mode not supported".to_owned(),
))
}
}
#[dbus_interface(signal)]
async fn notify_post_boot_sound(ctxt: &SignalContext<'_>, on: bool) -> zbus::Result<()> {}
/// Toggle to next platform_profile. Names provided by `Profiles`.
/// If fan-curves are supported will also activate a fan curve for profile.
async fn next_throttle_thermal_policy(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
) -> Result<(), FdoErr> {
let policy: PlatformPolicy =
platform_get_value!(self, throttle_thermal_policy, "throttle_thermal_policy")
.map(|n| n.into())?;
let policy = PlatformPolicy::next(&policy);
async fn set_panel_od(&mut self, overdrive: bool) {
match self.platform.set_panel_od(overdrive) {
Ok(_) => {
if let Some(mut lock) = self.config.try_lock() {
lock.panel_od = overdrive;
lock.write();
if self.platform.has_throttle_thermal_policy() {
if let Some(cpu) = self.cpu_control.as_ref() {
info!("PlatformPolicy setting EPP");
cpu.set_epp(policy.into())?
}
self.platform
.set_throttle_thermal_policy(policy.into())
.map_err(|err| {
warn!("RogPlatform: throttle_thermal_policy {}", err);
FdoErr::Failed(format!("RogPlatform: throttle_thermal_policy: {err}"))
})?;
self.config.lock().await.platform_policy_to_restore = policy;
Ok(self.throttle_thermal_policy_changed(&ctxt).await?)
} else {
Err(FdoErr::NotSupported(
"RogPlatform: throttle_thermal_policy not supported".to_owned(),
))
}
}
Err(err) => warn!("CtrlRogBios: set_panel_overdrive {}", err),
};
#[dbus_interface(property)]
fn throttle_thermal_policy(&self) -> Result<PlatformPolicy, FdoErr> {
platform_get_value!(self, throttle_thermal_policy, "throttle_thermal_policy")
.map(|n| n.into())
}
#[dbus_interface(property)]
async fn set_throttle_thermal_policy(&mut self, policy: PlatformPolicy) -> Result<(), FdoErr> {
// TODO: watch for external changes
if self.platform.has_throttle_thermal_policy() {
if let Some(cpu) = self.cpu_control.as_ref() {
info!("PlatformPolicy setting EPP");
cpu.set_epp(policy.into())?
}
self.config.lock().await.platform_policy_to_restore = policy;
self.platform
.set_throttle_thermal_policy(policy.into())
.map_err(|err| {
warn!("RogPlatform: throttle_thermal_policy {}", err);
FdoErr::Failed(format!("RogPlatform: throttle_thermal_policy: {err}"))
})
} else {
Err(FdoErr::NotSupported(
"RogPlatform: throttle_thermal_policy not supported".to_owned(),
))
}
}
#[dbus_interface(property)]
fn post_animation_sound(&self) -> Result<bool, FdoErr> {
platform_get_value!(self, post_animation_sound, "post_animation_sound")
}
#[dbus_interface(property)]
async fn set_post_animation_sound(&mut self, on: bool) -> Result<(), FdoErr> {
if self.platform.has_post_animation_sound() {
self.platform.set_post_animation_sound(on).map_err(|err| {
warn!("RogPlatform: set_post_animation_sound {}", err);
FdoErr::Failed(format!("RogPlatform: set_post_animation_sound: {err}"))
})
} else {
Err(FdoErr::NotSupported(
"RogPlatform: set_post_animation_sound not supported".to_owned(),
))
}
}
/// Get the `panel_od` value from platform. Updates the stored value in
/// internal config also.
fn panel_od(&self) -> bool {
self.platform
.get_panel_od()
.map_err(|err| {
warn!("CtrlRogBios: get_panel_od {}", err);
err
})
.unwrap_or(false)
#[dbus_interface(property)]
fn panel_od(&self) -> Result<bool, FdoErr> {
platform_get_value!(self, panel_od, "panel_od")
}
#[dbus_interface(signal)]
async fn notify_panel_od(signal_ctxt: &SignalContext<'_>, overdrive: bool) -> zbus::Result<()> {
}
async fn set_mini_led_mode(&mut self, on: bool) {
match self.platform.set_mini_led_mode(on) {
Ok(_) => {
if let Some(mut lock) = self.config.try_lock() {
lock.mini_led_mode = on;
lock.write();
}
}
Err(err) => warn!("CtrlRogBios: set_mini_led_mode {}", err),
};
#[dbus_interface(property)]
async fn set_panel_od(&mut self, overdrive: bool) -> Result<(), FdoErr> {
platform_set_bool!(self, panel_od, "panel_od", overdrive)
}
/// Get the `panel_od` value from platform. Updates the stored value in
/// internal config also.
fn mini_led_mode(&self) -> bool {
self.platform
.get_mini_led_mode()
.map_err(|err| {
warn!("CtrlRogBios: get_mini_led_mode {}", err);
err
})
.unwrap_or(false)
#[dbus_interface(property)]
fn mini_led_mode(&self) -> Result<bool, FdoErr> {
platform_get_value!(self, mini_led_mode, "mini_led_mode")
}
#[dbus_interface(signal)]
async fn notify_mini_led_mode(signal_ctxt: &SignalContext<'_>, on: bool) -> zbus::Result<()> {}
async fn set_dgpu_disable(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
disable: bool,
) {
match self.platform.set_dgpu_disable(disable) {
Ok(_) => {
Self::notify_dgpu_disable(&ctxt, disable).await.ok();
}
Err(err) => warn!("CtrlRogBios: set_dgpu_disable {}", err),
};
#[dbus_interface(property)]
async fn set_mini_led_mode(&mut self, on: bool) -> Result<(), FdoErr> {
platform_set_bool!(self, mini_led_mode, "mini_led_mode", on)
}
fn dgpu_disable(&self) -> bool {
self.platform
.get_dgpu_disable()
.map_err(|err| {
warn!("CtrlRogBios: get_dgpu_disable {}", err);
err
})
.unwrap_or(false)
#[dbus_interface(property)]
fn dgpu_disable(&self) -> Result<bool, FdoErr> {
platform_get_value!(self, dgpu_disable, "dgpu_disable")
}
#[dbus_interface(signal)]
async fn notify_dgpu_disable(
signal_ctxt: &SignalContext<'_>,
disable: bool,
) -> zbus::Result<()> {
#[dbus_interface(property)]
fn egpu_enable(&self) -> Result<bool, FdoErr> {
platform_get_value!(self, egpu_enable, "egpu_enable")
}
async fn set_egpu_enable(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
enable: bool,
) {
match self.platform.set_egpu_enable(enable) {
Ok(_) => {
Self::notify_egpu_enable(&ctxt, enable).await.ok();
}
Err(err) => warn!("CtrlRogBios: set_egpu_enable {}", err),
};
/// ************************************************************************
#[dbus_interface(property)]
async fn ppt_pl1_spl(&self) -> Result<u8, FdoErr> {
platform_get_value_if_some!(self, ppt_pl1_spl, "ppt_pl1_spl", 5)
}
fn egpu_enable(&self) -> bool {
self.platform
.get_egpu_enable()
.map_err(|err| {
warn!("CtrlRogBios: get_egpu_enable {}", err);
err
})
.unwrap_or(false)
#[dbus_interface(property)]
async fn set_ppt_pl1_spl(&mut self, value: u8) -> Result<(), FdoErr> {
platform_set_with_min_max!(self, ppt_pl1_spl, "ppt_pl1_spl", value, 5, 250)
}
#[dbus_interface(signal)]
async fn notify_egpu_enable(signal_ctxt: &SignalContext<'_>, enable: bool) -> zbus::Result<()> {
#[dbus_interface(property)]
async fn ppt_pl2_sppt(&self) -> Result<u8, FdoErr> {
platform_get_value_if_some!(self, ppt_pl2_sppt, "ppt_pl2_sppt", 5)
}
#[dbus_interface(property)]
async fn set_ppt_pl2_sppt(&mut self, value: u8) -> Result<(), FdoErr> {
platform_set_with_min_max!(self, ppt_pl2_sppt, "ppt_pl2_sppt", value, 5, 250)
}
#[dbus_interface(property)]
async fn ppt_fppt(&self) -> Result<u8, FdoErr> {
platform_get_value_if_some!(self, ppt_fppt, "ppt_fppt", 5)
}
#[dbus_interface(property)]
async fn set_ppt_fppt(&mut self, value: u8) -> Result<(), FdoErr> {
platform_set_with_min_max!(self, ppt_fppt, "ppt_fppt", value, 5, 250)
}
#[dbus_interface(property)]
async fn ppt_apu_sppt(&self) -> Result<u8, FdoErr> {
platform_get_value_if_some!(self, ppt_apu_sppt, "ppt_apu_sppt", 5)
}
#[dbus_interface(property)]
async fn set_ppt_apu_sppt(&mut self, value: u8) -> Result<(), FdoErr> {
platform_set_with_min_max!(self, ppt_apu_sppt, "ppt_apu_sppt", value, 5, 130)
}
#[dbus_interface(property)]
async fn ppt_platform_sppt(&self) -> Result<u8, FdoErr> {
platform_get_value_if_some!(self, ppt_platform_sppt, "ppt_platform_sppt", 5)
}
#[dbus_interface(property)]
async fn set_ppt_platform_sppt(&mut self, value: u8) -> Result<(), FdoErr> {
platform_set_with_min_max!(self, ppt_platform_sppt, "ppt_platform_sppt", value, 5, 130)
}
#[dbus_interface(property)]
async fn nv_dynamic_boost(&self) -> Result<u8, FdoErr> {
platform_get_value_if_some!(self, nv_dynamic_boost, "nv_dynamic_boost", 5)
}
#[dbus_interface(property)]
async fn set_nv_dynamic_boost(&mut self, value: u8) -> Result<(), FdoErr> {
platform_set_with_min_max!(self, nv_dynamic_boost, "nv_dynamic_boost", value, 5, 25)
}
#[dbus_interface(property)]
async fn nv_temp_target(&self) -> Result<u8, FdoErr> {
platform_get_value_if_some!(self, nv_temp_target, "nv_temp_target", 5)
}
#[dbus_interface(property)]
async fn set_nv_temp_target(&mut self, value: u8) -> Result<(), FdoErr> {
platform_set_with_min_max!(self, nv_temp_target, "nv_temp_target", value, 5, 87)
}
}
#[async_trait]
impl crate::ZbusRun for CtrlPlatform {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, "/org/asuslinux/Platform", server).await;
Self::add_to_server_helper(self, ZBUS_PATH, server).await;
}
}
@@ -327,13 +508,28 @@ impl crate::ZbusRun for CtrlPlatform {
impl crate::Reloadable for CtrlPlatform {
async fn reload(&mut self) -> Result<(), RogError> {
if self.platform.has_panel_od() {
let p = if let Some(lock) = self.config.try_lock() {
lock.panel_od
} else {
false
};
self.set_panel_overdrive(p)?;
self.platform
.set_panel_od(self.config.lock().await.panel_od)?;
}
if self.platform.has_mini_led_mode() {
self.platform
.set_mini_led_mode(self.config.lock().await.mini_led_mode)?;
}
if self.power.has_charge_control_end_threshold() {
self.power.set_charge_control_end_threshold(
self.config.lock().await.charge_control_end_threshold,
)?;
}
if let Ok(power_plugged) = self.power.get_online() {
if self.platform.has_throttle_thermal_policy() {
self.update_policy_ac_or_bat(power_plugged > 0).await;
}
self.run_ac_or_bat_cmd(power_plugged > 0).await;
}
Ok(())
}
}
@@ -341,13 +537,32 @@ impl crate::Reloadable for CtrlPlatform {
impl CtrlPlatform {
task_watch_item!(panel_od platform);
task_watch_item!(dgpu_disable platform);
task_watch_item!(egpu_enable platform);
task_watch_item!(mini_led_mode platform);
task_watch_item!(charge_control_end_threshold power);
task_watch_item_notify!(post_animation_sound platform);
task_watch_item_notify!(dgpu_disable platform);
task_watch_item_notify!(egpu_enable platform);
// NOTE: see note further below
// task_watch_item!(gpu_mux_mode platform);
task_watch_item_notify!(gpu_mux_mode platform);
task_watch_item_notify!(ppt_pl1_spl platform);
task_watch_item_notify!(ppt_pl2_sppt platform);
task_watch_item_notify!(ppt_fppt platform);
task_watch_item_notify!(ppt_apu_sppt platform);
task_watch_item_notify!(ppt_platform_sppt platform);
task_watch_item_notify!(nv_dynamic_boost platform);
task_watch_item_notify!(nv_temp_target platform);
}
#[async_trait]
@@ -359,16 +574,54 @@ impl CtrlTask for CtrlPlatform {
async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
let platform1 = self.clone();
let platform2 = self.clone();
let platform3 = self.clone();
self.create_sys_event_tasks(
move || async { {} },
move || {
move |sleeping| {
let platform1 = platform1.clone();
async move {
info!("CtrlRogBios reloading panel_od");
let lock = platform1.config.lock().await;
if platform1.platform.has_panel_od() {
info!("RogPlatform reloading panel_od");
if !sleeping && platform1.platform.has_panel_od() {
platform1
.set_panel_overdrive(lock.panel_od)
.platform
.set_panel_od(platform1.config.lock().await.panel_od)
.map_err(|err| {
warn!("CtrlCharge: panel_od {}", err);
err
})
.ok();
}
if sleeping && platform1.power.has_charge_control_end_threshold() {
platform1.config.lock().await.charge_control_end_threshold = platform1
.power
.get_charge_control_end_threshold()
.unwrap_or(100);
} else if !sleeping && platform1.power.has_charge_control_end_threshold() {
platform1
.power
.set_charge_control_end_threshold(
platform1.config.lock().await.charge_control_end_threshold,
)
.ok();
}
if let Ok(power_plugged) = platform1.power.get_online() {
if !sleeping && platform1.platform.has_throttle_thermal_policy() {
platform1.update_policy_ac_or_bat(power_plugged > 0).await;
}
if !sleeping {
platform1.run_ac_or_bat_cmd(power_plugged > 0).await;
}
}
}
},
move |shutting_down| {
let platform2 = platform2.clone();
async move {
info!("RogPlatform reloading panel_od");
let lock = platform2.config.lock().await;
if !shutting_down && platform2.platform.has_panel_od() {
platform2
.platform
.set_panel_od(lock.panel_od)
.map_err(|err| {
warn!("CtrlCharge: panel_od {}", err);
err
@@ -377,33 +630,73 @@ impl CtrlTask for CtrlPlatform {
}
}
},
move || async { {} },
move || {
let platform2 = platform2.clone();
move |_lid_closed| {
// on lid change
async move {}
},
move |power_plugged| {
let platform3 = platform3.clone();
// power change
async move {
info!("CtrlRogBios reloading panel_od");
let lock = platform2.config.lock().await;
if platform2.platform.has_panel_od() {
platform2
.set_panel_overdrive(lock.panel_od)
.map_err(|err| {
warn!("CtrlCharge: panel_od {}", err);
err
})
.ok();
if platform3.platform.has_throttle_thermal_policy() {
platform3.update_policy_ac_or_bat(power_plugged).await;
}
platform3.run_ac_or_bat_cmd(power_plugged).await;
}
},
)
.await;
// This spawns a new task for every item.
// TODO: find a better way to manage this
self.watch_panel_od(signal_ctxt.clone()).await?;
self.watch_mini_led_mode(signal_ctxt.clone()).await?;
self.watch_charge_control_end_threshold(signal_ctxt.clone())
.await?;
self.watch_dgpu_disable(signal_ctxt.clone()).await?;
self.watch_egpu_enable(signal_ctxt.clone()).await?;
self.watch_mini_led_mode(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?;
self.watch_post_animation_sound(signal_ctxt.clone()).await?;
self.watch_ppt_pl1_spl(signal_ctxt.clone()).await?;
self.watch_ppt_pl2_sppt(signal_ctxt.clone()).await?;
self.watch_ppt_fppt(signal_ctxt.clone()).await?;
self.watch_ppt_apu_sppt(signal_ctxt.clone()).await?;
self.watch_ppt_platform_sppt(signal_ctxt.clone()).await?;
self.watch_nv_dynamic_boost(signal_ctxt.clone()).await?;
self.watch_nv_temp_target(signal_ctxt.clone()).await?;
let watch_throttle_thermal_policy = self.platform.monitor_throttle_thermal_policy()?;
let ctrl = self.clone();
tokio::spawn(async move {
use futures_lite::StreamExt;
let mut buffer = [0; 32];
if let Ok(mut stream) = watch_throttle_thermal_policy.into_event_stream(&mut buffer) {
while (stream.next().await).is_some() {
// this blocks
debug!("Platform: watch_throttle_thermal_policy changed");
if let Ok(profile) = ctrl
.platform
.get_throttle_thermal_policy()
.map(PlatformPolicy::from)
.map_err(|e| {
error!("Platform: get_throttle_thermal_policy error: {e}");
})
{
if let Some(cpu) = ctrl.cpu_control.as_ref() {
info!("PlatformPolicy setting EPP");
cpu.set_epp(profile.into()).ok();
}
ctrl.config.lock().await.platform_policy_to_restore = profile;
}
}
}
});
Ok(())
}

View File

@@ -1,287 +0,0 @@
use std::process::Command;
use std::sync::Arc;
use std::time::Duration;
use async_trait::async_trait;
use config_traits::StdConfig;
use log::{error, info, warn};
use rog_platform::power::AsusPower;
use rog_platform::supported::ChargeSupportedFunctions;
use systemd_zbus::{ManagerProxy as SystemdProxy, Mode, UnitFileState};
use tokio::time::sleep;
use zbus::export::futures_util::lock::Mutex;
use zbus::{dbus_interface, Connection, SignalContext};
use crate::config::Config;
use crate::error::RogError;
use crate::{CtrlTask, GetSupported};
const ZBUS_PATH: &str = "/org/asuslinux/Power";
const NVIDIA_POWERD: &str = "nvidia-powerd.service";
impl GetSupported for CtrlPower {
type A = ChargeSupportedFunctions;
fn get_supported() -> Self::A {
ChargeSupportedFunctions {
charge_level_set: if let Ok(power) = AsusPower::new() {
power.has_charge_control_end_threshold()
} else {
false
},
}
}
}
#[derive(Clone)]
pub struct CtrlPower {
power: AsusPower,
config: Arc<Mutex<Config>>,
}
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlPower {
async fn set_charge_control_end_threshold(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
limit: u8,
) -> zbus::fdo::Result<()> {
if !(20..=100).contains(&limit) {
return Err(RogError::ChargeLimit(limit))?;
}
self.set(limit)
.map_err(|err| {
warn!("CtrlCharge: set_limit {}", err);
err
})
.ok();
Self::notify_charge_control_end_threshold(&ctxt, limit)
.await
.ok();
Ok(())
}
fn charge_control_end_threshold(&self) -> u8 {
loop {
if let Some(mut config) = self.config.try_lock() {
let limit = self
.power
.get_charge_control_end_threshold()
.map_err(|err| {
warn!("CtrlCharge: get_charge_control_end_threshold {}", err);
err
})
.unwrap_or(100);
config.read();
config.bat_charge_limit = limit;
config.write();
return config.bat_charge_limit;
}
}
}
fn mains_online(&self) -> bool {
if self.power.has_online() {
if let Ok(v) = self.power.get_online() {
return v == 1;
}
}
false
}
#[dbus_interface(signal)]
async fn notify_charge_control_end_threshold(
ctxt: &SignalContext<'_>,
limit: u8,
) -> zbus::Result<()>;
#[dbus_interface(signal)]
async fn notify_mains_online(ctxt: &SignalContext<'_>, on: bool) -> zbus::Result<()>;
}
#[async_trait]
impl crate::ZbusRun for CtrlPower {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, ZBUS_PATH, server).await;
}
}
#[async_trait]
impl crate::Reloadable for CtrlPower {
async fn reload(&mut self) -> Result<(), RogError> {
if let Some(mut config) = self.config.try_lock() {
config.read();
self.set(config.bat_charge_limit)?;
}
Ok(())
}
}
impl CtrlPower {
// task_watch_item!(charge_control_end_threshold power);
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
Ok(CtrlPower {
power: AsusPower::new()?,
config,
})
}
pub(super) fn set(&self, limit: u8) -> Result<(), RogError> {
if !(20..=100).contains(&limit) {
return Err(RogError::ChargeLimit(limit));
}
self.power.set_charge_control_end_threshold(limit)?;
info!("Battery charge limit: {}", limit);
if let Some(mut config) = self.config.try_lock() {
config.read();
config.bat_charge_limit = limit;
config.write();
}
Ok(())
}
}
#[async_trait]
impl CtrlTask for CtrlPower {
fn zbus_path() -> &'static str {
ZBUS_PATH
}
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 || {
let power = power1.clone();
let sysd = sysd1.clone();
async move {
info!("CtrlCharge reloading charge limit");
let lock = power.config.lock().await;
power
.set(lock.bat_charge_limit)
.map_err(|err| {
warn!("CtrlCharge: set_limit {}", err);
err
})
.ok();
if lock.disable_nvidia_powerd_on_battery {
if let Ok(value) = power.power.get_online() {
do_nvidia_powerd_action(&sysd, value == 1).await;
}
}
}
},
move || async {},
move || {
let power = power2.clone();
let sysd = sysd2.clone();
async move {
info!("CtrlCharge reloading charge limit");
let lock = power.config.lock().await;
power
.set(lock.bat_charge_limit)
.map_err(|err| {
warn!("CtrlCharge: set_limit {}", err);
err
})
.ok();
if lock.disable_nvidia_powerd_on_battery {
if let Ok(value) = power.power.get_online() {
do_nvidia_powerd_action(&sysd, value == 1).await;
}
}
}
},
)
.await;
let config = self.config.clone();
// self.watch_charge_control_end_threshold(signal_ctxt.clone())
// .await?;
let ctrl = self.clone();
tokio::spawn(async move {
let mut online = 10;
loop {
if let Ok(value) = ctrl.power.get_online() {
if online != value {
online = value;
let mut config = config.lock().await;
config.read();
if config.disable_nvidia_powerd_on_battery {
do_nvidia_powerd_action(&sysd3, value == 1).await;
}
Self::notify_mains_online(&signal_ctxt, value == 1)
.await
.unwrap();
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
// so we need to watch it with a thread and sleep unfortunately
sleep(Duration::from_secs(1)).await;
}
});
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

@@ -1,60 +0,0 @@
use std::path::PathBuf;
use config_traits::{StdConfig, StdConfigLoad};
use rog_profiles::fan_curve_set::CurveData;
use rog_profiles::Profile;
use serde_derive::{Deserialize, Serialize};
use crate::CONFIG_PATH_BASE;
const CONFIG_FILE: &str = "profile.ron";
const CONFIG_FAN_FILE: &str = "fan_curves.ron";
#[derive(Deserialize, Serialize, Debug)]
pub struct ProfileConfig {
/// For restore on boot
pub active_profile: Profile,
}
impl StdConfig for ProfileConfig {
fn new() -> Self {
Self {
active_profile: Profile::Balanced,
}
}
fn config_dir() -> std::path::PathBuf {
PathBuf::from(CONFIG_PATH_BASE)
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
}
impl StdConfigLoad for ProfileConfig {}
#[derive(Deserialize, Serialize, Debug, Default)]
pub struct FanCurveConfig {
pub balanced: Vec<CurveData>,
pub performance: Vec<CurveData>,
pub quiet: Vec<CurveData>,
}
impl StdConfig for FanCurveConfig {
/// Create a new config. The defaults are zeroed so the device must be read
/// to get the actual device defaults.
fn new() -> Self {
Self::default()
}
fn file_name(&self) -> String {
CONFIG_FAN_FILE.to_owned()
}
fn config_dir() -> std::path::PathBuf {
PathBuf::from(CONFIG_PATH_BASE)
}
}
impl StdConfigLoad for FanCurveConfig {}

View File

@@ -1,175 +0,0 @@
use config_traits::{StdConfig, StdConfigLoad};
use log::{info, warn};
use rog_platform::platform::AsusPlatform;
use rog_platform::supported::PlatformProfileFunctions;
use rog_profiles::error::ProfileError;
use rog_profiles::{FanCurveProfiles, Profile};
use super::config::{FanCurveConfig, ProfileConfig};
use crate::error::RogError;
use crate::GetSupported;
// TODO: macro wrapper for warn/info/error log macros to add module name
const MOD_NAME: &str = "CtrlPlatformProfile";
pub struct FanCurves {
config_file: FanCurveConfig,
profiles: FanCurveProfiles,
}
impl FanCurves {
pub fn update_profiles_from_config(&mut self) {
self.profiles.balanced = self.config_file.balanced.clone();
self.profiles.performance = self.config_file.performance.clone();
self.profiles.quiet = self.config_file.quiet.clone();
}
pub fn update_config_from_profiles(&mut self) {
self.config_file.balanced = self.profiles.balanced.clone();
self.config_file.performance = self.profiles.performance.clone();
self.config_file.quiet = self.profiles.quiet.clone();
}
pub fn profiles(&self) -> &FanCurveProfiles {
&self.profiles
}
pub fn profiles_mut(&mut self) -> &mut FanCurveProfiles {
&mut self.profiles
}
}
pub struct CtrlPlatformProfile {
pub profile_config: ProfileConfig,
pub fan_curves: Option<FanCurves>,
pub platform: AsusPlatform,
}
impl GetSupported for CtrlPlatformProfile {
type A = PlatformProfileFunctions;
fn get_supported() -> Self::A {
if !Profile::is_platform_profile_supported() {
warn!(
"platform_profile kernel interface not found, your laptop does not support this, \
or the interface is missing."
);
}
let res = FanCurveProfiles::supported_fans();
if res.is_err() {
info!(
"fan curves kernel interface not found, your laptop does not support this, or the \
interface is missing."
);
}
PlatformProfileFunctions {
platform_profile: Profile::is_platform_profile_supported(),
fans: res.unwrap_or_default(),
}
}
}
impl CtrlPlatformProfile {
pub fn new(config: ProfileConfig) -> Result<Self, RogError> {
let platform = AsusPlatform::new()?;
if platform.has_platform_profile() || platform.has_throttle_thermal_policy() {
info!("{MOD_NAME}: Device has profile control available");
let mut controller = CtrlPlatformProfile {
profile_config: config,
fan_curves: None,
platform,
};
if FanCurveProfiles::get_device().is_ok() {
info!("{MOD_NAME}: Device has fan curves available");
let fan_config = FanCurveConfig::new();
// Only do defaults if the config doesn't already exist
if !fan_config.file_path().exists()
|| fan_config.balanced.is_empty()
|| fan_config.performance.is_empty()
|| fan_config.quiet.is_empty()
{
info!("{MOD_NAME}: Fetching default fan curves");
controller.fan_curves = Some(FanCurves {
config_file: fan_config,
profiles: FanCurveProfiles::default(),
});
for _ in [Profile::Balanced, Profile::Performance, Profile::Quiet] {
// For each profile we need to switch to it before we
// can read the existing values from hardware. The ACPI method used
// for this is what limits us.
let next =
Profile::get_next_profile(controller.profile_config.active_profile);
Profile::set_profile(next)
.map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e))
.ok();
controller.profile_config.active_profile = next;
// Make sure to set the baseline to default
controller.set_active_curve_to_defaults()?;
let active = Profile::get_active_profile().unwrap_or(Profile::Balanced);
if let Some(curves) = controller.fan_curves.as_ref() {
info!("{MOD_NAME}: {active:?}:");
for curve in curves.profiles().get_fan_curves_for(active) {
info!("{}", String::from(curve));
}
}
}
if let Some(curves) = controller.fan_curves.as_ref() {
curves.config_file.write();
}
} else {
info!("{MOD_NAME}: Fan curves previously stored, loading...");
let mut fan_curves = FanCurves {
config_file: fan_config.load(),
profiles: FanCurveProfiles::default(),
};
fan_curves.update_profiles_from_config();
controller.fan_curves = Some(fan_curves);
}
}
return Ok(controller);
}
Err(ProfileError::NotSupported.into())
}
pub fn save_config(&mut self) {
self.profile_config.write();
if let Some(fans) = self.fan_curves.as_mut() {
fans.update_config_from_profiles();
fans.config_file.write(); // config write
}
}
/// Set the curve for the active profile active
pub(super) fn write_profile_curve_to_platform(&mut self) -> Result<(), RogError> {
if let Some(curves) = &mut self.fan_curves {
if let Ok(mut device) = FanCurveProfiles::get_device() {
curves.profiles_mut().write_profile_curve_to_platform(
self.profile_config.active_profile,
&mut device,
)?;
}
}
Ok(())
}
pub(super) fn set_active_curve_to_defaults(&mut self) -> Result<(), RogError> {
if let Some(curves) = self.fan_curves.as_mut() {
if let Ok(mut device) = FanCurveProfiles::get_device() {
curves.profiles_mut().set_active_curve_to_defaults(
self.profile_config.active_profile,
&mut device,
)?;
curves.update_config_from_profiles();
}
}
Ok(())
}
}

View File

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

View File

@@ -1,332 +0,0 @@
use std::str::FromStr;
use std::sync::Arc;
use async_trait::async_trait;
use config_traits::StdConfig;
use log::{error, info, warn};
use rog_profiles::fan_curve_set::CurveData;
use rog_profiles::{FanCurvePU, FanCurveProfiles, Profile};
use zbus::export::futures_util::lock::Mutex;
use zbus::export::futures_util::StreamExt;
use zbus::fdo::Error;
use zbus::{dbus_interface, Connection, SignalContext};
use super::controller::CtrlPlatformProfile;
use crate::error::RogError;
use crate::CtrlTask;
const MOD_NAME: &str = "ProfileZbus";
const ZBUS_PATH: &str = "/org/asuslinux/Profile";
const UNSUPPORTED_MSG: &str =
"Fan curves are not supported on this laptop or you require a patched kernel";
#[derive(Clone)]
pub struct ProfileZbus(pub Arc<Mutex<CtrlPlatformProfile>>);
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl ProfileZbus {
/// Fetch profile names
fn profiles(&mut self) -> zbus::fdo::Result<Vec<Profile>> {
if let Ok(profiles) = Profile::get_profile_names() {
return Ok(profiles);
}
Err(Error::Failed(
"Failed to get all profile details".to_owned(),
))
}
/// Toggle to next platform_profile. Names provided by `Profiles`.
/// If fan-curves are supported will also activate a fan curve for profile.
async fn next_profile(&mut self, #[zbus(signal_context)] ctxt: SignalContext<'_>) {
let mut ctrl = self.0.lock().await;
let next = Profile::get_next_profile(ctrl.profile_config.active_profile);
Profile::set_profile(next)
.map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e))
.ok();
ctrl.profile_config.active_profile = next;
ctrl.save_config();
Self::notify_profile(&ctxt, ctrl.profile_config.active_profile)
.await
.ok();
}
/// Fetch the active profile name
async fn active_profile(&mut self) -> zbus::fdo::Result<Profile> {
let mut ctrl = self.0.lock().await;
ctrl.profile_config.read();
Ok(ctrl.profile_config.active_profile)
}
/// Set this platform_profile name as active
async fn set_active_profile(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
profile: Profile,
) {
let mut ctrl = self.0.lock().await;
// Read first just incase the user has modified the config before calling this
ctrl.profile_config.read();
Profile::set_profile(profile)
.map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e))
.ok();
ctrl.profile_config.active_profile = profile;
ctrl.write_profile_curve_to_platform()
.map_err(|e| warn!("{MOD_NAME}: write_profile_curve_to_platform, {}", e))
.ok();
ctrl.save_config();
Self::notify_profile(&ctxt, ctrl.profile_config.active_profile)
.await
.ok();
}
/// Set all fan curves for a profile to enabled status. Will also activate a
/// fan curve if in the same profile mode
async fn set_fan_curves_enabled(
&mut self,
profile: Profile,
enabled: bool,
) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await;
ctrl.profile_config.read();
if let Some(curves) = &mut ctrl.fan_curves {
curves
.profiles_mut()
.set_profile_curves_enabled(profile, enabled);
ctrl.write_profile_curve_to_platform()
.map_err(|e| warn!("{MOD_NAME}: write_profile_curve_to_platform, {}", e))
.ok();
ctrl.save_config();
Ok(())
} else {
Err(Error::Failed(UNSUPPORTED_MSG.to_owned()))
}
}
/// Set a single fan curve for a profile to enabled status. Will also
/// activate a fan curve if in the same profile mode
async fn set_profile_fan_curve_enabled(
&mut self,
profile: Profile,
fan: FanCurvePU,
enabled: bool,
) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await;
ctrl.profile_config.read();
if let Some(curves) = &mut ctrl.fan_curves {
curves
.profiles_mut()
.set_profile_fan_curve_enabled(profile, fan, enabled);
ctrl.write_profile_curve_to_platform()
.map_err(|e| warn!("{MOD_NAME}: write_profile_curve_to_platform, {}", e))
.ok();
ctrl.save_config();
Ok(())
} else {
Err(Error::Failed(UNSUPPORTED_MSG.to_owned()))
}
}
/// Get the fan-curve data for the currently active Profile
async fn fan_curve_data(&mut self, profile: Profile) -> zbus::fdo::Result<Vec<CurveData>> {
let mut ctrl = self.0.lock().await;
ctrl.profile_config.read();
if let Some(curves) = &mut ctrl.fan_curves {
let curve = curves.profiles().get_fan_curves_for(profile);
return Ok(curve.to_vec());
}
Err(Error::Failed(UNSUPPORTED_MSG.to_owned()))
}
/// Set the fan curve for the specified profile.
/// Will also activate the fan curve if the user is in the same mode.
async fn set_fan_curve(&self, profile: Profile, curve: CurveData) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await;
ctrl.profile_config.read();
if let Some(curves) = &mut ctrl.fan_curves {
curves
.profiles_mut()
.save_fan_curve(curve, profile)
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
} else {
return Err(Error::Failed(UNSUPPORTED_MSG.to_owned()));
}
ctrl.write_profile_curve_to_platform()
.map_err(|e| warn!("{MOD_NAME}: Profile::set_profile, {}", e))
.ok();
ctrl.save_config();
Ok(())
}
/// Reset the stored (self) and device curve to the defaults of the
/// platform.
///
/// Each platform_profile has a different default and the defualt can be
/// read only for the currently active profile.
async fn set_active_curve_to_defaults(&self) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await;
ctrl.profile_config.read();
ctrl.set_active_curve_to_defaults()
.map_err(|e| warn!("{MOD_NAME}: Profile::set_active_curve_to_defaults, {}", e))
.ok();
ctrl.save_config();
Ok(())
}
/// Reset the stored (self) and device curve to the defaults of the
/// platform.
///
/// Each platform_profile has a different default and the defualt can be
/// read only for the currently active profile.
async fn reset_profile_curves(&self, profile: Profile) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await;
ctrl.profile_config.read();
let active = Profile::get_active_profile().unwrap_or(Profile::Balanced);
Profile::set_profile(profile)
.map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e))
.ok();
ctrl.set_active_curve_to_defaults()
.map_err(|e| warn!("{MOD_NAME}: Profile::set_active_curve_to_defaults, {}", e))
.ok();
Profile::set_profile(active)
.map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e))
.ok();
ctrl.save_config();
Ok(())
}
#[dbus_interface(signal)]
async fn notify_profile(signal_ctxt: &SignalContext<'_>, profile: Profile) -> zbus::Result<()> {
}
}
#[async_trait]
impl crate::ZbusRun for ProfileZbus {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, ZBUS_PATH, server).await;
}
}
#[async_trait]
impl CtrlTask for ProfileZbus {
fn zbus_path() -> &'static str {
ZBUS_PATH
}
async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
let ctrl = self.0.clone();
let sig_ctx = signal_ctxt.clone();
let watch = self
.0
.lock()
.await
.platform
.monitor_throttle_thermal_policy()?;
tokio::spawn(async move {
let mut buffer = [0; 32];
if let Ok(stream) = watch.into_event_stream(&mut buffer) {
stream
.for_each(|_| async {
let mut lock = ctrl.lock().await;
if let Ok(profile) =
lock.platform.get_throttle_thermal_policy().map_err(|e| {
error!("{MOD_NAME}: get_throttle_thermal_policy error: {e}");
})
{
let new_profile = Profile::from_throttle_thermal_policy(profile);
if new_profile != lock.profile_config.active_profile {
info!("{MOD_NAME}: platform_profile changed to {new_profile}");
lock.profile_config.active_profile = new_profile;
lock.write_profile_curve_to_platform().unwrap();
lock.save_config();
Profile::set_profile(lock.profile_config.active_profile)
.map_err(|e| {
error!("Profile::set_profile() error: {e}");
})
.ok();
Self::notify_profile(&sig_ctx, lock.profile_config.active_profile)
.await
.ok();
}
}
})
.await;
}
});
let ctrl = self.0.clone();
let watch = self.0.lock().await.platform.monitor_platform_profile()?;
tokio::spawn(async move {
let mut buffer = [0; 32];
if let Ok(stream) = watch.into_event_stream(&mut buffer) {
stream
.for_each(|_| async {
let mut lock = ctrl.lock().await;
if let Ok(profile) = lock.platform.get_platform_profile().map_err(|e| {
error!("get_platform_profile error: {e}");
}) {
if let Ok(new_profile) = Profile::from_str(&profile).map_err(|e| {
error!("Profile::from_str(&profile) error: {e}");
}) {
if new_profile != lock.profile_config.active_profile {
info!("{MOD_NAME}: platform_profile changed to {new_profile}");
lock.profile_config.active_profile = new_profile;
lock.write_profile_curve_to_platform().unwrap();
lock.save_config();
Profile::set_profile(lock.profile_config.active_profile)
.map_err(|e| {
error!("Profile::set_profile() error: {e}");
})
.ok();
Self::notify_profile(
&signal_ctxt,
lock.profile_config.active_profile,
)
.await
.ok();
}
}
}
})
.await;
}
});
Ok(())
}
}
#[async_trait]
impl crate::Reloadable for ProfileZbus {
/// Fetch the active profile and use that to set all related components up
async fn reload(&mut self) -> Result<(), RogError> {
let mut ctrl = self.0.lock().await;
let active = ctrl.profile_config.active_profile;
if let Some(curves) = &mut ctrl.fan_curves {
if let Ok(mut device) = FanCurveProfiles::get_device() {
// There is a possibility that the curve was default zeroed, so this call
// initialises the data from system read and we need to save it
// after
curves
.profiles_mut()
.write_profile_curve_to_platform(active, &mut device)?;
ctrl.profile_config.write();
}
}
Ok(())
}
}

View File

@@ -1,49 +0,0 @@
use async_trait::async_trait;
use serde_derive::{Deserialize, Serialize};
use zbus::zvariant::Type;
use zbus::{dbus_interface, Connection};
use crate::ctrl_anime::CtrlAnime;
use crate::ctrl_aura::controller::CtrlKbdLed;
use crate::ctrl_platform::CtrlPlatform;
use crate::ctrl_power::CtrlPower;
use crate::ctrl_profiles::controller::CtrlPlatformProfile;
use crate::GetSupported;
#[derive(Serialize, Deserialize, Debug, Type)]
pub struct SupportedFunctions(rog_platform::supported::SupportedFunctions);
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl SupportedFunctions {
pub fn supported_functions(
&self,
) -> zbus::fdo::Result<&rog_platform::supported::SupportedFunctions> {
Ok(&self.0)
}
#[dbus_interface(out_args("answer", "question"))]
fn meaning_of_life(&self) -> zbus::fdo::Result<(i32, String)> {
Ok((42, String::from("Meaning of life")))
}
}
#[async_trait]
impl crate::ZbusRun for SupportedFunctions {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, "/org/asuslinux/Supported", server).await;
}
}
impl GetSupported for SupportedFunctions {
type A = SupportedFunctions;
fn get_supported() -> Self::A {
Self(rog_platform::supported::SupportedFunctions {
anime_ctrl: CtrlAnime::get_supported(),
keyboard_led: CtrlKbdLed::get_supported(),
charge_ctrl: CtrlPower::get_supported(),
platform_profile: CtrlPlatformProfile::get_supported(),
rog_bios_ctrl: CtrlPlatform::get_supported(),
})
}
}

View File

@@ -11,19 +11,13 @@ use asusd::ctrl_anime::config::AnimeConfig;
use asusd::ctrl_anime::trait_impls::CtrlAnimeZbus;
use asusd::ctrl_anime::CtrlAnime;
use asusd::ctrl_aura::controller::CtrlKbdLed;
use asusd::ctrl_aura::trait_impls::CtrlKbdLedZbus;
use asusd::ctrl_aura::trait_impls::CtrlAuraZbus;
use asusd::ctrl_fancurves::CtrlFanCurveZbus;
use asusd::ctrl_platform::CtrlPlatform;
use asusd::ctrl_power::CtrlPower;
use asusd::ctrl_profiles::config::ProfileConfig;
use asusd::ctrl_profiles::controller::CtrlPlatformProfile;
use asusd::ctrl_profiles::trait_impls::ProfileZbus;
use asusd::ctrl_supported::SupportedFunctions;
use asusd::{print_board_info, CtrlTask, GetSupported, Reloadable, ZbusRun};
use config_traits::{StdConfig, StdConfigLoad, StdConfigLoad2};
use asusd::{print_board_info, CtrlTask, Reloadable, ZbusRun, DBUS_NAME};
use config_traits::{StdConfig, StdConfigLoad2};
use log::{error, info, warn};
use rog_aura::aura_detection::LaptopLedData;
use rog_dbus::DBUS_NAME;
use rog_profiles::Profile;
use tokio::time::sleep;
use zbus::SignalContext;
@@ -53,7 +47,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
info!(" daemon v{}", asusd::VERSION);
info!(" rog-anime v{}", rog_anime::VERSION);
info!(" rog-aura v{}", rog_aura::VERSION);
info!(" rog-dbus v{}", rog_dbus::VERSION);
info!(" rog-profiles v{}", rog_profiles::VERSION);
info!("rog-platform v{}", rog_platform::VERSION);
@@ -63,9 +56,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// The actual main loop for the daemon
async fn start_daemon() -> Result<(), Box<dyn Error>> {
let supported = SupportedFunctions::get_supported();
// let supported = SupportedFunctions::get_supported();
print_board_info();
println!("{:?}", supported.supported_functions());
// println!("{:?}", supported.supported_functions());
// Start zbus server
let mut connection = Connection::system().await?;
@@ -73,7 +66,7 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
let config = Config::new().load();
let config = Arc::new(Mutex::new(config));
supported.add_to_server(&mut connection).await;
// supported.add_to_server(&mut connection).await;
match CtrlPlatform::new(config.clone()) {
Ok(ctrl) => {
@@ -85,32 +78,16 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
}
}
match CtrlPower::new(config.clone()) {
match CtrlFanCurveZbus::new() {
Ok(ctrl) => {
let sig_ctx = CtrlPower::signal_context(&connection)?;
let sig_ctx = CtrlFanCurveZbus::signal_context(&connection)?;
start_tasks(ctrl, &mut connection, sig_ctx).await?;
}
Err(err) => {
error!("CtrlPower: {}", err);
error!("FanCurves: {}", err);
}
}
if Profile::is_platform_profile_supported() {
let profile_config = ProfileConfig::new().load();
match CtrlPlatformProfile::new(profile_config) {
Ok(ctrl) => {
let zbus = ProfileZbus(Arc::new(Mutex::new(ctrl)));
let sig_ctx = ProfileZbus::signal_context(&connection)?;
start_tasks(zbus, &mut connection, sig_ctx).await?;
}
Err(err) => {
error!("Profile control: {}", err);
}
}
} else {
warn!("platform_profile support not found");
}
match CtrlAnime::new(AnimeConfig::new().load()) {
Ok(ctrl) => {
let zbus = CtrlAnimeZbus(Arc::new(Mutex::new(ctrl)));
@@ -127,8 +104,8 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
// detection first
match CtrlKbdLed::new(laptop) {
Ok(ctrl) => {
let zbus = CtrlKbdLedZbus(Arc::new(Mutex::new(ctrl)));
let sig_ctx = CtrlKbdLedZbus::signal_context(&connection)?;
let zbus = CtrlAuraZbus(Arc::new(Mutex::new(ctrl)));
let sig_ctx = CtrlAuraZbus::signal_context(&connection)?;
start_tasks(zbus, &mut connection, sig_ctx).await?;
}
Err(err) => {

View File

@@ -5,30 +5,31 @@ pub mod config;
pub mod ctrl_anime;
/// Keyboard LED brightness control, RGB, and LED display modes
pub mod ctrl_aura;
/// Control platform profiles + fan-curves if available
pub mod ctrl_fancurves;
/// Control ASUS bios function such as boot sound, Optimus/Dedicated gfx mode
pub mod ctrl_platform;
/// Control of battery charge level
pub mod ctrl_power;
/// Control platform profiles + fan-curves if available
pub mod ctrl_profiles;
/// Fetch all supported functions for the laptop
pub mod ctrl_supported;
pub mod error;
use std::future::Future;
use std::time::Duration;
use async_trait::async_trait;
use dmi_id::DMIID;
use futures_lite::stream::StreamExt;
use log::{debug, info, warn};
use logind_zbus::manager::ManagerProxy;
use zbus::export::futures_util::StreamExt;
use tokio::time::sleep;
use zbus::zvariant::ObjectPath;
use zbus::{Connection, SignalContext};
use zbus::{CacheProperties, Connection, SignalContext};
use crate::error::RogError;
const CONFIG_PATH_BASE: &str = "/etc/asusd/";
pub static DBUS_NAME: &str = "org.asuslinux.Daemon";
pub static DBUS_PATH: &str = "/org/asuslinux/Daemon";
pub static DBUS_IFACE: &str = "org.asuslinux.Daemon";
/// This macro adds a function which spawns an `inotify` task on the passed in
/// `Executor`.
@@ -46,7 +47,7 @@ const CONFIG_PATH_BASE: &str = "/etc/asusd/";
/// # Example
///
/// ```ignore
/// impl CtrlRogBios {
/// impl RogPlatform {
/// task_watch_item!(panel_od platform);
/// task_watch_item!(gpu_mux_mode platform);
/// }
@@ -69,9 +70,45 @@ macro_rules! task_watch_item {
tokio::spawn(async move {
let mut buffer = [0; 32];
watch.into_event_stream(&mut buffer).unwrap().for_each(|_| async {
let value = ctrl.$name();
concat_idents::concat_idents!(notif_fn = notify_, $name {
Self::notif_fn(&signal_ctxt, value).await.ok();
if let Ok(value) = ctrl.$name() { // get new value from zbus method
concat_idents::concat_idents!(notif_fn = $name, _changed {
ctrl.notif_fn(&signal_ctxt).await.ok();
});
let mut lock = ctrl.config.lock().await;
lock.$name = value;
lock.write();
}
}).await;
});
}
Err(e) => info!("inotify watch failed: {}. You can ignore this if your device does not support the feature", e),
}
});
Ok(())
}
});
};
}
#[macro_export]
macro_rules! task_watch_item_notify {
($name:ident $self_inner:ident) => {
concat_idents::concat_idents!(fn_name = watch_, $name {
async fn fn_name(
&self,
signal_ctxt: SignalContext<'static>,
) -> Result<(), RogError> {
use zbus::export::futures_util::StreamExt;
let ctrl = self.clone();
concat_idents::concat_idents!(watch_fn = monitor_, $name {
match self.$self_inner.watch_fn() {
Ok(watch) => {
tokio::spawn(async move {
let mut buffer = [0; 32];
watch.into_event_stream(&mut buffer).unwrap().for_each(|_| async {
concat_idents::concat_idents!(notif_fn = $name, _changed {
ctrl.notif_fn(&signal_ctxt).await.ok();
});
}).await;
});
@@ -88,12 +125,9 @@ macro_rules! task_watch_item {
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub fn print_board_info() {
let dmi = sysfs_class::DmiId::default();
let board_name = dmi.board_name().expect("Could not get board_name");
let prod_family = dmi.product_family().expect("Could not get product_family");
info!("Product family: {}", prod_family.trim());
info!("Board name: {}", board_name.trim());
let dmi = DMIID::new().unwrap_or_default();
info!("Product family: {}", dmi.product_family);
info!("Board name: {}", dmi.board_name);
}
#[async_trait]
@@ -163,15 +197,15 @@ pub trait CtrlTask {
F4: Send + 'static,
>(
&self,
mut on_sleep: F1,
mut on_wake: F2,
mut on_shutdown: F3,
mut on_boot: F4,
mut on_prepare_for_sleep: F1,
mut on_prepare_for_shutdown: F2,
mut on_lid_change: F3,
mut on_external_power_change: F4,
) where
F1: FnMut() -> Fut1,
F2: FnMut() -> Fut2,
F3: FnMut() -> Fut3,
F4: FnMut() -> Fut4,
F1: FnMut(bool) -> Fut1,
F2: FnMut(bool) -> Fut2,
F3: FnMut(bool) -> Fut3,
F4: FnMut(bool) -> Fut4,
Fut1: Future<Output = ()> + Send,
Fut2: Future<Output = ()> + Send,
Fut3: Future<Output = ()> + Send,
@@ -181,43 +215,64 @@ pub trait CtrlTask {
.await
.expect("Controller could not create dbus connection");
let manager = ManagerProxy::new(&connection)
let manager = ManagerProxy::builder(&connection)
.cache_properties(CacheProperties::No)
.build()
.await
.expect("Controller could not create ManagerProxy");
let manager1 = manager.clone();
tokio::spawn(async move {
if let Ok(mut notif) = manager.receive_prepare_for_sleep().await {
if let Ok(mut notif) = manager1.receive_prepare_for_shutdown().await {
while let Some(event) = notif.next().await {
// blocks thread :|
if let Ok(args) = event.args() {
if args.start {
debug!("Doing on_sleep()");
on_sleep().await;
} else if !args.start() {
debug!("Doing on_wake()");
on_wake().await;
}
debug!("Doing on_prepare_for_shutdown({})", args.start);
on_prepare_for_shutdown(args.start).await;
}
}
}
});
let manager = ManagerProxy::new(&connection)
.await
.expect("Controller could not create ManagerProxy");
let manager2 = manager.clone();
tokio::spawn(async move {
if let Ok(mut notif) = manager2.receive_prepare_for_sleep().await {
while let Some(event) = notif.next().await {
// blocks thread :|
if let Ok(args) = event.args() {
debug!("Doing on_prepare_for_sleep({})", args.start);
on_prepare_for_sleep(args.start).await;
}
}
}
});
let manager3 = manager.clone();
tokio::spawn(async move {
let mut last_power = manager3.on_external_power().await.unwrap_or_default();
loop {
if let Ok(next) = manager3.on_external_power().await {
if next != last_power {
last_power = next;
on_external_power_change(next).await;
}
}
sleep(Duration::from_secs(2)).await;
}
});
tokio::spawn(async move {
if let Ok(mut notif) = manager.receive_prepare_for_shutdown().await {
while let Some(event) = notif.next().await {
if let Ok(args) = event.args() {
if args.start {
debug!("Doing on_shutdown()");
on_shutdown().await;
} else if !args.start() {
debug!("Doing on_boot()");
on_boot().await;
}
let mut last_lid = manager.lid_closed().await.unwrap_or_default();
// need to loop on these as they don't emit signals
loop {
if let Ok(next) = manager.lid_closed().await {
if next != last_lid {
last_lid = next;
on_lid_change(next).await;
}
}
sleep(Duration::from_secs(2)).await;
}
});
}

View File

@@ -8,39 +8,6 @@
<method name="Write">
<arg name="input" type="(ays)" direction="in"/>
</method>
<!--
Set the global AniMe brightness
-->
<method name="SetImageBrightness">
<arg name="bright" type="d" direction="in"/>
</method>
<!--
Set base brightness level
-->
<method name="SetBrightness">
<arg name="brightness" type="s" direction="in"/>
</method>
<!--
Enable the builtin animations or not
-->
<method name="SetBuiltinsEnabled">
<arg name="enabled" type="b" direction="in"/>
</method>
<!--
Set which builtin animation is used for each stage
-->
<method name="SetBuiltinAnimations">
<arg name="boot" type="s" direction="in"/>
<arg name="awake" type="s" direction="in"/>
<arg name="sleep" type="s" direction="in"/>
<arg name="shutdown" type="s" direction="in"/>
</method>
<!--
Set whether the AniMe is enabled at all
-->
<method name="SetEnableDisplay">
<arg name="enabled" type="b" direction="in"/>
</method>
<!--
The main loop is the base system set action if the user isn't running
the user daemon
@@ -52,14 +19,39 @@
Get the device state as stored by asusd
-->
<method name="DeviceState">
<arg type="bsb(ssss)" direction="out"/>
<arg type="(bub(ssss)bbbu)" direction="out"/>
</method>
<!--
Notify listeners of the status of AniMe LED power and factory
system-status animations
Set base brightness level
-->
<signal name="NotifyDeviceState">
<arg name="data" type="(bsb(ssss))"/>
</signal>
<!--
Set base brightness level
-->
<property name="Brightness" type="u" access="readwrite"/>
<!--
Set which builtin animation is used for each stage
-->
<property name="BuiltinAnimations" type="(ssss)" access="readwrite"/>
<!--
Enable the builtin animations or not. This is quivalent to "Powersave
animations" in Armory crate
-->
<property name="BuiltinsEnabled" type="b" access="readwrite"/>
<!--
Set whether the AniMe is enabled at all
-->
<property name="EnableDisplay" type="b" access="readwrite"/>
<!--
Set if to turn the AniMe Matrix off when the lid is closed
-->
<property name="OffWhenLidClosed" type="b" access="readwrite"/>
<!--
Set if to turn the AniMe Matrix off when the laptop is suspended
-->
<property name="OffWhenSuspended" type="b" access="readwrite"/>
<!--
Set if to turn the AniMe Matrix off when external power is unplugged
-->
<property name="OffWhenUnplugged" type="b" access="readwrite"/>
</interface>
</node>

View File

@@ -2,52 +2,10 @@
<node>
<interface name="org.asuslinux.Daemon">
<!--
Set the keyboard brightness level (0-3)
Get the data set for every mode available
-->
<method name="SetBrightness">
<arg name="brightness" type="s" direction="in"/>
</method>
<!--
Set a variety of states, input is array of enum.
`enabled` sets if the sent array should be disabled or enabled
For Modern ROG devices the "enabled" flag is ignored.
-->
<method name="SetLedPower">
<arg name="options" type="(asas((sbbbb)(sbbbb)(sbbbb)(sbbbb)(sbbbb)))" direction="in"/>
<arg name="enabled" type="b" direction="in"/>
</method>
<method name="SetLedMode">
<arg name="effect" type="(ss(yyy)(yyy)ss)" direction="in"/>
</method>
<method name="NextLedMode">
</method>
<method name="PrevLedMode">
</method>
<method name="NextLedBrightness">
</method>
<method name="PrevLedBrightness">
</method>
<!--
Return the device type for this Aura keyboard
-->
<method name="DeviceType">
<arg type="s" direction="out"/>
</method>
<method name="LedPower">
<arg type="asas((sbbbb)(sbbbb)(sbbbb)(sbbbb)(sbbbb))" direction="out"/>
</method>
<!--
Return the current mode data
-->
<method name="LedMode">
<arg type="s" direction="out"/>
</method>
<!--
Return a list of available modes
-->
<method name="LedModes">
<arg type="a{s(ss(yyy)(yyy)ss)}" direction="out"/>
<method name="AllModeData">
<arg type="a{u(us(yyy)(yyy)ss)}" direction="out"/>
</method>
<!--
On machine that have some form of either per-key keyboard or per-zone
@@ -57,15 +15,51 @@
<method name="DirectAddressingRaw">
<arg name="data" type="aay" direction="in"/>
</method>
<signal name="NotifyLed">
<arg name="data" type="(ss(yyy)(yyy)ss)"/>
</signal>
<signal name="NotifyPowerStates">
<arg name="data" type="(asas((sbbbb)(sbbbb)(sbbbb)(sbbbb)(sbbbb)))"/>
</signal>
<!--
Return the current LED brightness
-->
<property name="LedBrightness" type="n" access="read"/>
<!--
Set the keyboard brightness level (0-3)
-->
<property name="Brightness" type="u" access="readwrite"/>
<!--
Return the device type for this Aura keyboard
-->
<property name="DeviceType" type="s" access="read"/>
<!--
The current mode data
-->
<!--
Set an Aura effect if the effect mode or zone is supported.
On success the aura config file is read to refresh cached values, then
the effect is stored and config written to disk.
-->
<property name="LedMode" type="u" access="readwrite"/>
<!--
The current mode data
-->
<!--
Set an Aura effect if the effect mode or zone is supported.
On success the aura config file is read to refresh cached values, then
the effect is stored and config written to disk.
-->
<property name="LedModeData" type="(us(yyy)(yyy)ss)" access="readwrite"/>
<!--
Set a variety of states, input is array of enum.
`enabled` sets if the sent array should be disabled or enabled
For Modern ROG devices the "enabled" flag is ignored.
-->
<property name="LedPower" type="(asas((sbbbb)(sbbbb)(sbbbb)(sbbbb)(sbbbb)))" access="readwrite"/>
<!--
Total levels of brightness available
-->
<property name="SupportedBrightness" type="au" access="read"/>
<!--
The total available modes
-->
<property name="SupportedModes" type="au" access="read"/>
</interface>
</node>

View File

@@ -2,49 +2,28 @@
<node>
<interface name="org.asuslinux.Daemon">
<!--
Fetch profile names
Set all fan curves for a profile to enabled status. Will also activate a
fan curve if in the same profile mode
-->
<method name="Profiles">
<arg type="as" direction="out"/>
</method>
<!--
Toggle to next platform_profile. Names provided by `Profiles`.
If fan-curves are supported will also activate a fan curve for profile.
-->
<method name="NextProfile">
</method>
<!--
Fetch the active profile name
-->
<method name="ActiveProfile">
<arg type="s" direction="out"/>
</method>
<!--
Set this platform_profile name as active
-->
<method name="SetActiveProfile">
<arg name="profile" type="s" direction="in"/>
</method>
<!--
Get a list of profiles that have fan-curves enabled.
-->
<method name="EnabledFanProfiles">
<arg type="as" direction="out"/>
</method>
<!--
Set a profile fan curve enabled status. Will also activate a fan curve
if in the same profile mode
-->
<method name="SetFanCurveEnabled">
<method name="SetFanCurvesEnabled">
<arg name="profile" type="s" direction="in"/>
<arg name="enabled" type="b" direction="in"/>
</method>
<!--
Get the fan-curve data for the currently active Profile
Set a single fan curve for a profile to enabled status. Will also
activate a fan curve if in the same profile mode
-->
<method name="SetProfileFanCurveEnabled">
<arg name="profile" type="s" direction="in"/>
<arg name="fan" type="s" direction="in"/>
<arg name="enabled" type="b" direction="in"/>
</method>
<!--
Get the fan-curve data for the currently active PlatformPolicy
-->
<method name="FanCurveData">
<arg name="profile" type="s" direction="in"/>
<arg type="(b(s(yyyyyyyy)(yyyyyyyy))(s(yyyyyyyy)(yyyyyyyy)))" direction="out"/>
<arg type="a(s(yyyyyyyy)(yyyyyyyy)b)" direction="out"/>
</method>
<!--
Set the fan curve for the specified profile.
@@ -52,7 +31,7 @@
-->
<method name="SetFanCurve">
<arg name="profile" type="s" direction="in"/>
<arg name="curve" type="(s(yyyyyyyy)(yyyyyyyy))" direction="in"/>
<arg name="curve" type="(s(yyyyyyyy)(yyyyyyyy)b)" direction="in"/>
</method>
<!--
Reset the stored (self) and device curve to the defaults of the
@@ -73,8 +52,5 @@
<method name="ResetProfileCurves">
<arg name="profile" type="s" direction="in"/>
</method>
<signal name="NotifyProfile">
<arg name="profile" type="s"/>
</signal>
</interface>
</node>

View File

@@ -1,67 +1,46 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.asuslinux.Daemon">
<method name="SetGpuMuxMode">
<arg name="mode" type="u" direction="in"/>
<!--
Returns a list of property names that this system supports
-->
<method name="SupportedProperties">
<arg type="as" direction="out"/>
</method>
<method name="GpuMuxMode">
<arg type="u" direction="out"/>
<method name="SupportedInterfaces">
<arg type="as" direction="out"/>
</method>
<signal name="NotifyGpuMuxMode">
<arg name="mode" type="u"/>
</signal>
<method name="SetPostBootSound">
<arg name="on" type="b" direction="in"/>
</method>
<method name="PostBootSound">
<arg type="n" direction="out"/>
</method>
<signal name="NotifyPostBootSound">
<arg name="on" type="b"/>
</signal>
<method name="SetPanelOd">
<arg name="overdrive" type="b" direction="in"/>
<!--
Toggle to next platform_profile. Names provided by `Profiles`.
If fan-curves are supported will also activate a fan curve for profile.
-->
<method name="NextThrottleThermalPolicy">
</method>
<property name="ChargeControlEndThreshold" type="y" access="readwrite"/>
<property name="DgpuDisable" type="b" access="read"/>
<property name="EgpuEnable" type="b" access="read"/>
<property name="GpuMuxMode" type="y" access="readwrite"/>
<!--
Get the `panel_od` value from platform. Updates the stored value in
internal config also.
-->
<method name="PanelOd">
<arg type="b" direction="out"/>
</method>
<signal name="NotifyPanelOd">
<arg name="overdrive" type="b"/>
</signal>
<method name="SetMiniLedMode">
<arg name="on" type="b" direction="in"/>
</method>
<property name="MiniLedMode" type="b" access="readwrite"/>
<property name="NvDynamicBoost" type="y" access="readwrite"/>
<property name="NvTempTarget" type="y" access="readwrite"/>
<!--
Get the `panel_od` value from platform. Updates the stored value in
internal config also.
-->
<method name="MiniLedMode">
<arg type="b" direction="out"/>
</method>
<signal name="NotifyMiniLedMode">
<arg name="on" type="b"/>
</signal>
<method name="SetDgpuDisable">
<arg name="disable" type="b" direction="in"/>
</method>
<method name="DgpuDisable">
<arg type="b" direction="out"/>
</method>
<signal name="NotifyDgpuDisable">
<arg name="disable" type="b"/>
</signal>
<method name="SetEgpuEnable">
<arg name="enable" type="b" direction="in"/>
</method>
<method name="EgpuEnable">
<arg type="b" direction="out"/>
</method>
<signal name="NotifyEgpuEnable">
<arg name="enable" type="b"/>
</signal>
<property name="PanelOd" type="b" access="readwrite"/>
<property name="PostAnimationSound" type="b" access="readwrite"/>
<property name="PptApuSppt" type="y" access="readwrite"/>
<property name="PptFppt" type="y" access="readwrite"/>
<!--
************************************************************************
-->
<property name="PptPl1Spl" type="y" access="readwrite"/>
<property name="PptPl2Sppt" type="y" access="readwrite"/>
<property name="PptPlatformSppt" type="y" access="readwrite"/>
<property name="ThrottleThermalPolicy" type="s" access="readwrite"/>
</interface>
</node>

View File

@@ -1,20 +0,0 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.asuslinux.Daemon">
<method name="SetChargeControlEndThreshold">
<arg name="limit" type="y" direction="in"/>
</method>
<method name="ChargeControlEndThreshold">
<arg type="y" direction="out"/>
</method>
<method name="MainsOnline">
<arg type="b" direction="out"/>
</method>
<signal name="NotifyChargeControlEndThreshold">
<arg name="limit" type="y"/>
</signal>
<signal name="NotifyMainsOnline">
<arg name="on" type="b"/>
</signal>
</interface>
</node>

View File

@@ -1,12 +0,0 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.asuslinux.Daemon">
<method name="SupportedFunctions">
<arg type="b(b)(bb)(sbasassas)(bbbbbb)" direction="out"/>
</method>
<method name="MeaningOfLife">
<arg name="answer" type="i" direction="out"/>
<arg name="question" type="s" direction="out"/>
</method>
</interface>
</node>

View File

@@ -1,5 +1,5 @@
/*
Generated by typeshare 1.6.0
Generated by typeshare 1.7.0
*/
export enum AnimBooting {
@@ -42,6 +42,10 @@ export interface DeviceState {
display_brightness: Brightness;
builtin_anims_enabled: boolean;
builtin_anims: Animations;
off_when_unplugged: boolean;
off_when_suspended: boolean;
off_when_lid_closed: boolean;
brightness_on_battery: Brightness;
}
export enum AnimeType {

View File

@@ -1,5 +1,5 @@
/*
Generated by typeshare 1.6.0
Generated by typeshare 1.7.0
*/
/** Represents the per-key raw USB packets */
@@ -227,6 +227,7 @@ export enum AuraDevice {
X18c6 = "X18c6",
X19b6 = "X19b6",
X1a30 = "X1a30",
X1abe = "X1abe",
Unknown = "Unknown",
}

View File

@@ -1,50 +1,7 @@
/*
Generated by typeshare 1.6.0
Generated by typeshare 1.7.0
*/
export type AnimeSupportedFunctions = boolean;
export interface ChargeSupportedFunctions {
charge_level_set: boolean;
}
export interface PlatformProfileFunctions {
platform_profile: boolean;
fan_curves: boolean;
}
export enum AdvancedAura {
None = "None",
Zoned = "Zoned",
PerKey = "PerKey",
}
export interface LedSupportedFunctions {
dev_id: AuraDevice;
brightness: boolean;
basic_modes: AuraModeNum[];
basic_zones: AuraZone[];
advanced_type: AdvancedAura;
power_zones: PowerZones[];
}
export interface RogBiosSupportedFunctions {
post_sound: boolean;
gpu_mux: boolean;
panel_overdrive: boolean;
dgpu_disable: boolean;
egpu_enable: boolean;
mini_led_mode: boolean;
}
export interface SupportedFunctions {
anime_ctrl: AnimeSupportedFunctions;
charge_ctrl: ChargeSupportedFunctions;
platform_profile: PlatformProfileFunctions;
keyboard_led: LedSupportedFunctions;
rog_bios_ctrl: RogBiosSupportedFunctions;
}
export enum GpuMode {
Discrete = "Discrete",
Optimus = "Optimus",
@@ -56,3 +13,28 @@ export enum GpuMode {
NotSupported = "NotSupported",
}
export enum PlatformPolicy {
Balanced = "Balanced",
Performance = "Performance",
Quiet = "Quiet",
}
/** CamelCase names of the properties. Intended for use with DBUS */
export enum Properties {
ChargeControlEndThreshold = "ChargeControlEndThreshold",
DgpuDisable = "DgpuDisable",
GpuMuxMode = "GpuMuxMode",
PostAnimationSound = "PostAnimationSound",
PanelOd = "PanelOd",
MiniLedMode = "MiniLedMode",
EgpuEnable = "EgpuEnable",
PlatformPolicy = "PlatformPolicy",
PptPl1Spl = "PptPl1Spl",
PptPl2Sppt = "PptPl2Sppt",
PptFppt = "PptFppt",
PptApuSppt = "PptApuSppt",
PptPlatformSppt = "PptPlatformSppt",
NvDynamicBoost = "NvDynamicBoost",
NvTempTarget = "NvTempTarget",
}

View File

@@ -1,35 +1,24 @@
/*
Generated by typeshare 1.6.0
Generated by typeshare 1.7.0
*/
export enum FanCurvePU {
CPU = "CPU",
GPU = "GPU",
MID = "MID",
}
export interface CurveData {
fan: FanCurvePU;
pwm: [number, number, number, number, number, number, number, number];
temp: [number, number, number, number, number, number, number, number];
}
/** A `FanCurveSet` contains both CPU and GPU fan curve data */
export interface FanCurveSet {
enabled: boolean;
cpu: CurveData;
gpu: CurveData;
}
/** Main purpose of `FanCurves` is to enable restoring state on system boot */
export interface FanCurveProfiles {
balanced: FanCurveSet;
performance: FanCurveSet;
quiet: FanCurveSet;
}
export enum Profile {
Balanced = "Balanced",
Performance = "Performance",
Quiet = "Quiet",
balanced: CurveData[];
performance: CurveData[];
quiet: CurveData[];
}

7
cpuctl/Cargo.toml Normal file
View File

@@ -0,0 +1,7 @@
[package]
name = "cpuctl"
license = "MPL-2.0"
edition = "2021"
version.workspace = true
[dependencies]

14
cpuctl/src/lib.rs Normal file
View File

@@ -0,0 +1,14 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}

View File

@@ -1,6 +1,3 @@
#ACTION=="add|change", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="0b05", ENV{ID_MODEL_ID}=="1[89][a-zA-Z0-9][a-zA-Z0-9]|193b", ENV{ID_TYPE}=="hid", TAG+="systemd", ENV{SYSTEMD_WANTS}="asusd.service"
#ACTION=="add|remove", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="0b05", ENV{ID_MODEL_ID}=="1[89][a-zA-Z0-9][a-zA-Z0-9]|193b", RUN+="systemctl restart asusd.service"
ENV{DMI_VENDOR}="$attr{[dmi/id]sys_vendor}"
ENV{DMI_VENDOR}!="ASUSTeK COMPUTER INC.", GOTO="asusd_end"

View File

@@ -1,15 +1,13 @@
[Unit]
Description=ASUS Notebook Control
StartLimitInterval=200
StartLimitBurst=2
Before=multi-user.target
After=power-profiles-daemon.service
After=nvidia-powerd.service
StartLimitInterval=500
StartLimitBurst=5
After=nvidia-powerd.service systemd-udevd.service
[Service]
Environment=IS_SERVICE=1
Environment=RUST_LOG="info"
ExecStartPre=/bin/sleep 2
# ExecStartPre=/bin/sleep 2 # was required only for slow devices
ExecStart=/usr/bin/asusd
Restart=on-failure
RestartSec=1

View File

@@ -0,0 +1,25 @@
/* eslint-env node */
module.exports = {
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
root: true,
rules: {
// enable additional rules
indent: ["error", 4],
"linebreak-style": ["error", "unix"],
quotes: ["error", "double"],
semi: ["error", "always"],
// override configuration set by extending "eslint:recommended"
"no-empty": "warn",
"no-cond-assign": ["error", "always"],
// disable rules from base configurations
"for-direction": "off",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/ban-ts-comment": "off",
},
};

View File

@@ -0,0 +1,13 @@
# Generated files
/@types/gir-generated/*
# Build outputes
/dist/
/build/
# Node configuration and modules
/package.json
/node_modules/
# Files I prefer not to be formatted
*.md

View File

@@ -0,0 +1,9 @@
{
"printWidth": 100,
"useTabs": false,
"semi": true,
"singleQuote": false,
"trailingComma": "all",
"bracketSpacing": true,
"arrowParens": "always"
}

View File

@@ -0,0 +1,67 @@
import { build } from "esbuild";
import { exec } from "child_process";
import { copyFileSync, cpSync } from "fs";
import { resolve, dirname } from "path";
import { fileURLToPath } from "url";
import AdmZip from "adm-zip";
import metadata from "./src/metadata.json" assert { type: "json" };
build({
entryPoints: ["src/extension.ts"],
outdir: "dist",
bundle: true,
// Do not remove the functions `enable()`, `disable()` and `init()`
treeShaking: false,
// firefox60 // Since GJS 1.53.90
// firefox68 // Since GJS 1.63.90
// firefox78 // Since GJS 1.65.90
// firefox91 // Since GJS 1.71.1
// firefox102 // Since GJS 1.73.2
target: "firefox102",
//platform: "neutral",
platform: "node",
// mainFields: ['main'],
// conditions: ['require', 'default'],
format: "esm",
external: ["gi://*", "resource://*", "system", "gettext", "cairo"],
}).then(() => {
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const metaSrc = resolve(__dirname, "src/metadata.json");
const metaDist = resolve(__dirname, "dist/metadata.json");
const schemaSrc = resolve(__dirname, "schemas");
const schemaDist = resolve(__dirname, "dist/schemas");
const dbusXmlSrc = resolve(__dirname, "../../bindings/dbus-xml");
const dbusXmlDist = resolve(__dirname, "dist/resources/dbus");
const zipFilename = `${metadata.uuid}.zip`;
const zipDist = resolve(__dirname, zipFilename);
exec("glib-compile-schemas schemas/", (error, stdout, stderr) => {
console.log("stdout: " + stdout);
console.log("stderr: " + stderr);
});
copyFileSync(metaSrc, metaDist);
cpSync(schemaSrc, schemaDist, { recursive: true }, (err) => {
if (err) {
console.error(err);
}
});
cpSync(dbusXmlSrc, dbusXmlDist, { recursive: true }, (err) => {
if (err) {
console.error(err);
}
});
const zip = new AdmZip();
zip.addLocalFolder(resolve(__dirname, "dist"));
zip.writeZip(zipDist);
console.log(`Build complete. Zip file: ${zipFilename}\n`);
console.log(`Install with: gnome-extensions install ${zipFilename}`);
console.log(`Update with: gnome-extensions install ${zipFilename} --force`);
console.log(`Enable with: gnome-extensions enable ${metadata.uuid} --user`);
});

View File

@@ -0,0 +1,51 @@
#!/bin/bash
## Script to initialise dev-environment (types)
gv="44"
wd=${PWD}
# cleanup
rm -rf @types
# generate GJS from gir (this does not include the extensions)
echo "Generating GJS types from gir.."
npx ts-for-gir generate Shell-12 St-12 Gtk-4.0 \
-g /usr/share/gir-1.0 \
-g /usr/share/gnome-shell \
-g /usr/share/gnome-shell/gir-1.0 \
-g /usr/lib64/mutter-12 \
-t esm -o @types/Gjs
# get latest js (44) in this case and create the types for it
echo "Generating GJS Extension (Gex) types from extension source.."
mkdir -p ./_tmp/
cd ./_tmp
wget -q -O gnome-shell-js-${gv}.tar.gz https://gitlab.gnome.org/GNOME/gnome-shell/-/archive/gnome-${gv}/gnome-shell-gnome-${gv}.tar.gz?path=js
tar xf gnome-shell-js-${gv}.tar.gz
cd gnome-shell-gnome-${gv}-js
cat >tsconfig.json <<EOL
{
"include": ["js/ui/*"],
"exclude": [
"js/ui/shellDBus.js",
"node_modules",
"**/node_modules/*"
],
"compilerOptions": {
"allowJs": true,
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "gex-types",
"declarationMap": true,
"lib": ["es2019"]
}
}
EOL
npx tsc
cd ${wd}
mv ./_tmp/gnome-shell-gnome-${gv}-js/gex-types @types/Gex
# rm -rf ./_tmp/
echo "done."
exit 0

View File

@@ -1,21 +1,36 @@
{
"name": "asusctl-gnome",
"version": "4.7.0",
"version": "5.0.0-RC1",
"description": "asusctl-gnome a gnome extension exposing some of the base features of asusd in a helpful and easy to use way",
"type": "module",
"main": "dist/extension.js",
"scripts": {
"clear": "rm -rf dist",
"compile": "tsc --build tsconfig.json",
"build:app": "node esbuild.js",
"build": "yarn run clear && yarn run build:app",
"validate": "tsc --noEmit"
"validate": "tsc --noEmit",
"generate:gir-types": "ts-for-gir generate",
"check:types": "tsc --build tsconfig.types.json",
"lint": "eslint .",
"format": "prettier . -w"
},
"devDependencies": {
"@girs/gnome-shell": "^45.0.0-beta2",
"@typescript-eslint/eslint-plugin": "^5.60.1",
"@typescript-eslint/parser": "^5.60.1",
"adm-zip": "^0.5.10",
"esbuild": "^0.17.19",
"eslint": "^8.44.0",
"typescript": "^5.1.6"
"esbuild": "^0.19.5",
"eslint": "^8.51.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-promise": "^6.1.1",
"prettier": "^3.0.3",
"typescript": "^5.2.2"
},
"dependencies": {
"@girs/gjs": "^3.2.5",
"@girs/gobject-2.0": "^2.78.0-3.2.5",
"@girs/st-13": "^13.0.0-3.2.5"
},
"repository": {
"type": "git",

View File

@@ -10,6 +10,9 @@
<key type="b" name="anime-power">
<default>false</default>
</key>
<key type="b" name="anime-builtins">
<default>false</default>
</key>
<key name="charge-level" type="u">
<range min="20" max="100"/>
<default>100</default>

View File

@@ -0,0 +1,127 @@
import { Extension, gettext as _ } from "@girs/gnome-shell/extensions/extension";
import * as platform from "./bindings/platform";
import { AsusQuickToggle } from "./modules/rog_quick_toggle";
import { AsusMenuToggle } from "./modules/rog_menu_toggle";
import { AsusIndicator } from "./modules/rog_indicator";
import { AsusSlider } from "./modules/rog_slider_100pc";
import { FeatureMenuToggle } from "./modules/quick_menus/laptop_features";
import { DbusBase } from "./modules/dbus_proxy";
import { main } from "@girs/gnome-shell/ui";
export const uuid = "asusctl-gnome@asus-linux.org";
export default class AsusExtension extends Extension {
// public dbus_aura: AuraDbus = new AuraDbus;
// public dbus_anime: AnimeDbus = new AnimeDbus;
public dbus_platform: DbusBase | undefined;
public dbus_anime: DbusBase | undefined;
private individual = false;
public supported_properties!: platform.Properties;
public supported_interfaces: string[] = [];
private feature_menu = null;
private panel_od = null;
private mini_led = null;
private anime_display = null;
private anime_builtins = null;
private charge_thres = null;
// private _feature: typeof FeatureMenuToggle;
async enable() {
log(this.path);
if (this.dbus_platform == undefined) {
this.dbus_platform = new DbusBase("org-asuslinux-platform-4.xml", "/org/asuslinux/Platform");
await this.dbus_platform.start();
}
if (this.dbus_anime == undefined) {
this.dbus_anime = new DbusBase("org-asuslinux-anime-4.xml", "/org/asuslinux/Anime");
await this.dbus_anime.start();
}
this.supported_interfaces = this.dbus_platform?.proxy.SupportedInterfacesSync()[0];
this.supported_properties = this.dbus_platform?.proxy.SupportedPropertiesSync()[0];
log(this.supported_interfaces);
log(this.supported_properties);
// new AsusIndicator("selection-mode-symbolic", "mini-led-enabled");
// new AsusIndicator("selection-mode-symbolic", "panel-od-enabled");
if (!this.individual) {
if (this.feature_menu == null)
this.feature_menu = new FeatureMenuToggle(this.dbus_platform, this.dbus_anime);
} else {
if (this.supported_properties.includes("PanelOd") && this.dbus_platform.proxy.PanelOd != null)
if (this.panel_od == null) {
this.panel_od = new AsusQuickToggle(
this.dbus_platform,
"PanelOd",
"panel-od-enabled",
"Panel Overdrive",
);
}
if (this.supported_properties.includes("MiniLed") && this.dbus_platform.proxy.MiniLed != null)
if (this.mini_led == null) {
this.mini_led = new AsusQuickToggle(
this.dbus_platform,
"MiniLed",
"mini-led-enabled",
"Mini-LED",
);
}
if (
this.supported_interfaces.includes("Anime") &&
this.dbus_anime.proxy.EnableDisplay != null
)
if (this.anime_display == null) {
this.anime_display = new AsusQuickToggle(
this.dbus_anime,
"EnableDisplay",
"anime-power",
"AniMe Display",
);
}
if (
this.supported_interfaces.includes("Anime") &&
this.dbus_anime.proxy.BuiltinsEnabled != null
)
if (this.anime_builtins == null) {
this.anime_builtins = new AsusQuickToggle(
this.dbus_anime,
"BuiltinsEnabled",
"anime-builtins",
"Use builtins",
);
}
}
if (
this.supported_properties.includes("ChargeControlEndThreshold") &&
this.dbus_platform.proxy.ChargeControlEndThreshold != null
)
if (this.charge_thres == null) {
this.charge_thres = new AsusSlider(
this.dbus_platform,
"ChargeControlEndThreshold",
"charge-level",
"Charge Level",
);
}
}
disable() {
this.dbus_platform?.stop();
this.dbus_anime?.stop();
this.feature_menu?.destroy();
feature_menu?.destroy();
panel_od?.destroy();
mini_led?.destroy();
anime_display?.destroy();
anime_builtins?.destroy();
charge_thres?.destroy();
}
}

View File

@@ -5,7 +5,5 @@
"uuid-dev": "asusctl-gnome-dev@asus-linux.org",
"settings-schema": "org.gnome.shell.extensions.asusctl-gnome",
"version": "4.3.2",
"shell-version": [
"43", "44", "45"
]
"shell-version": ["45"]
}

View File

@@ -0,0 +1,98 @@
import { DbusBase } from "../dbus_proxy";
import {
DeviceState,
AnimBooting,
Brightness,
AnimAwake,
AnimSleeping,
AnimShutdown,
} from "../../bindings/anime";
export class AnimeDbus extends DbusBase {
deviceState: DeviceState = {
display_enabled: false,
display_brightness: Brightness.Med,
builtin_anims_enabled: false,
builtin_anims: {
boot: AnimBooting.GlitchConstruction,
awake: AnimAwake.BinaryBannerScroll,
sleep: AnimSleeping.BannerSwipe,
shutdown: AnimShutdown.GlitchOut,
},
off_when_unplugged: false,
off_when_suspended: false,
off_when_lid_closed: false,
};
// TODO: interface or something to enforce requirement of "sync()" method
public notifyAnimeStateSubscribers: any[] = [];
constructor() {
super("org-asuslinux-anime-4", "/org/asuslinux/Anime");
}
_parseData(data: any) {
if (data.length > 0) {
this.deviceState.display_enabled = data[0];
this.deviceState.display_brightness = Brightness[data[1] as Brightness];
this.deviceState.builtin_anims_enabled = data[2];
this.deviceState.builtin_anims.boot = AnimBooting[data[3][0] as AnimBooting];
this.deviceState.builtin_anims.awake = AnimAwake[data[3][1] as AnimAwake];
this.deviceState.builtin_anims.sleep = AnimSleeping[data[3][2] as AnimSleeping];
this.deviceState.builtin_anims.shutdown = AnimShutdown[data[3][3] as AnimShutdown];
this.deviceState.off_when_unplugged = data[4];
this.deviceState.off_when_suspended = data[5];
this.deviceState.off_when_lid_closed = data[6];
}
}
public getDeviceState() {
if (this.isRunning()) {
try {
// janky shit going on with DeviceStateSync
this._parseData(this.dbus_proxy.DeviceStateSync());
//@ts-ignore
log("Anime Matrix: display_enabled: " + this.deviceState.display_enabled);
//@ts-ignore
log("Anime Matrix: display_brightness: " + this.deviceState.display_brightness);
//@ts-ignore
log("Anime Matrix: builtin_anims_enabled: " + this.deviceState.builtin_anims_enabled);
//@ts-ignore
log("Anime Matrix: builtin_anims: " + this.deviceState.builtin_anims);
//@ts-ignore
log("Anime Matrix: off_when_unplugged: " + this.deviceState.off_when_unplugged);
//@ts-ignore
log("Anime Matrix: off_when_suspended: " + this.deviceState.off_when_suspended);
//@ts-ignore
log("Anime Matrix: off_when_lid_closed: " + this.deviceState.off_when_lid_closed);
} catch (e) {
//@ts-ignore
log("Failed to fetch DeviceState!", e);
}
}
return this.deviceState;
}
async start() {
await super.start();
this.getDeviceState();
this.dbus_proxy.connectSignal(
"NotifyDeviceState",
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(proxy: any = null, name: string, data: string) => {
if (proxy) {
// idiot xml parsing mneans the get is not nested while this is
this._parseData(data[0]);
this.notifyAnimeStateSubscribers.forEach((sub) => {
sub.sync();
});
}
},
);
}
async stop() {
await super.stop();
}
}

View File

@@ -0,0 +1,300 @@
import {
AuraDevRog1,
AuraDevTuf,
AuraDevice,
AuraEffect,
AuraModeNum,
AuraPower,
AuraPowerDev,
AuraZone,
Direction,
PowerZones,
Speed,
} from "../../bindings/aura";
import { DbusBase } from "./base";
export class AuraDbus extends DbusBase {
public device: AuraDevice = AuraDevice.Unknown;
public current_aura_mode: AuraModeNum = AuraModeNum.Static;
public aura_modes: Map<AuraModeNum, AuraEffect> = new Map();
public leds_powered: AuraPowerDev = {
tuf: [],
old_rog: [],
rog: {
keyboard: {
zone: PowerZones.Keyboard,
boot: false,
awake: false,
sleep: false,
shutdown: false,
},
logo: {
zone: PowerZones.Logo,
boot: false,
awake: false,
sleep: false,
shutdown: false,
},
lightbar: {
zone: PowerZones.Lightbar,
boot: false,
awake: false,
sleep: false,
shutdown: false,
},
lid: {
zone: PowerZones.Lid,
boot: false,
awake: false,
sleep: false,
shutdown: false,
},
rear_glow: {
zone: PowerZones.RearGlow,
boot: false,
awake: false,
sleep: false,
shutdown: false,
},
},
};
// TODO: interface or something to enforce requirement of "sync()" method
public notifyAuraModeSubscribers: any[] = [];
public notifyAuraPowerSubscribers: any[] = [];
constructor() {
super("org-asuslinux-aura-4", "/org/asuslinux/Aura");
}
public getDevice() {
if (this.isRunning()) {
try {
this.device = AuraDevice[this.dbus_proxy.DeviceTypeSync() as AuraDevice];
//@ts-ignore
log("LED device: " + this.device);
} catch (e) {
//@ts-ignore
log("Failed to fetch supported functionalities", e);
}
}
}
_parsePowerStates(data: any[]) {
const power: AuraPowerDev = this.leds_powered;
power.tuf = data[0].map((value: string) => {
return AuraDevTuf[value as AuraDevTuf];
});
power.old_rog = data[1].map((value: string) => {
return AuraDevRog1[value as AuraDevRog1];
});
power.rog = {
keyboard: {
zone: PowerZones[data[2][0][0] as PowerZones],
boot: data[2][0][1],
awake: data[2][0][2],
sleep: data[2][0][3],
shutdown: data[2][0][4],
},
logo: {
zone: PowerZones[data[2][1][0] as PowerZones],
boot: data[2][1][1],
awake: data[2][1][2],
sleep: data[2][1][3],
shutdown: data[2][1][4],
},
lightbar: {
zone: PowerZones[data[2][2][0] as PowerZones],
boot: data[2][2][1],
awake: data[2][2][2],
sleep: data[2][2][3],
shutdown: data[2][2][4],
},
lid: {
zone: PowerZones[data[2][3][0] as PowerZones],
boot: data[2][3][1],
awake: data[2][3][2],
sleep: data[2][3][3],
shutdown: data[2][3][4],
},
rear_glow: {
zone: PowerZones[data[2][4][0] as PowerZones],
boot: data[2][4][1],
awake: data[2][4][2],
sleep: data[2][4][3],
shutdown: data[2][4][4],
},
};
return power;
}
public getLedPower() {
if (this.isRunning()) {
try {
const data = this.dbus_proxy.LedPowerSync();
this.leds_powered = this._parsePowerStates(data);
//@ts-ignore
log("LED power tuf: " + this.leds_powered.tuf);
//@ts-ignore
log("LED power x1866: " + this.leds_powered.old_rog);
//@ts-ignore
log("LED power x19b6: " + this.leds_powered.rog);
} catch (e) {
//@ts-ignore
log("Failed to fetch supported functionalities", e);
}
}
}
public getLedMode() {
if (this.isRunning()) {
try {
this.current_aura_mode = AuraModeNum[this.dbus_proxy.LedModeSync() as AuraModeNum];
//@ts-ignore
log("Current LED mode:", this.current_aura_mode);
} catch (e) {
//@ts-ignore
log("Failed to fetch supported functionalities", e);
}
}
}
public setLedMode(mode: AuraEffect) {
if (this.isRunning()) {
try {
this.dbus_proxy.SetLedModeSync([
mode.mode,
mode.zone,
[mode.colour1.r, mode.colour1.g, mode.colour1.b],
[mode.colour2.r, mode.colour2.g, mode.colour2.b],
mode.speed,
mode.direction,
]);
} catch (e) {
//@ts-ignore
log("Failed to fetch supported functionalities", e);
}
}
}
_parseAuraEffect(data: any[]) {
const aura: AuraEffect = {
mode: AuraModeNum[data[0] as AuraModeNum],
zone: AuraZone[data[1] as AuraZone],
colour1: {
r: parseInt(data[2][0]),
g: parseInt(data[2][1]),
b: parseInt(data[2][2]),
},
colour2: {
r: parseInt(data[3][0]),
g: parseInt(data[3][1]),
b: parseInt(data[3][2]),
},
speed: Speed[data[4] as Speed],
direction: Direction[data[5] as Direction],
};
return aura;
}
// Return a list of the available modes, and the current settings for each
public getLedModes() {
// {'Breathe': ('Breathe', 'None', (166, 0, 0), (0, 0, 0), 'Med', 'Right'),
// 'Comet': ('Comet', 'None', (166, 0, 0), (0, 0, 0), 'Med', 'Right'),
// 'Static': ('Static', 'None', (78, 0, 0), (0, 0, 0), 'Med', 'Right'),
// 'Strobe': ('Strobe', 'None', (166, 0, 0), (0, 0, 0), 'Med', 'Right')}
if (this.isRunning()) {
try {
const _data = this.dbus_proxy.LedModesSync();
for (const key in _data[0]) {
const data = _data[0][key];
const aura: AuraEffect = this._parseAuraEffect(data);
this.aura_modes.set(AuraModeNum[key as AuraModeNum], aura);
}
for (const [key, value] of this.aura_modes) {
//@ts-ignore
log(key, value.zone, value.colour1.r, value.speed, value.direction);
}
} catch (e) {
//@ts-ignore
log("Failed to fetch supported functionalities", e);
}
}
}
async start() {
try {
await super.start();
this.getDevice();
this.getLedPower();
this.getLedMode();
this.getLedModes();
//@ts-ignore
log("Current LED mode data:", this.aura_modes.get(this.current_aura_mode)?.speed);
this.dbus_proxy.connectSignal("NotifyLed", (proxy: any = null, name: string, data: any) => {
if (proxy) {
const aura: AuraEffect = this._parseAuraEffect(data[0]);
this.current_aura_mode = aura.mode;
this.aura_modes.set(aura.mode, aura);
//@ts-ignore
log(
"LED data has changed to ",
aura.mode,
aura.zone,
aura.colour1.r,
aura.speed,
aura.direction,
);
this.notifyAuraModeSubscribers.forEach((sub) => {
sub.sync();
});
}
});
this.dbus_proxy.connectSignal(
"NotifyPowerStates",
(proxy: any = null, name: string, data: any) => {
if (proxy) {
const power: AuraPowerDev = this._parsePowerStates(data[0]);
this.leds_powered = power;
switch (this.device) {
case AuraDevice.Tuf:
//@ts-ignore
log("LED power has changed to ", this.leds_powered.tuf);
break;
case AuraDevice.X1854:
case AuraDevice.X1869:
case AuraDevice.X18c6:
//@ts-ignore
log("LED power has changed to ", this.leds_powered.old_rog);
break;
case AuraDevice.X19b6:
case AuraDevice.X1a30:
//@ts-ignore
log("LED power has changed to ", this.leds_powered.rog);
break;
default:
break;
}
//@ts-ignore
log("LED power has changed to ", this.leds_powered.rog);
this.notifyAuraPowerSubscribers.forEach((sub) => {
sub.sync();
});
}
},
);
} catch (e) {
//@ts-ignore
log("Supported DBus initialization failed!", e);
}
}
async stop() {
await super.stop();
}
}

View File

@@ -0,0 +1,64 @@
import { Extension, gettext as _ } from "@girs/gnome-shell/extensions/extension";
import { Gio } from "@girs/gio-2.0";
import { GLib } from "@girs/glib-2.0";
import { imports } from "@girs/gjs";
// Reads the contents of a file contained in the global resources archive. The data
// is returned as a string.
export function getStringResource(path: string | null) {
const data = Gio.resources_lookup_data(path, 0);
return new TextDecoder().decode(data.get_data()?.buffer);
}
export class DbusBase {
proxy!: Gio.DBusProxy;
connected = false;
ifaceXml = "";
dbus_path = "";
constructor(file_name: string, dbus_path: string) {
let extensionObject = Extension.lookupByUUID("asusctl-gnome@asus-linux.org");
const path = extensionObject?.path + "/resources/dbus/" + file_name;
const [ok, data] = GLib.file_get_contents(path);
if (!ok) {
throw new Error("could not read interface file");
}
this.ifaceXml = imports.byteArray.toString(data);
this.dbus_path = dbus_path;
}
async start() {
//@ts-ignore
log(`Starting ${this.dbus_path} dbus module`);
try {
log(this.ifaceXml);
this.proxy = Gio.DBusProxy.makeProxyWrapper(this.ifaceXml)(
Gio.DBus.system,
"org.asuslinux.Daemon",
this.dbus_path,
);
this.connected = true;
//@ts-ignore
log(`${this.dbus_path} client started successfully.`);
} catch (e) {
//@ts-ignore
logError(`${this.xml_resource} dbus init failed!`, e);
}
}
async stop() {
//@ts-ignore
log(`Stopping ${this.xml_resource} dbus module`);
if (this.connected && this.proxy != undefined) {
this.proxy.run_dispose();
this.proxy = undefined;
this.connected = false;
}
}
isRunning(): boolean {
return this.connected;
}
}

View File

@@ -0,0 +1,14 @@
import * as Main from "resource:///org/gnome/shell/ui/main.js";
import { QuickToggle } from "resource:///org/gnome/shell/ui/quickSettings.js";
export function addQuickSettingsItems(items: [typeof QuickToggle], width = 1) {
const QuickSettingsMenu = Main.panel.statusArea.quickSettings;
items.forEach((item) => QuickSettingsMenu.menu.addItem(item, width));
// Ensure the tile(s) are above the background apps menu
for (const item of items) {
QuickSettingsMenu.menu._grid.set_child_below_sibling(
item,
QuickSettingsMenu._backgroundApps.quickSettingsItems[0],
);
}
}

View File

@@ -0,0 +1,85 @@
import { addQuickSettingsItems } from "../helpers";
import { AuraDbus } from "../dbus/aura";
import { AuraEffect, AuraModeNum } from "../../bindings/aura";
import GObject from "gi://GObject";
import * as PopupMenu from "resource:///org/gnome/shell/ui/popupMenu.js";
import * as QuickSettings from "resource:///org/gnome/shell/ui/quickSettings.js";
export const AuraMenuToggle = GObject.registerClass(
class AuraMenuToggle extends QuickSettings.QuickMenuToggle {
private _dbus_aura: AuraDbus;
private _last_mode: AuraModeNum = AuraModeNum.Static;
constructor(dbus_aura: AuraDbus) {
super({
title: "Aura Modes",
iconName: "selection-mode-symbolic",
toggleMode: true,
});
this._dbus_aura = dbus_aura;
this.connectObject(this);
this.menu.setHeader("selection-mode-symbolic", this._dbus_aura.current_aura_mode);
this._itemsSection = new PopupMenu.PopupMenuSection();
this._dbus_aura.aura_modes.forEach((mode, key) => {
this._itemsSection.addAction(
key,
() => {
this._dbus_aura.setLedMode(mode);
this.sync();
},
"",
);
});
this.menu.addMenuItem(this._itemsSection);
// Add an entry-point for more settings
// this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
// const settingsItem = this.menu.addAction("More Settings",
// () => ExtensionUtils.openPrefs());
// // Ensure the settings are unavailable when the screen is locked
// settingsItem.visible = Main.sessionMode.allowSettings;
// this.menu._settingsActions[Me.uuid] = settingsItem;
this.connectObject(
"clicked",
() => {
let mode: AuraEffect | undefined;
if (this._dbus_aura.current_aura_mode == AuraModeNum.Static) {
mode = this._dbus_aura.aura_modes.get(this._last_mode);
} else {
mode = this._dbus_aura.aura_modes.get(AuraModeNum.Static);
}
if (mode != undefined) {
this._dbus_aura.setLedMode(mode);
this.sync();
}
},
this,
);
this._dbus_aura.notifyAuraModeSubscribers.push(this);
this.sync();
addQuickSettingsItems([this]);
}
sync() {
const checked = this._dbus_aura.current_aura_mode != AuraModeNum.Static;
this.title = this._dbus_aura.current_aura_mode;
if (
this._last_mode != this._dbus_aura.current_aura_mode &&
this._dbus_aura.current_aura_mode != AuraModeNum.Static
) {
this._last_mode = this._dbus_aura.current_aura_mode;
}
if (this.checked !== checked) this.set({ checked });
}
},
);

View File

@@ -0,0 +1,216 @@
import { Extension, gettext as _ } from "@girs/gnome-shell/extensions/extension";
import { quickSettings, popupMenu } from "@girs/gnome-shell/ui";
import { GObject } from "@girs/gobject-2.0";
import { DbusBase } from "../../modules/dbus_proxy";
import { addQuickSettingsItems } from "../helpers";
import * as AsusExtension from "../../extension";
import * as platform from "../../bindings/platform";
import { uuid } from "../../extension";
import { AsusMenuToggle } from "../rog_menu_toggle";
export const FeatureMenuToggle = GObject.registerClass(
class FeatureMenuToggle extends quickSettings.QuickMenuToggle {
private dbus_platform: DbusBase;
private dbus_anime: DbusBase;
private last_selection = "mini-led";
private supported_properties!: platform.Properties;
private supported_interfaces: string[] = [];
private miniLed?: typeof AsusMenuToggle;
private panelOd?: typeof AsusMenuToggle;
private animeDisplayPower?: typeof AsusMenuToggle;
private animePowersaveAnim?: typeof AsusMenuToggle;
_itemsSection: popupMenu.PopupMenuSection;
constructor(dbus_platform: DbusBase, dbus_anime: DbusBase) {
super({
label: "Laptop",
toggle_mode: true,
icon_name: "selection-mode-symbolic",
});
this.label = "Laptop";
this.title = "Laptop";
this.dbus_platform = dbus_platform;
this.dbus_anime = dbus_anime;
this.menu.setHeader("selection-mode-symbolic", "Laptop features");
this.last_selection = Extension.lookupByUUID(AsusExtension.uuid)
?.getSettings()
.get_string("primary-quickmenu-toggle")!;
this.supported_interfaces = this.dbus_platform?.proxy.SupportedInterfacesSync()[0];
this.supported_properties = this.dbus_platform?.proxy.SupportedPropertiesSync()[0];
// TODO: temporary block
if (this.last_selection == "mini-led" && !this.supported_properties.includes("MiniLed")) {
this.last_selection = "panel-od";
} else if (
this.last_selection == "panel-od" &&
!this.supported_properties.includes("PanelOd")
) {
this.last_selection = "anime-power";
} else if (
this.last_selection == "anime-power" &&
!this.supported_interfaces.includes("Anime")
) {
this.last_selection = "mini-led";
} else if (this.last_selection.length == 0) {
this.last_selection = "panel-od";
}
// AsusExtension.extension._settings.connect('changed::primary-quickmenu-toggle', this.sync);
Extension.lookupByUUID(uuid)
?.getSettings()
.set_string("primary-quickmenu-toggle", this.last_selection);
this._itemsSection = new popupMenu.PopupMenuSection();
if (this.supported_properties.includes("MiniLed")) {
if (this.miniLed == null) {
this.miniLed = new AsusMenuToggle(
this.dbus_platform,
"MiniLed",
"mini-led-enabled",
"Mini-LED Enabled",
);
this._itemsSection.addMenuItem(this.miniLed, 0);
this.miniLed.toggle_callback = () => {
this.last_selection = "mini-led";
};
}
}
if (this.supported_properties.includes("PanelOd")) {
if (this.panelOd == null) {
this.panelOd = new AsusMenuToggle(
this.dbus_platform,
"PanelOd",
"panel-od-enabled",
"Panel Overdrive Enabled",
);
this._itemsSection.addMenuItem(this.panelOd, 1);
this.panelOd.toggle_callback = () => {
this.last_selection = "panel-od";
};
}
}
if (this.supported_interfaces.includes("Anime")) {
if (this.animeDisplayPower == null) {
this.animeDisplayPower = new AsusMenuToggle(
this.dbus_anime,
"EnableDisplay",
"anime-power",
"AniMe Display Enabled",
);
this._itemsSection.addMenuItem(this.animeDisplayPower, 2);
this.animeDisplayPower.toggle_callback = () => {
this.last_selection = "anime-power";
};
}
if (this.animePowersaveAnim == null) {
this.animePowersaveAnim = new AsusMenuToggle(
this.dbus_anime,
"BuiltinsEnabled",
"anime-builtins",
"AniMe Built-in Animations",
);
this._itemsSection.addMenuItem(this.animePowersaveAnim, 3);
this.animePowersaveAnim.toggle_callback = () => {
this.last_selection = "anime-builtins";
};
}
}
this.connectObject(
"clicked",
() => {
this._toggle();
},
this,
);
this.menu.addMenuItem(this._itemsSection, 0);
this.dbus_platform?.proxy.connect("g-properties-changed", (_proxy, changed, invalidated) => {
//const properties = changed.deepUnpack();
this.sync();
});
this.dbus_anime?.proxy.connect("g-properties-changed", (_proxy, changed, invalidated) => {
//const properties = changed.deepUnpack();
this.sync();
});
// // Add an entry-point for more extension._settings
// this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
// const settingsItem = this.menu.addAction("More Settings",
// () => ExtensionUtils.openPrefs());
// // Ensure the extension._settings are unavailable when the screen is locked
// settingsItem.visible = Main.sessionMode.allowSettings;
// this.menu._settingsActions[Me.uuid] = settingsItem;
this.sync();
addQuickSettingsItems([this]);
}
_toggle() {
if (this.last_selection == "mini-led" && this.miniLed != null) {
if (this.checked !== this.dbus_platform.proxy.MiniLed)
this.dbus_platform.proxy.MiniLed = this.checked;
}
if (this.last_selection == "panel-od" && this.panelOd != null) {
if (this.checked !== this.dbus_platform.proxy.PanelOd) {
this.dbus_platform.proxy.PanelOd = this.checked;
}
}
if (this.last_selection == "anime-power" && this.animeDisplayPower != null) {
if (this.checked !== this.dbus_anime.proxy.EnableDisplay)
this.dbus_anime.proxy.EnableDisplay = this.checked;
}
if (this.last_selection == "anime-builtins" && this.animePowersaveAnim != null) {
if (this.checked !== this.dbus_anime.proxy.BuiltinsEnabled)
this.dbus_anime.proxy.BuiltinsEnabled = this.checked;
}
}
sync() {
let checked = false;
if (this.last_selection == "mini-led" && this.miniLed != null) {
this.title = this.miniLed.title;
checked = this.dbus_platform.proxy.MiniLed;
}
if (this.last_selection == "panel-od" && this.panelOd != null) {
this.title = this.panelOd.title;
checked = this.dbus_platform.proxy.PanelOd;
}
if (this.last_selection == "anime-power" && this.animeDisplayPower != null) {
this.title = this.animeDisplayPower.title;
checked = this.dbus_anime.proxy.EnableDisplay;
}
if (this.last_selection == "anime-builtins" && this.animePowersaveAnim != null) {
this.title = this.animePowersaveAnim.title;
checked = this.dbus_anime.proxy.BuiltinsEnabled;
}
// if (this.animePowersaveAnim != null) {
// }
if (this.checked !== checked) this.set({ checked });
}
destroy() {
// this.panelOd?.destroy();
}
},
);

View File

@@ -0,0 +1,26 @@
import { Extension, gettext as _ } from "@girs/gnome-shell/extensions/extension";
import { quickSettings, main } from "@girs/gnome-shell/ui";
import { Gio } from "@girs/gio-2.0";
import { GObject } from "@girs/gobject-2.0";
import { uuid } from "../extension";
//import { DbusBase } from '../dbus_proxy';
export const AsusIndicator = GObject.registerClass(
class AsusIndicator extends quickSettings.SystemIndicator {
private _indicator: any;
private _settings: Gio.Settings | undefined;
constructor(icon_name: string, setting_name: string) {
super();
// Create an icon for the indicator
this._indicator = this._addIndicator();
this._indicator.icon_name = icon_name;
// Showing an indicator when the feature is enabled
this._settings = Extension.lookupByUUID(uuid)?.getSettings();
this._settings?.bind(setting_name, this._indicator, "visible", Gio.SettingsBindFlags.DEFAULT);
main.panel.statusArea.quickSettings.addExternalIndicator(this);
}
},
);

View File

@@ -0,0 +1,50 @@
import { popupMenu } from "@girs/gnome-shell/ui";
import { GObject } from "@girs/gobject-2.0";
import { DbusBase } from "./dbus_proxy";
export const AsusMenuToggle = GObject.registerClass(
class AsusMenuToggle extends popupMenu.PopupSwitchMenuItem {
public title: string = "";
dbus!: DbusBase;
prop_name: string = "";
public toggle_callback = () => {};
constructor(dbus: DbusBase, prop_name: string, setting: string, title: string) {
super(title, true);
this.prop_name = prop_name;
this.dbus = dbus;
this.title = title;
this.dbus?.proxy.connect("g-properties-changed", (_proxy, changed, invalidated) => {
const properties = changed.deepUnpack();
// .find() fails on some shit for some reason
for (const v of Object.entries(properties)) {
if (v[0] == this.prop_name) {
this.sync();
break;
}
}
});
this.connectObject("toggled", () => this._toggleMode(), this);
this.connect("destroy", () => {
this.destroy();
});
this.sync();
}
_toggleMode() {
// hacky shit, index to get base object property and set it
const state = this.dbus.proxy[this.prop_name];
if (this.state !== state) this.dbus.proxy[this.prop_name] = this.state;
this.toggle_callback();
}
sync() {
const state = this.dbus.proxy[this.prop_name];
if (this.state !== state) this.setToggleState(state);
}
},
);

View File

@@ -0,0 +1,64 @@
import { Extension, gettext as _ } from "@girs/gnome-shell/extensions/extension";
import { addQuickSettingsItems } from "./helpers";
import { quickSettings } from "@girs/gnome-shell/ui";
import { Gio } from "@girs/gio-2.0";
import { GObject } from "@girs/gobject-2.0";
import { uuid } from "../extension";
import { DbusBase } from "./dbus_proxy";
export const AsusQuickToggle = GObject.registerClass(
class AsusQuickToggle extends quickSettings.QuickToggle {
dbus!: DbusBase;
prop_name: string = "";
public toggle_callback = () => {};
constructor(dbus: DbusBase, prop_name: string, setting: string, title: string) {
super({
label: title,
icon_name: "selection-mode-symbolic",
toggle_mode: true,
});
this.prop_name = prop_name;
this.label = title;
this.dbus = dbus;
this.dbus?.proxy.connect("g-properties-changed", (_proxy, changed, invalidated) => {
const properties = changed.deepUnpack();
// .find() fails on some shit for some reason
for (const v of Object.entries(properties)) {
if (v[0] == this.prop_name) {
const checked = v[1].unpack();
if (this.checked !== checked) this.checked = checked;
break;
}
}
});
this.connectObject("clicked", () => this._toggleMode(), this);
this.connect("destroy", () => {
this.destroy();
});
Extension.lookupByUUID(uuid)
?.getSettings()
.bind(setting, this, "checked", Gio.SettingsBindFlags.DEFAULT);
this.sync();
addQuickSettingsItems([this]);
}
_toggleMode() {
// hacky shit, index to get base object property and set it
const checked = this.dbus.proxy[this.prop_name];
if (this.checked !== checked) this.dbus.proxy[this.prop_name] = this.checked;
this.toggle_callback();
}
sync() {
const checked = this.dbus.proxy[this.prop_name];
if (this.checked !== checked) this.set({ checked });
}
},
);

View File

@@ -0,0 +1,75 @@
import { Extension, gettext as _ } from "@girs/gnome-shell/extensions/extension";
import { addQuickSettingsItems } from "./helpers";
import { quickSettings } from "@girs/gnome-shell/ui";
import { Gio } from "@girs/gio-2.0";
import { GObject } from "@girs/gobject-2.0";
import { uuid } from "../extension";
import { DbusBase } from "./dbus_proxy";
export const AsusSlider = GObject.registerClass(
class AsusSlider extends quickSettings.QuickSlider {
private dbus: DbusBase;
private settings: any = undefined;
private setting = "";
private prop_name = "";
constructor(dbus: DbusBase, prop_name: string, setting: string, title: string) {
super({
label: title,
icon_name: "selection-mode-symbolic",
});
this.label = title;
this.dbus = dbus;
this.setting = setting;
this.prop_name = prop_name;
this.settings = Extension.lookupByUUID(uuid)?.getSettings();
this._sliderChangedId = this.slider.connect("drag-end", this._onSliderChanged.bind(this));
// Binding the slider to a GSettings key
this.settings.connect(`changed::${this.setting}`, this._onSettingsChanged.bind(this));
// Set an accessible name for the slider
this.slider.accessible_name = title;
this.dbus?.proxy.connect("g-properties-changed", (_proxy, changed, invalidated) => {
const properties = changed.deepUnpack();
// .find() fails on some shit for some reason
for (const v of Object.entries(properties)) {
if (v[0] == this.prop_name) {
const checked = v[1].unpack();
this._sync();
break;
}
}
});
this._sync();
this._onSettingsChanged();
addQuickSettingsItems([this], 2);
}
_onSettingsChanged() {
// Prevent the slider from emitting a change signal while being updated
this.slider.block_signal_handler(this._sliderChangedId);
this.slider.value = this.settings.get_uint(this.setting) / 100.0;
this.slider.unblock_signal_handler(this._sliderChangedId);
}
_onSliderChanged() {
// Assuming our GSettings holds values between 0..100, adjust for the
// slider taking values between 0..1
const percent = Math.floor(this.slider.value * 100);
const stored = Math.floor(this.settings.get_uint(this.setting) / 100.0);
if (this.slider.value !== stored) this.dbus.proxy[this.prop_name] = percent;
this.settings.set_uint(this.setting, percent);
}
_sync() {
const value = this.dbus.proxy[this.prop_name];
if (this.slider.value !== value / 100) this.settings.set_uint(this.setting, value);
}
},
);

View File

@@ -0,0 +1,19 @@
{
// "extends": "@tsconfig/strictest/tsconfig.json",
"compilerOptions": {
"lib": ["ESNext"],
"types": [],
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"declaration": true,
"removeComments": true,
"strict": true,
"allowJs": true,
"paths": {},
"skipLibCheck": true
},
"files": ["./src/extension.ts"],
"include": ["src/*.ts"],
"exclude": [".ts-for-girrc.js", ".eslintrc.cjs"]
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,25 +0,0 @@
/* eslint-env node */
module.exports = {
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
root: true,
"rules": {
// enable additional rules
"indent": ["error", 4],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "double"],
"semi": ["error", "always"],
// override configuration set by extending "eslint:recommended"
"no-empty": "warn",
"no-cond-assign": ["error", "always"],
// disable rules from base configurations
"for-direction": "off",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/ban-ts-comment": "off"
}
};

View File

@@ -1,65 +0,0 @@
const { build } = require("esbuild");
const fs = require("fs");
const path = require("path");
var exec = require('child_process').exec;
const AdmZip = require("adm-zip");
const metadata = require("./src/metadata.json");
build({
entryPoints: ['src/extension.ts'],
outdir: 'dist',
bundle: true,
// Do not remove the functions `enable()`, `disable()` and `init()`
treeShaking: false,
// firefox60 // Since GJS 1.53.90
// firefox68 // Since GJS 1.63.90
// firefox78 // Since GJS 1.65.90
// firefox91 // Since GJS 1.71.1
// firefox102 // Since GJS 1.73.2
target: "firefox78",
platform: "node",
// platform: "neutral",
// mainFields: ['main'],
// conditions: ['require', 'default'],
// format: 'cjs',
external: ['gi://*', 'system', 'gettext', 'cairo'],
}).then(() => {
const metaSrc = path.resolve(__dirname, "src/metadata.json");
const metaDist = path.resolve(__dirname, "dist/metadata.json");
const schemaSrc = path.resolve(__dirname, "schemas");
const schemaDist = path.resolve(__dirname, "dist/schemas");
const dbusXmlSrc = path.resolve(__dirname, "../../bindings/dbus-xml");
const dbusXmlDist = path.resolve(__dirname, "dist/resources/dbus");
const zipFilename = `${metadata.uuid}.zip`;
const zipDist = path.resolve(__dirname, zipFilename);
exec('glib-compile-schemas schemas/',
(error, stdout, stderr) => {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
});
fs.copyFileSync(metaSrc, metaDist);
fs.cpSync(schemaSrc, schemaDist, { recursive: true }, (err) => {
if (err) {
console.error(err);
}
});
fs.cpSync(dbusXmlSrc, dbusXmlDist, { recursive: true }, (err) => {
if (err) {
console.error(err);
}
});
const zip = new AdmZip();
zip.addLocalFolder(path.resolve(__dirname, "dist"));
zip.writeZip(zipDist);
console.log(`Build complete. Zip file: ${zipFilename}\n`);
console.log(`Install with: gnome-extensions install ${zipFilename}`)
console.log(`Update with: gnome-extensions install ${zipFilename} --force`)
console.log(`Enable with: gnome-extensions enable ${metadata.uuid} --user`)
});

View File

@@ -1,112 +0,0 @@
import { AnimeDbus } from "./modules/dbus/animatrix";
import { Power } from "./modules/dbus/power";
import { Supported } from "./modules/dbus/supported";
import { Platform } from "./modules/dbus/platform";
import { QuickPanelOd } from "./modules/quick_toggles/panel_od";
import { IndicateMiniLed } from "./modules/indicators/mini_led";
import { QuickMiniLed } from "./modules/quick_toggles/mini_led";
import { SliderChargeLevel } from "./modules/sliders/charge";
import { QuickAnimePower } from "./modules/quick_toggles/anime_power";
import { FeatureMenuToggle } from "./modules/quick_menus/laptop_features";
import { AuraDbus } from "./modules/dbus/aura";
import { AuraMenuToggle } from "./modules/quick_menus/aura";
class Extension {
private _indicateMiniLed: typeof IndicateMiniLed;
private _quickMiniLed: typeof QuickMiniLed;
private _quickPanelOd: typeof QuickPanelOd;
private _quickAnimePower: typeof QuickAnimePower;
private _featureMenuToggle: typeof FeatureMenuToggle;
private _auraModeMenuToggle: typeof AuraMenuToggle;
private _sliderCharge: typeof SliderChargeLevel;
public dbus_supported: Supported = new Supported;
public dbus_power: Power = new Power;
public dbus_aura: AuraDbus = new AuraDbus;
public dbus_anime: AnimeDbus = new AnimeDbus;
public dbus_platform: Platform = new Platform;
constructor() {
this._indicateMiniLed = null;
this._quickMiniLed = null;
this._quickPanelOd = null;
this._quickAnimePower = null;
this._sliderCharge = null;
this.dbus_supported.start();
this.dbus_aura.start();
this.dbus_platform.start();
this.dbus_power.start();
this.dbus_anime.start();
}
enable() {
if (this._featureMenuToggle == null) {
this._featureMenuToggle = new FeatureMenuToggle(this.dbus_supported, this.dbus_platform, this.dbus_anime);
}
if (this._auraModeMenuToggle == null) {
this._auraModeMenuToggle = new AuraMenuToggle(this.dbus_aura);
}
if (this.dbus_supported.supported.rog_bios_ctrl.mini_led_mode) {
// if (this._quickMiniLed == null) {
// this._quickMiniLed = new QuickMiniLed(this.dbus_platform);
// this.dbus_platform.notifyMiniLedSubscribers.push(this._quickMiniLed);
// }
if (this._indicateMiniLed == null) {
this._indicateMiniLed = new IndicateMiniLed(this.dbus_platform);
}
}
// if (this.dbus_supported.supported.rog_bios_ctrl.panel_overdrive) {
// if (this._quickPanelOd == null) {
// this._quickPanelOd = new QuickPanelOd(this.dbus_platform);
// this.dbus_platform.notifyPanelOdSubscribers.push(this._quickPanelOd);
// }
// }
// if (this.dbus_supported.supported.anime_ctrl) {
// if (this._quickAnimePower == null) {
// this._quickAnimePower = new QuickAnimePower(this._dbus_anime);
// }
// }
if (this.dbus_supported.supported.charge_ctrl.charge_level_set) {
if (this._sliderCharge == null) {
this._sliderCharge = new SliderChargeLevel(this.dbus_power);
}
}
}
disable() {
if (this._indicateMiniLed != null) {
this._indicateMiniLed.destroy();
this._indicateMiniLed = null;
}
if (this._quickMiniLed != null) {
this._quickMiniLed.destroy();
this._quickMiniLed = null;
}
if (this._quickPanelOd != null) {
this._quickPanelOd.destroy();
this._quickPanelOd = null;
}
if (this._quickAnimePower != null) {
this._quickAnimePower.destroy();
this._quickAnimePower = null;
}
if (this._sliderCharge != null) {
this._sliderCharge.destroy();
this._sliderCharge = null;
}
this.dbus_power.stop();
this.dbus_platform.stop();
this.dbus_anime.stop();
this.dbus_aura.stop();
this.dbus_supported.stop();
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function init() {
return new Extension();
}

View File

@@ -1,119 +0,0 @@
import { DbusBase } from "./base";
import { DeviceState, AnimBooting, Brightness, AnimAwake, AnimSleeping, AnimShutdown } from "../../bindings/anime";
export class AnimeDbus extends DbusBase {
deviceState: DeviceState = {
display_enabled: false,
display_brightness: Brightness.Med,
builtin_anims_enabled: false,
builtin_anims: {
boot: AnimBooting.GlitchConstruction,
awake: AnimAwake.BinaryBannerScroll,
sleep: AnimSleeping.BannerSwipe,
shutdown: AnimShutdown.GlitchOut
},
};
// TODO: interface or something to enforce requirement of "sync()" method
public notifyAnimeStateSubscribers: any[] = [];
constructor() {
super("org-asuslinux-anime-4", "/org/asuslinux/Anime");
}
public setEnableDisplay(state: boolean | null) {
if (this.isRunning()) {
try {
// if null, toggle the current state
state = (state == null ? !this.deviceState.display_enabled : state);
if (this.deviceState.display_enabled !== state) {
this.deviceState.display_enabled = state;
}
return this.dbus_proxy.SetEnableDisplaySync(state);
} catch (e) {
//@ts-ignore
log("AniMe DBus set power failed!", e);
}
}
}
public setPowersaveAnim(state: boolean | null) {
if (this.isRunning()) {
try {
// if null, toggle the current state
state = (state == null ? !this.deviceState.builtin_anims_enabled : state);
if (this.deviceState.builtin_anims_enabled !== state) {
this.deviceState.builtin_anims_enabled = state;
}
return this.dbus_proxy.SetEnableBuiltinsSync(state);
} catch (e) {
//@ts-ignore
log("AniMe DBus set builtins failed!", e);
}
}
}
public setBrightness(brightness: Brightness) {
if (this.isRunning()) {
try {
if (this.deviceState.display_brightness !== brightness) {
this.deviceState.display_brightness = brightness;
}
return this.dbus_proxy.SetBrightnessSync(brightness);
} catch (e) {
//@ts-ignore
log("AniMe DBus set brightness failed!", e);
}
}
}
_parseData(data: any) {
if (data.length > 0) {
this.deviceState.display_enabled = data[0];
this.deviceState.display_brightness = Brightness[data[1] as Brightness];
this.deviceState.builtin_anims_enabled = data[2];
this.deviceState.builtin_anims.boot = AnimBooting[data[3][0] as AnimBooting];
this.deviceState.builtin_anims.awake = AnimAwake[data[3][1] as AnimAwake];
this.deviceState.builtin_anims.sleep = AnimSleeping[data[3][2] as AnimSleeping];
this.deviceState.builtin_anims.shutdown = AnimShutdown[data[3][2] as AnimShutdown];
}
}
public getDeviceState() {
if (this.isRunning()) {
try {
// janky shit going on with DeviceStateSync
this._parseData(this.dbus_proxy.DeviceStateSync());
} catch (e) {
//@ts-ignore
log("Failed to fetch DeviceState!", e);
}
}
return this.deviceState;
}
async start() {
await super.start();
this.getDeviceState();
this.dbus_proxy.connectSignal(
"NotifyDeviceState",
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(proxy: any = null, name: string, data: string) => {
if (proxy) {
// idiot xml parsing mneans the get is not nested while this is
this._parseData(data[0]);
this.notifyAnimeStateSubscribers.forEach(sub => {
sub.sync();
});
}
}
);
}
async stop() {
await super.stop();
}
}

View File

@@ -1,284 +0,0 @@
import { AuraDevRog1, AuraDevTuf, AuraDevice, AuraEffect, AuraModeNum, AuraPower, AuraPowerDev, AuraZone, Direction, PowerZones, Speed } from "../../bindings/aura";
import { DbusBase } from "./base";
export class AuraDbus extends DbusBase {
public device: AuraDevice = AuraDevice.Unknown;
public current_aura_mode: AuraModeNum = AuraModeNum.Static;
public aura_modes: Map<AuraModeNum, AuraEffect> = new Map;
public leds_powered: AuraPowerDev = {
tuf: [],
old_rog: [],
rog: {
keyboard: {
zone: PowerZones.Keyboard,
boot: false,
awake: false,
sleep: false,
shutdown: false
},
logo: {
zone: PowerZones.Logo,
boot: false,
awake: false,
sleep: false,
shutdown: false
},
lightbar: {
zone: PowerZones.Lightbar,
boot: false,
awake: false,
sleep: false,
shutdown: false
},
lid: {
zone: PowerZones.Lid,
boot: false,
awake: false,
sleep: false,
shutdown: false
},
rear_glow: {
zone: PowerZones.RearGlow,
boot: false,
awake: false,
sleep: false,
shutdown: false
},
}
};
// TODO: interface or something to enforce requirement of "sync()" method
public notifyAuraModeSubscribers: any[] = [];
public notifyAuraPowerSubscribers: any[] = [];
constructor() {
super("org-asuslinux-aura-4", "/org/asuslinux/Aura");
}
public getDevice() {
if (this.isRunning()) {
try {
this.device = AuraDevice[this.dbus_proxy.DeviceTypeSync() as AuraDevice];
//@ts-ignore
log("LED device: " + this.device);
} catch (e) {
//@ts-ignore
log("Failed to fetch supported functionalities", e);
}
}
}
_parsePowerStates(data: any[]) {
const power: AuraPowerDev = this.leds_powered;
power.tuf = data[0].map((value: string) => {
return AuraDevTuf[value as AuraDevTuf];
});
power.old_rog = data[1].map((value: string) => {
return AuraDevRog1[value as AuraDevRog1];
});
power.rog = {
keyboard: {
zone: PowerZones[data[2][0][0] as PowerZones],
boot: data[2][0][1],
awake: data[2][0][2],
sleep: data[2][0][3],
shutdown: data[2][0][4]
},
logo: {
zone: PowerZones[data[2][1][0] as PowerZones],
boot: data[2][1][1],
awake: data[2][1][2],
sleep: data[2][1][3],
shutdown: data[2][1][4]
},
lightbar: {
zone: PowerZones[data[2][2][0] as PowerZones],
boot: data[2][2][1],
awake: data[2][2][2],
sleep: data[2][2][3],
shutdown: data[2][2][4]
},
lid: {
zone: PowerZones[data[2][3][0] as PowerZones],
boot: data[2][3][1],
awake: data[2][3][2],
sleep: data[2][3][3],
shutdown: data[2][3][4]
},
rear_glow: {
zone: PowerZones[data[2][4][0] as PowerZones],
boot: data[2][4][1],
awake: data[2][4][2],
sleep: data[2][4][3],
shutdown: data[2][4][4]
}
};
return power;
}
public getLedPower() {
if (this.isRunning()) {
try {
const data = this.dbus_proxy.LedPowerSync();
this.leds_powered = this._parsePowerStates(data);
//@ts-ignore
log("LED power tuf: " + this.leds_powered.tuf);
//@ts-ignore
log("LED power x1866: " + this.leds_powered.old_rog);
//@ts-ignore
log("LED power x19b6: " + this.leds_powered.rog);
} catch (e) {
//@ts-ignore
log("Failed to fetch supported functionalities", e);
}
}
}
public getLedMode() {
if (this.isRunning()) {
try {
this.current_aura_mode = AuraModeNum[this.dbus_proxy.LedModeSync() as AuraModeNum];
//@ts-ignore
log("Current LED mode:", this.current_aura_mode);
} catch (e) {
//@ts-ignore
log("Failed to fetch supported functionalities", e);
}
}
}
public setLedMode(mode: AuraEffect) {
if (this.isRunning()) {
try {
this.dbus_proxy.SetLedModeSync([
mode.mode,
mode.zone,
[mode.colour1.r, mode.colour1.g, mode.colour1.b],
[mode.colour2.r, mode.colour2.g, mode.colour2.b],
mode.speed,
mode.direction]);
} catch (e) {
//@ts-ignore
log("Failed to fetch supported functionalities", e);
}
}
}
_parseAuraEffect(data: any[]) {
const aura: AuraEffect = {
mode: AuraModeNum[data[0] as AuraModeNum],
zone: AuraZone[data[1] as AuraZone],
colour1: {
r: parseInt(data[2][0]),
g: parseInt(data[2][1]),
b: parseInt(data[2][2]),
},
colour2: {
r: parseInt(data[3][0]),
g: parseInt(data[3][1]),
b: parseInt(data[3][2]),
},
speed: Speed[data[4] as Speed],
direction: Direction[data[5] as Direction],
};
return aura;
}
// Return a list of the available modes, and the current settings for each
public getLedModes() {
// {'Breathe': ('Breathe', 'None', (166, 0, 0), (0, 0, 0), 'Med', 'Right'),
// 'Comet': ('Comet', 'None', (166, 0, 0), (0, 0, 0), 'Med', 'Right'),
// 'Static': ('Static', 'None', (78, 0, 0), (0, 0, 0), 'Med', 'Right'),
// 'Strobe': ('Strobe', 'None', (166, 0, 0), (0, 0, 0), 'Med', 'Right')}
if (this.isRunning()) {
try {
const _data = this.dbus_proxy.LedModesSync();
for (const key in _data[0]) {
const data = _data[0][key];
const aura: AuraEffect = this._parseAuraEffect(data);
this.aura_modes.set(AuraModeNum[key as AuraModeNum], aura);
}
for (const [key, value] of this.aura_modes) {
//@ts-ignore
log(key, value.zone, value.colour1.r, value.speed, value.direction);
}
} catch (e) {
//@ts-ignore
log("Failed to fetch supported functionalities", e);
}
}
}
async start() {
try {
await super.start();
this.getDevice();
this.getLedPower();
this.getLedMode();
this.getLedModes();
//@ts-ignore
log("Current LED mode data:", this.aura_modes.get(this.current_aura_mode)?.speed);
this.dbus_proxy.connectSignal(
"NotifyLed",
(proxy: any = null, name: string, data: any) => {
if (proxy) {
const aura: AuraEffect = this._parseAuraEffect(data[0]);
this.current_aura_mode = aura.mode;
this.aura_modes.set(aura.mode, aura);
//@ts-ignore
log("LED data has changed to ", aura.mode, aura.zone, aura.colour1.r, aura.speed, aura.direction);
this.notifyAuraModeSubscribers.forEach(sub => {
sub.sync();
});
}
}
);
this.dbus_proxy.connectSignal(
"NotifyPowerStates",
(proxy: any = null, name: string, data: any) => {
if (proxy) {
const power: AuraPowerDev = this._parsePowerStates(data[0]);
this.leds_powered = power;
switch (this.device) {
case AuraDevice.Tuf:
//@ts-ignore
log("LED power has changed to ", this.leds_powered.tuf);
break;
case AuraDevice.X1854:
case AuraDevice.X1869:
case AuraDevice.X18c6:
//@ts-ignore
log("LED power has changed to ", this.leds_powered.old_rog);
break;
case AuraDevice.X19b6:
case AuraDevice.X1a30:
//@ts-ignore
log("LED power has changed to ", this.leds_powered.rog);
break;
default:
break;
}
//@ts-ignore
log("LED power has changed to ", this.leds_powered.rog);
this.notifyAuraPowerSubscribers.forEach(sub => {
sub.sync();
});
}
}
);
} catch (e) {
//@ts-ignore
log("Supported DBus initialization failed!", e);
}
}
async stop() {
await super.stop();
}
}

View File

@@ -1,52 +0,0 @@
declare const imports: any;
import * as Resources from "../resources";
const { Gio } = imports.gi;
export class DbusBase {
dbus_proxy: any = null; // type: Gio.DbusProxy
connected = false;
xml_resource = "";
dbus_path = "";
constructor(resource: string, dbus_path: string) {
this.xml_resource = resource;
this.dbus_path = dbus_path;
}
async start() {
//@ts-ignore
log(`Starting ${this.dbus_path} dbus module`);
try {
const xml = Resources.File.DBus(this.xml_resource);
this.dbus_proxy = new Gio.DBusProxy.makeProxyWrapper(xml)(
Gio.DBus.system,
"org.asuslinux.Daemon",
this.dbus_path,
);
this.connected = true;
//@ts-ignore
log(`${this.dbus_path} client started successfully.`);
} catch (e) {
//@ts-ignore
logError(`${this.xml_resource} dbus init failed!`, e);
}
}
async stop() {
//@ts-ignore
log(`Stopping ${this.xml_resource} dbus module`);
if (this.connected) {
this.dbus_proxy.destroy();
this.connected = false;
this.dbus_proxy = null;
}
}
isRunning(): boolean {
return this.connected;
}
}

View File

@@ -1,202 +0,0 @@
import * as bios from "../../bindings/platform";
import { DbusBase } from "./base";
// TODO: add callbacks for notifications
export class Platform extends DbusBase {
bios: bios.RogBiosSupportedFunctions = {
post_sound: false,
gpu_mux: false,
panel_overdrive: false,
dgpu_disable: false,
egpu_enable: false,
mini_led_mode: false
};
// TODO: interface or something to enforce requirement of "sync()" method
public notifyPanelOdSubscribers: any[] = [];
public notifyPostBootSoundSubscribers: any[] = [];
public notifyMiniLedSubscribers: any[] = [];
public notifyGpuMuxSubscribers: any[] = [];
constructor() {
super("org-asuslinux-platform-4", "/org/asuslinux/Platform");
}
public getPostBootSound() {
if (this.isRunning()) {
try {
this.bios.post_sound = this.dbus_proxy.PostBootSoundSync() == "true" ? true : false;
} catch (e) {
//@ts-ignore
log("Failed to get POST Boot Sound state!", e);
}
}
return this.bios.post_sound;
}
public setPostBootSound(state: boolean) {
if (this.isRunning()) {
try {
if (state !== this.bios.post_sound) {
this.bios.post_sound = state;
}
return this.dbus_proxy.SetPostBootSoundSync(state);
} catch (e) {
//@ts-ignore
log("Platform DBus set Post Boot Sound failed!", e);
}
}
}
public getGpuMuxMode() {
if (this.isRunning()) {
try {
this.bios.gpu_mux = this.dbus_proxy.GpuMuxModeSync() == "true" ? true : false;
} catch (e) {
//@ts-ignore
log("Failed to get MUX state!", e);
}
}
return this.bios.gpu_mux;
}
public setGpuMuxMode(state: boolean) {
if (this.isRunning()) {
try {
if (!state !== this.bios.gpu_mux) {
this.bios.gpu_mux = !state;
}
return this.dbus_proxy.SetGpuMuxModeSync(!state);
} catch (e) {
//@ts-ignore
log("Switching the MUX failed!", e);
}
}
}
public getPanelOd() {
if (this.isRunning()) {
try {
this.bios.panel_overdrive = this.dbus_proxy.PanelOdSync() == "true" ? true : false;
} catch (e) {
//@ts-ignore
log("Failed to get Overdrive state!", e);
}
}
return this.bios.panel_overdrive;
}
public setPanelOd(state: boolean) {
if (this.isRunning()) {
try {
if (state !== this.bios.panel_overdrive) {
this.bios.panel_overdrive = state;
}
return this.dbus_proxy.SetPanelOdSync(state);
} catch (e) {
//@ts-ignore
log("Overdrive DBus set overdrive state failed!", e);
}
}
}
public getMiniLedMode() {
if (this.isRunning()) {
try {
this.bios.mini_led_mode = this.dbus_proxy.MiniLedModeSync() == "true" ? true : false;
} catch (e) {
//@ts-ignore
log("Failed to get Overdrive state!", e);
}
}
return this.bios.mini_led_mode;
}
public setMiniLedMode(state: boolean) {
if (this.isRunning()) {
try {
if (state !== this.bios.mini_led_mode) {
this.bios.mini_led_mode = state;
}
return this.dbus_proxy.SetMiniLedModeSync(state);
} catch (e) {
//@ts-ignore
log("setMiniLedMode failed!", e);
}
}
}
async start() {
try {
await super.start();
this.getPostBootSound();
this.dbus_proxy.connectSignal(
"NotifyPostBootSound",
(proxy: any = null, _name: string, data: boolean) => {
if (proxy) {
//@ts-ignore
log(`PostBootSound changed to ${data}`);
this.notifyPostBootSoundSubscribers.forEach(sub => {
sub.sync();
});
}
}
);
this.getPanelOd();
this.dbus_proxy.connectSignal(
"NotifyPanelOd",
(proxy: any = null, _name: string, data: boolean) => {
if (proxy) {
//@ts-ignore
log(`NotifyPanelOd has changed to ${data}.`);
this.notifyPanelOdSubscribers.forEach(sub => {
sub.sync();
});
}
}
);
this.getMiniLedMode();
this.dbus_proxy.connectSignal(
"NotifyMiniLedMode",
(proxy: any = null, _name: string, data: boolean) => {
if (proxy) {
//@ts-ignore
log(`MiniLedMode has changed to ${data}.`);
this.notifyMiniLedSubscribers.forEach(sub => {
sub.sync();
});
}
}
);
this.getGpuMuxMode();
this.dbus_proxy.connectSignal(
"NotifyGpuMuxMode",
(proxy: any = null, _name: string, data: boolean) => {
if (proxy) {
//@ts-ignore
log(`MUX has changed to ${data}.`);
this.notifyGpuMuxSubscribers.forEach(sub => {
sub.sync();
});
}
}
);
} catch (e) {
//@ts-ignore
log("Platform DBus init failed!", e);
}
}
async stop() {
await super.stop();
this.bios.post_sound = false;
this.bios.panel_overdrive = false;
this.bios.mini_led_mode = false;
this.bios.gpu_mux = false;
}
}

View File

@@ -1,99 +0,0 @@
import { DbusBase } from "./base";
// function getMethods(obj: { [x: string]: { toString: () => string; }; }) {
// var result = [];
// for (var id in obj) {
// try {
// if (typeof(obj[id]) == "function") {
// result.push(id + ": " + obj[id].toString());
// }
// } catch (err) {
// result.push(id + ": inaccessible");
// }
// }
// return result;
// }
export class Power extends DbusBase {
chargeLimit = 100;
mainsOnline = false;
constructor() {
super("org-asuslinux-power-4", "/org/asuslinux/Power");
}
public getChargingLimit() {
if (this.isRunning()) {
try {
this.chargeLimit = this.dbus_proxy.ChargeControlEndThresholdSync();
} catch (e) {
//@ts-ignore
log("Failed to fetch Charging Limit!", e);
}
}
return this.chargeLimit;
}
public setChargingLimit(limit: number) {
if (this.isRunning()) {
try {
if (limit > 0 && this.chargeLimit !== limit) {
// update state
this.chargeLimit = limit;
}
return this.dbus_proxy.SetChargeControlEndThresholdSync(limit);
} catch (e) {
//@ts-ignore
log("Profile DBus set power profile failed!", e);
}
}
}
public getMainsOnline() {
if (this.isRunning()) {
try {
this.mainsOnline = this.dbus_proxy.MainsOnlineSync();
} catch (e) {
//@ts-ignore
log("Failed to fetch MainsLonline!", e);
}
}
return this.mainsOnline;
}
async start() {
try {
await super.start();
this.getChargingLimit();
this.dbus_proxy.connectSignal(
"NotifyChargeControlEndThreshold",
(proxy: any = null, name: string, data: string) => {
if (proxy) {
//@ts-ignore
log(`Charging Limit has changed to ${data}% (${name}).`);
this.chargeLimit = parseInt(data);
}
}
);
this.dbus_proxy.connectSignal(
"NotifyMainsOnline",
(proxy: any = null, name: string, data: string) => {
if (proxy) {
//@ts-ignore
log(`NotifyMainsOnline has changed to ${data}% (${name}).`);
this.mainsOnline = parseInt(data) == 1 ? true : false;
}
}
);
} catch (e) {
//@ts-ignore
log("Charging Limit DBus initialization failed!", e);
}
}
async stop() {
await super.stop();
}
}

View File

@@ -1,106 +0,0 @@
import { SupportedFunctions, AdvancedAura } from "../../bindings/platform";
import { AuraDevice, AuraModeNum, AuraZone, PowerZones } from "../../bindings/aura";
import { DbusBase } from "./base";
export class Supported extends DbusBase {
// False,
// (True,),
// (True, True),
// ('X19b6',
// True,
// ['Static',
// 'Breathe',
// 'Strobe',
// 'Rainbow',
// 'Star',
// 'Rain',
// 'Highlight',
// 'Laser',
// 'Ripple',
// 'Pulse',
// 'Comet',
// 'Flash'],
// [],
// 'PerKey',
// ['Keyboard', 'Lightbar', 'Logo', 'RearGlow']),
// (False, True, True, True, False, True)
supported: SupportedFunctions = {
anime_ctrl: false,
charge_ctrl: {
charge_level_set: false
},
platform_profile: {
platform_profile: false,
fan_curves: false
},
keyboard_led: {
dev_id: AuraDevice.Unknown,
brightness: false,
basic_modes: [],
basic_zones: [],
advanced_type: AdvancedAura.None
},
rog_bios_ctrl: {
post_sound: false,
gpu_mux: false,
panel_overdrive: false,
dgpu_disable: false,
egpu_enable: false,
mini_led_mode: false
}
};
constructor() {
super("org-asuslinux-supported-4", "/org/asuslinux/Supported");
}
public getSupported() {
if (this.isRunning()) {
try {
const _data = this.dbus_proxy.SupportedFunctionsSync();
this.supported.anime_ctrl = _data[0];
this.supported.charge_ctrl.charge_level_set = _data[1];
this.supported.platform_profile.platform_profile = _data[2][0];
this.supported.platform_profile.fan_curves = _data[2][1];
this.supported.keyboard_led.dev_id = AuraDevice[_data[3][0] as AuraDevice];
this.supported.keyboard_led.brightness = _data[3][1];
this.supported.keyboard_led.basic_modes = _data[3][2].map(function (value: string) {
return AuraModeNum[value as AuraModeNum];
});
this.supported.keyboard_led.basic_zones = _data[3][3].map(function (value: string) {
return AuraZone[value as AuraZone];
});
this.supported.keyboard_led.advanced_type = AdvancedAura[_data[3][4] as AdvancedAura];
this.supported.keyboard_led.power_zones = _data[3][5].map(function (value: string) {
return PowerZones[value as PowerZones];
});
this.supported.rog_bios_ctrl.post_sound = _data[4][0];
this.supported.rog_bios_ctrl.gpu_mux = _data[4][1];
this.supported.rog_bios_ctrl.panel_overdrive = _data[4][2];
this.supported.rog_bios_ctrl.dgpu_disable = _data[4][3];
this.supported.rog_bios_ctrl.egpu_enable = _data[4][4];
this.supported.rog_bios_ctrl.mini_led_mode = _data[4][5];
} catch (e) {
//@ts-ignore
log("Failed to fetch supported functionalities", e);
}
}
}
async start() {
try {
await super.start();
this.getSupported();
} catch (e) {
//@ts-ignore
log("Supported DBus initialization failed!", e);
}
}
async stop() {
await super.stop();
}
}

View File

@@ -1,15 +0,0 @@
declare const imports: any;
const { QuickToggle } = imports.ui.quickSettings;
const QuickSettingsMenu = imports.ui.main.panel.statusArea.quickSettings;
export function addQuickSettingsItems(items: [typeof QuickToggle], width = 1) {
// Add the items with the built-in function
QuickSettingsMenu._addItems(items, width);
// Ensure the tile(s) are above the background apps menu
for (const item of items) {
QuickSettingsMenu.menu._grid.set_child_below_sibling(item,
QuickSettingsMenu._backgroundApps.quickSettingsItems[0]);
}
}

View File

@@ -1,28 +0,0 @@
declare const imports: any;
// REF: https://gjs.guide/extensions/development/creating.html
const { GObject, Gio } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const { SystemIndicator } = imports.ui.quickSettings;
const QuickSettingsMenu = imports.ui.main.panel.statusArea.quickSettings;
export const IndicateMiniLed = GObject.registerClass(
class IndicateMiniLed extends SystemIndicator {
constructor() {
super();
// Create the icon for the indicator
this._indicator = this._addIndicator();
this._indicator.icon_name = "selection-mode-symbolic";
// Showing the indicator when the feature is enabled
this._settings = ExtensionUtils.getSettings();
this._settings.bind("mini-led-enabled",
this._indicator, "visible",
Gio.SettingsBindFlags.DEFAULT);
// Add the indicator to the panel and the toggle to the menu
QuickSettingsMenu._indicators.add_child(this);
}
});

View File

@@ -1,86 +0,0 @@
declare const imports: any;
import { AnimeDbus } from "../dbus/animatrix";
const { GObject } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const PopupMenu = imports.ui.popupMenu;
export const MenuToggleAnimePower = GObject.registerClass(
class MenuToggleAnimePower extends PopupMenu.PopupSwitchMenuItem {
private _dbus_anime: AnimeDbus;
public toggle_callback = () => {};
constructor(dbus_anime: AnimeDbus) {
super(
"AniMatrix Display Power", dbus_anime.deviceState.display_enabled
);
this._dbus_anime = dbus_anime;
this.label = "AniMatrix Display Power";
this._settings = ExtensionUtils.getSettings();
this.connectObject(
"destroy", () => this._settings.run_dispose(),
"toggled", () => this._toggleMode(),
this);
this.connect("destroy", () => {
this.destroy();
});
this.sync();
}
_toggleMode() {
this._dbus_anime.getDeviceState();
if (this.state !== this._dbus_anime.deviceState.display_enabled)
this._dbus_anime.setEnableDisplay(this.state);
this.toggle_callback();
}
sync() {
this._dbus_anime.getDeviceState();
const checked = this._dbus_anime.deviceState.display_enabled;
this.setToggleState(checked);
}
});
export const MenuToggleAnimeBuiltins = GObject.registerClass(
class MenuToggleAnimeBuiltins extends PopupMenu.PopupSwitchMenuItem {
private _dbus_anime: AnimeDbus;
public toggle_callback = () => {};
constructor(dbus_anime: AnimeDbus) {
super(
"AniMatrix Powersave Animation", dbus_anime.deviceState.builtin_anims_enabled
);
this._dbus_anime = dbus_anime;
this.label = "AniMatrix Powersave Animation";
this._settings = ExtensionUtils.getSettings();
this.connectObject(
"destroy", () => this._settings.run_dispose(),
"toggled", () => this._toggleMode(),
this);
this.connect("destroy", () => {
this.destroy();
});
this.sync();
}
_toggleMode() {
this._dbus_anime.getDeviceState();
if (this.state !== this._dbus_anime.deviceState.builtin_anims_enabled)
this._dbus_anime.setPowersaveAnim(this.state);
this.toggle_callback();
}
sync() {
this._dbus_anime.getDeviceState();
const checked = this._dbus_anime.deviceState.display_enabled;
this.setToggleState(checked);
}
});

View File

@@ -1,46 +0,0 @@
declare const imports: any;
import { Platform } from "../dbus/platform";
const { GObject, Gio } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const PopupMenu = imports.ui.popupMenu;
export const MenuToggleMiniLed = GObject.registerClass(
class MenuToggleMiniLed extends PopupMenu.PopupSwitchMenuItem {
private _dbus_platform: Platform;
public toggle_callback = () => {};
constructor(dbus_platform: Platform) {
super("MiniLED", dbus_platform.bios.mini_led_mode);
this._dbus_platform = dbus_platform;
this.label = "MiniLED";
this._settings = ExtensionUtils.getSettings();
this.connectObject(
"destroy", () => this._settings.run_dispose(),
"toggled", () => this._toggleMode(),
this);
this.connect("destroy", () => {
this.destroy();
});
this.sync();
}
_toggleMode() {
this._dbus_platform.getMiniLedMode();
const state = this._dbus_platform.bios.mini_led_mode;
if (this.state !== state)
this._dbus_platform.setMiniLedMode(this.state);
this.toggle_callback();
}
sync() {
this._dbus_platform.getMiniLedMode();
const toggled = this._dbus_platform.bios.mini_led_mode;
this.setToggleState(toggled);
}
});

View File

@@ -1,46 +0,0 @@
declare const imports: any;
import { Platform } from "../dbus/platform";
const { GObject, Gio } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const PopupMenu = imports.ui.popupMenu;
export const MenuTogglePanelOd = GObject.registerClass(
class MenuTogglePanelOd extends PopupMenu.PopupSwitchMenuItem {
private _dbus_platform: Platform;
public toggle_callback = () => {};
constructor(dbus_platform: Platform) {
super("Panel Overdrive", dbus_platform.bios.panel_overdrive);
this._dbus_platform = dbus_platform;
this.label = "Panel Overdrive";
this._settings = ExtensionUtils.getSettings();
this.connectObject(
"destroy", () => this._settings.run_dispose(),
"toggled", () => this._toggleMode(),
this);
this.connect("destroy", () => {
this.destroy();
});
this.sync();
}
_toggleMode() {
this._dbus_platform.getPanelOd();
const state = this._dbus_platform.bios.panel_overdrive;
if (this.state !== state)
this._dbus_platform.setPanelOd(this.state);
this.toggle_callback();
}
sync() {
this._dbus_platform.getPanelOd();
const toggled = this._dbus_platform.bios.panel_overdrive;
this.setToggleState(toggled);
}
});

View File

@@ -1,86 +0,0 @@
declare const imports: any;
// REF: https://gjs.guide/extensions/development/creating.html
import { addQuickSettingsItems } from "../helpers";
import { AuraDbus } from "../dbus/aura";
import { AuraEffect, AuraModeNum } from "../../bindings/aura";
const { GObject } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
// const Me = ExtensionUtils.getCurrentExtension();
// const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const QuickSettings = imports.ui.quickSettings;
export const AuraMenuToggle = GObject.registerClass(
class AuraMenuToggle extends QuickSettings.QuickMenuToggle {
private _dbus_aura: AuraDbus;
private _last_mode: AuraModeNum = AuraModeNum.Static;
constructor(dbus_aura: AuraDbus) {
super({
title: "Aura Modes",
iconName: "selection-mode-symbolic",
toggleMode: true,
});
this._dbus_aura = dbus_aura;
this.connectObject(
"destroy", () => this._settings.run_dispose(),
this);
this.menu.setHeader("selection-mode-symbolic", this._dbus_aura.current_aura_mode);
this._settings = ExtensionUtils.getSettings();
this._itemsSection = new PopupMenu.PopupMenuSection();
this._dbus_aura.aura_modes.forEach((mode, key) => {
this._itemsSection.addAction(key, () => {
this._dbus_aura.setLedMode(mode);
this.sync();
}, "");
});
this.menu.addMenuItem(this._itemsSection);
// Add an entry-point for more settings
// this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
// const settingsItem = this.menu.addAction("More Settings",
// () => ExtensionUtils.openPrefs());
// // Ensure the settings are unavailable when the screen is locked
// settingsItem.visible = Main.sessionMode.allowSettings;
// this.menu._settingsActions[Me.uuid] = settingsItem;
this.connectObject(
"clicked", () => {
let mode: AuraEffect | undefined;
if (this._dbus_aura.current_aura_mode == AuraModeNum.Static) {
mode = this._dbus_aura.aura_modes.get(this._last_mode);
} else {
mode = this._dbus_aura.aura_modes.get(AuraModeNum.Static);
}
if (mode != undefined) {
this._dbus_aura.setLedMode(mode);
this.sync();
}
},
this);
this._dbus_aura.notifyAuraModeSubscribers.push(this);
this.sync();
addQuickSettingsItems([this]);
}
sync() {
const checked = this._dbus_aura.current_aura_mode != AuraModeNum.Static;
this.title = this._dbus_aura.current_aura_mode;
if (this._last_mode != this._dbus_aura.current_aura_mode && this._dbus_aura.current_aura_mode != AuraModeNum.Static) {
this._last_mode = this._dbus_aura.current_aura_mode;
}
if (this.checked !== checked)
this.set({ checked });
}
});

View File

@@ -1,179 +0,0 @@
declare const imports: any;
// REF: https://gjs.guide/extensions/development/creating.html
import { AnimeDbus } from "../dbus/animatrix";
import { Supported } from "../dbus/supported";
import { Platform } from "../dbus/platform";
import { addQuickSettingsItems } from "../helpers";
import { MenuToggleAnimeBuiltins, MenuToggleAnimePower } from "../menu_toggles/anime";
import { MenuTogglePanelOd } from "../menu_toggles/panel_od";
import { MenuToggleMiniLed } from "../menu_toggles/mini_led";
const { GObject } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
// const Me = ExtensionUtils.getCurrentExtension();
// const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const QuickSettings = imports.ui.quickSettings;
export const FeatureMenuToggle = GObject.registerClass(
class FeatureMenuToggle extends QuickSettings.QuickMenuToggle {
private _dbus_supported: Supported;
private _dbus_platform: Platform;
private _dbus_anime: AnimeDbus;
public miniLed: typeof MenuToggleMiniLed;
public panelOd: typeof MenuTogglePanelOd;
public animeDisplayPower: typeof MenuToggleAnimePower;
public animePowersaveAnim: typeof MenuToggleAnimeBuiltins;
private primary = "mini-led";
constructor(dbus_supported: Supported, dbus_platform: Platform, dbus_anime: AnimeDbus) {
super({
title: "Laptop",
iconName: "selection-mode-symbolic",
toggleMode: true,
});
this._dbus_supported = dbus_supported;
this._dbus_platform = dbus_platform;
this._dbus_anime = dbus_anime;
this.menu.setHeader("selection-mode-symbolic", "Laptop features");
this._settings = ExtensionUtils.getSettings();
this.primary = this._settings.get_string("primary-quickmenu-toggle");
// TODO: temporary block
if (this.primary == "mini-led" && !this._dbus_supported.supported.rog_bios_ctrl.mini_led_mode) {
this.primary = "panel-od";
} else if (this.primary == "panel-od" && !this._dbus_supported.supported.rog_bios_ctrl.panel_overdrive) {
this.primary = "anime-power";
} else if (this.primary == "anime-power" && !this._dbus_supported.supported.anime_ctrl) {
this.primary = "mini-led";
} else if (this.primary.length == 0) {
this.primary = "panel-od";
}
this.connectObject(
"destroy", () => this._settings.run_dispose(),
this);
this._settings.connect('changed::primary-quickmenu-toggle',
this.sync);
this._settings.set_string("primary-quickmenu-toggle", this.primary);
this._itemsSection = new PopupMenu.PopupMenuSection();
if (this._dbus_supported.supported.rog_bios_ctrl.mini_led_mode) {
if (this.miniLed == null) {
this.miniLed = new MenuToggleMiniLed(this._dbus_platform);
this._dbus_platform.notifyMiniLedSubscribers.push(this.miniLed);
this._itemsSection.addMenuItem(this.miniLed);
this._dbus_platform.notifyMiniLedSubscribers.push(this);
this.miniLed.toggle_callback = () => {
this.primary = "mini-led";
this.sync();
}
}
}
if (this._dbus_supported.supported.rog_bios_ctrl.panel_overdrive) {
if (this.panelOd == null) {
this.panelOd = new MenuTogglePanelOd(this._dbus_platform);
this._dbus_platform.notifyPanelOdSubscribers.push(this.panelOd);
this._itemsSection.addMenuItem(this.panelOd);
this._dbus_platform.notifyPanelOdSubscribers.push(this);
this.panelOd.toggle_callback = () => {
this.primary = "panel-od";
this.sync();
}
}
}
if (this._dbus_supported.supported.anime_ctrl) {
if (this.animeDisplayPower == null) {
this.animeDisplayPower = new MenuToggleAnimePower(this._dbus_anime);
this._dbus_anime.notifyAnimeStateSubscribers.push(this.animeDisplayPower);
this._itemsSection.addMenuItem(this.animeDisplayPower);
this._dbus_anime.notifyAnimeStateSubscribers.push(this);
this.animeDisplayPower.toggle_callback = () => {
this.primary = "anime-power";
this.sync();
}
}
if (this.animePowersaveAnim == null) {
this.animePowersaveAnim = new MenuToggleAnimeBuiltins(this._dbus_anime);
this._dbus_anime.notifyAnimeStateSubscribers.push(this.animePowersaveAnim);
this._itemsSection.addMenuItem(this.animePowersaveAnim);
}
}
this.connectObject(
"clicked", () => {
this._toggle();
},
this);
this.menu.addMenuItem(this._itemsSection);
// // Add an entry-point for more settings
// this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
// const settingsItem = this.menu.addAction("More Settings",
// () => ExtensionUtils.openPrefs());
// // Ensure the settings are unavailable when the screen is locked
// settingsItem.visible = Main.sessionMode.allowSettings;
// this.menu._settingsActions[Me.uuid] = settingsItem;
this.sync();
addQuickSettingsItems([this]);
}
_toggle() {
if (this.primary == "mini-led" && this.miniLed != null) {
this._dbus_platform.getMiniLedMode();
const checked = this._dbus_platform.bios.mini_led_mode;
if (this.checked !== checked)
this._dbus_platform.setMiniLedMode(this.checked);
}
if (this.primary == "panel-od" && this.panelOd != null) {
this._dbus_platform.getPanelOd();
const checked = this._dbus_platform.bios.panel_overdrive;
if (this.checked !== checked)
this._dbus_platform.setPanelOd(this.checked);
}
if (this.primary == "anime-power" && this.animeDisplayPower != null) {
this._dbus_anime.getDeviceState();
const checked = this._dbus_anime.deviceState.display_enabled;
if (this.checked !== checked)
this._dbus_anime.setEnableDisplay(this.checked);
}
}
sync() {
let checked = false;
if (this.primary == "mini-led" && this.miniLed != null) {
this.title = this.miniLed.label;
checked = this._dbus_platform.bios.mini_led_mode;
}
if (this.primary == "panel-od" && this.panelOd != null) {
this.title = this.panelOd.label;
checked = this._dbus_platform.bios.panel_overdrive;
}
if (this.primary == "anime-power" && this.animeDisplayPower != null) {
this.title = this.animeDisplayPower.label;
checked = this._dbus_anime.deviceState.display_enabled;
}
// if (this.animePowersaveAnim != null) {
// }
if (this.checked !== checked)
this.set({ checked });
}
});

View File

@@ -1,56 +0,0 @@
declare const imports: any;
import { AnimeDbus } from "../dbus/animatrix";
import { addQuickSettingsItems } from "../helpers";
const { GObject, Gio } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const { QuickToggle } = imports.ui.quickSettings;
export const QuickAnimePower = GObject.registerClass(
class QuickAnimePower extends QuickToggle {
private _dbus_anime: AnimeDbus;
constructor(dbus_anime: AnimeDbus) {
super({
title: "AniMatrix Power",
iconName: "selection-mode-symbolic",
toggleMode: true,
});
this._dbus_anime = dbus_anime;
this.label = "AniMatrix Power";
this._settings = ExtensionUtils.getSettings();
this.connectObject(
"destroy", () => this._settings.run_dispose(),
"clicked", () => this._toggleMode(),
this);
this.connect("destroy", () => {
this.destroy();
});
this._settings.bind("anime-power",
this, "checked",
Gio.SettingsBindFlags.DEFAULT);
this.sync();
addQuickSettingsItems([this]);
}
_toggleMode() {
this._dbus_anime.getDeviceState();
const checked = this._dbus_anime.deviceState.display_enabled;
if (this.checked !== checked)
this._dbus_anime.setEnableDisplay(this.checked);
}
sync() {
this._dbus_anime.getDeviceState();
const checked = this._dbus_anime.deviceState.display_enabled;
if (this.checked !== checked)
this.set({ checked });
}
});

View File

@@ -1,54 +0,0 @@
declare const imports: any;
import { Platform } from "../dbus/platform";
import { addQuickSettingsItems } from "../helpers";
const { GObject, Gio } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const { QuickToggle } = imports.ui.quickSettings;
export const QuickMiniLed = GObject.registerClass(
class QuickMiniLed extends QuickToggle {
private _dbus_platform: Platform;
constructor(dbus_platform: Platform) {
super({
title: "MiniLED",
iconName: "selection-mode-symbolic",
toggleMode: true,
});
this._dbus_platform = dbus_platform;
this.label = "MiniLED";
this._settings = ExtensionUtils.getSettings();
this.connectObject(
"destroy", () => this._settings.run_dispose(),
"clicked", () => this._toggleMode(),
this);
this.connect("destroy", () => {
this.destroy();
});
this._settings.bind("mini-led-enabled",
this, "checked",
Gio.SettingsBindFlags.DEFAULT);
this.sync();
addQuickSettingsItems([this]);
}
_toggleMode() {
const checked = this._dbus_platform.getMiniLedMode();
if (this.checked !== checked)
this._dbus_platform.setMiniLedMode(this.checked);
}
sync() {
const checked = this._dbus_platform.getMiniLedMode();
if (this.checked !== checked)
this.set({ checked });
}
});

View File

@@ -1,54 +0,0 @@
declare const imports: any;
import { Platform } from "../dbus/platform";
import { addQuickSettingsItems } from "../helpers";
const { GObject, Gio } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const { QuickToggle } = imports.ui.quickSettings;
export const QuickPanelOd = GObject.registerClass(
class QuickPanelOd extends QuickToggle {
private _dbus_platform: Platform;
constructor(dbus_platform: Platform) {
super({
title: "Panel Overdrive",
iconName: "selection-mode-symbolic",
toggleMode: true,
});
this._dbus_platform = dbus_platform;
this.label = "Panel Overdrive";
this._settings = ExtensionUtils.getSettings();
this.connectObject(
"destroy", () => this._settings.run_dispose(),
"clicked", () => this._toggleMode(),
this);
this.connect("destroy", () => {
this.destroy();
});
this._settings.bind("panel-od-enabled",
this, "checked",
Gio.SettingsBindFlags.DEFAULT);
this.sync();
addQuickSettingsItems([this]);
}
_toggleMode() {
const checked = this._dbus_platform.getPanelOd();
if (this.checked !== checked)
this._dbus_platform.setPanelOd(this.checked);
}
sync() {
const checked = this._dbus_platform.getPanelOd();
if (this.checked !== checked)
this.set({ checked });
}
});

View File

@@ -1,20 +0,0 @@
declare const imports: any;
const Me = imports.misc.extensionUtils.getCurrentExtension();
const GLib = imports.gi.GLib;
export class File {
public static DBus(name: string) {
const file = `${Me.path}/resources/dbus/${name}.xml`;
try {
const [_ok, bytes] = GLib.file_get_contents(file);
if (!_ok)
//@ts-ignore
log(`Couldn't read contents of "${file}"`);
return _ok ? imports.byteArray.toString(bytes) : null;
} catch (e) {
//@ts-ignore
log(`Failed to load "${file}"`, e);
}
}
}

View File

@@ -1,60 +0,0 @@
import { Power } from "../dbus/power";
import { addQuickSettingsItems } from "../helpers";
declare const imports: any;
const { GObject } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const QuickSettings = imports.ui.quickSettings;
export const SliderChargeLevel = GObject.registerClass(
class SliderChargeLevel extends QuickSettings.QuickSlider {
private _dbus_power: Power;
constructor(dbus_power: Power) {
super({
iconName: "selection-mode-symbolic",
});
this._dbus_power = dbus_power;
this._sliderChangedId = this.slider.connect("drag-end",
this._onSliderChanged.bind(this));
// Binding the slider to a GSettings key
this._settings = ExtensionUtils.getSettings();
this._settings.connect("changed::charge-level",
this._onSettingsChanged.bind(this));
// Set an accessible name for the slider
this.slider.accessible_name = "Charge level";
this._sync();
this._onSettingsChanged();
addQuickSettingsItems([this], 2);
}
_onSettingsChanged() {
// Prevent the slider from emitting a change signal while being updated
this.slider.block_signal_handler(this._sliderChangedId);
this.slider.value = this._settings.get_uint("charge-level") / 100.0;
this.slider.unblock_signal_handler(this._sliderChangedId);
}
_onSliderChanged() {
// Assuming our GSettings holds values between 0..100, adjust for the
// slider taking values between 0..1
const percent = Math.floor(this.slider.value * 100);
const stored = Math.floor(this._settings.get_uint("charge-level") / 100.0);
if (this.slider.value !== stored)
this._dbus_power.setChargingLimit(percent);
this._settings.set_uint("charge-level", percent);
}
_sync() {
const value = this._dbus_power.getChargingLimit();
if (this.slider.value !== value / 100)
this._settings.set_uint("charge-level", value);
}
});

View File

@@ -1,22 +0,0 @@
{
"compilerOptions": {
"lib": [
"es2019"
],
"types": [],
"target": "es2019",
"module": "CommonJS",
"moduleResolution": "node",
"declaration": true,
"removeComments": true,
"strict": true,
"allowJs": true
},
"files": [
"./src/extension.ts",
],
"include": [
"src/*.ts",
"src/**/*.ts"
]
}

8
dmi-id/Cargo.toml Normal file
View File

@@ -0,0 +1,8 @@
[package]
name = "dmi_id"
edition = "2021"
version.workspace = true
[dependencies]
log.workspace = true
udev.workspace = true

113
dmi-id/src/lib.rs Normal file
View File

@@ -0,0 +1,113 @@
use log::{info, warn};
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Clone)]
pub struct DMIID {
pub id_model: String,
pub dmi_family: String,
pub dmi_vendor: String,
pub board_name: String,
pub board_vendor: String,
pub bios_date: String,
pub bios_release: String,
pub bios_vendor: String,
pub bios_version: String,
pub product_family: String,
pub product_name: String,
}
impl DMIID {
pub fn new() -> Result<Self, String> {
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
format!("dmi enumerator failed: {err}")
})?;
enumerator.match_subsystem("dmi").map_err(|err| {
warn!("{}", err);
format!("dmi match_subsystem failed: {err}")
})?;
let mut result = enumerator.scan_devices().map_err(|err| {
warn!("{}", err);
format!("dmi scan_devices failed: {err}")
})?;
if let Some(device) = (result).next() {
info!("Found dmi ID info at {:?}", device.sysname());
return Ok(Self {
id_model: device
.property_value("ID_MODEL")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
dmi_family: device
.property_value("DMI_FAMILY")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
dmi_vendor: device
.property_value("DMI_VENDOR")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
board_name: device
.attribute_value("board_name")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
board_vendor: device
.attribute_value("board_vendor")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
bios_date: device
.attribute_value("bios_date")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
bios_release: device
.attribute_value("bios_release")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
bios_vendor: device
.attribute_value("bios_vendor")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
bios_version: device
.attribute_value("bios_version")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
product_family: device
.attribute_value("product_family")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
product_name: device
.attribute_value("product_name")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
});
}
Err("dmi not found".into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore = "Does not run in docker images"]
fn dmi_sysfs_properties_not_unknown() {
let dmi = DMIID::new().unwrap();
assert_ne!(dmi.id_model, "Unknown".to_string());
dbg!(dmi.id_model);
assert_ne!(dmi.dmi_family, "Unknown".to_string());
dbg!(dmi.dmi_family);
assert_ne!(dmi.dmi_vendor, "Unknown".to_string());
dbg!(dmi.dmi_vendor);
assert_ne!(dmi.board_name, "Unknown".to_string());
dbg!(dmi.board_name);
assert_ne!(dmi.board_vendor, "Unknown".to_string());
dbg!(dmi.board_vendor);
assert_ne!(dmi.product_family, "Unknown".to_string());
dbg!(dmi.product_family);
assert_ne!(dmi.product_name, "Unknown".to_string());
dbg!(dmi.product_name);
}
}

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