Looper controlled by buttons
This commit is contained in:
@@ -12,4 +12,5 @@ pub struct Args {
|
||||
pub enum Command {
|
||||
Run,
|
||||
Collect,
|
||||
Mapper,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
mod args;
|
||||
mod collect;
|
||||
mod mapper;
|
||||
mod run;
|
||||
mod workspace;
|
||||
|
||||
@@ -20,5 +21,8 @@ async fn main() {
|
||||
Some(args::Command::Collect) => {
|
||||
collect::collect().await;
|
||||
}
|
||||
Some(args::Command::Mapper) => {
|
||||
mapper::mapper().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
153
xtask/src/mapper.rs
Normal file
153
xtask/src/mapper.rs
Normal file
@@ -0,0 +1,153 @@
|
||||
use std::sync::Arc;
|
||||
use tokio::process::{Child, Command};
|
||||
use tokio::sync::Mutex;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
type ProcessHandle = Arc<Mutex<Option<Child>>>;
|
||||
|
||||
pub async fn mapper() {
|
||||
let qjackctl_handle: ProcessHandle = Arc::new(Mutex::new(None));
|
||||
let firmware_handle: ProcessHandle = Arc::new(Mutex::new(None));
|
||||
let mapper_handle: ProcessHandle = Arc::new(Mutex::new(None));
|
||||
|
||||
let cancel_token = CancellationToken::new();
|
||||
|
||||
let cleanup_token = cancel_token.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
tokio::signal::ctrl_c()
|
||||
.await
|
||||
.expect("Failed to listen for ctrl+c");
|
||||
println!("Received Ctrl+C, shutting down...");
|
||||
cleanup_token.cancel();
|
||||
});
|
||||
|
||||
// Start processes
|
||||
println!("Starting qjackctl...");
|
||||
let qjackctl = spawn_qjackctl().await;
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
|
||||
|
||||
println!("Starting firmware debugger in new terminal...");
|
||||
let firmware = spawn_firmware_in_terminal().await;
|
||||
|
||||
println!("Starting mapper...");
|
||||
let mapper = spawn_mapper().await;
|
||||
|
||||
// Store handles for cleanup
|
||||
*qjackctl_handle.lock().await = Some(qjackctl);
|
||||
*firmware_handle.lock().await = Some(firmware);
|
||||
*mapper_handle.lock().await = Some(mapper);
|
||||
|
||||
// Get references back for waiting (firmware not monitored since gnome-terminal exits immediately)
|
||||
let mut qjackctl = qjackctl_handle.lock().await.take().unwrap();
|
||||
let mut mapper = mapper_handle.lock().await.take().unwrap();
|
||||
|
||||
// Wait for any process to exit or cancellation
|
||||
tokio::select! {
|
||||
result = qjackctl.wait() => {
|
||||
println!("qjackctl exited: {:?}", result);
|
||||
kill_process(&mut mapper, "mapper").await;
|
||||
cleanup_processes(vec![firmware_handle]).await;
|
||||
stop_jack_daemon().await;
|
||||
}
|
||||
result = mapper.wait() => {
|
||||
println!("mapper exited: {:?}", result);
|
||||
kill_process(&mut qjackctl, "qjackctl").await;
|
||||
cleanup_processes(vec![firmware_handle]).await;
|
||||
stop_jack_daemon().await;
|
||||
}
|
||||
_ = cancel_token.cancelled() => {
|
||||
println!("Shutdown requested");
|
||||
kill_process(&mut mapper, "mapper").await;
|
||||
kill_process(&mut qjackctl, "qjackctl").await;
|
||||
cleanup_processes(vec![firmware_handle]).await;
|
||||
stop_jack_daemon().await;
|
||||
}
|
||||
}
|
||||
|
||||
println!("All processes stopped");
|
||||
}
|
||||
|
||||
async fn cleanup_processes(handles: Vec<ProcessHandle>) {
|
||||
for handle in handles {
|
||||
if let Some(mut child) = handle.lock().await.take() {
|
||||
let _ = child.kill().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn stop_jack_daemon() {
|
||||
println!("Stopping JACK daemon...");
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let (jack, args) = ("taskkill", vec!["/f", "/im", "jackd.exe"]);
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
let (jack, args) = ("pkill", vec!["-f", "jackd"]);
|
||||
|
||||
Command::new(jack)
|
||||
.args(args)
|
||||
.output()
|
||||
.await
|
||||
.expect("Failed to stop JACK daemon");
|
||||
}
|
||||
|
||||
async fn spawn_qjackctl() -> Child {
|
||||
#[cfg(target_os = "windows")]
|
||||
let qjackctl = "C:\\Program Files\\JACK2\\qjackctl\\qjackctl.exe";
|
||||
#[cfg(target_os = "linux")]
|
||||
let qjackctl = "qjackctl";
|
||||
|
||||
Command::new(qjackctl)
|
||||
.arg("/start-server")
|
||||
.spawn()
|
||||
.expect("Could not start qjackctl")
|
||||
}
|
||||
|
||||
async fn spawn_firmware_in_terminal() -> Child {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
Command::new("C:\\Program Files\\Git\\git-bash.exe")
|
||||
.args(["-c", "cd firmware && cargo run; read -p 'Press Enter to close...'"])
|
||||
.spawn()
|
||||
.expect("Could not start firmware debugger in git-bash")
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
Command::new("gnome-terminal")
|
||||
.args([
|
||||
"--title=Firmware Debugger - FCB Looper",
|
||||
"--",
|
||||
"bash", "-c",
|
||||
"cd firmware && echo 'Starting firmware debugger...' && cargo run; echo 'Firmware debugger exited. Press Enter to close...'; read"
|
||||
])
|
||||
.spawn()
|
||||
.expect("Could not start firmware debugger in gnome-terminal")
|
||||
}
|
||||
}
|
||||
|
||||
async fn spawn_mapper() -> Child {
|
||||
Command::new("cargo")
|
||||
.args(["run", "--package", "mapper"])
|
||||
.stdin(std::process::Stdio::inherit())
|
||||
.stdout(std::process::Stdio::inherit())
|
||||
.stderr(std::process::Stdio::inherit())
|
||||
.spawn()
|
||||
.expect("Could not start mapper")
|
||||
}
|
||||
|
||||
async fn kill_process(child: &mut Child, name: &str) {
|
||||
match child.kill().await {
|
||||
Ok(()) => println!("Stopped {}", name),
|
||||
Err(e) => {
|
||||
// Don't print error if process already exited
|
||||
let error_msg = e.to_string().to_lowercase();
|
||||
if !error_msg.contains("no such process") &&
|
||||
!error_msg.contains("not found") &&
|
||||
!error_msg.contains("invalid argument") {
|
||||
eprintln!("Failed to stop {}: {}", name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,13 +124,8 @@ async fn spawn_qjackctl() -> Child {
|
||||
#[cfg(target_os = "linux")]
|
||||
let qjackctl = "qjackctl";
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let qjackctl_arg = "/start-server";
|
||||
#[cfg(target_os = "linux")]
|
||||
let qjackctl_arg = "--start-server";
|
||||
|
||||
Command::new(qjackctl)
|
||||
.arg(qjackctl_arg)
|
||||
.arg("/start-server")
|
||||
.spawn()
|
||||
.expect("Could not start qjackctl")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user