feat: cli

This commit is contained in:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 2026-01-17 01:03:24 +02:00
parent b1074bd831
commit 204e26a324
2 changed files with 99 additions and 54 deletions

View file

@ -145,6 +145,22 @@ pub mod model {
pub mod core { pub mod core {
use super::{*, model::*, gui::*}; use super::{*, model::*, gui::*};
impl App { impl App {
pub fn new (
jack: &Jack<'static>,
project: Arrangement,
config: Config,
mode: impl AsRef<str>
) -> Self {
Self {
color: ItemTheme::random(),
dialog: Dialog::welcome(),
jack: jack.clone(),
mode: config.modes.clone().read().unwrap().get(mode.as_ref()).cloned().unwrap(),
config,
project,
..Default::default()
}
}
pub fn update_clock (&self) { pub fn update_clock (&self) {
ViewCache::update_clock(&self.project.clock.view_cache, self.clock(), self.size.w() > 80) ViewCache::update_clock(&self.project.clock.view_cache, self.clock(), self.size.w() > 80)
} }
@ -847,7 +863,7 @@ pub mod glue {
} }
/// Command-line configuration. /// Command-line configuration.
#[cfg(feature = "cli")] pub mod cli { #[cfg(feature = "cli")] pub mod cli {
use super::{*, model::*, gui::*}; use super::{*, model::*};
use clap::{self, Parser, Subcommand}; use clap::{self, Parser, Subcommand};
/// CLI banner. /// CLI banner.
const HEADER: &'static str = r#" const HEADER: &'static str = r#"
@ -866,7 +882,7 @@ pub mod glue {
/// Pre-defined configuration modes. /// Pre-defined configuration modes.
/// ///
/// TODO: Replace these with scripted configurations. /// TODO: Replace these with scripted configurations.
#[command(subcommand)] mode: Option<LaunchMode>, #[command(subcommand)] action: Action,
/// Name of JACK client /// Name of JACK client
#[arg(short='n', long)] name: Option<String>, #[arg(short='n', long)] name: Option<String>,
/// Whether to attempt to become transport master /// Whether to attempt to become transport master
@ -895,10 +911,22 @@ pub mod glue {
#[arg(short='R', long)] right_to: Vec<String>, #[arg(short='R', long)] right_to: Vec<String>,
} }
/// Application modes /// Application modes
#[derive(Debug, Clone, Subcommand)] #[derive(Debug, Clone, Subcommand, Default)]
enum LaunchMode { enum Action {
/// Continue where you left off
#[default] Resume,
/// Show version.
Version,
/// Show configuration.
Config,
/// Show status of current session.
Status,
/// Run headlessly in current session.
Headless,
/// Create a new session instead of loading the previous one. /// Create a new session instead of loading the previous one.
New, New,
/// Create new session from importable file.
Import,
} }
impl Cli { impl Cli {
fn midi_froms (&self) -> Vec<Connect> { fn midi_froms (&self) -> Vec<Connect> {
@ -908,63 +936,61 @@ pub mod glue {
Connect::collect(&self.midi_to, &[] as &[&str], &self.midi_to_re) Connect::collect(&self.midi_to, &[] as &[&str], &self.midi_to_re)
} }
pub fn run (&self) -> Usually<()> { pub fn run (&self) -> Usually<()> {
let name = self.name.as_ref().map_or("tek", |x|x.as_str()); if matches!(self.action, Action::Version) {
let tracks = vec![]; println!("todo version");
let scenes = vec![]; } else {
let empty = &[] as &[&str]; let mut config = Config::new(None);
let left_froms = Connect::collect(&self.left_from, empty, empty); config.init()?;
let left_tos = Connect::collect(&self.left_to, empty, empty); if matches!(self.action, Action::Config) {
let right_froms = Connect::collect(&self.right_from, empty, empty); println!("{config:#?}");
let right_tos = Connect::collect(&self.right_to, empty, empty); } else {
let _audio_froms = &[left_froms.as_slice(), right_froms.as_slice()]; let name = self.name.as_ref().map_or("tek", |x|x.as_str());
let _audio_tos = &[left_tos.as_slice(), right_tos.as_slice()]; let jack = Jack::new(&name)?;
let mut config = Config::new(None); let tracks = vec![];
config.init()?; let scenes = vec![];
Tui::new()?.run(&Jack::new_run(&name, move|jack|{ let empty = &[] as &[&str];
let midi_ins = { let left_froms = Connect::collect(&self.left_from, empty, empty);
let left_tos = Connect::collect(&self.left_to, empty, empty);
let right_froms = Connect::collect(&self.right_from, empty, empty);
let right_tos = Connect::collect(&self.right_to, empty, empty);
let _audio_froms = &[left_froms.as_slice(), right_froms.as_slice()];
let _audio_tos = &[left_tos.as_slice(), right_tos.as_slice()];
let mut midi_ins = vec![]; let mut midi_ins = vec![];
let mut midi_outs = vec![];
for (index, connect) in self.midi_froms().iter().enumerate() { for (index, connect) in self.midi_froms().iter().enumerate() {
midi_ins.push(jack.midi_in(&format!("M/{index}"), &[connect.clone()])?); midi_ins.push(jack.midi_in(&format!("M/{index}"), &[connect.clone()])?);
} }
midi_ins
};
let midi_outs = {
let mut midi_outs = vec![];
for (index, connect) in self.midi_tos().iter().enumerate() { for (index, connect) in self.midi_tos().iter().enumerate() {
midi_outs.push(jack.midi_out(&format!("{index}/M"), &[connect.clone()])?); midi_outs.push(jack.midi_out(&format!("{index}/M"), &[connect.clone()])?);
}; };
midi_outs let clock = Clock::new(&jack, self.bpm)?;
}; let project = Arrangement::new(
let project = Arrangement { &jack, None, clock, tracks, scenes, midi_ins, midi_outs
name: Default::default(), );
color: ItemTheme::random(), if matches!(self.action, Action::Status) {
jack: jack.clone(), println!("{project:?}");
clock: Clock::new(&jack, self.bpm)?, // TODO git integration
tracks, } else {
scenes, let app = App::new(&jack, project, config, ":menu");
selection: Selection::TrackClip { track: 0, scene: 0 }, let client = jack.run(move|jack|{
midi_ins, jack.sync_lead(self.sync_lead, |mut state|{
midi_outs, let clock = app.clock();
..Default::default() clock.playhead.update_from_sample(state.position.frame() as f64);
}; state.position.bbt = Some(clock.bbt());
let app = App { state.position
jack: jack.clone(), })?;
color: ItemTheme::random(), jack.sync_follow(self.sync_follow)?;
dialog: Dialog::welcome(), Ok(app)
mode: config.modes.clone().read().unwrap().get(":menu").cloned().unwrap(), })?;
config, if matches!(self.action, Action::Headless) {
project, println!("todo headless");
..Default::default() } else {
}; return Tui::new()?.run(&client)
jack.sync_lead(self.sync_lead, |mut state|{ }
let clock = app.clock(); }
clock.playhead.update_from_sample(state.position.frame() as f64); }
state.position.bbt = Some(clock.bbt()); }
state.position Ok(())
})?;
jack.sync_follow(self.sync_follow)?;
Ok(app)
})?)
} }
} }
} }

View file

@ -74,6 +74,25 @@ impl Arrangement {
} }
impl Arrangement { impl Arrangement {
/// Create a new arrangement.
pub fn new (
jack: &Jack<'static>,
name: Option<Arc<str>>,
clock: Clock,
tracks: Vec<Track>,
scenes: Vec<Scene>,
midi_ins: Vec<MidiInput>,
midi_outs: Vec<MidiOutput>,
) -> Self {
Self {
clock, tracks, scenes, midi_ins, midi_outs,
jack: jack.clone(),
name: name.unwrap_or_default(),
color: ItemTheme::random(),
selection: Selection::TrackClip { track: 0, scene: 0 },
..Default::default()
}
}
/// Width of display /// Width of display
pub fn w (&self) -> u16 { pub fn w (&self) -> u16 {
self.size.w() as u16 self.size.w() as u16