~tsdh/public-inbox

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
2 2

[PATCH swayr] swayrbar: add cmd module

Details
Message ID
<irv3fpxlgwzwz23rinvarsy4b3mmt755uvu7abmvlgyjm6y5gz@5bv2sff4c4ke>
DKIM signature
pass
Download raw message
Patch: +131 -0
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
 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?
 - What are some sane defaults?
 - 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?
 - License header? Who should it credit?

 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

[swayr/patches/arch.yml] build success

builds.sr.ht <builds@sr.ht>
Details
Message ID
<D2LCGHZBIJI5.9YQ2Z18X309O@fra01>
In-Reply-To
<irv3fpxlgwzwz23rinvarsy4b3mmt755uvu7abmvlgyjm6y5gz@5bv2sff4c4ke> (view parent)
DKIM signature
missing
Download raw message
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]: luca@lucamatei.com

✓ #1272034 SUCCESS swayr/patches/arch.yml https://builds.sr.ht/~tsdh/job/1272034
Details
Message ID
<87ed7zf4rq.fsf@gnu.org>
In-Reply-To
<irv3fpxlgwzwz23rinvarsy4b3mmt755uvu7abmvlgyjm6y5gz@5bv2sff4c4ke> (view parent)
DKIM signature
pass
Download raw message
Luca Matei Pintilie <luca@lucamatei.com> writes:

Hi Luca,

>  Hi, big fan of swayrbar!

Thanks!

>  I've been using it for a couple of months now and one feature of
>  Waybar I've been missing has been the "Idle Inhibitor" module
>  
>  https://github.com/Alexays/Waybar/wiki/Module:-Idle-Inhibitor
> [...]  
>  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.

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.

>  - Is "cmd" a good name? Kinda vague but I'm a bit out of ideas.

Yes, it's ok.  Maybe "shell-cmd" is a bit more explanatory but I have no
strong opinion here.

>  - 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
Reply to thread Export thread (mbox)