From 11666225d0f674d8643093de97ac09274b1a9111 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 17 Oct 2024 19:09:58 -0700 Subject: [PATCH] Add "Power Savings" settings --- Cargo.lock | 10 ++ Cargo.toml | 3 + cosmic-settings/Cargo.toml | 1 + cosmic-settings/src/pages/power/mod.rs | 168 ++++++++++++++++++++++++- i18n/en/cosmic_settings.ftl | 6 + 5 files changed, 187 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 7640bac..841790e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1514,6 +1514,15 @@ dependencies = [ "zvariant 4.2.0", ] +[[package]] +name = "cosmic-idle-config" +version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-idle#f682e19f2105f7bb4b722b858bf747dcb60be2c0" +dependencies = [ + "cosmic-config", + "serde", +] + [[package]] name = "cosmic-panel-config" version = "0.1.0" @@ -1586,6 +1595,7 @@ dependencies = [ "cosmic-comp-config", "cosmic-config", "cosmic-dbus-networkmanager", + "cosmic-idle-config", "cosmic-panel-config", "cosmic-randr", "cosmic-randr-shell", diff --git a/Cargo.toml b/Cargo.toml index 528c16d..fcefdf1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,9 @@ git = "https://github.com/pop-os/cosmic-bg" [workspace.dependencies.cosmic-comp-config] git = "https://github.com/pop-os/cosmic-comp" +[workspace.dependencies.cosmic-idle-config] +git = "https://github.com/pop-os/cosmic-idle" + [workspace.dependencies.cosmic-panel-config] git = "https://github.com/pop-os/cosmic-panel" diff --git a/cosmic-settings/Cargo.toml b/cosmic-settings/Cargo.toml index 4d62a87..9c578a4 100644 --- a/cosmic-settings/Cargo.toml +++ b/cosmic-settings/Cargo.toml @@ -16,6 +16,7 @@ cosmic-bg-config.workspace = true cosmic-comp-config = { workspace = true, optional = true } cosmic-config.workspace = true cosmic-dbus-networkmanager = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true } +cosmic-idle-config.workspace = true cosmic-panel-config = { workspace = true, optional = true } cosmic-randr-shell.workspace = true cosmic-randr = { workspace = true, optional = true } diff --git a/cosmic-settings/src/pages/power/mod.rs b/cosmic-settings/src/pages/power/mod.rs index aad69fa..cc683a8 100644 --- a/cosmic-settings/src/pages/power/mod.rs +++ b/cosmic-settings/src/pages/power/mod.rs @@ -9,17 +9,82 @@ use cosmic::iced_widget::{column, row}; use cosmic::widget::{self, radio, settings, text}; use cosmic::Apply; use cosmic::Task; +use cosmic_config::{Config, CosmicConfigEntry}; +use cosmic_idle_config::CosmicIdleConfig; use cosmic_settings_page::{self as page, section, Section}; use itertools::Itertools; use slab::Slab; use slotmap::SlotMap; +use std::iter; +use std::time::Duration; + +static SCREEN_OFF_TIMES: &[Duration] = &[ + Duration::from_secs(2 * 60), + Duration::from_secs(5 * 60), + Duration::from_secs(10 * 60), + Duration::from_secs(15 * 60), + Duration::from_secs(30 * 60), +]; + +static SUSPEND_TIMES: &[Duration] = &[ + Duration::from_secs(15 * 60), + Duration::from_secs(20 * 60), + Duration::from_secs(25 * 60), + Duration::from_secs(30 * 60), + Duration::from_secs(45 * 60), + Duration::from_secs(1 * 60 * 60), + Duration::from_secs(80 * 60), + Duration::from_secs(90 * 60), + Duration::from_secs(100 * 60), + Duration::from_secs(2 * 60 * 60), +]; + +fn format_time(duration: Duration) -> String { + let m = duration.as_secs() / 60; + if m % 60 == 0 { + fl!("x-hours", number = (m / 60)) + } else { + fl!("x-minutes", number = m) + } +} -#[derive(Default)] pub struct Page { entity: page::Entity, battery: Battery, connected_devices: Vec, on_enter_handle: Option, + screen_off_labels: Vec, + suspend_labels: Vec, + idle_config: Config, + idle_conf: CosmicIdleConfig, +} + +impl Default for Page { + fn default() -> Self { + let idle_config = Config::new("com.system76.CosmicIdle", 1).unwrap(); + let idle_conf = CosmicIdleConfig::get_entry(&idle_config).unwrap_or_else(|(_, conf)| conf); + + Self { + entity: Default::default(), + battery: Default::default(), + connected_devices: Vec::new(), + on_enter_handle: None, + screen_off_labels: SCREEN_OFF_TIMES + .iter() + .copied() + .map(format_time) + .chain(iter::once(fl!("never"))) + .collect(), + suspend_labels: SUSPEND_TIMES + .iter() + .copied() + .map(format_time) + .chain(iter::once(fl!("never"))) + .collect(), + idle_config, + idle_conf, + } + } } impl page::Page for Page { @@ -41,6 +106,7 @@ impl page::Page for Page { sections.insert(battery_info()), sections.insert(connected_devices()), sections.insert(profiles()), + sections.insert(power_saving()), ]) } @@ -81,6 +147,9 @@ pub enum Message { PowerProfileChange(PowerProfile), UpdateBattery(Battery), UpdateConnectedDevices(Vec), + ScreenOffTimeChange(Option), + SuspendOnAcTimeChange(Option), + SuspendOnBatteryTimeChange(Option), } impl Page { @@ -99,6 +168,30 @@ impl Page { Message::UpdateConnectedDevices(connected_devices) => { self.connected_devices = connected_devices; } + Message::ScreenOffTimeChange(time) => { + let time = time.map(|x| x.as_millis() as u32); + if let Err(err) = self.idle_conf.set_screen_off_time(&self.idle_config, time) { + tracing::error!("failed to set screen off time: {}", err) + } + } + Message::SuspendOnAcTimeChange(time) => { + let time = time.map(|x| x.as_millis() as u32); + if let Err(err) = self + .idle_conf + .set_suspend_on_ac_time(&self.idle_config, time) + { + tracing::error!("failed to set suspend on ac time: {}", err) + } + } + Message::SuspendOnBatteryTimeChange(time) => { + let time = time.map(|x| x.as_millis() as u32); + if let Err(err) = self + .idle_conf + .set_suspend_on_battery_time(&self.idle_config, time) + { + tracing::error!("failed to set suspend on battery time: {}", err) + } + } }; } } @@ -259,4 +352,77 @@ fn profiles() -> Section { }) } +fn power_saving_row<'a>( + label: &'a str, + labels: &'a [String], + selected_time: Option, + times: &'static [Duration], + on_select: fn(Option) -> Message, +) -> cosmic::Element<'a, Message> { + let selected = if let Some(time) = selected_time { + times.iter().position(|x| *x == time) + } else { + // "Never" + Some(times.len()) + }; + + settings::item( + label, + widget::dropdown(labels, selected, move |i| on_select(times.get(i).copied())), + ) + .into() +} + +fn power_saving() -> Section { + let mut descriptions = Slab::new(); + + let turn_off_screen_desc = descriptions.insert(fl!("power-saving", "turn-off-screen-after")); + let auto_suspend_ac_desc = descriptions.insert(fl!("power-saving", "auto-suspend-ac")); + let auto_suspend_battery_desc = + descriptions.insert(fl!("power-saving", "auto-suspend-battery")); + + Section::default() + .title(fl!("power-saving")) + .descriptions(descriptions) + .view::(move |_binder, page, section| { + let screen_off_time = page + .idle_conf + .screen_off_time + .map(|t| Duration::from_millis(t.into())); + let suspend_on_ac_time = page + .idle_conf + .suspend_on_ac_time + .map(|t| Duration::from_millis(t.into())); + let suspend_on_battery_time = page + .idle_conf + .suspend_on_battery_time + .map(|t| Duration::from_millis(t.into())); + settings::section() + .title(§ion.title) + .add(power_saving_row( + §ion.descriptions[turn_off_screen_desc], + &page.screen_off_labels, + screen_off_time, + SCREEN_OFF_TIMES, + Message::ScreenOffTimeChange, + )) + .add(power_saving_row( + §ion.descriptions[auto_suspend_ac_desc], + &page.suspend_labels, + suspend_on_ac_time, + SUSPEND_TIMES, + Message::SuspendOnAcTimeChange, + )) + .add(power_saving_row( + §ion.descriptions[auto_suspend_battery_desc], + &page.suspend_labels, + suspend_on_battery_time, + SUSPEND_TIMES, + Message::SuspendOnBatteryTimeChange, + )) + .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 135d787..6b3c4ce 100644 --- a/i18n/en/cosmic_settings.ftl +++ b/i18n/en/cosmic_settings.ftl @@ -159,6 +159,7 @@ x-hours = { $number -> [1] 1 hour *[other] { $number } hours } +never = Never ## Desktop: Appearance @@ -461,6 +462,11 @@ power-mode = Power Mode .performance-desc = Peak performance and power usage. .no-backend = Backend not found. Install system76-power or power-profiles-daemon. +power-saving = Power Savings Options + .turn-off-screen-after = Turn off the screen after + .auto-suspend-ac = Automatic suspend when plugged in + .auto-suspend-battery = Automatic on battery power + ## Input acceleration-desc = Automatically adjusts tracking sensitivity based on speed.