From faa7fea0fd7ef028607dd093ef1d6eb4f6876a46 Mon Sep 17 00:00:00 2001 From: imgurbot12 Date: Sat, 19 Aug 2023 17:36:47 -0700 Subject: [PATCH] feat: move simple plugins to shell-script w/ new rmenu-build tool --- Cargo.toml | 2 - Makefile | 3 +- other-plugins/pactl-audio.sh | 17 +++ other-plugins/powermenu.sh | 65 +++++++++++ .../themes}/powermenu.css | 0 plugin-audio/Cargo.toml | 14 --- plugin-audio/src/main.rs | 43 ------- plugin-audio/src/pulse.rs | 100 ---------------- plugin-powermenu/Cargo.toml | 12 -- plugin-powermenu/src/action.rs | 107 ------------------ plugin-powermenu/src/main.rs | 57 ---------- rmenu-plugin/src/bin/main.rs | 2 +- rmenu/public/config.yaml | 11 +- 13 files changed, 91 insertions(+), 342 deletions(-) create mode 100755 other-plugins/pactl-audio.sh create mode 100755 other-plugins/powermenu.sh rename {plugin-powermenu/public => other-plugins/themes}/powermenu.css (100%) delete mode 100644 plugin-audio/Cargo.toml delete mode 100644 plugin-audio/src/main.rs delete mode 100644 plugin-audio/src/pulse.rs delete mode 100644 plugin-powermenu/Cargo.toml delete mode 100644 plugin-powermenu/src/action.rs delete mode 100644 plugin-powermenu/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 1295ac6..f0654d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,6 @@ members = [ "rmenu-plugin", "plugin-run", "plugin-desktop", - "plugin-audio", "plugin-network", "plugin-window", - "plugin-powermenu", ] diff --git a/Makefile b/Makefile index 809343a..d310d7d 100644 --- a/Makefile +++ b/Makefile @@ -17,11 +17,11 @@ install: build deploy deploy: mkdir -p ${DEST} + cp -vfr other-plugins/* ${DEST}/. cp -vf ./target/release/rmenu ${INSTALL}/rmenu cp -vf ./target/release/rmenu-build ${INSTALL}/rmenu-build cp -vf ./target/release/desktop ${DEST}/rmenu-desktop cp -vf ./target/release/run ${DEST}/rmenu-run - cp -vf ./target/release/audio ${DEST}/rmenu-audio cp -vf ./target/release/network ${DEST}/rmenu-network cp -vf ./target/release/window ${DEST}/rmenu-window cp -vf ./rmenu/public/config.yaml ${DEST}/config.yaml @@ -35,6 +35,5 @@ build-rmenu: build-plugins: ${CARGO} build -p run ${FLAGS} ${CARGO} build -p desktop ${FLAGS} - ${CARGO} build -p audio ${FLAGS} ${CARGO} build -p network ${FLAGS} ${CARGO} build -p window ${FLAGS} diff --git a/other-plugins/pactl-audio.sh b/other-plugins/pactl-audio.sh new file mode 100755 index 0000000..92b6843 --- /dev/null +++ b/other-plugins/pactl-audio.sh @@ -0,0 +1,17 @@ +#/bin/sh + +get_sinks() { + sinks=`pactl list sinks | grep -e 'Sink' -e 'Name' -e 'Description' | nl -s '>'` + default=`pactl get-default-sink` + for i in `seq 1 3 $(echo "$sinks" | wc -l)`; do + sink=`echo "$sinks" | grep "$i>" | cut -d '#' -f2` + name=`echo "$sinks" | grep "$(expr $i + 1)>" | cut -d ':' -f2 | xargs echo -n` + desc=`echo "$sinks" | grep "$(expr $i + 2)>" | cut -d ':' -f2 | xargs echo -n` + if [ "$name" = "$default" ]; then + desc="* $desc" + fi + rmenu-build entry -n "$desc" -a "`rmenu-build action "pactl set-default-sink $sink"`" + done +} + +get_sinks diff --git a/other-plugins/powermenu.sh b/other-plugins/powermenu.sh new file mode 100755 index 0000000..a7eb302 --- /dev/null +++ b/other-plugins/powermenu.sh @@ -0,0 +1,65 @@ +#!/bin/sh + +SELF=`realpath $0` +THEME=`realpath "$(dirname $0)/themes/powermenu.css"` +RMENU=${RMENU:-"rmenu"} + +options() { + rmenu-build options \ + -t $THEME \ + -n ArrowRight -p ArrowLeft \ + -w 550 -h 150 -M 0 +} + +#: desc => generate confirmation entries +#: usage => $cmd $name? +confirm() { + cmd=$1 + name="${2:-"Confirm"}" + options + rmenu-build entry -n "Cancel" -I "" -a "`rmenu-build action -m echo "$name Cancelled"`" + rmenu-build entry -n "$name" -I "" -a "`rmenu-build action "$cmd"`" +} + +#: desc => generate non-confirm entry +#: usage => $icon $name $cmd +gen_direct() { + rmenu-build entry -n "$2" -I "$1" -a "`rmenu-build action "$3"`" +} + +#: desc => generate confirmation entry +#: usage => $icon $name $cmd +gen_confirm() { + rmenu-build entry -n "$2" -I "$1" -a "`rmenu-build action "$SELF confirm '$2:$3'"`" +} + +#: desc => generate action-entry +#: usage => $icon $name $command $do-confirm +action() { + icon="$1" + name="$2" + cmd="$3" + confirm="$4" + [ -z "$confirm" ] \ + && gen_direct "$icon" "$name" "$cmd" \ + || gen_confirm "$icon" "$name" "$cmd" +} + +case "$1" in + "list") + confirm="$2" + options + action "⏻" "Shutdown" "systemctl poweroff" "$2" + action "" "Reboot" "systemctl reboot" "$2" + action "⏾" "Suspend" "systemctl suspend" "$2" + action "" "Log Out" "sway exit" "$2" + ;; + "confirm") + name=`echo $2 | cut -d ':' -f1` + action=`echo $2 | cut -d ':' -f2` + confirm "$action" "$name" | $RMENU + ;; + *) + echo "usage: $0 " && exit 1 + ;; +esac diff --git a/plugin-powermenu/public/powermenu.css b/other-plugins/themes/powermenu.css similarity index 100% rename from plugin-powermenu/public/powermenu.css rename to other-plugins/themes/powermenu.css diff --git a/plugin-audio/Cargo.toml b/plugin-audio/Cargo.toml deleted file mode 100644 index 0ac0d4e..0000000 --- a/plugin-audio/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "audio" -version = "0.0.1" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -clap = { version = "4.3.21", features = ["derive"] } -env_logger = "0.10.0" -log = "0.4.19" -rmenu-plugin = { version = "0.0.1", path = "../rmenu-plugin" } -serde_json = "1.0.104" -thiserror = "1.0.44" diff --git a/plugin-audio/src/main.rs b/plugin-audio/src/main.rs deleted file mode 100644 index 74fcd82..0000000 --- a/plugin-audio/src/main.rs +++ /dev/null @@ -1,43 +0,0 @@ -use clap::{Parser, Subcommand}; -use rmenu_plugin::Entry; - -mod pulse; - -#[derive(Debug, Subcommand)] -pub enum Commands { - ListSinks, - SetDefaultSink { sink: u32 }, -} - -#[derive(Parser)] -#[command(author, version, about, long_about = None)] -#[command(propagate_version = true)] -pub struct Cli { - #[clap(subcommand)] - command: Option, -} - -fn main() -> Result<(), pulse::PulseError> { - let cli = Cli::parse(); - let exe = std::env::current_exe()?.to_str().unwrap().to_string(); - - let command = cli.command.unwrap_or(Commands::ListSinks); - match command { - Commands::ListSinks => { - let sinks = pulse::get_sinks()?; - for sink in sinks { - let star = sink.default.then(|| "* ").unwrap_or(""); - let desc = format!("{star}{}", sink.description); - let exec = format!("{exe} set-default-sink {}", sink.index); - let entry = Entry::new(&desc, &exec, None); - println!("{}", serde_json::to_string(&entry).unwrap()); - } - } - Commands::SetDefaultSink { sink } => { - let sinkstr = format!("{sink}"); - pulse::set_default_sink(&sinkstr)?; - } - } - - Ok(()) -} diff --git a/plugin-audio/src/pulse.rs b/plugin-audio/src/pulse.rs deleted file mode 100644 index eb1eed5..0000000 --- a/plugin-audio/src/pulse.rs +++ /dev/null @@ -1,100 +0,0 @@ -use std::io::{BufRead, BufReader}; -use std::process::{Command, ExitStatus, Stdio}; - -use thiserror::Error; - -static PACTL: &'static str = "pactl"; - -#[derive(Debug, Error)] -pub enum PulseError { - #[error("Invalid Status")] - InvalidStatus(ExitStatus), - #[error("Cannot Read Command Output")] - InvalidStdout, - #[error("Invalid Output Encoding")] - InvalidEncoding(#[from] std::string::FromUtf8Error), - #[error("Unexepcted Command Output")] - UnexepctedOutput(String), - #[error("Command Error")] - CommandError(#[from] std::io::Error), -} - -#[derive(Debug)] -pub struct Sink { - pub index: u32, - pub name: String, - pub description: String, - pub default: bool, -} - -/// Retrieve Default Sink -pub fn get_default_sink() -> Result { - let output = Command::new(PACTL).arg("get-default-sink").output()?; - if !output.status.success() { - return Err(PulseError::InvalidStatus(output.status)); - } - Ok(String::from_utf8(output.stdout)?.trim().to_string()) -} - -/// Set Default PACTL Sink -pub fn set_default_sink(name: &str) -> Result<(), PulseError> { - let status = Command::new(PACTL) - .args(["set-default-sink", name]) - .status()?; - if !status.success() { - return Err(PulseError::InvalidStatus(status)); - } - Ok(()) -} - -/// Retrieve PulseAudio Sinks From PACTL -pub fn get_sinks() -> Result, PulseError> { - // retrieve default-sink - let default = get_default_sink()?; - // spawn command and begin reading - let mut command = Command::new(PACTL) - .args(["list", "sinks"]) - .stdout(Stdio::piped()) - .spawn()?; - let stdout = command.stdout.as_mut().ok_or(PulseError::InvalidStdout)?; - // collect output into only important lines - let mut lines = vec![]; - for line in BufReader::new(stdout).lines().filter_map(|l| l.ok()) { - let line = line.trim_start(); - if line.starts_with("Sink ") - || line.starts_with("Name: ") - || line.starts_with("Description: ") - { - lines.push(line.to_owned()); - } - } - // ensure status after command completion - let status = command.wait()?; - if !status.success() { - return Err(PulseError::InvalidStatus(status)); - } - // ensure number of lines matches expected - if lines.len() == 0 || lines.len() % 3 != 0 { - return Err(PulseError::UnexepctedOutput(lines.join("\n"))); - } - // parse details into chunks and generate sinks - let mut sinks = vec![]; - for chunk in lines.chunks(3) { - let (_, idx) = chunk[0] - .split_once("#") - .ok_or_else(|| PulseError::UnexepctedOutput(chunk[0].to_owned()))?; - let (_, name) = chunk[1] - .split_once(" ") - .ok_or_else(|| PulseError::UnexepctedOutput(chunk[1].to_owned()))?; - let (_, desc) = chunk[2] - .split_once(" ") - .ok_or_else(|| PulseError::UnexepctedOutput(chunk[2].to_owned()))?; - sinks.push(Sink { - index: idx.parse().unwrap(), - name: name.to_owned(), - description: desc.to_owned(), - default: name == default, - }); - } - Ok(sinks) -} diff --git a/plugin-powermenu/Cargo.toml b/plugin-powermenu/Cargo.toml deleted file mode 100644 index b3cab19..0000000 --- a/plugin-powermenu/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "powermenu" -version = "0.0.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -clap = { version = "4.3.21", features = ["derive"] } -rmenu-plugin = { version = "0.0.1", path = "../rmenu-plugin" } -serde_json = "1.0.105" -tempfile = "3.7.1" diff --git a/plugin-powermenu/src/action.rs b/plugin-powermenu/src/action.rs deleted file mode 100644 index db8b182..0000000 --- a/plugin-powermenu/src/action.rs +++ /dev/null @@ -1,107 +0,0 @@ -///! Functions to Build PowerMenu Actions -use std::collections::BTreeMap; -use std::env; -use std::io::Write; -use std::process; - -use rmenu_plugin::{Action, Entry}; -use tempfile::NamedTempFile; - -use crate::Command; - -//TODO: dynamically determine actions based on OS/Desktop/etc... - -/// Ordered Map of Configured Actions -pub type Actions = BTreeMap; - -/// Generate Confirmation for Specific Command -fn build_confirm(command: Command, actions: &Actions) -> Vec { - let entry = actions.get(&command).expect("Invalid Command"); - let cancel = format!("echo '{command} Cancelled'"); - vec![ - Entry { - name: "Cancel".to_owned(), - actions: vec![Action::new(&cancel)], - comment: None, - icon: None, - icon_alt: Some("".to_owned()), - }, - Entry { - name: "Confirm".to_owned(), - actions: entry.actions.to_owned(), - comment: None, - icon: None, - icon_alt: Some("".to_owned()), - }, - ] -} - -/// Generate Confirm Actions and Run Rmenu -pub fn confirm(command: Command, actions: &Actions) { - let rmenu = env::var("RMENU").unwrap_or_else(|_| "rmenu".to_owned()); - let entries = build_confirm(command, actions); - // write to temporary file - let mut f = NamedTempFile::new().expect("Failed to Open Temporary File"); - for entry in entries { - let json = serde_json::to_string(&entry).expect("Failed Serde Serialize"); - write!(f, "{json}\n").expect("Failed Write"); - } - // run command to read from temporary file - let path = f.path().to_str().expect("Invalid Temporary File Path"); - let mut command = process::Command::new(rmenu) - .args(["-i", path]) - .spawn() - .expect("Command Spawn Failed"); - let status = command.wait().expect("Command Wait Failed"); - if !status.success() { - panic!("Command Failed: {status:?}"); - } -} - -/// Calculate and Generate PowerMenu Actions -pub fn list_actions() -> Actions { - let mut actions = BTreeMap::new(); - actions.extend(vec![ - ( - Command::Shutdown, - Entry { - name: "Shut Down".to_owned(), - actions: vec![Action::new("systemctl poweroff")], - comment: None, - icon: None, - icon_alt: Some("⏻".to_owned()), - }, - ), - ( - Command::Reboot, - Entry { - name: "Reboot".to_owned(), - actions: vec![Action::new("systemctl reboot")], - comment: None, - icon: None, - icon_alt: Some(" ".to_owned()), - }, - ), - ( - Command::Suspend, - Entry { - name: "Suspend".to_owned(), - actions: vec![Action::new("systemctl suspend")], - comment: None, - icon: None, - icon_alt: Some("⏾".to_owned()), - }, - ), - ( - Command::Logout, - Entry { - name: "Log Out".to_owned(), - actions: vec![Action::new("sway exit")], - comment: None, - icon: None, - icon_alt: Some("".to_owned()), - }, - ), - ]); - actions -} diff --git a/plugin-powermenu/src/main.rs b/plugin-powermenu/src/main.rs deleted file mode 100644 index 6b97f71..0000000 --- a/plugin-powermenu/src/main.rs +++ /dev/null @@ -1,57 +0,0 @@ -mod action; - -use std::fmt::Display; - -use clap::{Parser, Subcommand}; -use rmenu_plugin::{self_exe, Method}; - -#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Subcommand)] -pub enum Command { - ListActions { no_confirm: bool }, - Shutdown, - Reboot, - Suspend, - Logout, -} - -impl Display for Command { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Command::ListActions { .. } => write!(f, "list-actions"), - Command::Shutdown => write!(f, "shutdown"), - Command::Reboot => write!(f, "reboot"), - Command::Suspend => write!(f, "suspend"), - Command::Logout => write!(f, "logout"), - } - } -} - -#[derive(Debug, Parser)] -struct Cli { - #[clap(subcommand)] - command: Option, -} - -fn main() { - let cli = Cli::parse(); - let exe = self_exe(); - - let actions = action::list_actions(); - let command = cli - .command - .unwrap_or(Command::ListActions { no_confirm: false }); - match command { - Command::ListActions { no_confirm } => { - for (command, mut entry) in actions { - if !no_confirm { - let exec = format!("{exe} {command}"); - entry.actions[0].exec = Method::Run(exec); - } - println!("{}", serde_json::to_string(&entry).unwrap()); - } - } - command => { - action::confirm(command, &actions); - } - } -} diff --git a/rmenu-plugin/src/bin/main.rs b/rmenu-plugin/src/bin/main.rs index f638aa4..7aaa471 100644 --- a/rmenu-plugin/src/bin/main.rs +++ b/rmenu-plugin/src/bin/main.rs @@ -125,7 +125,7 @@ struct OptionArgs { pub theme: Option, // search settings /// Override Default Placeholder - #[arg(short, long)] + #[arg(short = 'P', long)] pub placeholder: Option, /// Override Search Restriction #[arg(short = 'r', long)] diff --git a/rmenu/public/config.yaml b/rmenu/public/config.yaml index 7714161..ba6eda1 100644 --- a/rmenu/public/config.yaml +++ b/rmenu/public/config.yaml @@ -25,10 +25,6 @@ plugins: drun: exec: ["~/.config/rmenu/rmenu-desktop"] cache: onlogin - audio: - exec: ["~/.config/rmenu/rmenu-audio"] - cache: false - placeholder: "Select an Audio Sink" network: exec: ["~/.config/rmenu/rmenu-network"] cache: false @@ -37,6 +33,13 @@ plugins: exec: ["~/.config/rmenu/rmenu-window"] cache: false placeholder: "Jump to the Specified Window" + audio: + exec: ["sh", "~/.config/rmenu/pactl-audio.sh"] + cache: false + placeholder: "Select an Audio Sink" + powermenu: + exec: ["sh", "~/.config/rmenu/powermenu.sh", "list", "confirm"] + cache: false # custom keybindings keybinds: