use crate::prelude::*; mod transport; mod chain; mod sequencer; mod sampler; mod mixer; mod looper; mod plugin; pub use self::transport::Transport; pub use self::chain::Chain; pub use self::sequencer::Sequencer; pub use self::sampler::Sampler; pub use self::mixer::Mixer; pub use self::looper::Looper; pub use self::plugin::Plugin; use crossterm::event; pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box> { let device = Arc::new(Mutex::new(device)); let exited = Arc::new(AtomicBool::new(false)); let input_thread = { let poll = std::time::Duration::from_millis(100); let exited = exited.clone(); let device = device.clone(); spawn(move || loop { // Exit if flag is set if exited.fetch_and(true, Ordering::Relaxed) { break } // Listen for events and send them to the main thread if event::poll(poll).is_ok() { let event = event::read().unwrap(); match event { Event::Key(KeyEvent { code: KeyCode::Char('c'), modifiers: KeyModifiers::CONTROL, .. }) => { exited.store(true, Ordering::Relaxed); }, _ => if device.lock().unwrap().handle(&AppEvent::Input(event)).is_err() { break } } } }) }; stdout().execute(EnterAlternateScreen)?; enable_raw_mode()?; let mut terminal = ratatui::Terminal::new(CrosstermBackend::new(stdout()))?; //better_panic::install(); let better_panic_handler = better_panic::Settings::auto() .verbosity(better_panic::Verbosity::Full) .create_panic_handler(); std::panic::set_hook(Box::new(move |info: &std::panic::PanicInfo|{ stdout() .execute(crossterm::terminal::LeaveAlternateScreen) .unwrap(); crossterm::terminal::disable_raw_mode() .unwrap(); better_panic_handler(info); //writeln!(std::io::stderr(), "{}", info) //.unwrap(); //writeln!(std::io::stderr(), "{:?}", ::backtrace::Backtrace::new()) //.unwrap(); })); let sleep = std::time::Duration::from_millis(16); loop { terminal.draw(|frame|{ let area = frame.size(); let buffer = frame.buffer_mut(); device.lock().unwrap().render(buffer, area).expect("Failed to render content."); }).expect("Failed to render frame"); if exited.fetch_and(true, Ordering::Relaxed) { break } std::thread::sleep(sleep); }; //render_thread.join().expect("Failed to join render thread"); stdout() .queue(crossterm::terminal::LeaveAlternateScreen)? .flush()?; crossterm::terminal::disable_raw_mode()?; Ok(()) } pub trait Device: Send + Sync { fn handle (&mut self, _event: &AppEvent) -> Usually<()> { Ok(()) } fn render (&self, _buffer: &mut Buffer, _area: Rect) -> Usually { Ok(Rect { x: 0, y: 0, width: 0, height: 0 }) } fn process (&mut self, _client: Client, _scope: ProcessScope) {} } pub struct DynamicDevice { pub state: Arc>, pub render: MutexUsually + Send>>, pub handle: ArcUsually<()> + Send>>>, pub process: ArcControl + Send>>>, client: Option } impl Device for DynamicDevice { fn handle (&mut self, event: &AppEvent) -> Usually<()> { self.handle.lock().unwrap()(&mut *self.state.lock().unwrap(), event) } fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { self.render.lock().unwrap()(&*self.state.lock().unwrap(), buf, area) } } type DynamicAsyncClient = AsyncClient; type DynamicNotifications = Notifications>; type DynamicProcessHandler = ClosureProcessHandler; impl DynamicDevice { fn new <'a, R, H, P> (render: R, handle: H, process: P, state: T) -> Self where R: FnMut(&T, &mut Buffer, Rect)->Usually + Send + 'static, H: FnMut(&mut T, &AppEvent) -> Result<(), Box> + Send + 'static, P: FnMut(&mut T, &Client, &ProcessScope)->Control + Send + 'static, { Self { state: Arc::new(Mutex::new(state)), render: Mutex::new(Box::new(render)), handle: Arc::new(Mutex::new(Box::new(handle))), process: Arc::new(Mutex::new(Box::new(process))), client: None, } } fn state (&self) -> std::sync::MutexGuard<'_, T> { self.state.lock().unwrap() } fn activate (mut self, client: Client) -> Usually { self.client = Some(client.activate_async(Notifications(Box::new({ let state = self.state.clone(); let handle = self.handle.clone(); move|event|{ let mut state = state.lock().unwrap(); let mut handle = handle.lock().unwrap(); handle(&mut state, &event).unwrap() } }) as Box), ClosureProcessHandler::new(Box::new({ let state = self.state.clone(); let process = self.process.clone(); move|client: &Client, scope: &ProcessScope|{ let mut state = state.lock().unwrap(); let mut process = process.lock().unwrap(); (process)(&mut state, client, scope) } }) as BoxedProcessHandler))?); Ok(self) } } impl WidgetRef for &dyn Device { fn render_ref (&self, area: Rect, buf: &mut Buffer) { Device::render(*self, buf, area).expect("Failed to render device."); } } impl WidgetRef for dyn Device { fn render_ref (&self, area: Rect, buf: &mut Buffer) { Device::render(self, buf, area).expect("Failed to render device."); } } #[derive(Debug)] pub enum AppEvent { /// Terminal input Input(::crossterm::event::Event), /// Update values but not the whole form. Update, /// Update the whole form. Redraw, /// Device gains focus Focus, /// Device loses focus Blur, /// JACK notification Jack(JackEvent) } fn panic_hook (info: &std::panic::PanicInfo) { stdout() .execute(crossterm::terminal::LeaveAlternateScreen) .unwrap(); crossterm::terminal::disable_raw_mode() .unwrap(); writeln!(std::io::stderr(), "{}", info) .unwrap(); writeln!(std::io::stderr(), "{:?}", ::backtrace::Backtrace::new()) .unwrap(); } #[derive(Debug)] pub enum JackEvent { ThreadInit, Shutdown(ClientStatus, String), Freewheel(bool), SampleRate(Frames), ClientRegistration(String, bool), PortRegistration(PortId, bool), PortRename(PortId, String, String), PortsConnected(PortId, PortId, bool), GraphReorder, XRun, } pub struct Notifications(T); impl NotificationHandler for Notifications { fn thread_init (&self, _: &Client) { self.0(AppEvent::Jack(JackEvent::ThreadInit)); } fn shutdown (&mut self, status: ClientStatus, reason: &str) { self.0(AppEvent::Jack(JackEvent::Shutdown(status, reason.into()))); } fn freewheel (&mut self, _: &Client, enabled: bool) { self.0(AppEvent::Jack(JackEvent::Freewheel(enabled))); } fn sample_rate (&mut self, _: &Client, frames: Frames) -> Control { self.0(AppEvent::Jack(JackEvent::SampleRate(frames))); Control::Quit } fn client_registration (&mut self, _: &Client, name: &str, reg: bool) { self.0(AppEvent::Jack(JackEvent::ClientRegistration(name.into(), reg))); } fn port_registration (&mut self, _: &Client, id: PortId, reg: bool) { self.0(AppEvent::Jack(JackEvent::PortRegistration(id, reg))); } fn port_rename (&mut self, _: &Client, id: PortId, old: &str, new: &str) -> Control { self.0(AppEvent::Jack(JackEvent::PortRename(id, old.into(), new.into()))); Control::Continue } fn ports_connected (&mut self, _: &Client, a: PortId, b: PortId, are: bool) { self.0(AppEvent::Jack(JackEvent::PortsConnected(a, b, are))); } fn graph_reorder (&mut self, _: &Client) -> Control { self.0(AppEvent::Jack(JackEvent::GraphReorder)); Control::Continue } fn xrun (&mut self, _: &Client) -> Control { self.0(AppEvent::Jack(JackEvent::XRun)); Control::Continue } }