Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

customize power controls in config #185

Merged
Merged
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
63 changes: 41 additions & 22 deletions extra/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -95,39 +95,58 @@ show_border = true
border_color = "white"

[power_controls]
# Allow for the shutdown option to be used
allow_shutdown = true
# The text in the top-left to display how to shutdown. The text '%key%' will be
# replaced with the shutdown_key.
shutdown_hint = "Shutdown %key%"
# The margin between hints
hint_margin = 2

# There are no additional entries by default
entries = []

# Example
# Reboot to another os option
#[[power_controls.entries]]
## The text in the top-left to display how to reboot.
#hint = "Reboot to OS"
#
## The color and modifiers of the hint in the top-left corner
#hint_color = "dark gray"
#hint_modifiers = ""
#
## The key used to reboot. Possibilities are F1 to F12.
#key = "F3"
## The command that is executed when the key is pressed
#cmd = "efibootmgr -n0 && systemctl reboot -l"


# If you want to remove the base_entries
# base_entries = []

# Shutdown option
[[power_controls.base_entries]]
# The text in the top-left to display how to shutdown.
hint = "Shutdown"

# The color and modifiers of the hint in the top-left corner
shutdown_hint_color = "dark gray"
shutdown_hint_modifiers = ""
hint_color = "dark gray"
hint_modifiers = ""

# The key used to shutdown. Possibilities are F1 to F12.
shutdown_key = "F1"
key = "F1"
# The command that is executed when the key is pressed
shutdown_cmd = "systemctl poweroff -l"

# Allow for the reboot option to be used
allow_reboot = true
cmd = "systemctl poweroff -l"

# The text in the top-left to display how to reboot. The text '%key%' will be
# replaced with the shutdown_key.
reboot_hint = "Reboot %key%"
# Reboot option
[[power_controls.base_entries]]
# The text in the top-left to display how to reboot.
hint = "Reboot"

# The color and modifiers of the hint in the top-left corner
reboot_hint_color = "dark gray"
reboot_hint_modifiers = ""
hint_color = "dark gray"
hint_modifiers = ""

# The key used to reboot. Possibilities are F1 to F12.
reboot_key = "F2"
key = "F2"
# The command that is executed when the key is pressed
reboot_cmd = "systemctl reboot -l"

# The margin between the shutdown and reboot hints
hint_margin = 2
cmd = "systemctl reboot -l"

# Setting for the selector of the desktop environment you are using.
[environment_switcher]
Expand Down
84 changes: 67 additions & 17 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,21 +246,42 @@ toml_config_struct! { BackgroundConfig, PartialBackgroundConfig, RoughBackground
}

toml_config_struct! { PowerControlConfig, PartialPowerControlConfig, RoughPowerControlConfig,
allow_shutdown => bool,
shutdown_hint => String,
shutdown_hint_color => String,
shutdown_hint_modifiers => String,
shutdown_key => String,
shutdown_cmd => String,

allow_reboot => bool,
reboot_hint => String,
reboot_hint_color => String,
reboot_hint_modifiers => String,
reboot_key => String,
reboot_cmd => String,

hint_margin => u16,
base_entries => PowerControlVec [PartialPowerControlVec, RoughPowerControlVec],
entries => PowerControlVec [PartialPowerControlVec, RoughPowerControlVec],
}

#[derive(Clone, Debug, Deserialize)]
#[serde(transparent)]
#[repr(transparent)]
pub struct PowerControlVec(pub Vec<PowerControl>);
#[derive(Clone, Deserialize)]
#[serde(transparent)]
#[repr(transparent)]
pub struct PartialPowerControlVec(pub Vec<PartialPowerControl>);
#[derive(Clone, Debug, Deserialize)]
#[serde(transparent)]
#[repr(transparent)]
struct RoughPowerControlVec(pub Vec<RoughPowerControl>);

toml_config_struct! { PowerControl, PartialPowerControl, RoughPowerControl,
hint => String,
hint_color => String,
hint_modifiers => String,
key => String,
cmd => String,
}

impl Default for PowerControl {
fn default() -> Self {
PowerControl {
hint: "".to_string(),
hint_color: "dark gray".to_string(),
hint_modifiers: "".to_string(),
key: "".to_string(),
cmd: "true".to_string(),
}
}
}

toml_config_struct! { SwitcherConfig, PartialSwitcherConfig, RoughSwitcherConfig,
Expand Down Expand Up @@ -397,8 +418,8 @@ impl<'de> Deserialize<'de> for SwitcherVisibility {

impl Default for Config {
fn default() -> Config {
toml::from_str(include_str!("../extra/config.toml")).unwrap_or_else(|_| {
eprintln!("Default configuration file cannot be properly parsed");
toml::from_str(include_str!("../extra/config.toml")).unwrap_or_else(|e| {
eprintln!("Default configuration file cannot be properly parsed: {e}");
process::exit(1);
})
}
Expand Down Expand Up @@ -517,6 +538,35 @@ impl Display for VariableInsertionError {
}
}

impl PowerControlVec {
pub fn merge_in_partial(&mut self, partial: PartialPowerControlVec) {
*self = PowerControlVec(
partial
.0
.into_iter()
.map(|partial_elem| {
let mut elem = PowerControl::default();
elem.merge_in_partial(partial_elem);
elem
})
.collect::<Vec<PowerControl>>(),
);
}
}

impl RoughPowerControlVec {
pub fn into_partial(
self,
variables: &Variables,
) -> Result<PartialPowerControlVec, VariableInsertionError> {
self.0
.into_iter()
.map(|rough_elem| rough_elem.into_partial(variables))
.collect::<Result<Vec<PartialPowerControl>, VariableInsertionError>>()
.map(PartialPowerControlVec)
}
}

impl std::error::Error for VariableInsertionError {}

macro_rules! non_string_var_insert {
Expand Down Expand Up @@ -645,7 +695,7 @@ struct Variable<'a> {
}

impl<'a> Variable<'a> {
const START_SYMBOL: &str = "$";
const START_SYMBOL: &'static str = "$";

fn span(&self) -> std::ops::Range<usize> {
self.start..self.start + Self::START_SYMBOL.len() + self.ident.len()
Expand Down
140 changes: 60 additions & 80 deletions src/ui/key_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ use std::process::{Command, Output};

use crossterm::event::KeyCode;
use ratatui::layout::{Alignment, Rect};
use ratatui::style::Style;
use ratatui::style::{Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::Paragraph;
use ratatui::Frame;

use crate::config::{
get_color, get_key, get_modifiers, PowerControlConfig, SwitcherConfig, SwitcherVisibility,
get_color, get_key, get_modifiers, PowerControl, PowerControlConfig, SwitcherConfig,
SwitcherVisibility,
};

#[derive(Clone)]
Expand All @@ -17,31 +18,24 @@ pub struct KeyMenuWidget {
switcher_config: SwitcherConfig,
}

impl KeyMenuWidget {
pub fn new(power_config: PowerControlConfig, switcher_config: SwitcherConfig) -> Self {
Self {
power_config,
switcher_config,
}
}
fn shutdown_style(&self) -> Style {
let mut style = Style::default().fg(get_color(&self.power_config.shutdown_hint_color));
impl PowerControl {
fn style(&self) -> Style {
let mut style = Style::default().fg(get_color(&self.hint_color));

for modifier in get_modifiers(&self.power_config.shutdown_hint_modifiers) {
for modifier in get_modifiers(&self.hint_modifiers) {
style = style.add_modifier(modifier);
}

style
}
}

fn reboot_style(&self) -> Style {
let mut style = Style::default().fg(get_color(&self.power_config.reboot_hint_color));

for modifier in get_modifiers(&self.power_config.reboot_hint_modifiers) {
style = style.add_modifier(modifier);
impl KeyMenuWidget {
pub fn new(power_config: PowerControlConfig, switcher_config: SwitcherConfig) -> Self {
Self {
power_config,
switcher_config,
}

style
}

fn switcher_toggle_style(&self) -> Style {
Expand All @@ -57,27 +51,27 @@ impl KeyMenuWidget {
pub fn render(&self, frame: &mut Frame<impl ratatui::backend::Backend>, area: Rect) {
let mut items = Vec::new();

if self.power_config.allow_shutdown {
for power_control in self
.power_config
.base_entries
.0
.iter()
.chain(self.power_config.entries.0.iter())
{
items.push(Span::styled(
self.power_config
.shutdown_hint
.replace("%key%", &self.power_config.shutdown_key),
self.shutdown_style(),
power_control.key.as_str(),
power_control.style().add_modifier(Modifier::UNDERLINED),
));
items.push(Span::raw(" "));
items.push(Span::styled(
power_control.hint.as_str(),
power_control.style(),
));

// Add margin
items.push(Span::raw(" ".repeat(self.power_config.hint_margin.into())));
}

if self.power_config.allow_reboot {
items.push(Span::styled(
self.power_config
.reboot_hint
.replace("%key%", &self.power_config.reboot_key),
self.reboot_style(),
));
}

let left_widget = Paragraph::new(Line::from(items));
frame.render_widget(left_widget, area);

Expand All @@ -98,55 +92,41 @@ impl KeyMenuWidget {

pub(crate) fn key_press(&self, key_code: KeyCode) -> Option<super::ErrorStatusMessage> {
// TODO: Properly handle StdIn
if self.power_config.allow_shutdown && key_code == get_key(&self.power_config.shutdown_key)
for power_control in self
.power_config
.base_entries
.0
.iter()
.chain(self.power_config.entries.0.iter())
{
let cmd_status = Command::new("bash")
.arg("-c")
.arg(self.power_config.shutdown_cmd.clone())
.output();

match cmd_status {
Err(err) => {
log::error!("Failed to execute shutdown command: {:?}", err);
return Some(super::ErrorStatusMessage::FailedShutdown);
}
Ok(Output {
status,
stdout,
stderr,
}) if !status.success() => {
log::error!("Error while executing shutdown command");
log::error!("STDOUT:\n{:?}", stdout);
log::error!("STDERR:\n{:?}", stderr);

return Some(super::ErrorStatusMessage::FailedShutdown);
}
_ => {}
}
}
if self.power_config.allow_reboot && key_code == get_key(&self.power_config.reboot_key) {
let cmd_status = Command::new("bash")
.arg("-c")
.arg(self.power_config.reboot_cmd.clone())
.output();

match cmd_status {
Err(err) => {
log::error!("Failed to execute reboot command: {:?}", err);
return Some(super::ErrorStatusMessage::FailedReboot);
}
Ok(Output {
status,
stdout,
stderr,
}) if !status.success() => {
log::error!("Error while executing reboot command");
log::error!("STDOUT:\n{:?}", stdout);
log::error!("STDERR:\n{:?}", stderr);

return Some(super::ErrorStatusMessage::FailedReboot);
if key_code == get_key(&power_control.key) {
let cmd_status = Command::new("bash")
.arg("-c")
.arg(power_control.cmd.clone())
.output();

match cmd_status {
Err(err) => {
log::error!("Failed to execute shutdown command: {:?}", err);
return Some(super::ErrorStatusMessage::FailedPowerControl(
power_control.hint.clone(),
));
}
Ok(Output {
status,
stdout,
stderr,
}) if !status.success() => {
log::error!("Error while executing \"{}\"", power_control.hint);
log::error!("STDOUT:\n{:?}", stdout);
log::error!("STDERR:\n{:?}", stderr);

return Some(super::ErrorStatusMessage::FailedPowerControl(
power_control.hint.clone(),
));
}
_ => {}
}
_ => {}
}
}

Expand Down
Loading
Loading