From 1b3d00ebfcf46002bb04a716cee5308a4a75d949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20T=C3=BCrker?= <166562458+burakturkerdev@users.noreply.github.com> Date: Wed, 22 May 2024 20:37:50 +0300 Subject: [PATCH] feat: add power setttings page, with power profiles --- cosmic-settings/src/app.rs | 10 +- cosmic-settings/src/pages/mod.rs | 2 + .../src/pages/power/backend/mod.rs | 108 ++++++++++++++++++ .../src/pages/power/backend/s76powerdaemon.rs | 41 +++++++ cosmic-settings/src/pages/power/mod.rs | 83 ++++++++++++++ i18n/en/cosmic_settings.ftl | 13 +++ 6 files changed, 255 insertions(+), 2 deletions(-) create mode 100644 cosmic-settings/src/pages/power/backend/mod.rs create mode 100644 cosmic-settings/src/pages/power/backend/s76powerdaemon.rs create mode 100644 cosmic-settings/src/pages/power/mod.rs diff --git a/cosmic-settings/src/app.rs b/cosmic-settings/src/app.rs index 4389529..1f8662a 100644 --- a/cosmic-settings/src/app.rs +++ b/cosmic-settings/src/app.rs @@ -11,10 +11,11 @@ use crate::pages::desktop::{ }, }; use crate::pages::input::{self}; -use crate::pages::{self, display, sound, system, time}; +use crate::pages::{self, display, power, sound, system, time}; use crate::subscription::desktop_files; use crate::widget::{page_title, search_header}; use crate::PageCommands; +use cosmic::app::command::message; use cosmic::app::DbusActivationMessage; use cosmic::iced::futures::SinkExt; use cosmic::iced::Subscription; @@ -63,7 +64,7 @@ impl SettingsApp { PageCommands::Keyboard => self.pages.page_id::(), PageCommands::Mouse => self.pages.page_id::(), PageCommands::Network => None, - PageCommands::Power => None, + PageCommands::Power => self.pages.page_id::(), PageCommands::RegionLanguage => self.pages.page_id::(), PageCommands::Sound => self.pages.page_id::(), PageCommands::Time => self.pages.page_id::(), @@ -129,6 +130,7 @@ impl cosmic::Application for SettingsApp { app.insert_page::(); app.insert_page::(); app.insert_page::(); + app.insert_page::(); let active_id = match flags.subcommand { Some(p) => app.subcommand_to_page(&p), @@ -365,6 +367,10 @@ impl cosmic::Application for SettingsApp { return page.update(message).map(cosmic::app::Message::App); } } + + crate::pages::Message::Power(message) => { + page::update!(self.pages, message, power::Page) + } }, Message::PanelConfig(config) if config.name.to_lowercase().contains("panel") => { diff --git a/cosmic-settings/src/pages/mod.rs b/cosmic-settings/src/pages/mod.rs index 8ea18f3..213985d 100644 --- a/cosmic-settings/src/pages/mod.rs +++ b/cosmic-settings/src/pages/mod.rs @@ -10,12 +10,14 @@ pub mod networking; pub mod sound; pub mod system; pub mod time; +pub mod power; #[derive(Clone, Debug)] pub enum Message { About(system::about::Message), Appearance(desktop::appearance::Message), DateAndTime(time::date::Message), + Power(power::Message), Desktop(desktop::Message), DesktopWallpaper(desktop::wallpaper::Message), DesktopWorkspaces(desktop::workspaces::Message), diff --git a/cosmic-settings/src/pages/power/backend/mod.rs b/cosmic-settings/src/pages/power/backend/mod.rs new file mode 100644 index 0000000..e724d24 --- /dev/null +++ b/cosmic-settings/src/pages/power/backend/mod.rs @@ -0,0 +1,108 @@ +use zbus::Connection; +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(()); + } + }; + + 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(()) + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum PowerProfile { + Performance, + Balanced, + Battery, +} + +impl PowerProfile { + fn from_string(s: &str) -> PowerProfile { + match s { + "Performance" => Self::Performance, + "Battery" => Self::Battery, + _ => Self::Balanced, + } + } + + pub fn title(&self) -> String { + match self { + Self::Performance => fl!("power-profiles", "performance"), + Self::Balanced => fl!("power-profiles", "balanced"), + Self::Battery => fl!("power-profiles", "battery"), + } + } + + pub fn description(&self) -> String { + match self { + Self::Performance => fl!("power-profiles", "performance-desc"), + Self::Balanced => fl!("power-profiles", "balanced-desc"), + Self::Battery => fl!("power-profiles", "battery-desc"), + } + } +} + +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, + PowerProfile::Balanced, + PowerProfile::Battery, + ] +} + +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; + } + }; + + 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 + } + } +} diff --git a/cosmic-settings/src/pages/power/backend/s76powerdaemon.rs b/cosmic-settings/src/pages/power/backend/s76powerdaemon.rs new file mode 100644 index 0000000..e8ad435 --- /dev/null +++ b/cosmic-settings/src/pages/power/backend/s76powerdaemon.rs @@ -0,0 +1,41 @@ +use zbus::proxy; + +#[proxy( + interface = "com.system76.PowerDaemon", + default_path = "/com/system76/PowerDaemon", + assume_defaults = true +)] +pub trait PowerDaemon { + fn balanced(&self) -> zbus::Result<()>; + + fn battery(&self) -> zbus::Result<()>; + + fn get_charge_profiles( + &self, + ) -> zbus::Result>>; + + fn get_charge_thresholds(&self) -> zbus::Result<(u8, u8)>; + + fn get_default_graphics(&self) -> zbus::Result; + + fn get_external_displays_require_dgpu(&self) -> zbus::Result; + + fn get_graphics(&self) -> zbus::Result; + + fn get_graphics_power(&self) -> zbus::Result; + + fn get_profile(&self) -> zbus::Result; + + fn get_switchable(&self) -> zbus::Result; + + fn performance(&self) -> zbus::Result<()>; + + fn set_charge_thresholds(&self, thresholds: &(u8, u8)) -> zbus::Result<()>; + + fn set_graphics(&self, vendor: &str) -> zbus::Result<()>; + + fn set_graphics_power(&self, power: bool) -> zbus::Result<()>; + + #[zbus(signal)] + fn hot_plug_detect(&self, port: u64) -> zbus::Result<()>; +} diff --git a/cosmic-settings/src/pages/power/mod.rs b/cosmic-settings/src/pages/power/mod.rs new file mode 100644 index 0000000..81fbd57 --- /dev/null +++ b/cosmic-settings/src/pages/power/mod.rs @@ -0,0 +1,83 @@ +use cosmic_settings_page::{self as page, section, Section}; +use backend::PowerProfile; +use cosmic::iced::widget; +use cosmic::{widget::settings, Apply}; +use slotmap::SlotMap; + +pub mod backend; + +#[derive(Default)] +pub struct Page; + +impl page::Page for Page { + fn info(&self) -> page::Info { + page::Info::new("power", "battery-symbolic") + .title(fl!("power")) + .description(fl!("power", "desc")) + } + + fn content( + &self, + sections: &mut SlotMap>, + ) -> Option { + Some(vec![sections.insert(profiles())]) + } +} + +#[derive(Clone, Debug)] +pub enum Message { + PowerProfileChange(PowerProfile), +} + +impl Page { + pub fn update(&mut self, message: Message) { + let runtime = tokio::runtime::Runtime::new().unwrap(); + match message { + Message::PowerProfileChange(p) => runtime.block_on(backend::set_power_profile(p)), + }; + } +} + + +fn profiles() -> Section { + Section::default() + .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(); + + 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); + } + + section + .apply(cosmic::Element::from) + .map(crate::pages::Message::Power) + }) +} + +impl page::AutoBind for Page {} diff --git a/i18n/en/cosmic_settings.ftl b/i18n/en/cosmic_settings.ftl index 387f95e..7d0d01a 100644 --- a/i18n/en/cosmic_settings.ftl +++ b/i18n/en/cosmic_settings.ftl @@ -431,3 +431,16 @@ switch-to-next-workspace = Switch to next workspace switch-to-prev-workspace = Switch to prev workspace open-application-library = Open Application Library open-workspaces-view = Open Workspaces Overview + +## Power + +power = Power + .desc = Manage power settings + +power-profiles = Power Profiles + .performance = Performance Mode + .balanced = Balanced Mode + .battery = Power Save Mode + .performance-desc = Maximum performance but high power consumption. + .balanced-desc = Balanced performance and power consumption. + .battery-desc = Low performance but low power consumption.