diff options
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/dinggerd/src/main.rs | 2 | ||||
| -rw-r--r-- | crates/dinggerd/src/tmux.rs | 36 |
2 files changed, 29 insertions, 9 deletions
diff --git a/crates/dinggerd/src/main.rs b/crates/dinggerd/src/main.rs index 285f83a..3707d51 100644 --- a/crates/dinggerd/src/main.rs +++ b/crates/dinggerd/src/main.rs @@ -125,7 +125,7 @@ fn main() { /// by the watcher on its next tick). This is the single code path behind /// every click: the tmux status line and the menu bar rows. fn focus(client: Option<String>, pane: Option<String>) -> Result<(), String> { - let clients = tmux::clients(); + let clients = tmux::clients()?; // The named client if it is attached; otherwise the most recently active // one (a click on the status line makes the clicking client the most // recent, so this is right even if the name did not expand). diff --git a/crates/dinggerd/src/tmux.rs b/crates/dinggerd/src/tmux.rs index 6189c4b..48d9544 100644 --- a/crates/dinggerd/src/tmux.rs +++ b/crates/dinggerd/src/tmux.rs @@ -37,8 +37,30 @@ impl Pane { } } +/// The tmux binary. launchd starts dingger, and the menu bar app that runs +/// `dingger focus`, with PATH=/usr/bin:/bin:/usr/sbin:/sbin, where Homebrew's +/// tmux is not found: fall back to the usual install locations. +fn tmux_binary() -> &'static std::path::Path { + static TMUX: std::sync::OnceLock<std::path::PathBuf> = std::sync::OnceLock::new(); + TMUX.get_or_init(|| { + if let Some(path) = std::env::var_os("DINGGER_TMUX") { + return path.into(); + } + let on_path: Vec<std::path::PathBuf> = std::env::var_os("PATH") + .map(|path| std::env::split_paths(&path).map(|dir| dir.join("tmux")).collect()) + .unwrap_or_default(); + let known = ["/opt/homebrew/bin/tmux", "/usr/local/bin/tmux", "/usr/bin/tmux"] + .map(std::path::PathBuf::from); + on_path + .into_iter() + .chain(known) + .find(|candidate| candidate.is_file()) + .unwrap_or_else(|| "tmux".into()) + }) +} + fn run_tmux(args: &[&str]) -> Result<String, String> { - let mut command = Command::new("tmux"); + let mut command = Command::new(tmux_binary()); // -u forces UTF-8 mode: without it, tmux mangles format output in // non-UTF-8 locales (launchd/cron default to the C locale, turning the // tab separators in our -F formats into underscores). @@ -51,7 +73,7 @@ fn run_tmux(args: &[&str]) -> Result<String, String> { let output = command .args(args) .output() - .map_err(|err| format!("failed to run tmux: {err}"))?; + .map_err(|err| format!("failed to run tmux ({}): {err}", tmux_binary().display()))?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); return Err(format!("tmux {} failed: {}", args.join(" "), stderr.trim())); @@ -227,14 +249,12 @@ pub fn client_format(client: &str, format: &str) -> Option<String> { } /// Attached clients as (name, pid), most recently active first. -pub fn clients() -> Vec<(String, u32)> { - let Ok(out) = run_tmux(&[ +pub fn clients() -> Result<Vec<(String, u32)>, String> { + let out = run_tmux(&[ "list-clients", "-F", "#{client_activity}\t#{client_name}\t#{client_pid}", - ]) else { - return Vec::new(); - }; + ])?; let mut clients: Vec<(u64, String, u32)> = out .lines() .filter_map(|line| { @@ -246,7 +266,7 @@ pub fn clients() -> Vec<(String, u32)> { }) .collect(); clients.sort_by_key(|client| std::cmp::Reverse(client.0)); - clients.into_iter().map(|(_, name, pid)| (name, pid)).collect() + Ok(clients.into_iter().map(|(_, name, pid)| (name, pid)).collect()) } /// Show a pane in a client: switch the client to the pane's window and make |
