improv(power): support system76-power and gnome ppd

This commit is contained in:
Burak Türker 2024-05-28 18:31:58 +03:00 committed by GitHub
parent f8f4bd17ee
commit 99d642d644
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 272 additions and 83 deletions

View file

@ -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<s76powerdaemon::PowerDaemonProxy<'a>, ()> {
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<PowerBackendEnum> {
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<PowerProfile> {
vec![
PowerProfile::Performance,
@ -86,23 +93,147 @@ pub fn get_power_profiles() -> Vec<PowerProfile> {
]
}
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<s76powerdaemon::PowerDaemonProxy<'a>, ()> {
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<ppdaemon::PowerProfilesProxy<'a>, ()> {
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(())
}
}
}

View file

@ -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<u32>;
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<Vec<String>>;
#[zbus(property)]
fn active_profile(&self) -> zbus::Result<String>;
#[zbus(property)]
fn set_active_profile(&self, value: &str) -> zbus::Result<()>;
#[zbus(property)]
fn active_profile_holds(
&self,
) -> zbus::Result<Vec<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>>;
#[zbus(property)]
fn performance_degraded(&self) -> zbus::Result<String>;
#[zbus(property)]
fn performance_inhibited(&self) -> zbus::Result<String>;
#[zbus(property)]
fn profiles(
&self,
) -> zbus::Result<Vec<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>>;
#[zbus(property)]
fn version(&self) -> zbus::Result<String>;
}

View file

@ -1,5 +1,4 @@
use zbus::proxy;
use zbus::{proxy, Connection};
#[proxy(
interface = "com.system76.PowerDaemon",
default_path = "/com/system76/PowerDaemon",

View file

@ -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<crate::pages::Message> {
.title(fl!("power-profiles"))
.descriptions(vec![fl!("power", "desc").into()])
.view::<Page>(|_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(&section.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);
}

View file

@ -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.