diff --git a/data/net.nokyan.Resources.gschema.xml.in b/data/net.nokyan.Resources.gschema.xml.in
index 59062a6..feb0f51 100644
--- a/data/net.nokyan.Resources.gschema.xml.in
+++ b/data/net.nokyan.Resources.gschema.xml.in
@@ -209,5 +209,21 @@
false
Whether to show niceness values for CPU priorities
+
+ false
+ Show logout button in interface
+
+
+ false
+ Show reboot button in interface
+
+
+ false
+ Show shutdown button in interface
+
+
+ false
+ Show kill window button in interface when KWin is detected
+
\ No newline at end of file
diff --git a/data/resources/ui/dialogs/settings_dialog.ui b/data/resources/ui/dialogs/settings_dialog.ui
index 5f75246..34b5dec 100644
--- a/data/resources/ui/dialogs/settings_dialog.ui
+++ b/data/resources/ui/dialogs/settings_dialog.ui
@@ -114,6 +114,36 @@
+
+
+
diff --git a/data/resources/ui/pages/applications.ui b/data/resources/ui/pages/applications.ui
index 786eebc..361b25c 100644
--- a/data/resources/ui/pages/applications.ui
+++ b/data/resources/ui/pages/applications.ui
@@ -79,28 +79,93 @@
16
true
16
- end
-
- info-symbolic
- false
- Show App Information
-
- Show App Information
-
-
+
+ 16
+ start
+
+
+ window-close-symbolic
+ Kill a Window
+ app.kill-window
+ False
+
+ Kill a Window
+
+
+
+
+
+
+ system-log-out-symbolic
+ Logout
+ app.logout
+
+ Logout
+
+
+
+
+
+
+ system-reboot-symbolic
+ Reboot
+ app.reboot
+
+ Reboot
+
+
+
+
+
+
+ system-shutdown-symbolic
+ Shutdown
+ app.shutdown
+
+ Shutdown
+
+
+
+
-
- End App
- end_app_menu
- false
-
+
+ 16
+ end
+ true
+
+
+ info-symbolic
+ false
+ Show App Information
+
+ Show App Information
+
+
+
+
+
+
+ End App
+ end_app_menu
+ false
+
+
+
diff --git a/data/resources/ui/pages/processes.ui b/data/resources/ui/pages/processes.ui
index 1a2aeb5..d0af2b3 100644
--- a/data/resources/ui/pages/processes.ui
+++ b/data/resources/ui/pages/processes.ui
@@ -125,41 +125,106 @@
16
true
16
- end
-
- options-symbolic
- false
- Show Process Options
-
- Show Process Options
-
-
-
-
-
-
- info-symbolic
- false
- Show Process Information
-
- Show Process Information
-
-
+
+ 16
+ start
+
+
+ window-close-symbolic
+ Kill a Window
+ app.kill-window
+ False
+
+ Kill a Window
+
+
+
+
+
+
+ system-log-out-symbolic
+ Logout
+ app.logout
+
+ Logout
+
+
+
+
+
+
+ system-reboot-symbolic
+ Reboot
+ app.reboot
+
+ Reboot
+
+
+
+
+
+
+ system-shutdown-symbolic
+ Shutdown
+ app.shutdown
+
+ Shutdown
+
+
+
+
-
- End Process
- end_process_menu
- false
-
+
+ 16
+ end
+ true
+
+
+ options-symbolic
+ false
+ Show Process Options
+
+ Show Process Options
+
+
+
+
+
+
+ info-symbolic
+ false
+ Show Process Information
+
+ Show Process Information
+
+
+
+
+
+
+ End Process
+ end_process_menu
+ false
+
+
+
diff --git a/src/application.rs b/src/application.rs
index af11c1f..8e1d462 100644
--- a/src/application.rs
+++ b/src/application.rs
@@ -212,6 +212,48 @@ impl Application {
}
));
self.add_action(&action_process_options);
+
+ // Kill Window if using kwin
+ let action_kill_window = gio::SimpleAction::new("kill-window", None);
+ action_kill_window.connect_activate(clone!(
+ #[weak(rename_to = this)]
+ self,
+ move |_, _| {
+ this.main_window().shortcut_kill_window();
+ }
+ ));
+ self.add_action(&action_kill_window);
+
+ // System actions with confirmation dialogs
+ let action_logout = gio::SimpleAction::new("logout", None);
+ action_logout.connect_activate(clone!(
+ #[weak(rename_to = this)]
+ self,
+ move |_, _| {
+ this.main_window().shortcut_logout();
+ }
+ ));
+ self.add_action(&action_logout);
+
+ let action_reboot = gio::SimpleAction::new("reboot", None);
+ action_reboot.connect_activate(clone!(
+ #[weak(rename_to = this)]
+ self,
+ move |_, _| {
+ this.main_window().shortcut_reboot();
+ }
+ ));
+ self.add_action(&action_reboot);
+
+ let action_shutdown = gio::SimpleAction::new("shutdown", None);
+ action_shutdown.connect_activate(clone!(
+ #[weak(rename_to = this)]
+ self,
+ move |_, _| {
+ this.main_window().shortcut_shutdown();
+ }
+ ));
+ self.add_action(&action_shutdown);
}
// Sets up keyboard shortcuts
@@ -250,7 +292,8 @@ impl Application {
let settings = ResSettingsDialog::new();
- settings.init();
+ let kwin_running = self.main_window().is_kwin_running();
+ settings.init(kwin_running);
settings.present(Some(&self.main_window()));
imp.settings_window_opened.set(true);
diff --git a/src/ui/dialogs/settings_dialog.rs b/src/ui/dialogs/settings_dialog.rs
index a6469ff..f33ffc9 100644
--- a/src/ui/dialogs/settings_dialog.rs
+++ b/src/ui/dialogs/settings_dialog.rs
@@ -38,6 +38,16 @@ mod imp {
#[template_child]
pub normalize_cpu_usage_row: TemplateChild,
+ // System action buttons
+ #[template_child]
+ pub show_kill_window_button_row: TemplateChild,
+ #[template_child]
+ pub show_logout_button_row: TemplateChild,
+ #[template_child]
+ pub show_reboot_button_row: TemplateChild,
+ #[template_child]
+ pub show_shutdown_button_row: TemplateChild,
+
#[template_child]
pub apps_show_memory_row: TemplateChild,
#[template_child]
@@ -163,12 +173,12 @@ impl ResSettingsDialog {
glib::Object::new::()
}
- pub fn init(&self) {
- self.setup_widgets();
+ pub fn init(&self, kwin_running: bool) {
+ self.setup_widgets(kwin_running);
self.setup_signals();
}
- pub fn setup_widgets(&self) {
+ pub fn setup_widgets(&self, kwin_running: bool) {
trace!("Setting up ResSettingsDialog widgets…");
let imp = self.imp();
@@ -192,6 +202,17 @@ impl ResSettingsDialog {
imp.normalize_cpu_usage_row
.set_active(SETTINGS.normalize_cpu_usage());
+ // System action buttons
+ imp.show_kill_window_button_row
+ .set_active(SETTINGS.show_kill_window_button());
+ imp.show_kill_window_button_row.set_visible(kwin_running);
+ imp.show_logout_button_row
+ .set_active(SETTINGS.show_logout_button());
+ imp.show_reboot_button_row
+ .set_active(SETTINGS.show_reboot_button());
+ imp.show_shutdown_button_row
+ .set_active(SETTINGS.show_shutdown_button());
+
imp.apps_show_memory_row
.set_active(SETTINGS.apps_show_memory());
imp.apps_show_cpu_row.set_active(SETTINGS.apps_show_cpu());
@@ -321,6 +342,27 @@ impl ResSettingsDialog {
let _ = SETTINGS.set_normalize_cpu_usage(switch_row.is_active());
});
+ // System action buttons
+ imp.show_kill_window_button_row
+ .connect_active_notify(|switch_row| {
+ let _ = SETTINGS.set_show_kill_window_button(switch_row.is_active());
+ });
+
+ imp.show_logout_button_row
+ .connect_active_notify(|switch_row| {
+ let _ = SETTINGS.set_show_logout_button(switch_row.is_active());
+ });
+
+ imp.show_reboot_button_row
+ .connect_active_notify(|switch_row| {
+ let _ = SETTINGS.set_show_reboot_button(switch_row.is_active());
+ });
+
+ imp.show_shutdown_button_row
+ .connect_active_notify(|switch_row| {
+ let _ = SETTINGS.set_show_shutdown_button(switch_row.is_active());
+ });
+
imp.apps_show_cpu_row.connect_active_notify(|switch_row| {
let _ = SETTINGS.set_apps_show_cpu(switch_row.is_active());
});
diff --git a/src/ui/pages/applications/mod.rs b/src/ui/pages/applications/mod.rs
index 9045be1..b8b4731 100644
--- a/src/ui/pages/applications/mod.rs
+++ b/src/ui/pages/applications/mod.rs
@@ -58,6 +58,14 @@ mod imp {
#[template_child]
pub applications_scrolled_window: TemplateChild,
#[template_child]
+ pub kill_window_button: TemplateChild,
+ #[template_child]
+ pub logout_button: TemplateChild,
+ #[template_child]
+ pub reboot_button: TemplateChild,
+ #[template_child]
+ pub shutdown_button: TemplateChild,
+ #[template_child]
pub information_button: TemplateChild,
#[template_child]
pub end_application_button: TemplateChild,
@@ -125,6 +133,10 @@ mod imp {
info_dialog_closed: Default::default(),
sender: Default::default(),
applications_scrolled_window: Default::default(),
+ kill_window_button: Default::default(),
+ logout_button: Default::default(),
+ reboot_button: Default::default(),
+ shutdown_button: Default::default(),
end_application_button: Default::default(),
uses_progress_bar: Cell::new(false),
icon: RefCell::new(ThemedIcon::new("app-symbolic").into()),
@@ -580,9 +592,17 @@ impl ResApplications {
.and_then(|object| object.downcast::().ok())
}
- pub fn refresh_apps_list(&self, apps_context: &AppsContext) {
+ pub fn refresh_apps_list(&self, apps_context: &AppsContext, kwin_running: bool) {
let imp = self.imp();
+ // Update button visibility based on settings
+ imp.kill_window_button
+ .set_visible(kwin_running && SETTINGS.show_kill_window_button());
+ imp.logout_button.set_visible(SETTINGS.show_logout_button());
+ imp.reboot_button.set_visible(SETTINGS.show_reboot_button());
+ imp.shutdown_button
+ .set_visible(SETTINGS.show_shutdown_button());
+
if imp.info_dialog_closed.get() {
let _ = imp.open_info_dialog.take();
imp.info_dialog_closed.set(false);
@@ -1539,6 +1559,10 @@ fn get_action_name(action: ProcessAction, name: &str) -> String {
ProcessAction::STOP => i18n_f("Halt {}?", &[name]),
ProcessAction::KILL => i18n_f("Kill {}?", &[name]),
ProcessAction::CONT => i18n_f("Continue {}?", &[name]),
+ ProcessAction::KILLWINDOW => i18n("Kill a Window?"),
+ ProcessAction::LOGOUT => i18n("Logout?"),
+ ProcessAction::REBOOT => i18n("Reboot?"),
+ ProcessAction::SHUTDOWN => i18n("Shutdown?"),
}
}
@@ -1552,6 +1576,16 @@ fn get_action_warning(action: ProcessAction) -> String {
"Killing an app can come with serious risks such as losing data and security implications. Use with caution.",
),
ProcessAction::CONT => String::new(),
+ ProcessAction::KILLWINDOW => i18n("Click on a window to kill it."),
+ ProcessAction::LOGOUT => {
+ i18n("This action will be executed without checking for unsaved files.")
+ }
+ ProcessAction::REBOOT => {
+ i18n("This action will be executed without checking for unsaved files.")
+ }
+ ProcessAction::SHUTDOWN => {
+ i18n("This action will be executed without checking for unsaved files.")
+ }
}
}
@@ -1561,5 +1595,9 @@ fn get_action_description(action: ProcessAction) -> String {
ProcessAction::STOP => i18n("Halt App"),
ProcessAction::KILL => i18n("Kill App"),
ProcessAction::CONT => i18n("Continue App"),
+ ProcessAction::KILLWINDOW => i18n("Kill Window"),
+ ProcessAction::LOGOUT => i18n("Logout"),
+ ProcessAction::REBOOT => i18n("Reboot"),
+ ProcessAction::SHUTDOWN => i18n("Shutdown"),
}
}
diff --git a/src/ui/pages/processes/mod.rs b/src/ui/pages/processes/mod.rs
index 13b9683..b969ea5 100644
--- a/src/ui/pages/processes/mod.rs
+++ b/src/ui/pages/processes/mod.rs
@@ -87,6 +87,14 @@ mod imp {
#[template_child]
pub processes_scrolled_window: TemplateChild,
#[template_child]
+ pub kill_window_button: TemplateChild,
+ #[template_child]
+ pub logout_button: TemplateChild,
+ #[template_child]
+ pub reboot_button: TemplateChild,
+ #[template_child]
+ pub shutdown_button: TemplateChild,
+ #[template_child]
pub options_button: TemplateChild,
#[template_child]
pub information_button: TemplateChild,
@@ -155,6 +163,10 @@ mod imp {
search_bar: Default::default(),
search_entry: Default::default(),
processes_scrolled_window: Default::default(),
+ kill_window_button: Default::default(),
+ logout_button: Default::default(),
+ reboot_button: Default::default(),
+ shutdown_button: Default::default(),
options_button: Default::default(),
information_button: Default::default(),
end_process_button: Default::default(),
@@ -734,9 +746,17 @@ impl ResProcesses {
}
}
- pub fn refresh_processes_list(&self, apps_context: &AppsContext) {
+ pub fn refresh_processes_list(&self, apps_context: &AppsContext, kwin_running: bool) {
let imp = self.imp();
+ // Update button visibility based on settings
+ imp.kill_window_button
+ .set_visible(kwin_running && SETTINGS.show_kill_window_button());
+ imp.logout_button.set_visible(SETTINGS.show_logout_button());
+ imp.reboot_button.set_visible(SETTINGS.show_reboot_button());
+ imp.shutdown_button
+ .set_visible(SETTINGS.show_shutdown_button());
+
if imp.info_dialog_closed.get() {
let _ = imp.open_info_dialog.take();
imp.info_dialog_closed.set(false);
@@ -2073,6 +2093,10 @@ fn get_action_name(action: ProcessAction, name: &str) -> String {
ProcessAction::STOP => i18n_f("Halt {}?", &[name]),
ProcessAction::KILL => i18n_f("Kill {}?", &[name]),
ProcessAction::CONT => i18n_f("Continue {}?", &[name]),
+ ProcessAction::KILLWINDOW => i18n("Kill a Window?"),
+ ProcessAction::LOGOUT => i18n("Logout?"),
+ ProcessAction::REBOOT => i18n("Reboot?"),
+ ProcessAction::SHUTDOWN => i18n("Shutdown?"),
}
}
@@ -2102,6 +2126,10 @@ fn get_action_name_multiple(action: ProcessAction, count: usize) -> String {
count as u32,
&[&count.to_string()],
),
+ ProcessAction::KILLWINDOW => i18n("Kill a Window?"),
+ ProcessAction::LOGOUT => i18n("Logout?"),
+ ProcessAction::REBOOT => i18n("Reboot?"),
+ ProcessAction::SHUTDOWN => i18n("Shutdown?"),
}
}
@@ -2115,6 +2143,16 @@ fn get_action_warning(action: ProcessAction) -> String {
"Killing a process can come with serious risks such as losing data and security implications. Use with caution.",
),
ProcessAction::CONT => String::new(),
+ ProcessAction::KILLWINDOW => i18n("Click on a window to kill it."),
+ ProcessAction::LOGOUT => {
+ i18n("This action will be executed without checking for unsaved files.")
+ }
+ ProcessAction::REBOOT => {
+ i18n("This action will be executed without checking for unsaved files.")
+ }
+ ProcessAction::SHUTDOWN => {
+ i18n("This action will be executed without checking for unsaved files.")
+ }
}
}
@@ -2124,5 +2162,9 @@ fn get_action_description(action: ProcessAction) -> String {
ProcessAction::STOP => i18n("Halt Process"),
ProcessAction::KILL => i18n("Kill Process"),
ProcessAction::CONT => i18n("Continue Process"),
+ ProcessAction::KILLWINDOW => i18n("Kill Window"),
+ ProcessAction::LOGOUT => i18n("Logout"),
+ ProcessAction::REBOOT => i18n("Reboot"),
+ ProcessAction::SHUTDOWN => i18n("Shutdown"),
}
}
diff --git a/src/ui/window.rs b/src/ui/window.rs
index c7e627e..b9bfdc0 100644
--- a/src/ui/window.rs
+++ b/src/ui/window.rs
@@ -7,7 +7,7 @@ use adw::{ToolbarView, prelude::*, subclass::prelude::*};
use anyhow::{Context, Result};
use gtk::glib::{GString, MainContext, clone, timeout_future};
use gtk::{Widget, gdk, gio, glib};
-use log::{debug, info, trace, warn};
+use log::{debug, error, info, trace, warn};
use crate::application::Application;
use crate::config::PROFILE;
@@ -455,6 +455,77 @@ impl MainWindow {
}
}
+ pub fn shortcut_kill_window(&self) {
+ // Simply execute the kill window command directly
+ match Process::execute_kill_window() {
+ Ok(()) => info!("Kill Window mode activated"),
+ Err(e) => error!("Failed to activate Kill Window mode: {}", e),
+ }
+ }
+
+ fn show_system_action_confirmation_dialog(&self, action_name: &str, command_description: &str) {
+ let dialog = adw::AlertDialog::builder()
+ .heading(format!("Confirm {}", action_name))
+ .body(format!(
+ "Are you sure you want to {}?\n\nThis action will be executed without checking for unsaved files.",
+ command_description
+ ))
+ .build();
+
+ dialog.add_response("cancel", "Cancel");
+ dialog.add_response("confirm", action_name);
+ dialog.set_response_appearance("confirm", adw::ResponseAppearance::Destructive);
+
+ let action_name_owned = action_name.to_string();
+ dialog.connect_response(None, move |_, response| {
+ if response == "confirm" {
+ match action_name_owned.as_str() {
+ "Logout" => {
+ if let Err(e) = Process::execute_logout() {
+ error!("Failed to initiate logout: {}", e);
+ } else {
+ info!("Logout initiated");
+ }
+ }
+ "Reboot" => {
+ if let Err(e) = Process::execute_reboot() {
+ error!("Failed to initiate reboot: {}", e);
+ } else {
+ info!("Reboot initiated");
+ }
+ }
+ "Shutdown" => {
+ if let Err(e) = Process::execute_shutdown() {
+ error!("Failed to initiate shutdown: {}", e);
+ } else {
+ info!("Shutdown initiated");
+ }
+ }
+ _ => {}
+ }
+ }
+ });
+
+ dialog.present(Some(self));
+ }
+
+ pub fn shortcut_logout(&self) {
+ self.show_system_action_confirmation_dialog("Logout", "log out");
+ }
+
+ pub fn shortcut_reboot(&self) {
+ self.show_system_action_confirmation_dialog("Reboot", "reboot the system");
+ }
+
+ pub fn shortcut_shutdown(&self) {
+ self.show_system_action_confirmation_dialog("Shutdown", "shut down the system");
+ }
+
+ pub fn is_kwin_running(&self) -> bool {
+ let imp = self.imp();
+ imp.apps_context.borrow().is_kwin_running()
+ }
+
fn init_gpu_pages(self: &MainWindow, gpus: &[Gpu]) {
let imp = self.imp();
@@ -637,11 +708,15 @@ impl MainWindow {
let mut apps_context = imp.apps_context.borrow_mut();
apps_context.refresh(process_data);
+ // Check if kwin is running for the kill window button
+ let kwin_running = apps_context.is_kwin_running();
+
// if CTRL is held, don't update apps and processes like Windows Task Manager
if !imp.pause_updates.get() {
trace!("Skipping visual apps and processes updates");
- imp.apps.refresh_apps_list(&apps_context);
- imp.processes.refresh_processes_list(&apps_context);
+ imp.apps.refresh_apps_list(&apps_context, kwin_running);
+ imp.processes
+ .refresh_processes_list(&apps_context, kwin_running);
}
/*
@@ -1250,6 +1325,10 @@ fn get_action_success(action: ProcessAction, name: &str) -> String {
ProcessAction::STOP => i18n_f("Successfully halted {}", &[name]),
ProcessAction::KILL => i18n_f("Successfully killed {}", &[name]),
ProcessAction::CONT => i18n_f("Successfully continued {}", &[name]),
+ ProcessAction::KILLWINDOW => i18n("Kill Window mode activated"),
+ ProcessAction::LOGOUT => i18n("Logout initiated"),
+ ProcessAction::REBOOT => i18n("Reboot initiated"),
+ ProcessAction::SHUTDOWN => i18n("Shutdown initiated"),
}
}
@@ -1279,6 +1358,10 @@ fn get_processes_success(action: ProcessAction, count: usize) -> String {
count as u32,
&[&count.to_string()],
),
+ ProcessAction::KILLWINDOW => i18n("Kill Window mode activated"),
+ ProcessAction::LOGOUT => i18n("Logout initiated"),
+ ProcessAction::REBOOT => i18n("Reboot initiated"),
+ ProcessAction::SHUTDOWN => i18n("Shutdown initiated"),
}
}
@@ -1308,6 +1391,10 @@ fn get_action_failure(action: ProcessAction, count: usize) -> String {
count as u32,
&[&count.to_string()],
),
+ ProcessAction::KILLWINDOW => i18n("Failed to activate Kill Window mode"),
+ ProcessAction::LOGOUT => i18n("Failed to initiate logout"),
+ ProcessAction::REBOOT => i18n("Failed to initiate reboot"),
+ ProcessAction::SHUTDOWN => i18n("Failed to initiate shutdown"),
}
}
@@ -1317,5 +1404,9 @@ pub fn get_named_action_failure(action: ProcessAction, name: &str) -> String {
ProcessAction::STOP => i18n_f("There was a problem halting {}", &[name]),
ProcessAction::KILL => i18n_f("There was a problem killing {}", &[name]),
ProcessAction::CONT => i18n_f("There was a problem continuing {}", &[name]),
+ ProcessAction::KILLWINDOW => i18n("Failed to activate Kill Window mode"),
+ ProcessAction::LOGOUT => i18n("Failed to initiate logout"),
+ ProcessAction::REBOOT => i18n("Failed to initiate reboot"),
+ ProcessAction::SHUTDOWN => i18n("Failed to initiate shutdown"),
}
}
diff --git a/src/utils/app.rs b/src/utils/app.rs
index a987364..dbdcb0d 100644
--- a/src/utils/app.rs
+++ b/src/utils/app.rs
@@ -762,6 +762,13 @@ impl AppsContext {
})
}
+ /// Check if kwin_wayland or kwin_x11 is running
+ pub fn is_kwin_running(&self) -> bool {
+ self.processes_iter().any(|process| {
+ process.executable_name == "kwin_wayland" || process.executable_name == "kwin_x11"
+ })
+ }
+
/// Refreshes the statistics about the running applications and processes.
pub fn refresh(&mut self, new_process_data: Vec) {
trace!("Refreshing AppsContext…");
diff --git a/src/utils/process.rs b/src/utils/process.rs
index 690013d..ed18a3f 100644
--- a/src/utils/process.rs
+++ b/src/utils/process.rs
@@ -13,8 +13,8 @@ use std::{
use strum_macros::Display;
use gtk::{
- gio::{Icon, ThemedIcon},
- glib::GString,
+ gio::{DBusCallFlags, DBusProxy, DBusProxyFlags, Icon, ThemedIcon, prelude::DBusProxyExt},
+ glib::{GString, Variant},
};
use crate::config;
@@ -80,6 +80,10 @@ pub enum ProcessAction {
STOP,
KILL,
CONT,
+ KILLWINDOW,
+ LOGOUT,
+ REBOOT,
+ SHUTDOWN,
}
impl Process {
@@ -339,6 +343,114 @@ impl Process {
}
}
+ pub fn execute_kill_window() -> Result<()> {
+ debug!("Executing Kill Window command via D-Bus");
+
+ // Create a D-Bus proxy for the KDE global accelerator service
+ let proxy = DBusProxy::for_bus_sync(
+ gtk::gio::BusType::Session,
+ DBusProxyFlags::NONE,
+ None, // info
+ "org.kde.kglobalaccel",
+ "/component/kwin",
+ "org.kde.kglobalaccel.Component",
+ None::<>k::gio::Cancellable>,
+ )
+ .context("Failed to create D-Bus proxy")?;
+
+ // Create the method call parameters
+ let shortcut_name = Variant::from("Kill Window");
+ let parameters = Variant::tuple_from_iter([shortcut_name]);
+
+ // Call the D-Bus method
+ let result = proxy.call_sync(
+ "invokeShortcut",
+ Some(¶meters),
+ DBusCallFlags::NONE,
+ -1,
+ None::<>k::gio::Cancellable>,
+ );
+
+ match result {
+ Ok(_) => {
+ info!("Successfully invoked Kill Window");
+ Ok(())
+ }
+ Err(err) => {
+ error!("Kill Window D-Bus call failed: {}", err);
+ bail!("Kill Window D-Bus call failed: {}", err)
+ }
+ }
+ }
+
+ pub fn execute_logout() -> Result<()> {
+ debug!("Executing logout command");
+ let result = Command::new("pkill")
+ .args([
+ "-u",
+ &std::env::var("USER").unwrap_or_else(|_| "user".to_string()),
+ ])
+ .status();
+
+ match result {
+ Ok(status) if status.success() => {
+ info!("Successfully initiated logout");
+ Ok(())
+ }
+ Ok(status) => {
+ error!("Logout command failed with exit code: {:?}", status.code());
+ bail!("Logout command failed")
+ }
+ Err(err) => {
+ error!("Failed to execute logout command: {}", err);
+ bail!("Failed to execute logout command: {}", err)
+ }
+ }
+ }
+
+ pub fn execute_reboot() -> Result<()> {
+ debug!("Executing reboot command");
+ let result = Command::new("reboot").status();
+
+ match result {
+ Ok(status) if status.success() => {
+ info!("Successfully initiated reboot");
+ Ok(())
+ }
+ Ok(status) => {
+ error!("Reboot command failed with exit code: {:?}", status.code());
+ bail!("Reboot command failed")
+ }
+ Err(err) => {
+ error!("Failed to execute reboot command: {}", err);
+ bail!("Failed to execute reboot command: {}", err)
+ }
+ }
+ }
+
+ pub fn execute_shutdown() -> Result<()> {
+ debug!("Executing shutdown command");
+ let result = Command::new("poweroff").status();
+
+ match result {
+ Ok(status) if status.success() => {
+ info!("Successfully initiated shutdown");
+ Ok(())
+ }
+ Ok(status) => {
+ error!(
+ "Shutdown command failed with exit code: {:?}",
+ status.code()
+ );
+ bail!("Shutdown command failed")
+ }
+ Err(err) => {
+ error!("Failed to execute shutdown command: {}", err);
+ bail!("Failed to execute shutdown command: {}", err)
+ }
+ }
+ }
+
#[must_use]
pub fn cpu_time_ratio(&self) -> f32 {
if self.cpu_time_last == 0 {
diff --git a/src/utils/settings.rs b/src/utils/settings.rs
index 0f16b07..2eaf0fc 100644
--- a/src/utils/settings.rs
+++ b/src/utils/settings.rs
@@ -380,7 +380,11 @@ impl Settings {
show_logical_cpus,
show_graph_grids,
normalize_cpu_usage,
- detailed_priority
+ detailed_priority,
+ show_logout_button,
+ show_reboot_button,
+ show_shutdown_button,
+ show_kill_window_button
);
}