//#![feature(anonymous_lifetime_in_impl_trait)] //#![feature(associated_type_defaults)] //#![feature(const_default)] //#![feature(const_option_ops)] //#![feature(const_precise_live_drops)] //#![feature(const_trait_impl)] //#![feature(impl_trait_in_assoc_type)] //#![feature(step_trait)] //#![feature(trait_alias)] //#![feature(type_alias_impl_trait)] //#![feature(type_changing_struct_update)] mod deps; pub use self::deps::*; /// Define a trait an implement it for various mutation-enabled wrapper types. */ #[macro_export] macro_rules! flex_trait_mut ( ($Trait:ident $(<$($A:ident:$T:ident),+>)? { $(fn $fn:ident (&mut $self:ident $(, $arg:ident:$ty:ty)*) -> $ret:ty $body:block)* })=>{ pub trait $Trait $(<$($A: $T),+>)? { $(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret $body)* } impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &mut _T_ { $(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { (*$self).$fn($($arg),*) })* } impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for Option<_T_> { $(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { if let Some(this) = $self { this.$fn($($arg),*) } else { Ok(None) } })* } impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Mutex<_T_> { $(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.get_mut().unwrap().$fn($($arg),*) })* } impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<::std::sync::Mutex<_T_>> { $(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.lock().unwrap().$fn($($arg),*) })* } impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::RwLock<_T_> { $(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.write().unwrap().$fn($($arg),*) })* } impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<::std::sync::RwLock<_T_>> { $(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.write().unwrap().$fn($($arg),*) })* } }; ); /// Implement [Handle] for given `State` and `handler`. #[macro_export] macro_rules! impl_handle { //(|$self:ident:$State:ty,$input:ident|$handler:expr) => { //impl ::tengri::Handle for $State { //fn handle (&mut $self, $input: &E) -> Perhaps { //$handler //} //} //}; ($E:ty: |$self:ident:$State:ty,$input:ident|$handler:expr) => { //impl ::tengri::Handle<$E> for $State { //fn handle (&mut $self, $input: &$E) -> //Perhaps<<$E as ::tengri::Input>::Handled> //{ //$handler //} //} } } /// Implement [Default]. #[macro_export] macro_rules! impl_default { ($T:ty:$e:expr) => { impl Default for $T { fn default () -> Self { $e } } }; } /// Implement [`Debug`] in bulk. #[macro_export] macro_rules! impl_debug ( (<$($T:ident $(: $U:ident)?),+> $S:ty|$self:ident,$w:ident|$body:block)=>{ impl <$($T$(:$U)?),+> std::fmt::Debug for $S { fn fmt (&$self, $w: &mut std::fmt::Formatter) -> std::fmt::Result $body } }; ($S:ty|$self:ident,$w:ident|$body:block)=>{ impl std::fmt::Debug for $S { fn fmt (&$self, $w: &mut std::fmt::Formatter) -> std::fmt::Result $body } }; ); /// Implement [`Display`] in bulk. #[macro_export] macro_rules! impl_display ( (<$($T:ident $(: $U:ident)?),+> $S:ty|$self:ident,$w:ident|$body:block)=>{ impl <$($T$(:$U)?),+> std::fmt::Display for $S { fn fmt (&$self, $w: &mut std::fmt::Formatter) -> std::fmt::Result $body } }; ($S:ty|$self:ident,$w:ident|$body:block)=>{ impl std::fmt::Display for $S { fn fmt (&$self, $w: &mut std::fmt::Formatter) -> std::fmt::Result $body } }; ); /// Implement [`From`] in bulk. #[macro_export] macro_rules! impl_from ( ($(<$($lt:lifetime),+>)?$Target:ty:|$state:ident:$Source:ty|$cb:expr) => { impl $(<$($lt),+>)? From<$Source> for $Target { fn from ($state:$Source) -> Self { $cb }} }; ($($Struct:ty { $( $(<$($l:lifetime),* $($T:ident$(:$U:ident)?),*>)? ($source:ident: $From:ty) $expr:expr );+ $(;)? })*) => { $( $(impl $(<$($l),* $($T$(:$U)?),*>)? From<$From> for $Struct { fn from ($source: $From) -> Self { $expr } })+ )* }; ); /// Implement [AsRef]. #[macro_export] macro_rules! impl_as_ref (($T:ty: |$self:ident:$S:ty|$x:expr)=>{ impl AsRef<$T> for $S { fn as_ref (&$self) -> &$T { $x } } }); /// Implement [AsMut]. #[macro_export] macro_rules! impl_as_mut (($T:ty: |$self:ident:$S:ty|$x:expr)=>{ impl AsMut<$T> for $S { fn as_mut (&mut $self) -> &mut $T { $x } } }); /// Implement [AsRefOpt]. #[macro_export] macro_rules! impl_as_ref_opt (($T:ty: |$self:ident:$S:ty|$x:expr)=>{ impl AsRefOpt<$T> for $S { fn as_ref_opt (&$self) -> Option<&$T> { $x } } }); /// Implement [AsMutOpt]. #[macro_export] macro_rules! impl_as_mut_opt (($T:ty: |$self:ident:$S:ty|$x:expr)=>{ impl AsMutOpt<$T> for $S { fn as_mut_opt (&mut $self) -> Option<&mut $T> { $x } } }); pub trait AsRefOpt { fn as_ref_opt (&self) -> Option<&T>; } pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } /// Implement [AsRef] and [AsMut]. #[macro_export] macro_rules! impl_has ( ($T:ty: |$self:ident:$S:ty|$x:expr)=>{ impl AsRef<$T> for $S { fn as_ref (&$self) -> &$T { &$x } } impl AsMut<$T> for $S { fn as_mut (&mut $self) -> &mut $T { &mut $x } } }; ($T:ty: |$self:ident:$S:ty|$x:block;$y:block)=>{ impl AsRef<$T> for $S { fn as_ref (&$self) -> &$T $x } impl AsMut<$T> for $S { fn as_mut (&mut $self) -> &mut $T $y } } ); #[cfg(feature = "exit")] pub use self::exit::*; #[cfg(feature = "exit")] mod exit { use crate::*; use std::sync::{Arc, atomic::{AtomicBool, Ordering::Relaxed}}; use crossterm::event::*; #[derive(Clone, Default, Debug)] pub struct Exit(Arc); impl Exit { pub fn run (run: impl FnOnce(Self)->Usually) -> Usually { run(Self(Arc::new(AtomicBool::new(false)))) } pub fn is (event: &Event) -> bool { matches!(event, Event::Key(KeyEvent { modifiers: KeyModifiers::CONTROL, code: KeyCode::Char('c'), kind: KeyEventKind::Press, state: KeyEventState::NONE })) } pub fn exit (&self) { self.0.store(true, Relaxed) } } impl AsRef> for Exit { fn as_ref (&self) -> &Arc { &self.0 } } } #[cfg(feature = "time")] pub use self::time::*; #[cfg(feature = "time")] mod time { use ::std::sync::atomic::Ordering::*; use ::atomic_float::AtomicF64; /// Performance counter #[derive(Debug)] pub struct PerfModel { pub clock: quanta::Clock, /// Measurement has a small cost. Disable it here. pub enabled: bool, // In nanoseconds. Time used by last iteration. pub used: AtomicF64, // In microseconds. Max prescribed time for iteration (frame, chunk...). pub window: AtomicF64, } impl_default!(PerfModel: Self { enabled: true, clock: quanta::Clock::new(), used: Default::default(), window: Default::default(), }); impl PerfModel { pub fn get_t0 (&self) -> Option { if self.enabled { Some(self.clock.raw()) } else { None } } pub fn get_t1 (&self, t0: Option) -> Option { if let Some(t0) = t0 { if self.enabled { Some(self.clock.delta(t0, self.clock.raw())) } else { None } } else { None } } pub fn update (&self, t0: Option, microseconds: f64) { if let Some(t0) = t0 { let t1 = self.clock.raw(); self.used.store(self.clock.delta_as_nanos(t0, t1) as f64, Relaxed); self.window.store(microseconds, Relaxed,); } } pub fn percentage (&self) -> Option { let window = self.window.load(Relaxed) * 1000.0; if window > 0.0 { let used = self.used.load(Relaxed); Some(100.0 * used / window) } else { None } } pub fn cycle T, T> (&self, call: &mut F) -> T { let t0 = self.get_t0(); let result = call(self); let _t1 = self.get_t1(t0).unwrap(); result } } } #[cfg(feature = "sing")] pub use self::sing::*; #[cfg(feature = "sing")] mod sing { use crate::{*, time::PerfModel}; pub use ::jack::{*, contrib::{TimebaseInfo, ClosureProcessHandler, PositionBBT, Position as JackPosition}}; pub use ::midly::{Smf, TrackEventKind, MidiMessage, Error as MidiError, num::*, live::*}; use ConnectName::*; use ConnectScope::*; use ConnectStatus::*; use JackState::*; /// Wraps [JackState], and through it [jack::Client] when connected. /// /// ``` /// let jack = tengri::Jack::default(); /// ``` #[derive(Clone, Debug, Default)] pub struct Jack<'j> ( pub(crate) Arc>> ); /// This is a connection which may be [Inactive], [Activating], or [Active]. /// In the [Active] and [Inactive] states, [JackState::client] returns a /// [jack::Client], which you can use to talk to the JACK API. /// /// ``` /// let state = tengri::JackState::default(); /// ``` #[derive(Debug, Default)] pub enum JackState<'j> { /// Unused #[default] Inert, /// Before activation. Inactive(Client), /// During activation. Activating, /// After activation. Must not be dropped for JACK thread to persist. Active(DynamicAsyncClient<'j>), } /// Implement [Jack] constructor and methods impl<'j> Jack<'j> { /// Register new [Client] and wrap it for shared use. pub fn new_run + Audio + Send + Sync + 'static> ( name: impl AsRef, init: impl FnOnce(Jack<'j>)->Usually ) -> Usually>> { Jack::new(name)?.run(init) } pub fn new (name: impl AsRef) -> Usually { let client = Client::new(name.as_ref(), ClientOptions::NO_START_SERVER)?.0; Ok(Jack(Arc::new(RwLock::new(JackState::Inactive(client))))) } /// Run something with the client. pub fn with_client (&self, op: impl FnOnce(&Client)->T) -> T { match &*self.0.try_read().unwrap() { Inert => panic!("jack client not activated"), Inactive(client) => op(client), Activating => panic!("jack client has not finished activation"), Active(client) => op(client.as_client()), } } pub fn run + Audio + Send + Sync + 'static> (self, init: impl FnOnce(Self)->Usually) -> Usually>> { let client_state = self.0.clone(); let app: Arc> = Arc::new(RwLock::new(init(self)?)); let mut state = Activating; std::mem::swap(&mut*client_state.try_write().unwrap(), &mut state); if let Inactive(client) = state { // This is the misc notifications handler. It's a struct that wraps a [Box] // which performs type erasure on a callback that takes [JackEvent], which is // one of the available misc notifications. let notify = JackNotify(Box::new({ let app = app.clone(); move|event|(&mut*app.try_write().unwrap()).handle(event) }) as BoxedJackEventHandler); // This is the main processing handler. It's a struct that wraps a [Box] // which performs type erasure on a callback that takes [Client] and [ProcessScope] // and passes them down to the `app`'s `process` callback, which in turn // implements audio and MIDI input and output on a realtime basis. let process = ::jack::contrib::ClosureProcessHandler::new(Box::new({ let app = app.clone(); move|c: &_, s: &_|if let Some(mut app) = app.try_write() { app.process(c, s) } else { Control::Quit } }) as BoxedAudioHandler); // Launch a client with the two handlers. *client_state.try_write().unwrap() = Active( client.activate_async(notify, process)? ); } else { unreachable!(); } Ok(app) } } impl<'j> HasJack<'j> for Jack<'j> { fn jack (&self) -> &Jack<'j> { self } } impl<'j> HasJack<'j> for &Jack<'j> { fn jack (&self) -> &Jack<'j> { self } } impl<'j, T: HasJack<'j>> HasJack<'j> for Arc { fn jack (&self) -> &Jack<'j> { (&**self).jack() } } /// Event enum for JACK events. /// /// ``` /// let event = tengri::JackEvent::XRun; // kerpop /// ``` #[derive(Debug, Clone, PartialEq)] pub enum JackEvent { ThreadInit, Shutdown(ClientStatus, Arc), Freewheel(bool), SampleRate(Frames), ClientRegistration(Arc, bool), PortRegistration(PortId, bool), PortRename(PortId, Arc, Arc), PortsConnected(PortId, PortId, bool), GraphReorder, XRun, } /// Generic notification handler that emits [JackEvent] /// /// ``` /// let notify = tengri::JackNotify(|_|{}); /// ``` pub struct JackNotify(pub T); /// Notification handler wrapper for [BoxedJackEventHandler]. pub type DynamicNotifications<'j> = JackNotify>; /// Boxed [JackEvent] callback. pub type BoxedJackEventHandler<'j> = Box; impl NotificationHandler for JackNotify { fn thread_init(&self, _: &Client) { self.0(JackEvent::ThreadInit); } unsafe fn shutdown(&mut self, status: ClientStatus, reason: &str) { self.0(JackEvent::Shutdown(status, reason.into())); } fn freewheel(&mut self, _: &Client, enabled: bool) { self.0(JackEvent::Freewheel(enabled)); } fn sample_rate(&mut self, _: &Client, frames: Frames) -> Control { self.0(JackEvent::SampleRate(frames)); Control::Quit } fn client_registration(&mut self, _: &Client, name: &str, reg: bool) { self.0(JackEvent::ClientRegistration(name.into(), reg)); } fn port_registration(&mut self, _: &Client, id: PortId, reg: bool) { self.0(JackEvent::PortRegistration(id, reg)); } fn port_rename(&mut self, _: &Client, id: PortId, old: &str, new: &str) -> Control { self.0(JackEvent::PortRename(id, old.into(), new.into())); Control::Continue } fn ports_connected(&mut self, _: &Client, a: PortId, b: PortId, are: bool) { self.0(JackEvent::PortsConnected(a, b, are)); } fn graph_reorder(&mut self, _: &Client) -> Control { self.0(JackEvent::GraphReorder); Control::Continue } fn xrun(&mut self, _: &Client) -> Control { self.0(JackEvent::XRun); Control::Continue } } pub trait JackPerfModel { fn update_from_jack_scope (&self, t0: Option, scope: &ProcessScope); } impl JackPerfModel for PerfModel { fn update_from_jack_scope (&self, t0: Option, scope: &ProcessScope) { if let Some(t0) = t0 { let t1 = self.clock.raw(); self.used.store( self.clock.delta_as_nanos(t0, t1) as f64, Relaxed, ); self.window.store( scope.cycle_times().unwrap().period_usecs as f64, Relaxed, ); } } } /// Trait for thing that has a JACK process callback. pub trait Audio { /// Handle a JACK event. fn handle (&mut self, _event: JackEvent) {} /// Projecss a JACK chunk. fn process (&mut self, _: &Client, _: &ProcessScope) -> Control { Control::Continue } /// The JACK process callback function passed to the server. fn callback ( state: &Arc>, client: &Client, scope: &ProcessScope ) -> Control where Self: Sized { if let Some(mut state) = state.try_write() { state.process(client, scope) } else { Control::Quit } } } /// Running JACK [AsyncClient] with maximum type erasure. /// /// One [Box] contains function that handles [JackEvent]s. /// /// Another [Box] containing a function that handles realtime IO. /// /// That's all it knows about them. pub type DynamicAsyncClient<'j> = AsyncClient, DynamicAudioHandler<'j>>; /// Notification handler wrapper for [BoxedAudioHandler]. pub type DynamicAudioHandler<'j> = ::jack::contrib::ClosureProcessHandler<(), BoxedAudioHandler<'j>>; /// Boxed realtime callback. pub type BoxedAudioHandler<'j> = Box Control + Send + Sync + 'j>; /// Things that can provide a [jack::Client] reference. /// /// ``` /// use tengri::*; /// /// let jack: &Jack = Jacked::default().jack(); /// /// #[derive(Default)] struct Jacked<'j>(Jack<'j>); /// /// impl<'j> HasJack<'j> for Jacked<'j> { /// fn jack (&self) -> &Jack<'j> { &self.0 } /// } /// ``` pub trait HasJack<'j>: Send + Sync { /// Return the internal [jack::Client] handle /// that lets you call the JACK API. fn jack (&self) -> &Jack<'j>; fn with_client (&self, op: impl FnOnce(&Client)->T) -> T { self.jack().with_client(op) } fn port_by_name (&self, name: &str) -> Option> { self.with_client(|client|client.port_by_name(name)) } fn port_by_id (&self, id: u32) -> Option> { self.with_client(|c|c.port_by_id(id)) } fn register_port (&self, name: impl AsRef) -> Usually> { self.with_client(|client|Ok(client.register_port(name.as_ref(), PS::default())?)) } fn sync_lead (&self, enable: bool, callback: impl Fn(TimebaseInfo)->jack::contrib::Position) -> Usually<()> { if enable { self.with_client(|client|match client.register_timebase_callback(false, callback) { Ok(_) => Ok(()), Err(e) => Err(e) })? } Ok(()) } fn sync_follow (&self, _enable: bool) -> Usually<()> { // TODO: sync follow Ok(()) } } /// Implement [Audio]: provide JACK callbacks. #[macro_export] macro_rules! impl_audio { (| $self1:ident: $Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?,$c:ident,$s:ident |$cb:expr$(;|$self2:ident,$e:ident|$cb2:expr)?) => { impl $(<$($L),*$($T $(: $U)?),*>)? Audio for $Struct $(<$($L),*$($T),*>)? { #[inline] fn process (&mut $self1, $c: &Client, $s: &ProcessScope) -> Control { $cb } $(#[inline] fn handle (&mut $self2, $e: JackEvent) { $cb2 })? } }; ($Struct:ident: $process:ident, $handle:ident) => { impl Audio for $Struct { #[inline] fn process (&mut self, c: &Client, s: &ProcessScope) -> Control { $process(self, c, s) } #[inline] fn handle (&mut self, e: JackEvent) { $handle(self, e) } } }; ($Struct:ident: $process:ident) => { impl Audio for $Struct { #[inline] fn process (&mut self, c: &Client, s: &ProcessScope) -> Control { $process(self, c, s) } } }; } pub trait JackPorts: HasJack<'static> { /// Register a MIDI input port. fn midi_in (&self, name: &impl AsRef, connect: &[Connect]) -> Usually; /// Register a MIDI output port. fn midi_out (&self, name: &impl AsRef, connect: &[Connect]) -> Usually; /// Register an audio input port. fn audio_in (&self, name: &impl AsRef, connect: &[Connect]) -> Usually; /// Register an audio output port. fn audio_out (&self, name: &impl AsRef, connect: &[Connect]) -> Usually; } impl> JackPorts for J { fn midi_in (&self, name: &impl AsRef, connect: &[Connect]) -> Usually { MidiInput::new(self.jack(), name, connect) } fn midi_out (&self, name: &impl AsRef, connect: &[Connect]) -> Usually { MidiOutput::new(self.jack(), name, connect) } fn audio_in (&self, name: &impl AsRef, connect: &[Connect]) -> Usually { AudioInput::new(self.jack(), name, connect) } fn audio_out (&self, name: &impl AsRef, connect: &[Connect]) -> Usually { AudioOutput::new(self.jack(), name, connect) } } pub trait JackPort: HasJack<'static> { const KIND: &'static str = "Port"; type Port: PortSpec + Default; type Pair: PortSpec + Default; fn new (jack: &Jack<'static>, name: &impl AsRef, connect: &[Connect]) -> Usually where Self: Sized; fn register (jack: &Jack<'static>, name: &impl AsRef) -> Usually> { jack.with_client(|c|c.register_port::(name.as_ref(), Default::default())) .map_err(|e|e.into()) } fn close (self) -> Usually<()> where Self: Sized { let jack = self.jack().clone(); Ok(jack.with_client(|c|c.unregister_port(self.into_port()))?) } fn into_port (self) -> Port where Self: Sized; fn port_name (&self) -> &Arc; fn port (&self) -> &Port; fn port_mut (&mut self) -> &mut Port; fn ports (&self, re_name: Option<&str>, re_type: Option<&str>, flags: PortFlags) -> Vec { self.with_client(|c|c.ports(re_name, re_type, flags)) } fn port_by_id (&self, id: u32) -> Option> { self.with_client(|c|c.port_by_id(id)) } fn port_by_name (&self, name: impl AsRef) -> Option> { self.with_client(|c|c.port_by_name(name.as_ref())) } fn connections (&self) -> &[Connect]; fn connect_to_matching <'k> (&'k self) -> Usually<()> { for connect in self.connections().iter() { match &connect.name { Some(Exact(name)) => { *connect.status.try_write().unwrap() = self.connect_exact(name)?; }, Some(RegExp(re)) => { *connect.status.try_write().unwrap() = self.connect_regexp(re, connect.scope)?; }, _ => {}, }; } Ok(()) } fn connect_exact <'k> (&'k self, name: &str) -> Usually, Arc, ConnectStatus)>> { self.with_client(move|c|{ let mut status = vec![]; for port in c.ports(None, None, PortFlags::empty()).iter() { if port.as_str() == &*name { if let Some(port) = c.port_by_name(port.as_str()) { let port_status = self.connect_to_unowned(&port)?; let name = port.name()?.into(); status.push((port, name, port_status)); if port_status == Connected { break } } } } Ok(status) }) } fn connect_regexp <'k> ( &'k self, re: &str, scope: Option ) -> Usually, Arc, ConnectStatus)>> { self.with_client(move|c|{ let mut status = vec![]; let ports = c.ports(Some(&re), None, PortFlags::empty()); for port in ports.iter() { if let Some(port) = c.port_by_name(port.as_str()) { let port_status = self.connect_to_unowned(&port)?; let name = port.name()?.into(); status.push((port, name, port_status)); if port_status == Connected && scope == Some(One) { break } } } Ok(status) }) } /** Connect to a matching port by name. */ fn connect_to_name (&self, name: impl AsRef) -> Usually { self.with_client(|c|if let Some(ref port) = c.port_by_name(name.as_ref()) { self.connect_to_unowned(port) } else { Ok(Missing) }) } /** Connect to a matching port by reference. */ fn connect_to_unowned (&self, port: &Port) -> Usually { self.with_client(|c|Ok(if let Ok(_) = c.connect_ports(self.port(), port) { Connected } else if let Ok(_) = c.connect_ports(port, self.port()) { Connected } else { Mismatch })) } /** Connect to an owned matching port by reference. */ fn connect_to_owned (&self, port: &Port) -> Usually { self.with_client(|c|Ok(if let Ok(_) = c.connect_ports(self.port(), port) { Connected } else if let Ok(_) = c.connect_ports(port, self.port()) { Connected } else { Mismatch })) } } /// Audio input port. #[derive(Debug)] pub struct AudioInput { /// Handle to JACK client, for receiving reconnect events. pub jack: Jack<'static>, /// Port name pub name: Arc, /// Port handle. pub port: Port, /// List of ports to connect to. pub connections: Vec, } /// Audio output port. #[derive(Debug)] pub struct AudioOutput { /// Handle to JACK client, for receiving reconnect events. pub jack: Jack<'static>, /// Port name pub name: Arc, /// Port handle. pub port: Port, /// List of ports to connect to. pub connections: Vec, } /// MIDI input port. #[derive(Debug)] pub struct MidiInput { /// Handle to JACK client, for receiving reconnect events. pub jack: Jack<'static>, /// Port name pub name: Arc, /// Port handle. pub port: Port, /// List of currently held notes. pub held: Arc>, /// List of ports to connect to. pub connections: Vec, } /// MIDI output port. #[derive(Debug)] pub struct MidiOutput { /// Handle to JACK client, for receiving reconnect events. pub jack: Jack<'static>, /// Port name pub name: Arc, /// Port handle. pub port: Port, /// List of currently held notes. pub held: Arc>, /// List of ports to connect to. pub connections: Vec, /// Buffer pub note_buffer: Vec, /// Buffer pub output_buffer: Vec>>, } macro_rules! jack_port { ($($Struct:ty = ($Port:ty => $Pair:ty) $({ $($tt:tt)* })?),*) => { $( impl HasJack<'static> for $Struct { fn jack (&self) -> &Jack<'static> { &self.jack } } impl JackPort for $Struct { type Port = $Port; type Pair = $Pair; fn port_name (&self) -> &Arc { &self.name } fn port (&self) -> &Port { &self.port } fn port_mut (&mut self) -> &mut Port { &mut self.port } fn into_port (self) -> Port { self.port } fn connections (&self) -> &[Connect] { self.connections.as_slice() } $($($tt)*)? } )* }; } jack_port!( AudioInput = (AudioIn => AudioOut) { const KIND: &'static str = "Audio In"; fn new (jack: &Jack<'static>, name: &impl AsRef, connect: &[Connect]) -> Usually where Self: Sized { let port = Self { port: Self::register(jack, name)?, jack: jack.clone(), name: name.as_ref().into(), connections: connect.to_vec(), }; port.connect_to_matching()?; Ok(port) } }, AudioOutput = (AudioOut => AudioIn) { const KIND: &'static str = "Audio Out"; fn new (jack: &Jack<'static>, name: &impl AsRef, connect: &[Connect]) -> Usually where Self: Sized { let port = Self { port: Self::register(jack, name)?, jack: jack.clone(), name: name.as_ref().into(), connections: connect.to_vec(), }; port.connect_to_matching()?; Ok(port) } }, MidiInput = (MidiIn => MidiOut) { const KIND: &'static str = "MIDI In"; fn new (jack: &Jack<'static>, name: &impl AsRef, connect: &[Connect]) -> Usually where Self: Sized { let port = Self { port: Self::register(jack, name)?, jack: jack.clone(), name: name.as_ref().into(), connections: connect.to_vec(), held: Arc::new(RwLock::new([false;128])) }; port.connect_to_matching()?; Ok(port) } }, MidiOutput = (MidiOut => MidiIn) { const KIND: &'static str = "MIDI Out"; fn new (jack: &Jack<'static>, name: &impl AsRef, connect: &[Connect]) -> Usually where Self: Sized { let port = Self::register(jack, name)?; let jack = jack.clone(); let name = name.as_ref().into(); let connections = connect.to_vec(); let port = Self { jack, port, name, connections, held: Arc::new([false;128].into()), note_buffer: vec![0;8], output_buffer: vec![vec![];65536], }; port.connect_to_matching()?; Ok(port) } } ); pub type CollectedMidiInput<'a> = Vec, MidiError>)>>; /// Trait for thing that may receive MIDI. pub trait HasMidiIns { fn midi_ins (&self) -> &Vec; fn midi_ins_mut (&mut self) -> &mut Vec; /// Collect MIDI input from app ports (TODO preallocate large buffers) fn midi_input_collect <'a> (&'a self, scope: &'a ProcessScope) -> CollectedMidiInput<'a> { self.midi_ins().iter() .map(|port|port.port().iter(scope) .map(|RawMidi { time, bytes }|(time, LiveEvent::parse(bytes))) .collect::>()) .collect::>() } fn midi_ins_with_sizes <'a> (&'a self) -> impl Iterator, &'a [Connect], usize, usize)> + Send + Sync + 'a { let mut y = 0; self.midi_ins().iter().enumerate().map(move|(i, input)|{ let height = 1 + input.connections().len(); let data = (i, input.port_name(), input.connections(), y, y + height); y += height; data }) } } /// Trait for thing that may output MIDI. pub trait HasMidiOuts { fn midi_outs (&self) -> &Vec; fn midi_outs_mut (&mut self) -> &mut Vec; fn midi_outs_with_sizes <'a> (&'a self) -> impl Iterator, &'a [Connect], usize, usize)> + Send + Sync + 'a { let mut y = 0; self.midi_outs().iter().enumerate().map(move|(i, output)|{ let height = 1 + output.connections().len(); let data = (i, output.port_name(), output.connections(), y, y + height); y += height; data }) } fn midi_outs_emit (&mut self, scope: &ProcessScope) { for port in self.midi_outs_mut().iter_mut() { port.buffer_emit(scope) } } } impl MidiOutput { /// Clear the section of the output buffer that we will be using, /// emitting "all notes off" at start of buffer if requested. pub fn buffer_clear (&mut self, scope: &ProcessScope, reset: bool) { let n_frames = (scope.n_frames() as usize).min(self.output_buffer.len()); for frame in &mut self.output_buffer[0..n_frames] { frame.clear(); } if reset { all_notes_off(&mut self.output_buffer); } } /// Write a note to the output buffer pub fn buffer_write <'a> ( &'a mut self, sample: usize, event: LiveEvent, ) { self.note_buffer.fill(0); event.write(&mut self.note_buffer).expect("failed to serialize MIDI event"); self.output_buffer[sample].push(self.note_buffer.clone()); // Update the list of currently held notes. if let LiveEvent::Midi { ref message, .. } = event { update_keys(&mut*self.held.try_write().unwrap(), message); } } /// Write a chunk of MIDI data from the output buffer to the output port. pub fn buffer_emit (&mut self, scope: &ProcessScope) { let samples = scope.n_frames() as usize; let mut writer = self.port.writer(scope); for (time, events) in self.output_buffer.iter().enumerate().take(samples) { for bytes in events.iter() { writer.write(&RawMidi { time: time as u32, bytes }).unwrap_or_else(|_|{ panic!("Failed to write MIDI data: {bytes:?}"); }); } } } } impl MidiInput { pub fn parsed <'a> (&'a self, scope: &'a ProcessScope) -> impl Iterator, &'a [u8])> { parse_midi_input(self.port().iter(scope)) } } /// Return boxed iterator of MIDI events pub fn parse_midi_input <'a> (input: ::jack::MidiIter<'a>) -> Box, &'a [u8])> + 'a> { Box::new(input.map(|::jack::RawMidi { time, bytes }|( time as usize, LiveEvent::parse(bytes).unwrap(), bytes ))) } /// Add "all notes off" to the start of a buffer. pub fn all_notes_off (output: &mut [Vec>]) { let mut buf = vec![]; let msg = MidiMessage::Controller { controller: 123.into(), value: 0.into() }; let evt = LiveEvent::Midi { channel: 0.into(), message: msg }; evt.write(&mut buf).unwrap(); output[0].push(buf); } /// Update notes_in array pub fn update_keys (keys: &mut[bool;128], message: &MidiMessage) { match message { MidiMessage::NoteOn { key, .. } => { keys[key.as_int() as usize] = true; } MidiMessage::NoteOff { key, .. } => { keys[key.as_int() as usize] = false; }, _ => {} } } impl> + AsMut>> HasMidiIns for T { fn midi_ins (&self) -> &Vec { self.as_ref() } fn midi_ins_mut (&mut self) -> &mut Vec { self.as_mut() } } impl> + AsMut>> HasMidiOuts for T { fn midi_outs (&self) -> &Vec { self.as_ref() } fn midi_outs_mut (&mut self) -> &mut Vec { self.as_mut() } } impl> AddMidiIn for T { fn midi_in_add (&mut self) -> Usually<()> { let index = self.midi_ins().len(); let port = MidiInput::new(self.jack(), &format!("M/{index}"), &[])?; self.midi_ins_mut().push(port); Ok(()) } } /// Trail for thing that may gain new MIDI ports. impl> AddMidiOut for T { fn midi_out_add (&mut self) -> Usually<()> { let index = self.midi_outs().len(); let port = MidiOutput::new(self.jack(), &format!("{index}/M"), &[])?; self.midi_outs_mut().push(port); Ok(()) } } /// May create new MIDI input ports. pub trait AddMidiIn { fn midi_in_add (&mut self) -> Usually<()>; } /// May create new MIDI output ports. pub trait AddMidiOut { fn midi_out_add (&mut self) -> Usually<()>; } #[derive(Clone, Debug, PartialEq)] pub enum ConnectName { /** Exact match */ Exact(Arc), /** Match regular expression */ RegExp(Arc), } #[derive(Clone, Copy, Debug, PartialEq)] pub enum ConnectScope { One, All } #[derive(Clone, Copy, Debug, PartialEq)] pub enum ConnectStatus { Missing, Disconnected, Connected, Mismatch, } /// Port connection manager. /// /// ``` /// let connect = tengri::Connect::default(); /// ``` #[derive(Clone, Debug, Default)] pub struct Connect { pub name: Option, pub scope: Option, pub status: Arc, Arc, ConnectStatus)>>>, pub info: Arc, } impl Connect { pub fn new > ( exact: Option>, re: Option>, re_all: Option>, ) -> Vec { let mut connections = vec![]; if let Some(exact ) = exact { for port in exact { connections.push(Self::exact(port)) } } if let Some(regexp) = re { for port in regexp { connections.push(Self::regexp(port)) } } if let Some(re_all) = re_all { for port in re_all { connections.push(Self::regexp_all(port)) } } connections } /// Connect to this exact port pub fn exact (name: impl AsRef) -> Self { let info = format!("=:{}", name.as_ref()).into(); let name = Some(Exact(name.as_ref().into())); Self { name, scope: Some(One), status: Arc::new(RwLock::new(vec![])), info } } pub fn regexp (name: impl AsRef) -> Self { let info = format!("~:{}", name.as_ref()).into(); let name = Some(RegExp(name.as_ref().into())); Self { name, scope: Some(One), status: Arc::new(RwLock::new(vec![])), info } } pub fn regexp_all (name: impl AsRef) -> Self { let info = format!("+:{}", name.as_ref()).into(); let name = Some(RegExp(name.as_ref().into())); Self { name, scope: Some(All), status: Arc::new(RwLock::new(vec![])), info } } pub fn info (&self) -> Arc { format!(" ({}) {} {}", { let status = self.status.try_read().unwrap(); let mut ok = 0; for (_, _, state) in status.iter() { if *state == Connected { ok += 1 } } format!("{ok}/{}", status.len()) }, match self.scope { None => "x", Some(One) => " ", Some(All) => "*", }, match &self.name { None => format!("x"), Some(Exact(name)) => format!("= {name}"), Some(RegExp(name)) => format!("~ {name}"), }).into() } } pub fn connect_midi_ins > ( jack: &Jack<'static>, name: &T, midi_from: &[T], midi_from_re: Option<&[T]>, ) -> Usually> { Ok(Connect::new( Some(midi_from.into_iter()), Some([].into_iter()), midi_from_re.map(|x|x.into_iter())).iter().enumerate() .map(|(index, connect)|jack.midi_in(&format!("{}/{index}", name.as_ref()), &[connect.clone()])) .collect::>()?) } pub fn connect_midi_outs > ( jack: &Jack<'static>, name: &T, midi_to: &[T], midi_to_re: Option<&[T]>, ) -> Usually> { Ok(Connect::new( Some(midi_to.into_iter()), Some([].into_iter()), midi_to_re.map(|x|x.into_iter())).iter().enumerate() .map(|(index, connect)|jack.midi_out(&format!("{index}/{}", name.as_ref()), &[connect.clone()])) .collect::>()?) } pub fn connect_audio_ins > ( jack: &Jack<'static>, name: &T, audio_from: &[T], audio_from_re: Option<&[T]>, ) -> Usually> { Ok(Connect::new( Some(audio_from.into_iter()), Some([].into_iter()), audio_from_re.map(|x|x.into_iter())).iter().enumerate() .map(|(index, connect)|jack.audio_in(&format!("{}/{index}", name.as_ref()), &[connect.clone()])) .collect::>()?) } pub fn connect_audio_outs > ( jack: &Jack<'static>, name: &T, audio_to: &[T], audio_to_re: Option<&[T]>, ) -> Usually> { Ok(Connect::new( Some(audio_to.into_iter()), Some([].into_iter()), audio_to_re.map(|x|x.into_iter())).iter().enumerate() .map(|(index, connect)|jack.audio_out(&format!("{index}/{}", name.as_ref()), &[connect.clone()])) .collect::>()?) } } #[cfg(feature = "play")] pub use self::task::*; #[cfg(feature = "play")] mod task { use std::{ time::Duration, sync::{Arc, atomic::{AtomicBool, Ordering::*}}, thread::{Builder, JoinHandle, sleep}, }; #[cfg(feature = "term")] use ::crossterm::event::poll; use crate::time::PerfModel; #[derive(Debug)] pub struct Task { /// Human-friendly name pub name: Arc, /// Exit flag. pub exit: Arc, /// Performance counter. pub perf: Arc, /// Use this to wait for the thread to finish. pub join: JoinHandle<()>, } impl Task { /// Spawn a TUI thread that runs `callt least one, then repeats until `exit`. pub fn new () + Send + Sync + 'static> ( name: Option>, exit: Arc, mut call: F ) -> Result { let perf = Arc::new(PerfModel::default()); let name: Arc = name.map(|x|x.as_ref().into()).unwrap_or_else(||"tengri".into()); Ok(Self { exit: exit.clone(), perf: perf.clone(), join: Builder::new().name(name.as_ref().into()).spawn(move || { #[cfg(feature = "prof")] profiling::register_thread!(); while !exit.fetch_and(true, Relaxed) { let _ = perf.cycle(&mut call); } })?.into(), name }) } /// Spawn a thread that runs `call` least one, then repeats /// until `exit`, sleeping for `time` msec after every iteration. pub fn new_sleep ( name: Option>, exit: Arc, time: Duration, mut call: F ) -> Result where F: FnMut(&PerfModel)->() + Send + Sync + 'static { Self::new(name, exit, move |perf| { let _ = call(perf); sleep(time); }) } /// Spawn a thread that uses [crossterm::event::poll] /// to run `call` every `time` msec. #[cfg(feature = "term")] pub fn new_poll ( name: Option>, exit: Arc, time: Duration, mut call: F ) -> Result where F: FnMut(&PerfModel)->() + Send + Sync + 'static { Self::new(name, exit, move |perf| { if poll(time).is_ok() { let _ = call(perf); } }) } pub fn join (self) -> Result<(), Box> { self.join.join() } } } #[cfg(feature = "draw")] pub use self::draw::*; #[cfg(feature = "draw")] mod draw { use crate::*; use Azimuth::*; use Split::*; /// Output target. See [Tui] for example implementation. pub trait Screen: Xy + Wh + Send + Sync + Sized { type Unit: Coord; /// Get current clipping area fn area (&self) -> XYWH; /// Set current clipping area fn clip ( &mut self, area: impl Into>>, draw: &impl Fn(&mut Self)->T ) -> T; /// Determine used area without drawing fn size <'a> ( &mut self, area: impl Into>>, draw: impl Draw ) -> Drawn; /// Draw fn draw <'a> ( &mut self, area: impl Into>>, draw: impl Draw ) -> Drawn; } /// Implement the [Draw] trait for a particular drawable and [Screen]. /// /// ``` /// use tengri::*; /// struct MyDrawable; /// impl_draw!(|self: MyDrawable, to: Tui|{ /// todo!("your draw logic") /// }); /// ``` #[macro_export] macro_rules! impl_draw ( ($(<$($T:ident: $Trait:path,)+>)?| $self:ident:$Self:path, $to:ident:$To:ty |$draw:block)=>{ impl$(<$($T:$Trait),+>)? Draw<$To> for $Self { fn draw (&$self, $to: &mut $To) -> Perhaps::Unit>> $draw } }; ($(<$($T:ident: $Trait:path,)+>)?| $self:ident:$Self:ty, $to:ident:$To:ty |$draw:block)=>{ impl$(<$($T:$Trait),+>)? Draw<$To> for $Self { fn draw (&$self, $to: &mut $To) -> Perhaps::Unit>> $draw } } ); /// Drawable that supports dynamic dispatch. /// /// Drawables are composable, e.g. the [when] and [either] conditionals /// or the layout constraints. /// /// Drawables are consumable, i.e. the [Draw::draw] method receives an /// owned `self` and does not return it, consuming the drawable. /// /// To draw a thing multiple times, instead of explicitly constructing it /// every time, implement the [View] trait instead, which will construct /// a [Draw]able. /// /// ``` /// use tengri::*; /// struct MyWidget(bool); /// impl Draw for MyWidget { /// fn draw (&self, to: &mut Tui) -> Perhaps> { /// todo!("your draw logic") /// } /// } /// ``` pub trait Draw { fn draw (&self, to: &mut S) -> Drawn; } ///// Emit a [Draw]able. ///// ///// Speculative. How to avoid conflicts with [Draw] proper? //pub trait View { //fn view (&self) -> impl Draw ; //} //impl View for () { //fn view (&self) -> impl Draw { //() //} //} ///// Return a [Draw]able. ///// ///// ``` ///// # use tengri::*; ///// let _ = view::(||"drawable"); ///// let _ = view::(||Some("drawable")); ///// ``` //pub const fn view Draw, F: Fn()->T> (view: F) -> impl View { //ViewThunk(view, PhantomData) //} ///// Because we can't implement [Draw] for `F: FnOnce...` without conflicts. //pub struct ViewThunk(pub F, std::marker::PhantomData); //impl Draw, F: Fn()->T> View for ViewThunk { //fn view (&self) -> impl Draw { //self.0() //} //} /// Because we can't implement [Draw] for `F: FnOnce...` without conflicts. pub struct DrawThunk(pub F, std::marker::PhantomData); implDrawn> Draw for DrawThunk { fn draw (&self, to: &mut T) -> Drawn { to.clip(None, &self.0) } } /// Basic [Draw]able closure. /// /// ``` /// # use tengri::*; /// let _ = draw(|to: &mut Tui|Ok(Some(to.area()))); // draws nothing /// ``` pub const fn draw <'a, T: Screen, F: Fn(&mut T)->PerhapsRef<'a, XYWH>> ( item: F ) -> DrawThunk { DrawThunk(item, std::marker::PhantomData) } pub type Drawn = Perhaps>; impl<'a, S: Screen> Draw for () { fn draw (&self, _: &mut S) -> Drawn { Ok(None) } } impl> Draw for Arc { fn draw (&self, to: &mut S) -> Drawn { (**self).draw(to) } } impl> Draw for Box { fn draw (&self, to: &mut S) -> Drawn { (**self).draw(to) } } impl> Draw for Option { fn draw (&self, to: &mut S) -> Drawn { self.as_ref().map(|it|it.draw(to)).transpose().map(Option::unwrap_or_default) } } //impl> Draw for RwLock { //fn draw (&self, __: &mut S) -> Drawn { //todo!() //} //} impl<'a, T: Screen, V: Draw> Draw for &V { fn draw (&self, to: &mut T) -> Drawn { (*self).draw(to) } } //impl> Draw for &V { //fn draw (&self, to: &mut T) -> Perhaps> { //self.view().draw(to) //} //} pub trait Xy { fn x (&self) -> N; fn y (&self) -> N; } pub trait Wh: Wide + Tall { fn wh (&self) -> [N;2]; } pub trait Xywh: Xy + Wh { fn xywh (&self) -> XYWH { XYWH(self.x(), self.y(), self.w(), self.h()) } } pub trait Wide: Xy { fn w (&self) -> N { N::zero() } fn w_min (&self) -> N { self.w() } fn w_max (&self) -> N { self.w() } } pub trait Tall { fn h (&self) -> N { N::zero() } fn h_min (&self) -> N { self.h() } fn h_max (&self) -> N { self.h() } } /// Point with size. /// /// ``` /// # use tengri::*; /// let xywh = XYWH(0u16, 0, 0, 0); /// assert_eq!(XYWH(10u16, 10, 20, 20).center(), (20, 20)); /// ``` /// /// * [ ] TODO: origin field (determines at which corner/side is X0 Y0) /// #[cfg_attr(test, derive(Arbitrary))] #[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct XYWH(pub N, pub N, pub N, pub N); impl Xy for XYWH { fn x (&self) -> N { self.0 } fn y (&self) -> N { self.1 } } impl Wide for XYWH { fn w (&self) -> N { self.2 } } impl Tall for XYWH { fn h (&self) -> N { self.3 } } impl XYWH { pub fn zero () -> Self { Self(0.into(), 0.into(), 0.into(), 0.into()) } pub fn center (&self) -> (N, N) { let Self(x, y, w, h) = *self; (x.plus(w/2.into()), y.plus(h/2.into())) } pub fn centered (&self) -> (N, N) { let Self(x, y, w, h) = *self; (x.minus(w/2.into()), y.minus(h/2.into())) } pub fn centered_x (&self, n: N) -> Self { let Self(x, y, w, h) = *self; let x_center = (x.plus(w / 2.into())).minus(n / 2.into()); let y_center = y.plus(h / 2.into()); XYWH(x_center, y_center, n, 1.into()) } pub fn centered_y (&self, n: N) -> Self { let Self(x, y, w, h) = *self; let x_center = x.plus(w / 2.into()); let y_corner = (y.plus(h / 2.into())).minus(n / 2.into()); XYWH(x_center, y_corner, 1.into(), n) } pub fn centered_xy (&self, [n, m]: [N;2]) -> Self { let Self(x, y, w, h) = *self; let x_center = (x.plus(w / 2.into())).minus(n / 2.into()); let y_corner = (y.plus(h / 2.into())).minus(m / 2.into()); XYWH(x_center, y_corner, n, m) } pub fn split_half (&self, direction: &Split) -> (Self, Self) { let XYWH(x, y, w, h) = self.xywh(); match direction { South => (XYWH(x, y, w, h - h / 2.into()), XYWH(x, y + h / 2.into(), w, h / 2.into())), East => (XYWH(x, y, w - w / 2.into(), h), XYWH(x + w / 2.into(), y, w / 2.into(), h)), North => (XYWH(x, y + h / 2.into(), w, h - h / 2.into()), XYWH(x, y, w, h / 2.into())), West => (XYWH(x + w / 2.into(), y, w - w / 2.into(), h), XYWH(x, y, w / 2.into(), h)), Above | Below => (XYWH(x, y, w, h), XYWH(x, y, w, h)) } } } impl From<&ratatui::prelude::Rect> for XYWH { fn from (rect: &ratatui::prelude::Rect) -> Self { Self(rect.x, rect.y, rect.width, rect.height) } } impl + Tall> Wh for T { fn wh (&self) -> [N;2] { [self.w(), self.h()] } } impl + Wh> Xywh for T {} impl> Lrtb for T {} pub trait Lrtb: Xywh { fn lrtb (&self) -> [N;4] { // FIXME: factor origin [self.x(), self.y(), self.x()+self.w(), self.y()+self.h()] } fn iter_x (&self) -> std::ops::Range where Self: HasOrigin { self.x_west()..self.x_east() } fn x_west (&self) -> N where Self: HasOrigin { let w = self.w(); let a = self.origin(); let d = match a { NW|W|SW => 0.into(), N|X|C|Y|S => w/2.into(), NE|E|SE => w }; self.x().minus(d) } fn x_east (&self) -> N where Self: HasOrigin { let w = self.w(); let a = self.origin(); let d = match a { NW|W|SW => w, N|X|C|Y|S => w/2.into(), NE|E|SE => 0.into() }; self.x().plus(d) } fn x_center (&self) -> N where Self: HasOrigin { todo!() } fn iter_y (&self) -> std::ops::Range where Self: HasOrigin { self.y_north()..self.y_south() } fn y_north (&self) -> N where Self: HasOrigin { let a = self.origin(); let h = self.h(); let d = match a { NW|N|NE => 0.into(), W|X|C|Y|E => h/2.into(), SW|S|SE => h }; self.y().minus(d) } fn y_south (&self) -> N where Self: HasOrigin { let a = self.origin(); let h = self.h(); let d = match a { NW|N|NE => h, W|X|C|Y|E => h/2.into(), SW|S|SE => 0.into() }; self.y().plus(d) } fn y_center (&self) -> N where Self: HasOrigin { todo!() } } } #[cfg(feature = "draw")] pub use self::coord::*; #[cfg(feature = "draw")] mod coord { use crate::*; /// A numeric type that can be used as coordinate. /// /// FIXME: Replace with `num` crate? /// FIXME: Use AsRef/AsMut? /// /// ``` /// use tengri::*; /// let a: u16 = Coord::zero(); /// let b: u16 = a.plus(1); /// let c: u16 = a.minus(2); /// let d = a.atomic(); /// ``` pub trait Coord: Send + Sync + Copy + Add + Sub + Mul + Div + Ord + PartialEq + Eq + Debug + Display + Default + From + Into + Into + Into //+ std::iter::Step { /// Zero in own type. fn zero () -> Self { 0.into() } /// Addition. fn plus (self, other: Self) -> Self; /// Saturating subtraction. fn minus (self, other: Self) -> Self { if self >= other { self - other } else { 0.into() } } /// Convert to [AtomicUsize]. fn atomic (self) -> AtomicUsize { AtomicUsize::new(self.into()) } } /// TUI works in u16 coordinates. impl Coord for u16 { fn plus (self, other: Self) -> Self { self.saturating_add(other) } } } #[cfg(feature = "draw")] pub use self::layout::*; #[cfg(feature = "draw")] mod layout; #[cfg(feature = "draw")] pub use self::color::*; #[cfg(feature = "draw")] mod color { use crate::*; use dizzle::LanguageError::*; use ::ratatui::style::Color; use ::rand::distributions::uniform::UniformSampler; pub(crate) use ::palette::{ Okhsl, Srgb, OklabHue, Mix, okhsl::UniformOkhsl, convert::{FromColor, FromColorUnclamped} }; pub fn rgb (r: u8, g: u8, b: u8) -> ItemColor { let term = Color::Rgb(r, g, b); ItemColor { okhsl: rgb_to_okhsl(term), term } } pub fn g (g: u8) -> Color { Color::Rgb(g, g, g) } pub fn okhsl_to_rgb (color: Okhsl) -> Color { let Srgb { red, green, blue, .. }: Srgb = Srgb::from_color_unclamped(color); Color::Rgb((red * 255.0) as u8, (green * 255.0) as u8, (blue * 255.0) as u8,) } pub fn rgb_to_okhsl (color: Color) -> Okhsl { if let Color::Rgb(r, g, b) = color { Okhsl::from_color(Srgb::new(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0)) } else { unreachable!("only Color::Rgb is supported") } } pub trait HasColor { fn color (&self) -> ItemColor; } #[macro_export] macro_rules! has_color { (|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => { impl $(<$($L),*$($T $(: $U)?),*>)? HasColor for $Struct $(<$($L),*$($T),*>)? { fn color (&$self) -> ItemColor { $cb } } } } #[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct ItemColor { pub term: Color, pub okhsl: Okhsl } impl_from!(ItemColor: |term: Color| Self { term, okhsl: rgb_to_okhsl(term) }); impl_from!(ItemColor: |okhsl: Okhsl| Self { okhsl, term: okhsl_to_rgb(okhsl) }); // A single color within item theme parameters, in OKHSL and RGB representations. impl ItemColor { #[cfg(feature = "term")] pub const fn from_tui (term: Color) -> Self { Self { term, okhsl: Okhsl::new_const(OklabHue::new(0.0), 0.0, 0.0) } } pub fn random () -> Self { let mut rng = ::rand::thread_rng(); let lo = Okhsl::new(-180.0, 0.01, 0.25); let hi = Okhsl::new( 180.0, 0.9, 0.5); UniformOkhsl::new(lo, hi).sample(&mut rng).into() } pub fn random_dark () -> Self { let mut rng = ::rand::thread_rng(); let lo = Okhsl::new(-180.0, 0.025, 0.075); let hi = Okhsl::new( 180.0, 0.5, 0.150); UniformOkhsl::new(lo, hi).sample(&mut rng).into() } pub fn random_near (color: Self, distance: f32) -> Self { color.mix(Self::random(), distance) } pub fn mix (&self, other: Self, distance: f32) -> Self { if distance > 1.0 { panic!("color mixing takes distance between 0.0 and 1.0"); } self.okhsl.mix(other.okhsl, distance).into() } } #[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct ItemTheme { pub base: ItemColor, pub light: ItemColor, pub lighter: ItemColor, pub lightest: ItemColor, pub dark: ItemColor, pub darker: ItemColor, pub darkest: ItemColor, } impl_from!(ItemTheme: |base: ItemColor| Self::from_item_color(base)); impl_from!(ItemTheme: |base: Color| Self::from_tui_color(base)); impl ItemTheme { #[cfg(feature = "term")] pub const G: [Self;256] = { let mut builder = ::konst::array::ArrayBuilder::new(); while !builder.is_full() { let index = builder.len() as u8; let light = (index as f64 * 1.15) as u8; let lighter = (index as f64 * 1.7) as u8; let lightest = (index as f64 * 1.85) as u8; let dark = (index as f64 * 0.9) as u8; let darker = (index as f64 * 0.6) as u8; let darkest = (index as f64 * 0.3) as u8; builder.push(ItemTheme { base: ItemColor::from_tui(Color::Rgb(index, index, index )), light: ItemColor::from_tui(Color::Rgb(light, light, light, )), lighter: ItemColor::from_tui(Color::Rgb(lighter, lighter, lighter, )), lightest: ItemColor::from_tui(Color::Rgb(lightest, lightest, lightest, )), dark: ItemColor::from_tui(Color::Rgb(dark, dark, dark, )), darker: ItemColor::from_tui(Color::Rgb(darker, darker, darker, )), darkest: ItemColor::from_tui(Color::Rgb(darkest, darkest, darkest, )), }); } builder.build() }; pub fn random () -> Self { ItemColor::random().into() } pub fn random_near (color: Self, distance: f32) -> Self { color.base.mix(ItemColor::random(), distance).into() } pub const G00: Self = { let color: ItemColor = ItemColor { okhsl: Okhsl { hue: OklabHue::new(0.0), lightness: 0.0, saturation: 0.0 }, term: Color::Rgb(0, 0, 0) }; Self { base: color, light: color, lighter: color, lightest: color, dark: color, darker: color, darkest: color, } }; #[cfg(feature = "term")] pub fn from_tui_color (base: Color) -> Self { Self::from_item_color(ItemColor::from_tui(base)) } pub fn from_item_color (base: ItemColor) -> Self { let mut light = base.okhsl; light.lightness = (light.lightness * 1.3).min(1.0); let mut lighter = light; lighter.lightness = (lighter.lightness * 1.3).min(1.0); let mut lightest = base.okhsl; lightest.lightness = 0.95; let mut dark = base.okhsl; dark.lightness = (dark.lightness * 0.75).max(0.0); dark.saturation = (dark.saturation * 0.75).max(0.0); let mut darker = dark; darker.lightness = (darker.lightness * 0.66).max(0.0); darker.saturation = (darker.saturation * 0.66).max(0.0); let mut darkest = darker; darkest.lightness = 0.1; darkest.saturation = (darkest.saturation * 0.50).max(0.0); Self { base, light: light.into(), lighter: lighter.into(), lightest: lightest.into(), dark: dark.into(), darker: darker.into(), darkest: darkest.into(), } } } pub trait ColorDsl<'a, T>: Sized { fn new_g (expr: &'a T, try_to_u8: impl Fn(PerhapsRef<'a, &'a str>)->PerhapsRef<'a, u8>) -> UsuallyRef<'a, Self>; fn new_rgb (expr: &'a T, try_to_u8: impl Fn(PerhapsRef<'a, &str>)->PerhapsRef<'a, u8>) -> UsuallyRef<'a, Self>; } impl<'a, T: Language + 'a> ColorDsl<'a, T> for Color { fn new_g (expr: &'a T, try_to_u8: impl Fn(PerhapsRef<'a, &'a str>)->PerhapsRef<'a, u8>) -> Result> { let n = try_to_u8(expr.tail().map_err(Into::into))?.ok_or(LanguageError::domain("not gray"))?; Ok(Self::Rgb(n, n, n)) } fn new_rgb (expr: &'a T, try_to_u8: impl Fn(PerhapsRef<'a, &str>)->PerhapsRef<'a, u8>) -> Result> { let r = try_to_u8(expr.nth(1).map_err(Into::into))?.ok_or(LanguageError::domain("not red"))?; let g = try_to_u8(expr.nth(2).map_err(Into::into))?.ok_or(LanguageError::domain("not green"))?; let b = try_to_u8(expr.nth(3).map_err(Into::into))?.ok_or(LanguageError::domain("not blue"))?; Ok(Color::Rgb(r, g, b)) } } } #[cfg(feature = "text")] pub use self::text::*; #[cfg(feature = "text")] mod text { #![allow(unused)] pub(crate) use ::unicode_width::*; /// Displays an owned [str]-like with fixed maximum width. /// /// Width is computed using [unicode_width]. pub struct TrimString>(pub u16, pub T); impl> AsRef for TrimString { fn as_ref (&self) -> &str { self.1.as_ref() } } impl<'a, T: AsRef> TrimString { fn to_ref (&self) -> TrimStr<'_, T> { TrimStr(self.0, &self.1) } } /// Displays a borrowed [str]-like with fixed maximum width /// /// Width is computed using [unicode_width]. pub struct TrimStr<'a, T: AsRef>(pub u16, pub &'a T); impl> AsRef for TrimStr<'_, T> { fn as_ref (&self) -> &str { self.1.as_ref() } } pub(crate) fn width_chars_max (max: u16, text: impl AsRef) -> u16 { let mut width: u16 = 0; let mut chars = text.as_ref().chars(); while let Some(c) = chars.next() { width += c.width().unwrap_or(0) as u16; if width >= max { break } } return width } /// Trim string with [unicode_width]. pub fn trim_string (max_width: usize, input: impl AsRef) -> String { let input = input.as_ref(); let mut output = Vec::with_capacity(input.len()); let mut width: usize = 1; let mut chars = input.chars(); while let Some(c) = chars.next() { if width > max_width { break } output.push(c); width += c.width().unwrap_or(0); } return output.into_iter().collect() } } #[cfg(feature = "eval")] pub use self::eval::*; #[cfg(feature = "eval")] mod eval { use crate::*; /// ``` /// # use ::tengri::{*, dizzle::*, ratatui::prelude::Color}; /// /// #[namespace(bool)] /// #[namespace(u8)] /// #[namespace(u16)] /// #[namespace(Option)] /// #[namespace(Color get_color)] /// struct App; /// /// fn get_color (state: &App, src: impl Language) -> Perhaps { /// Ok(if let Some(color) = Tui::eval_color_expr(state, &src)? { /// Some(color) /// } else if let Some(sym) = src.word()? { /// Some(match sym { /// ":color/bg" => Color::Rgb(28, 32, 36), /// ":color/fg" => Color::Rgb(98, 92, 96), /// _ => return Err(format!("not a color: {sym}").into()) /// }) /// } else { /// return Err(format!("not a color: {:?}", src.src()?).into()) /// }) /// } /// /// impl Interpret>> for App { /// fn interpret_expr <'a> (&'a self, to: &mut Tui, lang: &'a impl Language) /// -> Usually>> /// { /// self.keyword(to, lang) /// } /// } /// /// impl Keywords> for App { /// fn keywords () -> impl Iterator Perhaps>> { /// [ /// kw_when, kw_either, kw_split, kw_align, /// kw_exact, kw_min, kw_max, kw_push, /// kw_tui_text, kw_tui_fg, kw_tui_bg /// ].into_iter() /// } /// } /// /// # fn main () -> tengri::Usually<()> { /// let state = App; /// let mut out = Tui::new(80, 25); /// state.interpret_expr(&mut out, &"")?; /// state.interpret_expr(&mut out, &"text Hello world!")?; /// state.interpret_expr(&mut out, &"fg (g 0) (text Hello world!)")?; /// state.interpret_expr(&mut out, &"bg (g 2) (text Hello world!)")?; /// state.interpret_expr(&mut out, &"(bg (g 3) (fg (g 4) (text Hello world!)))")?; /// # Ok(()) } /// ``` pub trait Keywords : 'static { fn keywords <'a> () -> impl Iterator PerhapsRef<'a, V>>; fn keyword <'a> (&self, to: &mut U, expr: &'a (impl Language + ?Sized)) -> PerhapsRef<'a, V> { if let Some(expr) = expr.src()? { for keyword in Self::keywords() { if let Some(result) = keyword(self, to, &expr)? { return Ok(Some(result)) } } } Ok(None) } } #[macro_export] macro_rules! impl_keywords { ($T:ty, $U:ty, $V:ty [ $($kw:ident),* ]) => { impl Keywords<$T, $U> for $V { fn keywords <'a> () -> impl Iterator PerhapsRef<'a, $U>> { [ $($kw),* ].into_iter() } } } } } #[cfg(feature = "term")] pub use self::term::*; #[cfg(feature = "term")] mod term;