Luca Matei Pintilie: 1 swayrbar: add cmd module 3 files changed, 131 insertions(+), 0 deletions(-)
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~tsdh/public-inbox/patches/53781/mbox | git am -3Learn more about email & git
Signed-off-by: Luca Matei Pintilie <luca@lucamatei.com> --- Hi, big fan of swayrbar! I've been using it for a couple of months now
Thanks!
and one feature of Waybar I've been missing has been the "Idle Inhibitor" module https://github.com/Alexays/Waybar/wiki/Module:-Idle-Inhibitor As such the easiest way for me to implement this in swayrbar has been to run an external script that runs another program to idle inhibit. In particular I've set it up with the following config and scripts to run wayland-idle-inhibitor https://github.com/stwa/wayland-idle-inhibitor config.toml ```toml [[modules]] name = "cmd" instance = "0" format = "{HOME}/.config/swayrbar/status-idle" html_escape = false [modules.on_click] Left = ["{HOME}/.config/swayrbar/toggle-idle"] ``` status-idle ```sh #!/usr/bin/env sh pid_file="/tmp/wayland-idle-inhibitor-$(id --user).pid" pid=$(cat "${pid_file}") shif [ -n "${pid}" ] && kill -0 "${pid}" then echo -n "Awake..." else echo -n "I'll go to sleep whenever" fi ``` ```sh #!/usr/bin/env sh pid_file="/tmp/wayland-idle-inhibitor-$(id --user).pid" pid=$(cat "${pid_file}") if [ -n "${pid}" ] then kill -- "${pid}" rm "${pid_file}" else nohup "$HOME/.config/swayrbar/wayland-idle-inhibitor.py" > /dev/null 2>&1 & echo $! > "${pid_file}" fi ``` I'm sending this patch mostly to start a discussion. There are a couple of open questions I have, and I feel like it's easier to have a PoC to discuss. - Is this even the right approach to implementing what I want? Essentially custom code execution. - Is "cmd" a good name? Kinda vague but I'm a bit out of ideas - What config value should be used here to input the command?
I think that "format" isn't bad. It executes the command in format and displays its output. It has to be documented, though. And the question is how to cope with shell commands with arguments, e.g., "my-command {arg1} '{arg2} {arg3}'" which would usually be specified as ["cmd", "{arg1}", "'{arg2} {arg3}'"]... I guess that should be run with Command::new("sh") .arg("-c") .arg(command) .output() so that the interpretation is deferred to sh.
- What are some sane defaults?
I don't think there can be any for this module.
- No other module does environment variable substitution in strings (on_click/format), but personally I would like to have these scripts in $HOME/.config/swayrbar/, but not want to hard code my $HOME. Is this a desirable feature, or should it be removed?
Wouldn't ~/.config/swayrbar/ work as well? And could't you defer that business to sh by using $HOME/.config/swayrbar/ and passing that to sh -c as shown above? Then any environment variable would work. We should also think about what other placeholders could be useful for passing to shell commands. I think at least the {pid} of the currently focused window could be useful.
- License header? Who should it credit?
You wrote it, so credit yourself. Bye, Tassilo
Thank you for taking the time to look over this patch and I hope to hear back from you! swayrbar/src/bar.rs | 1 + swayrbar/src/module.rs | 1 + swayrbar/src/module/cmd.rs | 129 +++++++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 swayrbar/src/module/cmd.rs diff --git a/swayrbar/src/bar.rs b/swayrbar/src/bar.rs index 7f2c0c5..a25ec0f 100644 --- a/swayrbar/src/bar.rs +++ b/swayrbar/src/bar.rs @@ -99,6 +99,7 @@ fn create_modules(config: config::Config) -> Vec<Box<dyn BarModuleFn>> { "pactl" => module::pactl::create(mc), "nmcli" => module::wifi::create(module::wifi::WifiTool::Nmcli, mc), "iwctl" => module::wifi::create(module::wifi::WifiTool::Iwctl, mc), + "cmd" => module::cmd::create(mc), unknown => { log::warn!("Unknown module name '{unknown}'. Ignoring..."); continue; diff --git a/swayrbar/src/module.rs b/swayrbar/src/module.rs index d050658..979eed5 100644 --- a/swayrbar/src/module.rs +++ b/swayrbar/src/module.rs @@ -25,6 +25,7 @@ pub mod pactl; pub mod sysinfo; pub mod wifi; pub mod window; +pub mod cmd; #[derive(Debug)] pub enum RefreshReason { diff --git a/swayrbar/src/module/cmd.rs b/swayrbar/src/module/cmd.rs new file mode 100644 index 0000000..acd40ca --- /dev/null +++ b/swayrbar/src/module/cmd.rs @@ -0,0 +1,129 @@ +// Copyright (C) 2024 Luca Matei Pintilie <luca@lucamatei.com> +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program. If not, see <https://www.gnu.org/licenses/>. + +//! The cmd `swayrbar` module. + +use crate::config; +use crate::module::{BarModuleFn, RefreshReason}; +use crate::shared::fmt::subst_placeholders; +use std::process::Command; +use std::string::String; +use std::sync::Mutex; +use swaybar_types as s; + +const NAME: &str = "cmd"; + +struct State { + cached_home: String, + cached_text: String, +} + +pub struct BarModuleCmd { + config: config::ModuleConfig, + state: Mutex<State>, +} + +fn refresh_state(program: &str) -> String { + match Command::new(program).output() { + Ok(output) => String::from_utf8_lossy(&output.stdout).to_string(), + Err(err) => { + log::error!("Could not run command: {err}"); + String::new() + } + } +} + +fn subst_placeholders(fmt: &str, html_escape: bool, state: &State) -> String { + subst_placeholders!(fmt, html_escape, { + "HOME" => { + state.cached_home.to_owned() + }, + }) +} + +pub fn create(config: config::ModuleConfig) -> Box<dyn BarModuleFn> { + Box::new(BarModuleCmd { + config, + state: Mutex::new(State { + cached_home: std::env::var("HOME").unwrap(), + cached_text: String::new(), + }), + }) +} + +impl BarModuleFn for BarModuleCmd { + fn default_config(instance: String) -> config::ModuleConfig + where + Self: Sized, + { + config::ModuleConfig { + name: NAME.to_owned(), + instance, + format: "/usr/bin/echo".to_owned(), + html_escape: Some(true), + on_click: None, + } + } + + fn get_config(&self) -> &config::ModuleConfig { + &self.config + } + + fn build(&self, reason: &RefreshReason) -> s::Block { + let mut state = self.state.lock().expect("Could not lock state."); + + if match reason { + RefreshReason::TimerEvent => true, + RefreshReason::ClickEvent { name, instance } => { + name == &self.config.name && instance == &self.config.instance + } + _ => false, + } { + // FIXME: config.format is wrong here, but it's the best we have + state.cached_text = refresh_state(&subst_placeholders( + &self.config.format, + self.config.html_escape.unwrap_or(false), + &state, + )); + } + + s::Block { + name: Some(NAME.to_owned()), + instance: Some(self.config.instance.clone()), + full_text: state.cached_text.to_owned(), + align: Some(s::Align::Left), + markup: Some(s::Markup::Pango), + short_text: None, + color: None, + background: None, + border: None, + border_top: None, + border_bottom: None, + border_left: None, + border_right: None, + min_width: None, + urgent: None, + separator: Some(true), + separator_block_width: None, + } + } + + fn subst_cmd_args<'a>(&'a self, cmd: &'a [String]) -> Vec<String> { + let state = self.state.lock().expect("Could not lock state."); + cmd.iter() + .map(|arg| subst_placeholders(arg, false, &state)) + .collect() + } +} -- 2.45.2
builds.sr.ht <builds@sr.ht>swayr/patches/arch.yml: SUCCESS in 1m18s [swayrbar: add cmd module][0] from [Luca Matei Pintilie][1] [0]: https://lists.sr.ht/~tsdh/public-inbox/patches/53781 [1]: mailto:luca@lucamatei.com ✓ #1272034 SUCCESS swayr/patches/arch.yml https://builds.sr.ht/~tsdh/job/1272034
Luca Matei Pintilie <luca@lucamatei.com> writes: Hi Luca,
I think a generic shell command module is nice in any case. If there were a standard idle inhibitor interface you could operate using sway IPC, then a specialized module would also be nice but I have no knowledge about that. I've never needed idle inhibitors until now.
Yes, it's ok. Maybe "shell-cmd" is a bit more explanatory but I have no strong opinion here.