Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cosmic-settings/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ page-networking = [
"dep:nm-secret-agent-manager",
"dep:zbus",
]
page-power = ["dep:upower_dbus", "dep:zbus"]
page-power = ["dep:cosmic-settings-config", "dep:upower_dbus", "dep:zbus"]
page-region = [
"gettext",
"dep:locales-rs",
Expand Down
90 changes: 90 additions & 0 deletions cosmic-settings/src/pages/power/backend/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
use std::collections::BTreeMap;

use cosmic_config::{Config, ConfigGet};
use cosmic_settings_config::shortcuts::action::System;
use futures::future::join_all;
use futures::{FutureExt, Stream, StreamExt};
use jiff::{Span, SpanRelativeTo, SpanRound, ToSpan, Unit};
Expand Down Expand Up @@ -97,6 +101,92 @@ pub fn get_power_profiles() -> Vec<PowerProfile> {
]
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PowerButtonBehavior {
ConfirmShutdown,
ConfirmLogOut,
ConfirmRestart,
Shutdown,
LogOut,
Restart,
Suspend,
Hibernate,
}

impl PowerButtonBehavior {
pub fn from_config() -> Option<Self> {
match Config::new("com.system76.CosmicSettings.Shortcuts", 1)
.and_then(|cfg| cfg.get::<BTreeMap<System, String>>("system_actions")) {
Ok(system_actions) => match system_actions.get(&System::PowerOff) {
None => Some(PowerButtonBehavior::ConfirmShutdown),
Some(cmd) if cmd == "cosmic-osd shutdown" => Some(Self::ConfirmShutdown),
Some(cmd) if cmd == "cosmic-osd log-out" => Some(Self::ConfirmLogOut),
Some(cmd) if cmd == "cosmic-osd restart" => Some(Self::ConfirmRestart),
Some(cmd) if cmd == "systemctl poweroff" => Some(Self::Shutdown),
Some(cmd) if cmd == "loginctl terminate-user $USER" => Some(Self::LogOut),
Some(cmd) if cmd == "systemctl reboot" => Some(Self::Restart),
Some(cmd) if cmd == "systemctl suspend" => Some(Self::Suspend),
Some(cmd) if cmd == "systemctl hibernate" => Some(Self::Hibernate),
// Only return None if the user has customized the command
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The power button behavior is managed by logind on systemd systems so it should obtain the value from this. In /etc/systemd/logind.conf, there is a HandlePowerKey option which defaults to poweroff. But you should get the value of this key directly from the DBus service with https://docs.rs/logind-zbus/latest/logind_zbus/manager/struct.ManagerProxy.html#method.handle_power_key.

Copy link
Copy Markdown
Author

@gsboylan gsboylan May 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I was first looking into this, it seemed that cosmic-session inhibits logind from receiving the button press event so it can handle it internally. For my understanding, are you proposing that I retain the logind inhibitor in cosmic-session, but respect/update the logind configuration anyway?

If the goal is to respect logind.conf, I'm not entirely certain why we'd still want to take the inhibitor lock, and vice-versa if the goal is to handle the button event internally for compat with non-systemd systems then this would leave us with a behavior gap to reconcile. Alternately, maybe there needs to be a specific config option to not take the inhibitor and let the system handle the button press however it's configured.

Happy to make the tweak either way, just want to make sure I understand the desired functionality here. My initial approach was just to edit the existing cosmic shortcut configuration to keep things tidy.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just so we're on the same page, here's the source for the systemd/logind inhibitor that I'm referring to: https://github.com/pop-os/cosmic-session/blob/master/src/main.rs#L194-L199

_ => None,
},
Err(_) => Some(Self::ConfirmShutdown),
}
}

pub fn title(&self) -> String {
match self {
Self::ConfirmShutdown => fl!("power-button-behavior", "confirm-shutdown"),
Self::ConfirmLogOut => fl!("power-button-behavior", "confirm-log-out"),
Self::ConfirmRestart => fl!("power-button-behavior", "confirm-restart"),
Self::Shutdown => fl!("power-button-behavior", "shutdown"),
Self::LogOut => fl!("power-button-behavior", "log-out"),
Self::Restart => fl!("power-button-behavior", "restart"),
Self::Suspend => fl!("power-button-behavior", "suspend"),
Self::Hibernate => fl!("power-button-behavior", "hibernate"),
}
}

pub fn description(&self) -> String {
match self {
Self::ConfirmShutdown => fl!("power-button-behavior", "confirm-shutdown-desc"),
Self::ConfirmLogOut => fl!("power-button-behavior", "confirm-log-out-desc"),
Self::ConfirmRestart => fl!("power-button-behavior", "confirm-restart-desc"),
Self::Shutdown => fl!("power-button-behavior", "shutdown-desc"),
Self::LogOut => fl!("power-button-behavior", "log-out-desc"),
Self::Restart => fl!("power-button-behavior", "restart-desc"),
Self::Suspend => fl!("power-button-behavior", "suspend-desc"),
Self::Hibernate => fl!("power-button-behavior", "hibernate-desc"),
}
}

pub fn command(&self) -> String {
match self {
Self::ConfirmShutdown => "cosmic-osd shutdown".to_string(),
Self::ConfirmLogOut => "cosmic-osd log-out".to_string(),
Self::ConfirmRestart => "cosmic-osd restart".to_string(),
Self::Shutdown => "systemctl poweroff".to_string(),
Self::LogOut => "loginctl terminate-user $USER".to_string(),
Self::Restart => "systemctl reboot".to_string(),
Self::Suspend => "systemctl suspend".to_string(),
Self::Hibernate => "systemctl hibernate".to_string(),
}
}
}

pub fn get_power_button_behaviors() -> Vec<PowerButtonBehavior> {
vec![
PowerButtonBehavior::ConfirmShutdown,
PowerButtonBehavior::ConfirmLogOut,
PowerButtonBehavior::ConfirmRestart,
PowerButtonBehavior::Shutdown,
PowerButtonBehavior::LogOut,
PowerButtonBehavior::Restart,
PowerButtonBehavior::Suspend,
PowerButtonBehavior::Hibernate,
]
}

#[derive(Debug, Clone)]
pub struct S76Backend {}

Expand Down
54 changes: 53 additions & 1 deletion cosmic-settings/src/pages/power/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use cosmic_config::ConfigGet;
mod backend;

use crate::pages::power::backend::PowerButtonBehavior;
use std::collections::BTreeMap;

use self::backend::{GetCurrentPowerProfile, SetPowerProfile};
use backend::{Battery, ConnectedDevice, PowerProfile};

Expand All @@ -8,7 +12,7 @@ use cosmic::iced::widget::{column, row};
use cosmic::iced::{self, Alignment, Length, stream};
use cosmic::widget::{self, settings, space, text};
use cosmic::{Apply, Task, surface};
use cosmic_config::{Config, CosmicConfigEntry};
use cosmic_config::{Config, ConfigSet, CosmicConfigEntry};
use cosmic_idle_config::CosmicIdleConfig;
use cosmic_settings_page::{self as page, Section, section};
use futures::{SinkExt, StreamExt};
Expand All @@ -18,6 +22,7 @@ use slotmap::SlotMap;
use std::hash::Hash;
use std::iter;
use std::time::Duration;
use cosmic_settings_config::shortcuts::action::System;
use upower_dbus::DeviceProxy;

static SCREEN_OFF_TIMES: &[Duration] = &[
Expand Down Expand Up @@ -61,12 +66,14 @@ pub struct Page {
idle_conf: CosmicIdleConfig,
backend: Option<backend::PowerBackendEnum>,
current_power_profile: Option<PowerProfile>,
power_button_behavior: Option<PowerButtonBehavior>,
}

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);
let power_button_behavior = PowerButtonBehavior::from_config();

Self {
entity: Default::default(),
Expand All @@ -89,6 +96,7 @@ impl Default for Page {
idle_conf,
backend: None,
current_power_profile: None,
power_button_behavior,
}
}
}
Expand All @@ -113,6 +121,7 @@ impl page::Page<crate::pages::Message> for Page {
sections.insert(connected_devices()),
sections.insert(profiles()),
sections.insert(power_saving()),
sections.insert(power_button())
])
}

Expand Down Expand Up @@ -300,6 +309,7 @@ impl page::Page<crate::pages::Message> for Page {
#[derive(Clone, Debug)]
pub enum Message {
PowerProfileChange(PowerProfile),
PowerButtonBehaviorChange(PowerButtonBehavior),
/// Update the system battery
UpdateBattery(Battery),
/// Update the battery of a connected device
Expand Down Expand Up @@ -340,6 +350,21 @@ impl Page {
});
}
}
Message::PowerButtonBehaviorChange(behavior) => {
match Config::new("com.system76.CosmicSettings.Shortcuts", 1) {
Ok(config) => {
let mut system_actions: BTreeMap<System, String> = config
.get("system_actions")
.unwrap_or_default();
system_actions.insert(System::PowerOff, behavior.command());
if let Err(err) = config.set("system_actions", system_actions) {
tracing::error!("Failed to set power button action: {}", err);
}
}
Err(err) => tracing::error!("Failed to open shortcuts config: {}", err),
}
self.power_button_behavior = Some(behavior);
}
Message::UpdateBattery(battery) => self.battery = battery,
Message::UpdateDeviceBattery(path, battery) => {
for device in &mut self.connected_devices {
Expand Down Expand Up @@ -555,6 +580,33 @@ fn profiles() -> Section<crate::pages::Message> {
})
}

fn power_button() -> Section<crate::pages::Message> {
crate::slab!(descriptions {
_power_button_desc = fl!("power-button-behavior", "desc");
});
Section::default()
.title(fl!("power-button-behavior"))
.descriptions(descriptions)
.view::<Page>(move |_binder, page, section| {
let mut section = settings::section().title(&section.title);

if page.backend.is_some() {
let behaviors = backend::get_power_button_behaviors();
section = behaviors
.into_iter()
.map(|behavior| {
settings::item::builder(behavior.title())
.description(behavior.description())
.radio(behavior, page.power_button_behavior, Message::PowerButtonBehaviorChange)
}).fold(section, settings::Section::add);
}

section
.apply(cosmic::Element::from)
.map(crate::pages::Message::Power)
})
}

fn power_saving_row<'a>(
label: &'a str,
labels: &'a [String],
Expand Down
20 changes: 20 additions & 0 deletions i18n/en/cosmic_settings.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,26 @@ power-saving = Power saving options
.auto-suspend-ac = Automatic suspend when plugged in
.auto-suspend-battery = Automatic suspend on battery power

power-button-behavior = Power button
.desc = Sets how the power button behaves when pressed
.confirm-shutdown = Confirm Shutdown
.confirm-shutdown-desc = Shut the system down after the timer ends (default)
.confirm-log-out = Confirm Log Out
.confirm-log-out-desc = Log out of the system after the timer ends
.confirm-restart = Confirm Restart
.confirm-restart-desc = Restart the system after the timer ends

.shutdown = Shutdown
.shutdown-desc = Shut the system down immediately
.log-out = Log Out
.log-out-desc = Log out of the system immediately
.restart = Restart
.restart-desc = Restart the system immediately
.suspend = Suspend
.suspend-desc = Suspend the system immediately
.hibernate = Hibernate
.hibernate-desc = Hibernate the system immediately

## Input

acceleration-desc = Automatically adjusts tracking sensitivity based on speed
Expand Down