use std::process::Command;
#[derive(Debug)]
enum LinkOutcome {
Ok, Benign, Failed, }
fn run_pw_link(args: &[&str]) -> LinkOutcome {
let output = match Command::new("pw-link").args(args).output() {
Ok(o) => o,
Err(e) => {
tracing::error!("failed to execute pw-link: {e}");
return LinkOutcome::Failed;
}
};
if output.status.success() {
tracing::info!(args = ?args, "pw-link success");
return LinkOutcome::Ok;
}
let stderr = String::from_utf8_lossy(&output.stderr);
if stderr.contains("File exists") || stderr.contains("No such file") {
tracing::debug!(args = ?args, "pw-link idempotent no-op");
return LinkOutcome::Benign; }
tracing::error!("pw-link failed: {}", stderr.trim());
LinkOutcome::Failed
}
pub(crate) fn create_monitor_links(out_node_id: u32, in_node_id: u32) {
for (out_port, in_port) in &[("monitor_FL", "input_FL"), ("monitor_FR", "input_FR")] {
run_pw_link(&[
&format!("{out_node_id}:{out_port}"),
&format!("{in_node_id}:{in_port}"),
]);
}
}
pub(crate) fn create_device_output_links(filter_id: u32, device_id: u32) -> bool {
let mut ok = true;
for (out_port, in_port) in [("output_FL", "playback_FL"), ("output_FR", "playback_FR")] {
ok &= !matches!(
run_pw_link(&[
&format!("{filter_id}:{out_port}"),
&format!("{device_id}:{in_port}"),
]),
LinkOutcome::Failed
);
}
ok
}
pub(crate) fn remove_device_output_links(filter_id: u32, device_id: u32) -> bool {
let mut ok = true;
for (out_port, in_port) in [("output_FL", "playback_FL"), ("output_FR", "playback_FR")] {
ok &= !matches!(
run_pw_link(&[
"-d",
&format!("{filter_id}:{out_port}"),
&format!("{device_id}:{in_port}"),
]),
LinkOutcome::Failed
);
}
ok
}
pub(crate) fn check_null_sink_input_source(null_sink_id: u32) -> Option<bool> {
let output = match Command::new("pw-link").arg("-I").output() {
Ok(o) => o,
Err(e) => {
tracing::warn!(%e, "pw-link -I failed — cannot check null sink input source");
return None;
}
};
let text = String::from_utf8_lossy(&output.stdout);
Some(text.lines().any(|line| {
line.contains(&format!("-> {null_sink_id}:playback_FL"))
|| line.contains(&format!("-> {null_sink_id}:playback_FR"))
}))
}