Skip to content

Commit

Permalink
customize power controls in config (#185)
Browse files Browse the repository at this point in the history
* customize power controls in config

* streamline config

* appearance + use hint in logs

* re-enable substitution

* Re-add proper variable subsitution and partial fields

---------

Co-authored-by: Gijs Burghoorn <g.burghoorn@gmail.com>
  • Loading branch information
bits0rcerer and coastalwhite committed Dec 1, 2023
1 parent 2f5b654 commit 09003a8
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 136 deletions.
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

0 comments on commit 09003a8

Please sign in to comment.