From 99d642d644419a9a1644b2486a2df2fde51363c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20T=C3=BCrker?= <166562458+burakturkerdev@users.noreply.github.com> Date: Tue, 28 May 2024 18:31:58 +0300 Subject: [PATCH] improv(power): support system76-power and gnome ppd --- .../src/pages/power/backend/mod.rs | 245 ++++++++++++++---- .../src/pages/power/backend/ppdaemon.rs | 42 +++ .../src/pages/power/backend/s76powerdaemon.rs | 3 +- cosmic-settings/src/pages/power/mod.rs | 64 +++-- i18n/en/cosmic_settings.ftl | 1 + 5 files changed, 272 insertions(+), 83 deletions(-) create mode 100644 cosmic-settings/src/pages/power/backend/ppdaemon.rs diff --git a/cosmic-settings/src/pages/power/backend/mod.rs b/cosmic-settings/src/pages/power/backend/mod.rs index e724d24..470e46e 100644 --- a/cosmic-settings/src/pages/power/backend/mod.rs +++ b/cosmic-settings/src/pages/power/backend/mod.rs @@ -1,25 +1,57 @@ use zbus::Connection; + +mod ppdaemon; mod s76powerdaemon; -// Get backend, if exist s76powerdaemon preferred. If not exist power profiles deamon preferred. -async fn get_power_daemon<'a>() -> Result, ()> { - let connection = match Connection::system().await { - Ok(c) => c, - Err(e) => { - println!("[Cosmic Settings] zbus connection failed. {e}"); - return Err(()); - } - }; +pub trait SetPowerProfile { + async fn set_power_profile(&self, profile: PowerProfile); +} - match s76powerdaemon::PowerDaemonProxy::new(&connection).await { - Ok(d) => Ok(d), - Err(e) => { - println!("[S76PowerDaemon] Power daemon proxy can't created. Is it installed? {e}"); - Err(()) +pub trait GetCurrentPowerProfile { + async fn get_current_power_profile(&self) -> PowerProfile; +} + +pub enum PowerBackendEnum { + S76(S76Backend), + PP(PPBackend), +} + +impl SetPowerProfile for PowerBackendEnum { + async fn set_power_profile(&self, profile: PowerProfile) { + match self { + PowerBackendEnum::S76(backend) => backend.set_power_profile(profile).await, + PowerBackendEnum::PP(backend) => backend.set_power_profile(profile).await, } } } +impl GetCurrentPowerProfile for PowerBackendEnum { + async fn get_current_power_profile(&self) -> PowerProfile { + match self { + PowerBackendEnum::S76(backend) => backend.get_current_power_profile().await, + PowerBackendEnum::PP(backend) => backend.get_current_power_profile().await, + } + } +} + +pub trait PowerBackend: SetPowerProfile + GetCurrentPowerProfile {} + +pub async fn get_backend() -> Option { + match get_s76power_daemon_proxy().await { + Ok(p) => match p.get_profile().await { + Ok(_) => Some(PowerBackendEnum::S76(S76Backend {})), + Err(_) => match get_power_profiles_proxy().await { + Ok(pr) => match pr.active_profile().await { + Ok(_) => Some(PowerBackendEnum::PP(PPBackend {})), + Err(_) => None, + }, + Err(()) => None, + }, + }, + Err(()) => None, + } +} + #[derive(Clone, Debug, PartialEq)] pub enum PowerProfile { Performance, @@ -30,8 +62,8 @@ pub enum PowerProfile { impl PowerProfile { fn from_string(s: &str) -> PowerProfile { match s { - "Performance" => Self::Performance, - "Battery" => Self::Battery, + "Performance" | "performance" => Self::Performance, + "Battery" | "power-saver" => Self::Battery, _ => Self::Balanced, } } @@ -53,31 +85,6 @@ impl PowerProfile { } } -pub async fn set_power_profile(profile: PowerProfile) { - let daemon = match get_power_daemon().await { - Ok(c) => c, - Err(e) => { - println!("[Cosmic Settings] Problem while setting power profile."); - return; - } - }; - - match profile { - PowerProfile::Performance => match daemon.performance().await { - Ok(x) => println!("[Cosmic Settings] Performance mode activated."), - Err(e) => println!("{e}"), - }, - PowerProfile::Balanced => match daemon.balanced().await { - Ok(x) => println!("[Cosmic Settings] Balanced mode activated."), - Err(e) => println!("{e}"), - }, - PowerProfile::Battery => match daemon.battery().await { - Ok(x) => println!("[Cosmic Settings] Battery mode activated."), - Err(e) => println!("{e}"), - }, - } -} - pub fn get_power_profiles() -> Vec { vec![ PowerProfile::Performance, @@ -86,23 +93,147 @@ pub fn get_power_profiles() -> Vec { ] } -pub async fn get_current_power_profile() -> PowerProfile { - let daemon = match get_power_daemon().await { - Ok(c) => c, - Err(e) => { - println!("[Cosmic Settings] Problem while setting power profile."); - //Default - return PowerProfile::Balanced; - } - }; +pub struct S76Backend {} - match daemon.get_profile().await { - Ok(p) => PowerProfile::from_string(p.as_str()), - //Default - Err(_) => { - println!("[Cosmic Settings] Problem while setting power profile."); - //Default - PowerProfile::Balanced +impl PowerBackend for S76Backend {} + +impl SetPowerProfile for S76Backend { + async fn set_power_profile(&self, profile: PowerProfile) { + let daemon = match get_s76power_daemon_proxy().await { + Ok(c) => c, + Err(e) => { + tracing::error!("Problem while setting power profile."); + return; + } + }; + + match profile { + PowerProfile::Performance => match daemon.performance().await { + Ok(x) => tracing::info!("Performance mode activated."), + Err(e) => tracing::error!("{e}"), + }, + PowerProfile::Balanced => match daemon.balanced().await { + Ok(x) => tracing::info!("Balanced mode activated."), + Err(e) => tracing::error!("{e}"), + }, + PowerProfile::Battery => match daemon.battery().await { + Ok(x) => tracing::info!("Battery mode activated."), + Err(e) => tracing::error!("{e}"), + }, + } + } +} + +impl GetCurrentPowerProfile for S76Backend { + async fn get_current_power_profile(&self) -> PowerProfile { + let daemon = match get_s76power_daemon_proxy().await { + Ok(c) => c, + Err(e) => { + tracing::error!("Problem while getting power profile."); + //Default + return PowerProfile::Balanced; + } + }; + + match daemon.get_profile().await { + Ok(p) => PowerProfile::from_string(p.as_str()), + //Default + Err(_) => { + tracing::error!("Problem while getting power profile."); + //Default + PowerProfile::Balanced + } + } + } +} + +async fn get_s76power_daemon_proxy<'a>() -> Result, ()> { + let connection = match Connection::system().await { + Ok(c) => c, + Err(e) => { + tracing::error!("zbus connection failed. {e}"); + return Err(()); + } + }; + + match s76powerdaemon::PowerDaemonProxy::new(&connection).await { + Ok(d) => Ok(d), + Err(e) => { + tracing::error!("Power daemon proxy can't created. Is it installed? {e}"); + Err(()) + } + } +} + +pub struct PPBackend {} + +impl PowerBackend for PPBackend {} + +impl SetPowerProfile for PPBackend { + async fn set_power_profile(&self, profile: PowerProfile) { + let daemon = match get_power_profiles_proxy().await { + Ok(c) => c, + Err(e) => { + tracing::error!("Problem while setting power profile."); + return; + } + }; + + match profile { + PowerProfile::Performance => match daemon.set_active_profile("performance").await { + Ok(x) => tracing::info!("Performance mode activated."), + Err(e) => tracing::error!("{e}"), + }, + PowerProfile::Balanced => match daemon.set_active_profile("balanced").await { + Ok(x) => tracing::info!("Balanced mode activated."), + Err(e) => tracing::error!("{e}"), + }, + PowerProfile::Battery => match daemon.set_active_profile("power-saver").await { + Ok(x) => tracing::info!("Battery mode activated."), + Err(e) => tracing::error!("{e}"), + }, + } + } +} + +impl GetCurrentPowerProfile for PPBackend { + async fn get_current_power_profile(&self) -> PowerProfile { + let daemon = match get_power_profiles_proxy().await { + Ok(c) => c, + Err(e) => { + tracing::error!("Problem while getting power profile."); + //Default + return PowerProfile::Balanced; + } + }; + + let profile = match daemon.active_profile().await { + Ok(p) => p, + Err(e) => { + tracing::error!("Problem while getting power profile."); + //Default + return PowerProfile::Balanced; + } + }; + + PowerProfile::from_string(&profile) + } +} + +async fn get_power_profiles_proxy<'a>() -> Result, ()> { + let connection = match Connection::system().await { + Ok(c) => c, + Err(e) => { + tracing::error!("zbus connection failed. {e}"); + return Err(()); + } + }; + + match ppdaemon::PowerProfilesProxy::new(&connection).await { + Ok(d) => Ok(d), + Err(e) => { + tracing::error!("Power daemon proxy can't created. Is it installed? {e}"); + Err(()) } } } diff --git a/cosmic-settings/src/pages/power/backend/ppdaemon.rs b/cosmic-settings/src/pages/power/backend/ppdaemon.rs new file mode 100644 index 0000000..674febf --- /dev/null +++ b/cosmic-settings/src/pages/power/backend/ppdaemon.rs @@ -0,0 +1,42 @@ +use zbus::{proxy, Connection}; + +#[proxy( + interface = "org.freedesktop.UPower.PowerProfiles", + default_path = "/org/freedesktop/UPower/PowerProfiles", + assume_defaults = true +)] +trait PowerProfiles { + fn hold_profile(&self, profile: &str, reason: &str, application_id: &str) -> zbus::Result; + + fn release_profile(&self, cookie: u32) -> zbus::Result<()>; + + #[zbus(signal)] + fn profile_released(&self, cookie: u32) -> zbus::Result<()>; + + #[zbus(property)] + fn actions(&self) -> zbus::Result>; + + #[zbus(property)] + fn active_profile(&self) -> zbus::Result; + #[zbus(property)] + fn set_active_profile(&self, value: &str) -> zbus::Result<()>; + + #[zbus(property)] + fn active_profile_holds( + &self, + ) -> zbus::Result>>; + + #[zbus(property)] + fn performance_degraded(&self) -> zbus::Result; + + #[zbus(property)] + fn performance_inhibited(&self) -> zbus::Result; + + #[zbus(property)] + fn profiles( + &self, + ) -> zbus::Result>>; + + #[zbus(property)] + fn version(&self) -> zbus::Result; +} diff --git a/cosmic-settings/src/pages/power/backend/s76powerdaemon.rs b/cosmic-settings/src/pages/power/backend/s76powerdaemon.rs index e8ad435..479655c 100644 --- a/cosmic-settings/src/pages/power/backend/s76powerdaemon.rs +++ b/cosmic-settings/src/pages/power/backend/s76powerdaemon.rs @@ -1,5 +1,4 @@ -use zbus::proxy; - +use zbus::{proxy, Connection}; #[proxy( interface = "com.system76.PowerDaemon", default_path = "/com/system76/PowerDaemon", diff --git a/cosmic-settings/src/pages/power/mod.rs b/cosmic-settings/src/pages/power/mod.rs index 4089bf1..411cc6e 100644 --- a/cosmic-settings/src/pages/power/mod.rs +++ b/cosmic-settings/src/pages/power/mod.rs @@ -4,7 +4,9 @@ use cosmic::{widget::settings, Apply}; use cosmic_settings_page::{self as page, section, Section}; use slotmap::SlotMap; -pub mod backend; +use self::backend::{GetCurrentPowerProfile, SetPowerProfile}; + +mod backend; #[derive(Default)] pub struct Page; @@ -32,8 +34,15 @@ pub enum Message { impl Page { pub fn update(&mut self, message: Message) { let runtime = tokio::runtime::Runtime::new().unwrap(); + + let backend = runtime.block_on(backend::get_backend()); + match message { - Message::PowerProfileChange(p) => runtime.block_on(backend::set_power_profile(p)), + Message::PowerProfileChange(p) => { + if let Some(b) = backend { + runtime.block_on(b.set_power_profile(p)); + } + } }; } } @@ -43,33 +52,40 @@ fn profiles() -> Section { .title(fl!("power-profiles")) .descriptions(vec![fl!("power", "desc").into()]) .view::(|_binder, page, section| { - let runtime = tokio::runtime::Runtime::new().unwrap(); - - let profiles = backend::get_power_profiles(); - - let current_profile = runtime.block_on(backend::get_current_power_profile()); - let mut section = settings::view_section(§ion.title); - let mut widgets = Vec::new(); + let runtime = tokio::runtime::Runtime::new().unwrap(); - for profile in profiles { - let selected = if current_profile == profile { - Some(true) - } else { - None - }; + let backend = runtime.block_on(backend::get_backend()); - let widget = widget::Radio::new("", true, selected, |_| { - Message::PowerProfileChange(profile.clone()) - }); - let item = settings::item::builder(profile.title()) - .description(profile.description()) - .control(widget); - widgets.push(item); - } + if let Some(b) = backend { + let profiles = backend::get_power_profiles(); - for item in widgets { + let current_profile = runtime.block_on(b.get_current_power_profile()); + + let mut widgets = Vec::new(); + + for profile in profiles { + let selected = if current_profile == profile { + Some(true) + } else { + None + }; + + let widget = widget::Radio::new("", true, selected, |_| { + Message::PowerProfileChange(profile.clone()) + }); + let item = settings::item::builder(profile.title()) + .description(profile.description()) + .control(widget); + widgets.push(item); + } + + for item in widgets { + section = section.add(item); + } + } else { + let item = widget::Text::new(fl!("power-profiles", "nobackend")); section = section.add(item); } diff --git a/i18n/en/cosmic_settings.ftl b/i18n/en/cosmic_settings.ftl index 52589bd..b45c268 100644 --- a/i18n/en/cosmic_settings.ftl +++ b/i18n/en/cosmic_settings.ftl @@ -452,3 +452,4 @@ power-profiles = Power Profiles .performance-desc = Maximum performance but high power consumption. .balanced-desc = Balanced performance and power consumption. .battery-desc = Low performance but low power consumption. + .nobackend = Backend not found. Install system76-power or power-profiles-daemon.