bitraid: 1 Add pipewire (wpctl) module 3 files changed, 205 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/55265/mbox | git am -3Learn more about email & git
--- swayrbar/src/bar.rs | 1 + swayrbar/src/module.rs | 1 + swayrbar/src/module/wpctl.rs | 203 +++++++++++++++++++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 swayrbar/src/module/wpctl.rs diff --git a/swayrbar/src/bar.rs b/swayrbar/src/bar.rs index 8321c3b..b62053b 100644 --- a/swayrbar/src/bar.rs +++ b/swayrbar/src/bar.rs @@ -97,6 +97,7 @@ fn create_modules(config: config::Config) -> Vec<Box<dyn BarModuleFn>> { "battery" => module::battery::create(mc), "date" => module::date::create(mc), "pactl" => module::pactl::create(mc), + "wpctl" => module::wpctl::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), diff --git a/swayrbar/src/module.rs b/swayrbar/src/module.rs index fe2d39f..c3e3ba4 100644 --- a/swayrbar/src/module.rs +++ b/swayrbar/src/module.rs @@ -23,6 +23,7 @@ pub mod battery; pub mod cmd; pub mod date; pub mod pactl; +pub mod wpctl; pub mod sysinfo; pub mod wifi; pub mod window; diff --git a/swayrbar/src/module/wpctl.rs b/swayrbar/src/module/wpctl.rs new file mode 100644 index 0000000..f3c7241 --- /dev/null +++ b/swayrbar/src/module/wpctl.rs @@ -0,0 +1,203 @@ +// Copyright (C) 2022-2023 Tassilo Horn <tsdh@gnu.org>
This should be just 2024. The module didn't exist before. And feel free to mention you as author, too. And would you mind writing a short README.md section about the new module in the configuration section and mention it in the swayrbar summary? Basically look for pactl and write something similar about wpctl directly below. Thanks again, Tassilo
+// +// 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 wpctl `swayrbar` module. + +use crate::config; +use crate::module::{BarModuleFn, RefreshReason}; +use crate::shared::fmt::subst_placeholders; +use once_cell::sync::Lazy; +use regex::Regex; +use std::collections::HashMap; +use std::process::Command; +use std::sync::Mutex; +use swaybar_types as s; + +const NAME: &str = "wpctl"; + +struct State { + volume: u8, + muted: bool, + volume_source: u8, + muted_source: bool, + cached_text: String, +} + +pub static VOLUME_RX: Lazy<Regex> = + Lazy::new(|| Regex::new(r".* (?<num>\d+)\.(?<frac>\d{2}).*").unwrap()); + +fn run_wpctl(args: &[&str]) -> String { + match Command::new("wpctl").args(args).output() { + Ok(output) => String::from_utf8_lossy(&output.stdout).to_string(), + Err(err) => { + log::error!("Could not run wpctl: {err}"); + String::new() + } + } +} + +fn get_volume(device: &str) -> (u8, bool) { + let output = run_wpctl(&["get-volume", device]); + let mut volume = String::new(); + VOLUME_RX + .captures(&output) + .unwrap() + .expand("$num$frac", &mut volume); + (volume.parse::<u8>().unwrap_or(255_u8), output.contains("[MUTED]")) +} + +pub struct BarModuleWpctl { + config: config::ModuleConfig, + state: Mutex<State>, +} + +fn refresh_state(state: &mut State, fmt_str: &str, html_escape: bool) { + (state.volume, state.muted) = get_volume("@DEFAULT_AUDIO_SINK@"); + (state.volume_source, state.muted_source) = get_volume("@DEFAULT_AUDIO_SOURCE@"); + state.cached_text = subst_placeholders(fmt_str, html_escape, state); +} + +fn subst_placeholders(fmt: &str, html_escape: bool, state: &State) -> String { + subst_placeholders!(fmt, html_escape, { + "volume" => { + state.volume + }, + "muted" =>{ + if state.muted { + " muted" + } else { + "" + } + }, + "volume_source" => { + state.volume_source + }, + "muted_source" =>{ + if state.muted_source { + " muted" + } else { + "" + } + }, + }) +} + +pub fn create(config: config::ModuleConfig) -> Box<dyn BarModuleFn> { + Box::new(BarModuleWpctl { + config, + state: Mutex::new(State { + volume: 255_u8, + muted: false, + volume_source: 255_u8, + muted_source: false, + cached_text: String::new(), + }), + }) +} + +impl BarModuleFn for BarModuleWpctl { + fn default_config(instance: String) -> config::ModuleConfig + where + Self: Sized, + { + config::ModuleConfig { + name: NAME.to_owned(), + instance, + format: "🔈 Vol: {volume:{:3}}%{muted}".to_owned(), + html_escape: Some(true), + on_click: Some(HashMap::from([ + ("Left".to_owned(), + vec!["foot".to_owned(), "watch".to_owned(), + "wpctl".to_owned(), "status".to_owned()]), + ( + "Right".to_owned(), + vec![ + "wpctl".to_owned(), + "set-mute".to_owned(), + "@DEFAULT_AUDIO_SINK@".to_owned(), + "toggle".to_owned(), + ], + ), + ( + "WheelUp".to_owned(), + vec![ + "wpctl".to_owned(), + "set-volume".to_owned(), + "@DEFAULT_AUDIO_SINK@".to_owned(), + "1%+".to_owned(), + ], + ), + ( + "WheelDown".to_owned(), + vec![ + "wpctl".to_owned(), + "set-volume".to_owned(), + "@DEFAULT_AUDIO_SINK@".to_owned(), + "1%-".to_owned(), + ], + ), + ])), + } + } + + 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, + } { + refresh_state( + &mut state, + &self.config.format, + self.config.is_html_escape(), + ); + } + + 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.46.1
bitraid <bitraid@protonmail.ch> writes: Hi, thanks a lot! the code looks fine and I'm happy to accept your contribution. Some minor nitpicks below.