From 0ed97db4c1298c3cd5d94a075194ba7a88ba4daa Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Sun, 22 Aug 2021 14:01:05 +1200 Subject: [PATCH 01/11] Temporary checkpoint --- Cargo.lock | 85 ++--- asus-notify/src/main.rs | 18 +- asusctl/Cargo.toml | 1 - asusctl/src/main.rs | 97 +----- asusctl/src/profiles_cli.rs | 36 -- daemon/Cargo.toml | 1 - daemon/src/config.rs | 49 +-- daemon/src/config_anime.rs | 257 -------------- daemon/src/config_aura.rs | 267 --------------- daemon/src/config_old.rs | 59 +--- daemon/src/ctrl_anime.rs | 456 ------------------------- daemon/src/ctrl_gfx/controller.rs | 7 +- daemon/src/ctrl_gfx/mod.rs | 2 +- daemon/src/ctrl_gfx/zbus_gfx.rs | 56 --- daemon/src/ctrl_leds/controller.rs | 394 --------------------- daemon/src/ctrl_leds/mod.rs | 2 - daemon/src/ctrl_leds/zbus.rs | 165 --------- daemon/src/ctrl_profiles/controller.rs | 168 +++++---- daemon/src/ctrl_profiles/mod.rs | 4 +- daemon/src/ctrl_profiles/zbus.rs | 136 ++++---- daemon/src/ctrl_supported.rs | 12 +- daemon/src/daemon.rs | 33 +- daemon/src/error.rs | 9 - daemon/src/lib.rs | 6 +- rog-dbus/Cargo.toml | 2 +- rog-dbus/src/lib.rs | 2 +- rog-dbus/src/zbus_profile.rs | 58 +--- rog-profiles/Cargo.toml | 2 - rog-profiles/src/error.rs | 21 -- rog-profiles/src/lib.rs | 284 ++++++++++++++- rog-profiles/src/profiles.rs | 185 ---------- rog-types/src/supported.rs | 20 +- 32 files changed, 553 insertions(+), 2341 deletions(-) delete mode 100644 daemon/src/config_anime.rs delete mode 100644 daemon/src/config_aura.rs delete mode 100644 daemon/src/ctrl_anime.rs delete mode 100644 daemon/src/ctrl_gfx/zbus_gfx.rs delete mode 100644 daemon/src/ctrl_leds/controller.rs delete mode 100644 daemon/src/ctrl_leds/mod.rs delete mode 100644 daemon/src/ctrl_leds/zbus.rs delete mode 100644 rog-profiles/src/profiles.rs diff --git a/Cargo.lock b/Cargo.lock index 0a29491a..c3e4811e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,7 +52,6 @@ dependencies = [ "rog_anime", "rog_aura", "rog_dbus", - "rog_fan_curve 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "rog_profiles", "rog_types", "tinybmp", @@ -109,9 +108,9 @@ checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "blake2b_simd" @@ -214,7 +213,6 @@ dependencies = [ "rog_anime", "rog_aura", "rog_dbus", - "rog_fan_curve 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "rog_profiles", "rog_types", "rusb", @@ -253,7 +251,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.74", + "syn 1.0.75", ] [[package]] @@ -305,7 +303,7 @@ checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.74", + "syn 1.0.75", ] [[package]] @@ -403,7 +401,7 @@ dependencies = [ "proc-macro-hack", "proc-macro2", "quote 1.0.9", - "syn 1.0.74", + "syn 1.0.75", ] [[package]] @@ -497,7 +495,7 @@ checksum = "915ef07c710d84733522461de2a734d4d62a3fd39a4d4f404c2f385ef8618d05" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.74", + "syn 1.0.75", ] [[package]] @@ -524,16 +522,6 @@ dependencies = [ "cfg-if 1.0.0", ] -[[package]] -name = "intel-pstate" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dbd48c2f4886e44c137f4acb6ba3cf8df15154a2c996a65ee5e57c54a04c01f" -dependencies = [ - "smart-default", - "thiserror", -] - [[package]] name = "itoa" version = "0.4.7" @@ -548,9 +536,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" +checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5" [[package]] name = "libudev-sys" @@ -620,9 +608,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "miniz_oxide" @@ -650,7 +638,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" dependencies = [ - "bitflags 1.2.1", + "bitflags 1.3.2", "cc", "cfg-if 0.1.10", "libc", @@ -861,7 +849,7 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" dependencies = [ - "bitflags 1.2.1", + "bitflags 1.3.2", ] [[package]] @@ -939,26 +927,10 @@ dependencies = [ "zvariant", ] -[[package]] -name = "rog_fan_curve" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a810b86236d51df31d9098ba1a1876c9f470573f903d13b78f5ee48d4e99ee90" -dependencies = [ - "serde", -] - -[[package]] -name = "rog_fan_curve" -version = "0.1.10" -source = "git+https://github.com/Yarn/rog_fan_curve.git#d46ead0db7c8c4870dae960c3a43b4ac6ab57c2d" - [[package]] name = "rog_profiles" version = "0.1.2" dependencies = [ - "intel-pstate", - "rog_fan_curve 0.1.10 (git+https://github.com/Yarn/rog_fan_curve.git)", "serde", "serde_derive", "zvariant", @@ -1012,22 +984,22 @@ checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" [[package]] name = "serde" -version = "1.0.127" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8" +checksum = "1056a0db1978e9dbf0f6e4fca677f6f9143dc1c19de346f22cac23e422196834" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.127" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc" +checksum = "13af2fbb8b60a8950d6c72a56d2095c28870367cc8e10c55e9745bac4995a2c4" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.74", + "syn 1.0.75", ] [[package]] @@ -1049,7 +1021,7 @@ checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.74", + "syn 1.0.75", ] [[package]] @@ -1058,17 +1030,6 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590" -[[package]] -name = "smart-default" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" -dependencies = [ - "proc-macro2", - "quote 1.0.9", - "syn 1.0.74", -] - [[package]] name = "socket2" version = "0.4.1" @@ -1114,9 +1075,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.74" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" +checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7" dependencies = [ "proc-macro2", "quote 1.0.9", @@ -1167,7 +1128,7 @@ checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.74", + "syn 1.0.75", ] [[package]] @@ -1375,7 +1336,7 @@ dependencies = [ "proc-macro-crate 0.1.5", "proc-macro2", "quote 1.0.9", - "syn 1.0.74", + "syn 1.0.75", ] [[package]] @@ -1400,5 +1361,5 @@ dependencies = [ "proc-macro-crate 1.0.0", "proc-macro2", "quote 1.0.9", - "syn 1.0.74", + "syn 1.0.75", ] diff --git a/asus-notify/src/main.rs b/asus-notify/src/main.rs index 71910957..83f96401 100644 --- a/asus-notify/src/main.rs +++ b/asus-notify/src/main.rs @@ -1,7 +1,7 @@ use notify_rust::{Hint, Notification, NotificationHandle}; use rog_aura::AuraEffect; use rog_dbus::{DbusProxies, Signals}; -use rog_profiles::profiles::{FanLevel, Profile}; +use rog_profiles::Profile; use rog_types::gfx_vendors::GfxRequiredUserAction; use rog_types::gfx_vendors::GfxVendors; use std::error::Error; @@ -88,19 +88,17 @@ fn main() -> Result<(), Box> { } fn do_thermal_notif(profile: &Profile) -> Result> { - let fan = profile.fan_preset; - let turbo = if profile.turbo { "enabled" } else { "disabled" }; - let icon = match fan { - FanLevel::Normal => "asus_notif_yellow", - FanLevel::Boost => "asus_notif_red", - FanLevel::Silent => "asus_notif_green", + let icon = match profile { + Profile::Balanced => "asus_notif_yellow", + Profile::Performance => "asus_notif_red", + Profile::Quiet => "asus_notif_green", }; + let profile: &str = (*profile).into(); let x = Notification::new() .summary("ASUS ROG") .body(&format!( - "Thermal profile changed to {}, turbo {}", - profile.name.to_uppercase(), - turbo + "Thermal profile changed to {}", + profile.to_uppercase(), )) .hint(Hint::Resident(true)) .timeout(2000) diff --git a/asusctl/Cargo.toml b/asusctl/Cargo.toml index b9cbd593..c84f1d2b 100644 --- a/asusctl/Cargo.toml +++ b/asusctl/Cargo.toml @@ -13,7 +13,6 @@ rog_dbus = { path = "../rog-dbus" } rog_profiles = { path = "../rog-profiles" } rog_types = { path = "../rog-types" } daemon = { path = "../daemon" } -rog_fan_curve = { version = "^0.1", features = ["serde"] } gumdrop = "^0.8" yansi-term = "^0.1" diff --git a/asusctl/src/main.rs b/asusctl/src/main.rs index cf736f8c..6e41a292 100644 --- a/asusctl/src/main.rs +++ b/asusctl/src/main.rs @@ -9,11 +9,10 @@ use profiles_cli::ProfileCommand; use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2, ANIME_DATA_LEN}; use rog_aura::{self, AuraEffect}; use rog_dbus::RogDbusClient; -use rog_profiles::profiles::Profile; use rog_types::{ gfx_vendors::{GfxRequiredUserAction, GfxVendors}, supported::{ - AnimeSupportedFunctions, FanCpuSupportedFunctions, LedSupportedFunctions, + AnimeSupportedFunctions, LedSupportedFunctions, PlatformProfileFunctions, RogBiosSupportedFunctions, }, }; @@ -152,7 +151,7 @@ fn main() -> Result<(), Box> { match parsed.command { Some(CliCommand::LedMode(mode)) => handle_led_mode(&dbus, &supported.keyboard_led, &mode)?, - Some(CliCommand::Profile(cmd)) => handle_profile(&dbus, &supported.fan_cpu_ctrl, &cmd)?, + Some(CliCommand::Profile(cmd)) => handle_profile(&dbus, &supported.platform_profile, &cmd)?, Some(CliCommand::Graphics(cmd)) => do_gfx(&dbus, &supported.rog_bios_ctrl, cmd)?, Some(CliCommand::Anime(cmd)) => handle_anime(&dbus, &supported.anime_ctrl, &cmd)?, Some(CliCommand::Bios(cmd)) => handle_bios_option(&dbus, &supported.rog_bios_ctrl, &cmd)?, @@ -384,24 +383,10 @@ fn handle_led_mode( fn handle_profile( dbus: &RogDbusClient, - supported: &FanCpuSupportedFunctions, + supported: &PlatformProfileFunctions, cmd: &ProfileCommand, ) -> Result<(), Box> { - if !cmd.next - && !cmd.create // TODO - && !cmd.list - && cmd.profile.is_none() - && !cmd.active_name - && !cmd.active_data - && !cmd.profiles_data - && cmd.remove.is_none() - && cmd.curve.is_none() // TODO - && cmd.fan_preset.is_none() // TODO - && cmd.turbo.is_none() // TODO - && cmd.max_percentage.is_none() // TODO - && cmd.min_percentage.is_none() - // TODO - { + if !cmd.next && !cmd.list && !cmd.active_name && !cmd.active_data && !cmd.profiles_data { if !cmd.help { println!("Missing arg or command\n"); } @@ -411,7 +396,7 @@ fn handle_profile( .collect(); for line in usage .iter() - .filter(|line| !line.contains("--curve") || supported.fan_curve_set) + .filter(|line| !line.contains("--curve") || supported.fan_curves) { println!("{}", line); } @@ -426,78 +411,8 @@ fn handle_profile( } if cmd.next { - dbus.proxies().profile().next_fan()?; + dbus.proxies().profile().next_profile()?; } - if let Some(profile) = &cmd.remove { - dbus.proxies().profile().remove(profile)? - } - if cmd.list { - let profile_names = dbus.proxies().profile().profile_names()?; - println!("Available profiles are {:?}", profile_names); - } - if cmd.active_name { - println!( - "Active profile: {:?}", - dbus.proxies().profile().active_name()? - ); - } - if cmd.active_data { - println!("Active profile:"); - println!("{:?}", dbus.proxies().profile().active_data()?); - } - if cmd.profiles_data { - println!("Profiles:"); - for s in dbus.proxies().profile().all_profile_data()? { - println!("{:?}", s); - } - } - - let mut set_profile = false; - let mut profile = Profile::default(); - if cmd.create { - set_profile = true; - } else if let Some(ref name) = cmd.profile { - let profiles = dbus.proxies().profile().all_profile_data()?; - for p in profiles { - if p.name == *name { - profile = p; - break; - } - } - if profile.name != *name { - println!("The requested profile doesn't exist, you may need to create it"); - std::process::exit(-1); - } - } - - if let Some(turbo) = cmd.turbo { - set_profile = true; - profile.turbo = turbo; - } - if let Some(min) = cmd.min_percentage { - set_profile = true; - profile.min_percentage = min; - } - if let Some(max) = cmd.max_percentage { - set_profile = true; - profile.max_percentage = max; - } - if let Some(preset) = cmd.fan_preset { - set_profile = true; - profile.fan_preset = preset; - } - if let Some(ref curve) = cmd.curve { - set_profile = true; - profile.fan_curve = curve.as_config_string(); - } - if let Some(ref name) = cmd.profile { - set_profile = true; - profile.name = name.clone(); - } - if set_profile { - dbus.proxies().profile().new_or_modify(&profile)?; - } - Ok(()) } diff --git a/asusctl/src/profiles_cli.rs b/asusctl/src/profiles_cli.rs index ada243f1..921ba7a2 100644 --- a/asusctl/src/profiles_cli.rs +++ b/asusctl/src/profiles_cli.rs @@ -1,6 +1,4 @@ use gumdrop::Options; -use rog_fan_curve::{Curve, Fan}; -use rog_profiles::profiles::FanLevel; #[derive(Debug, Clone, Options)] pub struct ProfileCommand { @@ -8,10 +6,6 @@ pub struct ProfileCommand { pub help: bool, #[options(help = "toggle to next profile in list")] pub next: bool, - #[options(help = "create the profile if it doesn't exist")] - pub create: bool, - #[options(meta = "", help = "remove a profile by name")] - pub remove: Option, #[options(help = "list available profiles")] pub list: bool, #[options(help = "get active profile name")] @@ -20,34 +14,4 @@ pub struct ProfileCommand { pub active_data: bool, #[options(help = "get all profile data")] pub profiles_data: bool, - - // Options for profile - #[options(meta = "", help = "enable or disable cpu turbo")] - pub turbo: Option, - #[options(meta = "", help = "set min cpu scaling (intel)")] - pub min_percentage: Option, - #[options(meta = "", help = "set max cpu scaling (intel)")] - pub max_percentage: Option, - - #[options(meta = "", help = "")] - pub fan_preset: Option, - #[options( - meta = "", - parse(try_from_str = "parse_fan_curve"), - help = "set fan curve" - )] - pub curve: Option, - #[options(free)] - pub profile: Option, -} - -fn parse_fan_curve(data: &str) -> Result { - let curve = Curve::from_config_str(data)?; - if let Err(err) = curve.check_safety(Fan::Cpu) { - return Err(format!("Unsafe curve {:?}", err)); - } - if let Err(err) = curve.check_safety(Fan::Gpu) { - return Err(format!("Unsafe curve {:?}", err)); - } - Ok(curve) } diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index c54010ab..60e167f8 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -43,4 +43,3 @@ toml = "^0.5" # Device control sysfs-class = "^0.1.2" # used for backlight control and baord ID -rog_fan_curve = { version = "0.1", features = ["serde"] } diff --git a/daemon/src/config.rs b/daemon/src/config.rs index b194802d..5d7c73c3 100644 --- a/daemon/src/config.rs +++ b/daemon/src/config.rs @@ -1,8 +1,6 @@ use log::{error, info, warn}; -use rog_profiles::profiles::{FanLevel, Profile}; use rog_types::gfx_vendors::GfxVendors; use serde_derive::{Deserialize, Serialize}; -use std::collections::BTreeMap; use std::fs::{File, OpenOptions}; use std::io::{Read, Write}; @@ -20,61 +18,18 @@ pub struct Config { pub gfx_tmp_mode: Option, pub gfx_managed: bool, pub gfx_vfio_enable: bool, - pub active_profile: String, - pub toggle_profiles: Vec, - #[serde(skip)] - pub curr_fan_mode: u8, + /// Save charge limit for restoring on boot pub bat_charge_limit: u8, - pub power_profiles: BTreeMap, } impl Default for Config { fn default() -> Self { - let mut pwr = BTreeMap::new(); - pwr.insert( - "normal".into(), - Profile::new( - "normal".into(), - 0, - 100, - true, - FanLevel::Normal, - "".to_string(), - ), - ); - pwr.insert( - "boost".into(), - Profile::new( - "boost".into(), - 0, - 100, - true, - FanLevel::Boost, - "".to_string(), - ), - ); - pwr.insert( - "silent".into(), - Profile::new( - "silent".into(), - 0, - 100, - false, - FanLevel::Silent, - "".to_string(), - ), - ); - Config { gfx_mode: GfxVendors::Hybrid, gfx_tmp_mode: None, gfx_managed: true, gfx_vfio_enable: false, - active_profile: "normal".into(), - toggle_profiles: vec!["normal".into(), "boost".into(), "silent".into()], - curr_fan_mode: 0, bat_charge_limit: 100, - power_profiles: pwr, } } } @@ -125,6 +80,7 @@ impl Config { fn create_default(file: &mut File) -> Self { let config = Config::default(); + // Should be okay to unwrap this as is since it is a Default let json = serde_json::to_string_pretty(&config).unwrap(); file.write_all(json.as_bytes()) @@ -146,7 +102,6 @@ impl Config { .unwrap_or_else(|_| panic!("Could not deserialise {}", CONFIG_PATH)); // copy over serde skipped values x.gfx_tmp_mode = self.gfx_tmp_mode; - x.curr_fan_mode = self.curr_fan_mode; *self = x; } } diff --git a/daemon/src/config_anime.rs b/daemon/src/config_anime.rs deleted file mode 100644 index e189ad8e..00000000 --- a/daemon/src/config_anime.rs +++ /dev/null @@ -1,257 +0,0 @@ -use crate::VERSION; -use log::{error, info, warn}; -use rog_anime::Fade; -use rog_anime::{error::AnimeError, ActionData, ActionLoader, AnimTime, Vec2}; -use serde_derive::{Deserialize, Serialize}; -use std::fs::{File, OpenOptions}; -use std::io::{Read, Write}; -use std::time::Duration; - -pub static ANIME_CONFIG_PATH: &str = "/etc/asusd/anime.conf"; -pub static ANIME_CACHE_PATH: &str = "/etc/asusd/anime-cache.conf"; - -#[derive(Deserialize, Serialize)] -pub struct AnimeConfigV341 { - pub system: Option, - pub boot: Option, - pub suspend: Option, - pub shutdown: Option, -} - -impl AnimeConfigV341 { - pub(crate) fn into_current(self) -> AnimeConfig { - AnimeConfig { - system: if let Some(ani) = self.system { - vec![ani] - } else { - vec![] - }, - boot: if let Some(ani) = self.boot { - vec![ani] - } else { - vec![] - }, - wake: if let Some(ani) = self.suspend { - vec![ani] - } else { - vec![] - }, - shutdown: if let Some(ani) = self.shutdown { - vec![ani] - } else { - vec![] - }, - brightness: 1.0, - awake_enabled: true, - boot_anim_enabled: true, - } - } -} - -#[derive(Deserialize, Serialize)] -pub struct AnimeConfigV352 { - pub system: Vec, - pub boot: Vec, - pub wake: Vec, - pub shutdown: Vec, - pub brightness: f32, -} - -impl AnimeConfigV352 { - pub(crate) fn into_current(self) -> AnimeConfig { - AnimeConfig { - system: self.system, - boot: self.boot, - wake: self.wake, - shutdown: self.shutdown, - brightness: 1.0, - awake_enabled: true, - boot_anim_enabled: true, - } - } -} - -#[derive(Deserialize, Serialize, Default)] -pub struct AnimeConfigCached { - pub system: Vec, - pub boot: Vec, - pub wake: Vec, - pub shutdown: Vec, -} - -impl AnimeConfigCached { - pub fn init_from_config(&mut self, config: &AnimeConfig) -> Result<(), AnimeError> { - let mut sys = Vec::with_capacity(config.system.len()); - for ani in config.system.iter() { - sys.push(ActionData::from_anime_action(ani)?); - } - self.system = sys; - - let mut boot = Vec::with_capacity(config.boot.len()); - for ani in config.boot.iter() { - boot.push(ActionData::from_anime_action(ani)?); - } - self.boot = boot; - - let mut wake = Vec::with_capacity(config.wake.len()); - for ani in config.wake.iter() { - wake.push(ActionData::from_anime_action(ani)?); - } - self.wake = wake; - - let mut shutdown = Vec::with_capacity(config.shutdown.len()); - for ani in config.shutdown.iter() { - shutdown.push(ActionData::from_anime_action(ani)?); - } - self.shutdown = shutdown; - Ok(()) - } -} - -/// Config for base system actions for the anime display -#[derive(Deserialize, Serialize)] -pub struct AnimeConfig { - pub system: Vec, - pub boot: Vec, - pub wake: Vec, - pub shutdown: Vec, - pub brightness: f32, - pub awake_enabled: bool, - pub boot_anim_enabled: bool, -} - -impl Default for AnimeConfig { - fn default() -> Self { - AnimeConfig { - system: Vec::new(), - boot: Vec::new(), - wake: Vec::new(), - shutdown: Vec::new(), - brightness: 1.0, - awake_enabled: true, - boot_anim_enabled: true, - } - } -} - -impl AnimeConfig { - /// `load` will attempt to read the config, and panic if the dir is missing - pub fn load() -> Self { - let mut file = OpenOptions::new() - .read(true) - .write(true) - .create(true) - .open(&ANIME_CONFIG_PATH) - .unwrap_or_else(|_| { - panic!( - "The file {} or directory /etc/asusd/ is missing", - ANIME_CONFIG_PATH - ) - }); // okay to cause panic here - let mut buf = String::new(); - if let Ok(read_len) = file.read_to_string(&mut buf) { - if read_len == 0 { - return AnimeConfig::create_default(&mut file); - } else { - if let Ok(data) = serde_json::from_str(&buf) { - return data; - } else if let Ok(data) = serde_json::from_str::(&buf) { - let config = data.into_current(); - config.write(); - info!("Updated config version to: {}", VERSION); - return config; - } else if let Ok(data) = serde_json::from_str::(&buf) { - let config = data.into_current(); - config.write(); - info!("Updated config version to: {}", VERSION); - return config; - } - AnimeConfig::write_backup(buf); - warn!( - "Could not deserialise {}. Backed up as *-old", - ANIME_CONFIG_PATH - ); - } - } - AnimeConfig::create_default(&mut file) - } - - fn create_default(file: &mut File) -> Self { - // create a default config here - let config = AnimeConfig { - system: vec![], - boot: vec![ActionLoader::ImageAnimation { - file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(), - scale: 0.9, - angle: 0.65, - translation: Vec2::default(), - brightness: 1.0, - time: AnimTime::Fade(Fade::new( - Duration::from_secs(2), - Some(Duration::from_secs(2)), - Duration::from_secs(2), - )), - }], - wake: vec![ActionLoader::ImageAnimation { - file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(), - scale: 0.9, - angle: 0.65, - translation: Vec2::default(), - brightness: 1.0, - time: AnimTime::Fade(Fade::new( - Duration::from_secs(2), - Some(Duration::from_secs(2)), - Duration::from_secs(2), - )), - }], - shutdown: 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, - }], - brightness: 1.0, - awake_enabled: true, - boot_anim_enabled: true, - }; - // Should be okay to unwrap this as is since it is a Default - let json = serde_json::to_string_pretty(&config).unwrap(); - file.write_all(json.as_bytes()) - .unwrap_or_else(|_| panic!("Could not write {}", ANIME_CONFIG_PATH)); - config - } - - pub fn read(&mut self) { - let mut file = OpenOptions::new() - .read(true) - .open(&ANIME_CONFIG_PATH) - .unwrap_or_else(|err| panic!("Error reading {}: {}", ANIME_CONFIG_PATH, err)); - let mut buf = String::new(); - if let Ok(l) = file.read_to_string(&mut buf) { - if l == 0 { - warn!("File is empty {}", ANIME_CONFIG_PATH); - } else { - let x: AnimeConfig = serde_json::from_str(&buf) - .unwrap_or_else(|_| panic!("Could not deserialise {}", ANIME_CONFIG_PATH)); - *self = x; - } - } - } - - pub fn write(&self) { - let mut file = File::create(ANIME_CONFIG_PATH).expect("Couldn't overwrite config"); - let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed"); - file.write_all(json.as_bytes()) - .unwrap_or_else(|err| error!("Could not write config: {}", err)); - } - - fn write_backup(buf: String) { - let mut path = ANIME_CONFIG_PATH.to_string(); - path.push_str("-old"); - let mut file = File::create(&path).expect("Couldn't overwrite config"); - file.write_all(buf.as_bytes()) - .unwrap_or_else(|err| error!("Could not write config: {}", err)); - } -} diff --git a/daemon/src/config_aura.rs b/daemon/src/config_aura.rs deleted file mode 100644 index 6e5d3778..00000000 --- a/daemon/src/config_aura.rs +++ /dev/null @@ -1,267 +0,0 @@ -use crate::laptops::LaptopLedData; -use log::{error, info, warn}; -use rog_aura::{AuraEffect, AuraModeNum, AuraZone, LedBrightness}; -use serde_derive::{Deserialize, Serialize}; -use std::collections::BTreeMap; -use std::fs::{File, OpenOptions}; -use std::io::{Read, Write}; - -pub static AURA_CONFIG_PATH: &str = "/etc/asusd/aura.conf"; - -#[derive(Deserialize, Serialize)] -pub struct AuraConfigV320 { - pub brightness: u32, - pub current_mode: AuraModeNum, - pub builtins: BTreeMap, - pub multizone: Option, -} - -impl AuraConfigV320 { - pub(crate) fn into_current(self) -> AuraConfig { - AuraConfig { - brightness: ::from(self.brightness), - current_mode: self.current_mode, - builtins: self.builtins, - multizone: self.multizone, - awake_enabled: true, - sleep_anim_enabled: true, - } - } -} - -#[derive(Deserialize, Serialize)] -pub struct AuraConfigV352 { - pub brightness: LedBrightness, - pub current_mode: AuraModeNum, - pub builtins: BTreeMap, - pub multizone: Option, -} - -impl AuraConfigV352 { - pub(crate) fn into_current(self) -> AuraConfig { - AuraConfig { - brightness: self.brightness, - current_mode: self.current_mode, - builtins: self.builtins, - multizone: self.multizone, - awake_enabled: true, - sleep_anim_enabled: true, - } - } -} - -#[derive(Deserialize, Serialize)] -pub struct AuraConfig { - pub brightness: LedBrightness, - pub current_mode: AuraModeNum, - pub builtins: BTreeMap, - pub multizone: Option, - pub awake_enabled: bool, - pub sleep_anim_enabled: bool, -} - -impl Default for AuraConfig { - fn default() -> Self { - AuraConfig { - brightness: LedBrightness::Med, - current_mode: AuraModeNum::Static, - builtins: BTreeMap::new(), - multizone: None, - awake_enabled: true, - sleep_anim_enabled: true, - } - } -} - -impl AuraConfig { - /// `load` will attempt to read the config, and panic if the dir is missing - pub fn load(supported_led_modes: &LaptopLedData) -> Self { - let mut file = OpenOptions::new() - .read(true) - .write(true) - .create(true) - .open(&AURA_CONFIG_PATH) - .unwrap_or_else(|_| { - panic!( - "The file {} or directory /etc/asusd/ is missing", - AURA_CONFIG_PATH - ) - }); // okay to cause panic here - let mut buf = String::new(); - if let Ok(read_len) = file.read_to_string(&mut buf) { - if read_len == 0 { - return AuraConfig::create_default(&mut file, supported_led_modes); - } else { - if let Ok(data) = serde_json::from_str(&buf) { - return data; - } else if let Ok(data) = serde_json::from_str::(&buf) { - let config = data.into_current(); - config.write(); - info!("Updated AuraConfig version"); - return config; - } else if let Ok(data) = serde_json::from_str::(&buf) { - let config = data.into_current(); - config.write(); - info!("Updated AuraConfig version"); - return config; - } - warn!("Could not deserialise {}", AURA_CONFIG_PATH); - panic!("Please remove {} then restart asusd", AURA_CONFIG_PATH); - } - } - AuraConfig::create_default(&mut file, supported_led_modes) - } - - fn create_default(file: &mut File, support_data: &LaptopLedData) -> Self { - // create a default config here - let mut config = AuraConfig::default(); - - for n in &support_data.standard { - config - .builtins - .insert(*n, AuraEffect::default_with_mode(*n)); - } - - // Should be okay to unwrap this as is since it is a Default - let json = serde_json::to_string(&config).unwrap(); - file.write_all(json.as_bytes()) - .unwrap_or_else(|_| panic!("Could not write {}", AURA_CONFIG_PATH)); - config - } - - pub fn read(&mut self) { - let mut file = OpenOptions::new() - .read(true) - .open(&AURA_CONFIG_PATH) - .unwrap_or_else(|err| panic!("Error reading {}: {}", AURA_CONFIG_PATH, err)); - let mut buf = String::new(); - if let Ok(l) = file.read_to_string(&mut buf) { - if l == 0 { - warn!("File is empty {}", AURA_CONFIG_PATH); - } else { - let x: AuraConfig = serde_json::from_str(&buf) - .unwrap_or_else(|_| panic!("Could not deserialise {}", AURA_CONFIG_PATH)); - *self = x; - } - } - } - - pub fn write(&self) { - let mut file = File::create(AURA_CONFIG_PATH).expect("Couldn't overwrite config"); - let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed"); - file.write_all(json.as_bytes()) - .unwrap_or_else(|err| error!("Could not write config: {}", err)); - } - - /// Multipurpose, will accept AuraEffect with zones and put in the correct store - pub fn set_builtin(&mut self, effect: AuraEffect) { - match effect.zone() { - AuraZone::None => { - self.builtins.insert(*effect.mode(), effect); - } - _ => { - if let Some(multi) = self.multizone.as_mut() { - multi.set(effect) - } - } - } - } - - pub fn get_multizone(&self, aura_type: AuraModeNum) -> Option<&[AuraEffect; 4]> { - if let Some(multi) = &self.multizone { - if aura_type == AuraModeNum::Static { - return Some(multi.static_()); - } else if aura_type == AuraModeNum::Breathe { - return Some(multi.breathe()); - } - } - None - } -} - -#[derive(Deserialize, Serialize)] -pub struct AuraMultiZone { - static_: [AuraEffect; 4], - breathe: [AuraEffect; 4], -} - -impl AuraMultiZone { - pub fn set(&mut self, effect: AuraEffect) { - if effect.mode == AuraModeNum::Static { - match effect.zone { - AuraZone::None => {} - AuraZone::One => self.static_[0] = effect, - AuraZone::Two => self.static_[1] = effect, - AuraZone::Three => self.static_[2] = effect, - AuraZone::Four => self.static_[3] = effect, - } - } else if effect.mode == AuraModeNum::Breathe { - match effect.zone { - AuraZone::None => {} - AuraZone::One => self.breathe[0] = effect, - AuraZone::Two => self.breathe[1] = effect, - AuraZone::Three => self.breathe[2] = effect, - AuraZone::Four => self.breathe[3] = effect, - } - } - } - - pub fn static_(&self) -> &[AuraEffect; 4] { - &self.static_ - } - - pub fn breathe(&self) -> &[AuraEffect; 4] { - &self.breathe - } -} - -impl Default for AuraMultiZone { - fn default() -> Self { - Self { - static_: [ - AuraEffect { - mode: AuraModeNum::Static, - zone: AuraZone::One, - ..Default::default() - }, - AuraEffect { - mode: AuraModeNum::Static, - zone: AuraZone::Two, - ..Default::default() - }, - AuraEffect { - mode: AuraModeNum::Static, - zone: AuraZone::Three, - ..Default::default() - }, - AuraEffect { - mode: AuraModeNum::Static, - zone: AuraZone::Four, - ..Default::default() - }, - ], - breathe: [ - AuraEffect { - mode: AuraModeNum::Breathe, - zone: AuraZone::One, - ..Default::default() - }, - AuraEffect { - mode: AuraModeNum::Breathe, - zone: AuraZone::Two, - ..Default::default() - }, - AuraEffect { - mode: AuraModeNum::Breathe, - zone: AuraZone::Three, - ..Default::default() - }, - AuraEffect { - mode: AuraModeNum::Breathe, - zone: AuraZone::Four, - ..Default::default() - }, - ], - } - } -} diff --git a/daemon/src/config_old.rs b/daemon/src/config_old.rs index 1a1fe159..49254b3e 100644 --- a/daemon/src/config_old.rs +++ b/daemon/src/config_old.rs @@ -1,5 +1,3 @@ -use rog_fan_curve::Curve; -use rog_profiles::profiles::Profile; use rog_types::gfx_vendors::GfxVendors; use serde_derive::{Deserialize, Serialize}; use std::collections::BTreeMap; @@ -31,11 +29,7 @@ impl ConfigV317 { gfx_tmp_mode: None, gfx_managed: self.gfx_managed, gfx_vfio_enable: false, - active_profile: self.active_profile, - toggle_profiles: self.toggle_profiles, - curr_fan_mode: self.curr_fan_mode, bat_charge_limit: self.bat_charge_limit, - power_profiles: ProfileV317::transform_map(self.power_profiles), } } } @@ -59,11 +53,7 @@ impl ConfigV324 { gfx_tmp_mode: None, gfx_managed: self.gfx_managed, gfx_vfio_enable: false, - active_profile: self.active_profile, - toggle_profiles: self.toggle_profiles, - curr_fan_mode: self.curr_fan_mode, bat_charge_limit: self.bat_charge_limit, - power_profiles: ProfileV317::transform_map(self.power_profiles), } } } @@ -88,11 +78,7 @@ impl ConfigV341 { gfx_tmp_mode: None, gfx_managed: self.gfx_managed, gfx_vfio_enable: false, - active_profile: self.active_profile, - toggle_profiles: self.toggle_profiles, - curr_fan_mode: self.curr_fan_mode, bat_charge_limit: self.bat_charge_limit, - power_profiles: ProfileV317::transform_map(self.power_profiles), } } } @@ -119,43 +105,32 @@ impl ConfigV352 { gfx_tmp_mode: None, gfx_managed: self.gfx_managed, gfx_vfio_enable: false, - active_profile: self.active_profile, - toggle_profiles: self.toggle_profiles, - curr_fan_mode: self.curr_fan_mode, bat_charge_limit: self.bat_charge_limit, - power_profiles: ProfileV317::transform_map(self.power_profiles), } } } +#[derive(Deserialize, Serialize)] +pub struct ConfigV372 { + pub gfx_mode: GfxVendors, + /// Only for informational purposes. + #[serde(skip)] + pub gfx_tmp_mode: Option, + pub gfx_managed: bool, + pub gfx_vfio_enable: bool, + pub active_profile: String, + pub toggle_profiles: Vec, + #[serde(skip)] + pub curr_fan_mode: u8, + pub bat_charge_limit: u8, + pub power_profiles: BTreeMap, +} + #[derive(Debug, Clone, Deserialize, Serialize)] pub struct ProfileV317 { pub min_percentage: u8, pub max_percentage: u8, pub turbo: bool, pub fan_preset: u8, - pub fan_curve: Option, -} - -impl ProfileV317 { - fn into_current(self, name: String) -> Profile { - Profile { - name, - min_percentage: self.min_percentage, - max_percentage: self.max_percentage, - turbo: self.turbo, - fan_preset: self.fan_preset.into(), - fan_curve: self - .fan_curve - .map_or_else(|| "".to_string(), |c| c.as_config_string()), - } - } - - fn transform_map(map: BTreeMap) -> BTreeMap { - let mut new_map = BTreeMap::new(); - map.iter().for_each(|(k, v)| { - new_map.insert(k.to_string(), v.clone().into_current(k.to_string())); - }); - new_map - } + pub fan_curve: Option<()>, } diff --git a/daemon/src/ctrl_anime.rs b/daemon/src/ctrl_anime.rs deleted file mode 100644 index e1400f2e..00000000 --- a/daemon/src/ctrl_anime.rs +++ /dev/null @@ -1,456 +0,0 @@ -use log::{error, info, warn}; -use logind_zbus::ManagerProxy; -use rog_anime::{ - usb::{ - pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on, pkts_for_init, PROD_ID, - VENDOR_ID, - }, - ActionData, AnimeDataBuffer, AnimePacketType, AnimePowerStates, ANIME_DATA_LEN, -}; -use rog_types::supported::AnimeSupportedFunctions; -use rusb::{Device, DeviceHandle}; -use std::{ - error::Error, - sync::{Arc, Mutex}, - thread::sleep, -}; -use std::{ - sync::atomic::{AtomicBool, Ordering}, - time::Duration, -}; -use zbus::{dbus_interface, Connection}; -use zvariant::ObjectPath; - -use crate::{ - config_anime::{AnimeConfig, AnimeConfigCached}, - error::RogError, - GetSupported, -}; - -impl GetSupported for CtrlAnime { - type A = AnimeSupportedFunctions; - - fn get_supported() -> Self::A { - AnimeSupportedFunctions(CtrlAnime::get_device(VENDOR_ID, PROD_ID).is_ok()) - } -} - -pub struct CtrlAnime { - handle: DeviceHandle, - cache: AnimeConfigCached, - config: AnimeConfig, - // set to force thread to exit - thread_exit: Arc, - // Set to false when the thread exits - thread_running: Arc, -} - -impl CtrlAnime { - #[inline] - pub fn new(config: AnimeConfig) -> Result> { - // We don't expect this ID to ever change - let device = CtrlAnime::get_device(0x0b05, 0x193b)?; - - let mut device = device.open()?; - device.reset()?; - - device.set_auto_detach_kernel_driver(true).map_err(|err| { - error!("Auto-detach kernel driver failed: {}", err); - err - })?; - - device.claim_interface(0).map_err(|err| { - error!("Could not claim device interface: {}", err); - err - })?; - - info!("Device has an AniMe Matrix display"); - let mut cache = AnimeConfigCached::default(); - cache.init_from_config(&config)?; - - let ctrl = CtrlAnime { - handle: device, - cache, - config, - thread_exit: Arc::new(AtomicBool::new(false)), - thread_running: Arc::new(AtomicBool::new(false)), - }; - ctrl.do_initialization(); - - Ok(ctrl) - } - - fn get_device(vendor: u16, product: u16) -> Result, rusb::Error> { - for device in rusb::devices()?.iter() { - let device_desc = device.device_descriptor()?; - if device_desc.vendor_id() == vendor && device_desc.product_id() == product { - return Ok(device); - } - } - Err(rusb::Error::NoDevice) - } - - /// Start an action thread. This is classed as a singleton and there should be only - /// one running - so the thread uses atomics to signal run/exit. - /// - /// Because this also writes to the usb device, other write tries (display only) *must* - /// get the mutex lock and set the thread_exit atomic. - fn run_thread(inner: Arc>, actions: Vec, mut once: bool) { - if actions.is_empty() { - warn!("AniMe system actions was empty"); - return; - } - // Loop rules: - // - Lock the mutex **only when required**. That is, the lock must be held for the shortest duration possible. - // - An AtomicBool used for thread exit should be checked in every loop, including nested - - // The only reason for this outer thread is to prevent blocking while waiting for the - // next spawned thread to exit - std::thread::Builder::new() - .name("AniMe system thread start".into()) - .spawn(move || { - info!("AniMe system thread started"); - // Getting copies of these Atomics is done *in* the thread to ensure - // we don't block other threads/main - let thread_exit; - let thread_running; - // First two loops are to ensure we *do* aquire a lock on the mutex - // The reason the loop is required is because the USB writes can block - // for up to 10ms. We can't fail to get the atomics. - loop { - if let Ok(lock) = inner.try_lock() { - thread_exit = lock.thread_exit.clone(); - thread_running = lock.thread_running.clone(); - // Make any running loop exit first - thread_exit.store(true, Ordering::SeqCst); - break; - } - } - - loop { - // wait for other threads to set not running so we know they exited - if !thread_running.load(Ordering::SeqCst) { - thread_exit.store(false, Ordering::SeqCst); - info!("AniMe forced a thread to exit"); - break; - } - } - - 'main: loop { - if thread_exit.load(Ordering::SeqCst) { - break 'main; - } - for action in actions.iter() { - match action { - ActionData::Animation(frames) => { - rog_anime::run_animation(frames, thread_exit.clone(), &|frame| { - if let Ok(lock) = inner.try_lock() { - lock.write_data_buffer(frame); - } - }) - .unwrap(); - - if thread_exit.load(Ordering::SeqCst) { - break 'main; - } - } - ActionData::Image(image) => { - once = false; - if let Ok(lock) = inner.try_lock() { - lock.write_data_buffer(image.as_ref().clone()) - } - } - ActionData::Pause(duration) => sleep(*duration), - ActionData::AudioEq => {} - ActionData::SystemInfo => {} - ActionData::TimeDate => {} - ActionData::Matrix => {} - } - } - if once || actions.is_empty() { - break 'main; - } - } - // Clear the display on exit - if let Ok(lock) = inner.try_lock() { - let data = AnimeDataBuffer::from_vec([0u8; ANIME_DATA_LEN].to_vec()); - lock.write_data_buffer(data); - } - // Loop ended, set the atmonics - thread_exit.store(false, Ordering::SeqCst); - thread_running.store(false, Ordering::SeqCst); - info!("AniMe system thread exited"); - }) - .map(|err| info!("AniMe system thread: {:?}", err)) - .ok(); - } - - fn write_bytes(&self, message: &[u8]) { - match self.handle.write_control( - 0x21, // request_type - 0x09, // request - 0x35e, // value - 0x00, // index - message, - Duration::from_millis(200), - ) { - Ok(_) => {} - Err(err) => match err { - rusb::Error::Timeout => {} - _ => error!("Failed to write to led interrupt: {}", err), - }, - } - } - - /// Write only a data packet. This will modify the leds brightness using the - /// global brightness set in config. - fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) { - for led in buffer.get_mut()[7..].iter_mut() { - let mut bright = *led as f32 * self.config.brightness; - if bright > 254.0 { - bright = 254.0; - } - *led = bright as u8; - } - let data = AnimePacketType::from(buffer); - for row in data.iter() { - self.write_bytes(row); - } - self.write_bytes(&pkt_for_flush()); - } - - fn do_initialization(&self) { - let pkts = pkts_for_init(); - self.write_bytes(&pkts[0]); - self.write_bytes(&pkts[1]); - } -} - -pub struct CtrlAnimeTask<'a> { - inner: Arc>, - _c: Connection, - manager: ManagerProxy<'a>, -} - -impl<'a> CtrlAnimeTask<'a> { - pub fn new(inner: Arc>) -> Self { - let connection = Connection::new_system().unwrap(); - - let manager = ManagerProxy::new(&connection).unwrap(); - - let c1 = inner.clone(); - // Run this action when the system starts shutting down - manager - .connect_prepare_for_shutdown(move |shutdown| { - if shutdown { - 'outer: loop { - if let Ok(lock) = c1.try_lock() { - lock.thread_exit.store(true, Ordering::SeqCst); - CtrlAnime::run_thread(c1.clone(), lock.cache.shutdown.clone(), false); - break 'outer; - } - } - } - Ok(()) - }) - .map_err(|err| { - warn!("CtrlAnimeTask: new() {}", err); - err - }) - .ok(); - - let c1 = inner.clone(); - // Run this action when the system wakes up from sleep - manager - .connect_prepare_for_sleep(move |sleep| { - if !sleep { - // wait a fraction for things to wake up properly - std::thread::sleep(Duration::from_millis(100)); - 'outer: loop { - if let Ok(lock) = c1.try_lock() { - lock.thread_exit.store(true, Ordering::SeqCst); - CtrlAnime::run_thread(c1.clone(), lock.cache.wake.clone(), true); - break 'outer; - } - } - } - Ok(()) - }) - .map_err(|err| { - warn!("CtrlAnimeTask: new() {}", err); - err - }) - .ok(); - - Self { - inner, - _c: connection, - manager, - } - } -} - -impl<'a> crate::CtrlTask for CtrlAnimeTask<'a> { - fn do_task(&self) -> Result<(), RogError> { - if let Ok(mut lock) = self.inner.try_lock() { - // Refresh the config and cache incase the user has edited it - let config = AnimeConfig::load(); - lock.cache - .init_from_config(&config) - .map_err(|err| { - warn!("CtrlAnimeTask: do_task {}", err); - err - }) - .ok(); - } - - // Check for signals on each task iteration, this will run the callbacks - // if any signal is recieved - self.manager.next_signal()?; - Ok(()) - } -} - -pub struct CtrlAnimeReloader(pub Arc>); - -impl crate::Reloadable for CtrlAnimeReloader { - fn reload(&mut self) -> Result<(), RogError> { - if let Ok(lock) = self.0.try_lock() { - lock.write_bytes(&pkt_for_set_on(lock.config.awake_enabled)); - lock.write_bytes(&pkt_for_apply()); - lock.write_bytes(&pkt_for_set_boot(lock.config.boot_anim_enabled)); - lock.write_bytes(&pkt_for_apply()); - - let action = lock.cache.boot.clone(); - CtrlAnime::run_thread(self.0.clone(), action, true); - } - Ok(()) - } -} - -pub struct CtrlAnimeZbus(pub Arc>); - -/// The struct with the main dbus methods requires this trait -impl crate::ZbusAdd for CtrlAnimeZbus { - fn add_to_server(self, server: &mut zbus::ObjectServer) { - server - .at( - &ObjectPath::from_str_unchecked("/org/asuslinux/Anime"), - self, - ) - .map_err(|err| { - warn!("CtrlAnimeDisplay: add_to_server {}", err); - err - }) - .ok(); - } -} - -// None of these calls can be guarnateed to succeed unless we loop until okay -// If the try_lock *does* succeed then any other thread trying to lock will not grab it -// until we finish. -#[dbus_interface(name = "org.asuslinux.Daemon")] -impl CtrlAnimeZbus { - /// Writes a data stream of length. Will force system thread to exit until it is restarted - fn write(&self, input: AnimeDataBuffer) { - 'outer: loop { - if let Ok(lock) = self.0.try_lock() { - lock.thread_exit.store(true, Ordering::SeqCst); - lock.write_data_buffer(input); - break 'outer; - } - } - } - - /// Set the global AniMe brightness - fn set_brightness(&self, bright: f32) { - 'outer: loop { - if let Ok(mut lock) = self.0.try_lock() { - let mut bright = bright; - if bright < 0.0 { - bright = 0.0 - } else if bright > 254.0 { - bright = 254.0; - } - lock.config.brightness = bright; - lock.config.write(); - break 'outer; - } - } - } - - /// Set whether the AniMe is displaying images/data - fn set_on_off(&self, status: bool) { - 'outer: loop { - if let Ok(mut lock) = self.0.try_lock() { - lock.write_bytes(&pkt_for_set_on(status)); - lock.config.awake_enabled = status; - lock.config.write(); - - let states = AnimePowerStates { - enabled: lock.config.awake_enabled, - boot_anim_enabled: lock.config.boot_anim_enabled, - }; - self.notify_power_states(&states) - .unwrap_or_else(|err| warn!("{}", err)); - break 'outer; - } - } - } - - /// Set whether the AniMe will show boot, suspend, or off animations - fn set_boot_on_off(&self, on: bool) { - 'outer: loop { - if let Ok(mut lock) = self.0.try_lock() { - lock.write_bytes(&pkt_for_set_boot(on)); - lock.write_bytes(&pkt_for_apply()); - lock.config.boot_anim_enabled = on; - lock.config.write(); - - let states = AnimePowerStates { - enabled: lock.config.awake_enabled, - boot_anim_enabled: lock.config.boot_anim_enabled, - }; - self.notify_power_states(&states) - .unwrap_or_else(|err| warn!("{}", err)); - break 'outer; - } - } - } - - /// The main loop is the base system set action if the user isn't running - /// the user daemon - fn run_main_loop(&self, start: bool) { - if start { - 'outer: loop { - if let Ok(lock) = self.0.try_lock() { - lock.thread_exit.store(true, Ordering::SeqCst); - CtrlAnime::run_thread(self.0.clone(), lock.cache.system.clone(), false); - break 'outer; - } - } - } - } - - /// Get status of if the AniMe LEDs are on - #[dbus_interface(property)] - fn awake_enabled(&self) -> bool { - if let Ok(ctrl) = self.0.try_lock() { - return ctrl.config.awake_enabled; - } - true - } - - /// Get the status of if factory system-status animations are enabled - #[dbus_interface(property)] - fn boot_enabled(&self) -> bool { - if let Ok(ctrl) = self.0.try_lock() { - return ctrl.config.boot_anim_enabled; - } - true - } - - /// Notify listeners of the status of AniMe LED power and factory system-status animations - #[dbus_interface(signal)] - fn notify_power_states(&self, data: &AnimePowerStates) -> zbus::Result<()>; -} diff --git a/daemon/src/ctrl_gfx/controller.rs b/daemon/src/ctrl_gfx/controller.rs index 6a2f23b2..9fa83233 100644 --- a/daemon/src/ctrl_gfx/controller.rs +++ b/daemon/src/ctrl_gfx/controller.rs @@ -264,13 +264,16 @@ impl CtrlGraphics { let unbinds = devices.iter().map(|dev| dev.unbind()); // Remove NVIDIA graphics devices and their functions let removes = devices.iter().map(|dev| dev.remove()); - unbinds.chain(removes).collect::>() + unbinds + .chain(removes) + .collect::>() .map_err(|err| RogError::Command("device unbind error".into(), err)) } fn unbind_only(devices: &[GraphicsDevice]) -> Result<(), RogError> { let unbinds = devices.iter().map(|dev| dev.unbind()); - unbinds.collect::>() + unbinds + .collect::>() .map_err(|err| RogError::Command("device unbind error".into(), err)) } diff --git a/daemon/src/ctrl_gfx/mod.rs b/daemon/src/ctrl_gfx/mod.rs index 2a2e997b..183fc2a9 100644 --- a/daemon/src/ctrl_gfx/mod.rs +++ b/daemon/src/ctrl_gfx/mod.rs @@ -4,7 +4,7 @@ pub mod controller; pub mod system; -pub mod zbus_gfx; +pub mod zbus; const NVIDIA_DRIVERS: [&str; 4] = ["nvidia_drm", "nvidia_modeset", "nvidia_uvm", "nvidia"]; diff --git a/daemon/src/ctrl_gfx/zbus_gfx.rs b/daemon/src/ctrl_gfx/zbus_gfx.rs deleted file mode 100644 index 8c66ac1d..00000000 --- a/daemon/src/ctrl_gfx/zbus_gfx.rs +++ /dev/null @@ -1,56 +0,0 @@ -use ::zbus::dbus_interface; -use log::{error, info, warn}; -use rog_types::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}; -use zvariant::ObjectPath; - -use crate::ZbusAdd; - -use super::controller::CtrlGraphics; - -#[dbus_interface(name = "org.asuslinux.Daemon")] -impl CtrlGraphics { - fn vendor(&self) -> zbus::fdo::Result { - self.get_gfx_mode().map_err(|err| { - error!("GFX: {}", err); - zbus::fdo::Error::Failed(format!("GFX fail: {}", err)) - }) - } - - fn power(&self) -> zbus::fdo::Result { - Self::get_runtime_status().map_err(|err| { - error!("GFX: {}", err); - zbus::fdo::Error::Failed(format!("GFX fail: {}", err)) - }) - } - - fn set_vendor(&mut self, vendor: GfxVendors) -> zbus::fdo::Result { - info!("GFX: Switching gfx mode to {}", <&str>::from(vendor)); - let msg = self.set_gfx_mode(vendor).map_err(|err| { - error!("GFX: {}", err); - zbus::fdo::Error::Failed(format!("GFX fail: {}", err)) - })?; - self.notify_gfx(&vendor) - .unwrap_or_else(|err| warn!("GFX: {}", err)); - self.notify_action(&msg) - .unwrap_or_else(|err| warn!("GFX: {}", err)); - Ok(msg) - } - - #[dbus_interface(signal)] - fn notify_gfx(&self, vendor: &GfxVendors) -> zbus::Result<()> {} - - #[dbus_interface(signal)] - fn notify_action(&self, action: &GfxRequiredUserAction) -> zbus::Result<()> {} -} - -impl ZbusAdd for CtrlGraphics { - fn add_to_server(self, server: &mut zbus::ObjectServer) { - server - .at(&ObjectPath::from_str_unchecked("/org/asuslinux/Gfx"), self) - .map_err(|err| { - warn!("GFX: CtrlGraphics: add_to_server {}", err); - err - }) - .ok(); - } -} diff --git a/daemon/src/ctrl_leds/controller.rs b/daemon/src/ctrl_leds/controller.rs deleted file mode 100644 index d9e0a64f..00000000 --- a/daemon/src/ctrl_leds/controller.rs +++ /dev/null @@ -1,394 +0,0 @@ -// Only these two packets must be 17 bytes -static KBD_BRIGHT_PATH: &str = "/sys/class/leds/asus::kbd_backlight/brightness"; - -use crate::{ - config_aura::AuraConfig, - error::RogError, - laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES}, - CtrlTask, -}; -use log::{info, warn}; -use logind_zbus::ManagerProxy; -use rog_aura::{ - usb::{ - LED_APPLY, LED_AWAKE_OFF_SLEEP_OFF, LED_AWAKE_OFF_SLEEP_ON, LED_AWAKE_ON_SLEEP_OFF, - LED_AWAKE_ON_SLEEP_ON, LED_SET, - }, - AuraEffect, LedBrightness, LED_MSG_LEN, -}; -use rog_types::supported::LedSupportedFunctions; -use std::io::{Read, Write}; -use std::path::Path; -use std::sync::Arc; -use std::sync::Mutex; -use std::{fs::OpenOptions, thread::spawn}; -use zbus::Connection; - -use crate::GetSupported; - -impl GetSupported for CtrlKbdLed { - type A = LedSupportedFunctions; - - fn get_supported() -> Self::A { - // let mode = <&str>::from(&::from(*mode)); - let multizone_led_mode = false; - let per_key_led_mode = false; - let laptop = LaptopLedData::get_data(); - let stock_led_modes = laptop.standard; - - LedSupportedFunctions { - brightness_set: CtrlKbdLed::get_kbd_bright_path().is_some(), - stock_led_modes, - multizone_led_mode, - per_key_led_mode, - } - } -} - -pub struct CtrlKbdLed { - pub led_node: Option, - pub bright_node: String, - pub supported_modes: LaptopLedData, - pub flip_effect_write: bool, - pub config: AuraConfig, -} - -pub struct CtrlKbdLedTask<'a> { - inner: Arc>, - _c: Connection, - manager: ManagerProxy<'a>, -} - -impl<'a> CtrlKbdLedTask<'a> { - pub fn new(inner: Arc>) -> Self { - let connection = Connection::new_system().unwrap(); - - let manager = ManagerProxy::new(&connection).unwrap(); - - let c1 = inner.clone(); - // Run this action when the system wakes up from sleep - manager - .connect_prepare_for_sleep(move |sleep| { - if !sleep { - let c1 = c1.clone(); - spawn(move || { - // wait a fraction for things to wake up properly - //std::thread::sleep(Duration::from_millis(100)); - loop { - if let Ok(ref mut lock) = c1.try_lock() { - lock.set_brightness(lock.config.brightness).ok(); - break; - } - } - }); - } - Ok(()) - }) - .map_err(|err| { - warn!("CtrlAnimeTask: new() {}", err); - err - }) - .ok(); - - Self { - inner, - _c: connection, - manager, - } - } - - fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> { - let mut file = OpenOptions::new() - .read(true) - .open(&lock.bright_node) - .map_err(|err| match err.kind() { - std::io::ErrorKind::NotFound => { - RogError::MissingLedBrightNode((&lock.bright_node).into(), err) - } - _ => RogError::Path((&lock.bright_node).into(), err), - })?; - let mut buf = [0u8; 1]; - file.read_exact(&mut buf) - .map_err(|err| RogError::Read("buffer".into(), err))?; - if let Some(num) = char::from(buf[0]).to_digit(10) { - if lock.config.brightness != num.into() { - lock.config.read(); - lock.config.brightness = num.into(); - lock.config.write(); - } - return Ok(()); - } - Err(RogError::ParseLed) - } -} - -impl<'a> CtrlTask for CtrlKbdLedTask<'a> { - fn do_task(&self) -> Result<(), RogError> { - self.manager.next_signal()?; - if let Ok(ref mut lock) = self.inner.try_lock() { - return Self::update_config(lock); - } - Ok(()) - } -} - -pub struct CtrlKbdLedReloader(pub Arc>); - -impl crate::Reloadable for CtrlKbdLedReloader { - fn reload(&mut self) -> Result<(), RogError> { - if let Ok(mut ctrl) = self.0.try_lock() { - let current = ctrl.config.current_mode; - if let Some(mode) = ctrl.config.builtins.get(¤t).cloned() { - ctrl.do_command(mode).ok(); - } - - ctrl.set_states_enabled(ctrl.config.awake_enabled, ctrl.config.sleep_anim_enabled) - .map_err(|err| warn!("{}", err)) - .ok(); - } - Ok(()) - } -} - -pub struct CtrlKbdLedZbus(pub Arc>); - -impl CtrlKbdLedZbus { - pub fn new(inner: Arc>) -> Self { - Self(inner) - } -} - -impl CtrlKbdLed { - #[inline] - pub fn new(supported_modes: LaptopLedData, config: AuraConfig) -> Result { - // TODO: return error if *all* nodes are None - let mut led_node = None; - for prod in ASUS_KEYBOARD_DEVICES.iter() { - match Self::find_led_node(prod) { - Ok(node) => { - led_node = Some(node); - break; - } - Err(err) => warn!("led_node: {}", err), - } - } - - let bright_node = Self::get_kbd_bright_path(); - - if led_node.is_none() && bright_node.is_none() { - return Err(RogError::MissingFunction( - "All keyboard features missing, you may require a v5.11 series kernel or newer" - .into(), - )); - } - - if bright_node.is_none() { - return Err(RogError::MissingFunction( - "No brightness control, you may require a v5.11 series kernel or newer".into(), - )); - } - - let ctrl = CtrlKbdLed { - led_node, - bright_node: bright_node.unwrap(), // If was none then we already returned above - supported_modes, - flip_effect_write: false, - config, - }; - Ok(ctrl) - } - - fn get_kbd_bright_path() -> Option { - if Path::new(KBD_BRIGHT_PATH).exists() { - return Some(KBD_BRIGHT_PATH.to_string()); - } - None - } - - pub(super) fn get_brightness(&self) -> Result { - let mut file = OpenOptions::new() - .read(true) - .open(&self.bright_node) - .map_err(|err| match err.kind() { - std::io::ErrorKind::NotFound => { - RogError::MissingLedBrightNode((&self.bright_node).into(), err) - } - _ => RogError::Path((&self.bright_node).into(), err), - })?; - let mut buf = [0u8; 1]; - file.read_exact(&mut buf) - .map_err(|err| RogError::Read("buffer".into(), err))?; - Ok(buf[0]) - } - - pub(super) fn set_brightness(&self, brightness: LedBrightness) -> Result<(), RogError> { - let path = Path::new(&self.bright_node); - let mut file = - OpenOptions::new() - .write(true) - .open(&path) - .map_err(|err| match err.kind() { - std::io::ErrorKind::NotFound => { - RogError::MissingLedBrightNode((&self.bright_node).into(), err) - } - _ => RogError::Path((&self.bright_node).into(), err), - })?; - file.write_all(&[brightness.as_char_code()]) - .map_err(|err| RogError::Read("buffer".into(), err))?; - Ok(()) - } - - /// Set if awake/on LED active, and/or sleep animation active - pub(super) fn set_states_enabled(&self, awake: bool, sleep: bool) -> Result<(), RogError> { - let bytes = if awake && sleep { - LED_AWAKE_ON_SLEEP_ON - } else if awake && !sleep { - LED_AWAKE_ON_SLEEP_OFF - } else if !awake && sleep { - LED_AWAKE_OFF_SLEEP_ON - } else if !awake && !sleep { - LED_AWAKE_OFF_SLEEP_OFF - } else { - LED_AWAKE_ON_SLEEP_ON - }; - self.write_bytes(&bytes)?; - self.write_bytes(&LED_SET)?; - // Changes won't persist unless apply is set - self.write_bytes(&LED_APPLY)?; - Ok(()) - } - - fn find_led_node(id_product: &str) -> Result { - let mut enumerator = udev::Enumerator::new().map_err(|err| { - warn!("{}", err); - RogError::Udev("enumerator failed".into(), err) - })?; - enumerator.match_subsystem("hidraw").map_err(|err| { - warn!("{}", err); - RogError::Udev("match_subsystem failed".into(), err) - })?; - - for device in enumerator.scan_devices().map_err(|err| { - warn!("{}", err); - RogError::Udev("scan_devices failed".into(), err) - })? { - if let Some(parent) = device - .parent_with_subsystem_devtype("usb", "usb_device") - .map_err(|err| { - warn!("{}", err); - RogError::Udev("parent_with_subsystem_devtype failed".into(), err) - })? - { - if parent - .attribute_value("idProduct") - .ok_or_else(|| RogError::NotFound("LED idProduct".into()))? - == id_product - { - if let Some(dev_node) = device.devnode() { - info!("Using device at: {:?} for LED control", dev_node); - return Ok(dev_node.to_string_lossy().to_string()); - } - } - } - } - Err(RogError::MissingFunction( - "ASUS LED device node not found".into(), - )) - } - - pub(crate) fn do_command(&mut self, mode: AuraEffect) -> Result<(), RogError> { - self.set_and_save(mode) - } - - /// Should only be used if the bytes you are writing are verified correct - #[inline] - fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> { - if let Some(led_node) = &self.led_node { - if let Ok(mut file) = OpenOptions::new().write(true).open(led_node) { - // println!("write: {:02x?}", &message); - return file - .write_all(message) - .map_err(|err| RogError::Write("write_bytes".into(), err)); - } - } - Err(RogError::NotSupported) - } - - /// Write an effect block - #[inline] - fn _write_effect(&mut self, effect: &[Vec]) -> Result<(), RogError> { - if self.flip_effect_write { - for row in effect.iter().rev() { - self.write_bytes(row)?; - } - } else { - for row in effect.iter() { - self.write_bytes(row)?; - } - } - self.flip_effect_write = !self.flip_effect_write; - Ok(()) - } - - /// Used to set a builtin mode and save the settings for it - /// - /// This needs to be universal so that settings applied by dbus stick - #[inline] - fn set_and_save(&mut self, mode: AuraEffect) -> Result<(), RogError> { - self.config.read(); - self.write_mode(&mode)?; - self.config.current_mode = *mode.mode(); - self.config.set_builtin(mode); - self.config.write(); - Ok(()) - } - - #[inline] - pub(super) fn toggle_mode(&mut self, reverse: bool) -> Result<(), RogError> { - let current = self.config.current_mode; - if let Some(idx) = self - .supported_modes - .standard - .iter() - .position(|v| *v == current) - { - let mut idx = idx; - // goes past end of array - if reverse { - if idx == 0 { - idx = self.supported_modes.standard.len() - 1; - } else { - idx -= 1; - } - } else { - idx += 1; - if idx == self.supported_modes.standard.len() { - idx = 0; - } - } - let next = self.supported_modes.standard[idx]; - - self.config.read(); - if let Some(data) = self.config.builtins.get(&next) { - self.write_mode(data)?; - self.config.current_mode = next; - } - self.config.write(); - } - - Ok(()) - } - - #[inline] - fn write_mode(&self, mode: &AuraEffect) -> Result<(), RogError> { - if !self.supported_modes.standard.contains(mode.mode()) { - return Err(RogError::NotSupported); - } - let bytes: [u8; LED_MSG_LEN] = mode.into(); - self.write_bytes(&bytes)?; - self.write_bytes(&LED_SET)?; - // Changes won't persist unless apply is set - self.write_bytes(&LED_APPLY)?; - Ok(()) - } -} diff --git a/daemon/src/ctrl_leds/mod.rs b/daemon/src/ctrl_leds/mod.rs deleted file mode 100644 index 65359c9f..00000000 --- a/daemon/src/ctrl_leds/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod controller; -pub mod zbus; diff --git a/daemon/src/ctrl_leds/zbus.rs b/daemon/src/ctrl_leds/zbus.rs deleted file mode 100644 index d96fa095..00000000 --- a/daemon/src/ctrl_leds/zbus.rs +++ /dev/null @@ -1,165 +0,0 @@ -use log::{error, warn}; -use rog_aura::{AuraEffect, LedBrightness, LedPowerStates}; -use zbus::dbus_interface; -use zvariant::ObjectPath; - -use super::controller::CtrlKbdLedZbus; - -impl crate::ZbusAdd for CtrlKbdLedZbus { - fn add_to_server(self, server: &mut zbus::ObjectServer) { - server - .at(&ObjectPath::from_str_unchecked("/org/asuslinux/Led"), self) - .map_err(|err| { - error!("DbusKbdLed: add_to_server {}", err); - }) - .ok(); - } -} - -/// The main interface for changing, reading, or notfying signals -/// -/// LED commands are split between Brightness, Modes, Per-Key -#[dbus_interface(name = "org.asuslinux.Daemon")] -impl CtrlKbdLedZbus { - /// Set the keyboard brightness level (0-3) - fn set_brightness(&mut self, brightness: LedBrightness) { - if let Ok(ctrl) = self.0.try_lock() { - ctrl.set_brightness(brightness) - .map_err(|err| warn!("{}", err)) - .ok(); - } - } - - /// Set the keyboard LED to enabled while the device is awake - fn set_awake_enabled(&mut self, enabled: bool) { - if let Ok(mut ctrl) = self.0.try_lock() { - ctrl.set_states_enabled(enabled, ctrl.config.sleep_anim_enabled) - .map_err(|err| warn!("{}", err)) - .ok(); - ctrl.config.awake_enabled = enabled; - ctrl.config.write(); - - let states = LedPowerStates { - enabled: ctrl.config.awake_enabled, - sleep_anim_enabled: ctrl.config.sleep_anim_enabled, - }; - self.notify_power_states(&states) - .unwrap_or_else(|err| warn!("{}", err)); - } - } - - /// Set the keyboard LED suspend animation to enabled while the device is suspended - fn set_sleep_enabled(&mut self, enabled: bool) { - if let Ok(mut ctrl) = self.0.try_lock() { - ctrl.set_states_enabled(ctrl.config.awake_enabled, enabled) - .map_err(|err| warn!("{}", err)) - .ok(); - ctrl.config.sleep_anim_enabled = enabled; - ctrl.config.write(); - let states = LedPowerStates { - enabled: ctrl.config.awake_enabled, - sleep_anim_enabled: ctrl.config.sleep_anim_enabled, - }; - self.notify_power_states(&states) - .unwrap_or_else(|err| warn!("{}", err)); - } - } - - fn set_led_mode(&mut self, effect: AuraEffect) { - if let Ok(mut ctrl) = self.0.try_lock() { - match ctrl.do_command(effect) { - Ok(_) => { - if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { - self.notify_led(mode.clone()) - .unwrap_or_else(|err| warn!("{}", err)); - } - } - Err(err) => { - warn!("{}", err); - } - } - } - } - - fn next_led_mode(&self) { - if let Ok(mut ctrl) = self.0.try_lock() { - ctrl.toggle_mode(false) - .unwrap_or_else(|err| warn!("{}", err)); - - if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { - self.notify_led(mode.clone()) - .unwrap_or_else(|err| warn!("{}", err)); - } - } - } - - fn prev_led_mode(&self) { - if let Ok(mut ctrl) = self.0.try_lock() { - ctrl.toggle_mode(true) - .unwrap_or_else(|err| warn!("{}", err)); - - if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { - self.notify_led(mode.clone()) - .unwrap_or_else(|err| warn!("{}", err)); - } - } - } - - #[dbus_interface(property)] - fn awake_enabled(&self) -> bool { - if let Ok(ctrl) = self.0.try_lock() { - return ctrl.config.awake_enabled; - } - true - } - - #[dbus_interface(property)] - fn sleep_enabled(&self) -> bool { - if let Ok(ctrl) = self.0.try_lock() { - return ctrl.config.sleep_anim_enabled; - } - true - } - - /// Return the current mode data - #[dbus_interface(property)] - fn led_mode(&self) -> String { - if let Ok(ctrl) = self.0.try_lock() { - if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { - if let Ok(json) = serde_json::to_string(&mode) { - return json; - } - } - } - warn!("SetKeyBacklight could not deserialise"); - "SetKeyBacklight could not deserialise".to_string() - } - - /// Return a list of available modes - #[dbus_interface(property)] - fn led_modes(&self) -> String { - if let Ok(ctrl) = self.0.try_lock() { - if let Ok(json) = serde_json::to_string(&ctrl.config.builtins) { - return json; - } - } - warn!("SetKeyBacklight could not deserialise"); - "SetKeyBacklight could not serialise".to_string() - } - - /// Return the current LED brightness - #[dbus_interface(property)] - fn led_brightness(&self) -> i8 { - if let Ok(ctrl) = self.0.try_lock() { - return ctrl.get_brightness().map(|n| n as i8).unwrap_or(-1); - } - warn!("SetKeyBacklight could not serialise"); - -1 - } - - #[dbus_interface(signal)] - fn notify_led(&self, data: AuraEffect) -> zbus::Result<()>; - - #[dbus_interface(signal)] - fn notify_power_states(&self, data: &LedPowerStates) -> zbus::Result<()>; -} diff --git a/daemon/src/ctrl_profiles/controller.rs b/daemon/src/ctrl_profiles/controller.rs index e6cd3791..81f06945 100644 --- a/daemon/src/ctrl_profiles/controller.rs +++ b/daemon/src/ctrl_profiles/controller.rs @@ -1,116 +1,114 @@ use crate::error::RogError; -use crate::{config::Config, GetSupported}; +use crate::GetSupported; use log::{info, warn}; -use rog_profiles::profiles::Profile; -use rog_types::supported::FanCpuSupportedFunctions; +use rog_profiles::error::ProfileError; +use rog_profiles::{FanCurves, Profile}; +use rog_types::supported::PlatformProfileFunctions; use std::sync::Arc; use std::sync::Mutex; -pub struct CtrlFanAndCpu { - pub config: Arc>, +use super::config::ProfileConfig; + +pub struct CtrlPlatformTask { + config: Arc>, } -impl GetSupported for CtrlFanAndCpu { - type A = FanCpuSupportedFunctions; +impl CtrlPlatformTask { + pub fn new(config: Arc>) -> Self { + Self { config } + } +} + +impl crate::CtrlTask for CtrlPlatformTask { + fn do_task(&self) -> Result<(), RogError> { + if let Ok(mut lock) = self.config.try_lock() { + // Refresh the config in-case the user has edited it + if let Some(curves) = &mut lock.fan_curves { + curves.update_from_platform(); + } + } + Ok(()) + } +} + +pub struct CtrlPlatformProfile { + pub config: Arc>, +} + +impl GetSupported for CtrlPlatformProfile { + type A = PlatformProfileFunctions; fn get_supported() -> Self::A { - FanCpuSupportedFunctions { - stock_fan_modes: Profile::get_fan_path().is_ok(), - min_max_freq: Profile::get_intel_supported(), - fan_curve_set: rog_fan_curve::Board::from_board_name().is_some(), + if !Profile::is_platform_profile_supported() { + warn!(r#" +platform_profile kernel interface not found, your laptop does not support this, or the iterface is missing. +To enable profile support you require a kernel with the following patch applied: +https://lkml.org/lkml/2021/8/18/1022 +"#); + } + if !FanCurves::is_fan_curves_supported() { + info!(r#" +fan curves kernel interface not found, your laptop does not support this, or the iterface is missing. +To enable fan-curve support you require a kernel with the following patch applied: +https://lkml.org/lkml/2021/8/20/232 +Please note that as of 24/08/2021 this is not final. +"#); + } + PlatformProfileFunctions { + platform_profile: Profile::is_platform_profile_supported(), + fan_curves: FanCurves::is_fan_curves_supported(), } } } -impl crate::Reloadable for CtrlFanAndCpu { - /// Fetcht he active profile and use that to set all related components up +impl crate::Reloadable for CtrlPlatformProfile { + /// Fetch the active profile and use that to set all related components up fn reload(&mut self) -> Result<(), RogError> { - if let Ok(mut cfg) = self.config.clone().try_lock() { - let active = cfg.active_profile.clone(); - if let Some(existing) = cfg.power_profiles.get_mut(&active) { - existing.set_system_all()?; + if let Ok(cfg) = self.config.clone().try_lock() { + if let Some(curves) = &cfg.fan_curves { + curves.update_platform(); } } Ok(()) } } -impl CtrlFanAndCpu { - pub fn new(config: Arc>) -> Result { - Profile::get_fan_path()?; - info!("Device has fan control available"); - Ok(CtrlFanAndCpu { config }) +impl CtrlPlatformProfile { + pub fn new(config: Arc>) -> Result { + if Profile::is_platform_profile_supported() { + info!("Device has profile control available"); + return Ok(CtrlPlatformProfile { config }); + } + Err(ProfileError::NotSupported.into()) } - /// Toggle to next profile in list - pub(super) fn do_next_profile(&mut self) -> Result<(), RogError> { + pub fn save_config(&self) { + if let Ok(lock) = self.config.lock() { + lock.write(); + } + } + + /// Toggle to next profile in list. This will first read the config, switch, then write out + pub(super) fn set_next_profile(&mut self) -> Result<(), RogError> { if let Ok(mut config) = self.config.clone().try_lock() { // Read first just incase the user has modified the config before calling this config.read(); - let mut toggle_index = config - .toggle_profiles - .binary_search(&config.active_profile) - .unwrap_or(0) - + 1; - if toggle_index >= config.toggle_profiles.len() { - toggle_index = 0; + match config.active { + Profile::Balanced => { + Profile::set_profile(Profile::Performance); + config.active = Profile::Performance; + } + Profile::Performance => { + Profile::set_profile(Profile::Quiet); + config.active = Profile::Quiet; + } + Profile::Quiet => { + Profile::set_profile(Profile::Balanced); + config.active = Profile::Balanced; + } } - let profile = config.toggle_profiles[toggle_index].clone(); - - if let Some(existing) = config.power_profiles.get(&profile) { - existing.set_system_all()?; - config.active_profile = existing.name.clone(); - config.write(); - info!("Profile was changed to: {}", &profile); - } else { - warn!( - "toggle_profile {} does not exist in power_profiles", - &profile - ); - return Err(RogError::MissingProfile(profile.to_string())); - } - } - Ok(()) - } - - pub(super) fn set_active(&mut self, profile: &str) -> Result<(), RogError> { - if let Ok(mut config) = self.config.clone().try_lock() { - // Read first just incase the user has modified the config before calling this - config.read(); - if let Some(existing) = config.power_profiles.get(profile) { - existing.set_system_all()?; - config.active_profile = existing.name.clone(); - config.write(); - info!("Profile was changed to: {}", profile); - } else { - warn!( - "toggle_profile {} does not exist in power_profiles", - profile - ); - return Err(RogError::MissingProfile(profile.to_string())); - } - } - Ok(()) - } - - /// Create a new profile if the requested name doesn't exist, or modify existing - pub(super) fn new_or_modify(&mut self, profile: &Profile) -> Result<(), RogError> { - if let Ok(mut config) = self.config.clone().try_lock() { - config.read(); - - if let Some(existing) = config.power_profiles.get_mut(&profile.name) { - *existing = profile.clone(); - existing.set_system_all()?; - } else { - config - .power_profiles - .insert(profile.name.clone(), profile.clone()); - profile.set_system_all()?; - } - - config.active_profile = profile.name.clone(); config.write(); } Ok(()) diff --git a/daemon/src/ctrl_profiles/mod.rs b/daemon/src/ctrl_profiles/mod.rs index 7df7470d..85a4d229 100644 --- a/daemon/src/ctrl_profiles/mod.rs +++ b/daemon/src/ctrl_profiles/mod.rs @@ -1,3 +1,3 @@ -pub mod zbus; - +pub mod config; pub mod controller; +pub mod zbus; diff --git a/daemon/src/ctrl_profiles/zbus.rs b/daemon/src/ctrl_profiles/zbus.rs index 84c7da2b..4f26d79a 100644 --- a/daemon/src/ctrl_profiles/zbus.rs +++ b/daemon/src/ctrl_profiles/zbus.rs @@ -1,58 +1,54 @@ use log::warn; -use rog_profiles::profiles::Profile; +use rog_profiles::FanCurve; +use rog_profiles::Profile; use std::sync::Arc; use std::sync::Mutex; use zbus::{dbus_interface, fdo::Error}; use zvariant::ObjectPath; -use super::controller::CtrlFanAndCpu; +use super::controller::CtrlPlatformProfile; -pub struct FanAndCpuZbus { - inner: Arc>, +static UNSUPPORTED_MSG: &str = + "Fan curves are not supported on this laptop or you require a patched kernel"; + +pub struct ProfileZbus { + inner: Arc>, } -impl FanAndCpuZbus { - pub fn new(inner: Arc>) -> Self { +impl ProfileZbus { + pub fn new(inner: Arc>) -> Self { Self { inner } } } #[dbus_interface(name = "org.asuslinux.Daemon")] -impl FanAndCpuZbus { - /// Create new profile and make active - fn set_profile(&self, profile: String) { - if let Ok(mut ctrl) = self.inner.try_lock() { - ctrl.set_active(&profile) - .unwrap_or_else(|err| warn!("{}", err)); +impl ProfileZbus { + /// Fetch profile names + fn profiles(&mut self) -> zbus::fdo::Result> { + if let Ok(profiles) = Profile::get_profile_names() { + return Ok(profiles); } - self.do_notification(); + Err(Error::Failed( + "Failed to get all profile details".to_string(), + )) } - /// New or modify profile details and make active, will create if it does not exist - fn new_or_modify(&self, profile: Profile) { - if let Ok(mut ctrl) = self.inner.try_lock() { - ctrl.new_or_modify(&profile) - .unwrap_or_else(|err| warn!("{}", err)); - } - self.do_notification(); - } - - /// Fetch the active profile name + /// Toggle to next platform_profile. Names provided by `Profiles` fn next_profile(&mut self) { if let Ok(mut ctrl) = self.inner.try_lock() { - ctrl.do_next_profile() + ctrl.set_next_profile() .unwrap_or_else(|err| warn!("{}", err)); } self.do_notification(); } /// Fetch the active profile name - fn active_name(&mut self) -> zbus::fdo::Result { + fn active_profile(&mut self) -> zbus::fdo::Result { if let Ok(ctrl) = self.inner.try_lock() { if let Ok(mut cfg) = ctrl.config.try_lock() { cfg.read(); - return Ok(cfg.active_profile.clone()); + return Ok(cfg.active); } } Err(Error::Failed( @@ -60,94 +56,96 @@ impl FanAndCpuZbus { )) } - // TODO: Profile can't implement Type because of Curve - /// Fetch the active profile details - fn active_data(&mut self) -> zbus::fdo::Result { + /// Set this platform_profile name as active + fn set_active_profile(&self, profile: Profile) { + if let Ok(ctrl) = self.inner.try_lock() { + if let Ok(mut cfg) = ctrl.config.try_lock() { + // Read first just incase the user has modified the config before calling this + cfg.read(); + Profile::set_profile(profile); + cfg.active = profile; + } + ctrl.save_config(); + } + self.do_notification(); + } + + /// Get a list of profiles that have fan-curves enabled. + fn enabled_fan_profiles(&mut self) -> zbus::fdo::Result> { if let Ok(ctrl) = self.inner.try_lock() { if let Ok(mut cfg) = ctrl.config.try_lock() { cfg.read(); - if let Some(profile) = cfg.power_profiles.get(&cfg.active_profile) { - return Ok(profile.clone()); + if let Some(curves) = &cfg.fan_curves { + return Ok(curves.get_enabled_curve_names().to_vec()); } + return Err(Error::Failed(UNSUPPORTED_MSG.to_string())); } } Err(Error::Failed( - "Failed to get active profile details".to_string(), + "Failed to get enabled fan curve names".to_string(), )) } - /// Fetch all profile data - fn profiles(&mut self) -> zbus::fdo::Result> { + /// Get the fan-curve data for the currently active Profile + fn active_fan_curve_data(&mut self) -> zbus::fdo::Result { if let Ok(ctrl) = self.inner.try_lock() { if let Ok(mut cfg) = ctrl.config.try_lock() { cfg.read(); - return Ok(cfg.power_profiles.values().cloned().collect()); + if let Some(curves) = &cfg.fan_curves { + return Ok((*curves.get_active_fan_curves()).clone()); + } + return Err(Error::Failed(UNSUPPORTED_MSG.to_string())); } } - Err(Error::Failed( - "Failed to get all profile details".to_string(), - )) + Err(Error::Failed("Failed to get fan curve data".to_string())) } - fn profile_names(&self) -> zbus::fdo::Result> { + /// Get fan-curve data for each Profile as an array of objects + fn fan_curves(&self) -> zbus::fdo::Result> { if let Ok(ctrl) = self.inner.try_lock() { if let Ok(mut cfg) = ctrl.config.try_lock() { cfg.read(); - let profile_names = cfg.power_profiles.keys().cloned().collect::>(); - return Ok(profile_names); + if let Some(curves) = &cfg.fan_curves { + return Ok(curves.get_all_fan_curves()); + } + return Err(Error::Failed(UNSUPPORTED_MSG.to_string())); } } - - Err(Error::Failed("Failed to get all profile names".to_string())) + Err(Error::Failed("Failed to get all fan curves".to_string())) } - fn remove(&self, profile: &str) -> zbus::fdo::Result<()> { + /// Set this fan-curve data + fn set_fan_curve(&self, curve: FanCurve) -> zbus::fdo::Result<()> { if let Ok(ctrl) = self.inner.try_lock() { if let Ok(mut cfg) = ctrl.config.try_lock() { cfg.read(); - - if !cfg.power_profiles.contains_key(profile) { - return Err(Error::Failed("Invalid profile specified".to_string())); + if let Some(curves) = &mut cfg.fan_curves { + curves.set_fan_curve(curve); } - - if cfg.power_profiles.keys().len() == 1 { - return Err(Error::Failed("Cannot delete the last profile".to_string())); - } - - if cfg.active_profile == *profile { - return Err(Error::Failed( - "Cannot delete the active profile".to_string(), - )); - } - - cfg.power_profiles.remove(profile); - cfg.write(); - - return Ok(()); + return Err(Error::Failed(UNSUPPORTED_MSG.to_string())); } + ctrl.save_config(); } - Err(Error::Failed("Failed to lock configuration".to_string())) + Err(Error::Failed("Failed to set fan curves".to_string())) } #[dbus_interface(signal)] fn notify_profile(&self, profile: &Profile) -> zbus::Result<()> {} } -impl FanAndCpuZbus { +impl ProfileZbus { fn do_notification(&self) { if let Ok(ctrl) = self.inner.try_lock() { if let Ok(cfg) = ctrl.config.clone().try_lock() { - if let Some(profile) = cfg.power_profiles.get(&cfg.active_profile) { - self.notify_profile(profile) - .unwrap_or_else(|err| warn!("{}", err)); - } + self.notify_profile(&cfg.active) + .unwrap_or_else(|err| warn!("{}", err)); } } } } -impl crate::ZbusAdd for FanAndCpuZbus { +impl crate::ZbusAdd for ProfileZbus { fn add_to_server(self, server: &mut zbus::ObjectServer) { server .at( diff --git a/daemon/src/ctrl_supported.rs b/daemon/src/ctrl_supported.rs index 60fedf07..8f0f30de 100644 --- a/daemon/src/ctrl_supported.rs +++ b/daemon/src/ctrl_supported.rs @@ -5,20 +5,20 @@ use zvariant::ObjectPath; use zvariant_derive::Type; use crate::{ - ctrl_anime::CtrlAnime, ctrl_charge::CtrlCharge, ctrl_leds::controller::CtrlKbdLed, - ctrl_profiles::controller::CtrlFanAndCpu, ctrl_rog_bios::CtrlRogBios, GetSupported, + ctrl_anime::CtrlAnime, ctrl_aura::controller::CtrlKbdLed, ctrl_charge::CtrlCharge, + ctrl_profiles::controller::CtrlPlatformProfile, ctrl_rog_bios::CtrlRogBios, GetSupported, }; use rog_types::supported::{ - AnimeSupportedFunctions, ChargeSupportedFunctions, FanCpuSupportedFunctions, - LedSupportedFunctions, RogBiosSupportedFunctions, + AnimeSupportedFunctions, ChargeSupportedFunctions, LedSupportedFunctions, + PlatformProfileFunctions, RogBiosSupportedFunctions, }; #[derive(Serialize, Deserialize, Type)] pub struct SupportedFunctions { pub anime_ctrl: AnimeSupportedFunctions, pub charge_ctrl: ChargeSupportedFunctions, - pub fan_cpu_ctrl: FanCpuSupportedFunctions, + pub platform_profile: PlatformProfileFunctions, pub keyboard_led: LedSupportedFunctions, pub rog_bios_ctrl: RogBiosSupportedFunctions, } @@ -53,7 +53,7 @@ impl GetSupported for SupportedFunctions { anime_ctrl: CtrlAnime::get_supported(), keyboard_led: CtrlKbdLed::get_supported(), charge_ctrl: CtrlCharge::get_supported(), - fan_cpu_ctrl: CtrlFanAndCpu::get_supported(), + platform_profile: CtrlPlatformProfile::get_supported(), rog_bios_ctrl: CtrlRogBios::get_supported(), } } diff --git a/daemon/src/daemon.rs b/daemon/src/daemon.rs index 4bd8c604..d04d285f 100644 --- a/daemon/src/daemon.rs +++ b/daemon/src/daemon.rs @@ -1,33 +1,38 @@ -use daemon::ctrl_leds::controller::{ +use daemon::ctrl_anime::config::AnimeConfig; +use daemon::ctrl_anime::zbus::CtrlAnimeZbus; +use daemon::ctrl_aura::config::AuraConfig; +use daemon::ctrl_aura::controller::{ CtrlKbdLed, CtrlKbdLedReloader, CtrlKbdLedTask, CtrlKbdLedZbus, }; +use daemon::ctrl_charge::CtrlCharge; +use daemon::ctrl_profiles::config::ProfileConfig; +use daemon::ctrl_profiles::controller::CtrlPlatformTask; use daemon::{ config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported, }; -use daemon::{config_anime::AnimeConfig, config_aura::AuraConfig, ctrl_charge::CtrlCharge}; use daemon::{ctrl_anime::*, ctrl_gfx::controller::CtrlGraphics}; use daemon::{ - ctrl_profiles::{controller::CtrlFanAndCpu, zbus::FanAndCpuZbus}, + ctrl_profiles::{controller::CtrlPlatformProfile, zbus::ProfileZbus}, laptops::LaptopLedData, }; +use ::zbus::{fdo, Connection, ObjectServer}; use daemon::{CtrlTask, Reloadable, ZbusAdd}; use log::LevelFilter; use log::{error, info, warn}; use rog_dbus::DBUS_NAME; use rog_types::gfx_vendors::GfxVendors; +use std::env; use std::error::Error; use std::io::Write; use std::sync::Arc; use std::sync::Mutex; -use std::env; use daemon::ctrl_rog_bios::CtrlRogBios; -use std::convert::Into; -use zbus::fdo; -use zbus::Connection; use zvariant::ObjectPath; +static PROFILE_CONFIG_PATH: &str = "/etc/asusd/profile.conf"; + pub fn main() -> Result<(), Box> { let mut logger = env_logger::Builder::new(); logger @@ -43,7 +48,9 @@ pub fn main() -> Result<(), Box> { if !is_service { println!("asusd schould be only run from the right systemd service"); - println!("do not run in your terminal, if you need an logs please use journalctl -b -u asusd"); + println!( + "do not run in your terminal, if you need an logs please use journalctl -b -u asusd" + ); println!("asusd will now exit"); return Ok(()); } @@ -71,7 +78,7 @@ fn start_daemon() -> Result<(), Box> { let connection = Connection::new_system()?; fdo::DBusProxy::new(&connection)? .request_name(DBUS_NAME, fdo::RequestNameFlags::ReplaceExisting.into())?; - let mut object_server = zbus::ObjectServer::new(&connection); + let mut object_server = ObjectServer::new(&connection); let config = Config::load(); let enable_gfx_switching = config.gfx_managed; @@ -105,12 +112,16 @@ fn start_daemon() -> Result<(), Box> { } } - match CtrlFanAndCpu::new(config.clone()) { + let profile_config = Arc::new(Mutex::new(ProfileConfig::load(PROFILE_CONFIG_PATH.into()))); + match CtrlPlatformProfile::new(profile_config.clone()) { Ok(mut ctrl) => { ctrl.reload() .unwrap_or_else(|err| warn!("Profile control: {}", err)); + let tmp = Arc::new(Mutex::new(ctrl)); - FanAndCpuZbus::new(tmp).add_to_server(&mut object_server); + ProfileZbus::new(tmp).add_to_server(&mut object_server); + + tasks.push(Box::new(CtrlPlatformTask::new(profile_config))); } Err(err) => { error!("Profile control: {}", err); diff --git a/daemon/src/error.rs b/daemon/src/error.rs index fa61f61e..c3052c8c 100644 --- a/daemon/src/error.rs +++ b/daemon/src/error.rs @@ -1,4 +1,3 @@ -use rog_fan_curve::CurveError; use rog_profiles::error::ProfileError; use rog_types::error::GraphicsError; use std::convert::From; @@ -17,7 +16,6 @@ pub enum RogError { Write(String, std::io::Error), NotSupported, NotFound(String), - FanCurve(CurveError), DoTask(String), MissingFunction(String), MissingLedBrightNode(String, std::io::Error), @@ -44,7 +42,6 @@ impl fmt::Display for RogError { RogError::Write(path, error) => write!(f, "Write {}: {}", path, error), RogError::NotSupported => write!(f, "Not supported"), RogError::NotFound(deets) => write!(f, "Not found: {}", deets), - RogError::FanCurve(err) => write!(f, "Custom fan-curve error: {}", err), RogError::DoTask(deets) => write!(f, "Task error: {}", deets), RogError::MissingFunction(deets) => write!(f, "Missing functionality: {}", deets), RogError::MissingLedBrightNode(path, error) => write!(f, "Led node at {} is missing, please check you have the required patch or dkms module installed: {}", path, error), @@ -62,12 +59,6 @@ impl fmt::Display for RogError { impl std::error::Error for RogError {} -impl From for RogError { - fn from(err: CurveError) -> Self { - RogError::FanCurve(err) - } -} - impl From for RogError { fn from(err: GraphicsError) -> Self { match err { diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index c331569a..f87a5cf4 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -1,17 +1,15 @@ #![deny(unused_must_use)] /// Configuration loading, saving pub mod config; -pub mod config_anime; -pub mod config_aura; pub(crate) mod config_old; /// Control of AniMe matrix display pub mod ctrl_anime; +/// Keyboard LED brightness control, RGB, and LED display modes +pub mod ctrl_aura; /// Control of battery charge level pub mod ctrl_charge; /// GPU switching and power pub mod ctrl_gfx; -/// Keyboard LED brightness control, RGB, and LED display modes -pub mod ctrl_leds; /// Control CPU min/max freq and turbo, fan mode, fan curves /// /// Intel machines can control: diff --git a/rog-dbus/Cargo.toml b/rog-dbus/Cargo.toml index 46e080ac..4fe86180 100644 --- a/rog-dbus/Cargo.toml +++ b/rog-dbus/Cargo.toml @@ -16,4 +16,4 @@ rog_profiles = { path = "../rog-profiles" } rog_types = { path = "../rog-types" } zbus = "^1.9" zbus_macros = "^1.9" -zvariant = "^2.5" +zvariant = "^2.8" diff --git a/rog-dbus/src/lib.rs b/rog-dbus/src/lib.rs index ae692cfb..d7709969 100644 --- a/rog-dbus/src/lib.rs +++ b/rog-dbus/src/lib.rs @@ -12,7 +12,7 @@ pub mod zbus_supported; use rog_anime::AnimePowerStates; use rog_aura::{AuraEffect, LedPowerStates}; -use rog_profiles::profiles::Profile; +use rog_profiles::Profile; use rog_types::gfx_vendors::{GfxRequiredUserAction, GfxVendors}; use std::sync::mpsc::{channel, Receiver}; use zbus::{Connection, Result, SignalReceiver}; diff --git a/rog-dbus/src/zbus_profile.rs b/rog-dbus/src/zbus_profile.rs index 38d8c7e2..7e5247ac 100644 --- a/rog-dbus/src/zbus_profile.rs +++ b/rog-dbus/src/zbus_profile.rs @@ -21,7 +21,7 @@ use std::sync::mpsc::Sender; -use rog_profiles::profiles::Profile; +use rog_profiles::{FanCurve, Profile}; use zbus::{dbus_proxy, Connection, Result}; #[dbus_proxy( @@ -29,26 +29,29 @@ use zbus::{dbus_proxy, Connection, Result}; default_path = "/org/asuslinux/Profile" )] trait Daemon { + /// Profiles method + fn profiles(&self) -> zbus::Result>; + /// NextProfile method fn next_profile(&self) -> zbus::Result<()>; /// Profile, get the active profile - fn active_name(&self) -> zbus::Result; + fn active_profile(&self) -> zbus::Result; + + /// Set the specific profile as active + fn set_active_profile(&self, profile: Profile) -> zbus::Result<()>; + + /// Get enabled fan curves + fn enabled_fan_profiles(&self) -> zbus::Result>; /// Get the active `Profile` data - fn active_data(&self) -> zbus::Result; + fn active_fan_data(&self) -> zbus::Result; - /// Profiles method - fn profiles(&self) -> zbus::Result>; + /// Get all fan curve data + fn fan_curves(&self) -> zbus::Result>; - /// ProfileNames method - fn profile_names(&self) -> zbus::Result>; - - /// Remove method - fn remove(&self, profile: &str) -> zbus::Result<()>; - - /// SetProfile method - fn new_or_modify(&self, profile: &Profile) -> zbus::Result<()>; + /// Set a fan curve. If a field is empty then the exisiting saved curve is used + fn set_fan_curve(&self, curve: FanCurve) -> zbus::Result<()>; /// NotifyProfile signal #[dbus_proxy(signal)] @@ -69,40 +72,15 @@ impl<'a> ProfileProxy<'a> { } #[inline] - pub fn active_name(&self) -> Result { - self.0.active_name() - } - - #[inline] - pub fn active_data(&self) -> Result { - self.0.active_data() - } - - #[inline] - pub fn all_profile_data(&self) -> Result> { + pub fn profiles(&self) -> Result> { self.0.profiles() } #[inline] - pub fn next_fan(&self) -> Result<()> { + pub fn next_profile(&self) -> Result<()> { self.0.next_profile() } - #[inline] - pub fn profile_names(&self) -> Result> { - self.0.profile_names() - } - - #[inline] - pub fn remove(&self, profile: &str) -> Result<()> { - self.0.remove(profile) - } - - #[inline] - pub fn new_or_modify(&self, profile: &Profile) -> Result<()> { - self.0.new_or_modify(profile) - } - #[inline] pub fn connect_notify_profile(&self, send: Sender) -> zbus::fdo::Result<()> { self.0.connect_notify_profile(move |data| { diff --git a/rog-profiles/Cargo.toml b/rog-profiles/Cargo.toml index bc434fce..5da2153b 100644 --- a/rog-profiles/Cargo.toml +++ b/rog-profiles/Cargo.toml @@ -9,10 +9,8 @@ default = ["dbus"] dbus = ["zvariant", "zvariant_derive"] [dependencies] -rog_fan_curve = { git = "https://github.com/Yarn/rog_fan_curve.git" } serde = "^1.0" serde_derive = "^1.0" -intel-pstate = "^0.2" zvariant = { version = "^2.6", optional = true } zvariant_derive = { version = "^2.6", optional = true } \ No newline at end of file diff --git a/rog-profiles/src/error.rs b/rog-profiles/src/error.rs index c5dc7c12..f057e040 100644 --- a/rog-profiles/src/error.rs +++ b/rog-profiles/src/error.rs @@ -1,18 +1,12 @@ use std::fmt; -use intel_pstate::PStateError; -use rog_fan_curve::CurveError; - #[derive(Debug)] pub enum ProfileError { - ParseFanLevel, Path(String, std::io::Error), Read(String, std::io::Error), Write(String, std::io::Error), NotSupported, NotFound(String), - IntelPstate(PStateError), - FanCurve(CurveError), Io(std::io::Error), //Zbus(zbus::Error), } @@ -21,14 +15,11 @@ impl fmt::Display for ProfileError { // This trait requires `fmt` with this exact signature. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - ProfileError::ParseFanLevel => write!(f, "Parse profile error"), ProfileError::Path(path, error) => write!(f, "Path {}: {}", path, error), ProfileError::Read(path, error) => write!(f, "Read {}: {}", path, error), ProfileError::Write(path, error) => write!(f, "Write {}: {}", path, error), ProfileError::NotSupported => write!(f, "Not supported"), ProfileError::NotFound(deets) => write!(f, "Not found: {}", deets), - ProfileError::IntelPstate(err) => write!(f, "Intel pstate error: {}", err), - ProfileError::FanCurve(err) => write!(f, "Custom fan-curve error: {}", err), ProfileError::Io(detail) => write!(f, "std::io error: {}", detail), //Error::Zbus(detail) => write!(f, "Zbus error: {}", detail), } @@ -36,15 +27,3 @@ impl fmt::Display for ProfileError { } impl std::error::Error for ProfileError {} - -impl From for ProfileError { - fn from(err: PStateError) -> Self { - ProfileError::IntelPstate(err) - } -} - -impl From for ProfileError { - fn from(err: CurveError) -> Self { - ProfileError::FanCurve(err) - } -} diff --git a/rog-profiles/src/lib.rs b/rog-profiles/src/lib.rs index 96d68664..eaa12415 100644 --- a/rog-profiles/src/lib.rs +++ b/rog-profiles/src/lib.rs @@ -1,8 +1,284 @@ pub mod error; -pub mod profiles; -static FAN_TYPE_1_PATH: &str = "/sys/devices/platform/asus-nb-wmi/throttle_thermal_policy"; -static FAN_TYPE_2_PATH: &str = "/sys/devices/platform/asus-nb-wmi/fan_boost_mode"; -static AMD_BOOST_PATH: &str = "/sys/devices/system/cpu/cpufreq/boost"; +use std::{ + fs::OpenOptions, + io::{Read, Write}, + path::{Path, PathBuf}, +}; + +use error::ProfileError; +use serde_derive::{Deserialize, Serialize}; + +#[cfg(feature = "dbus")] +use zvariant_derive::Type; + +pub static PLATFORM_PROFILE: &str = "/sys/firmware/acpi/platform_profile"; +pub static PLATFORM_PROFILES: &str = "/sys/firmware/acpi/platform_profile_choices"; + +pub static FAN_CURVE_BASE_PATH: &str = "/sys/devices/platform/asus-nb-wmi/"; +pub static FAN_CURVE_ACTIVE_FILE: &str = "enabled_fan_curve_profiles"; +pub static FAN_CURVE_FILENAME_PART: &str = "_fan_curve_"; pub static VERSION: &str = env!("CARGO_PKG_VERSION"); + +#[cfg_attr(feature = "dbus", derive(Type))] +#[derive(Deserialize, Serialize, Debug, Clone, Copy)] +pub enum Profile { + Balanced, + Performance, + Quiet, +} + +impl Profile { + pub fn is_platform_profile_supported() -> bool { + Path::new(PLATFORM_PROFILES).exists() + } + + pub fn get_active_profile() -> Result { + let mut file = OpenOptions::new() + .read(true) + .open(&PLATFORM_PROFILE) + .unwrap_or_else(|_| panic!("{} not found", &PLATFORM_PROFILE)); + + let mut buf = String::new(); + file.read_to_string(&mut buf).unwrap(); + Ok(buf.as_str().into()) + } + + pub fn get_profile_names() -> Result, ProfileError> { + let mut file = OpenOptions::new() + .read(true) + .open(&PLATFORM_PROFILES) + .unwrap_or_else(|_| panic!("{} not found", &PLATFORM_PROFILES)); + + let mut buf = String::new(); + file.read_to_string(&mut buf).unwrap(); + Ok(buf.rsplit(' ').map(|p| p.into()).collect()) + } + + pub fn set_profile(profile: Profile) { + let mut file = OpenOptions::new() + .write(true) + .open(PLATFORM_PROFILE) + .unwrap_or_else(|_| panic!("{} not found", PLATFORM_PROFILE)); + + file.write_all(<&str>::from(profile).as_bytes()).unwrap(); + } +} + +impl Default for Profile { + fn default() -> Self { + Self::Balanced + } +} + +impl From for &str { + fn from(profile: Profile) -> &'static str { + match profile { + Profile::Balanced => "balanced", + Profile::Performance => "performance", + Profile::Quiet => "quiet", + } + } +} + +impl From<&str> for Profile { + fn from(profile: &str) -> Profile { + match profile.to_ascii_lowercase().trim() { + "balanced" => Profile::Balanced, + "performance" => Profile::Performance, + "quiet" => Profile::Quiet, + _ => Profile::Balanced, + } + } +} + +#[cfg_attr(feature = "dbus", derive(Type))] +#[derive(Deserialize, Serialize, Debug, Clone, Copy)] +pub enum FanCurvePU { + CPU, + GPU, +} + +impl From for &str { + fn from(pu: FanCurvePU) -> &'static str { + match pu { + FanCurvePU::CPU => "cpu", + FanCurvePU::GPU => "gpu", + } + } +} + +impl Default for FanCurvePU { + fn default() -> Self { + Self::CPU + } +} + +#[cfg_attr(feature = "dbus", derive(Type))] +#[derive(Deserialize, Serialize, Default, Debug, Clone)] +pub struct FanCurve { + pub profile: Profile, + pub cpu: String, + pub gpu: String, +} + +/// Main purpose of `FanCurves` is to enable retoring state on system boot +#[cfg_attr(feature = "dbus", derive(Type))] +#[derive(Deserialize, Serialize, Debug)] +pub struct FanCurves { + active_curves: Vec, + balanced: FanCurve, + performance: FanCurve, + quiet: FanCurve, +} + +impl Default for FanCurves { + fn default() -> Self { + let mut curves = Self { + active_curves: Default::default(), + balanced: Default::default(), + performance: Default::default(), + quiet: Default::default(), + }; + curves.balanced.profile = Profile::Balanced; + curves.performance.profile = Profile::Performance; + curves.quiet.profile = Profile::Quiet; + curves + } +} + +impl FanCurves { + pub fn is_fan_curves_supported() -> bool { + let mut path = PathBuf::new(); + path.push(FAN_CURVE_BASE_PATH); + path.push(FAN_CURVE_ACTIVE_FILE); + path.exists() + } + + pub fn update_from_platform(&mut self) { + self.balanced.cpu = Self::get_fan_curve_from_file(Profile::Balanced, FanCurvePU::CPU); + self.balanced.gpu = Self::get_fan_curve_from_file(Profile::Balanced, FanCurvePU::GPU); + + self.performance.cpu = Self::get_fan_curve_from_file(Profile::Performance, FanCurvePU::CPU); + self.performance.gpu = Self::get_fan_curve_from_file(Profile::Performance, FanCurvePU::GPU); + + self.quiet.cpu = Self::get_fan_curve_from_file(Profile::Quiet, FanCurvePU::CPU); + self.quiet.gpu = Self::get_fan_curve_from_file(Profile::Quiet, FanCurvePU::GPU); + } + + pub fn update_platform(&self) { + Self::set_fan_curve_for_platform(Profile::Balanced, FanCurvePU::CPU, &self.balanced.cpu); + Self::set_fan_curve_for_platform(Profile::Balanced, FanCurvePU::GPU, &self.balanced.gpu); + + Self::set_fan_curve_for_platform( + Profile::Performance, + FanCurvePU::CPU, + &self.performance.cpu, + ); + Self::set_fan_curve_for_platform( + Profile::Performance, + FanCurvePU::GPU, + &self.performance.gpu, + ); + + Self::set_fan_curve_for_platform(Profile::Quiet, FanCurvePU::CPU, &self.quiet.cpu); + Self::set_fan_curve_for_platform(Profile::Quiet, FanCurvePU::GPU, &self.quiet.gpu); + } + + pub fn get_enabled_curve_names(&self) -> &[Profile] { + &self.active_curves + } + + pub fn get_all_fan_curves(&self) -> Vec { + vec![ + self.balanced.clone(), + self.performance.clone(), + self.quiet.clone(), + ] + } + + pub fn get_active_fan_curves(&self) -> &FanCurve { + match Profile::get_active_profile().unwrap() { + Profile::Balanced => &self.balanced, + Profile::Performance => &self.performance, + Profile::Quiet => &self.quiet, + } + } + + pub fn get_fan_curves_for(&self, name: Profile) -> &FanCurve { + match name { + Profile::Balanced => &self.balanced, + Profile::Performance => &self.performance, + Profile::Quiet => &self.quiet, + } + } + + fn get_fan_curve_from_file(name: Profile, pu: FanCurvePU) -> String { + let mut file: String = FAN_CURVE_BASE_PATH.into(); + file.push_str(pu.into()); + file.push_str(FAN_CURVE_FILENAME_PART); + file.push_str(name.into()); + + let mut file = OpenOptions::new() + .read(true) + .open(&file) + .unwrap_or_else(|_| panic!("{} not found", &file)); + + let mut buf = String::new(); + file.read_to_string(&mut buf).unwrap(); + buf.trim().to_string() + } + + pub fn get_fan_curve_for(&self, name: &Profile, pu: &FanCurvePU) -> &str { + match name { + Profile::Balanced => match pu { + FanCurvePU::CPU => &self.balanced.cpu, + FanCurvePU::GPU => &self.balanced.gpu, + }, + Profile::Performance => match pu { + FanCurvePU::CPU => &self.balanced.cpu, + FanCurvePU::GPU => &self.balanced.gpu, + }, + Profile::Quiet => match pu { + FanCurvePU::CPU => &self.balanced.cpu, + FanCurvePU::GPU => &self.balanced.gpu, + }, + } + } + + fn set_fan_curve_for_platform(name: Profile, pu: FanCurvePU, curve: &str) { + let mut file: String = FAN_CURVE_BASE_PATH.into(); + file.push_str(pu.into()); + file.push_str(FAN_CURVE_FILENAME_PART); + file.push_str(name.into()); + + let mut file = OpenOptions::new() + .write(true) + .open(&file) + .unwrap_or_else(|_| panic!("{} not found", &file)); + + file.write_all(curve.as_bytes()).unwrap(); + } + + pub fn set_fan_curve(&mut self, curve: FanCurve) { + // First, set the profiles. + Self::set_fan_curve_for_platform(curve.profile, FanCurvePU::CPU, &curve.cpu); + match curve.profile { + Profile::Balanced => self.balanced.cpu = curve.cpu, + Profile::Performance => self.performance.cpu = curve.cpu, + Profile::Quiet => self.quiet.cpu = curve.cpu, + }; + + Self::set_fan_curve_for_platform(curve.profile, FanCurvePU::GPU, &curve.gpu); + match curve.profile { + Profile::Balanced => self.balanced.gpu = curve.gpu, + Profile::Performance => self.performance.gpu = curve.gpu, + Profile::Quiet => self.quiet.cpu = curve.gpu, + }; + + // Any curve that was blank will have been reset, so repopulate the settings + // Note: successfully set curves will just be re-read in. + self.update_from_platform(); + } +} diff --git a/rog-profiles/src/profiles.rs b/rog-profiles/src/profiles.rs deleted file mode 100644 index 53754dc8..00000000 --- a/rog-profiles/src/profiles.rs +++ /dev/null @@ -1,185 +0,0 @@ -use rog_fan_curve::{Curve, Fan}; -use serde_derive::{Deserialize, Serialize}; -use std::io::Write; -use std::{fs::OpenOptions, path::Path, str::FromStr}; -#[cfg(feature = "dbus")] -use zvariant_derive::Type; - -use crate::{error::ProfileError, AMD_BOOST_PATH, FAN_TYPE_1_PATH, FAN_TYPE_2_PATH}; - -#[cfg_attr(feature = "dbus", derive(Type))] -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct Profile { - pub name: String, - pub min_percentage: u8, - pub max_percentage: u8, - pub turbo: bool, - pub fan_preset: FanLevel, - pub fan_curve: String, -} - -impl Default for Profile { - fn default() -> Self { - Profile { - name: "new".into(), - min_percentage: 0, - max_percentage: 100, - turbo: false, - fan_preset: FanLevel::Normal, - fan_curve: "".to_string(), - } - } -} - -impl Profile { - pub fn new( - name: String, - min_percentage: u8, - max_percentage: u8, - turbo: bool, - fan_preset: FanLevel, - fan_curve: String, - ) -> Self { - Profile { - name, - min_percentage, - max_percentage, - turbo, - fan_preset, - fan_curve, - } - } - - pub fn get_intel_supported() -> bool { - intel_pstate::PState::new().is_ok() - } - - pub fn get_fan_path() -> Result<&'static str, ProfileError> { - if Path::new(FAN_TYPE_1_PATH).exists() { - Ok(FAN_TYPE_1_PATH) - } else if Path::new(FAN_TYPE_2_PATH).exists() { - Ok(FAN_TYPE_2_PATH) - } else { - Err(ProfileError::NotSupported) - } - } - - pub fn set_system_pstate(&self) -> Result<(), ProfileError> { - // Set CPU pstate - if let Ok(pstate) = intel_pstate::PState::new() { - pstate.set_min_perf_pct(self.min_percentage)?; - pstate.set_max_perf_pct(self.max_percentage)?; - pstate.set_no_turbo(!self.turbo)?; - } else { - // must be AMD CPU - let mut file = OpenOptions::new() - .write(true) - .open(AMD_BOOST_PATH) - .map_err(|err| ProfileError::Path(AMD_BOOST_PATH.into(), err))?; - - let boost = if self.turbo { "1" } else { "0" }; // opposite of Intel - file.write_all(boost.as_bytes()) - .map_err(|err| ProfileError::Write(AMD_BOOST_PATH.into(), err))?; - } - Ok(()) - } - - pub fn set_system_fan_mode(&self) -> Result<(), ProfileError> { - let path = Profile::get_fan_path()?; - let mut fan_ctrl = OpenOptions::new() - .write(true) - .open(path) - .map_err(|err| ProfileError::Path(path.into(), err))?; - fan_ctrl - .write_all(format!("{}\n", ::from(self.fan_preset)).as_bytes()) - .map_err(|err| ProfileError::Write(path.into(), err))?; - Ok(()) - } - - pub fn set_system_fan_curve(&self) -> Result<(), ProfileError> { - if !self.fan_curve.is_empty() { - if let Ok(curve) = Profile::parse_fan_curve(&self.fan_curve) { - use rog_fan_curve::Board; - if let Some(board) = Board::from_board_name() { - curve.apply(board, Fan::Cpu)?; - curve.apply(board, Fan::Gpu)?; - } - } - } - - Ok(()) - } - - pub fn set_system_all(&self) -> Result<(), ProfileError> { - self.set_system_pstate()?; - if self.fan_curve.is_empty() { - self.set_system_fan_mode()?; - } else { - self.set_system_fan_curve()?; - } - Ok(()) - } - - fn parse_fan_curve(data: &str) -> Result { - let curve = Curve::from_config_str(data)?; - if let Err(err) = curve.check_safety(Fan::Cpu) { - return Err(format!("Unsafe curve {:?}", err)); - } - if let Err(err) = curve.check_safety(Fan::Gpu) { - return Err(format!("Unsafe curve {:?}", err)); - } - Ok(curve) - } -} - -#[cfg_attr(feature = "dbus", derive(Type))] -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] -pub enum FanLevel { - Normal, - Boost, - Silent, -} - -impl FromStr for FanLevel { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "normal" => Ok(FanLevel::Normal), - "boost" => Ok(FanLevel::Boost), - "silent" => Ok(FanLevel::Silent), - _ => Err("Invalid fan level"), - } - } -} - -impl From for FanLevel { - fn from(n: u8) -> Self { - match n { - 0 => FanLevel::Normal, - 1 => FanLevel::Boost, - 2 => FanLevel::Silent, - _ => FanLevel::Normal, - } - } -} - -impl From for u8 { - fn from(n: FanLevel) -> Self { - match n { - FanLevel::Normal => 0, - FanLevel::Boost => 1, - FanLevel::Silent => 2, - } - } -} - -impl From<&FanLevel> for u8 { - fn from(n: &FanLevel) -> Self { - match n { - FanLevel::Normal => 0, - FanLevel::Boost => 1, - FanLevel::Silent => 2, - } - } -} diff --git a/rog-types/src/supported.rs b/rog-types/src/supported.rs index e8e3fef8..a132a814 100644 --- a/rog-types/src/supported.rs +++ b/rog-types/src/supported.rs @@ -7,7 +7,7 @@ use zvariant_derive::Type; pub struct SupportedFunctions { pub anime_ctrl: AnimeSupportedFunctions, pub charge_ctrl: ChargeSupportedFunctions, - pub fan_cpu_ctrl: FanCpuSupportedFunctions, + pub platform_profile: PlatformProfileFunctions, pub keyboard_led: LedSupportedFunctions, pub rog_bios_ctrl: RogBiosSupportedFunctions, } @@ -21,10 +21,9 @@ pub struct ChargeSupportedFunctions { } #[derive(Serialize, Deserialize, Type, Debug)] -pub struct FanCpuSupportedFunctions { - pub stock_fan_modes: bool, - pub min_max_freq: bool, - pub fan_curve_set: bool, +pub struct PlatformProfileFunctions { + pub platform_profile: bool, + pub fan_curves: bool, } #[derive(Serialize, Deserialize, Type, Debug)] @@ -45,7 +44,7 @@ impl fmt::Display for SupportedFunctions { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "{}", self.anime_ctrl)?; writeln!(f, "{}", self.charge_ctrl)?; - writeln!(f, "{}", self.fan_cpu_ctrl)?; + writeln!(f, "{}", self.platform_profile)?; writeln!(f, "{}", self.keyboard_led)?; writeln!(f, "{}", self.rog_bios_ctrl) } @@ -67,12 +66,11 @@ impl fmt::Display for ChargeSupportedFunctions { ) } } -impl fmt::Display for FanCpuSupportedFunctions { +impl fmt::Display for PlatformProfileFunctions { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "Fan:")?; - writeln!(f, "\tStock fan modes: {}", self.stock_fan_modes)?; - writeln!(f, "\tMin/max frequency: {}", self.min_max_freq)?; - writeln!(f, "\tFan curve control: {}", self.fan_curve_set) + writeln!(f, "Platform profiles:")?; + writeln!(f, "\tplatform: {}", self.platform_profile)?; + writeln!(f, "\tfan curves: {}", self.fan_curves) } } impl fmt::Display for LedSupportedFunctions { From 6ceb5cf9393c643a979b6cb274a58258e5d813e1 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Wed, 25 Aug 2021 11:16:23 +1200 Subject: [PATCH 02/11] Major restructure to move gfx control out to crate --- Cargo.lock | 19 + Cargo.toml | 4 +- asus-notify/Cargo.toml | 1 + asus-notify/src/main.rs | 3 +- asusctl/Cargo.toml | 1 + asusctl/src/main.rs | 4 +- daemon/Cargo.toml | 1 + daemon/src/config.rs | 69 +-- daemon/src/config_old.rs | 85 +--- daemon/src/ctrl_anime/config.rs | 257 ++++++++++++ daemon/src/ctrl_anime/mod.rs | 329 +++++++++++++++ daemon/src/ctrl_anime/zbus.rs | 140 +++++++ daemon/src/ctrl_aura/config.rs | 267 ++++++++++++ daemon/src/ctrl_aura/controller.rs | 395 ++++++++++++++++++ daemon/src/ctrl_aura/mod.rs | 3 + daemon/src/ctrl_aura/zbus.rs | 165 ++++++++ daemon/src/ctrl_profiles/config.rs | 92 ++++ daemon/src/daemon.rs | 16 +- daemon/src/error.rs | 5 +- daemon/src/lib.rs | 2 - rog-dbus/Cargo.toml | 1 + rog-dbus/src/lib.rs | 2 +- rog-dbus/src/zbus_gfx.rs | 2 +- rog-types/src/lib.rs | 2 - supergfx/Cargo.toml | 26 ++ supergfx/LICENSE | 373 +++++++++++++++++ supergfx/src/config.rs | 89 ++++ .../ctrl_gfx => supergfx/src}/controller.rs | 110 +++-- .../src/ctrl_gfx => supergfx/src}/error.rs | 24 +- {rog-types => supergfx}/src/gfx_vendors.rs | 13 +- .../ctrl_gfx/mod.rs => supergfx/src/lib.rs | 5 +- .../src/ctrl_gfx => supergfx/src}/system.rs | 0 supergfx/src/zbus.rs | 55 +++ 33 files changed, 2330 insertions(+), 230 deletions(-) create mode 100644 daemon/src/ctrl_anime/config.rs create mode 100644 daemon/src/ctrl_anime/mod.rs create mode 100644 daemon/src/ctrl_anime/zbus.rs create mode 100644 daemon/src/ctrl_aura/config.rs create mode 100644 daemon/src/ctrl_aura/controller.rs create mode 100644 daemon/src/ctrl_aura/mod.rs create mode 100644 daemon/src/ctrl_aura/zbus.rs create mode 100644 daemon/src/ctrl_profiles/config.rs create mode 100644 supergfx/Cargo.toml create mode 100644 supergfx/LICENSE create mode 100644 supergfx/src/config.rs rename {daemon/src/ctrl_gfx => supergfx/src}/controller.rs (90%) rename {daemon/src/ctrl_gfx => supergfx/src}/error.rs (71%) rename {rog-types => supergfx}/src/gfx_vendors.rs (90%) rename daemon/src/ctrl_gfx/mod.rs => supergfx/src/lib.rs (97%) rename {daemon/src/ctrl_gfx => supergfx/src}/system.rs (100%) create mode 100644 supergfx/src/zbus.rs diff --git a/Cargo.lock b/Cargo.lock index c3e4811e..1c89b48e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,6 +39,7 @@ dependencies = [ "rog_profiles", "rog_types", "serde_json", + "supergfxctl", ] [[package]] @@ -54,6 +55,7 @@ dependencies = [ "rog_dbus", "rog_profiles", "rog_types", + "supergfxctl", "tinybmp", "yansi-term", ] @@ -219,6 +221,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", + "supergfxctl", "sysfs-class", "toml", "udev", @@ -922,6 +925,7 @@ dependencies = [ "rog_aura", "rog_profiles", "rog_types", + "supergfxctl", "zbus", "zbus_macros", "zvariant", @@ -1062,6 +1066,21 @@ dependencies = [ "syn 0.11.11", ] +[[package]] +name = "supergfxctl" +version = "1.1.0" +dependencies = [ + "log", + "logind-zbus", + "serde", + "serde_derive", + "serde_json", + "sysfs-class", + "zbus", + "zvariant", + "zvariant_derive", +] + [[package]] name = "syn" version = "0.11.11" diff --git a/Cargo.toml b/Cargo.toml index 774d9f13..e4983797 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-types", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles"] +members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-types", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "supergfx"] [profile.release] lto = true @@ -13,4 +13,4 @@ opt-level = 1 [profile.bench] debug = false -opt-level = 3 \ No newline at end of file +opt-level = 3 diff --git a/asus-notify/Cargo.toml b/asus-notify/Cargo.toml index 251708a2..7f5bc0f6 100644 --- a/asus-notify/Cargo.toml +++ b/asus-notify/Cargo.toml @@ -13,6 +13,7 @@ rog_dbus = { path = "../rog-dbus" } rog_aura = { path = "../rog-aura" } rog_types = { path = "../rog-types" } rog_profiles = { path = "../rog-profiles" } +supergfxctl = { path = "../supergfx" } [dependencies.notify-rust] version = "^4.3" diff --git a/asus-notify/src/main.rs b/asus-notify/src/main.rs index 83f96401..e36c75f8 100644 --- a/asus-notify/src/main.rs +++ b/asus-notify/src/main.rs @@ -2,8 +2,7 @@ use notify_rust::{Hint, Notification, NotificationHandle}; use rog_aura::AuraEffect; use rog_dbus::{DbusProxies, Signals}; use rog_profiles::Profile; -use rog_types::gfx_vendors::GfxRequiredUserAction; -use rog_types::gfx_vendors::GfxVendors; +use supergfxctl::gfx_vendors::{GfxRequiredUserAction, GfxVendors}; use std::error::Error; use std::process; use std::thread::sleep; diff --git a/asusctl/Cargo.toml b/asusctl/Cargo.toml index c84f1d2b..1e1cce13 100644 --- a/asusctl/Cargo.toml +++ b/asusctl/Cargo.toml @@ -15,6 +15,7 @@ rog_types = { path = "../rog-types" } daemon = { path = "../daemon" } gumdrop = "^0.8" yansi-term = "^0.1" +supergfxctl = { path = "../supergfx" } [dev-dependencies] tinybmp = "^0.2.3" diff --git a/asusctl/src/main.rs b/asusctl/src/main.rs index 6e41a292..bdfc6ca8 100644 --- a/asusctl/src/main.rs +++ b/asusctl/src/main.rs @@ -10,12 +10,12 @@ use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2, ANIME_DATA_LEN}; use rog_aura::{self, AuraEffect}; use rog_dbus::RogDbusClient; use rog_types::{ - gfx_vendors::{GfxRequiredUserAction, GfxVendors}, supported::{ AnimeSupportedFunctions, LedSupportedFunctions, PlatformProfileFunctions, RogBiosSupportedFunctions, }, }; +use supergfxctl::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}; use std::{env::args, path::Path}; use yansi_term::Colour::Green; use yansi_term::Colour::Red; @@ -240,7 +240,7 @@ fn do_gfx( if command.pow { let res = dbus.proxies().gfx().gfx_get_pwr()?; match res { - rog_types::gfx_vendors::GfxPower::Active => { + GfxPower::Active => { println!("Current power status: {}", Red.paint(<&str>::from(&res))) } _ => println!("Current power status: {}", Green.paint(<&str>::from(&res))), diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index 60e167f8..e6addacd 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -23,6 +23,7 @@ rog_aura = { path = "../rog-aura" } rog_types = { path = "../rog-types" } rog_profiles = { path = "../rog-profiles" } rog_dbus = { path = "../rog-dbus" } +supergfxctl = { path = "../supergfx" } rusb = "^0.8" udev = "^0.6" diff --git a/daemon/src/config.rs b/daemon/src/config.rs index 5d7c73c3..260e34b8 100644 --- a/daemon/src/config.rs +++ b/daemon/src/config.rs @@ -1,5 +1,4 @@ use log::{error, info, warn}; -use rog_types::gfx_vendors::GfxVendors; use serde_derive::{Deserialize, Serialize}; use std::fs::{File, OpenOptions}; use std::io::{Read, Write}; @@ -8,33 +7,20 @@ use crate::config_old::*; use crate::VERSION; pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf"; -pub static AURA_CONFIG_PATH: &str = "/etc/asusd/asusd.conf"; #[derive(Deserialize, Serialize)] pub struct Config { - pub gfx_mode: GfxVendors, - /// Only for informational purposes. - #[serde(skip)] - pub gfx_tmp_mode: Option, - pub gfx_managed: bool, - pub gfx_vfio_enable: bool, /// Save charge limit for restoring on boot pub bat_charge_limit: u8, } -impl Default for Config { - fn default() -> Self { +impl Config { + fn new() -> Self { Config { - gfx_mode: GfxVendors::Hybrid, - gfx_tmp_mode: None, - gfx_managed: true, - gfx_vfio_enable: false, bat_charge_limit: 100, } } -} -impl Config { /// `load` will attempt to read the config, and panic if the dir is missing pub fn load() -> Self { let mut file = OpenOptions::new() @@ -44,47 +30,23 @@ impl Config { .open(&CONFIG_PATH) .unwrap_or_else(|_| panic!("The directory /etc/asusd/ is missing")); // okay to cause panic here let mut buf = String::new(); + let config; if let Ok(read_len) = file.read_to_string(&mut buf) { if read_len == 0 { - return Config::create_default(&mut file); + config = Self::new(); + } else if let Ok(data) = serde_json::from_str(&buf) { + config = data; + } else if let Ok(data) = serde_json::from_str::(&buf) { + config = data.into_current(); + info!("Updated config version to: {}", VERSION); } else { - if let Ok(data) = serde_json::from_str(&buf) { - return data; - } else if let Ok(data) = serde_json::from_str::(&buf) { - let config = data.into_current(); - config.write(); - info!("Updated config version to: {}", VERSION); - return config; - } else if let Ok(data) = serde_json::from_str::(&buf) { - let config = data.into_current(); - config.write(); - info!("Updated config version to: {}", VERSION); - return config; - } else if let Ok(data) = serde_json::from_str::(&buf) { - let config = data.into_current(); - config.write(); - info!("Updated config version to: {}", VERSION); - return config; - } else if let Ok(data) = serde_json::from_str::(&buf) { - let config = data.into_current(); - config.write(); - info!("Updated config version to: {}", VERSION); - return config; - } warn!("Could not deserialise {}", CONFIG_PATH); panic!("Please remove {} then restart asusd", CONFIG_PATH); } + } else { + config = Self::new() } - Config::create_default(&mut file) - } - - fn create_default(file: &mut File) -> Self { - let config = Config::default(); - - // Should be okay to unwrap this as is since it is a Default - let json = serde_json::to_string_pretty(&config).unwrap(); - file.write_all(json.as_bytes()) - .unwrap_or_else(|_| panic!("Could not write {}", CONFIG_PATH)); + config.write(); config } @@ -98,11 +60,8 @@ impl Config { if l == 0 { warn!("File is empty {}", CONFIG_PATH); } else { - let mut x: Config = serde_json::from_str(&buf) - .unwrap_or_else(|_| panic!("Could not deserialise {}", CONFIG_PATH)); - // copy over serde skipped values - x.gfx_tmp_mode = self.gfx_tmp_mode; - *self = x; + serde_json::from_str(&buf) + .unwrap_or_else(|_| panic!("Could not deserialise {}", CONFIG_PATH)) } } } diff --git a/daemon/src/config_old.rs b/daemon/src/config_old.rs index 49254b3e..0271380b 100644 --- a/daemon/src/config_old.rs +++ b/daemon/src/config_old.rs @@ -1,88 +1,9 @@ -use rog_types::gfx_vendors::GfxVendors; use serde_derive::{Deserialize, Serialize}; +use supergfxctl::gfx_vendors::GfxVendors; use std::collections::BTreeMap; use crate::config::Config; -/// for parsing old v3.1.7 config -#[allow(dead_code)] -#[derive(Deserialize)] -pub(crate) struct ConfigV317 { - pub gfx_mode: GfxVendors, - pub gfx_managed: bool, - pub active_profile: String, - pub toggle_profiles: Vec, - #[serde(skip)] - pub curr_fan_mode: u8, - pub bat_charge_limit: u8, - pub kbd_led_brightness: u8, - pub kbd_backlight_mode: u8, - #[serde(skip)] - pub kbd_backlight_modes: Option, - pub power_profiles: BTreeMap, -} - -impl ConfigV317 { - pub(crate) fn into_current(self) -> Config { - Config { - gfx_mode: self.gfx_mode, - gfx_tmp_mode: None, - gfx_managed: self.gfx_managed, - gfx_vfio_enable: false, - bat_charge_limit: self.bat_charge_limit, - } - } -} - -#[derive(Deserialize, Serialize)] -pub struct ConfigV324 { - pub gfx_mode: GfxVendors, - pub gfx_managed: bool, - pub active_profile: String, - pub toggle_profiles: Vec, - #[serde(skip)] - pub curr_fan_mode: u8, - pub bat_charge_limit: u8, - pub power_profiles: BTreeMap, -} - -impl ConfigV324 { - pub(crate) fn into_current(self) -> Config { - Config { - gfx_mode: GfxVendors::Hybrid, - gfx_tmp_mode: None, - gfx_managed: self.gfx_managed, - gfx_vfio_enable: false, - bat_charge_limit: self.bat_charge_limit, - } - } -} - -#[derive(Deserialize, Serialize)] -pub struct ConfigV341 { - pub gfx_mode: GfxVendors, - pub gfx_managed: bool, - pub gfx_vfio_enable: bool, - pub active_profile: String, - pub toggle_profiles: Vec, - #[serde(skip)] - pub curr_fan_mode: u8, - pub bat_charge_limit: u8, - pub power_profiles: BTreeMap, -} - -impl ConfigV341 { - pub(crate) fn into_current(self) -> Config { - Config { - gfx_mode: GfxVendors::Hybrid, - gfx_tmp_mode: None, - gfx_managed: self.gfx_managed, - gfx_vfio_enable: false, - bat_charge_limit: self.bat_charge_limit, - } - } -} - #[derive(Deserialize, Serialize)] pub struct ConfigV352 { pub gfx_mode: GfxVendors, @@ -101,10 +22,6 @@ pub struct ConfigV352 { impl ConfigV352 { pub(crate) fn into_current(self) -> Config { Config { - gfx_mode: GfxVendors::Hybrid, - gfx_tmp_mode: None, - gfx_managed: self.gfx_managed, - gfx_vfio_enable: false, bat_charge_limit: self.bat_charge_limit, } } diff --git a/daemon/src/ctrl_anime/config.rs b/daemon/src/ctrl_anime/config.rs new file mode 100644 index 00000000..e189ad8e --- /dev/null +++ b/daemon/src/ctrl_anime/config.rs @@ -0,0 +1,257 @@ +use crate::VERSION; +use log::{error, info, warn}; +use rog_anime::Fade; +use rog_anime::{error::AnimeError, ActionData, ActionLoader, AnimTime, Vec2}; +use serde_derive::{Deserialize, Serialize}; +use std::fs::{File, OpenOptions}; +use std::io::{Read, Write}; +use std::time::Duration; + +pub static ANIME_CONFIG_PATH: &str = "/etc/asusd/anime.conf"; +pub static ANIME_CACHE_PATH: &str = "/etc/asusd/anime-cache.conf"; + +#[derive(Deserialize, Serialize)] +pub struct AnimeConfigV341 { + pub system: Option, + pub boot: Option, + pub suspend: Option, + pub shutdown: Option, +} + +impl AnimeConfigV341 { + pub(crate) fn into_current(self) -> AnimeConfig { + AnimeConfig { + system: if let Some(ani) = self.system { + vec![ani] + } else { + vec![] + }, + boot: if let Some(ani) = self.boot { + vec![ani] + } else { + vec![] + }, + wake: if let Some(ani) = self.suspend { + vec![ani] + } else { + vec![] + }, + shutdown: if let Some(ani) = self.shutdown { + vec![ani] + } else { + vec![] + }, + brightness: 1.0, + awake_enabled: true, + boot_anim_enabled: true, + } + } +} + +#[derive(Deserialize, Serialize)] +pub struct AnimeConfigV352 { + pub system: Vec, + pub boot: Vec, + pub wake: Vec, + pub shutdown: Vec, + pub brightness: f32, +} + +impl AnimeConfigV352 { + pub(crate) fn into_current(self) -> AnimeConfig { + AnimeConfig { + system: self.system, + boot: self.boot, + wake: self.wake, + shutdown: self.shutdown, + brightness: 1.0, + awake_enabled: true, + boot_anim_enabled: true, + } + } +} + +#[derive(Deserialize, Serialize, Default)] +pub struct AnimeConfigCached { + pub system: Vec, + pub boot: Vec, + pub wake: Vec, + pub shutdown: Vec, +} + +impl AnimeConfigCached { + pub fn init_from_config(&mut self, config: &AnimeConfig) -> Result<(), AnimeError> { + let mut sys = Vec::with_capacity(config.system.len()); + for ani in config.system.iter() { + sys.push(ActionData::from_anime_action(ani)?); + } + self.system = sys; + + let mut boot = Vec::with_capacity(config.boot.len()); + for ani in config.boot.iter() { + boot.push(ActionData::from_anime_action(ani)?); + } + self.boot = boot; + + let mut wake = Vec::with_capacity(config.wake.len()); + for ani in config.wake.iter() { + wake.push(ActionData::from_anime_action(ani)?); + } + self.wake = wake; + + let mut shutdown = Vec::with_capacity(config.shutdown.len()); + for ani in config.shutdown.iter() { + shutdown.push(ActionData::from_anime_action(ani)?); + } + self.shutdown = shutdown; + Ok(()) + } +} + +/// Config for base system actions for the anime display +#[derive(Deserialize, Serialize)] +pub struct AnimeConfig { + pub system: Vec, + pub boot: Vec, + pub wake: Vec, + pub shutdown: Vec, + pub brightness: f32, + pub awake_enabled: bool, + pub boot_anim_enabled: bool, +} + +impl Default for AnimeConfig { + fn default() -> Self { + AnimeConfig { + system: Vec::new(), + boot: Vec::new(), + wake: Vec::new(), + shutdown: Vec::new(), + brightness: 1.0, + awake_enabled: true, + boot_anim_enabled: true, + } + } +} + +impl AnimeConfig { + /// `load` will attempt to read the config, and panic if the dir is missing + pub fn load() -> Self { + let mut file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(&ANIME_CONFIG_PATH) + .unwrap_or_else(|_| { + panic!( + "The file {} or directory /etc/asusd/ is missing", + ANIME_CONFIG_PATH + ) + }); // okay to cause panic here + let mut buf = String::new(); + if let Ok(read_len) = file.read_to_string(&mut buf) { + if read_len == 0 { + return AnimeConfig::create_default(&mut file); + } else { + if let Ok(data) = serde_json::from_str(&buf) { + return data; + } else if let Ok(data) = serde_json::from_str::(&buf) { + let config = data.into_current(); + config.write(); + info!("Updated config version to: {}", VERSION); + return config; + } else if let Ok(data) = serde_json::from_str::(&buf) { + let config = data.into_current(); + config.write(); + info!("Updated config version to: {}", VERSION); + return config; + } + AnimeConfig::write_backup(buf); + warn!( + "Could not deserialise {}. Backed up as *-old", + ANIME_CONFIG_PATH + ); + } + } + AnimeConfig::create_default(&mut file) + } + + fn create_default(file: &mut File) -> Self { + // create a default config here + let config = AnimeConfig { + system: vec![], + boot: vec![ActionLoader::ImageAnimation { + file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(), + scale: 0.9, + angle: 0.65, + translation: Vec2::default(), + brightness: 1.0, + time: AnimTime::Fade(Fade::new( + Duration::from_secs(2), + Some(Duration::from_secs(2)), + Duration::from_secs(2), + )), + }], + wake: vec![ActionLoader::ImageAnimation { + file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(), + scale: 0.9, + angle: 0.65, + translation: Vec2::default(), + brightness: 1.0, + time: AnimTime::Fade(Fade::new( + Duration::from_secs(2), + Some(Duration::from_secs(2)), + Duration::from_secs(2), + )), + }], + shutdown: 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, + }], + brightness: 1.0, + awake_enabled: true, + boot_anim_enabled: true, + }; + // Should be okay to unwrap this as is since it is a Default + let json = serde_json::to_string_pretty(&config).unwrap(); + file.write_all(json.as_bytes()) + .unwrap_or_else(|_| panic!("Could not write {}", ANIME_CONFIG_PATH)); + config + } + + pub fn read(&mut self) { + let mut file = OpenOptions::new() + .read(true) + .open(&ANIME_CONFIG_PATH) + .unwrap_or_else(|err| panic!("Error reading {}: {}", ANIME_CONFIG_PATH, err)); + let mut buf = String::new(); + if let Ok(l) = file.read_to_string(&mut buf) { + if l == 0 { + warn!("File is empty {}", ANIME_CONFIG_PATH); + } else { + let x: AnimeConfig = serde_json::from_str(&buf) + .unwrap_or_else(|_| panic!("Could not deserialise {}", ANIME_CONFIG_PATH)); + *self = x; + } + } + } + + pub fn write(&self) { + let mut file = File::create(ANIME_CONFIG_PATH).expect("Couldn't overwrite config"); + let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed"); + file.write_all(json.as_bytes()) + .unwrap_or_else(|err| error!("Could not write config: {}", err)); + } + + fn write_backup(buf: String) { + let mut path = ANIME_CONFIG_PATH.to_string(); + path.push_str("-old"); + let mut file = File::create(&path).expect("Couldn't overwrite config"); + file.write_all(buf.as_bytes()) + .unwrap_or_else(|err| error!("Could not write config: {}", err)); + } +} diff --git a/daemon/src/ctrl_anime/mod.rs b/daemon/src/ctrl_anime/mod.rs new file mode 100644 index 00000000..61c603a0 --- /dev/null +++ b/daemon/src/ctrl_anime/mod.rs @@ -0,0 +1,329 @@ +pub mod config; +pub mod zbus; + +use ::zbus::Connection; +use log::{error, info, warn}; +use logind_zbus::ManagerProxy; +use rog_anime::{ + usb::{ + pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on, pkts_for_init, PROD_ID, + VENDOR_ID, + }, + ActionData, AnimeDataBuffer, AnimePacketType, ANIME_DATA_LEN, +}; +use rog_types::supported::AnimeSupportedFunctions; +use rusb::{Device, DeviceHandle}; +use std::{ + error::Error, + sync::{Arc, Mutex}, + thread::sleep, +}; +use std::{ + sync::atomic::{AtomicBool, Ordering}, + time::Duration, +}; + +use crate::{error::RogError, GetSupported}; + +use self::config::{AnimeConfig, AnimeConfigCached}; + +impl GetSupported for CtrlAnime { + type A = AnimeSupportedFunctions; + + fn get_supported() -> Self::A { + AnimeSupportedFunctions(CtrlAnime::get_device(VENDOR_ID, PROD_ID).is_ok()) + } +} + +pub struct CtrlAnime { + handle: DeviceHandle, + cache: AnimeConfigCached, + config: AnimeConfig, + // set to force thread to exit + thread_exit: Arc, + // Set to false when the thread exits + thread_running: Arc, +} + +impl CtrlAnime { + #[inline] + pub fn new(config: AnimeConfig) -> Result> { + // We don't expect this ID to ever change + let device = CtrlAnime::get_device(0x0b05, 0x193b)?; + + let mut device = device.open()?; + device.reset()?; + + device.set_auto_detach_kernel_driver(true).map_err(|err| { + error!("Auto-detach kernel driver failed: {}", err); + err + })?; + + device.claim_interface(0).map_err(|err| { + error!("Could not claim device interface: {}", err); + err + })?; + + info!("Device has an AniMe Matrix display"); + let mut cache = AnimeConfigCached::default(); + cache.init_from_config(&config)?; + + let ctrl = CtrlAnime { + handle: device, + cache, + config, + thread_exit: Arc::new(AtomicBool::new(false)), + thread_running: Arc::new(AtomicBool::new(false)), + }; + ctrl.do_initialization(); + + Ok(ctrl) + } + + fn get_device(vendor: u16, product: u16) -> Result, rusb::Error> { + for device in rusb::devices()?.iter() { + let device_desc = device.device_descriptor()?; + if device_desc.vendor_id() == vendor && device_desc.product_id() == product { + return Ok(device); + } + } + Err(rusb::Error::NoDevice) + } + + /// Start an action thread. This is classed as a singleton and there should be only + /// one running - so the thread uses atomics to signal run/exit. + /// + /// Because this also writes to the usb device, other write tries (display only) *must* + /// get the mutex lock and set the thread_exit atomic. + fn run_thread(inner: Arc>, actions: Vec, mut once: bool) { + if actions.is_empty() { + warn!("AniMe system actions was empty"); + return; + } + // Loop rules: + // - Lock the mutex **only when required**. That is, the lock must be held for the shortest duration possible. + // - An AtomicBool used for thread exit should be checked in every loop, including nested + + // The only reason for this outer thread is to prevent blocking while waiting for the + // next spawned thread to exit + std::thread::Builder::new() + .name("AniMe system thread start".into()) + .spawn(move || { + info!("AniMe system thread started"); + // Getting copies of these Atomics is done *in* the thread to ensure + // we don't block other threads/main + let thread_exit; + let thread_running; + // First two loops are to ensure we *do* aquire a lock on the mutex + // The reason the loop is required is because the USB writes can block + // for up to 10ms. We can't fail to get the atomics. + loop { + if let Ok(lock) = inner.try_lock() { + thread_exit = lock.thread_exit.clone(); + thread_running = lock.thread_running.clone(); + // Make any running loop exit first + thread_exit.store(true, Ordering::SeqCst); + break; + } + } + + loop { + // wait for other threads to set not running so we know they exited + if !thread_running.load(Ordering::SeqCst) { + thread_exit.store(false, Ordering::SeqCst); + info!("AniMe forced a thread to exit"); + break; + } + } + + 'main: loop { + if thread_exit.load(Ordering::SeqCst) { + break 'main; + } + for action in actions.iter() { + match action { + ActionData::Animation(frames) => { + rog_anime::run_animation(frames, thread_exit.clone(), &|frame| { + if let Ok(lock) = inner.try_lock() { + lock.write_data_buffer(frame); + } + }) + .unwrap(); + + if thread_exit.load(Ordering::SeqCst) { + break 'main; + } + } + ActionData::Image(image) => { + once = false; + if let Ok(lock) = inner.try_lock() { + lock.write_data_buffer(image.as_ref().clone()) + } + } + ActionData::Pause(duration) => sleep(*duration), + ActionData::AudioEq => {} + ActionData::SystemInfo => {} + ActionData::TimeDate => {} + ActionData::Matrix => {} + } + } + if once || actions.is_empty() { + break 'main; + } + } + // Clear the display on exit + if let Ok(lock) = inner.try_lock() { + let data = AnimeDataBuffer::from_vec([0u8; ANIME_DATA_LEN].to_vec()); + lock.write_data_buffer(data); + } + // Loop ended, set the atmonics + thread_exit.store(false, Ordering::SeqCst); + thread_running.store(false, Ordering::SeqCst); + info!("AniMe system thread exited"); + }) + .map(|err| info!("AniMe system thread: {:?}", err)) + .ok(); + } + + fn write_bytes(&self, message: &[u8]) { + match self.handle.write_control( + 0x21, // request_type + 0x09, // request + 0x35e, // value + 0x00, // index + message, + Duration::from_millis(200), + ) { + Ok(_) => {} + Err(err) => match err { + rusb::Error::Timeout => {} + _ => error!("Failed to write to led interrupt: {}", err), + }, + } + } + + /// Write only a data packet. This will modify the leds brightness using the + /// global brightness set in config. + fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) { + for led in buffer.get_mut()[7..].iter_mut() { + let mut bright = *led as f32 * self.config.brightness; + if bright > 254.0 { + bright = 254.0; + } + *led = bright as u8; + } + let data = AnimePacketType::from(buffer); + for row in data.iter() { + self.write_bytes(row); + } + self.write_bytes(&pkt_for_flush()); + } + + fn do_initialization(&self) { + let pkts = pkts_for_init(); + self.write_bytes(&pkts[0]); + self.write_bytes(&pkts[1]); + } +} + +pub struct CtrlAnimeTask<'a> { + inner: Arc>, + _c: Connection, + manager: ManagerProxy<'a>, +} + +impl<'a> CtrlAnimeTask<'a> { + pub fn new(inner: Arc>) -> Self { + let connection = Connection::new_system().unwrap(); + + let manager = ManagerProxy::new(&connection).unwrap(); + + let c1 = inner.clone(); + // Run this action when the system starts shutting down + manager + .connect_prepare_for_shutdown(move |shutdown| { + if shutdown { + 'outer: loop { + if let Ok(lock) = c1.try_lock() { + lock.thread_exit.store(true, Ordering::SeqCst); + CtrlAnime::run_thread(c1.clone(), lock.cache.shutdown.clone(), false); + break 'outer; + } + } + } + Ok(()) + }) + .map_err(|err| { + warn!("CtrlAnimeTask: new() {}", err); + err + }) + .ok(); + + let c1 = inner.clone(); + // Run this action when the system wakes up from sleep + manager + .connect_prepare_for_sleep(move |sleep| { + if !sleep { + // wait a fraction for things to wake up properly + std::thread::sleep(Duration::from_millis(100)); + 'outer: loop { + if let Ok(lock) = c1.try_lock() { + lock.thread_exit.store(true, Ordering::SeqCst); + CtrlAnime::run_thread(c1.clone(), lock.cache.wake.clone(), true); + break 'outer; + } + } + } + Ok(()) + }) + .map_err(|err| { + warn!("CtrlAnimeTask: new() {}", err); + err + }) + .ok(); + + Self { + inner, + _c: connection, + manager, + } + } +} + +impl<'a> crate::CtrlTask for CtrlAnimeTask<'a> { + fn do_task(&self) -> Result<(), RogError> { + if let Ok(mut lock) = self.inner.try_lock() { + // Refresh the config and cache incase the user has edited it + let config = AnimeConfig::load(); + lock.cache + .init_from_config(&config) + .map_err(|err| { + warn!("CtrlAnimeTask: do_task {}", err); + err + }) + .ok(); + } + + // Check for signals on each task iteration, this will run the callbacks + // if any signal is recieved + self.manager.next_signal()?; + Ok(()) + } +} + +pub struct CtrlAnimeReloader(pub Arc>); + +impl crate::Reloadable for CtrlAnimeReloader { + fn reload(&mut self) -> Result<(), RogError> { + if let Ok(lock) = self.0.try_lock() { + lock.write_bytes(&pkt_for_set_on(lock.config.awake_enabled)); + lock.write_bytes(&pkt_for_apply()); + lock.write_bytes(&pkt_for_set_boot(lock.config.boot_anim_enabled)); + lock.write_bytes(&pkt_for_apply()); + + let action = lock.cache.boot.clone(); + CtrlAnime::run_thread(self.0.clone(), action, true); + } + Ok(()) + } +} diff --git a/daemon/src/ctrl_anime/zbus.rs b/daemon/src/ctrl_anime/zbus.rs new file mode 100644 index 00000000..f5e28758 --- /dev/null +++ b/daemon/src/ctrl_anime/zbus.rs @@ -0,0 +1,140 @@ +use std::sync::{Arc, Mutex}; + +use log::warn; +use rog_anime::{ + usb::{pkt_for_apply, pkt_for_set_boot, pkt_for_set_on}, + AnimeDataBuffer, AnimePowerStates, +}; +use zbus::dbus_interface; +use zvariant::ObjectPath; + +use std::sync::atomic::Ordering; + +use super::CtrlAnime; + +pub struct CtrlAnimeZbus(pub Arc>); + +/// The struct with the main dbus methods requires this trait +impl crate::ZbusAdd for CtrlAnimeZbus { + fn add_to_server(self, server: &mut zbus::ObjectServer) { + server + .at( + &ObjectPath::from_str_unchecked("/org/asuslinux/Anime"), + self, + ) + .map_err(|err| { + warn!("CtrlAnimeDisplay: add_to_server {}", err); + err + }) + .ok(); + } +} + +// None of these calls can be guarnateed to succeed unless we loop until okay +// If the try_lock *does* succeed then any other thread trying to lock will not grab it +// until we finish. +#[dbus_interface(name = "org.asuslinux.Daemon")] +impl CtrlAnimeZbus { + /// Writes a data stream of length. Will force system thread to exit until it is restarted + fn write(&self, input: AnimeDataBuffer) { + 'outer: loop { + if let Ok(lock) = self.0.try_lock() { + lock.thread_exit.store(true, Ordering::SeqCst); + lock.write_data_buffer(input); + break 'outer; + } + } + } + + /// Set the global AniMe brightness + fn set_brightness(&self, bright: f32) { + 'outer: loop { + if let Ok(mut lock) = self.0.try_lock() { + let mut bright = bright; + if bright < 0.0 { + bright = 0.0 + } else if bright > 254.0 { + bright = 254.0; + } + lock.config.brightness = bright; + lock.config.write(); + break 'outer; + } + } + } + + /// Set whether the AniMe is displaying images/data + fn set_on_off(&self, status: bool) { + 'outer: loop { + if let Ok(mut lock) = self.0.try_lock() { + lock.write_bytes(&pkt_for_set_on(status)); + lock.config.awake_enabled = status; + lock.config.write(); + + let states = AnimePowerStates { + enabled: lock.config.awake_enabled, + boot_anim_enabled: lock.config.boot_anim_enabled, + }; + self.notify_power_states(&states) + .unwrap_or_else(|err| warn!("{}", err)); + break 'outer; + } + } + } + + /// Set whether the AniMe will show boot, suspend, or off animations + fn set_boot_on_off(&self, on: bool) { + 'outer: loop { + if let Ok(mut lock) = self.0.try_lock() { + lock.write_bytes(&pkt_for_set_boot(on)); + lock.write_bytes(&pkt_for_apply()); + lock.config.boot_anim_enabled = on; + lock.config.write(); + + let states = AnimePowerStates { + enabled: lock.config.awake_enabled, + boot_anim_enabled: lock.config.boot_anim_enabled, + }; + self.notify_power_states(&states) + .unwrap_or_else(|err| warn!("{}", err)); + break 'outer; + } + } + } + + /// The main loop is the base system set action if the user isn't running + /// the user daemon + fn run_main_loop(&self, start: bool) { + if start { + 'outer: loop { + if let Ok(lock) = self.0.try_lock() { + lock.thread_exit.store(true, Ordering::SeqCst); + CtrlAnime::run_thread(self.0.clone(), lock.cache.system.clone(), false); + break 'outer; + } + } + } + } + + /// Get status of if the AniMe LEDs are on + #[dbus_interface(property)] + fn awake_enabled(&self) -> bool { + if let Ok(ctrl) = self.0.try_lock() { + return ctrl.config.awake_enabled; + } + true + } + + /// Get the status of if factory system-status animations are enabled + #[dbus_interface(property)] + fn boot_enabled(&self) -> bool { + if let Ok(ctrl) = self.0.try_lock() { + return ctrl.config.boot_anim_enabled; + } + true + } + + /// Notify listeners of the status of AniMe LED power and factory system-status animations + #[dbus_interface(signal)] + fn notify_power_states(&self, data: &AnimePowerStates) -> zbus::Result<()>; +} diff --git a/daemon/src/ctrl_aura/config.rs b/daemon/src/ctrl_aura/config.rs new file mode 100644 index 00000000..6e5d3778 --- /dev/null +++ b/daemon/src/ctrl_aura/config.rs @@ -0,0 +1,267 @@ +use crate::laptops::LaptopLedData; +use log::{error, info, warn}; +use rog_aura::{AuraEffect, AuraModeNum, AuraZone, LedBrightness}; +use serde_derive::{Deserialize, Serialize}; +use std::collections::BTreeMap; +use std::fs::{File, OpenOptions}; +use std::io::{Read, Write}; + +pub static AURA_CONFIG_PATH: &str = "/etc/asusd/aura.conf"; + +#[derive(Deserialize, Serialize)] +pub struct AuraConfigV320 { + pub brightness: u32, + pub current_mode: AuraModeNum, + pub builtins: BTreeMap, + pub multizone: Option, +} + +impl AuraConfigV320 { + pub(crate) fn into_current(self) -> AuraConfig { + AuraConfig { + brightness: ::from(self.brightness), + current_mode: self.current_mode, + builtins: self.builtins, + multizone: self.multizone, + awake_enabled: true, + sleep_anim_enabled: true, + } + } +} + +#[derive(Deserialize, Serialize)] +pub struct AuraConfigV352 { + pub brightness: LedBrightness, + pub current_mode: AuraModeNum, + pub builtins: BTreeMap, + pub multizone: Option, +} + +impl AuraConfigV352 { + pub(crate) fn into_current(self) -> AuraConfig { + AuraConfig { + brightness: self.brightness, + current_mode: self.current_mode, + builtins: self.builtins, + multizone: self.multizone, + awake_enabled: true, + sleep_anim_enabled: true, + } + } +} + +#[derive(Deserialize, Serialize)] +pub struct AuraConfig { + pub brightness: LedBrightness, + pub current_mode: AuraModeNum, + pub builtins: BTreeMap, + pub multizone: Option, + pub awake_enabled: bool, + pub sleep_anim_enabled: bool, +} + +impl Default for AuraConfig { + fn default() -> Self { + AuraConfig { + brightness: LedBrightness::Med, + current_mode: AuraModeNum::Static, + builtins: BTreeMap::new(), + multizone: None, + awake_enabled: true, + sleep_anim_enabled: true, + } + } +} + +impl AuraConfig { + /// `load` will attempt to read the config, and panic if the dir is missing + pub fn load(supported_led_modes: &LaptopLedData) -> Self { + let mut file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(&AURA_CONFIG_PATH) + .unwrap_or_else(|_| { + panic!( + "The file {} or directory /etc/asusd/ is missing", + AURA_CONFIG_PATH + ) + }); // okay to cause panic here + let mut buf = String::new(); + if let Ok(read_len) = file.read_to_string(&mut buf) { + if read_len == 0 { + return AuraConfig::create_default(&mut file, supported_led_modes); + } else { + if let Ok(data) = serde_json::from_str(&buf) { + return data; + } else if let Ok(data) = serde_json::from_str::(&buf) { + let config = data.into_current(); + config.write(); + info!("Updated AuraConfig version"); + return config; + } else if let Ok(data) = serde_json::from_str::(&buf) { + let config = data.into_current(); + config.write(); + info!("Updated AuraConfig version"); + return config; + } + warn!("Could not deserialise {}", AURA_CONFIG_PATH); + panic!("Please remove {} then restart asusd", AURA_CONFIG_PATH); + } + } + AuraConfig::create_default(&mut file, supported_led_modes) + } + + fn create_default(file: &mut File, support_data: &LaptopLedData) -> Self { + // create a default config here + let mut config = AuraConfig::default(); + + for n in &support_data.standard { + config + .builtins + .insert(*n, AuraEffect::default_with_mode(*n)); + } + + // Should be okay to unwrap this as is since it is a Default + let json = serde_json::to_string(&config).unwrap(); + file.write_all(json.as_bytes()) + .unwrap_or_else(|_| panic!("Could not write {}", AURA_CONFIG_PATH)); + config + } + + pub fn read(&mut self) { + let mut file = OpenOptions::new() + .read(true) + .open(&AURA_CONFIG_PATH) + .unwrap_or_else(|err| panic!("Error reading {}: {}", AURA_CONFIG_PATH, err)); + let mut buf = String::new(); + if let Ok(l) = file.read_to_string(&mut buf) { + if l == 0 { + warn!("File is empty {}", AURA_CONFIG_PATH); + } else { + let x: AuraConfig = serde_json::from_str(&buf) + .unwrap_or_else(|_| panic!("Could not deserialise {}", AURA_CONFIG_PATH)); + *self = x; + } + } + } + + pub fn write(&self) { + let mut file = File::create(AURA_CONFIG_PATH).expect("Couldn't overwrite config"); + let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed"); + file.write_all(json.as_bytes()) + .unwrap_or_else(|err| error!("Could not write config: {}", err)); + } + + /// Multipurpose, will accept AuraEffect with zones and put in the correct store + pub fn set_builtin(&mut self, effect: AuraEffect) { + match effect.zone() { + AuraZone::None => { + self.builtins.insert(*effect.mode(), effect); + } + _ => { + if let Some(multi) = self.multizone.as_mut() { + multi.set(effect) + } + } + } + } + + pub fn get_multizone(&self, aura_type: AuraModeNum) -> Option<&[AuraEffect; 4]> { + if let Some(multi) = &self.multizone { + if aura_type == AuraModeNum::Static { + return Some(multi.static_()); + } else if aura_type == AuraModeNum::Breathe { + return Some(multi.breathe()); + } + } + None + } +} + +#[derive(Deserialize, Serialize)] +pub struct AuraMultiZone { + static_: [AuraEffect; 4], + breathe: [AuraEffect; 4], +} + +impl AuraMultiZone { + pub fn set(&mut self, effect: AuraEffect) { + if effect.mode == AuraModeNum::Static { + match effect.zone { + AuraZone::None => {} + AuraZone::One => self.static_[0] = effect, + AuraZone::Two => self.static_[1] = effect, + AuraZone::Three => self.static_[2] = effect, + AuraZone::Four => self.static_[3] = effect, + } + } else if effect.mode == AuraModeNum::Breathe { + match effect.zone { + AuraZone::None => {} + AuraZone::One => self.breathe[0] = effect, + AuraZone::Two => self.breathe[1] = effect, + AuraZone::Three => self.breathe[2] = effect, + AuraZone::Four => self.breathe[3] = effect, + } + } + } + + pub fn static_(&self) -> &[AuraEffect; 4] { + &self.static_ + } + + pub fn breathe(&self) -> &[AuraEffect; 4] { + &self.breathe + } +} + +impl Default for AuraMultiZone { + fn default() -> Self { + Self { + static_: [ + AuraEffect { + mode: AuraModeNum::Static, + zone: AuraZone::One, + ..Default::default() + }, + AuraEffect { + mode: AuraModeNum::Static, + zone: AuraZone::Two, + ..Default::default() + }, + AuraEffect { + mode: AuraModeNum::Static, + zone: AuraZone::Three, + ..Default::default() + }, + AuraEffect { + mode: AuraModeNum::Static, + zone: AuraZone::Four, + ..Default::default() + }, + ], + breathe: [ + AuraEffect { + mode: AuraModeNum::Breathe, + zone: AuraZone::One, + ..Default::default() + }, + AuraEffect { + mode: AuraModeNum::Breathe, + zone: AuraZone::Two, + ..Default::default() + }, + AuraEffect { + mode: AuraModeNum::Breathe, + zone: AuraZone::Three, + ..Default::default() + }, + AuraEffect { + mode: AuraModeNum::Breathe, + zone: AuraZone::Four, + ..Default::default() + }, + ], + } + } +} diff --git a/daemon/src/ctrl_aura/controller.rs b/daemon/src/ctrl_aura/controller.rs new file mode 100644 index 00000000..5cef3651 --- /dev/null +++ b/daemon/src/ctrl_aura/controller.rs @@ -0,0 +1,395 @@ +// Only these two packets must be 17 bytes +static KBD_BRIGHT_PATH: &str = "/sys/class/leds/asus::kbd_backlight/brightness"; + +use crate::{ + error::RogError, + laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES}, + CtrlTask, +}; +use log::{info, warn}; +use logind_zbus::ManagerProxy; +use rog_aura::{ + usb::{ + LED_APPLY, LED_AWAKE_OFF_SLEEP_OFF, LED_AWAKE_OFF_SLEEP_ON, LED_AWAKE_ON_SLEEP_OFF, + LED_AWAKE_ON_SLEEP_ON, LED_SET, + }, + AuraEffect, LedBrightness, LED_MSG_LEN, +}; +use rog_types::supported::LedSupportedFunctions; +use std::io::{Read, Write}; +use std::path::Path; +use std::sync::Arc; +use std::sync::Mutex; +use std::{fs::OpenOptions, thread::spawn}; +use zbus::Connection; + +use crate::GetSupported; + +use super::config::AuraConfig; + +impl GetSupported for CtrlKbdLed { + type A = LedSupportedFunctions; + + fn get_supported() -> Self::A { + // let mode = <&str>::from(&::from(*mode)); + let multizone_led_mode = false; + let per_key_led_mode = false; + let laptop = LaptopLedData::get_data(); + let stock_led_modes = laptop.standard; + + LedSupportedFunctions { + brightness_set: CtrlKbdLed::get_kbd_bright_path().is_some(), + stock_led_modes, + multizone_led_mode, + per_key_led_mode, + } + } +} + +pub struct CtrlKbdLed { + pub led_node: Option, + pub bright_node: String, + pub supported_modes: LaptopLedData, + pub flip_effect_write: bool, + pub config: AuraConfig, +} + +pub struct CtrlKbdLedTask<'a> { + inner: Arc>, + _c: Connection, + manager: ManagerProxy<'a>, +} + +impl<'a> CtrlKbdLedTask<'a> { + pub fn new(inner: Arc>) -> Self { + let connection = Connection::new_system().unwrap(); + + let manager = ManagerProxy::new(&connection).unwrap(); + + let c1 = inner.clone(); + // Run this action when the system wakes up from sleep + manager + .connect_prepare_for_sleep(move |sleep| { + if !sleep { + let c1 = c1.clone(); + spawn(move || { + // wait a fraction for things to wake up properly + //std::thread::sleep(Duration::from_millis(100)); + loop { + if let Ok(ref mut lock) = c1.try_lock() { + lock.set_brightness(lock.config.brightness).ok(); + break; + } + } + }); + } + Ok(()) + }) + .map_err(|err| { + warn!("CtrlAnimeTask: new() {}", err); + err + }) + .ok(); + + Self { + inner, + _c: connection, + manager, + } + } + + fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> { + let mut file = OpenOptions::new() + .read(true) + .open(&lock.bright_node) + .map_err(|err| match err.kind() { + std::io::ErrorKind::NotFound => { + RogError::MissingLedBrightNode((&lock.bright_node).into(), err) + } + _ => RogError::Path((&lock.bright_node).into(), err), + })?; + let mut buf = [0u8; 1]; + file.read_exact(&mut buf) + .map_err(|err| RogError::Read("buffer".into(), err))?; + if let Some(num) = char::from(buf[0]).to_digit(10) { + if lock.config.brightness != num.into() { + lock.config.read(); + lock.config.brightness = num.into(); + lock.config.write(); + } + return Ok(()); + } + Err(RogError::ParseLed) + } +} + +impl<'a> CtrlTask for CtrlKbdLedTask<'a> { + fn do_task(&self) -> Result<(), RogError> { + self.manager.next_signal()?; + if let Ok(ref mut lock) = self.inner.try_lock() { + return Self::update_config(lock); + } + Ok(()) + } +} + +pub struct CtrlKbdLedReloader(pub Arc>); + +impl crate::Reloadable for CtrlKbdLedReloader { + fn reload(&mut self) -> Result<(), RogError> { + if let Ok(mut ctrl) = self.0.try_lock() { + let current = ctrl.config.current_mode; + if let Some(mode) = ctrl.config.builtins.get(¤t).cloned() { + ctrl.do_command(mode).ok(); + } + + ctrl.set_states_enabled(ctrl.config.awake_enabled, ctrl.config.sleep_anim_enabled) + .map_err(|err| warn!("{}", err)) + .ok(); + } + Ok(()) + } +} + +pub struct CtrlKbdLedZbus(pub Arc>); + +impl CtrlKbdLedZbus { + pub fn new(inner: Arc>) -> Self { + Self(inner) + } +} + +impl CtrlKbdLed { + #[inline] + pub fn new(supported_modes: LaptopLedData, config: AuraConfig) -> Result { + // TODO: return error if *all* nodes are None + let mut led_node = None; + for prod in ASUS_KEYBOARD_DEVICES.iter() { + match Self::find_led_node(prod) { + Ok(node) => { + led_node = Some(node); + break; + } + Err(err) => warn!("led_node: {}", err), + } + } + + let bright_node = Self::get_kbd_bright_path(); + + if led_node.is_none() && bright_node.is_none() { + return Err(RogError::MissingFunction( + "All keyboard features missing, you may require a v5.11 series kernel or newer" + .into(), + )); + } + + if bright_node.is_none() { + return Err(RogError::MissingFunction( + "No brightness control, you may require a v5.11 series kernel or newer".into(), + )); + } + + let ctrl = CtrlKbdLed { + led_node, + bright_node: bright_node.unwrap(), // If was none then we already returned above + supported_modes, + flip_effect_write: false, + config, + }; + Ok(ctrl) + } + + fn get_kbd_bright_path() -> Option { + if Path::new(KBD_BRIGHT_PATH).exists() { + return Some(KBD_BRIGHT_PATH.to_string()); + } + None + } + + pub(super) fn get_brightness(&self) -> Result { + let mut file = OpenOptions::new() + .read(true) + .open(&self.bright_node) + .map_err(|err| match err.kind() { + std::io::ErrorKind::NotFound => { + RogError::MissingLedBrightNode((&self.bright_node).into(), err) + } + _ => RogError::Path((&self.bright_node).into(), err), + })?; + let mut buf = [0u8; 1]; + file.read_exact(&mut buf) + .map_err(|err| RogError::Read("buffer".into(), err))?; + Ok(buf[0]) + } + + pub(super) fn set_brightness(&self, brightness: LedBrightness) -> Result<(), RogError> { + let path = Path::new(&self.bright_node); + let mut file = + OpenOptions::new() + .write(true) + .open(&path) + .map_err(|err| match err.kind() { + std::io::ErrorKind::NotFound => { + RogError::MissingLedBrightNode((&self.bright_node).into(), err) + } + _ => RogError::Path((&self.bright_node).into(), err), + })?; + file.write_all(&[brightness.as_char_code()]) + .map_err(|err| RogError::Read("buffer".into(), err))?; + Ok(()) + } + + /// Set if awake/on LED active, and/or sleep animation active + pub(super) fn set_states_enabled(&self, awake: bool, sleep: bool) -> Result<(), RogError> { + let bytes = if awake && sleep { + LED_AWAKE_ON_SLEEP_ON + } else if awake && !sleep { + LED_AWAKE_ON_SLEEP_OFF + } else if !awake && sleep { + LED_AWAKE_OFF_SLEEP_ON + } else if !awake && !sleep { + LED_AWAKE_OFF_SLEEP_OFF + } else { + LED_AWAKE_ON_SLEEP_ON + }; + self.write_bytes(&bytes)?; + self.write_bytes(&LED_SET)?; + // Changes won't persist unless apply is set + self.write_bytes(&LED_APPLY)?; + Ok(()) + } + + fn find_led_node(id_product: &str) -> Result { + let mut enumerator = udev::Enumerator::new().map_err(|err| { + warn!("{}", err); + RogError::Udev("enumerator failed".into(), err) + })?; + enumerator.match_subsystem("hidraw").map_err(|err| { + warn!("{}", err); + RogError::Udev("match_subsystem failed".into(), err) + })?; + + for device in enumerator.scan_devices().map_err(|err| { + warn!("{}", err); + RogError::Udev("scan_devices failed".into(), err) + })? { + if let Some(parent) = device + .parent_with_subsystem_devtype("usb", "usb_device") + .map_err(|err| { + warn!("{}", err); + RogError::Udev("parent_with_subsystem_devtype failed".into(), err) + })? + { + if parent + .attribute_value("idProduct") + .ok_or_else(|| RogError::NotFound("LED idProduct".into()))? + == id_product + { + if let Some(dev_node) = device.devnode() { + info!("Using device at: {:?} for LED control", dev_node); + return Ok(dev_node.to_string_lossy().to_string()); + } + } + } + } + Err(RogError::MissingFunction( + "ASUS LED device node not found".into(), + )) + } + + pub(crate) fn do_command(&mut self, mode: AuraEffect) -> Result<(), RogError> { + self.set_and_save(mode) + } + + /// Should only be used if the bytes you are writing are verified correct + #[inline] + fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> { + if let Some(led_node) = &self.led_node { + if let Ok(mut file) = OpenOptions::new().write(true).open(led_node) { + // println!("write: {:02x?}", &message); + return file + .write_all(message) + .map_err(|err| RogError::Write("write_bytes".into(), err)); + } + } + Err(RogError::NotSupported) + } + + /// Write an effect block + #[inline] + fn _write_effect(&mut self, effect: &[Vec]) -> Result<(), RogError> { + if self.flip_effect_write { + for row in effect.iter().rev() { + self.write_bytes(row)?; + } + } else { + for row in effect.iter() { + self.write_bytes(row)?; + } + } + self.flip_effect_write = !self.flip_effect_write; + Ok(()) + } + + /// Used to set a builtin mode and save the settings for it + /// + /// This needs to be universal so that settings applied by dbus stick + #[inline] + fn set_and_save(&mut self, mode: AuraEffect) -> Result<(), RogError> { + self.config.read(); + self.write_mode(&mode)?; + self.config.current_mode = *mode.mode(); + self.config.set_builtin(mode); + self.config.write(); + Ok(()) + } + + #[inline] + pub(super) fn toggle_mode(&mut self, reverse: bool) -> Result<(), RogError> { + let current = self.config.current_mode; + if let Some(idx) = self + .supported_modes + .standard + .iter() + .position(|v| *v == current) + { + let mut idx = idx; + // goes past end of array + if reverse { + if idx == 0 { + idx = self.supported_modes.standard.len() - 1; + } else { + idx -= 1; + } + } else { + idx += 1; + if idx == self.supported_modes.standard.len() { + idx = 0; + } + } + let next = self.supported_modes.standard[idx]; + + self.config.read(); + if let Some(data) = self.config.builtins.get(&next) { + self.write_mode(data)?; + self.config.current_mode = next; + } + self.config.write(); + } + + Ok(()) + } + + #[inline] + fn write_mode(&self, mode: &AuraEffect) -> Result<(), RogError> { + if !self.supported_modes.standard.contains(mode.mode()) { + return Err(RogError::NotSupported); + } + let bytes: [u8; LED_MSG_LEN] = mode.into(); + self.write_bytes(&bytes)?; + self.write_bytes(&LED_SET)?; + // Changes won't persist unless apply is set + self.write_bytes(&LED_APPLY)?; + Ok(()) + } +} diff --git a/daemon/src/ctrl_aura/mod.rs b/daemon/src/ctrl_aura/mod.rs new file mode 100644 index 00000000..85a4d229 --- /dev/null +++ b/daemon/src/ctrl_aura/mod.rs @@ -0,0 +1,3 @@ +pub mod config; +pub mod controller; +pub mod zbus; diff --git a/daemon/src/ctrl_aura/zbus.rs b/daemon/src/ctrl_aura/zbus.rs new file mode 100644 index 00000000..d96fa095 --- /dev/null +++ b/daemon/src/ctrl_aura/zbus.rs @@ -0,0 +1,165 @@ +use log::{error, warn}; +use rog_aura::{AuraEffect, LedBrightness, LedPowerStates}; +use zbus::dbus_interface; +use zvariant::ObjectPath; + +use super::controller::CtrlKbdLedZbus; + +impl crate::ZbusAdd for CtrlKbdLedZbus { + fn add_to_server(self, server: &mut zbus::ObjectServer) { + server + .at(&ObjectPath::from_str_unchecked("/org/asuslinux/Led"), self) + .map_err(|err| { + error!("DbusKbdLed: add_to_server {}", err); + }) + .ok(); + } +} + +/// The main interface for changing, reading, or notfying signals +/// +/// LED commands are split between Brightness, Modes, Per-Key +#[dbus_interface(name = "org.asuslinux.Daemon")] +impl CtrlKbdLedZbus { + /// Set the keyboard brightness level (0-3) + fn set_brightness(&mut self, brightness: LedBrightness) { + if let Ok(ctrl) = self.0.try_lock() { + ctrl.set_brightness(brightness) + .map_err(|err| warn!("{}", err)) + .ok(); + } + } + + /// Set the keyboard LED to enabled while the device is awake + fn set_awake_enabled(&mut self, enabled: bool) { + if let Ok(mut ctrl) = self.0.try_lock() { + ctrl.set_states_enabled(enabled, ctrl.config.sleep_anim_enabled) + .map_err(|err| warn!("{}", err)) + .ok(); + ctrl.config.awake_enabled = enabled; + ctrl.config.write(); + + let states = LedPowerStates { + enabled: ctrl.config.awake_enabled, + sleep_anim_enabled: ctrl.config.sleep_anim_enabled, + }; + self.notify_power_states(&states) + .unwrap_or_else(|err| warn!("{}", err)); + } + } + + /// Set the keyboard LED suspend animation to enabled while the device is suspended + fn set_sleep_enabled(&mut self, enabled: bool) { + if let Ok(mut ctrl) = self.0.try_lock() { + ctrl.set_states_enabled(ctrl.config.awake_enabled, enabled) + .map_err(|err| warn!("{}", err)) + .ok(); + ctrl.config.sleep_anim_enabled = enabled; + ctrl.config.write(); + let states = LedPowerStates { + enabled: ctrl.config.awake_enabled, + sleep_anim_enabled: ctrl.config.sleep_anim_enabled, + }; + self.notify_power_states(&states) + .unwrap_or_else(|err| warn!("{}", err)); + } + } + + fn set_led_mode(&mut self, effect: AuraEffect) { + if let Ok(mut ctrl) = self.0.try_lock() { + match ctrl.do_command(effect) { + Ok(_) => { + if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { + self.notify_led(mode.clone()) + .unwrap_or_else(|err| warn!("{}", err)); + } + } + Err(err) => { + warn!("{}", err); + } + } + } + } + + fn next_led_mode(&self) { + if let Ok(mut ctrl) = self.0.try_lock() { + ctrl.toggle_mode(false) + .unwrap_or_else(|err| warn!("{}", err)); + + if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { + self.notify_led(mode.clone()) + .unwrap_or_else(|err| warn!("{}", err)); + } + } + } + + fn prev_led_mode(&self) { + if let Ok(mut ctrl) = self.0.try_lock() { + ctrl.toggle_mode(true) + .unwrap_or_else(|err| warn!("{}", err)); + + if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { + self.notify_led(mode.clone()) + .unwrap_or_else(|err| warn!("{}", err)); + } + } + } + + #[dbus_interface(property)] + fn awake_enabled(&self) -> bool { + if let Ok(ctrl) = self.0.try_lock() { + return ctrl.config.awake_enabled; + } + true + } + + #[dbus_interface(property)] + fn sleep_enabled(&self) -> bool { + if let Ok(ctrl) = self.0.try_lock() { + return ctrl.config.sleep_anim_enabled; + } + true + } + + /// Return the current mode data + #[dbus_interface(property)] + fn led_mode(&self) -> String { + if let Ok(ctrl) = self.0.try_lock() { + if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { + if let Ok(json) = serde_json::to_string(&mode) { + return json; + } + } + } + warn!("SetKeyBacklight could not deserialise"); + "SetKeyBacklight could not deserialise".to_string() + } + + /// Return a list of available modes + #[dbus_interface(property)] + fn led_modes(&self) -> String { + if let Ok(ctrl) = self.0.try_lock() { + if let Ok(json) = serde_json::to_string(&ctrl.config.builtins) { + return json; + } + } + warn!("SetKeyBacklight could not deserialise"); + "SetKeyBacklight could not serialise".to_string() + } + + /// Return the current LED brightness + #[dbus_interface(property)] + fn led_brightness(&self) -> i8 { + if let Ok(ctrl) = self.0.try_lock() { + return ctrl.get_brightness().map(|n| n as i8).unwrap_or(-1); + } + warn!("SetKeyBacklight could not serialise"); + -1 + } + + #[dbus_interface(signal)] + fn notify_led(&self, data: AuraEffect) -> zbus::Result<()>; + + #[dbus_interface(signal)] + fn notify_power_states(&self, data: &LedPowerStates) -> zbus::Result<()>; +} diff --git a/daemon/src/ctrl_profiles/config.rs b/daemon/src/ctrl_profiles/config.rs new file mode 100644 index 00000000..9907ba5f --- /dev/null +++ b/daemon/src/ctrl_profiles/config.rs @@ -0,0 +1,92 @@ +use log::{error, warn}; +use rog_profiles::error::ProfileError; +use rog_profiles::{FanCurves, Profile}; +use serde_derive::{Deserialize, Serialize}; +use std::fs::{File, OpenOptions}; +use std::io::{Read, Write}; + +#[derive(Deserialize, Serialize, Debug)] +pub struct ProfileConfig { + #[serde(skip)] + config_path: String, + /// For restore on boot + pub active: Profile, + /// States to restore + pub fan_curves: Option, +} + +impl ProfileConfig { + fn new(config_path: String) -> Result { + let mut platform = ProfileConfig { + config_path, + active: Profile::Balanced, + fan_curves: None, + }; + + if !Profile::is_platform_profile_supported() { + return Err(ProfileError::NotSupported); + } + + if FanCurves::is_fan_curves_supported() { + let mut curves = FanCurves::default(); + curves.update_from_platform(); + platform.fan_curves = Some(curves); + } + Ok(platform) + } +} + +impl ProfileConfig { + pub fn load(config_path: String) -> Self { + let mut file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(&config_path) + .unwrap_or_else(|_| panic!("The directory /etc/asusd/ is missing")); // okay to cause panic here + let mut buf = String::new(); + let mut config; + if let Ok(read_len) = file.read_to_string(&mut buf) { + if read_len == 0 { + config = Self::new(config_path).unwrap(); + } else if let Ok(data) = serde_json::from_str(&buf) { + config = data; + config.config_path = config_path; + } else { + warn!("Could not deserialise {}", config_path); + panic!("Please remove {} then restart service", config_path); + } + } else { + config = Self::new(config_path).unwrap() + } + config.write(); + config + } + + pub fn read(&mut self) { + let mut file = OpenOptions::new() + .read(true) + .open(&self.config_path) + .unwrap_or_else(|err| panic!("Error reading {}: {}", self.config_path, err)); + + let mut buf = String::new(); + if let Ok(l) = file.read_to_string(&mut buf) { + if l == 0 { + warn!("File is empty {}", self.config_path); + } else { + let mut data: ProfileConfig = serde_json::from_str(&buf) + .unwrap_or_else(|_| panic!("Could not deserialise {}", self.config_path)); + // copy over serde skipped values + data.config_path = self.config_path.clone(); + *self = data; + } + } + } + + pub fn write(&self) { + let mut file = File::create(&self.config_path).expect("Couldn't overwrite config"); + let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed"); + file.write_all(json.as_bytes()) + .unwrap_or_else(|err| error!("Could not write config: {}", err)); + } +} diff --git a/daemon/src/daemon.rs b/daemon/src/daemon.rs index d04d285f..808df1a0 100644 --- a/daemon/src/daemon.rs +++ b/daemon/src/daemon.rs @@ -10,18 +10,20 @@ use daemon::ctrl_profiles::controller::CtrlPlatformTask; use daemon::{ config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported, }; -use daemon::{ctrl_anime::*, ctrl_gfx::controller::CtrlGraphics}; +use daemon::{ctrl_anime::*}; use daemon::{ ctrl_profiles::{controller::CtrlPlatformProfile, zbus::ProfileZbus}, laptops::LaptopLedData, }; +use supergfxctl::config::GfxConfig; +use supergfxctl::controller::CtrlGraphics; +use supergfxctl::gfx_vendors::GfxVendors; use ::zbus::{fdo, Connection, ObjectServer}; use daemon::{CtrlTask, Reloadable, ZbusAdd}; use log::LevelFilter; use log::{error, info, warn}; use rog_dbus::DBUS_NAME; -use rog_types::gfx_vendors::GfxVendors; use std::env; use std::error::Error; use std::io::Write; @@ -32,6 +34,7 @@ use daemon::ctrl_rog_bios::CtrlRogBios; use zvariant::ObjectPath; static PROFILE_CONFIG_PATH: &str = "/etc/asusd/profile.conf"; +static GFX_CONFIG_PATH: &str = "/etc/asusd/supergfx.conf"; pub fn main() -> Result<(), Box> { let mut logger = env_logger::Builder::new(); @@ -81,9 +84,12 @@ fn start_daemon() -> Result<(), Box> { let mut object_server = ObjectServer::new(&connection); let config = Config::load(); - let enable_gfx_switching = config.gfx_managed; let config = Arc::new(Mutex::new(config)); + let gfx_config = GfxConfig::load(GFX_CONFIG_PATH.into()); + let enable_gfx_switching = gfx_config.gfx_managed; + let gfx_config = Arc::new(Mutex::new(gfx_config)); + supported.add_to_server(&mut object_server); match CtrlRogBios::new(config.clone()) { @@ -169,12 +175,12 @@ fn start_daemon() -> Result<(), Box> { // Graphics switching requires some checks on boot specifically for g-sync capable laptops if enable_gfx_switching { - match CtrlGraphics::new(config.clone()) { + match CtrlGraphics::new(gfx_config.clone()) { Ok(mut ctrl) => { // Need to check if a laptop has the dedicated gfx switch if CtrlRogBios::has_dedicated_gfx_toggle() { if let Ok(ded) = CtrlRogBios::get_gfx_mode() { - if let Ok(config) = config.lock() { + if let Ok(config) = gfx_config.lock() { if ded == 1 { warn!("Dedicated GFX toggle is on but driver mode is not nvidia \nSetting to nvidia driver mode"); let devices = ctrl.devices(); diff --git a/daemon/src/error.rs b/daemon/src/error.rs index c3052c8c..27384ef0 100644 --- a/daemon/src/error.rs +++ b/daemon/src/error.rs @@ -1,10 +1,9 @@ use rog_profiles::error::ProfileError; use rog_types::error::GraphicsError; +use supergfxctl::error::GfxError; use std::convert::From; use std::fmt; -use crate::ctrl_gfx::error::GfxError; - #[derive(Debug)] pub enum RogError { ParseVendor, @@ -24,7 +23,6 @@ pub enum RogError { Profiles(ProfileError), Initramfs(String), Modprobe(String), - Command(String, std::io::Error), Io(std::io::Error), Zbus(zbus::Error), } @@ -50,7 +48,6 @@ impl fmt::Display for RogError { RogError::Profiles(deets) => write!(f, "Profile error: {}", deets), RogError::Initramfs(detail) => write!(f, "Initiramfs error: {}", detail), RogError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail), - RogError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error), RogError::Io(detail) => write!(f, "std::io error: {}", detail), RogError::Zbus(detail) => write!(f, "Zbus error: {}", detail), } diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index f87a5cf4..82984d50 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -8,8 +8,6 @@ pub mod ctrl_anime; pub mod ctrl_aura; /// Control of battery charge level pub mod ctrl_charge; -/// GPU switching and power -pub mod ctrl_gfx; /// Control CPU min/max freq and turbo, fan mode, fan curves /// /// Intel machines can control: diff --git a/rog-dbus/Cargo.toml b/rog-dbus/Cargo.toml index 4fe86180..aa97b47d 100644 --- a/rog-dbus/Cargo.toml +++ b/rog-dbus/Cargo.toml @@ -14,6 +14,7 @@ rog_anime = { path = "../rog-anime" } rog_aura = { path = "../rog-aura" } rog_profiles = { path = "../rog-profiles" } rog_types = { path = "../rog-types" } +supergfxctl = { path = "../supergfx" } zbus = "^1.9" zbus_macros = "^1.9" zvariant = "^2.8" diff --git a/rog-dbus/src/lib.rs b/rog-dbus/src/lib.rs index d7709969..7babbb09 100644 --- a/rog-dbus/src/lib.rs +++ b/rog-dbus/src/lib.rs @@ -13,7 +13,7 @@ pub mod zbus_supported; use rog_anime::AnimePowerStates; use rog_aura::{AuraEffect, LedPowerStates}; use rog_profiles::Profile; -use rog_types::gfx_vendors::{GfxRequiredUserAction, GfxVendors}; +use supergfxctl::gfx_vendors::{GfxRequiredUserAction, GfxVendors}; use std::sync::mpsc::{channel, Receiver}; use zbus::{Connection, Result, SignalReceiver}; diff --git a/rog-dbus/src/zbus_gfx.rs b/rog-dbus/src/zbus_gfx.rs index d60e6c02..a84f1e6f 100644 --- a/rog-dbus/src/zbus_gfx.rs +++ b/rog-dbus/src/zbus_gfx.rs @@ -21,7 +21,7 @@ use std::sync::mpsc::Sender; -use rog_types::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}; +use supergfxctl::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}; use zbus::{dbus_proxy, Connection, Result}; #[dbus_proxy( diff --git a/rog-types/src/lib.rs b/rog-types/src/lib.rs index 15078b86..478c9e5b 100644 --- a/rog-types/src/lib.rs +++ b/rog-types/src/lib.rs @@ -6,8 +6,6 @@ pub static DBUS_NAME: &str = "org.asuslinux.Daemon"; pub static DBUS_PATH: &str = "/org/asuslinux/Daemon"; pub static DBUS_IFACE: &str = "org.asuslinux.Daemon"; -pub mod gfx_vendors; - pub mod supported; pub mod error; diff --git a/supergfx/Cargo.toml b/supergfx/Cargo.toml new file mode 100644 index 00000000..c67e654f --- /dev/null +++ b/supergfx/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "supergfxctl" +version = "1.1.0" +license = "MPL-2.0" +readme = "README.md" +authors = ["Luke "] +repository = "https://gitlab.com/asus-linux/asusctl" +homepage = "https://gitlab.com/asus-linux/asusctl" +documentation = "https://docs.rs/rog-anime" +description = "Types useful for fancy keyboards on ASUS ROG laptops" +keywords = ["graphics", "nvidia", "switching"] +edition = "2018" +exclude = ["data"] + +[dependencies] +serde = "^1.0" +serde_derive = "^1.0" +serde_json = "^1.0" +log = "^0.4" + +zbus = "^1.9.1" +zvariant = "^2.8" +zvariant_derive = "^2.8" +logind-zbus = "^0.7.1" + +sysfs-class = "^0.1.2" \ No newline at end of file diff --git a/supergfx/LICENSE b/supergfx/LICENSE new file mode 100644 index 00000000..a612ad98 --- /dev/null +++ b/supergfx/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/supergfx/src/config.rs b/supergfx/src/config.rs new file mode 100644 index 00000000..e05409d2 --- /dev/null +++ b/supergfx/src/config.rs @@ -0,0 +1,89 @@ +use log::{error, warn}; +use serde_derive::{Deserialize, Serialize}; +use std::fs::{File, OpenOptions}; +use std::io::{Read, Write}; + +use crate::gfx_vendors::GfxVendors; + +pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf"; +pub static AURA_CONFIG_PATH: &str = "/etc/asusd/asusd.conf"; + +#[derive(Deserialize, Serialize)] +pub struct GfxConfig { + #[serde(skip)] + config_path: String, + /// The current mode set, also applies on boot + pub gfx_mode: GfxVendors, + /// Only for informational purposes + #[serde(skip)] + pub gfx_tmp_mode: Option, + /// Set if graphics management is enabled + pub gfx_managed: bool, + /// Set if vfio option is enabled. This requires the vfio drivers to be built as modules + pub gfx_vfio_enable: bool, +} + +impl GfxConfig { + fn new(config_path: String) -> Self { + Self { + config_path, + gfx_mode: GfxVendors::Hybrid, + gfx_tmp_mode: None, + gfx_managed: true, + gfx_vfio_enable: false, + } + } + + /// `load` will attempt to read the config, and panic if the dir is missing + pub fn load(config_path: String) -> Self { + let mut file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(&config_path) + .unwrap_or_else(|_| panic!("The directory {} is missing", config_path)); // okay to cause panic here + let mut buf = String::new(); + let mut config; + if let Ok(read_len) = file.read_to_string(&mut buf) { + if read_len == 0 { + config = Self::new(config_path); + } else if let Ok(data) = serde_json::from_str(&buf) { + config = data; + config.config_path = config_path; + } else { + warn!("Could not deserialise {}", config_path); + panic!("Please remove {} then restart service", config_path); + } + } else { + config = Self::new(config_path) + } + config.write(); + config + } + + pub fn read(&mut self) { + let mut file = OpenOptions::new() + .read(true) + .open(&self.config_path) + .unwrap_or_else(|err| panic!("Error reading {}: {}", self.config_path, err)); + let mut buf = String::new(); + if let Ok(l) = file.read_to_string(&mut buf) { + if l == 0 { + warn!("File is empty {}", self.config_path); + } else { + let mut x: Self = serde_json::from_str(&buf) + .unwrap_or_else(|_| panic!("Could not deserialise {}", self.config_path)); + // copy over serde skipped values + x.gfx_tmp_mode = self.gfx_tmp_mode; + *self = x; + } + } + } + + pub fn write(&self) { + let mut file = File::create(&self.config_path).expect("Couldn't overwrite config"); + let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed"); + file.write_all(json.as_bytes()) + .unwrap_or_else(|err| error!("Could not write config: {}", err)); + } +} diff --git a/daemon/src/ctrl_gfx/controller.rs b/supergfx/src/controller.rs similarity index 90% rename from daemon/src/ctrl_gfx/controller.rs rename to supergfx/src/controller.rs index 9fa83233..9aeae7eb 100644 --- a/daemon/src/ctrl_gfx/controller.rs +++ b/supergfx/src/controller.rs @@ -1,24 +1,23 @@ -use ::zbus::Connection; -use ctrl_gfx::error::GfxError; -use ctrl_gfx::*; -use ctrl_rog_bios::CtrlRogBios; use log::{error, info, warn}; use logind_zbus::{ types::{SessionClass, SessionInfo, SessionState, SessionType}, ManagerProxy, SessionProxy, }; -use rog_types::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}; +use ::zbus::Connection; use std::{io::Write, ops::Add, path::Path, time::Instant}; use std::{process::Command, thread::sleep, time::Duration}; use std::{str::FromStr, sync::mpsc}; use std::{sync::Arc, sync::Mutex}; use sysfs_class::RuntimePM; use sysfs_class::{PciDevice, SysClass}; -use system::{GraphicsDevice, PciBus}; -use crate::*; +use crate::{*, error::GfxError, system::{GraphicsDevice, PciBus}}; + +use super::config::GfxConfig; +use super::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}; const THREAD_TIMEOUT_MSG: &str = "GFX: thread time exceeded 3 minutes, exiting"; +const NVIDIA_RUNTIME_STATUS_PATH: &str = "/sys/bus/pci/devices/0000:01:00.0/power/runtime_status"; pub struct CtrlGraphics { bus: PciBus, @@ -27,20 +26,12 @@ pub struct CtrlGraphics { nvidia: Vec, #[allow(dead_code)] other: Vec, - config: Arc>, + config: Arc>, thread_kill: Arc>>>, } -impl Reloadable for CtrlGraphics { - fn reload(&mut self) -> Result<(), RogError> { - self.auto_power()?; - info!("GFX: Reloaded gfx mode: {:?}", self.get_gfx_mode()?); - Ok(()) - } -} - impl CtrlGraphics { - pub fn new(config: Arc>) -> std::io::Result { + pub fn new(config: Arc>) -> std::io::Result { let bus = PciBus::new()?; info!("GFX: Rescanning PCI bus"); bus.rescan()?; @@ -108,6 +99,13 @@ impl CtrlGraphics { }) } + /// Force reinit of all state, including reset of device state + pub fn reload(&mut self) -> Result<(), GfxError> { + self.auto_power()?; + info!("GFX: Reloaded gfx mode: {:?}", self.get_gfx_mode()?); + Ok(()) + } + pub fn bus(&self) -> PciBus { self.bus.clone() } @@ -117,7 +115,7 @@ impl CtrlGraphics { } /// Save the selected `Vendor` mode to config - fn save_gfx_mode(vendor: GfxVendors, config: Arc>) { + fn save_gfx_mode(vendor: GfxVendors, config: Arc>) { if let Ok(mut config) = config.lock() { config.gfx_mode = vendor; config.write(); @@ -125,7 +123,7 @@ impl CtrlGraphics { } /// Associated method to get which vendor mode is set - pub(super) fn get_gfx_mode(&self) -> Result { + pub(super) fn get_gfx_mode(&self) -> Result { if let Ok(config) = self.config.lock() { if let Some(mode) = config.gfx_tmp_mode { return Ok(mode); @@ -136,12 +134,12 @@ impl CtrlGraphics { Ok(GfxVendors::Hybrid) } - pub(super) fn get_runtime_status() -> Result { - let path = Path::new("/sys/bus/pci/devices/0000:01:00.0/power/runtime_status"); + pub(super) fn get_runtime_status() -> Result { + let path = Path::new(NVIDIA_RUNTIME_STATUS_PATH); if path.exists() { let buf = std::fs::read_to_string(path).map_err(|err| { - RogError::Read( - "/sys/bus/pci/devices/0000:01:00.0/power/runtime_status".to_string(), + GfxError::Read( + path.to_string_lossy().to_string(), err, ) })?; @@ -152,7 +150,7 @@ impl CtrlGraphics { } /// Some systems have a fallback service to load nouveau if nvidia fails - fn toggle_fallback_service(vendor: GfxVendors) -> Result<(), RogError> { + fn toggle_fallback_service(vendor: GfxVendors) -> Result<(), GfxError> { let action = if vendor == GfxVendors::Nvidia { info!("GFX: Enabling nvidia-fallback.service"); "enable" @@ -165,7 +163,7 @@ impl CtrlGraphics { .arg(action) .arg("nvidia-fallback.service") .status() - .map_err(|err| RogError::Command("systemctl".into(), err))?; + .map_err(|err| GfxError::Command("systemctl".into(), err))?; if !status.success() { // Error is ignored in case this service is removed @@ -179,7 +177,7 @@ impl CtrlGraphics { } /// Write the appropriate xorg config for the chosen mode - fn write_xorg_conf(vendor: GfxVendors) -> Result<(), RogError> { + fn write_xorg_conf(vendor: GfxVendors) -> Result<(), GfxError> { let text = if vendor == GfxVendors::Nvidia { [PRIMARY_GPU_BEGIN, PRIMARY_GPU_NVIDIA, PRIMARY_GPU_END].concat() } else { @@ -187,7 +185,7 @@ impl CtrlGraphics { }; if !Path::new(XORG_PATH).exists() { - std::fs::create_dir(XORG_PATH).map_err(|err| RogError::Write(XORG_PATH.into(), err))?; + std::fs::create_dir(XORG_PATH).map_err(|err| GfxError::Write(XORG_PATH.into(), err))?; } let file = XORG_PATH.to_string().add(XORG_FILE); @@ -197,11 +195,11 @@ impl CtrlGraphics { .truncate(true) .write(true) .open(&file) - .map_err(|err| RogError::Write(file, err))?; + .map_err(|err| GfxError::Write(file, err))?; file.write_all(&text) .and_then(|_| file.sync_all()) - .map_err(|err| RogError::Write(MODPROBE_PATH.into(), err))?; + .map_err(|err| GfxError::Write(MODPROBE_PATH.into(), err))?; Ok(()) } @@ -232,7 +230,7 @@ impl CtrlGraphics { conf } - fn write_modprobe_conf(vendor: GfxVendors, devices: &[GraphicsDevice]) -> Result<(), RogError> { + fn write_modprobe_conf(vendor: GfxVendors, devices: &[GraphicsDevice]) -> Result<(), GfxError> { info!("GFX: Writing {}", MODPROBE_PATH); let content = match vendor { GfxVendors::Nvidia | GfxVendors::Hybrid => { @@ -250,16 +248,16 @@ impl CtrlGraphics { .truncate(true) .write(true) .open(MODPROBE_PATH) - .map_err(|err| RogError::Path(MODPROBE_PATH.into(), err))?; + .map_err(|err| GfxError::Path(MODPROBE_PATH.into(), err))?; file.write_all(&content) .and_then(|_| file.sync_all()) - .map_err(|err| RogError::Write(MODPROBE_PATH.into(), err))?; + .map_err(|err| GfxError::Write(MODPROBE_PATH.into(), err))?; Ok(()) } - fn unbind_remove_nvidia(devices: &[GraphicsDevice]) -> Result<(), RogError> { + fn unbind_remove_nvidia(devices: &[GraphicsDevice]) -> Result<(), GfxError> { // Unbind NVIDIA graphics devices and their functions let unbinds = devices.iter().map(|dev| dev.unbind()); // Remove NVIDIA graphics devices and their functions @@ -267,14 +265,14 @@ impl CtrlGraphics { unbinds .chain(removes) .collect::>() - .map_err(|err| RogError::Command("device unbind error".into(), err)) + .map_err(|err| GfxError::Command("device unbind error".into(), err)) } - fn unbind_only(devices: &[GraphicsDevice]) -> Result<(), RogError> { + fn unbind_only(devices: &[GraphicsDevice]) -> Result<(), GfxError> { let unbinds = devices.iter().map(|dev| dev.unbind()); unbinds .collect::>() - .map_err(|err| RogError::Command("device unbind error".into(), err)) + .map_err(|err| GfxError::Command("device unbind error".into(), err)) } /// Add or remove driver modules @@ -288,7 +286,7 @@ impl CtrlGraphics { if count > MAX_TRIES { let msg = format!("{} {} failed for unknown reason", action, driver); error!("GFX: {}", msg); - return Ok(()); //Err(RogError::Modprobe(msg)); + return Ok(()); //Err(GfxError::Modprobe(msg)); } let output = cmd @@ -337,25 +335,25 @@ impl CtrlGraphics { } } - fn do_display_manager_action(action: &str) -> Result<(), RogError> { + fn do_display_manager_action(action: &str) -> Result<(), GfxError> { let mut cmd = Command::new("systemctl"); cmd.arg(action); cmd.arg(DISPLAY_MANAGER); let status = cmd .status() - .map_err(|err| RogError::Command(format!("{:?}", cmd), err))?; + .map_err(|err| GfxError::Command(format!("{:?}", cmd), err))?; if !status.success() { let msg = format!( "systemctl {} {} failed: {:?}", action, DISPLAY_MANAGER, status ); - return Err(GfxError::DisplayManagerAction(msg, status).into()); + return Err(GfxError::DisplayManagerAction(msg, status)); } Ok(()) } - fn wait_display_manager_state(state: &str) -> Result<(), RogError> { + fn wait_display_manager_state(state: &str) -> Result<(), GfxError> { let mut cmd = Command::new("systemctl"); cmd.arg("is-active"); cmd.arg(DISPLAY_MANAGER); @@ -366,14 +364,14 @@ impl CtrlGraphics { // 3 seconds max let output = cmd .output() - .map_err(|err| RogError::Command(format!("{:?}", cmd), err))?; + .map_err(|err| GfxError::Command(format!("{:?}", cmd), err))?; if output.stdout.starts_with(state.as_bytes()) { return Ok(()); } std::thread::sleep(std::time::Duration::from_millis(250)); count += 1; } - Err(GfxError::DisplayManagerTimeout(state.into()).into()) + Err(GfxError::DisplayManagerTimeout(state.into())) } /// Determine if we need to logout/thread. Integrated<->Vfio mode does not @@ -417,7 +415,7 @@ impl CtrlGraphics { vfio_enable: bool, devices: &[GraphicsDevice], bus: &PciBus, - ) -> Result<(), RogError> { + ) -> Result<(), GfxError> { // Rescan before doing remove or add drivers bus.rescan()?; // Make sure the power management is set to auto for nvidia devices @@ -467,7 +465,7 @@ impl CtrlGraphics { Self::unbind_only(devices)?; Self::do_driver_action("vfio-pci", "modprobe")?; } else { - return Err(GfxError::VfioDisabled.into()); + return Err(GfxError::VfioDisabled); } } GfxVendors::Integrated => { @@ -490,7 +488,7 @@ impl CtrlGraphics { fn graphical_user_sessions_exist( connection: &Connection, sessions: &[SessionInfo], - ) -> Result { + ) -> Result { for session in sessions { let session_proxy = SessionProxy::new(connection, session)?; if session_proxy.get_class()? == SessionClass::User { @@ -514,8 +512,8 @@ impl CtrlGraphics { devices: Vec, bus: PciBus, thread_stop: mpsc::Receiver, - config: Arc>, - ) -> Result { + config: Arc>, + ) -> Result { info!("GFX: display-manager thread started"); const SLEEP_PERIOD: Duration = Duration::from_millis(100); @@ -631,12 +629,12 @@ impl CtrlGraphics { /// to switch modes. /// /// For manually calling (not on boot/startup) via dbus - pub fn set_gfx_mode(&mut self, vendor: GfxVendors) -> Result { - if let Ok(gsync) = CtrlRogBios::get_gfx_mode() { - if gsync == 1 { - return Err(GfxError::GsyncModeActive.into()); - } - } + pub fn set_gfx_mode(&mut self, vendor: GfxVendors) -> Result { + // if let Ok(gsync) = CtrlRogBios::get_gfx_mode() { + // if gsync == 1 { + // return Err(GfxError::GsyncModeActive.into()); + // } + // } let vfio_enable = if let Ok(config) = self.config.try_lock() { config.gfx_vfio_enable @@ -645,7 +643,7 @@ impl CtrlGraphics { }; if !vfio_enable && matches!(vendor, GfxVendors::Vfio) { - return Err(GfxError::VfioDisabled.into()); + return Err(GfxError::VfioDisabled); } // Must always cancel any thread running @@ -687,7 +685,7 @@ impl CtrlGraphics { } /// Used only on boot to set correct mode - fn auto_power(&mut self) -> Result<(), RogError> { + fn auto_power(&mut self) -> Result<(), GfxError> { let vendor = self.get_gfx_mode()?; let devices = self.nvidia.clone(); let bus = self.bus.clone(); diff --git a/daemon/src/ctrl_gfx/error.rs b/supergfx/src/error.rs similarity index 71% rename from daemon/src/ctrl_gfx/error.rs rename to supergfx/src/error.rs index ec765653..a43d9445 100644 --- a/daemon/src/ctrl_gfx/error.rs +++ b/supergfx/src/error.rs @@ -1,8 +1,6 @@ use std::fmt; use std::{error, process::ExitStatus}; -use crate::error::RogError; - #[derive(Debug)] pub enum GfxError { ParseVendor, @@ -16,6 +14,11 @@ pub enum GfxError { MissingModule(String), Modprobe(String), Command(String, std::io::Error), + Path(String, std::io::Error), + Read(String, std::io::Error), + Write(String, std::io::Error), + Io(std::io::Error), + Zbus(zbus::Error), } impl fmt::Display for GfxError { @@ -45,14 +48,25 @@ impl fmt::Display for GfxError { GfxError::MissingModule(m) => write!(f, "The module {} is missing", m), GfxError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail), GfxError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error), + GfxError::Path(path, error) => write!(f, "Path {}: {}", path, error), + GfxError::Read(path, error) => write!(f, "Read {}: {}", path, error), + GfxError::Write(path, error) => write!(f, "Write {}: {}", path, error), + GfxError::Io(detail) => write!(f, "std::io error: {}", detail), + GfxError::Zbus(detail) => write!(f, "Zbus error: {}", detail), } } } impl error::Error for GfxError {} -impl From for RogError { - fn from(err: GfxError) -> Self { - RogError::GfxSwitching(err) +impl From for GfxError { + fn from(err: zbus::Error) -> Self { + GfxError::Zbus(err) } } + +impl From for GfxError { + fn from(err: std::io::Error) -> Self { + GfxError::Io(err) + } +} \ No newline at end of file diff --git a/rog-types/src/gfx_vendors.rs b/supergfx/src/gfx_vendors.rs similarity index 90% rename from rog-types/src/gfx_vendors.rs rename to supergfx/src/gfx_vendors.rs index 86636b55..f16200a8 100644 --- a/rog-types/src/gfx_vendors.rs +++ b/supergfx/src/gfx_vendors.rs @@ -1,8 +1,9 @@ -use crate::error::GraphicsError; use serde_derive::{Deserialize, Serialize}; use std::str::FromStr; use zvariant_derive::Type; +use crate::error::GfxError; + #[derive(Debug, Type, PartialEq, Copy, Clone, Deserialize, Serialize)] pub enum GfxPower { Active, @@ -12,9 +13,9 @@ pub enum GfxPower { } impl FromStr for GfxPower { - type Err = GraphicsError; + type Err = GfxError; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { match s.to_lowercase().trim() { "active" => Ok(GfxPower::Active), "suspended" => Ok(GfxPower::Suspended), @@ -45,9 +46,9 @@ pub enum GfxVendors { } impl FromStr for GfxVendors { - type Err = GraphicsError; + type Err = GfxError; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { match s.to_lowercase().as_str() { "nvidia" => Ok(GfxVendors::Nvidia), "hybrid" => Ok(GfxVendors::Hybrid), @@ -59,7 +60,7 @@ impl FromStr for GfxVendors { "compute\n" => Ok(GfxVendors::Compute), "vfio\n" => Ok(GfxVendors::Vfio), "integrated\n" => Ok(GfxVendors::Integrated), - _ => Err(GraphicsError::ParseVendor), + _ => Err(GfxError::ParseVendor), } } } diff --git a/daemon/src/ctrl_gfx/mod.rs b/supergfx/src/lib.rs similarity index 97% rename from daemon/src/ctrl_gfx/mod.rs rename to supergfx/src/lib.rs index 183fc2a9..527667c9 100644 --- a/daemon/src/ctrl_gfx/mod.rs +++ b/supergfx/src/lib.rs @@ -1,9 +1,8 @@ pub mod error; - +pub mod config; +pub mod gfx_vendors; pub mod controller; - pub mod system; - pub mod zbus; const NVIDIA_DRIVERS: [&str; 4] = ["nvidia_drm", "nvidia_modeset", "nvidia_uvm", "nvidia"]; diff --git a/daemon/src/ctrl_gfx/system.rs b/supergfx/src/system.rs similarity index 100% rename from daemon/src/ctrl_gfx/system.rs rename to supergfx/src/system.rs diff --git a/supergfx/src/zbus.rs b/supergfx/src/zbus.rs new file mode 100644 index 00000000..18551f33 --- /dev/null +++ b/supergfx/src/zbus.rs @@ -0,0 +1,55 @@ +use log::{error, info, warn}; +use zvariant::ObjectPath; +use ::zbus::dbus_interface; + +use crate::{gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}}; + +use super::controller::CtrlGraphics; + +#[dbus_interface(name = "org.asuslinux.Daemon")] +impl CtrlGraphics { + fn vendor(&self) -> zbus::fdo::Result { + self.get_gfx_mode().map_err(|err| { + error!("GFX: {}", err); + zbus::fdo::Error::Failed(format!("GFX fail: {}", err)) + }) + } + + fn power(&self) -> zbus::fdo::Result { + Self::get_runtime_status().map_err(|err| { + error!("GFX: {}", err); + zbus::fdo::Error::Failed(format!("GFX fail: {}", err)) + }) + } + + fn set_vendor(&mut self, vendor: GfxVendors) -> zbus::fdo::Result { + info!("GFX: Switching gfx mode to {}", <&str>::from(vendor)); + let msg = self.set_gfx_mode(vendor).map_err(|err| { + error!("GFX: {}", err); + zbus::fdo::Error::Failed(format!("GFX fail: {}", err)) + })?; + self.notify_gfx(&vendor) + .unwrap_or_else(|err| warn!("GFX: {}", err)); + self.notify_action(&msg) + .unwrap_or_else(|err| warn!("GFX: {}", err)); + Ok(msg) + } + + #[dbus_interface(signal)] + fn notify_gfx(&self, vendor: &GfxVendors) -> zbus::Result<()> {} + + #[dbus_interface(signal)] + fn notify_action(&self, action: &GfxRequiredUserAction) -> zbus::Result<()> {} +} + +impl CtrlGraphics { + pub fn add_to_server(self, server: &mut zbus::ObjectServer) { + server + .at(&ObjectPath::from_str_unchecked("/org/asuslinux/Gfx"), self) + .map_err(|err| { + warn!("GFX: CtrlGraphics: add_to_server {}", err); + err + }) + .ok(); + } +} From 60b7f3be692c48deef92b734772edfb14c360ea5 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Wed, 25 Aug 2021 11:42:32 +1200 Subject: [PATCH 03/11] Begin cleanup --- .gitignore | 1 + daemon/src/config.rs | 4 ++-- supergfx/src/config.rs | 3 --- supergfx/src/controller.rs | 29 ++++++++++++++++------------- supergfx/src/error.rs | 4 ++-- supergfx/src/lib.rs | 15 +++++++-------- supergfx/src/special.rs | 25 +++++++++++++++++++++++++ supergfx/src/zbus.rs | 2 +- 8 files changed, 54 insertions(+), 29 deletions(-) create mode 100644 supergfx/src/special.rs diff --git a/.gitignore b/.gitignore index 8d235fc8..bec5eaa6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ cargo-config .idea vendor-* vendor_* +.vscode-ctags diff --git a/daemon/src/config.rs b/daemon/src/config.rs index 260e34b8..604c95b8 100644 --- a/daemon/src/config.rs +++ b/daemon/src/config.rs @@ -60,8 +60,8 @@ impl Config { if l == 0 { warn!("File is empty {}", CONFIG_PATH); } else { - serde_json::from_str(&buf) - .unwrap_or_else(|_| panic!("Could not deserialise {}", CONFIG_PATH)) + *self = serde_json::from_str(&buf) + .unwrap_or_else(|_| panic!("Could not deserialise {}", CONFIG_PATH)); } } } diff --git a/supergfx/src/config.rs b/supergfx/src/config.rs index e05409d2..1967d2db 100644 --- a/supergfx/src/config.rs +++ b/supergfx/src/config.rs @@ -5,9 +5,6 @@ use std::io::{Read, Write}; use crate::gfx_vendors::GfxVendors; -pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf"; -pub static AURA_CONFIG_PATH: &str = "/etc/asusd/asusd.conf"; - #[derive(Deserialize, Serialize)] pub struct GfxConfig { #[serde(skip)] diff --git a/supergfx/src/controller.rs b/supergfx/src/controller.rs index 9aeae7eb..e6e788b4 100644 --- a/supergfx/src/controller.rs +++ b/supergfx/src/controller.rs @@ -1,9 +1,9 @@ +use ::zbus::Connection; use log::{error, info, warn}; use logind_zbus::{ types::{SessionClass, SessionInfo, SessionState, SessionType}, ManagerProxy, SessionProxy, }; -use ::zbus::Connection; use std::{io::Write, ops::Add, path::Path, time::Instant}; use std::{process::Command, thread::sleep, time::Duration}; use std::{str::FromStr, sync::mpsc}; @@ -11,7 +11,12 @@ use std::{sync::Arc, sync::Mutex}; use sysfs_class::RuntimePM; use sysfs_class::{PciDevice, SysClass}; -use crate::{*, error::GfxError, system::{GraphicsDevice, PciBus}}; +use crate::{ + error::GfxError, + special::{get_asus_gsync_gfx_mode, has_asus_gsync_gfx_mode}, + system::{GraphicsDevice, PciBus}, + *, +}; use super::config::GfxConfig; use super::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}; @@ -137,12 +142,8 @@ impl CtrlGraphics { pub(super) fn get_runtime_status() -> Result { let path = Path::new(NVIDIA_RUNTIME_STATUS_PATH); if path.exists() { - let buf = std::fs::read_to_string(path).map_err(|err| { - GfxError::Read( - path.to_string_lossy().to_string(), - err, - ) - })?; + let buf = std::fs::read_to_string(path) + .map_err(|err| GfxError::Read(path.to_string_lossy().to_string(), err))?; Ok(GfxPower::from_str(&buf)?) } else { Ok(GfxPower::Off) @@ -630,11 +631,13 @@ impl CtrlGraphics { /// /// For manually calling (not on boot/startup) via dbus pub fn set_gfx_mode(&mut self, vendor: GfxVendors) -> Result { - // if let Ok(gsync) = CtrlRogBios::get_gfx_mode() { - // if gsync == 1 { - // return Err(GfxError::GsyncModeActive.into()); - // } - // } + if has_asus_gsync_gfx_mode() { + if let Ok(gsync) = get_asus_gsync_gfx_mode() { + if gsync == 1 { + return Err(GfxError::AsusGsyncModeActive); + } + } + } let vfio_enable = if let Ok(config) = self.config.try_lock() { config.gfx_vfio_enable diff --git a/supergfx/src/error.rs b/supergfx/src/error.rs index a43d9445..b5e94612 100644 --- a/supergfx/src/error.rs +++ b/supergfx/src/error.rs @@ -8,7 +8,7 @@ pub enum GfxError { Bus(String, std::io::Error), DisplayManagerAction(String, ExitStatus), DisplayManagerTimeout(String), - GsyncModeActive, + AsusGsyncModeActive, VfioBuiltin, VfioDisabled, MissingModule(String), @@ -34,7 +34,7 @@ impl fmt::Display for GfxError { GfxError::DisplayManagerTimeout(state) => { write!(f, "Timed out waiting for display-manager {} state", state) } - GfxError::GsyncModeActive => write!( + GfxError::AsusGsyncModeActive => write!( f, "Can not switch gfx modes when dedicated/G-Sync mode is active" ), diff --git a/supergfx/src/lib.rs b/supergfx/src/lib.rs index 527667c9..484aece7 100644 --- a/supergfx/src/lib.rs +++ b/supergfx/src/lib.rs @@ -3,6 +3,9 @@ pub mod config; pub mod gfx_vendors; pub mod controller; pub mod system; +/// Special-case functions for check/read/write of key functions on unique laptops +/// such as the G-Sync mode available on some ASUS ROG laptops +pub(crate) mod special; pub mod zbus; const NVIDIA_DRIVERS: [&str; 4] = ["nvidia_drm", "nvidia_modeset", "nvidia_uvm", "nvidia"]; @@ -17,13 +20,9 @@ const VFIO_DRIVERS: [&str; 5] = [ const DISPLAY_MANAGER: &str = "display-manager.service"; -const MODPROBE_PATH: &str = "/etc/modprobe.d/asusd.conf"; +const MODPROBE_PATH: &str = "/etc/modprobe.d/supergfxd.conf"; -static MODPROBE_BASE: &[u8] = br#"# Automatically generated by asusd -# If you have issues with i2c_nvidia_gpu, copy the 2 lines below to a -# new blacklist file and uncomment -#blacklist i2c_nvidia_gpu -#alias i2c_nvidia_gpu off +static MODPROBE_BASE: &[u8] = br#"# Automatically generated by supergfxd blacklist nouveau alias nouveau off options nvidia NVreg_DynamicPowerManagement=0x02 @@ -33,7 +32,7 @@ static MODPROBE_DRM_MODESET: &[u8] = br#" options nvidia-drm modeset=1 "#; -static MODPROBE_INTEGRATED: &[u8] = br#"# Automatically generated by asusd +static MODPROBE_INTEGRATED: &[u8] = br#"# Automatically generated by supergfxd blacklist i2c_nvidia_gpu blacklist nvidia blacklist nvidia-drm @@ -47,7 +46,7 @@ static MODPROBE_VFIO: &[u8] = br#"options vfio-pci ids="#; const XORG_FILE: &str = "90-nvidia-primary.conf"; const XORG_PATH: &str = "/etc/X11/xorg.conf.d/"; -static PRIMARY_GPU_BEGIN: &[u8] = br#"# Automatically generated by asusd +static PRIMARY_GPU_BEGIN: &[u8] = br#"# Automatically generated by supergfxd Section "OutputClass" Identifier "nvidia" MatchDriver "nvidia-drm" diff --git a/supergfx/src/special.rs b/supergfx/src/special.rs new file mode 100644 index 00000000..fdccc37e --- /dev/null +++ b/supergfx/src/special.rs @@ -0,0 +1,25 @@ +use std::{fs::OpenOptions, io::Read, path::Path}; + +use crate::error::GfxError; + +static ASUS_SWITCH_GRAPHIC_MODE: &str = + "/sys/firmware/efi/efivars/AsusSwitchGraphicMode-607005d5-3f75-4b2e-98f0-85ba66797a3e"; + +pub(crate) fn has_asus_gsync_gfx_mode() -> bool { + Path::new(ASUS_SWITCH_GRAPHIC_MODE).exists() +} + +pub(crate) fn get_asus_gsync_gfx_mode() -> Result { + let path = ASUS_SWITCH_GRAPHIC_MODE; + let mut file = OpenOptions::new() + .read(true) + .open(path) + .map_err(|err| GfxError::Path(path.into(), err))?; + + let mut data = Vec::new(); + file.read_to_end(&mut data) + .map_err(|err| GfxError::Read(path.into(), err))?; + + let idx = data.len() - 1; + Ok(data[idx] as i8) +} \ No newline at end of file diff --git a/supergfx/src/zbus.rs b/supergfx/src/zbus.rs index 18551f33..b625912b 100644 --- a/supergfx/src/zbus.rs +++ b/supergfx/src/zbus.rs @@ -2,7 +2,7 @@ use log::{error, info, warn}; use zvariant::ObjectPath; use ::zbus::dbus_interface; -use crate::{gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}}; +use crate::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}; use super::controller::CtrlGraphics; From 498e6045318bcd51b221c6aaba8162ffa616ab68 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Thu, 26 Aug 2021 11:43:47 +1200 Subject: [PATCH 04/11] Major update to supergfx and others --- Cargo.lock | 15 +-- Makefile | 19 +++- asus-notify/Cargo.toml | 1 + asus-notify/src/main.rs | 54 +++++++-- asusctl/Cargo.toml | 2 +- asusctl/src/main.rs | 92 ++++++++------- daemon/Cargo.toml | 1 - daemon/src/config.rs | 4 - daemon/src/config_old.rs | 53 --------- daemon/src/ctrl_profiles/controller.rs | 12 +- daemon/src/daemon.rs | 54 +-------- daemon/src/error.rs | 13 --- daemon/src/lib.rs | 1 - data/anime/asus/festive/Love u mom.gif | Bin 0 -> 156884 bytes data/anime/asus/festive/Mother's day.gif | Bin 0 -> 98584 bytes data/anime/asus/music/Diamond.gif | Bin 0 -> 131281 bytes data/anime/asus/trend/Hero.gif | Bin 0 -> 109829 bytes rog-dbus/src/lib.rs | 39 +------ rog-types/src/error.rs | 20 ---- rog-types/src/lib.rs | 2 - supergfx/Cargo.toml | 25 ++++- supergfx/Makefile | 74 ++++++++++++ .../data}/90-asusd-nvidia-pm.rules | 0 .../data}/90-nvidia-screen-G05.conf | 0 supergfx/data/org.supergfxctl.Daemon.conf | 26 +++++ supergfx/data/supergfxd.service | 16 +++ supergfx/src/cli.rs | 105 ++++++++++++++++++ supergfx/src/daemon.rs | 105 ++++++++++++++++++ supergfx/src/error.rs | 2 +- supergfx/src/lib.rs | 15 ++- supergfx/src/special.rs | 6 +- supergfx/src/{zbus.rs => zbus_iface.rs} | 18 ++- .../zbus_gfx.rs => supergfx/src/zbus_proxy.rs | 39 +++++-- 33 files changed, 535 insertions(+), 278 deletions(-) delete mode 100644 daemon/src/config_old.rs create mode 100755 data/anime/asus/festive/Love u mom.gif create mode 100755 data/anime/asus/festive/Mother's day.gif create mode 100755 data/anime/asus/music/Diamond.gif create mode 100755 data/anime/asus/trend/Hero.gif delete mode 100644 rog-types/src/error.rs create mode 100644 supergfx/Makefile rename {data => supergfx/data}/90-asusd-nvidia-pm.rules (100%) rename {data => supergfx/data}/90-nvidia-screen-G05.conf (100%) create mode 100644 supergfx/data/org.supergfxctl.Daemon.conf create mode 100644 supergfx/data/supergfxd.service create mode 100644 supergfx/src/cli.rs create mode 100644 supergfx/src/daemon.rs rename supergfx/src/{zbus.rs => zbus_iface.rs} (87%) rename rog-dbus/src/zbus_gfx.rs => supergfx/src/zbus_proxy.rs (70%) diff --git a/Cargo.lock b/Cargo.lock index 1c89b48e..45f268dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,6 +40,7 @@ dependencies = [ "rog_types", "serde_json", "supergfxctl", + "zbus", ] [[package]] @@ -57,7 +58,7 @@ dependencies = [ "rog_types", "supergfxctl", "tinybmp", - "yansi-term", + "zbus", ] [[package]] @@ -221,7 +222,6 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "supergfxctl", "sysfs-class", "toml", "udev", @@ -1070,6 +1070,8 @@ dependencies = [ name = "supergfxctl" version = "1.1.0" dependencies = [ + "env_logger", + "gumdrop", "log", "logind-zbus", "serde", @@ -1314,15 +1316,6 @@ dependencies = [ "bitflags 0.9.1", ] -[[package]] -name = "yansi-term" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1" -dependencies = [ - "winapi", -] - [[package]] name = "zbus" version = "1.9.1" diff --git a/Makefile b/Makefile index 0125072d..190d4f8a 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,8 @@ BIN_C := asusctl BIN_D := asusd BIN_U := asusd-user BIN_N := asus-notify +BIN_SD := supergfxd +BIN_SC := supergfxctl LEDCFG := asusd-ledmodes.toml X11CFG := 90-nvidia-screen-G05.conf PMRULES := 90-asusd-nvidia-pm.rules @@ -45,14 +47,15 @@ install: $(INSTALL_PROGRAM) "./target/release/$(BIN_D)" "$(DESTDIR)$(bindir)/$(BIN_D)" $(INSTALL_PROGRAM) "./target/release/$(BIN_U)" "$(DESTDIR)$(bindir)/$(BIN_U)" $(INSTALL_PROGRAM) "./target/release/$(BIN_N)" "$(DESTDIR)$(bindir)/$(BIN_N)" - $(INSTALL_DATA) "./data/$(PMRULES)" "$(DESTDIR)$(libdir)/udev/rules.d/$(PMRULES)" + $(INSTALL_DATA) "./data/$(BIN_D).rules" "$(DESTDIR)$(libdir)/udev/rules.d/99-$(BIN_D).rules" $(INSTALL_DATA) "./data/$(LEDCFG)" "$(DESTDIR)/etc/asusd/$(LEDCFG)" $(INSTALL_DATA) "./data/$(BIN_D).conf" "$(DESTDIR)$(datarootdir)/dbus-1/system.d/$(BIN_D).conf" - $(INSTALL_DATA) "./data/$(X11CFG)" "$(DESTDIR)$(datarootdir)/X11/xorg.conf.d/$(X11CFG)" + $(INSTALL_DATA) "./data/$(BIN_D).service" "$(DESTDIR)$(libdir)/systemd/system/$(BIN_D).service" $(INSTALL_DATA) "./data/$(BIN_N).service" "$(DESTDIR)$(libdir)/systemd/user/$(BIN_N).service" $(INSTALL_DATA) "./data/$(BIN_U).service" "$(DESTDIR)$(libdir)/systemd/user/$(BIN_U).service" + $(INSTALL_DATA) "./data/icons/asus_notif_yellow.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_yellow.png" $(INSTALL_DATA) "./data/icons/asus_notif_green.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_green.png" $(INSTALL_DATA) "./data/icons/asus_notif_red.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_red.png" @@ -63,10 +66,18 @@ install: $(INSTALL_DATA) "./data/icons/scalable/gpu-nvidia.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-nvidia.svg" $(INSTALL_DATA) "./data/icons/scalable/gpu-vfio.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-vfio.svg" $(INSTALL_DATA) "./data/icons/scalable/notification-reboot.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/notification-reboot.svg" + $(INSTALL_DATA) "./data/_asusctl" "$(DESTDIR)$(zshcpl)/_asusctl" $(INSTALL_DATA) "./data/completions/asusctl.fish" "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish" cd data && find "./anime" -type f -exec install -Dm 755 "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \; + $(INSTALL_PROGRAM) "./target/release/$(BIN_SD)" "$(DESTDIR)$(bindir)/$(BIN_SD)" + $(INSTALL_PROGRAM) "./target/release/$(BIN_SC)" "$(DESTDIR)$(bindir)/$(BIN_SC)" + $(INSTALL_DATA) "./supergfx/data/$(BIN_SD).service" "$(DESTDIR)$(libdir)/systemd/system/$(BIN_SD).service" + $(INSTALL_DATA) "./supergfx/data/org.supergfxctl.Daemon.conf" "$(DESTDIR)$(datarootdir)/dbus-1/system.d/org.supergfxctl.Daemon.conf" + $(INSTALL_DATA) "./supergfx/data/$(X11CFG)" "$(DESTDIR)$(datarootdir)/X11/xorg.conf.d/$(X11CFG)" + $(INSTALL_DATA) "./supergfx/data/$(PMRULES)" "$(DESTDIR)$(libdir)/udev/rules.d/$(PMRULES)" + uninstall: rm -f "$(DESTDIR)$(bindir)/$(BIN_C)" rm -f "$(DESTDIR)$(bindir)/$(BIN_D)" @@ -90,6 +101,10 @@ uninstall: rm -f "$(DESTDIR)$(zshcpl)/_asusctl" rm -f "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish" rm -rf "$(DESTDIR)$(datarootdir)/asusd" + rm -f "$(DESTDIR)$(bindir)/$(BIN_SC)" + rm -f "$(DESTDIR)$(bindir)/$(BIN_SD)" + rm -f "$(DESTDIR)$(libdir)/systemd/system/$(BIN_SD).service" + rm -f "$(DESTDIR)$(datarootdir)/dbus-1/system.d/org.supergfxctl.Daemon.conf" update: cargo update diff --git a/asus-notify/Cargo.toml b/asus-notify/Cargo.toml index 7f5bc0f6..a4d6d103 100644 --- a/asus-notify/Cargo.toml +++ b/asus-notify/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +zbus = "^1.9" # serialisation serde_json = "^1.0" rog_dbus = { path = "../rog-dbus" } diff --git a/asus-notify/src/main.rs b/asus-notify/src/main.rs index e36c75f8..bb3593bd 100644 --- a/asus-notify/src/main.rs +++ b/asus-notify/src/main.rs @@ -2,11 +2,14 @@ use notify_rust::{Hint, Notification, NotificationHandle}; use rog_aura::AuraEffect; use rog_dbus::{DbusProxies, Signals}; use rog_profiles::Profile; -use supergfxctl::gfx_vendors::{GfxRequiredUserAction, GfxVendors}; use std::error::Error; -use std::process; +use std::sync::mpsc::channel; use std::thread::sleep; use std::time::Duration; +use std::{process, thread}; +use supergfxctl::gfx_vendors::{GfxRequiredUserAction, GfxVendors}; +use supergfxctl::zbus_proxy::GfxProxy; +use zbus::Connection; const NOTIF_HEADER: &str = "ROG Control"; @@ -42,6 +45,9 @@ fn main() -> Result<(), Box> { let recv = proxies.setup_recv(conn); let mut err_count = 0; + + gfx_thread()?; + loop { sleep(Duration::from_millis(100)); if let Err(err) = recv.next_signal() { @@ -67,23 +73,57 @@ fn main() -> Result<(), Box> { if let Ok(data) = signals.charge.try_recv() { notify!(do_charge_notif, last_notification, &data); } - if let Ok(data) = signals.gfx_vendor.try_recv() { + } +} + +fn gfx_thread() -> Result<(), Box> { + let mut last_notification: Option = None; + + let conn = Connection::new_system()?; + let proxy = GfxProxy::new(&conn)?; + + let (tx1, rx1) = channel(); + proxy.connect_notify_gfx(tx1)?; + + let (tx2, rx2) = channel(); + proxy.connect_notify_action(tx2)?; + + thread::spawn(move || loop { + if proxy + .next_signal() + .map_err(|e| println!("Error: {}", e)) + .is_err() + { + break; + } + + if let Ok(data) = rx1.try_recv() { notify!(do_gfx_notif, last_notification, &data); } - if let Ok(data) = signals.gfx_action.try_recv() { + + if let Ok(data) = rx2.try_recv() { match data { GfxRequiredUserAction::Logout | GfxRequiredUserAction::Reboot => { - do_gfx_action_notif(&data)?; + do_gfx_action_notif(&data) + .map_err(|e| { + println!("Error: {}", e); + }) + .ok(); } GfxRequiredUserAction::Integrated => { base_notification!( "You must be in integrated mode first to switch to the requested mode" - )?; + ) + .map_err(|e| { + println!("Error: {}", e); + }) + .ok(); } GfxRequiredUserAction::None => {} } } - } + }); + Ok(()) } fn do_thermal_notif(profile: &Profile) -> Result> { diff --git a/asusctl/Cargo.toml b/asusctl/Cargo.toml index 1e1cce13..3c182768 100644 --- a/asusctl/Cargo.toml +++ b/asusctl/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +zbus = "^1.9.1" rog_anime = { path = "../rog-anime" } rog_aura = { path = "../rog-aura" } rog_dbus = { path = "../rog-dbus" } @@ -14,7 +15,6 @@ rog_profiles = { path = "../rog-profiles" } rog_types = { path = "../rog-types" } daemon = { path = "../daemon" } gumdrop = "^0.8" -yansi-term = "^0.1" supergfxctl = { path = "../supergfx" } [dev-dependencies] diff --git a/asusctl/src/main.rs b/asusctl/src/main.rs index bdfc6ca8..448f04fd 100644 --- a/asusctl/src/main.rs +++ b/asusctl/src/main.rs @@ -9,16 +9,17 @@ use profiles_cli::ProfileCommand; use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2, ANIME_DATA_LEN}; use rog_aura::{self, AuraEffect}; use rog_dbus::RogDbusClient; -use rog_types::{ - supported::{ - AnimeSupportedFunctions, LedSupportedFunctions, PlatformProfileFunctions, - RogBiosSupportedFunctions, - }, +use rog_types::supported::{ + AnimeSupportedFunctions, LedSupportedFunctions, PlatformProfileFunctions, + RogBiosSupportedFunctions, }; -use supergfxctl::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}; -use std::{env::args, path::Path}; -use yansi_term::Colour::Green; -use yansi_term::Colour::Red; +use std::{env::args, path::Path, sync::mpsc::channel}; +use supergfxctl::{ + gfx_vendors::{GfxRequiredUserAction, GfxVendors}, + special::{get_asus_gsync_gfx_mode, has_asus_gsync_gfx_mode}, + zbus_proxy::GfxProxy, +}; +use zbus::Connection; #[derive(Default, Options)] struct CliStart { @@ -152,7 +153,7 @@ fn main() -> Result<(), Box> { match parsed.command { Some(CliCommand::LedMode(mode)) => handle_led_mode(&dbus, &supported.keyboard_led, &mode)?, Some(CliCommand::Profile(cmd)) => handle_profile(&dbus, &supported.platform_profile, &cmd)?, - Some(CliCommand::Graphics(cmd)) => do_gfx(&dbus, &supported.rog_bios_ctrl, cmd)?, + Some(CliCommand::Graphics(cmd)) => do_gfx(cmd)?, Some(CliCommand::Anime(cmd)) => handle_anime(&dbus, &supported.anime_ctrl, &cmd)?, Some(CliCommand::Bios(cmd)) => handle_bios_option(&dbus, &supported.rog_bios_ctrl, &cmd)?, None => { @@ -190,62 +191,67 @@ fn main() -> Result<(), Box> { Ok(()) } -fn do_gfx( - dbus: &RogDbusClient, - supported: &RogBiosSupportedFunctions, - command: GraphicsCommand, -) -> Result<(), Box> { +fn do_gfx(command: GraphicsCommand) -> Result<(), Box> { if command.mode.is_none() && !command.get && !command.pow && !command.force || command.help { println!("{}", command.self_usage()); } + let conn = Connection::new_system()?; + let proxy = GfxProxy::new(&conn)?; + + let (tx, rx) = channel(); + proxy.connect_notify_action(tx)?; + if let Some(mode) = command.mode { - if supported.dedicated_gfx_toggle && dbus.proxies().rog_bios().get_dedicated_gfx()? == 1 { + if has_asus_gsync_gfx_mode() && get_asus_gsync_gfx_mode()? == 1 { println!("You can not change modes until you turn dedicated/G-Sync off and reboot"); std::process::exit(-1); } println!("If anything fails check `journalctl -b -u asusd`\n"); - dbus.proxies().gfx().gfx_write_mode(&mode).map_err(|err|{ + proxy.gfx_write_mode(&mode).map_err(|err|{ println!("Graphics mode change error. You may be in an invalid state."); println!("Check mode with `asusctl graphics -g` and switch to opposite\nmode to correct it, e.g: if integrated, switch to hybrid, or if nvidia, switch to integrated.\n"); err })?; - let res = dbus.gfx_wait_changed()?; - match res { - GfxRequiredUserAction::Integrated => { - println!( - "You must change to Integrated before you can change to {}", - <&str>::from(mode) - ); - } - GfxRequiredUserAction::Logout | GfxRequiredUserAction::Reboot => { - println!( - "Graphics mode changed to {}. User action required is: {}", - <&str>::from(mode), - <&str>::from(&res) - ); - } - GfxRequiredUserAction::None => { - println!("Graphics mode changed to {}", <&str>::from(mode)); + + loop { + proxy.next_signal()?; + + if let Ok(res) = rx.try_recv() { + match res { + GfxRequiredUserAction::Integrated => { + println!( + "You must change to Integrated before you can change to {}", + <&str>::from(mode) + ); + } + GfxRequiredUserAction::Logout | GfxRequiredUserAction::Reboot => { + println!( + "Graphics mode changed to {}. User action required is: {}", + <&str>::from(mode), + <&str>::from(&res) + ); + } + GfxRequiredUserAction::None => { + println!("Graphics mode changed to {}", <&str>::from(mode)); + } + } } + std::process::exit(0) } - std::process::exit(0) } + if command.get { - let res = dbus.proxies().gfx().gfx_get_mode()?; + let res = proxy.gfx_get_mode()?; println!("Current graphics mode: {}", <&str>::from(res)); } if command.pow { - let res = dbus.proxies().gfx().gfx_get_pwr()?; - match res { - GfxPower::Active => { - println!("Current power status: {}", Red.paint(<&str>::from(&res))) - } - _ => println!("Current power status: {}", Green.paint(<&str>::from(&res))), - } + let res = proxy.gfx_get_pwr()?; + println!("Current power status: {}", <&str>::from(&res)); } + Ok(()) } diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index e6addacd..60e167f8 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -23,7 +23,6 @@ rog_aura = { path = "../rog-aura" } rog_types = { path = "../rog-types" } rog_profiles = { path = "../rog-profiles" } rog_dbus = { path = "../rog-dbus" } -supergfxctl = { path = "../supergfx" } rusb = "^0.8" udev = "^0.6" diff --git a/daemon/src/config.rs b/daemon/src/config.rs index 604c95b8..5f5b0a6c 100644 --- a/daemon/src/config.rs +++ b/daemon/src/config.rs @@ -3,7 +3,6 @@ use serde_derive::{Deserialize, Serialize}; use std::fs::{File, OpenOptions}; use std::io::{Read, Write}; -use crate::config_old::*; use crate::VERSION; pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf"; @@ -36,9 +35,6 @@ impl Config { config = Self::new(); } else if let Ok(data) = serde_json::from_str(&buf) { config = data; - } else if let Ok(data) = serde_json::from_str::(&buf) { - config = data.into_current(); - info!("Updated config version to: {}", VERSION); } else { warn!("Could not deserialise {}", CONFIG_PATH); panic!("Please remove {} then restart asusd", CONFIG_PATH); diff --git a/daemon/src/config_old.rs b/daemon/src/config_old.rs deleted file mode 100644 index 0271380b..00000000 --- a/daemon/src/config_old.rs +++ /dev/null @@ -1,53 +0,0 @@ -use serde_derive::{Deserialize, Serialize}; -use supergfxctl::gfx_vendors::GfxVendors; -use std::collections::BTreeMap; - -use crate::config::Config; - -#[derive(Deserialize, Serialize)] -pub struct ConfigV352 { - pub gfx_mode: GfxVendors, - pub gfx_last_mode: GfxVendors, - pub gfx_managed: bool, - pub gfx_vfio_enable: bool, - pub gfx_save_compute_vfio: bool, - pub active_profile: String, - pub toggle_profiles: Vec, - #[serde(skip)] - pub curr_fan_mode: u8, - pub bat_charge_limit: u8, - pub power_profiles: BTreeMap, -} - -impl ConfigV352 { - pub(crate) fn into_current(self) -> Config { - Config { - bat_charge_limit: self.bat_charge_limit, - } - } -} - -#[derive(Deserialize, Serialize)] -pub struct ConfigV372 { - pub gfx_mode: GfxVendors, - /// Only for informational purposes. - #[serde(skip)] - pub gfx_tmp_mode: Option, - pub gfx_managed: bool, - pub gfx_vfio_enable: bool, - pub active_profile: String, - pub toggle_profiles: Vec, - #[serde(skip)] - pub curr_fan_mode: u8, - pub bat_charge_limit: u8, - pub power_profiles: BTreeMap, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct ProfileV317 { - pub min_percentage: u8, - pub max_percentage: u8, - pub turbo: bool, - pub fan_preset: u8, - pub fan_curve: Option<()>, -} diff --git a/daemon/src/ctrl_profiles/controller.rs b/daemon/src/ctrl_profiles/controller.rs index 81f06945..c49d130c 100644 --- a/daemon/src/ctrl_profiles/controller.rs +++ b/daemon/src/ctrl_profiles/controller.rs @@ -40,19 +40,23 @@ impl GetSupported for CtrlPlatformProfile { fn get_supported() -> Self::A { if !Profile::is_platform_profile_supported() { - warn!(r#" + warn!( + r#" platform_profile kernel interface not found, your laptop does not support this, or the iterface is missing. To enable profile support you require a kernel with the following patch applied: https://lkml.org/lkml/2021/8/18/1022 -"#); +"# + ); } if !FanCurves::is_fan_curves_supported() { - info!(r#" + info!( + r#" fan curves kernel interface not found, your laptop does not support this, or the iterface is missing. To enable fan-curve support you require a kernel with the following patch applied: https://lkml.org/lkml/2021/8/20/232 Please note that as of 24/08/2021 this is not final. -"#); +"# + ); } PlatformProfileFunctions { platform_profile: Profile::is_platform_profile_supported(), diff --git a/daemon/src/daemon.rs b/daemon/src/daemon.rs index 808df1a0..3b4be38f 100644 --- a/daemon/src/daemon.rs +++ b/daemon/src/daemon.rs @@ -1,5 +1,6 @@ use daemon::ctrl_anime::config::AnimeConfig; use daemon::ctrl_anime::zbus::CtrlAnimeZbus; +use daemon::ctrl_anime::*; use daemon::ctrl_aura::config::AuraConfig; use daemon::ctrl_aura::controller::{ CtrlKbdLed, CtrlKbdLedReloader, CtrlKbdLedTask, CtrlKbdLedZbus, @@ -10,15 +11,11 @@ use daemon::ctrl_profiles::controller::CtrlPlatformTask; use daemon::{ config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported, }; -use daemon::{ctrl_anime::*}; use daemon::{ ctrl_profiles::{controller::CtrlPlatformProfile, zbus::ProfileZbus}, laptops::LaptopLedData, }; -use supergfxctl::config::GfxConfig; -use supergfxctl::controller::CtrlGraphics; -use supergfxctl::gfx_vendors::GfxVendors; use ::zbus::{fdo, Connection, ObjectServer}; use daemon::{CtrlTask, Reloadable, ZbusAdd}; use log::LevelFilter; @@ -34,7 +31,6 @@ use daemon::ctrl_rog_bios::CtrlRogBios; use zvariant::ObjectPath; static PROFILE_CONFIG_PATH: &str = "/etc/asusd/profile.conf"; -static GFX_CONFIG_PATH: &str = "/etc/asusd/supergfx.conf"; pub fn main() -> Result<(), Box> { let mut logger = env_logger::Builder::new(); @@ -86,10 +82,6 @@ fn start_daemon() -> Result<(), Box> { let config = Config::load(); let config = Arc::new(Mutex::new(config)); - let gfx_config = GfxConfig::load(GFX_CONFIG_PATH.into()); - let enable_gfx_switching = gfx_config.gfx_managed; - let gfx_config = Arc::new(Mutex::new(gfx_config)); - supported.add_to_server(&mut object_server); match CtrlRogBios::new(config.clone()) { @@ -105,7 +97,7 @@ fn start_daemon() -> Result<(), Box> { } } - match CtrlCharge::new(config.clone()) { + match CtrlCharge::new(config) { Ok(mut ctrl) => { // Do a reload of any settings ctrl.reload() @@ -173,48 +165,6 @@ fn start_daemon() -> Result<(), Box> { } } - // Graphics switching requires some checks on boot specifically for g-sync capable laptops - if enable_gfx_switching { - match CtrlGraphics::new(gfx_config.clone()) { - Ok(mut ctrl) => { - // Need to check if a laptop has the dedicated gfx switch - if CtrlRogBios::has_dedicated_gfx_toggle() { - if let Ok(ded) = CtrlRogBios::get_gfx_mode() { - if let Ok(config) = gfx_config.lock() { - if ded == 1 { - warn!("Dedicated GFX toggle is on but driver mode is not nvidia \nSetting to nvidia driver mode"); - let devices = ctrl.devices(); - let bus = ctrl.bus(); - CtrlGraphics::do_mode_setup_tasks( - GfxVendors::Nvidia, - false, - &devices, - &bus, - )?; - } else if ded == 0 { - info!("Dedicated GFX toggle is off"); - let devices = ctrl.devices(); - let bus = ctrl.bus(); - CtrlGraphics::do_mode_setup_tasks( - config.gfx_mode, - false, - &devices, - &bus, - )?; - } - } - } - } - ctrl.reload() - .unwrap_or_else(|err| error!("Gfx controller: {}", err)); - ctrl.add_to_server(&mut object_server); - } - Err(err) => { - error!("Gfx control: {}", err); - } - } - } - // TODO: implement messaging between threads to check fails // Run tasks diff --git a/daemon/src/error.rs b/daemon/src/error.rs index 27384ef0..bcfb9111 100644 --- a/daemon/src/error.rs +++ b/daemon/src/error.rs @@ -1,6 +1,4 @@ use rog_profiles::error::ProfileError; -use rog_types::error::GraphicsError; -use supergfxctl::error::GfxError; use std::convert::From; use std::fmt; @@ -19,7 +17,6 @@ pub enum RogError { MissingFunction(String), MissingLedBrightNode(String, std::io::Error), ReloadFail(String), - GfxSwitching(GfxError), Profiles(ProfileError), Initramfs(String), Modprobe(String), @@ -44,7 +41,6 @@ impl fmt::Display for RogError { RogError::MissingFunction(deets) => write!(f, "Missing functionality: {}", deets), RogError::MissingLedBrightNode(path, error) => write!(f, "Led node at {} is missing, please check you have the required patch or dkms module installed: {}", path, error), RogError::ReloadFail(deets) => write!(f, "Task error: {}", deets), - RogError::GfxSwitching(deets) => write!(f, "Graphics switching error: {}", deets), RogError::Profiles(deets) => write!(f, "Profile error: {}", deets), RogError::Initramfs(detail) => write!(f, "Initiramfs error: {}", detail), RogError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail), @@ -56,15 +52,6 @@ impl fmt::Display for RogError { impl std::error::Error for RogError {} -impl From for RogError { - fn from(err: GraphicsError) -> Self { - match err { - GraphicsError::ParseVendor => RogError::GfxSwitching(GfxError::ParseVendor), - GraphicsError::ParsePower => RogError::GfxSwitching(GfxError::ParsePower), - } - } -} - impl From for RogError { fn from(err: ProfileError) -> Self { RogError::Profiles(err) diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index 82984d50..766f9cd7 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -1,7 +1,6 @@ #![deny(unused_must_use)] /// Configuration loading, saving pub mod config; -pub(crate) mod config_old; /// Control of AniMe matrix display pub mod ctrl_anime; /// Keyboard LED brightness control, RGB, and LED display modes diff --git a/data/anime/asus/festive/Love u mom.gif b/data/anime/asus/festive/Love u mom.gif new file mode 100755 index 0000000000000000000000000000000000000000..8fd6d363b59843d8a7eeb2674d30f29a9c5c1b9b GIT binary patch literal 156884 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�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 literal 0 HcmV?d00001 diff --git a/data/anime/asus/festive/Mother's day.gif b/data/anime/asus/festive/Mother's day.gif new file mode 100755 index 0000000000000000000000000000000000000000..e95d0243f58119c796c8a66199e8c0aaa5a50330 GIT binary patch literal 98584 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

%+7z+PH}z|RuiNF+u1Fx<`+P)< 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

NX0%pMFz^ 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 i@(J5)X { anime: zbus_anime::AnimeProxy<'a>, charge: zbus_charge::ChargeProxy<'a>, - gfx: zbus_gfx::GfxProxy<'a>, led: zbus_led::LedProxy<'a>, profile: zbus_profile::ProfileProxy<'a>, rog_bios: zbus_rogbios::RogBiosProxy<'a>, @@ -38,7 +36,6 @@ impl<'a> DbusProxies<'a> { anime: zbus_anime::AnimeProxy::new(&conn)?, led: zbus_led::LedProxy::new(&conn)?, charge: zbus_charge::ChargeProxy::new(&conn)?, - gfx: zbus_gfx::GfxProxy::new(&conn)?, profile: zbus_profile::ProfileProxy::new(&conn)?, rog_bios: zbus_rogbios::RogBiosProxy::new(&conn)?, supported: zbus_supported::SupportProxy::new(&conn)?, @@ -52,7 +49,6 @@ impl<'a> DbusProxies<'a> { recv.receive_for(self.anime.proxy()); recv.receive_for(self.led.proxy()); recv.receive_for(self.charge.proxy()); - recv.receive_for(self.gfx.proxy()); recv.receive_for(self.profile.proxy()); recv.receive_for(self.rog_bios.proxy()); recv.receive_for(self.supported.proxy()); @@ -67,10 +63,6 @@ impl<'a> DbusProxies<'a> { &self.charge } - pub fn gfx(&self) -> &zbus_gfx::GfxProxy<'a> { - &self.gfx - } - pub fn led(&self) -> &zbus_led::LedProxy<'a> { &self.led } @@ -90,8 +82,6 @@ impl<'a> DbusProxies<'a> { // Signals separated out pub struct Signals { - pub gfx_vendor: Receiver, - pub gfx_action: Receiver, pub profile: Receiver, pub led_mode: Receiver, pub led_power_state: Receiver, @@ -105,16 +95,6 @@ impl Signals { #[inline] pub fn new(proxies: &DbusProxies) -> Result { Ok(Signals { - gfx_vendor: { - let (tx, rx) = channel(); - proxies.gfx.connect_notify_gfx(tx)?; - rx - }, - gfx_action: { - let (tx, rx) = channel(); - proxies.gfx.connect_notify_action(tx)?; - rx - }, profile: { let (tx, rx) = channel(); proxies.profile.connect_notify_profile(tx)?; @@ -182,26 +162,9 @@ impl<'a> RogDbusClient<'a> { recv.receive_for(self.proxies.anime.proxy()); recv.receive_for(self.proxies.led.proxy()); recv.receive_for(self.proxies.charge.proxy()); - recv.receive_for(self.proxies.gfx.proxy()); recv.receive_for(self.proxies.profile.proxy()); recv.receive_for(self.proxies.rog_bios.proxy()); recv.receive_for(self.proxies.supported.proxy()); recv } - - /* - * GFX - */ - pub fn gfx_wait_changed(&self) -> Result { - loop { - if let Ok(res) = self.proxies.gfx.proxy().next_signal() { - if res.is_none() { - if let Ok(stuff) = self.signals.gfx_action.try_recv() { - return Ok(stuff); - } - // return Ok("Failed for unknown reason".to_owned()); - } - } - } - } } diff --git a/rog-types/src/error.rs b/rog-types/src/error.rs deleted file mode 100644 index 432772f0..00000000 --- a/rog-types/src/error.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::error::Error; -use std::fmt; - -#[derive(Debug)] -pub enum GraphicsError { - ParseVendor, - ParsePower, -} - -impl fmt::Display for GraphicsError { - // This trait requires `fmt` with this exact signature. - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - GraphicsError::ParseVendor => write!(f, "Could not parse vendor name"), - GraphicsError::ParsePower => write!(f, "Could not parse dGPU power status"), - } - } -} - -impl Error for GraphicsError {} diff --git a/rog-types/src/lib.rs b/rog-types/src/lib.rs index 478c9e5b..eddc8a53 100644 --- a/rog-types/src/lib.rs +++ b/rog-types/src/lib.rs @@ -8,6 +8,4 @@ pub static DBUS_IFACE: &str = "org.asuslinux.Daemon"; pub mod supported; -pub mod error; - pub static VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/supergfx/Cargo.toml b/supergfx/Cargo.toml index c67e654f..1bcfc747 100644 --- a/supergfx/Cargo.toml +++ b/supergfx/Cargo.toml @@ -12,6 +12,26 @@ keywords = ["graphics", "nvidia", "switching"] edition = "2018" exclude = ["data"] +[features] +daemon = ["env_logger"] +cli = ["gumdrop"] +default = ["daemon", "cli"] + +[lib] +name = "supergfxctl" +path = "src/lib.rs" + +[[bin]] +name = "supergfxd" +path = "src/daemon.rs" +required-features = ["daemon"] + +[[bin]] +name = "supergfxctl" +path = "src/cli.rs" +required-features = ["cli"] +default-features = ["cli"] + [dependencies] serde = "^1.0" serde_derive = "^1.0" @@ -23,4 +43,7 @@ zvariant = "^2.8" zvariant_derive = "^2.8" logind-zbus = "^0.7.1" -sysfs-class = "^0.1.2" \ No newline at end of file +sysfs-class = "^0.1.2" + +env_logger = { version = "^0.8", optional = true } +gumdrop = { version = "^0.8", optional = true } \ No newline at end of file diff --git a/supergfx/Makefile b/supergfx/Makefile new file mode 100644 index 00000000..fb4f47ae --- /dev/null +++ b/supergfx/Makefile @@ -0,0 +1,74 @@ +VERSION := $(shell grep -Pm1 'version = "(\d.\d.\d)"' daemon/Cargo.toml | cut -d'"' -f2) + +INSTALL = install +INSTALL_PROGRAM = ${INSTALL} -D -m 0755 +INSTALL_DATA = ${INSTALL} -D -m 0644 + +prefix = /usr +exec_prefix = $(prefix) +bindir = $(exec_prefix)/bin +datarootdir = $(prefix)/share +libdir = $(exec_prefix)/lib + +BIN_SD := supergfxd +BIN_SC := supergfxctl +X11CFG := 90-nvidia-screen-G05.conf +PMRULES := 90-asusd-nvidia-pm.rules + +SRC := Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.rs') + +DEBUG ?= 0 +ifeq ($(DEBUG),0) + ARGS += --release + TARGET = release +endif + +VENDORED ?= 0 +ifeq ($(VENDORED),1) + ARGS += --frozen +endif + +all: build + +clean: + cargo clean + +distclean: + rm -rf .cargo vendor vendor.tar.xz + +install: + $(INSTALL_PROGRAM) "./target/release/$(BIN_SD)" "$(DESTDIR)$(bindir)/$(BIN_SD)" + $(INSTALL_PROGRAM) "./target/release/$(BIN_SC)" "$(DESTDIR)$(bindir)/$(BIN_SC)" + $(INSTALL_DATA) "./data/$(BIN_SD).service" "$(DESTDIR)$(libdir)/systemd/system/$(BIN_SD).service" + $(INSTALL_DATA) "./data/org.supergfxctl.Daemon.conf" "$(DESTDIR)$(datarootdir)/dbus-1/system.d/org.supergfxctl.Daemon.conf" + $(INSTALL_DATA) "./data/$(X11CFG)" "$(DESTDIR)$(datarootdir)/X11/xorg.conf.d/$(X11CFG)" + $(INSTALL_DATA) "./data/$(PMRULES)" "$(DESTDIR)$(libdir)/udev/rules.d/$(PMRULES)" + +uninstall: + rm -f "$(DESTDIR)$(bindir)/$(BIN_SC)" + rm -f "$(DESTDIR)$(bindir)/$(BIN_SD)" + rm -f "$(DESTDIR)$(libdir)/systemd/system/$(BIN_SD).service" + rm -f "$(DESTDIR)$(datarootdir)/dbus-1/system.d/org.supergfxctl.Daemon.conf" + rm -f "$(DESTDIR)$(datarootdir)/X11/xorg.conf.d/$(X11CFG)" + rm -f "$(DESTDIR)$(libdir)/udev/rules.d/$(PMRULES)" + +update: + cargo update + +vendor: + mkdir -p .cargo + cargo vendor | head -n -1 > .cargo/config + echo 'directory = "vendor"' >> .cargo/config + mv .cargo/config ./cargo-config + rm -rf .cargo + tar pcfJ vendor_asusctl_$(VERSION).tar.xz vendor + rm -rf vendor + +build: +ifeq ($(VENDORED),1) + @echo "version = $(VERSION)" + tar pxf vendor_asusctl_$(VERSION).tar.xz +endif + cargo build $(ARGS) + +.PHONY: all clean distclean install uninstall update build diff --git a/data/90-asusd-nvidia-pm.rules b/supergfx/data/90-asusd-nvidia-pm.rules similarity index 100% rename from data/90-asusd-nvidia-pm.rules rename to supergfx/data/90-asusd-nvidia-pm.rules diff --git a/data/90-nvidia-screen-G05.conf b/supergfx/data/90-nvidia-screen-G05.conf similarity index 100% rename from data/90-nvidia-screen-G05.conf rename to supergfx/data/90-nvidia-screen-G05.conf diff --git a/supergfx/data/org.supergfxctl.Daemon.conf b/supergfx/data/org.supergfxctl.Daemon.conf new file mode 100644 index 00000000..77fa6276 --- /dev/null +++ b/supergfx/data/org.supergfxctl.Daemon.conf @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/supergfx/data/supergfxd.service b/supergfx/data/supergfxd.service new file mode 100644 index 00000000..cbe73694 --- /dev/null +++ b/supergfx/data/supergfxd.service @@ -0,0 +1,16 @@ +[Unit] +Description=SUPERGFX +StartLimitInterval=200 +StartLimitBurst=2 +Before=display-manager.service + +[Service] +Environment=IS_SUPERGFX_SERVICE=1 +ExecStart=/usr/bin/supergfxd +Restart=on-failure +Restart=always +RestartSec=1 +Type=dbus +BusName=org.supergfxctl.Daemon +SELinuxContext=system_u:system_r:unconfined_t:s0 +#SELinuxContext=system_u:object_r:modules_object_t:s0 \ No newline at end of file diff --git a/supergfx/src/cli.rs b/supergfx/src/cli.rs new file mode 100644 index 00000000..f2926cae --- /dev/null +++ b/supergfx/src/cli.rs @@ -0,0 +1,105 @@ +use std::{env::args, sync::mpsc::channel}; +use supergfxctl::{ + gfx_vendors::{GfxRequiredUserAction, GfxVendors}, + special::{get_asus_gsync_gfx_mode, has_asus_gsync_gfx_mode}, + zbus_proxy::GfxProxy, +}; + +use gumdrop::Options; +use zbus::Connection; + +#[derive(Default, Options)] +struct CliStart { + #[options(help = "print help message")] + help: bool, + #[options( + meta = "", + help = "Set graphics mode: " + )] + mode: Option, + #[options(help = "Get the current mode")] + get: bool, + #[options(help = "Get the current power status")] + pow: bool, + #[options(help = "Do not ask for confirmation")] + force: bool, +} + +fn main() -> Result<(), Box> { + let args: Vec = args().skip(1).collect(); + + match CliStart::parse_args_default(&args) { + Ok(command) => { + do_gfx(command)?; + } + Err(err) => { + eprintln!("source {}", err); + std::process::exit(2); + } + } + + Ok(()) +} + +fn do_gfx(command: CliStart) -> Result<(), Box> { + if command.mode.is_none() && !command.get && !command.pow && !command.force || command.help { + println!("{}", command.self_usage()); + } + + let conn = Connection::new_system()?; + let proxy = GfxProxy::new(&conn)?; + + let (tx, rx) = channel(); + proxy.connect_notify_action(tx)?; + + if let Some(mode) = command.mode { + if has_asus_gsync_gfx_mode() && get_asus_gsync_gfx_mode()? == 1 { + println!("You can not change modes until you turn dedicated/G-Sync off and reboot"); + std::process::exit(-1); + } + + println!("If anything fails check `journalctl -b -u supergfxd`\n"); + + proxy.gfx_write_mode(&mode).map_err(|err|{ + println!("Graphics mode change error. You may be in an invalid state."); + println!("Check mode with `-g` and switch to opposite\nmode to correct it, e.g: if integrated, switch to hybrid, or if nvidia, switch to integrated.\n"); + err + })?; + + loop { + proxy.next_signal()?; + + if let Ok(res) = rx.try_recv() { + match res { + GfxRequiredUserAction::Integrated => { + println!( + "You must change to Integrated before you can change to {}", + <&str>::from(mode) + ); + } + GfxRequiredUserAction::Logout | GfxRequiredUserAction::Reboot => { + println!( + "Graphics mode changed to {}. User action required is: {}", + <&str>::from(mode), + <&str>::from(&res) + ); + } + GfxRequiredUserAction::None => { + println!("Graphics mode changed to {}", <&str>::from(mode)); + } + } + } + std::process::exit(0) + } + } + if command.get { + let res = proxy.gfx_get_mode()?; + println!("Current graphics mode: {}", <&str>::from(res)); + } + if command.pow { + let res = proxy.gfx_get_pwr()?; + println!("Current power status: {}", <&str>::from(&res)); + } + + Ok(()) +} diff --git a/supergfx/src/daemon.rs b/supergfx/src/daemon.rs new file mode 100644 index 00000000..f898f046 --- /dev/null +++ b/supergfx/src/daemon.rs @@ -0,0 +1,105 @@ +use std::{ + env, + error::Error, + sync::{Arc, Mutex}, +}; + +use log::{error, info, warn, LevelFilter}; +use std::io::Write; +use supergfxctl::{ + config::GfxConfig, + controller::CtrlGraphics, + error::GfxError, + gfx_vendors::GfxVendors, + special::{get_asus_gsync_gfx_mode, has_asus_gsync_gfx_mode}, + DBUS_DEST_NAME, GFX_CONFIG_PATH, +}; +use zbus::{fdo, Connection, ObjectServer}; + +pub fn main() -> Result<(), Box> { + let mut logger = env_logger::Builder::new(); + logger + .target(env_logger::Target::Stdout) + .format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args())) + .filter(None, LevelFilter::Info) + .init(); + + let is_service = match env::var_os("IS_SUPERGFX_SERVICE") { + Some(val) => val == "1", + None => false, + }; + + if !is_service { + println!("supergfxd schould be only run from the right systemd service"); + println!( + "do not run in your terminal, if you need an logs please use journalctl -b -u supergfxd" + ); + println!("supergfxd will now exit"); + return Ok(()); + } + + start_daemon() +} + +fn start_daemon() -> Result<(), Box> { + // Start zbus server + let connection = Connection::new_system()?; + fdo::DBusProxy::new(&connection)?.request_name( + DBUS_DEST_NAME, + fdo::RequestNameFlags::ReplaceExisting.into(), + )?; + + let mut object_server = ObjectServer::new(&connection); + + let config = GfxConfig::load(GFX_CONFIG_PATH.into()); + let enable_gfx_switching = config.gfx_managed; + let config = Arc::new(Mutex::new(config)); + + // Graphics switching requires some checks on boot specifically for g-sync capable laptops + if enable_gfx_switching { + match CtrlGraphics::new(config.clone()) { + Ok(mut ctrl) => { + // Need to check if a laptop has the dedicated gfx switch + if has_asus_gsync_gfx_mode() { + do_asus_laptop_checks(&ctrl, config)?; + } + + ctrl.reload() + .unwrap_or_else(|err| error!("Gfx controller: {}", err)); + ctrl.add_to_server(&mut object_server); + } + Err(err) => { + error!("Gfx control: {}", err); + } + } + } + + // Loop to check errors and iterate zbus server + loop { + if let Err(err) = object_server.try_handle_next() { + error!("{}", err); + } + } +} + +fn do_asus_laptop_checks( + ctrl: &CtrlGraphics, + config: Arc>, +) -> Result<(), GfxError> { + if let Ok(ded) = get_asus_gsync_gfx_mode() { + if let Ok(config) = config.lock() { + if ded == 1 { + warn!("Dedicated GFX toggle is on but driver mode is not nvidia \nSetting to nvidia driver mode"); + let devices = ctrl.devices(); + let bus = ctrl.bus(); + CtrlGraphics::do_mode_setup_tasks(GfxVendors::Nvidia, false, &devices, &bus)?; + } else if ded == 0 { + info!("Dedicated GFX toggle is off"); + let devices = ctrl.devices(); + let bus = ctrl.bus(); + CtrlGraphics::do_mode_setup_tasks(config.gfx_mode, false, &devices, &bus)?; + } + } + } + Ok(()) +} diff --git a/supergfx/src/error.rs b/supergfx/src/error.rs index b5e94612..d47c7d1a 100644 --- a/supergfx/src/error.rs +++ b/supergfx/src/error.rs @@ -69,4 +69,4 @@ impl From for GfxError { fn from(err: std::io::Error) -> Self { GfxError::Io(err) } -} \ No newline at end of file +} diff --git a/supergfx/src/lib.rs b/supergfx/src/lib.rs index 484aece7..4fbc1af3 100644 --- a/supergfx/src/lib.rs +++ b/supergfx/src/lib.rs @@ -1,12 +1,17 @@ -pub mod error; pub mod config; -pub mod gfx_vendors; pub mod controller; -pub mod system; +pub mod error; +pub mod gfx_vendors; /// Special-case functions for check/read/write of key functions on unique laptops /// such as the G-Sync mode available on some ASUS ROG laptops -pub(crate) mod special; -pub mod zbus; +pub mod special; +pub mod system; +pub mod zbus_iface; +pub mod zbus_proxy; + +pub const GFX_CONFIG_PATH: &str = "/etc/supergfxd.conf"; +pub const DBUS_DEST_NAME: &str = "org.supergfxctl.Daemon"; +pub const DBUS_IFACE_PATH: &str = "/org/supergfxctl/Gfx"; const NVIDIA_DRIVERS: [&str; 4] = ["nvidia_drm", "nvidia_modeset", "nvidia_uvm", "nvidia"]; diff --git a/supergfx/src/special.rs b/supergfx/src/special.rs index fdccc37e..ee1ef4d5 100644 --- a/supergfx/src/special.rs +++ b/supergfx/src/special.rs @@ -5,11 +5,11 @@ use crate::error::GfxError; static ASUS_SWITCH_GRAPHIC_MODE: &str = "/sys/firmware/efi/efivars/AsusSwitchGraphicMode-607005d5-3f75-4b2e-98f0-85ba66797a3e"; -pub(crate) fn has_asus_gsync_gfx_mode() -> bool { +pub fn has_asus_gsync_gfx_mode() -> bool { Path::new(ASUS_SWITCH_GRAPHIC_MODE).exists() } -pub(crate) fn get_asus_gsync_gfx_mode() -> Result { +pub fn get_asus_gsync_gfx_mode() -> Result { let path = ASUS_SWITCH_GRAPHIC_MODE; let mut file = OpenOptions::new() .read(true) @@ -22,4 +22,4 @@ pub(crate) fn get_asus_gsync_gfx_mode() -> Result { let idx = data.len() - 1; Ok(data[idx] as i8) -} \ No newline at end of file +} diff --git a/supergfx/src/zbus.rs b/supergfx/src/zbus_iface.rs similarity index 87% rename from supergfx/src/zbus.rs rename to supergfx/src/zbus_iface.rs index b625912b..c6fd371c 100644 --- a/supergfx/src/zbus.rs +++ b/supergfx/src/zbus_iface.rs @@ -1,12 +1,15 @@ +use ::zbus::dbus_interface; use log::{error, info, warn}; use zvariant::ObjectPath; -use ::zbus::dbus_interface; -use crate::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}; +use crate::{ + gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}, + DBUS_IFACE_PATH, +}; use super::controller::CtrlGraphics; -#[dbus_interface(name = "org.asuslinux.Daemon")] +#[dbus_interface(name = "org.supergfxctl.Daemon")] impl CtrlGraphics { fn vendor(&self) -> zbus::fdo::Result { self.get_gfx_mode().map_err(|err| { @@ -28,10 +31,13 @@ impl CtrlGraphics { error!("GFX: {}", err); zbus::fdo::Error::Failed(format!("GFX fail: {}", err)) })?; - self.notify_gfx(&vendor) - .unwrap_or_else(|err| warn!("GFX: {}", err)); + self.notify_action(&msg) .unwrap_or_else(|err| warn!("GFX: {}", err)); + + self.notify_gfx(&vendor) + .unwrap_or_else(|err| warn!("GFX: {}", err)); + Ok(msg) } @@ -45,7 +51,7 @@ impl CtrlGraphics { impl CtrlGraphics { pub fn add_to_server(self, server: &mut zbus::ObjectServer) { server - .at(&ObjectPath::from_str_unchecked("/org/asuslinux/Gfx"), self) + .at(&ObjectPath::from_str_unchecked(DBUS_IFACE_PATH), self) .map_err(|err| { warn!("GFX: CtrlGraphics: add_to_server {}", err); err diff --git a/rog-dbus/src/zbus_gfx.rs b/supergfx/src/zbus_proxy.rs similarity index 70% rename from rog-dbus/src/zbus_gfx.rs rename to supergfx/src/zbus_proxy.rs index a84f1e6f..2f7a1725 100644 --- a/rog-dbus/src/zbus_gfx.rs +++ b/supergfx/src/zbus_proxy.rs @@ -1,7 +1,7 @@ //! # DBus interface proxy for: `org.asuslinux.Gfx` //! //! This code was generated by `zbus-xmlgen` `1.0.0` from DBus introspection data. -//! Source: `Interface '/org/asuslinux/Gfx' from service 'org.asuslinux.Daemon' on system bus`. +//! Source: `Interface '/org/supergfxctl/Gfx' from service 'org.asuslinux.Daemon' on system bus`. //! //! You may prefer to adapt it, instead of using it verbatim. //! @@ -19,15 +19,16 @@ //! //! …consequently `zbus-xmlgen` did not generate code for the above interfaces. -use std::sync::mpsc::Sender; +use std::sync::mpsc::{Receiver, Sender}; -use supergfxctl::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}; -use zbus::{dbus_proxy, Connection, Result}; +use zbus::{dbus_proxy, Connection, Message, Result}; -#[dbus_proxy( - interface = "org.asuslinux.Daemon", - default_path = "/org/asuslinux/Gfx" -)] +use crate::{ + gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}, + DBUS_IFACE_PATH, +}; + +#[dbus_proxy(interface = "org.supergfxctl.Daemon")] trait Daemon { /// Power method fn power(&self) -> zbus::Result; @@ -47,12 +48,25 @@ trait Daemon { fn notify_gfx(&self, vendor: GfxVendors) -> zbus::Result<()>; } -pub struct GfxProxy<'a>(DaemonProxy<'a>); +pub struct GfxProxy<'a>(pub DaemonProxy<'a>); impl<'a> GfxProxy<'a> { #[inline] pub fn new(conn: &Connection) -> Result { - Ok(GfxProxy(DaemonProxy::new(conn)?)) + let proxy = DaemonProxy::new_for(conn, "org.supergfxctl.Daemon", DBUS_IFACE_PATH)?; + Ok(GfxProxy(proxy)) + } + + #[inline] + pub fn new_for(conn: &Connection, destination: &'a str, path: &'a str) -> Result { + let proxy = DaemonProxy::new_for(conn, destination, path)?; + Ok(GfxProxy(proxy)) + } + + #[inline] + pub fn new_for_owned(conn: Connection, destination: String, path: String) -> Result { + let proxy = DaemonProxy::new_for_owned(conn, destination, path)?; + Ok(GfxProxy(proxy)) } #[inline] @@ -95,4 +109,9 @@ impl<'a> GfxProxy<'a> { Ok(()) }) } + + #[inline] + pub fn next_signal(&self) -> Result> { + self.0.next_signal() + } } From 326ca378476060cc55dc3b745412441d1866136f Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Thu, 26 Aug 2021 13:15:43 +1200 Subject: [PATCH 05/11] rog-supported crate --- CHANGELOG.md | 3 +++ Cargo.lock | 14 +++++++------- Cargo.toml | 2 +- asus-notify/Cargo.toml | 2 +- asus-notify/src/main.rs | 1 + asusctl/Cargo.toml | 2 +- asusctl/src/main.rs | 17 +++++++++-------- daemon-user/Cargo.toml | 2 +- daemon-user/src/daemon.rs | 8 ++++---- daemon/Cargo.toml | 2 +- daemon/src/ctrl_anime/mod.rs | 2 +- daemon/src/ctrl_aura/controller.rs | 2 +- daemon/src/ctrl_charge.rs | 2 +- daemon/src/ctrl_profiles/controller.rs | 2 +- daemon/src/ctrl_rog_bios.rs | 2 +- daemon/src/ctrl_supported.rs | 2 +- daemon/src/daemon.rs | 12 ++++++------ rog-dbus/Cargo.toml | 2 +- rog-dbus/src/zbus_supported.rs | 2 +- {rog-types => rog-supported}/Cargo.toml | 4 ++-- .../supported.rs => rog-supported/src/lib.rs | 2 ++ rog-types/src/lib.rs | 11 ----------- supergfx/Cargo.toml | 2 +- supergfx/src/daemon.rs | 4 ++-- supergfx/src/lib.rs | 3 ++- 25 files changed, 52 insertions(+), 55 deletions(-) rename {rog-types => rog-supported}/Cargo.toml (79%) rename rog-types/src/supported.rs => rog-supported/src/lib.rs (98%) delete mode 100644 rog-types/src/lib.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 18b391d8..6bca7573 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + Support 8bit RGB, RGBA, 16bit Greyscalw, RGB, RGBA + add `AsusImage` type for slanted-template pixel-perfect images + `BREAKING:` plain `Image` with time period is changed and old anime configs break as a result (sorry) +### Changed +- Graphics control: + + BREAKING: graphics control is pulled out of asusd and moved to new crate; supergfxctl # [3.7.2] - 2021-08-02 ### Added diff --git a/Cargo.lock b/Cargo.lock index 45f268dc..7ccad0b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,7 +37,7 @@ dependencies = [ "rog_aura", "rog_dbus", "rog_profiles", - "rog_types", + "rog_supported", "serde_json", "supergfxctl", "zbus", @@ -55,7 +55,7 @@ dependencies = [ "rog_aura", "rog_dbus", "rog_profiles", - "rog_types", + "rog_supported", "supergfxctl", "tinybmp", "zbus", @@ -217,7 +217,7 @@ dependencies = [ "rog_aura", "rog_dbus", "rog_profiles", - "rog_types", + "rog_supported", "rusb", "serde", "serde_derive", @@ -237,7 +237,7 @@ dependencies = [ "dirs 3.0.2", "rog_anime", "rog_dbus", - "rog_types", + "rog_supported", "serde", "serde_derive", "serde_json", @@ -924,7 +924,7 @@ dependencies = [ "rog_anime", "rog_aura", "rog_profiles", - "rog_types", + "rog_supported", "supergfxctl", "zbus", "zbus_macros", @@ -942,7 +942,7 @@ dependencies = [ ] [[package]] -name = "rog_types" +name = "rog_supported" version = "3.2.0" dependencies = [ "rog_aura", @@ -1068,7 +1068,7 @@ dependencies = [ [[package]] name = "supergfxctl" -version = "1.1.0" +version = "2.0.0" dependencies = [ "env_logger", "gumdrop", diff --git a/Cargo.toml b/Cargo.toml index e4983797..abc0d8d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-types", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "supergfx"] +members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-supported", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "supergfx"] [profile.release] lto = true diff --git a/asus-notify/Cargo.toml b/asus-notify/Cargo.toml index a4d6d103..c81cab9c 100644 --- a/asus-notify/Cargo.toml +++ b/asus-notify/Cargo.toml @@ -12,7 +12,7 @@ zbus = "^1.9" serde_json = "^1.0" rog_dbus = { path = "../rog-dbus" } rog_aura = { path = "../rog-aura" } -rog_types = { path = "../rog-types" } +rog_supported = { path = "../rog-supported" } rog_profiles = { path = "../rog-profiles" } supergfxctl = { path = "../supergfx" } diff --git a/asus-notify/src/main.rs b/asus-notify/src/main.rs index bb3593bd..76353279 100644 --- a/asus-notify/src/main.rs +++ b/asus-notify/src/main.rs @@ -37,6 +37,7 @@ macro_rules! base_notification { fn main() -> Result<(), Box> { println!("asus-notify version {}", env!("CARGO_PKG_VERSION")); println!(" rog-dbus version {}", rog_dbus::VERSION); + println!("supergfxctl version {}", supergfxctl::VERSION); let (proxies, conn) = DbusProxies::new()?; let signals = Signals::new(&proxies)?; diff --git a/asusctl/Cargo.toml b/asusctl/Cargo.toml index 3c182768..23a6f1d8 100644 --- a/asusctl/Cargo.toml +++ b/asusctl/Cargo.toml @@ -12,7 +12,7 @@ rog_anime = { path = "../rog-anime" } rog_aura = { path = "../rog-aura" } rog_dbus = { path = "../rog-dbus" } rog_profiles = { path = "../rog-profiles" } -rog_types = { path = "../rog-types" } +rog_supported = { path = "../rog-supported" } daemon = { path = "../daemon" } gumdrop = "^0.8" supergfxctl = { path = "../supergfx" } diff --git a/asusctl/src/main.rs b/asusctl/src/main.rs index 448f04fd..09f6ad76 100644 --- a/asusctl/src/main.rs +++ b/asusctl/src/main.rs @@ -9,7 +9,7 @@ use profiles_cli::ProfileCommand; use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2, ANIME_DATA_LEN}; use rog_aura::{self, AuraEffect}; use rog_dbus::RogDbusClient; -use rog_types::supported::{ +use rog_supported::{ AnimeSupportedFunctions, LedSupportedFunctions, PlatformProfileFunctions, RogBiosSupportedFunctions, }; @@ -139,14 +139,15 @@ fn main() -> Result<(), Box> { if parsed.version { println!("\nApp and daemon versions:"); - println!(" asusctl v{}", env!("CARGO_PKG_VERSION")); - println!(" asusd v{}", daemon::VERSION); + println!(" asusctl v{}", env!("CARGO_PKG_VERSION")); + println!(" asusd v{}", daemon::VERSION); println!("\nComponent crate versions:"); - println!(" rog-anime v{}", rog_anime::VERSION); - println!(" rog-aura v{}", rog_aura::VERSION); - println!(" rog-dbus v{}", rog_dbus::VERSION); - println!("rog-profiles v{}", rog_profiles::VERSION); - println!(" rog-types v{}", rog_types::VERSION); + println!(" rog-anime v{}", rog_anime::VERSION); + println!(" rog-aura v{}", rog_aura::VERSION); + println!(" rog-dbus v{}", rog_dbus::VERSION); + println!(" rog-profiles v{}", rog_profiles::VERSION); + println!("rog-supported v{}", rog_supported::VERSION); + println!(" supergfxctl v{}", supergfxctl::VERSION); return Ok(()); } diff --git a/daemon-user/Cargo.toml b/daemon-user/Cargo.toml index cf7f22cd..85751dc8 100644 --- a/daemon-user/Cargo.toml +++ b/daemon-user/Cargo.toml @@ -21,7 +21,7 @@ serde_derive = "^1.0" rog_anime = { path = "../rog-anime" } rog_dbus = { path = "../rog-dbus" } -rog_types = { path = "../rog-types" } +rog_supported = { path = "../rog-supported" } dirs = "3.0.1" diff --git a/daemon-user/src/daemon.rs b/daemon-user/src/daemon.rs index 14cdc532..d105f1de 100644 --- a/daemon-user/src/daemon.rs +++ b/daemon-user/src/daemon.rs @@ -12,10 +12,10 @@ use zbus::{fdo, Connection}; use std::sync::atomic::AtomicBool; fn main() -> Result<(), Box> { - println!(" user daemon v{}", rog_user::VERSION); - println!(" rog-anime v{}", rog_anime::VERSION); - println!(" rog-dbus v{}", rog_dbus::VERSION); - println!(" rog-types v{}", rog_types::VERSION); + println!(" user daemon v{}", rog_user::VERSION); + println!(" rog-anime v{}", rog_anime::VERSION); + println!(" rog-dbus v{}", rog_dbus::VERSION); + println!("rog-supported v{}", rog_supported::VERSION); let (client, _) = RogDbusClient::new().unwrap(); let supported = client.proxies().supported().get_supported_functions()?; diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index 60e167f8..f98837af 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -20,7 +20,7 @@ path = "src/daemon.rs" [dependencies] rog_anime = { path = "../rog-anime" } rog_aura = { path = "../rog-aura" } -rog_types = { path = "../rog-types" } +rog_supported = { path = "../rog-supported" } rog_profiles = { path = "../rog-profiles" } rog_dbus = { path = "../rog-dbus" } rusb = "^0.8" diff --git a/daemon/src/ctrl_anime/mod.rs b/daemon/src/ctrl_anime/mod.rs index 61c603a0..e96d2dc0 100644 --- a/daemon/src/ctrl_anime/mod.rs +++ b/daemon/src/ctrl_anime/mod.rs @@ -11,7 +11,7 @@ use rog_anime::{ }, ActionData, AnimeDataBuffer, AnimePacketType, ANIME_DATA_LEN, }; -use rog_types::supported::AnimeSupportedFunctions; +use rog_supported::AnimeSupportedFunctions; use rusb::{Device, DeviceHandle}; use std::{ error::Error, diff --git a/daemon/src/ctrl_aura/controller.rs b/daemon/src/ctrl_aura/controller.rs index 5cef3651..e294fa04 100644 --- a/daemon/src/ctrl_aura/controller.rs +++ b/daemon/src/ctrl_aura/controller.rs @@ -15,7 +15,7 @@ use rog_aura::{ }, AuraEffect, LedBrightness, LED_MSG_LEN, }; -use rog_types::supported::LedSupportedFunctions; +use rog_supported::LedSupportedFunctions; use std::io::{Read, Write}; use std::path::Path; use std::sync::Arc; diff --git a/daemon/src/ctrl_charge.rs b/daemon/src/ctrl_charge.rs index dbd7202d..b5a71594 100644 --- a/daemon/src/ctrl_charge.rs +++ b/daemon/src/ctrl_charge.rs @@ -1,7 +1,7 @@ use crate::{config::Config, error::RogError, GetSupported}; //use crate::dbus::DbusEvents; use log::{info, warn}; -use rog_types::supported::ChargeSupportedFunctions; +use rog_supported::ChargeSupportedFunctions; use std::fs::OpenOptions; use std::io::Write; use std::path::Path; diff --git a/daemon/src/ctrl_profiles/controller.rs b/daemon/src/ctrl_profiles/controller.rs index c49d130c..3faffcec 100644 --- a/daemon/src/ctrl_profiles/controller.rs +++ b/daemon/src/ctrl_profiles/controller.rs @@ -3,7 +3,7 @@ use crate::GetSupported; use log::{info, warn}; use rog_profiles::error::ProfileError; use rog_profiles::{FanCurves, Profile}; -use rog_types::supported::PlatformProfileFunctions; +use rog_supported::PlatformProfileFunctions; use std::sync::Arc; use std::sync::Mutex; diff --git a/daemon/src/ctrl_rog_bios.rs b/daemon/src/ctrl_rog_bios.rs index e2817e8b..d9fbef7c 100644 --- a/daemon/src/ctrl_rog_bios.rs +++ b/daemon/src/ctrl_rog_bios.rs @@ -1,6 +1,6 @@ use crate::{config::Config, error::RogError, GetSupported}; use log::{error, info, warn}; -use rog_types::supported::RogBiosSupportedFunctions; +use rog_supported::RogBiosSupportedFunctions; use std::fs::OpenOptions; use std::io::BufRead; use std::io::{Read, Write}; diff --git a/daemon/src/ctrl_supported.rs b/daemon/src/ctrl_supported.rs index 8f0f30de..6c1a04e6 100644 --- a/daemon/src/ctrl_supported.rs +++ b/daemon/src/ctrl_supported.rs @@ -9,7 +9,7 @@ use crate::{ ctrl_profiles::controller::CtrlPlatformProfile, ctrl_rog_bios::CtrlRogBios, GetSupported, }; -use rog_types::supported::{ +use rog_supported::{ AnimeSupportedFunctions, ChargeSupportedFunctions, LedSupportedFunctions, PlatformProfileFunctions, RogBiosSupportedFunctions, }; diff --git a/daemon/src/daemon.rs b/daemon/src/daemon.rs index 3b4be38f..105cfba9 100644 --- a/daemon/src/daemon.rs +++ b/daemon/src/daemon.rs @@ -54,12 +54,12 @@ pub fn main() -> Result<(), Box> { return Ok(()); } - info!(" daemon v{}", daemon::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-types v{}", rog_types::VERSION); + info!(" daemon v{}", daemon::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-supported v{}", rog_supported::VERSION); start_daemon()?; Ok(()) diff --git a/rog-dbus/Cargo.toml b/rog-dbus/Cargo.toml index aa97b47d..62011f70 100644 --- a/rog-dbus/Cargo.toml +++ b/rog-dbus/Cargo.toml @@ -13,7 +13,7 @@ edition = "2018" rog_anime = { path = "../rog-anime" } rog_aura = { path = "../rog-aura" } rog_profiles = { path = "../rog-profiles" } -rog_types = { path = "../rog-types" } +rog_supported = { path = "../rog-supported" } supergfxctl = { path = "../supergfx" } zbus = "^1.9" zbus_macros = "^1.9" diff --git a/rog-dbus/src/zbus_supported.rs b/rog-dbus/src/zbus_supported.rs index 565d7e92..54c06fb8 100644 --- a/rog-dbus/src/zbus_supported.rs +++ b/rog-dbus/src/zbus_supported.rs @@ -19,7 +19,7 @@ //! //! …consequently `zbus-xmlgen` did not generate code for the above interfaces. -use rog_types::supported::SupportedFunctions; +use rog_supported::SupportedFunctions; use zbus::{dbus_proxy, Connection, Result}; #[dbus_proxy( diff --git a/rog-types/Cargo.toml b/rog-supported/Cargo.toml similarity index 79% rename from rog-types/Cargo.toml rename to rog-supported/Cargo.toml index 34d07c45..6da0212e 100644 --- a/rog-types/Cargo.toml +++ b/rog-supported/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "rog_types" +name = "rog_supported" version = "3.2.0" license = "MPL-2.0" readme = "README.md" authors = ["Luke "] repository = "https://gitlab.com/asus-linux/asus-nb-ctrl" homepage = "https://gitlab.com/asus-linux/asus-nb-ctrl" -description = "A small library of effect types and conversions for ROG Aura" +description = "Helper to determine what is supported by asus laptops" edition = "2018" [dependencies] diff --git a/rog-types/src/supported.rs b/rog-supported/src/lib.rs similarity index 98% rename from rog-types/src/supported.rs rename to rog-supported/src/lib.rs index a132a814..203b0390 100644 --- a/rog-types/src/supported.rs +++ b/rog-supported/src/lib.rs @@ -1,3 +1,5 @@ +pub static VERSION: &str = env!("CARGO_PKG_VERSION"); + use rog_aura::AuraModeNum; use serde_derive::{Deserialize, Serialize}; use std::fmt; diff --git a/rog-types/src/lib.rs b/rog-types/src/lib.rs deleted file mode 100644 index eddc8a53..00000000 --- a/rog-types/src/lib.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! This crate is intended for shared types (eg, between daemon and CLI), or -//! for types that might be useful in third-party crates perhaps for -//! sending messages over dbus wire - -pub static DBUS_NAME: &str = "org.asuslinux.Daemon"; -pub static DBUS_PATH: &str = "/org/asuslinux/Daemon"; -pub static DBUS_IFACE: &str = "org.asuslinux.Daemon"; - -pub mod supported; - -pub static VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/supergfx/Cargo.toml b/supergfx/Cargo.toml index 1bcfc747..7c0a93f3 100644 --- a/supergfx/Cargo.toml +++ b/supergfx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "supergfxctl" -version = "1.1.0" +version = "2.0.0" license = "MPL-2.0" readme = "README.md" authors = ["Luke "] diff --git a/supergfx/src/daemon.rs b/supergfx/src/daemon.rs index f898f046..5d684497 100644 --- a/supergfx/src/daemon.rs +++ b/supergfx/src/daemon.rs @@ -12,7 +12,7 @@ use supergfxctl::{ error::GfxError, gfx_vendors::GfxVendors, special::{get_asus_gsync_gfx_mode, has_asus_gsync_gfx_mode}, - DBUS_DEST_NAME, GFX_CONFIG_PATH, + CONFIG_PATH, DBUS_DEST_NAME, }; use zbus::{fdo, Connection, ObjectServer}; @@ -51,7 +51,7 @@ fn start_daemon() -> Result<(), Box> { let mut object_server = ObjectServer::new(&connection); - let config = GfxConfig::load(GFX_CONFIG_PATH.into()); + let config = GfxConfig::load(CONFIG_PATH.into()); let enable_gfx_switching = config.gfx_managed; let config = Arc::new(Mutex::new(config)); diff --git a/supergfx/src/lib.rs b/supergfx/src/lib.rs index 4fbc1af3..52a44f2b 100644 --- a/supergfx/src/lib.rs +++ b/supergfx/src/lib.rs @@ -9,7 +9,8 @@ pub mod system; pub mod zbus_iface; pub mod zbus_proxy; -pub const GFX_CONFIG_PATH: &str = "/etc/supergfxd.conf"; +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); +pub const CONFIG_PATH: &str = "/etc/supergfxd.conf"; pub const DBUS_DEST_NAME: &str = "org.supergfxctl.Daemon"; pub const DBUS_IFACE_PATH: &str = "/org/supergfxctl/Gfx"; From cf915b9e000ec21d1a082f250d37f0e2f450963a Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Thu, 26 Aug 2021 13:53:54 +1200 Subject: [PATCH 06/11] Move anime data. Twiddle supergfxctl --- Makefile | 2 +- daemon/src/config.rs | 4 +- daemon/src/daemon.rs | 25 ++++--- data/user-example.json | 64 ------------------ .../data}/anime/asus/festive/Cupid.gif | Bin .../data}/anime/asus/festive/Firework.gif | Bin .../data}/anime/asus/festive/Halloween.gif | Bin .../anime/asus/festive/Happy Holiday.gif | Bin .../anime/asus/festive/Happy new year.gif | Bin .../data}/anime/asus/festive/Lantern.gif | Bin .../data}/anime/asus/festive/Love u mom.gif | Bin .../data}/anime/asus/festive/Mother's day.gif | Bin .../anime/asus/festive/Valentine's Day.gif | Bin .../anime/asus/festive/Year of the Ox.gif | Bin .../data}/anime/asus/gaming/Bird.gif | Bin .../data}/anime/asus/gaming/Controller.gif | Bin .../data}/anime/asus/gaming/FPS.gif | Bin .../data}/anime/asus/gaming/Fight.gif | Bin .../data}/anime/asus/gaming/Keyboard.gif | Bin .../data}/anime/asus/gaming/MOBA.gif | Bin .../data}/anime/asus/gaming/UFO.gif | Bin .../data}/anime/asus/music/DJ.gif | Bin .../data}/anime/asus/music/Diamond.gif | Bin .../data}/anime/asus/music/Music-player.gif | Bin .../anime/asus/rog/For-those-who-dare.gif | Bin .../anime/asus/rog/For-those-who-dare_2.gif | Bin .../data}/anime/asus/rog/Fragment.gif | Bin .../anime/asus/rog/Infinite-triangle.gif | Bin .../data}/anime/asus/rog/Kaleidoscope1.gif | Bin .../data}/anime/asus/rog/Kaleidoscope2.gif | Bin .../data}/anime/asus/rog/Kaleidoscope2.png | Bin .../data}/anime/asus/rog/ROG city.gif | Bin .../data}/anime/asus/rog/ROG glitch.gif | Bin .../data}/anime/asus/rog/Sunset.gif | Bin .../data}/anime/asus/trend/Dog.gif | Bin .../data}/anime/asus/trend/Hero.gif | Bin .../data}/anime/asus/trend/Ski.gif | Bin .../data}/anime/asus/trend/The scream.gif | Bin .../data}/anime/asus/trend/Wave.gif | Bin .../data}/anime/custom/nyancat_zombie.gif | Bin .../data}/anime/custom/rust.png | Bin .../data}/anime/custom/sonic-run.gif | Bin .../data}/anime/custom/sonic-wait.gif | Bin supergfx/src/cli.rs | 3 + supergfx/src/controller.rs | 2 +- supergfx/src/error.rs | 4 -- supergfx/src/lib.rs | 11 +++ supergfx/src/zbus_proxy.rs | 2 +- 48 files changed, 30 insertions(+), 87 deletions(-) delete mode 100644 data/user-example.json rename {data => rog-anime/data}/anime/asus/festive/Cupid.gif (100%) rename {data => rog-anime/data}/anime/asus/festive/Firework.gif (100%) rename {data => rog-anime/data}/anime/asus/festive/Halloween.gif (100%) rename {data => rog-anime/data}/anime/asus/festive/Happy Holiday.gif (100%) rename {data => rog-anime/data}/anime/asus/festive/Happy new year.gif (100%) rename {data => rog-anime/data}/anime/asus/festive/Lantern.gif (100%) rename {data => rog-anime/data}/anime/asus/festive/Love u mom.gif (100%) rename {data => rog-anime/data}/anime/asus/festive/Mother's day.gif (100%) rename {data => rog-anime/data}/anime/asus/festive/Valentine's Day.gif (100%) rename {data => rog-anime/data}/anime/asus/festive/Year of the Ox.gif (100%) rename {data => rog-anime/data}/anime/asus/gaming/Bird.gif (100%) rename {data => rog-anime/data}/anime/asus/gaming/Controller.gif (100%) rename {data => rog-anime/data}/anime/asus/gaming/FPS.gif (100%) rename {data => rog-anime/data}/anime/asus/gaming/Fight.gif (100%) rename {data => rog-anime/data}/anime/asus/gaming/Keyboard.gif (100%) rename {data => rog-anime/data}/anime/asus/gaming/MOBA.gif (100%) rename {data => rog-anime/data}/anime/asus/gaming/UFO.gif (100%) rename {data => rog-anime/data}/anime/asus/music/DJ.gif (100%) rename {data => rog-anime/data}/anime/asus/music/Diamond.gif (100%) rename {data => rog-anime/data}/anime/asus/music/Music-player.gif (100%) rename {data => rog-anime/data}/anime/asus/rog/For-those-who-dare.gif (100%) rename {data => rog-anime/data}/anime/asus/rog/For-those-who-dare_2.gif (100%) rename {data => rog-anime/data}/anime/asus/rog/Fragment.gif (100%) rename {data => rog-anime/data}/anime/asus/rog/Infinite-triangle.gif (100%) rename {data => rog-anime/data}/anime/asus/rog/Kaleidoscope1.gif (100%) rename {data => rog-anime/data}/anime/asus/rog/Kaleidoscope2.gif (100%) rename {data => rog-anime/data}/anime/asus/rog/Kaleidoscope2.png (100%) rename {data => rog-anime/data}/anime/asus/rog/ROG city.gif (100%) rename {data => rog-anime/data}/anime/asus/rog/ROG glitch.gif (100%) rename {data => rog-anime/data}/anime/asus/rog/Sunset.gif (100%) rename {data => rog-anime/data}/anime/asus/trend/Dog.gif (100%) rename {data => rog-anime/data}/anime/asus/trend/Hero.gif (100%) rename {data => rog-anime/data}/anime/asus/trend/Ski.gif (100%) rename {data => rog-anime/data}/anime/asus/trend/The scream.gif (100%) rename {data => rog-anime/data}/anime/asus/trend/Wave.gif (100%) rename {data => rog-anime/data}/anime/custom/nyancat_zombie.gif (100%) rename {data => rog-anime/data}/anime/custom/rust.png (100%) rename {data => rog-anime/data}/anime/custom/sonic-run.gif (100%) rename {data => rog-anime/data}/anime/custom/sonic-wait.gif (100%) diff --git a/Makefile b/Makefile index 190d4f8a..5bc05dd0 100644 --- a/Makefile +++ b/Makefile @@ -69,7 +69,7 @@ install: $(INSTALL_DATA) "./data/_asusctl" "$(DESTDIR)$(zshcpl)/_asusctl" $(INSTALL_DATA) "./data/completions/asusctl.fish" "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish" - cd data && find "./anime" -type f -exec install -Dm 755 "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \; + cd rog-anime/data && find "./anime" -type f -exec install -Dm 755 "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \; $(INSTALL_PROGRAM) "./target/release/$(BIN_SD)" "$(DESTDIR)$(bindir)/$(BIN_SD)" $(INSTALL_PROGRAM) "./target/release/$(BIN_SC)" "$(DESTDIR)$(bindir)/$(BIN_SC)" diff --git a/daemon/src/config.rs b/daemon/src/config.rs index 5f5b0a6c..273fbcac 100644 --- a/daemon/src/config.rs +++ b/daemon/src/config.rs @@ -1,10 +1,8 @@ -use log::{error, info, warn}; +use log::{error, warn}; use serde_derive::{Deserialize, Serialize}; use std::fs::{File, OpenOptions}; use std::io::{Read, Write}; -use crate::VERSION; - pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf"; #[derive(Deserialize, Serialize)] diff --git a/daemon/src/daemon.rs b/daemon/src/daemon.rs index 105cfba9..78318fc6 100644 --- a/daemon/src/daemon.rs +++ b/daemon/src/daemon.rs @@ -28,7 +28,6 @@ use std::sync::Arc; use std::sync::Mutex; use daemon::ctrl_rog_bios::CtrlRogBios; -use zvariant::ObjectPath; static PROFILE_CONFIG_PATH: &str = "/etc/asusd/profile.conf"; @@ -183,18 +182,18 @@ fn start_daemon() -> Result<(), Box> { }); // Run zbus server - object_server - .with( - &ObjectPath::from_str_unchecked("/org/asuslinux/Charge"), - |obj: &CtrlCharge| { - let x = obj.limit(); - obj.notify_charge(x as u8) - }, - ) - .map_err(|err| { - warn!("object_server notify_charge error: {}", err); - }) - .ok(); + // object_server + // .with( + // &ObjectPath::from_str_unchecked("/org/asuslinux/Charge"), + // |obj: &CtrlCharge| { + // let x = obj.limit(); + // obj.notify_charge(x as u8) + // }, + // ) + // .map_err(|err| { + // warn!("object_server notify_charge error: {}", err); + // }) + // .ok(); // Loop to check errors and iterate zbus server loop { diff --git a/data/user-example.json b/data/user-example.json deleted file mode 100644 index 9f3600e9..00000000 --- a/data/user-example.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "anime": [ - { - "AsusAnimation": { - "file": "/usr/share/asusd/anime/asus/rog/Sunset.gif", - "time": { - "Cycles": 1 - }, - "brightness": 0.5 - } - }, - { - "ImageAnimation": { - "file": "/usr/share/asusd/anime/custom/sonic-run.gif", - "scale": 0.9, - "angle": 0.65, - "translation": [ - 0.0, - 0.0 - ], - "time": { - "Time": { - "secs": 5, - "nanos": 0 - } - }, - "brightness": 0.5 - } - }, - { - "Image": { - "file": "/usr/share/asusd/anime/custom/rust.png", - "scale": 1.0, - "angle": 0.0, - "translation": [ - 0.0, - 0.0 - ], - "brightness": 0.6 - } - }, - { - "Pause": { - "secs": 6, - "nanos": 0 - } - }, - { - "ImageAnimation": { - "file": "/usr/share/asusd/anime/custom/sonic-wait.gif", - "scale": 0.9, - "angle": 0.0, - "translation": [ - 3.0, - 2.0 - ], - "time": { - "Cycles": 2 - }, - "brightness": 0.5 - } - } - ] -} diff --git a/data/anime/asus/festive/Cupid.gif b/rog-anime/data/anime/asus/festive/Cupid.gif similarity index 100% rename from data/anime/asus/festive/Cupid.gif rename to rog-anime/data/anime/asus/festive/Cupid.gif diff --git a/data/anime/asus/festive/Firework.gif b/rog-anime/data/anime/asus/festive/Firework.gif similarity index 100% rename from data/anime/asus/festive/Firework.gif rename to rog-anime/data/anime/asus/festive/Firework.gif diff --git a/data/anime/asus/festive/Halloween.gif b/rog-anime/data/anime/asus/festive/Halloween.gif similarity index 100% rename from data/anime/asus/festive/Halloween.gif rename to rog-anime/data/anime/asus/festive/Halloween.gif diff --git a/data/anime/asus/festive/Happy Holiday.gif b/rog-anime/data/anime/asus/festive/Happy Holiday.gif similarity index 100% rename from data/anime/asus/festive/Happy Holiday.gif rename to rog-anime/data/anime/asus/festive/Happy Holiday.gif diff --git a/data/anime/asus/festive/Happy new year.gif b/rog-anime/data/anime/asus/festive/Happy new year.gif similarity index 100% rename from data/anime/asus/festive/Happy new year.gif rename to rog-anime/data/anime/asus/festive/Happy new year.gif diff --git a/data/anime/asus/festive/Lantern.gif b/rog-anime/data/anime/asus/festive/Lantern.gif similarity index 100% rename from data/anime/asus/festive/Lantern.gif rename to rog-anime/data/anime/asus/festive/Lantern.gif diff --git a/data/anime/asus/festive/Love u mom.gif b/rog-anime/data/anime/asus/festive/Love u mom.gif similarity index 100% rename from data/anime/asus/festive/Love u mom.gif rename to rog-anime/data/anime/asus/festive/Love u mom.gif diff --git a/data/anime/asus/festive/Mother's day.gif b/rog-anime/data/anime/asus/festive/Mother's day.gif similarity index 100% rename from data/anime/asus/festive/Mother's day.gif rename to rog-anime/data/anime/asus/festive/Mother's day.gif diff --git a/data/anime/asus/festive/Valentine's Day.gif b/rog-anime/data/anime/asus/festive/Valentine's Day.gif similarity index 100% rename from data/anime/asus/festive/Valentine's Day.gif rename to rog-anime/data/anime/asus/festive/Valentine's Day.gif diff --git a/data/anime/asus/festive/Year of the Ox.gif b/rog-anime/data/anime/asus/festive/Year of the Ox.gif similarity index 100% rename from data/anime/asus/festive/Year of the Ox.gif rename to rog-anime/data/anime/asus/festive/Year of the Ox.gif diff --git a/data/anime/asus/gaming/Bird.gif b/rog-anime/data/anime/asus/gaming/Bird.gif similarity index 100% rename from data/anime/asus/gaming/Bird.gif rename to rog-anime/data/anime/asus/gaming/Bird.gif diff --git a/data/anime/asus/gaming/Controller.gif b/rog-anime/data/anime/asus/gaming/Controller.gif similarity index 100% rename from data/anime/asus/gaming/Controller.gif rename to rog-anime/data/anime/asus/gaming/Controller.gif diff --git a/data/anime/asus/gaming/FPS.gif b/rog-anime/data/anime/asus/gaming/FPS.gif similarity index 100% rename from data/anime/asus/gaming/FPS.gif rename to rog-anime/data/anime/asus/gaming/FPS.gif diff --git a/data/anime/asus/gaming/Fight.gif b/rog-anime/data/anime/asus/gaming/Fight.gif similarity index 100% rename from data/anime/asus/gaming/Fight.gif rename to rog-anime/data/anime/asus/gaming/Fight.gif diff --git a/data/anime/asus/gaming/Keyboard.gif b/rog-anime/data/anime/asus/gaming/Keyboard.gif similarity index 100% rename from data/anime/asus/gaming/Keyboard.gif rename to rog-anime/data/anime/asus/gaming/Keyboard.gif diff --git a/data/anime/asus/gaming/MOBA.gif b/rog-anime/data/anime/asus/gaming/MOBA.gif similarity index 100% rename from data/anime/asus/gaming/MOBA.gif rename to rog-anime/data/anime/asus/gaming/MOBA.gif diff --git a/data/anime/asus/gaming/UFO.gif b/rog-anime/data/anime/asus/gaming/UFO.gif similarity index 100% rename from data/anime/asus/gaming/UFO.gif rename to rog-anime/data/anime/asus/gaming/UFO.gif diff --git a/data/anime/asus/music/DJ.gif b/rog-anime/data/anime/asus/music/DJ.gif similarity index 100% rename from data/anime/asus/music/DJ.gif rename to rog-anime/data/anime/asus/music/DJ.gif diff --git a/data/anime/asus/music/Diamond.gif b/rog-anime/data/anime/asus/music/Diamond.gif similarity index 100% rename from data/anime/asus/music/Diamond.gif rename to rog-anime/data/anime/asus/music/Diamond.gif diff --git a/data/anime/asus/music/Music-player.gif b/rog-anime/data/anime/asus/music/Music-player.gif similarity index 100% rename from data/anime/asus/music/Music-player.gif rename to rog-anime/data/anime/asus/music/Music-player.gif diff --git a/data/anime/asus/rog/For-those-who-dare.gif b/rog-anime/data/anime/asus/rog/For-those-who-dare.gif similarity index 100% rename from data/anime/asus/rog/For-those-who-dare.gif rename to rog-anime/data/anime/asus/rog/For-those-who-dare.gif diff --git a/data/anime/asus/rog/For-those-who-dare_2.gif b/rog-anime/data/anime/asus/rog/For-those-who-dare_2.gif similarity index 100% rename from data/anime/asus/rog/For-those-who-dare_2.gif rename to rog-anime/data/anime/asus/rog/For-those-who-dare_2.gif diff --git a/data/anime/asus/rog/Fragment.gif b/rog-anime/data/anime/asus/rog/Fragment.gif similarity index 100% rename from data/anime/asus/rog/Fragment.gif rename to rog-anime/data/anime/asus/rog/Fragment.gif diff --git a/data/anime/asus/rog/Infinite-triangle.gif b/rog-anime/data/anime/asus/rog/Infinite-triangle.gif similarity index 100% rename from data/anime/asus/rog/Infinite-triangle.gif rename to rog-anime/data/anime/asus/rog/Infinite-triangle.gif diff --git a/data/anime/asus/rog/Kaleidoscope1.gif b/rog-anime/data/anime/asus/rog/Kaleidoscope1.gif similarity index 100% rename from data/anime/asus/rog/Kaleidoscope1.gif rename to rog-anime/data/anime/asus/rog/Kaleidoscope1.gif diff --git a/data/anime/asus/rog/Kaleidoscope2.gif b/rog-anime/data/anime/asus/rog/Kaleidoscope2.gif similarity index 100% rename from data/anime/asus/rog/Kaleidoscope2.gif rename to rog-anime/data/anime/asus/rog/Kaleidoscope2.gif diff --git a/data/anime/asus/rog/Kaleidoscope2.png b/rog-anime/data/anime/asus/rog/Kaleidoscope2.png similarity index 100% rename from data/anime/asus/rog/Kaleidoscope2.png rename to rog-anime/data/anime/asus/rog/Kaleidoscope2.png diff --git a/data/anime/asus/rog/ROG city.gif b/rog-anime/data/anime/asus/rog/ROG city.gif similarity index 100% rename from data/anime/asus/rog/ROG city.gif rename to rog-anime/data/anime/asus/rog/ROG city.gif diff --git a/data/anime/asus/rog/ROG glitch.gif b/rog-anime/data/anime/asus/rog/ROG glitch.gif similarity index 100% rename from data/anime/asus/rog/ROG glitch.gif rename to rog-anime/data/anime/asus/rog/ROG glitch.gif diff --git a/data/anime/asus/rog/Sunset.gif b/rog-anime/data/anime/asus/rog/Sunset.gif similarity index 100% rename from data/anime/asus/rog/Sunset.gif rename to rog-anime/data/anime/asus/rog/Sunset.gif diff --git a/data/anime/asus/trend/Dog.gif b/rog-anime/data/anime/asus/trend/Dog.gif similarity index 100% rename from data/anime/asus/trend/Dog.gif rename to rog-anime/data/anime/asus/trend/Dog.gif diff --git a/data/anime/asus/trend/Hero.gif b/rog-anime/data/anime/asus/trend/Hero.gif similarity index 100% rename from data/anime/asus/trend/Hero.gif rename to rog-anime/data/anime/asus/trend/Hero.gif diff --git a/data/anime/asus/trend/Ski.gif b/rog-anime/data/anime/asus/trend/Ski.gif similarity index 100% rename from data/anime/asus/trend/Ski.gif rename to rog-anime/data/anime/asus/trend/Ski.gif diff --git a/data/anime/asus/trend/The scream.gif b/rog-anime/data/anime/asus/trend/The scream.gif similarity index 100% rename from data/anime/asus/trend/The scream.gif rename to rog-anime/data/anime/asus/trend/The scream.gif diff --git a/data/anime/asus/trend/Wave.gif b/rog-anime/data/anime/asus/trend/Wave.gif similarity index 100% rename from data/anime/asus/trend/Wave.gif rename to rog-anime/data/anime/asus/trend/Wave.gif diff --git a/data/anime/custom/nyancat_zombie.gif b/rog-anime/data/anime/custom/nyancat_zombie.gif similarity index 100% rename from data/anime/custom/nyancat_zombie.gif rename to rog-anime/data/anime/custom/nyancat_zombie.gif diff --git a/data/anime/custom/rust.png b/rog-anime/data/anime/custom/rust.png similarity index 100% rename from data/anime/custom/rust.png rename to rog-anime/data/anime/custom/rust.png diff --git a/data/anime/custom/sonic-run.gif b/rog-anime/data/anime/custom/sonic-run.gif similarity index 100% rename from data/anime/custom/sonic-run.gif rename to rog-anime/data/anime/custom/sonic-run.gif diff --git a/data/anime/custom/sonic-wait.gif b/rog-anime/data/anime/custom/sonic-wait.gif similarity index 100% rename from data/anime/custom/sonic-wait.gif rename to rog-anime/data/anime/custom/sonic-wait.gif diff --git a/supergfx/src/cli.rs b/supergfx/src/cli.rs index f2926cae..ae0369d0 100644 --- a/supergfx/src/cli.rs +++ b/supergfx/src/cli.rs @@ -1,3 +1,5 @@ +//! Basic CLI tool to control the `supergfxd` daemon + use std::{env::args, sync::mpsc::channel}; use supergfxctl::{ gfx_vendors::{GfxRequiredUserAction, GfxVendors}, @@ -59,6 +61,7 @@ fn do_gfx(command: CliStart) -> Result<(), Box> { } println!("If anything fails check `journalctl -b -u supergfxd`\n"); + println!("Note that nvidia-drm.modeset=0 is required in kernel cmdline to enable the nvidia drivers to be unloaded on demand`\n"); proxy.gfx_write_mode(&mode).map_err(|err|{ println!("Graphics mode change error. You may be in an invalid state."); diff --git a/supergfx/src/controller.rs b/supergfx/src/controller.rs index e6e788b4..67632b30 100644 --- a/supergfx/src/controller.rs +++ b/supergfx/src/controller.rs @@ -543,7 +543,7 @@ impl CtrlGraphics { // exit if 3 minutes pass if Instant::now().duration_since(start_time).as_secs() > 180 { warn!("{}", THREAD_TIMEOUT_MSG); - return Ok(THREAD_TIMEOUT_MSG.into()); + return Err(GfxError::DisplayManagerTimeout(THREAD_TIMEOUT_MSG.into())); } // Don't spin at max speed diff --git a/supergfx/src/error.rs b/supergfx/src/error.rs index d47c7d1a..bc261774 100644 --- a/supergfx/src/error.rs +++ b/supergfx/src/error.rs @@ -4,8 +4,6 @@ use std::{error, process::ExitStatus}; #[derive(Debug)] pub enum GfxError { ParseVendor, - ParsePower, - Bus(String, std::io::Error), DisplayManagerAction(String, ExitStatus), DisplayManagerTimeout(String), AsusGsyncModeActive, @@ -26,8 +24,6 @@ impl fmt::Display for GfxError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { GfxError::ParseVendor => write!(f, "Could not parse vendor name"), - GfxError::ParsePower => write!(f, "Could not parse dGPU power status"), - GfxError::Bus(func, error) => write!(f, "Bus error: {}: {}", func, error), GfxError::DisplayManagerAction(action, status) => { write!(f, "Display-manager action {} failed: {}", action, status) } diff --git a/supergfx/src/lib.rs b/supergfx/src/lib.rs index 52a44f2b..30194af4 100644 --- a/supergfx/src/lib.rs +++ b/supergfx/src/lib.rs @@ -1,17 +1,28 @@ +/// The configuration for graphics. This should be saved and loaded on boot. pub mod config; +/// Control functions for setting graphics. pub mod controller; +/// Error: 404 pub mod error; +/// Mode names, follows what distros defined as common. pub mod gfx_vendors; /// Special-case functions for check/read/write of key functions on unique laptops /// such as the G-Sync mode available on some ASUS ROG laptops pub mod special; +/// System interface helpers. pub mod system; +/// Defined DBUS Interface for supergfxctl pub mod zbus_iface; +/// Defined DBUS Proxy for supergfxctl pub mod zbus_proxy; +/// Helper to expose the current crate version to external code pub const VERSION: &str = env!("CARGO_PKG_VERSION"); +/// Generic path that is used to save the daemon config state pub const CONFIG_PATH: &str = "/etc/supergfxd.conf"; +/// Destination name to be used in the daemon when setting up DBUS connection pub const DBUS_DEST_NAME: &str = "org.supergfxctl.Daemon"; +/// Interface path name. Should be common across daemon and client. pub const DBUS_IFACE_PATH: &str = "/org/supergfxctl/Gfx"; const NVIDIA_DRIVERS: [&str; 4] = ["nvidia_drm", "nvidia_modeset", "nvidia_uvm", "nvidia"]; diff --git a/supergfx/src/zbus_proxy.rs b/supergfx/src/zbus_proxy.rs index 2f7a1725..21edaafa 100644 --- a/supergfx/src/zbus_proxy.rs +++ b/supergfx/src/zbus_proxy.rs @@ -19,7 +19,7 @@ //! //! …consequently `zbus-xmlgen` did not generate code for the above interfaces. -use std::sync::mpsc::{Receiver, Sender}; +use std::sync::mpsc::{Sender}; use zbus::{dbus_proxy, Connection, Message, Result}; From 8db37491f3fe43b1f8a096603342466cbc82b749 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Thu, 26 Aug 2021 13:57:19 +1200 Subject: [PATCH 07/11] Mention breaking changes in log --- CHANGELOG.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bca7573..dcc786c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + Support 8bit RGB, RGBA, 16bit Greyscalw, RGB, RGBA + add `AsusImage` type for slanted-template pixel-perfect images + `BREAKING:` plain `Image` with time period is changed and old anime configs break as a result (sorry) -### Changed +### BREAKING CHANGES - Graphics control: - + BREAKING: graphics control is pulled out of asusd and moved to new crate; supergfxctl + + graphics control is pulled out of asusd and moved to new crate; supergfxctl +- Proflies: + + profiles now depend on power-profile-daemon plus kernel patches for support of platform_profile + - if your system supports fan-curves you will also require upcoming kernel patches for this + + profiles are now moved to a new file # [3.7.2] - 2021-08-02 ### Added From 453d3091c12e79cae6a409d037c92cdf47ac0e3a Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Thu, 26 Aug 2021 14:17:42 +1200 Subject: [PATCH 08/11] Bump major versions --- Cargo.lock | 28 ++++++++++++++-------------- asus-notify/Cargo.toml | 2 +- asusctl/Cargo.toml | 2 +- daemon/Cargo.toml | 2 +- data/asusd.service | 2 +- rog-dbus/Cargo.toml | 2 +- rog-profiles/Cargo.toml | 2 +- rog-supported/Cargo.toml | 2 +- supergfx/data/supergfxd.service | 7 +++++-- 9 files changed, 26 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7ccad0b9..4f71a8f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,7 +31,7 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "asus-notify" -version = "3.0.1" +version = "3.0.2" dependencies = [ "notify-rust", "rog_aura", @@ -45,7 +45,7 @@ dependencies = [ [[package]] name = "asusctl" -version = "3.5.1" +version = "4.0.0" dependencies = [ "daemon", "gif", @@ -208,7 +208,7 @@ dependencies = [ [[package]] name = "daemon" -version = "3.7.2" +version = "4.0.0" dependencies = [ "env_logger", "log", @@ -527,9 +527,9 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "lazy_static" @@ -539,9 +539,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5" +checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" [[package]] name = "libudev-sys" @@ -919,7 +919,7 @@ dependencies = [ [[package]] name = "rog_dbus" -version = "3.5.1" +version = "4.0.0" dependencies = [ "rog_anime", "rog_aura", @@ -933,7 +933,7 @@ dependencies = [ [[package]] name = "rog_profiles" -version = "0.1.2" +version = "1.0.0" dependencies = [ "serde", "serde_derive", @@ -943,7 +943,7 @@ dependencies = [ [[package]] name = "rog_supported" -version = "3.2.0" +version = "4.0.0" dependencies = [ "rog_aura", "serde", @@ -988,18 +988,18 @@ checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" [[package]] name = "serde" -version = "1.0.128" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1056a0db1978e9dbf0f6e4fca677f6f9143dc1c19de346f22cac23e422196834" +checksum = "d1f72836d2aa753853178eda473a3b9d8e4eefdaf20523b919677e6de489f8f1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.128" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13af2fbb8b60a8950d6c72a56d2095c28870367cc8e10c55e9745bac4995a2c4" +checksum = "e57ae87ad533d9a56427558b516d0adac283614e347abf85b0dc0cbbf0a249f3" dependencies = [ "proc-macro2", "quote 1.0.9", diff --git a/asus-notify/Cargo.toml b/asus-notify/Cargo.toml index c81cab9c..81c1238d 100644 --- a/asus-notify/Cargo.toml +++ b/asus-notify/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "asus-notify" -version = "3.0.1" +version = "3.0.2" authors = ["Luke D Jones "] edition = "2018" diff --git a/asusctl/Cargo.toml b/asusctl/Cargo.toml index 23a6f1d8..31a31713 100644 --- a/asusctl/Cargo.toml +++ b/asusctl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "asusctl" -version = "3.5.1" +version = "4.0.0" authors = ["Luke D Jones "] edition = "2018" diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index f98837af..cb54d5d8 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "daemon" -version = "3.7.2" +version = "4.0.0" license = "MPL-2.0" readme = "README.md" authors = ["Luke "] diff --git a/data/asusd.service b/data/asusd.service index 37d17010..9f0ee849 100644 --- a/data/asusd.service +++ b/data/asusd.service @@ -2,7 +2,7 @@ Description=ASUS Notebook Control StartLimitInterval=200 StartLimitBurst=2 -Before=display-manager.service +Before=multi-user.target [Service] Environment=IS_SERVICE=1 diff --git a/rog-dbus/Cargo.toml b/rog-dbus/Cargo.toml index 62011f70..a997bd46 100644 --- a/rog-dbus/Cargo.toml +++ b/rog-dbus/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rog_dbus" -version = "3.5.1" +version = "4.0.0" license = "MPL-2.0" readme = "README.md" authors = ["Luke "] diff --git a/rog-profiles/Cargo.toml b/rog-profiles/Cargo.toml index 5da2153b..dec19f61 100644 --- a/rog-profiles/Cargo.toml +++ b/rog-profiles/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rog_profiles" -version = "0.1.2" +version = "1.0.0" authors = ["Luke D. Jones "] edition = "2018" diff --git a/rog-supported/Cargo.toml b/rog-supported/Cargo.toml index 6da0212e..461bc729 100644 --- a/rog-supported/Cargo.toml +++ b/rog-supported/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rog_supported" -version = "3.2.0" +version = "4.0.0" license = "MPL-2.0" readme = "README.md" authors = ["Luke "] diff --git a/supergfx/data/supergfxd.service b/supergfx/data/supergfxd.service index cbe73694..16f8f591 100644 --- a/supergfx/data/supergfxd.service +++ b/supergfx/data/supergfxd.service @@ -2,7 +2,7 @@ Description=SUPERGFX StartLimitInterval=200 StartLimitBurst=2 -Before=display-manager.service +Before=multi-user.target [Service] Environment=IS_SUPERGFX_SERVICE=1 @@ -13,4 +13,7 @@ RestartSec=1 Type=dbus BusName=org.supergfxctl.Daemon SELinuxContext=system_u:system_r:unconfined_t:s0 -#SELinuxContext=system_u:object_r:modules_object_t:s0 \ No newline at end of file +#SELinuxContext=system_u:object_r:modules_object_t:s0 + +[Install] +WantedBy=multi-user.target \ No newline at end of file From 2431dd9e9354805031ef553459caccdf827c9e5f Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Thu, 26 Aug 2021 16:00:31 +1200 Subject: [PATCH 09/11] Remove supergfxctl to own repo --- CHANGELOG.md | 2 +- Cargo.lock | 3 +- Cargo.toml | 2 +- Makefile | 15 - asus-notify/Cargo.toml | 2 +- asusctl/Cargo.toml | 2 +- rog-dbus/Cargo.toml | 2 +- supergfx/Cargo.toml | 49 -- supergfx/LICENSE | 373 ------------ supergfx/Makefile | 74 --- supergfx/data/90-asusd-nvidia-pm.rules | 7 - supergfx/data/90-nvidia-screen-G05.conf | 4 - supergfx/data/org.supergfxctl.Daemon.conf | 26 - supergfx/data/supergfxd.service | 19 - supergfx/src/cli.rs | 108 ---- supergfx/src/config.rs | 86 --- supergfx/src/controller.rs | 706 ---------------------- supergfx/src/daemon.rs | 105 ---- supergfx/src/error.rs | 68 --- supergfx/src/gfx_vendors.rs | 103 ---- supergfx/src/lib.rs | 77 --- supergfx/src/special.rs | 25 - supergfx/src/system.rs | 160 ----- supergfx/src/zbus_iface.rs | 61 -- supergfx/src/zbus_proxy.rs | 117 ---- 25 files changed, 6 insertions(+), 2190 deletions(-) delete mode 100644 supergfx/Cargo.toml delete mode 100644 supergfx/LICENSE delete mode 100644 supergfx/Makefile delete mode 100644 supergfx/data/90-asusd-nvidia-pm.rules delete mode 100644 supergfx/data/90-nvidia-screen-G05.conf delete mode 100644 supergfx/data/org.supergfxctl.Daemon.conf delete mode 100644 supergfx/data/supergfxd.service delete mode 100644 supergfx/src/cli.rs delete mode 100644 supergfx/src/config.rs delete mode 100644 supergfx/src/controller.rs delete mode 100644 supergfx/src/daemon.rs delete mode 100644 supergfx/src/error.rs delete mode 100644 supergfx/src/gfx_vendors.rs delete mode 100644 supergfx/src/lib.rs delete mode 100644 supergfx/src/special.rs delete mode 100644 supergfx/src/system.rs delete mode 100644 supergfx/src/zbus_iface.rs delete mode 100644 supergfx/src/zbus_proxy.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index dcc786c6..60cebb75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + `BREAKING:` plain `Image` with time period is changed and old anime configs break as a result (sorry) ### BREAKING CHANGES - Graphics control: - + graphics control is pulled out of asusd and moved to new crate; supergfxctl + + graphics control is pulled out of asusd and moved to new package; https://gitlab.com/asus-linux/supergfxctl - Proflies: + profiles now depend on power-profile-daemon plus kernel patches for support of platform_profile - if your system supports fan-curves you will also require upcoming kernel patches for this diff --git a/Cargo.lock b/Cargo.lock index 4f71a8f8..03220719 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1069,9 +1069,8 @@ dependencies = [ [[package]] name = "supergfxctl" version = "2.0.0" +source = "git+https://gitlab.com/asus-linux/supergfxctl.git?tag=2.0.0#3f040cd3ec334242631122cd038aa361cc860be6" dependencies = [ - "env_logger", - "gumdrop", "log", "logind-zbus", "serde", diff --git a/Cargo.toml b/Cargo.toml index abc0d8d9..40cd341e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-supported", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "supergfx"] +members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-supported", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles"] [profile.release] lto = true diff --git a/Makefile b/Makefile index 5bc05dd0..c0e7265d 100644 --- a/Makefile +++ b/Makefile @@ -15,11 +15,7 @@ BIN_C := asusctl BIN_D := asusd BIN_U := asusd-user BIN_N := asus-notify -BIN_SD := supergfxd -BIN_SC := supergfxctl LEDCFG := asusd-ledmodes.toml -X11CFG := 90-nvidia-screen-G05.conf -PMRULES := 90-asusd-nvidia-pm.rules SRC := Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.rs') @@ -71,13 +67,6 @@ install: $(INSTALL_DATA) "./data/completions/asusctl.fish" "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish" cd rog-anime/data && find "./anime" -type f -exec install -Dm 755 "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \; - $(INSTALL_PROGRAM) "./target/release/$(BIN_SD)" "$(DESTDIR)$(bindir)/$(BIN_SD)" - $(INSTALL_PROGRAM) "./target/release/$(BIN_SC)" "$(DESTDIR)$(bindir)/$(BIN_SC)" - $(INSTALL_DATA) "./supergfx/data/$(BIN_SD).service" "$(DESTDIR)$(libdir)/systemd/system/$(BIN_SD).service" - $(INSTALL_DATA) "./supergfx/data/org.supergfxctl.Daemon.conf" "$(DESTDIR)$(datarootdir)/dbus-1/system.d/org.supergfxctl.Daemon.conf" - $(INSTALL_DATA) "./supergfx/data/$(X11CFG)" "$(DESTDIR)$(datarootdir)/X11/xorg.conf.d/$(X11CFG)" - $(INSTALL_DATA) "./supergfx/data/$(PMRULES)" "$(DESTDIR)$(libdir)/udev/rules.d/$(PMRULES)" - uninstall: rm -f "$(DESTDIR)$(bindir)/$(BIN_C)" rm -f "$(DESTDIR)$(bindir)/$(BIN_D)" @@ -101,10 +90,6 @@ uninstall: rm -f "$(DESTDIR)$(zshcpl)/_asusctl" rm -f "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish" rm -rf "$(DESTDIR)$(datarootdir)/asusd" - rm -f "$(DESTDIR)$(bindir)/$(BIN_SC)" - rm -f "$(DESTDIR)$(bindir)/$(BIN_SD)" - rm -f "$(DESTDIR)$(libdir)/systemd/system/$(BIN_SD).service" - rm -f "$(DESTDIR)$(datarootdir)/dbus-1/system.d/org.supergfxctl.Daemon.conf" update: cargo update diff --git a/asus-notify/Cargo.toml b/asus-notify/Cargo.toml index 81c1238d..5bb57b2c 100644 --- a/asus-notify/Cargo.toml +++ b/asus-notify/Cargo.toml @@ -14,7 +14,7 @@ rog_dbus = { path = "../rog-dbus" } rog_aura = { path = "../rog-aura" } rog_supported = { path = "../rog-supported" } rog_profiles = { path = "../rog-profiles" } -supergfxctl = { path = "../supergfx" } +supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", tag = "2.0.0" } [dependencies.notify-rust] version = "^4.3" diff --git a/asusctl/Cargo.toml b/asusctl/Cargo.toml index 31a31713..8d2fbe07 100644 --- a/asusctl/Cargo.toml +++ b/asusctl/Cargo.toml @@ -15,7 +15,7 @@ rog_profiles = { path = "../rog-profiles" } rog_supported = { path = "../rog-supported" } daemon = { path = "../daemon" } gumdrop = "^0.8" -supergfxctl = { path = "../supergfx" } +supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", tag = "2.0.0" } [dev-dependencies] tinybmp = "^0.2.3" diff --git a/rog-dbus/Cargo.toml b/rog-dbus/Cargo.toml index a997bd46..81b7a443 100644 --- a/rog-dbus/Cargo.toml +++ b/rog-dbus/Cargo.toml @@ -14,7 +14,7 @@ rog_anime = { path = "../rog-anime" } rog_aura = { path = "../rog-aura" } rog_profiles = { path = "../rog-profiles" } rog_supported = { path = "../rog-supported" } -supergfxctl = { path = "../supergfx" } +supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", tag = "2.0.0" } zbus = "^1.9" zbus_macros = "^1.9" zvariant = "^2.8" diff --git a/supergfx/Cargo.toml b/supergfx/Cargo.toml deleted file mode 100644 index 7c0a93f3..00000000 --- a/supergfx/Cargo.toml +++ /dev/null @@ -1,49 +0,0 @@ -[package] -name = "supergfxctl" -version = "2.0.0" -license = "MPL-2.0" -readme = "README.md" -authors = ["Luke "] -repository = "https://gitlab.com/asus-linux/asusctl" -homepage = "https://gitlab.com/asus-linux/asusctl" -documentation = "https://docs.rs/rog-anime" -description = "Types useful for fancy keyboards on ASUS ROG laptops" -keywords = ["graphics", "nvidia", "switching"] -edition = "2018" -exclude = ["data"] - -[features] -daemon = ["env_logger"] -cli = ["gumdrop"] -default = ["daemon", "cli"] - -[lib] -name = "supergfxctl" -path = "src/lib.rs" - -[[bin]] -name = "supergfxd" -path = "src/daemon.rs" -required-features = ["daemon"] - -[[bin]] -name = "supergfxctl" -path = "src/cli.rs" -required-features = ["cli"] -default-features = ["cli"] - -[dependencies] -serde = "^1.0" -serde_derive = "^1.0" -serde_json = "^1.0" -log = "^0.4" - -zbus = "^1.9.1" -zvariant = "^2.8" -zvariant_derive = "^2.8" -logind-zbus = "^0.7.1" - -sysfs-class = "^0.1.2" - -env_logger = { version = "^0.8", optional = true } -gumdrop = { version = "^0.8", optional = true } \ No newline at end of file diff --git a/supergfx/LICENSE b/supergfx/LICENSE deleted file mode 100644 index a612ad98..00000000 --- a/supergfx/LICENSE +++ /dev/null @@ -1,373 +0,0 @@ -Mozilla Public License Version 2.0 -================================== - -1. Definitions --------------- - -1.1. "Contributor" - means each individual or legal entity that creates, contributes to - the creation of, or owns Covered Software. - -1.2. "Contributor Version" - means the combination of the Contributions of others (if any) used - by a Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - means Source Code Form to which the initial Contributor has attached - the notice in Exhibit A, the Executable Form of such Source Code - Form, and Modifications of such Source Code Form, in each case - including portions thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - (a) that the initial Contributor has attached the notice described - in Exhibit B to the Covered Software; or - - (b) that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the - terms of a Secondary License. - -1.6. "Executable Form" - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - means a work that combines Covered Software with other material, in - a separate file or files, that is not Covered Software. - -1.8. "License" - means this document. - -1.9. "Licensable" - means having the right to grant, to the maximum extent possible, - whether at the time of the initial grant or subsequently, any and - all of the rights conveyed by this License. - -1.10. "Modifications" - means any of the following: - - (a) any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered - Software; or - - (b) any new file in Source Code Form that contains any Covered - Software. - -1.11. "Patent Claims" of a Contributor - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the - License, by the making, using, selling, offering for sale, having - made, import, or transfer of either its Contributions or its - Contributor Version. - -1.12. "Secondary License" - means either the GNU General Public License, Version 2.0, the GNU - Lesser General Public License, Version 2.1, the GNU Affero General - Public License, Version 3.0, or any later versions of those - licenses. - -1.13. "Source Code Form" - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that - controls, is controlled by, or is under common control with You. For - purposes of this definition, "control" means (a) the power, direct - or indirect, to cause the direction or management of such entity, - whether by contract or otherwise, or (b) ownership of more than - fifty percent (50%) of the outstanding shares or beneficial - ownership of such entity. - -2. License Grants and Conditions --------------------------------- - -2.1. Grants - -Each Contributor hereby grants You a world-wide, royalty-free, -non-exclusive license: - -(a) under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - -(b) under Patent Claims of such Contributor to make, use, sell, offer - for sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - -The licenses granted in Section 2.1 with respect to any Contribution -become effective for each Contribution on the date the Contributor first -distributes such Contribution. - -2.3. Limitations on Grant Scope - -The licenses granted in this Section 2 are the only rights granted under -this License. No additional rights or licenses will be implied from the -distribution or licensing of Covered Software under this License. -Notwithstanding Section 2.1(b) above, no patent license is granted by a -Contributor: - -(a) for any code that a Contributor has removed from Covered Software; - or - -(b) for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - -(c) under Patent Claims infringed by Covered Software in the absence of - its Contributions. - -This License does not grant any rights in the trademarks, service marks, -or logos of any Contributor (except as may be necessary to comply with -the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - -No Contributor makes additional grants as a result of Your choice to -distribute the Covered Software under a subsequent version of this -License (see Section 10.2) or under the terms of a Secondary License (if -permitted under the terms of Section 3.3). - -2.5. Representation - -Each Contributor represents that the Contributor believes its -Contributions are its original creation(s) or it has sufficient rights -to grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - -This License is not intended to limit any rights You have under -applicable copyright doctrines of fair use, fair dealing, or other -equivalents. - -2.7. Conditions - -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted -in Section 2.1. - -3. Responsibilities -------------------- - -3.1. Distribution of Source Form - -All distribution of Covered Software in Source Code Form, including any -Modifications that You create or to which You contribute, must be under -the terms of this License. You must inform recipients that the Source -Code Form of the Covered Software is governed by the terms of this -License, and how they can obtain a copy of this License. You may not -attempt to alter or restrict the recipients' rights in the Source Code -Form. - -3.2. Distribution of Executable Form - -If You distribute Covered Software in Executable Form then: - -(a) such Covered Software must also be made available in Source Code - Form, as described in Section 3.1, and You must inform recipients of - the Executable Form how they can obtain a copy of such Source Code - Form by reasonable means in a timely manner, at a charge no more - than the cost of distribution to the recipient; and - -(b) You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter - the recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - -You may create and distribute a Larger Work under terms of Your choice, -provided that You also comply with the requirements of this License for -the Covered Software. If the Larger Work is a combination of Covered -Software with a work governed by one or more Secondary Licenses, and the -Covered Software is not Incompatible With Secondary Licenses, this -License permits You to additionally distribute such Covered Software -under the terms of such Secondary License(s), so that the recipient of -the Larger Work may, at their option, further distribute the Covered -Software under the terms of either this License or such Secondary -License(s). - -3.4. Notices - -You may not remove or alter the substance of any license notices -(including copyright notices, patent notices, disclaimers of warranty, -or limitations of liability) contained within the Source Code Form of -the Covered Software, except that You may alter any license notices to -the extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - -You may choose to offer, and to charge a fee for, warranty, support, -indemnity or liability obligations to one or more recipients of Covered -Software. However, You may do so only on Your own behalf, and not on -behalf of any Contributor. You must make it absolutely clear that any -such warranty, support, indemnity, or liability obligation is offered by -You alone, and You hereby agree to indemnify every Contributor for any -liability incurred by such Contributor as a result of warranty, support, -indemnity or liability terms You offer. You may include additional -disclaimers of warranty and limitations of liability specific to any -jurisdiction. - -4. Inability to Comply Due to Statute or Regulation ---------------------------------------------------- - -If it is impossible for You to comply with any of the terms of this -License with respect to some or all of the Covered Software due to -statute, judicial order, or regulation then You must: (a) comply with -the terms of this License to the maximum extent possible; and (b) -describe the limitations and the code they affect. Such description must -be placed in a text file included with all distributions of the Covered -Software under this License. Except to the extent prohibited by statute -or regulation, such description must be sufficiently detailed for a -recipient of ordinary skill to be able to understand it. - -5. Termination --------------- - -5.1. The rights granted under this License will terminate automatically -if You fail to comply with any of its terms. However, if You become -compliant, then the rights granted under this License from a particular -Contributor are reinstated (a) provisionally, unless and until such -Contributor explicitly and finally terminates Your grants, and (b) on an -ongoing basis, if such Contributor fails to notify You of the -non-compliance by some reasonable means prior to 60 days after You have -come back into compliance. Moreover, Your grants from a particular -Contributor are reinstated on an ongoing basis if such Contributor -notifies You of the non-compliance by some reasonable means, this is the -first time You have received notice of non-compliance with this License -from such Contributor, and You become compliant prior to 30 days after -Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent -infringement claim (excluding declaratory judgment actions, -counter-claims, and cross-claims) alleging that a Contributor Version -directly or indirectly infringes any patent, then the rights granted to -You by any and all Contributors for the Covered Software under Section -2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all -end user license agreements (excluding distributors and resellers) which -have been validly granted by You or Your distributors under this License -prior to termination shall survive termination. - -************************************************************************ -* * -* 6. Disclaimer of Warranty * -* ------------------------- * -* * -* Covered Software is provided under this License on an "as is" * -* basis, without warranty of any kind, either expressed, implied, or * -* statutory, including, without limitation, warranties that the * -* Covered Software is free of defects, merchantable, fit for a * -* particular purpose or non-infringing. The entire risk as to the * -* quality and performance of the Covered Software is with You. * -* Should any Covered Software prove defective in any respect, You * -* (not any Contributor) assume the cost of any necessary servicing, * -* repair, or correction. This disclaimer of warranty constitutes an * -* essential part of this License. No use of any Covered Software is * -* authorized under this License except under this disclaimer. * -* * -************************************************************************ - -************************************************************************ -* * -* 7. Limitation of Liability * -* -------------------------- * -* * -* Under no circumstances and under no legal theory, whether tort * -* (including negligence), contract, or otherwise, shall any * -* Contributor, or anyone who distributes Covered Software as * -* permitted above, be liable to You for any direct, indirect, * -* special, incidental, or consequential damages of any character * -* including, without limitation, damages for lost profits, loss of * -* goodwill, work stoppage, computer failure or malfunction, or any * -* and all other commercial damages or losses, even if such party * -* shall have been informed of the possibility of such damages. This * -* limitation of liability shall not apply to liability for death or * -* personal injury resulting from such party's negligence to the * -* extent applicable law prohibits such limitation. Some * -* jurisdictions do not allow the exclusion or limitation of * -* incidental or consequential damages, so this exclusion and * -* limitation may not apply to You. * -* * -************************************************************************ - -8. Litigation -------------- - -Any litigation relating to this License may be brought only in the -courts of a jurisdiction where the defendant maintains its principal -place of business and such litigation shall be governed by laws of that -jurisdiction, without reference to its conflict-of-law provisions. -Nothing in this Section shall prevent a party's ability to bring -cross-claims or counter-claims. - -9. Miscellaneous ----------------- - -This License represents the complete agreement concerning the subject -matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. Any law or regulation which provides -that the language of a contract shall be construed against the drafter -shall not be used to construe this License against a Contributor. - -10. Versions of the License ---------------------------- - -10.1. New Versions - -Mozilla Foundation is the license steward. Except as provided in Section -10.3, no one other than the license steward has the right to modify or -publish new versions of this License. Each version will be given a -distinguishing version number. - -10.2. Effect of New Versions - -You may distribute the Covered Software under the terms of the version -of the License under which You originally received the Covered Software, -or under the terms of any subsequent version published by the license -steward. - -10.3. Modified Versions - -If you create software not governed by this License, and you want to -create a new license for such software, you may create and use a -modified version of this License if you rename the license and remove -any references to the name of the license steward (except to note that -such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary -Licenses - -If You choose to distribute Source Code Form that is Incompatible With -Secondary Licenses under the terms of this version of the License, the -notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice -------------------------------------------- - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular -file, then You may include the notice in a location (such as a LICENSE -file in a relevant directory) where a recipient would be likely to look -for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice ---------------------------------------------------------- - - This Source Code Form is "Incompatible With Secondary Licenses", as - defined by the Mozilla Public License, v. 2.0. diff --git a/supergfx/Makefile b/supergfx/Makefile deleted file mode 100644 index fb4f47ae..00000000 --- a/supergfx/Makefile +++ /dev/null @@ -1,74 +0,0 @@ -VERSION := $(shell grep -Pm1 'version = "(\d.\d.\d)"' daemon/Cargo.toml | cut -d'"' -f2) - -INSTALL = install -INSTALL_PROGRAM = ${INSTALL} -D -m 0755 -INSTALL_DATA = ${INSTALL} -D -m 0644 - -prefix = /usr -exec_prefix = $(prefix) -bindir = $(exec_prefix)/bin -datarootdir = $(prefix)/share -libdir = $(exec_prefix)/lib - -BIN_SD := supergfxd -BIN_SC := supergfxctl -X11CFG := 90-nvidia-screen-G05.conf -PMRULES := 90-asusd-nvidia-pm.rules - -SRC := Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.rs') - -DEBUG ?= 0 -ifeq ($(DEBUG),0) - ARGS += --release - TARGET = release -endif - -VENDORED ?= 0 -ifeq ($(VENDORED),1) - ARGS += --frozen -endif - -all: build - -clean: - cargo clean - -distclean: - rm -rf .cargo vendor vendor.tar.xz - -install: - $(INSTALL_PROGRAM) "./target/release/$(BIN_SD)" "$(DESTDIR)$(bindir)/$(BIN_SD)" - $(INSTALL_PROGRAM) "./target/release/$(BIN_SC)" "$(DESTDIR)$(bindir)/$(BIN_SC)" - $(INSTALL_DATA) "./data/$(BIN_SD).service" "$(DESTDIR)$(libdir)/systemd/system/$(BIN_SD).service" - $(INSTALL_DATA) "./data/org.supergfxctl.Daemon.conf" "$(DESTDIR)$(datarootdir)/dbus-1/system.d/org.supergfxctl.Daemon.conf" - $(INSTALL_DATA) "./data/$(X11CFG)" "$(DESTDIR)$(datarootdir)/X11/xorg.conf.d/$(X11CFG)" - $(INSTALL_DATA) "./data/$(PMRULES)" "$(DESTDIR)$(libdir)/udev/rules.d/$(PMRULES)" - -uninstall: - rm -f "$(DESTDIR)$(bindir)/$(BIN_SC)" - rm -f "$(DESTDIR)$(bindir)/$(BIN_SD)" - rm -f "$(DESTDIR)$(libdir)/systemd/system/$(BIN_SD).service" - rm -f "$(DESTDIR)$(datarootdir)/dbus-1/system.d/org.supergfxctl.Daemon.conf" - rm -f "$(DESTDIR)$(datarootdir)/X11/xorg.conf.d/$(X11CFG)" - rm -f "$(DESTDIR)$(libdir)/udev/rules.d/$(PMRULES)" - -update: - cargo update - -vendor: - mkdir -p .cargo - cargo vendor | head -n -1 > .cargo/config - echo 'directory = "vendor"' >> .cargo/config - mv .cargo/config ./cargo-config - rm -rf .cargo - tar pcfJ vendor_asusctl_$(VERSION).tar.xz vendor - rm -rf vendor - -build: -ifeq ($(VENDORED),1) - @echo "version = $(VERSION)" - tar pxf vendor_asusctl_$(VERSION).tar.xz -endif - cargo build $(ARGS) - -.PHONY: all clean distclean install uninstall update build diff --git a/supergfx/data/90-asusd-nvidia-pm.rules b/supergfx/data/90-asusd-nvidia-pm.rules deleted file mode 100644 index ba3cefef..00000000 --- a/supergfx/data/90-asusd-nvidia-pm.rules +++ /dev/null @@ -1,7 +0,0 @@ -# Enable runtime PM for NVIDIA VGA/3D controller devices on driver bind -ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="auto" -ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="auto" - -# Disable runtime PM for NVIDIA VGA/3D controller devices on driver unbind -ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="on" -ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="on" diff --git a/supergfx/data/90-nvidia-screen-G05.conf b/supergfx/data/90-nvidia-screen-G05.conf deleted file mode 100644 index 7e561fc5..00000000 --- a/supergfx/data/90-nvidia-screen-G05.conf +++ /dev/null @@ -1,4 +0,0 @@ -Section "ServerLayout" - Identifier "layout" - Option "AllowNVIDIAGPUScreens" -EndSection diff --git a/supergfx/data/org.supergfxctl.Daemon.conf b/supergfx/data/org.supergfxctl.Daemon.conf deleted file mode 100644 index 77fa6276..00000000 --- a/supergfx/data/org.supergfxctl.Daemon.conf +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/supergfx/data/supergfxd.service b/supergfx/data/supergfxd.service deleted file mode 100644 index 16f8f591..00000000 --- a/supergfx/data/supergfxd.service +++ /dev/null @@ -1,19 +0,0 @@ -[Unit] -Description=SUPERGFX -StartLimitInterval=200 -StartLimitBurst=2 -Before=multi-user.target - -[Service] -Environment=IS_SUPERGFX_SERVICE=1 -ExecStart=/usr/bin/supergfxd -Restart=on-failure -Restart=always -RestartSec=1 -Type=dbus -BusName=org.supergfxctl.Daemon -SELinuxContext=system_u:system_r:unconfined_t:s0 -#SELinuxContext=system_u:object_r:modules_object_t:s0 - -[Install] -WantedBy=multi-user.target \ No newline at end of file diff --git a/supergfx/src/cli.rs b/supergfx/src/cli.rs deleted file mode 100644 index ae0369d0..00000000 --- a/supergfx/src/cli.rs +++ /dev/null @@ -1,108 +0,0 @@ -//! Basic CLI tool to control the `supergfxd` daemon - -use std::{env::args, sync::mpsc::channel}; -use supergfxctl::{ - gfx_vendors::{GfxRequiredUserAction, GfxVendors}, - special::{get_asus_gsync_gfx_mode, has_asus_gsync_gfx_mode}, - zbus_proxy::GfxProxy, -}; - -use gumdrop::Options; -use zbus::Connection; - -#[derive(Default, Options)] -struct CliStart { - #[options(help = "print help message")] - help: bool, - #[options( - meta = "", - help = "Set graphics mode: " - )] - mode: Option, - #[options(help = "Get the current mode")] - get: bool, - #[options(help = "Get the current power status")] - pow: bool, - #[options(help = "Do not ask for confirmation")] - force: bool, -} - -fn main() -> Result<(), Box> { - let args: Vec = args().skip(1).collect(); - - match CliStart::parse_args_default(&args) { - Ok(command) => { - do_gfx(command)?; - } - Err(err) => { - eprintln!("source {}", err); - std::process::exit(2); - } - } - - Ok(()) -} - -fn do_gfx(command: CliStart) -> Result<(), Box> { - if command.mode.is_none() && !command.get && !command.pow && !command.force || command.help { - println!("{}", command.self_usage()); - } - - let conn = Connection::new_system()?; - let proxy = GfxProxy::new(&conn)?; - - let (tx, rx) = channel(); - proxy.connect_notify_action(tx)?; - - if let Some(mode) = command.mode { - if has_asus_gsync_gfx_mode() && get_asus_gsync_gfx_mode()? == 1 { - println!("You can not change modes until you turn dedicated/G-Sync off and reboot"); - std::process::exit(-1); - } - - println!("If anything fails check `journalctl -b -u supergfxd`\n"); - println!("Note that nvidia-drm.modeset=0 is required in kernel cmdline to enable the nvidia drivers to be unloaded on demand`\n"); - - proxy.gfx_write_mode(&mode).map_err(|err|{ - println!("Graphics mode change error. You may be in an invalid state."); - println!("Check mode with `-g` and switch to opposite\nmode to correct it, e.g: if integrated, switch to hybrid, or if nvidia, switch to integrated.\n"); - err - })?; - - loop { - proxy.next_signal()?; - - if let Ok(res) = rx.try_recv() { - match res { - GfxRequiredUserAction::Integrated => { - println!( - "You must change to Integrated before you can change to {}", - <&str>::from(mode) - ); - } - GfxRequiredUserAction::Logout | GfxRequiredUserAction::Reboot => { - println!( - "Graphics mode changed to {}. User action required is: {}", - <&str>::from(mode), - <&str>::from(&res) - ); - } - GfxRequiredUserAction::None => { - println!("Graphics mode changed to {}", <&str>::from(mode)); - } - } - } - std::process::exit(0) - } - } - if command.get { - let res = proxy.gfx_get_mode()?; - println!("Current graphics mode: {}", <&str>::from(res)); - } - if command.pow { - let res = proxy.gfx_get_pwr()?; - println!("Current power status: {}", <&str>::from(&res)); - } - - Ok(()) -} diff --git a/supergfx/src/config.rs b/supergfx/src/config.rs deleted file mode 100644 index 1967d2db..00000000 --- a/supergfx/src/config.rs +++ /dev/null @@ -1,86 +0,0 @@ -use log::{error, warn}; -use serde_derive::{Deserialize, Serialize}; -use std::fs::{File, OpenOptions}; -use std::io::{Read, Write}; - -use crate::gfx_vendors::GfxVendors; - -#[derive(Deserialize, Serialize)] -pub struct GfxConfig { - #[serde(skip)] - config_path: String, - /// The current mode set, also applies on boot - pub gfx_mode: GfxVendors, - /// Only for informational purposes - #[serde(skip)] - pub gfx_tmp_mode: Option, - /// Set if graphics management is enabled - pub gfx_managed: bool, - /// Set if vfio option is enabled. This requires the vfio drivers to be built as modules - pub gfx_vfio_enable: bool, -} - -impl GfxConfig { - fn new(config_path: String) -> Self { - Self { - config_path, - gfx_mode: GfxVendors::Hybrid, - gfx_tmp_mode: None, - gfx_managed: true, - gfx_vfio_enable: false, - } - } - - /// `load` will attempt to read the config, and panic if the dir is missing - pub fn load(config_path: String) -> Self { - let mut file = OpenOptions::new() - .read(true) - .write(true) - .create(true) - .open(&config_path) - .unwrap_or_else(|_| panic!("The directory {} is missing", config_path)); // okay to cause panic here - let mut buf = String::new(); - let mut config; - if let Ok(read_len) = file.read_to_string(&mut buf) { - if read_len == 0 { - config = Self::new(config_path); - } else if let Ok(data) = serde_json::from_str(&buf) { - config = data; - config.config_path = config_path; - } else { - warn!("Could not deserialise {}", config_path); - panic!("Please remove {} then restart service", config_path); - } - } else { - config = Self::new(config_path) - } - config.write(); - config - } - - pub fn read(&mut self) { - let mut file = OpenOptions::new() - .read(true) - .open(&self.config_path) - .unwrap_or_else(|err| panic!("Error reading {}: {}", self.config_path, err)); - let mut buf = String::new(); - if let Ok(l) = file.read_to_string(&mut buf) { - if l == 0 { - warn!("File is empty {}", self.config_path); - } else { - let mut x: Self = serde_json::from_str(&buf) - .unwrap_or_else(|_| panic!("Could not deserialise {}", self.config_path)); - // copy over serde skipped values - x.gfx_tmp_mode = self.gfx_tmp_mode; - *self = x; - } - } - } - - pub fn write(&self) { - let mut file = File::create(&self.config_path).expect("Couldn't overwrite config"); - let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed"); - file.write_all(json.as_bytes()) - .unwrap_or_else(|err| error!("Could not write config: {}", err)); - } -} diff --git a/supergfx/src/controller.rs b/supergfx/src/controller.rs deleted file mode 100644 index 67632b30..00000000 --- a/supergfx/src/controller.rs +++ /dev/null @@ -1,706 +0,0 @@ -use ::zbus::Connection; -use log::{error, info, warn}; -use logind_zbus::{ - types::{SessionClass, SessionInfo, SessionState, SessionType}, - ManagerProxy, SessionProxy, -}; -use std::{io::Write, ops::Add, path::Path, time::Instant}; -use std::{process::Command, thread::sleep, time::Duration}; -use std::{str::FromStr, sync::mpsc}; -use std::{sync::Arc, sync::Mutex}; -use sysfs_class::RuntimePM; -use sysfs_class::{PciDevice, SysClass}; - -use crate::{ - error::GfxError, - special::{get_asus_gsync_gfx_mode, has_asus_gsync_gfx_mode}, - system::{GraphicsDevice, PciBus}, - *, -}; - -use super::config::GfxConfig; -use super::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}; - -const THREAD_TIMEOUT_MSG: &str = "GFX: thread time exceeded 3 minutes, exiting"; -const NVIDIA_RUNTIME_STATUS_PATH: &str = "/sys/bus/pci/devices/0000:01:00.0/power/runtime_status"; - -pub struct CtrlGraphics { - bus: PciBus, - _amd: Vec, - _intel: Vec, - nvidia: Vec, - #[allow(dead_code)] - other: Vec, - config: Arc>, - thread_kill: Arc>>>, -} - -impl CtrlGraphics { - pub fn new(config: Arc>) -> std::io::Result { - let bus = PciBus::new()?; - info!("GFX: Rescanning PCI bus"); - bus.rescan()?; - let devs = PciDevice::all()?; - - let functions = |parent: &PciDevice| -> Vec { - let mut functions = Vec::new(); - if let Some(parent_slot) = parent.id().split('.').next() { - for func in devs.iter() { - if let Some(func_slot) = func.id().split('.').next() { - if func_slot == parent_slot { - info!("GFX: {}: Function for {}", func.id(), parent.id()); - functions.push(func.clone()); - } - } - } - } - functions - }; - - let mut amd = Vec::new(); - let mut intel = Vec::new(); - let mut nvidia = Vec::new(); - let mut other = Vec::new(); - for dev in devs.iter() { - let c = dev.class().map_err(|err| { - error!( - "GFX: device error: {}, {}", - dev.path().to_string_lossy(), - err - ); - err - })?; - if 0x03 == (c >> 16) & 0xFF { - match dev.vendor()? { - 0x1002 => { - info!("GFX: {}: AMD graphics", dev.id()); - amd.push(GraphicsDevice::new(dev.id().to_owned(), functions(dev))); - } - 0x10DE => { - info!("GFX: {}: NVIDIA graphics", dev.id()); - dev.set_runtime_pm(sysfs_class::RuntimePowerManagement::On)?; - nvidia.push(GraphicsDevice::new(dev.id().to_owned(), functions(dev))); - } - 0x8086 => { - info!("GFX: {}: Intel graphics", dev.id()); - intel.push(GraphicsDevice::new(dev.id().to_owned(), functions(dev))); - } - vendor => { - info!("GFX: {}: Other({:X}) graphics", dev.id(), vendor); - other.push(GraphicsDevice::new(dev.id().to_owned(), functions(dev))); - } - } - } - } - - Ok(CtrlGraphics { - bus, - _amd: amd, - _intel: intel, - nvidia, - other, - config, - thread_kill: Arc::new(Mutex::new(None)), - }) - } - - /// Force reinit of all state, including reset of device state - pub fn reload(&mut self) -> Result<(), GfxError> { - self.auto_power()?; - info!("GFX: Reloaded gfx mode: {:?}", self.get_gfx_mode()?); - Ok(()) - } - - pub fn bus(&self) -> PciBus { - self.bus.clone() - } - - pub fn devices(&self) -> Vec { - self.nvidia.clone() - } - - /// Save the selected `Vendor` mode to config - fn save_gfx_mode(vendor: GfxVendors, config: Arc>) { - if let Ok(mut config) = config.lock() { - config.gfx_mode = vendor; - config.write(); - } - } - - /// Associated method to get which vendor mode is set - pub(super) fn get_gfx_mode(&self) -> Result { - if let Ok(config) = self.config.lock() { - if let Some(mode) = config.gfx_tmp_mode { - return Ok(mode); - } - return Ok(config.gfx_mode); - } - // TODO: Error here - Ok(GfxVendors::Hybrid) - } - - pub(super) fn get_runtime_status() -> Result { - let path = Path::new(NVIDIA_RUNTIME_STATUS_PATH); - if path.exists() { - let buf = std::fs::read_to_string(path) - .map_err(|err| GfxError::Read(path.to_string_lossy().to_string(), err))?; - Ok(GfxPower::from_str(&buf)?) - } else { - Ok(GfxPower::Off) - } - } - - /// Some systems have a fallback service to load nouveau if nvidia fails - fn toggle_fallback_service(vendor: GfxVendors) -> Result<(), GfxError> { - let action = if vendor == GfxVendors::Nvidia { - info!("GFX: Enabling nvidia-fallback.service"); - "enable" - } else { - info!("GFX: Disabling nvidia-fallback.service"); - "disable" - }; - - let status = Command::new("systemctl") - .arg(action) - .arg("nvidia-fallback.service") - .status() - .map_err(|err| GfxError::Command("systemctl".into(), err))?; - - if !status.success() { - // Error is ignored in case this service is removed - warn!( - "systemctl: {} (ignore warning if service does not exist!)", - status - ); - } - - Ok(()) - } - - /// Write the appropriate xorg config for the chosen mode - fn write_xorg_conf(vendor: GfxVendors) -> Result<(), GfxError> { - let text = if vendor == GfxVendors::Nvidia { - [PRIMARY_GPU_BEGIN, PRIMARY_GPU_NVIDIA, PRIMARY_GPU_END].concat() - } else { - [PRIMARY_GPU_BEGIN, PRIMARY_GPU_END].concat() - }; - - if !Path::new(XORG_PATH).exists() { - std::fs::create_dir(XORG_PATH).map_err(|err| GfxError::Write(XORG_PATH.into(), err))?; - } - - let file = XORG_PATH.to_string().add(XORG_FILE); - info!("GFX: Writing {}", file); - let mut file = std::fs::OpenOptions::new() - .create(true) - .truncate(true) - .write(true) - .open(&file) - .map_err(|err| GfxError::Write(file, err))?; - - file.write_all(&text) - .and_then(|_| file.sync_all()) - .map_err(|err| GfxError::Write(MODPROBE_PATH.into(), err))?; - Ok(()) - } - - /// Creates the full modprobe.conf required for vfio pass-through - fn get_vfio_conf(devices: &[GraphicsDevice]) -> Vec { - let mut vifo = MODPROBE_VFIO.to_vec(); - for (d_count, dev) in devices.iter().enumerate() { - for (f_count, func) in dev.functions().iter().enumerate() { - let vendor = func.vendor().unwrap(); - let device = func.device().unwrap(); - unsafe { - vifo.append(format!("{:x}", vendor).as_mut_vec()); - } - vifo.append(&mut vec![b':']); - unsafe { - vifo.append(format!("{:x}", device).as_mut_vec()); - } - if f_count < dev.functions().len() - 1 { - vifo.append(&mut vec![b',']); - } - } - if d_count < dev.functions().len() - 1 { - vifo.append(&mut vec![b',']); - } - } - let mut conf = MODPROBE_INTEGRATED.to_vec(); - conf.append(&mut vifo); - conf - } - - fn write_modprobe_conf(vendor: GfxVendors, devices: &[GraphicsDevice]) -> Result<(), GfxError> { - info!("GFX: Writing {}", MODPROBE_PATH); - let content = match vendor { - GfxVendors::Nvidia | GfxVendors::Hybrid => { - let mut base = MODPROBE_BASE.to_vec(); - base.append(&mut MODPROBE_DRM_MODESET.to_vec()); - base - } - GfxVendors::Vfio => Self::get_vfio_conf(devices), - GfxVendors::Integrated => MODPROBE_INTEGRATED.to_vec(), - GfxVendors::Compute => MODPROBE_BASE.to_vec(), - }; - - let mut file = std::fs::OpenOptions::new() - .create(true) - .truncate(true) - .write(true) - .open(MODPROBE_PATH) - .map_err(|err| GfxError::Path(MODPROBE_PATH.into(), err))?; - - file.write_all(&content) - .and_then(|_| file.sync_all()) - .map_err(|err| GfxError::Write(MODPROBE_PATH.into(), err))?; - - Ok(()) - } - - fn unbind_remove_nvidia(devices: &[GraphicsDevice]) -> Result<(), GfxError> { - // Unbind NVIDIA graphics devices and their functions - let unbinds = devices.iter().map(|dev| dev.unbind()); - // Remove NVIDIA graphics devices and their functions - let removes = devices.iter().map(|dev| dev.remove()); - unbinds - .chain(removes) - .collect::>() - .map_err(|err| GfxError::Command("device unbind error".into(), err)) - } - - fn unbind_only(devices: &[GraphicsDevice]) -> Result<(), GfxError> { - let unbinds = devices.iter().map(|dev| dev.unbind()); - unbinds - .collect::>() - .map_err(|err| GfxError::Command("device unbind error".into(), err)) - } - - /// Add or remove driver modules - fn do_driver_action(driver: &str, action: &str) -> Result<(), GfxError> { - let mut cmd = Command::new(action); - cmd.arg(driver); - - let mut count = 0; - const MAX_TRIES: i32 = 6; - loop { - if count > MAX_TRIES { - let msg = format!("{} {} failed for unknown reason", action, driver); - error!("GFX: {}", msg); - return Ok(()); //Err(GfxError::Modprobe(msg)); - } - - let output = cmd - .output() - .map_err(|err| GfxError::Command(format!("{:?}", cmd), err))?; - if !output.status.success() { - if output - .stderr - .ends_with("is not currently loaded\n".as_bytes()) - { - return Ok(()); - } - if output.stderr.ends_with("is builtin.\n".as_bytes()) { - return Err(GfxError::VfioBuiltin); - } - if output.stderr.ends_with("Permission denied\n".as_bytes()) { - warn!( - "{} {} failed: {:?}", - action, - driver, - String::from_utf8_lossy(&output.stderr) - ); - warn!("GFX: It may be safe to ignore the above error, run `lsmod |grep {}` to confirm modules loaded", driver); - return Ok(()); - } - if String::from_utf8_lossy(&output.stderr) - .contains(&format!("Module {} not found", driver)) - { - return Err(GfxError::MissingModule(driver.into())); - } - if count >= MAX_TRIES { - let msg = format!( - "{} {} failed: {:?}", - action, - driver, - String::from_utf8_lossy(&output.stderr) - ); - return Err(GfxError::Modprobe(msg)); - } - } else if output.status.success() { - return Ok(()); - } - - count += 1; - std::thread::sleep(std::time::Duration::from_millis(50)); - } - } - - fn do_display_manager_action(action: &str) -> Result<(), GfxError> { - let mut cmd = Command::new("systemctl"); - cmd.arg(action); - cmd.arg(DISPLAY_MANAGER); - - let status = cmd - .status() - .map_err(|err| GfxError::Command(format!("{:?}", cmd), err))?; - if !status.success() { - let msg = format!( - "systemctl {} {} failed: {:?}", - action, DISPLAY_MANAGER, status - ); - return Err(GfxError::DisplayManagerAction(msg, status)); - } - Ok(()) - } - - fn wait_display_manager_state(state: &str) -> Result<(), GfxError> { - let mut cmd = Command::new("systemctl"); - cmd.arg("is-active"); - cmd.arg(DISPLAY_MANAGER); - - let mut count = 0; - - while count <= (4 * 3) { - // 3 seconds max - let output = cmd - .output() - .map_err(|err| GfxError::Command(format!("{:?}", cmd), err))?; - if output.stdout.starts_with(state.as_bytes()) { - return Ok(()); - } - std::thread::sleep(std::time::Duration::from_millis(250)); - count += 1; - } - Err(GfxError::DisplayManagerTimeout(state.into())) - } - - /// Determine if we need to logout/thread. Integrated<->Vfio mode does not - /// require logout. - fn is_logout_required(&self, vendor: GfxVendors) -> GfxRequiredUserAction { - if let Ok(config) = self.config.lock() { - let current = config.gfx_mode; - // Modes that can switch without logout - if matches!( - current, - GfxVendors::Integrated | GfxVendors::Vfio | GfxVendors::Compute - ) && matches!( - vendor, - GfxVendors::Integrated | GfxVendors::Vfio | GfxVendors::Compute - ) { - return GfxRequiredUserAction::None; - } - // Modes that require a switch to integrated first - if matches!(current, GfxVendors::Nvidia | GfxVendors::Hybrid) - && matches!(vendor, GfxVendors::Compute | GfxVendors::Vfio) - { - return GfxRequiredUserAction::Integrated; - } - } - GfxRequiredUserAction::Logout - } - - /// Do a full setup flow for the chosen mode: - /// - /// Tasks: - /// - rescan for devices - /// - write xorg config - /// - write modprobe config - /// + add drivers - /// + or remove drivers and devices - /// - /// The daemon needs direct access to this function when it detects that the - /// bios has G-Sync switch is enabled - pub fn do_mode_setup_tasks( - vendor: GfxVendors, - vfio_enable: bool, - devices: &[GraphicsDevice], - bus: &PciBus, - ) -> Result<(), GfxError> { - // Rescan before doing remove or add drivers - bus.rescan()?; - // Make sure the power management is set to auto for nvidia devices - let devs = PciDevice::all()?; - for dev in devs.iter() { - let c = dev.class().map_err(|err| { - error!( - "GFX: device error: {}, {}", - dev.path().to_string_lossy(), - err - ); - err - })?; - if 0x03 == (c >> 16) & 0xFF && dev.vendor()? == 0x10DE { - info!("GFX: {}: NVIDIA graphics, setting PM to auto", dev.id()); - dev.set_runtime_pm(sysfs_class::RuntimePowerManagement::On)?; - } - } - // Only these modes should have xorg config - if matches!( - vendor, - GfxVendors::Nvidia | GfxVendors::Hybrid | GfxVendors::Integrated - ) { - Self::write_xorg_conf(vendor)?; - } - - // Write different modprobe to enable boot control to work - Self::write_modprobe_conf(vendor, devices)?; - - match vendor { - GfxVendors::Nvidia | GfxVendors::Hybrid | GfxVendors::Compute => { - if vfio_enable { - for driver in VFIO_DRIVERS.iter() { - Self::do_driver_action(driver, "rmmod")?; - } - } - for driver in NVIDIA_DRIVERS.iter() { - Self::do_driver_action(driver, "modprobe")?; - } - } - GfxVendors::Vfio => { - if vfio_enable { - Self::do_driver_action("nouveau", "rmmod")?; - for driver in NVIDIA_DRIVERS.iter() { - Self::do_driver_action(driver, "rmmod")?; - } - Self::unbind_only(devices)?; - Self::do_driver_action("vfio-pci", "modprobe")?; - } else { - return Err(GfxError::VfioDisabled); - } - } - GfxVendors::Integrated => { - Self::do_driver_action("nouveau", "rmmod")?; - if vfio_enable { - for driver in VFIO_DRIVERS.iter() { - Self::do_driver_action(driver, "rmmod")?; - } - } - for driver in NVIDIA_DRIVERS.iter() { - Self::do_driver_action(driver, "rmmod")?; - } - Self::unbind_remove_nvidia(devices)?; - } - } - Ok(()) - } - - /// Check if the user has any graphical uiser sessions that are active or online - fn graphical_user_sessions_exist( - connection: &Connection, - sessions: &[SessionInfo], - ) -> Result { - for session in sessions { - let session_proxy = SessionProxy::new(connection, session)?; - if session_proxy.get_class()? == SessionClass::User { - match session_proxy.get_type()? { - SessionType::X11 | SessionType::Wayland | SessionType::MIR => { - match session_proxy.get_state()? { - SessionState::Online | SessionState::Active => return Ok(true), - SessionState::Closing | SessionState::Invalid => {} - } - } - _ => {} - } - } - } - Ok(false) - } - - /// Spools until all user sessions are ended then switches to requested mode - fn create_mode_change_thread( - vendor: GfxVendors, - devices: Vec, - bus: PciBus, - thread_stop: mpsc::Receiver, - config: Arc>, - ) -> Result { - info!("GFX: display-manager thread started"); - - const SLEEP_PERIOD: Duration = Duration::from_millis(100); - let start_time = Instant::now(); - - let connection = Connection::new_system()?; - let manager = ManagerProxy::new(&connection)?; - let mut sessions = manager.list_sessions()?; - - loop { - let tmp = manager.list_sessions()?; - if !tmp.iter().eq(&sessions) { - info!("GFX thread: Sessions list changed"); - sessions = tmp; - } - - if !Self::graphical_user_sessions_exist(&connection, &sessions)? { - break; - } - - if let Ok(stop) = thread_stop.try_recv() { - if stop { - return Ok("Graphics mode change was cancelled".into()); - } - } - // exit if 3 minutes pass - if Instant::now().duration_since(start_time).as_secs() > 180 { - warn!("{}", THREAD_TIMEOUT_MSG); - return Err(GfxError::DisplayManagerTimeout(THREAD_TIMEOUT_MSG.into())); - } - - // Don't spin at max speed - sleep(SLEEP_PERIOD); - } - - info!("GFX thread: all graphical user sessions ended, continuing"); - Self::do_display_manager_action("stop")?; - Self::wait_display_manager_state("inactive")?; - - let mut mode_to_save = vendor; - // Need to change to integrated before we can change to vfio or compute - if let Ok(mut config) = config.try_lock() { - // Since we have a lock, reset tmp to none. This thread should only ever run - // for Integrated, Hybrid, or Nvidia. Tmp is also only for informational - config.gfx_tmp_mode = None; - // - let vfio_enable = config.gfx_vfio_enable; - - // Failsafe. In the event this loop is run with a switch from nvidia in use - // to vfio or compute do a forced switch to integrated instead to prevent issues - if matches!(vendor, GfxVendors::Compute | GfxVendors::Vfio) - && matches!(config.gfx_mode, GfxVendors::Nvidia | GfxVendors::Hybrid) - { - Self::do_mode_setup_tasks(GfxVendors::Integrated, vfio_enable, &devices, &bus)?; - Self::do_display_manager_action("restart")?; - mode_to_save = GfxVendors::Integrated; - } else { - Self::do_mode_setup_tasks(vendor, vfio_enable, &devices, &bus)?; - Self::do_display_manager_action("restart")?; - } - } - - // Save selected mode in case of reboot - Self::save_gfx_mode(mode_to_save, config); - info!("GFX thread: display-manager started"); - - let v: &str = vendor.into(); - info!("GFX thread: Graphics mode changed to {} successfully", v); - Ok(format!("Graphics mode changed to {} successfully", v)) - } - - /// Before starting a new thread the old one *must* be cancelled - fn cancel_mode_change_thread(&self) { - if let Ok(lock) = self.thread_kill.lock() { - if let Some(tx) = lock.as_ref() { - // Cancel the running thread - info!("GFX: Cancelling previous thread"); - tx.send(true) - .map_err(|err| { - warn!("GFX thread: {}", err); - }) - .ok(); - } - } - } - - /// The thread is used only in cases where a logout is required - fn setup_mode_change_thread(&mut self, vendor: GfxVendors) { - let config = self.config.clone(); - let devices = self.nvidia.clone(); - let bus = self.bus.clone(); - let (tx, rx) = mpsc::channel(); - if let Ok(mut lock) = self.thread_kill.lock() { - *lock = Some(tx); - } - let thread_kill = self.thread_kill.clone(); - - std::thread::spawn(move || { - Self::create_mode_change_thread(vendor, devices, bus, rx, config) - .map_err(|err| { - error!("GFX: {}", err); - }) - .ok(); - // clear the tx/rx when done - if let Ok(mut lock) = thread_kill.try_lock() { - *lock = None; - } - }); - } - - /// Initiates a mode change by starting a thread that will wait until all - /// graphical sessions are exited before performing the tasks required - /// to switch modes. - /// - /// For manually calling (not on boot/startup) via dbus - pub fn set_gfx_mode(&mut self, vendor: GfxVendors) -> Result { - if has_asus_gsync_gfx_mode() { - if let Ok(gsync) = get_asus_gsync_gfx_mode() { - if gsync == 1 { - return Err(GfxError::AsusGsyncModeActive); - } - } - } - - let vfio_enable = if let Ok(config) = self.config.try_lock() { - config.gfx_vfio_enable - } else { - false - }; - - if !vfio_enable && matches!(vendor, GfxVendors::Vfio) { - return Err(GfxError::VfioDisabled); - } - - // Must always cancel any thread running - self.cancel_mode_change_thread(); - // determine which method we need here - let action_required = self.is_logout_required(vendor); - - match action_required { - GfxRequiredUserAction::Logout => { - info!("GFX: mode change requires a logout to complete"); - self.setup_mode_change_thread(vendor); - } - GfxRequiredUserAction::Reboot => { - info!("GFX: mode change requires reboot"); - let devices = self.nvidia.clone(); - let bus = self.bus.clone(); - Self::do_mode_setup_tasks(vendor, vfio_enable, &devices, &bus)?; - info!("GFX: Graphics mode changed to {}", <&str>::from(vendor)); - } - GfxRequiredUserAction::Integrated => { - info!("GFX: mode change requires user to be in Integrated mode first"); - } - GfxRequiredUserAction::None => { - info!("GFX: mode change does not require logout"); - let devices = self.nvidia.clone(); - let bus = self.bus.clone(); - Self::do_mode_setup_tasks(vendor, vfio_enable, &devices, &bus)?; - info!("GFX: Graphics mode changed to {}", <&str>::from(vendor)); - if let Ok(mut config) = self.config.try_lock() { - config.gfx_tmp_mode = None; - if matches!(vendor, GfxVendors::Vfio | GfxVendors::Compute) { - config.gfx_tmp_mode = Some(vendor); - } - } - } - } - - Ok(action_required) - } - - /// Used only on boot to set correct mode - fn auto_power(&mut self) -> Result<(), GfxError> { - let vendor = self.get_gfx_mode()?; - let devices = self.nvidia.clone(); - let bus = self.bus.clone(); - - let vfio_enable = if let Ok(config) = self.config.try_lock() { - config.gfx_vfio_enable - } else { - false - }; - - Self::do_mode_setup_tasks(vendor, vfio_enable, &devices, &bus)?; - Self::toggle_fallback_service(vendor)?; - Ok(()) - } -} diff --git a/supergfx/src/daemon.rs b/supergfx/src/daemon.rs deleted file mode 100644 index 5d684497..00000000 --- a/supergfx/src/daemon.rs +++ /dev/null @@ -1,105 +0,0 @@ -use std::{ - env, - error::Error, - sync::{Arc, Mutex}, -}; - -use log::{error, info, warn, LevelFilter}; -use std::io::Write; -use supergfxctl::{ - config::GfxConfig, - controller::CtrlGraphics, - error::GfxError, - gfx_vendors::GfxVendors, - special::{get_asus_gsync_gfx_mode, has_asus_gsync_gfx_mode}, - CONFIG_PATH, DBUS_DEST_NAME, -}; -use zbus::{fdo, Connection, ObjectServer}; - -pub fn main() -> Result<(), Box> { - let mut logger = env_logger::Builder::new(); - logger - .target(env_logger::Target::Stdout) - .format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args())) - .filter(None, LevelFilter::Info) - .init(); - - let is_service = match env::var_os("IS_SUPERGFX_SERVICE") { - Some(val) => val == "1", - None => false, - }; - - if !is_service { - println!("supergfxd schould be only run from the right systemd service"); - println!( - "do not run in your terminal, if you need an logs please use journalctl -b -u supergfxd" - ); - println!("supergfxd will now exit"); - return Ok(()); - } - - start_daemon() -} - -fn start_daemon() -> Result<(), Box> { - // Start zbus server - let connection = Connection::new_system()?; - fdo::DBusProxy::new(&connection)?.request_name( - DBUS_DEST_NAME, - fdo::RequestNameFlags::ReplaceExisting.into(), - )?; - - let mut object_server = ObjectServer::new(&connection); - - let config = GfxConfig::load(CONFIG_PATH.into()); - let enable_gfx_switching = config.gfx_managed; - let config = Arc::new(Mutex::new(config)); - - // Graphics switching requires some checks on boot specifically for g-sync capable laptops - if enable_gfx_switching { - match CtrlGraphics::new(config.clone()) { - Ok(mut ctrl) => { - // Need to check if a laptop has the dedicated gfx switch - if has_asus_gsync_gfx_mode() { - do_asus_laptop_checks(&ctrl, config)?; - } - - ctrl.reload() - .unwrap_or_else(|err| error!("Gfx controller: {}", err)); - ctrl.add_to_server(&mut object_server); - } - Err(err) => { - error!("Gfx control: {}", err); - } - } - } - - // Loop to check errors and iterate zbus server - loop { - if let Err(err) = object_server.try_handle_next() { - error!("{}", err); - } - } -} - -fn do_asus_laptop_checks( - ctrl: &CtrlGraphics, - config: Arc>, -) -> Result<(), GfxError> { - if let Ok(ded) = get_asus_gsync_gfx_mode() { - if let Ok(config) = config.lock() { - if ded == 1 { - warn!("Dedicated GFX toggle is on but driver mode is not nvidia \nSetting to nvidia driver mode"); - let devices = ctrl.devices(); - let bus = ctrl.bus(); - CtrlGraphics::do_mode_setup_tasks(GfxVendors::Nvidia, false, &devices, &bus)?; - } else if ded == 0 { - info!("Dedicated GFX toggle is off"); - let devices = ctrl.devices(); - let bus = ctrl.bus(); - CtrlGraphics::do_mode_setup_tasks(config.gfx_mode, false, &devices, &bus)?; - } - } - } - Ok(()) -} diff --git a/supergfx/src/error.rs b/supergfx/src/error.rs deleted file mode 100644 index bc261774..00000000 --- a/supergfx/src/error.rs +++ /dev/null @@ -1,68 +0,0 @@ -use std::fmt; -use std::{error, process::ExitStatus}; - -#[derive(Debug)] -pub enum GfxError { - ParseVendor, - DisplayManagerAction(String, ExitStatus), - DisplayManagerTimeout(String), - AsusGsyncModeActive, - VfioBuiltin, - VfioDisabled, - MissingModule(String), - Modprobe(String), - Command(String, std::io::Error), - Path(String, std::io::Error), - Read(String, std::io::Error), - Write(String, std::io::Error), - Io(std::io::Error), - Zbus(zbus::Error), -} - -impl fmt::Display for GfxError { - // This trait requires `fmt` with this exact signature. - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - GfxError::ParseVendor => write!(f, "Could not parse vendor name"), - GfxError::DisplayManagerAction(action, status) => { - write!(f, "Display-manager action {} failed: {}", action, status) - } - GfxError::DisplayManagerTimeout(state) => { - write!(f, "Timed out waiting for display-manager {} state", state) - } - GfxError::AsusGsyncModeActive => write!( - f, - "Can not switch gfx modes when dedicated/G-Sync mode is active" - ), - GfxError::VfioBuiltin => write!( - f, - "Can not switch to vfio mode if the modules are built in to kernel" - ), - GfxError::VfioDisabled => { - write!(f, "Can not switch to vfio mode if disabled in config file") - } - GfxError::MissingModule(m) => write!(f, "The module {} is missing", m), - GfxError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail), - GfxError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error), - GfxError::Path(path, error) => write!(f, "Path {}: {}", path, error), - GfxError::Read(path, error) => write!(f, "Read {}: {}", path, error), - GfxError::Write(path, error) => write!(f, "Write {}: {}", path, error), - GfxError::Io(detail) => write!(f, "std::io error: {}", detail), - GfxError::Zbus(detail) => write!(f, "Zbus error: {}", detail), - } - } -} - -impl error::Error for GfxError {} - -impl From for GfxError { - fn from(err: zbus::Error) -> Self { - GfxError::Zbus(err) - } -} - -impl From for GfxError { - fn from(err: std::io::Error) -> Self { - GfxError::Io(err) - } -} diff --git a/supergfx/src/gfx_vendors.rs b/supergfx/src/gfx_vendors.rs deleted file mode 100644 index f16200a8..00000000 --- a/supergfx/src/gfx_vendors.rs +++ /dev/null @@ -1,103 +0,0 @@ -use serde_derive::{Deserialize, Serialize}; -use std::str::FromStr; -use zvariant_derive::Type; - -use crate::error::GfxError; - -#[derive(Debug, Type, PartialEq, Copy, Clone, Deserialize, Serialize)] -pub enum GfxPower { - Active, - Suspended, - Off, - Unknown, -} - -impl FromStr for GfxPower { - type Err = GfxError; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().trim() { - "active" => Ok(GfxPower::Active), - "suspended" => Ok(GfxPower::Suspended), - "off" => Ok(GfxPower::Off), - _ => Ok(GfxPower::Unknown), - } - } -} - -impl From<&GfxPower> for &str { - fn from(gfx: &GfxPower) -> &'static str { - match gfx { - GfxPower::Active => "active", - GfxPower::Suspended => "suspended", - GfxPower::Off => "off", - GfxPower::Unknown => "unknown", - } - } -} - -#[derive(Debug, Type, PartialEq, Copy, Clone, Deserialize, Serialize)] -pub enum GfxVendors { - Nvidia, - Integrated, - Compute, - Vfio, - Hybrid, -} - -impl FromStr for GfxVendors { - type Err = GfxError; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "nvidia" => Ok(GfxVendors::Nvidia), - "hybrid" => Ok(GfxVendors::Hybrid), - "compute" => Ok(GfxVendors::Compute), - "vfio" => Ok(GfxVendors::Vfio), - "integrated" => Ok(GfxVendors::Integrated), - "nvidia\n" => Ok(GfxVendors::Nvidia), - "hybrid\n" => Ok(GfxVendors::Hybrid), - "compute\n" => Ok(GfxVendors::Compute), - "vfio\n" => Ok(GfxVendors::Vfio), - "integrated\n" => Ok(GfxVendors::Integrated), - _ => Err(GfxError::ParseVendor), - } - } -} - -impl From<&GfxVendors> for &str { - fn from(gfx: &GfxVendors) -> &'static str { - match gfx { - GfxVendors::Nvidia => "nvidia", - GfxVendors::Hybrid => "hybrid", - GfxVendors::Compute => "compute", - GfxVendors::Vfio => "vfio", - GfxVendors::Integrated => "integrated", - } - } -} - -impl From for &str { - fn from(gfx: GfxVendors) -> &'static str { - (&gfx).into() - } -} - -#[derive(Debug, Type, PartialEq, Copy, Clone, Deserialize, Serialize)] -pub enum GfxRequiredUserAction { - Logout, - Reboot, - Integrated, - None, -} - -impl From<&GfxRequiredUserAction> for &str { - fn from(gfx: &GfxRequiredUserAction) -> &'static str { - match gfx { - GfxRequiredUserAction::Logout => "logout", - GfxRequiredUserAction::Reboot => "reboot", - GfxRequiredUserAction::Integrated => "switch to integrated first", - GfxRequiredUserAction::None => "no action", - } - } -} diff --git a/supergfx/src/lib.rs b/supergfx/src/lib.rs deleted file mode 100644 index 30194af4..00000000 --- a/supergfx/src/lib.rs +++ /dev/null @@ -1,77 +0,0 @@ -/// The configuration for graphics. This should be saved and loaded on boot. -pub mod config; -/// Control functions for setting graphics. -pub mod controller; -/// Error: 404 -pub mod error; -/// Mode names, follows what distros defined as common. -pub mod gfx_vendors; -/// Special-case functions for check/read/write of key functions on unique laptops -/// such as the G-Sync mode available on some ASUS ROG laptops -pub mod special; -/// System interface helpers. -pub mod system; -/// Defined DBUS Interface for supergfxctl -pub mod zbus_iface; -/// Defined DBUS Proxy for supergfxctl -pub mod zbus_proxy; - -/// Helper to expose the current crate version to external code -pub const VERSION: &str = env!("CARGO_PKG_VERSION"); -/// Generic path that is used to save the daemon config state -pub const CONFIG_PATH: &str = "/etc/supergfxd.conf"; -/// Destination name to be used in the daemon when setting up DBUS connection -pub const DBUS_DEST_NAME: &str = "org.supergfxctl.Daemon"; -/// Interface path name. Should be common across daemon and client. -pub const DBUS_IFACE_PATH: &str = "/org/supergfxctl/Gfx"; - -const NVIDIA_DRIVERS: [&str; 4] = ["nvidia_drm", "nvidia_modeset", "nvidia_uvm", "nvidia"]; - -const VFIO_DRIVERS: [&str; 5] = [ - "vfio-pci", - "vfio_iommu_type1", - "vfio_virqfd", - "vfio_mdev", - "vfio", -]; - -const DISPLAY_MANAGER: &str = "display-manager.service"; - -const MODPROBE_PATH: &str = "/etc/modprobe.d/supergfxd.conf"; - -static MODPROBE_BASE: &[u8] = br#"# Automatically generated by supergfxd -blacklist nouveau -alias nouveau off -options nvidia NVreg_DynamicPowerManagement=0x02 -"#; - -static MODPROBE_DRM_MODESET: &[u8] = br#" -options nvidia-drm modeset=1 -"#; - -static MODPROBE_INTEGRATED: &[u8] = br#"# Automatically generated by supergfxd -blacklist i2c_nvidia_gpu -blacklist nvidia -blacklist nvidia-drm -blacklist nvidia-modeset -blacklist nouveau -alias nouveau off -"#; - -static MODPROBE_VFIO: &[u8] = br#"options vfio-pci ids="#; - -const XORG_FILE: &str = "90-nvidia-primary.conf"; -const XORG_PATH: &str = "/etc/X11/xorg.conf.d/"; - -static PRIMARY_GPU_BEGIN: &[u8] = br#"# Automatically generated by supergfxd -Section "OutputClass" - Identifier "nvidia" - MatchDriver "nvidia-drm" - Driver "nvidia" - Option "AllowEmptyInitialConfiguration" "true""#; - -static PRIMARY_GPU_NVIDIA: &[u8] = br#" - Option "PrimaryGPU" "true""#; - -static PRIMARY_GPU_END: &[u8] = br#" -EndSection"#; diff --git a/supergfx/src/special.rs b/supergfx/src/special.rs deleted file mode 100644 index ee1ef4d5..00000000 --- a/supergfx/src/special.rs +++ /dev/null @@ -1,25 +0,0 @@ -use std::{fs::OpenOptions, io::Read, path::Path}; - -use crate::error::GfxError; - -static ASUS_SWITCH_GRAPHIC_MODE: &str = - "/sys/firmware/efi/efivars/AsusSwitchGraphicMode-607005d5-3f75-4b2e-98f0-85ba66797a3e"; - -pub fn has_asus_gsync_gfx_mode() -> bool { - Path::new(ASUS_SWITCH_GRAPHIC_MODE).exists() -} - -pub fn get_asus_gsync_gfx_mode() -> Result { - let path = ASUS_SWITCH_GRAPHIC_MODE; - let mut file = OpenOptions::new() - .read(true) - .open(path) - .map_err(|err| GfxError::Path(path.into(), err))?; - - let mut data = Vec::new(); - file.read_to_end(&mut data) - .map_err(|err| GfxError::Read(path.into(), err))?; - - let idx = data.len() - 1; - Ok(data[idx] as i8) -} diff --git a/supergfx/src/system.rs b/supergfx/src/system.rs deleted file mode 100644 index ce1ddf67..00000000 --- a/supergfx/src/system.rs +++ /dev/null @@ -1,160 +0,0 @@ -use log::{error, info, warn}; -use std::fs::read_to_string; -use std::{fs::write, io, path::PathBuf}; -use sysfs_class::{PciDevice, SysClass}; - -pub struct Module { - pub name: String, -} - -impl Module { - fn parse(line: &str) -> io::Result { - let mut parts = line.split(' '); - - let name = parts - .next() - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "module name not found"))?; - - Ok(Module { - name: name.to_string(), - }) - } - - pub fn all() -> io::Result> { - let mut modules = Vec::new(); - - let data = read_to_string("/proc/modules")?; - for line in data.lines() { - let module = Module::parse(line)?; - modules.push(module); - } - - Ok(modules) - } -} - -#[derive(Clone)] -pub struct PciBus { - path: PathBuf, -} - -impl PciBus { - pub fn new() -> io::Result { - let path = PathBuf::from("/sys/bus/pci"); - if path.is_dir() { - Ok(PciBus { path }) - } else { - Err(io::Error::new( - io::ErrorKind::NotFound, - "pci directory not found", - )) - } - } - - /// Will rescan the device tree, which adds all removed devices back - pub fn rescan(&self) -> io::Result<()> { - write(self.path.join("rescan"), "1") - } -} - -#[derive(Clone)] -pub struct GraphicsDevice { - _id: String, - functions: Vec, -} - -impl GraphicsDevice { - pub fn new(id: String, functions: Vec) -> GraphicsDevice { - GraphicsDevice { _id: id, functions } - } - - pub fn exists(&self) -> bool { - self.functions.iter().any(|func| func.path().exists()) - } - - pub fn functions(&self) -> &[PciDevice] { - &self.functions - } - - pub fn unbind(&self) -> Result<(), std::io::Error> { - for func in self.functions.iter() { - if func.path().exists() { - match func.driver() { - Ok(driver) => { - info!("{}: Unbinding {}", driver.id(), func.id()); - unsafe { - driver.unbind(func).map_err(|err| { - error!("gfx unbind: {}", err); - err - })?; - } - } - Err(err) => match err.kind() { - io::ErrorKind::NotFound => (), - _ => { - error!("gfx driver: {:?}, {}", func.path(), err); - return Err(err); - } - }, - } - } - } - Ok(()) - } - - pub fn rebind(&self) -> Result<(), std::io::Error> { - for func in self.functions.iter() { - if func.path().exists() { - match func.driver() { - Ok(driver) => { - info!("{}: Binding {}", driver.id(), func.id()); - unsafe { - driver.bind(func).map_err(|err| { - error!("gfx bind: {}", err); - err - })?; - } - } - Err(err) => match err.kind() { - io::ErrorKind::NotFound => (), - _ => { - error!("gfx driver: {:?}, {}", func.path(), err); - return Err(err); - } - }, - } - } - } - Ok(()) - } - - pub fn remove(&self) -> Result<(), std::io::Error> { - for func in self.functions.iter() { - if func.path().exists() { - match func.driver() { - Ok(driver) => { - error!("{}: in use by {}", func.id(), driver.id()); - } - Err(why) => match why.kind() { - std::io::ErrorKind::NotFound => { - info!("{}: Removing", func.id()); - unsafe { - // ignore errors and carry on - if let Err(err) = func.remove() { - error!("gfx remove: {}", err); - } - } - } - _ => { - error!("Remove device failed"); - } - }, - } - } else { - warn!("{}: Already removed", func.id()); - } - } - info!("Removed all gfx devices"); - Ok(()) - } -} diff --git a/supergfx/src/zbus_iface.rs b/supergfx/src/zbus_iface.rs deleted file mode 100644 index c6fd371c..00000000 --- a/supergfx/src/zbus_iface.rs +++ /dev/null @@ -1,61 +0,0 @@ -use ::zbus::dbus_interface; -use log::{error, info, warn}; -use zvariant::ObjectPath; - -use crate::{ - gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}, - DBUS_IFACE_PATH, -}; - -use super::controller::CtrlGraphics; - -#[dbus_interface(name = "org.supergfxctl.Daemon")] -impl CtrlGraphics { - fn vendor(&self) -> zbus::fdo::Result { - self.get_gfx_mode().map_err(|err| { - error!("GFX: {}", err); - zbus::fdo::Error::Failed(format!("GFX fail: {}", err)) - }) - } - - fn power(&self) -> zbus::fdo::Result { - Self::get_runtime_status().map_err(|err| { - error!("GFX: {}", err); - zbus::fdo::Error::Failed(format!("GFX fail: {}", err)) - }) - } - - fn set_vendor(&mut self, vendor: GfxVendors) -> zbus::fdo::Result { - info!("GFX: Switching gfx mode to {}", <&str>::from(vendor)); - let msg = self.set_gfx_mode(vendor).map_err(|err| { - error!("GFX: {}", err); - zbus::fdo::Error::Failed(format!("GFX fail: {}", err)) - })?; - - self.notify_action(&msg) - .unwrap_or_else(|err| warn!("GFX: {}", err)); - - self.notify_gfx(&vendor) - .unwrap_or_else(|err| warn!("GFX: {}", err)); - - Ok(msg) - } - - #[dbus_interface(signal)] - fn notify_gfx(&self, vendor: &GfxVendors) -> zbus::Result<()> {} - - #[dbus_interface(signal)] - fn notify_action(&self, action: &GfxRequiredUserAction) -> zbus::Result<()> {} -} - -impl CtrlGraphics { - pub fn add_to_server(self, server: &mut zbus::ObjectServer) { - server - .at(&ObjectPath::from_str_unchecked(DBUS_IFACE_PATH), self) - .map_err(|err| { - warn!("GFX: CtrlGraphics: add_to_server {}", err); - err - }) - .ok(); - } -} diff --git a/supergfx/src/zbus_proxy.rs b/supergfx/src/zbus_proxy.rs deleted file mode 100644 index 21edaafa..00000000 --- a/supergfx/src/zbus_proxy.rs +++ /dev/null @@ -1,117 +0,0 @@ -//! # DBus interface proxy for: `org.asuslinux.Gfx` -//! -//! This code was generated by `zbus-xmlgen` `1.0.0` from DBus introspection data. -//! Source: `Interface '/org/supergfxctl/Gfx' from service 'org.asuslinux.Daemon' on system bus`. -//! -//! You may prefer to adapt it, instead of using it verbatim. -//! -//! More information can be found in the -//! [Writing a client proxy](https://zeenix.pages.freedesktop.org/zbus/client.html) -//! section of the zbus documentation. -//! -//! This DBus object implements -//! [standard DBus interfaces](https://dbus.freedesktop.org/doc/dbus-specification.html), -//! (`org.freedesktop.DBus.*`) for which the following zbus proxies can be used: -//! -//! * [`zbus::fdo::PropertiesProxy`] -//! * [`zbus::fdo::IntrospectableProxy`] -//! * [`zbus::fdo::PeerProxy`] -//! -//! …consequently `zbus-xmlgen` did not generate code for the above interfaces. - -use std::sync::mpsc::{Sender}; - -use zbus::{dbus_proxy, Connection, Message, Result}; - -use crate::{ - gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}, - DBUS_IFACE_PATH, -}; - -#[dbus_proxy(interface = "org.supergfxctl.Daemon")] -trait Daemon { - /// Power method - fn power(&self) -> zbus::Result; - - /// SetVendor method - fn set_vendor(&self, vendor: &GfxVendors) -> zbus::Result; - - /// Vendor method - fn vendor(&self) -> zbus::Result; - - /// NotifyAction signal - #[dbus_proxy(signal)] - fn notify_action(&self, action: GfxRequiredUserAction) -> zbus::Result<()>; - - /// NotifyGfx signal - #[dbus_proxy(signal)] - fn notify_gfx(&self, vendor: GfxVendors) -> zbus::Result<()>; -} - -pub struct GfxProxy<'a>(pub DaemonProxy<'a>); - -impl<'a> GfxProxy<'a> { - #[inline] - pub fn new(conn: &Connection) -> Result { - let proxy = DaemonProxy::new_for(conn, "org.supergfxctl.Daemon", DBUS_IFACE_PATH)?; - Ok(GfxProxy(proxy)) - } - - #[inline] - pub fn new_for(conn: &Connection, destination: &'a str, path: &'a str) -> Result { - let proxy = DaemonProxy::new_for(conn, destination, path)?; - Ok(GfxProxy(proxy)) - } - - #[inline] - pub fn new_for_owned(conn: Connection, destination: String, path: String) -> Result { - let proxy = DaemonProxy::new_for_owned(conn, destination, path)?; - Ok(GfxProxy(proxy)) - } - - #[inline] - pub fn proxy(&self) -> &DaemonProxy<'a> { - &self.0 - } - - #[inline] - pub fn gfx_get_pwr(&self) -> Result { - self.0.power() - } - - #[inline] - pub fn gfx_get_mode(&self) -> Result { - self.0.vendor() - } - - #[inline] - pub fn gfx_write_mode(&self, vendor: &GfxVendors) -> Result { - self.0.set_vendor(vendor) - } - - #[inline] - pub fn connect_notify_action( - &self, - send: Sender, - ) -> zbus::fdo::Result<()> { - self.0.connect_notify_action(move |data| { - send.send(data) - .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; - Ok(()) - }) - } - - #[inline] - pub fn connect_notify_gfx(&self, send: Sender) -> zbus::fdo::Result<()> { - self.0.connect_notify_gfx(move |data| { - send.send(data) - .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; - Ok(()) - }) - } - - #[inline] - pub fn next_signal(&self) -> Result> { - self.0.next_signal() - } -} From e89e7ca10fee3c69b7a9711669cacce7852e71c9 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Fri, 27 Aug 2021 20:14:19 +1200 Subject: [PATCH 10/11] Add LED brightness pre/next cycle Closes #129 --- CHANGELOG.md | 2 ++ asusctl/src/main.rs | 16 ++++++++++++++-- daemon/src/ctrl_aura/controller.rs | 22 ++++++++++++++++++++++ daemon/src/ctrl_aura/zbus.rs | 14 ++++++++++++++ rog-dbus/src/zbus_led.rs | 16 ++++++++++++++++ 5 files changed, 68 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60cebb75..c2319347 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + Support 8bit RGB, RGBA, 16bit Greyscalw, RGB, RGBA + add `AsusImage` type for slanted-template pixel-perfect images + `BREAKING:` plain `Image` with time period is changed and old anime configs break as a result (sorry) +- LED: + + By popular request LED prev/next cycle is added ### BREAKING CHANGES - Graphics control: + graphics control is pulled out of asusd and moved to new package; https://gitlab.com/asus-linux/supergfxctl diff --git a/asusctl/src/main.rs b/asusctl/src/main.rs index 09f6ad76..20669e72 100644 --- a/asusctl/src/main.rs +++ b/asusctl/src/main.rs @@ -31,6 +31,10 @@ struct CliStart { show_supported: bool, #[options(meta = "", help = "")] kbd_bright: Option, + #[options(help = "Toggle to next keyboard brightness")] + next_kbd_bright: bool, + #[options(help = "Toggle to previous keyboard brightness")] + prev_kbd_bright: bool, #[options(meta = "", help = "<20-100>")] chg_limit: Option, #[options(command)] @@ -158,8 +162,8 @@ fn main() -> Result<(), Box> { Some(CliCommand::Anime(cmd)) => handle_anime(&dbus, &supported.anime_ctrl, &cmd)?, Some(CliCommand::Bios(cmd)) => handle_bios_option(&dbus, &supported.rog_bios_ctrl, &cmd)?, None => { - if (!parsed.show_supported && parsed.kbd_bright.is_none() && parsed.chg_limit.is_none()) - || parsed.help + if (!parsed.show_supported && parsed.kbd_bright.is_none() && parsed.chg_limit.is_none() + && !parsed.next_kbd_bright && !parsed.prev_kbd_bright) || parsed.help { println!("{}", CliStart::usage()); println!(); @@ -181,6 +185,14 @@ fn main() -> Result<(), Box> { } } + if parsed.next_kbd_bright { + dbus.proxies().led().next_led_brightness()?; + } + + if parsed.prev_kbd_bright { + dbus.proxies().led().prev_led_brightness()?; + } + if parsed.show_supported { println!("Supported laptop functions:\n\n{}", supported); } diff --git a/daemon/src/ctrl_aura/controller.rs b/daemon/src/ctrl_aura/controller.rs index e294fa04..67e4b21e 100644 --- a/daemon/src/ctrl_aura/controller.rs +++ b/daemon/src/ctrl_aura/controller.rs @@ -239,6 +239,28 @@ impl CtrlKbdLed { Ok(()) } + 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 = ::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 = ::from(bright); + self.config.write(); + self.set_brightness(self.config.brightness) + } + /// Set if awake/on LED active, and/or sleep animation active pub(super) fn set_states_enabled(&self, awake: bool, sleep: bool) -> Result<(), RogError> { let bytes = if awake && sleep { diff --git a/daemon/src/ctrl_aura/zbus.rs b/daemon/src/ctrl_aura/zbus.rs index d96fa095..eeab2619 100644 --- a/daemon/src/ctrl_aura/zbus.rs +++ b/daemon/src/ctrl_aura/zbus.rs @@ -105,6 +105,20 @@ impl CtrlKbdLedZbus { } } + fn next_led_brightness(&self) { + if let Ok(mut ctrl) = self.0.try_lock() { + ctrl.next_brightness() + .unwrap_or_else(|err| warn!("{}", err)); + } + } + + fn prev_led_brightness(&self) { + if let Ok(mut ctrl) = self.0.try_lock() { + ctrl.prev_brightness() + .unwrap_or_else(|err| warn!("{}", err)); + } + } + #[dbus_interface(property)] fn awake_enabled(&self) -> bool { if let Ok(ctrl) = self.0.try_lock() { diff --git a/rog-dbus/src/zbus_led.rs b/rog-dbus/src/zbus_led.rs index 675a4143..5440961f 100644 --- a/rog-dbus/src/zbus_led.rs +++ b/rog-dbus/src/zbus_led.rs @@ -38,6 +38,12 @@ trait Daemon { /// PrevLedMode method fn prev_led_mode(&self) -> zbus::Result<()>; + /// Toggle to next led brightness + fn next_led_brightness(&self) -> zbus::Result<()>; + + /// Toggle to previous led brightness + fn prev_led_brightness(&self) -> zbus::Result<()>; + /// SetBrightness method fn set_brightness(&self, brightness: LedBrightness) -> zbus::Result<()>; @@ -124,6 +130,16 @@ impl<'a> LedProxy<'a> { self.0.prev_led_mode() } + #[inline] + pub fn next_led_brightness(&self) -> Result<()> { + self.0.next_led_brightness() + } + + #[inline] + pub fn prev_led_brightness(&self) -> Result<()> { + self.0.prev_led_brightness() + } + #[inline] pub fn set_led_mode(&self, mode: &AuraEffect) -> Result<()> { self.0.set_led_mode(mode) From 52f3b5a7bf2caf9def6571a962cbccab071b380b Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Fri, 27 Aug 2021 20:22:15 +1200 Subject: [PATCH 11/11] Charge limit: try to support BAT Closes #128 --- daemon/src/ctrl_charge.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/daemon/src/ctrl_charge.rs b/daemon/src/ctrl_charge.rs index b5a71594..ba50bc94 100644 --- a/daemon/src/ctrl_charge.rs +++ b/daemon/src/ctrl_charge.rs @@ -10,7 +10,9 @@ use std::sync::Mutex; use zbus::dbus_interface; use zvariant::ObjectPath; -static BAT_CHARGE_PATH: &str = "/sys/class/power_supply/BAT0/charge_control_end_threshold"; +static BAT_CHARGE_PATH0: &str = "/sys/class/power_supply/BAT0/charge_control_end_threshold"; +static BAT_CHARGE_PATH1: &str = "/sys/class/power_supply/BAT1/charge_control_end_threshold"; +static BAT_CHARGE_PATH2: &str = "/sys/class/power_supply/BAT2/charge_control_end_threshold"; impl GetSupported for CtrlCharge { type A = ChargeSupportedFunctions; @@ -88,8 +90,12 @@ impl CtrlCharge { } fn get_battery_path() -> Result<&'static str, RogError> { - if Path::new(BAT_CHARGE_PATH).exists() { - Ok(BAT_CHARGE_PATH) + if Path::new(BAT_CHARGE_PATH0).exists() { + Ok(BAT_CHARGE_PATH0) + } else if Path::new(BAT_CHARGE_PATH1).exists() { + Ok(BAT_CHARGE_PATH1) + } else if Path::new(BAT_CHARGE_PATH2).exists() { + Ok(BAT_CHARGE_PATH2) } else { Err(RogError::MissingFunction( "Charge control not available, you may require a v5.8.10 series kernel or newer" @@ -106,12 +112,14 @@ impl CtrlCharge { ); } + let path = Self::get_battery_path()?; + let mut file = OpenOptions::new() .write(true) - .open(BAT_CHARGE_PATH) - .map_err(|err| RogError::Path(BAT_CHARGE_PATH.into(), err))?; + .open(path) + .map_err(|err| RogError::Path(path.into(), err))?; file.write_all(limit.to_string().as_bytes()) - .map_err(|err| RogError::Write(BAT_CHARGE_PATH.into(), err))?; + .map_err(|err| RogError::Write(path.into(), err))?; info!("Battery charge limit: {}", limit); config.read();