diff --git a/src/layout/area.rs b/src/layout/area.rs index 11647eb..89d9e4d 100644 --- a/src/layout/area.rs +++ b/src/layout/area.rs @@ -19,6 +19,7 @@ pub struct Area( impl<'a, S: Screen, T: Draw> Draw for Area { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("area_draw"); to.draw(self.0, &self.1) } } diff --git a/src/layout/axis.rs b/src/layout/axis.rs index 37c98df..0ea9cfa 100644 --- a/src/layout/axis.rs +++ b/src/layout/axis.rs @@ -297,6 +297,7 @@ where impl <'a, S: Screen, I: Draw> Draw for Full<'a, S, I> { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("full_draw"); let XYWH(x0, y0, w0, h0) = to.area(); let item = match self { Self::W(i) => i, Self::H(i) => i, Self::WH(i) => i, _ => unreachable!() @@ -315,6 +316,7 @@ impl <'a, S: Screen, I: Draw> Draw for Full<'a, S, I> { impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Exact<'a, S, I, X> { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("exact_draw"); let XYWH(x0, y0, w0, h0) = to.area(); let (item, w1, h1) = match self { Self::W(item, w1) => (item, (*w1).into(), None), @@ -337,6 +339,7 @@ impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Ex impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Min<'a, S, I, X> { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("min_draw"); let XYWH(x0, y0, w0, h0) = to.area(); let (item, w1, h1) = match self { Self::W(item, w1) => (item, (*w1).into(), None), @@ -358,6 +361,7 @@ impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Mi impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Max<'a, S, I, X> { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("max_draw"); let XYWH(x, y, w0, h0) = to.area(); let (item, w, h) = match self { Self::W(item, w) => (item, (*w).into().unwrap_or(w0), h0), @@ -371,6 +375,7 @@ impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Ma impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Push<'a, S, I, X> { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("push_draw"); match self { Self::__(_) => unreachable!(), Self::X(item, x1) if let Some(XYWH(x, y, w, h)) = to.size(None, item)? => { @@ -389,6 +394,7 @@ impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Pu impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Pull<'a, S, I, X> { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("pull_draw"); match self { Self::__(_) => unreachable!(), Self::X(item, x1) if let Some(XYWH(x, y, w, h)) = to.size(None, item)? => { @@ -413,6 +419,7 @@ impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Pu impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Pad<'a, S, I, X> { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("pad_draw"); let XYWH(x, y, w0, h0) = to.area(); let (item, w, h) = match self { Self::X(item, w) => (item, (*w).into().unwrap_or_default(), Default::default()), diff --git a/src/layout/azimuth.rs b/src/layout/azimuth.rs index 9433ed3..53c8853 100644 --- a/src/layout/azimuth.rs +++ b/src/layout/azimuth.rs @@ -116,16 +116,17 @@ pub trait CanAlign<'a, S: Screen>: Draw + Sized { } pub struct Align( - pub(crate) Option, - pub(crate) T, + pub Option, + pub T, ); impl<'a, S: Screen, T: Draw> Draw for Align { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("align_draw"); let Self(azimuth, item) = self; let area0 = to.area(); let size = to.size(area0, item)?; - let area1 = align::(area0, size, *azimuth); + let area1 = aligned::(area0, size, *azimuth); //println!("\n\r{azimuth:?} {area0:?} {size:?}=>{area1:?}"); Ok(if let Some(area) = area1 { to.draw(area, &item)? @@ -135,9 +136,10 @@ impl<'a, S: Screen, T: Draw> Draw for Align { } } -fn align ( +fn aligned ( area0: XYWH, area: Option>, azimuth: Option ) -> Option> { + #[cfg(feature = "prof")] profiling::scope!("align"); area.map(|XYWH(x, y, w, h)|{ let XYWH(x0, y0, w0, h0) = area0; match azimuth { @@ -158,6 +160,7 @@ fn align ( } fn_kw_layout!(kw_split |state, output, expr| { + #[cfg(feature = "prof")] profiling::scope!("kw_split"); let thunk_a = draw(move|screen|ok_flat(expr.nth(1)?.map(|x|state.interpret(screen, x)))); let thunk_b = draw(move|screen|ok_flat(expr.nth(2)?.map(|x|state.interpret(screen, x)))); Ok(match expr.head()? { @@ -205,6 +208,7 @@ pub fn split <'a, S: Screen, A: Draw, B: Draw> ( impl<'a, S: Screen, A: Draw, B: Draw> Draw for Pair { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("pair_draw"); let Self(split, a, b, ..) = self; let (area_a, area_b) = stack_areas(split, to, a, b)?; let (drawn_a, drawn_b) = draw_stacks(split, to, a, area_a, None, b, area_b, None)?; @@ -222,6 +226,7 @@ fn draw_stacks <'a, S: Screen> ( area_b: impl Into>>, origin_b: impl Into>, ) -> UsuallyRef<'a, (Option>, Option>)> { + #[cfg(feature = "prof")] profiling::scope!("draw_stacks"); let draw_a = |to: &mut S|Ok::<_, Box>(match origin_a.into() { Some(origin_a) => to.draw(area_a.into(), a.align(origin_a))?, None => to.draw(area_a.into(), a)? @@ -303,6 +308,7 @@ fn stack_drawn ( drawn_a: Option>, drawn_b: Option>, ) -> Option> { + #[cfg(feature = "prof")] profiling::scope!("stack_drawn"); if let (Some(XYWH(xa, ya, wa, ha)), Some(XYWH(xb, yb, wb, hb))) = (drawn_a, drawn_b) { match split { Split::South => Some(XYWH(xa.min(xb), ya, wa.max(wb), ha + hb)), diff --git a/src/layout/cond.rs b/src/layout/cond.rs index 9dab1d1..496f575 100644 --- a/src/layout/cond.rs +++ b/src/layout/cond.rs @@ -1,11 +1,28 @@ use crate::*; -fn_kw_layout!(kw_when |state, output, expr| { - let thunk = draw(move|screen|ok_flat(expr.nth(2)?.map(|x|state.interpret(screen, x)))); - ok_flat(matches!(expr.head()?, Some("when")).then(||{ - when(state.namespace(&expr.nth(1)?)?.unwrap(), thunk).draw(output) - })) -}); +pub struct When(pub bool, pub T); + +impl> Draw for When { + fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("when"); + if self.0 { + self.1.draw(to) + } else { + Ok(Default::default()) + } + } +} + +impl When { + + fn_kw_layout!(interpret |state, output, expr| { + #[cfg(feature = "prof")] profiling::scope!("kw_when"); + let thunk = draw(move|screen|ok_flat(expr.nth(2)?.map(|x|state.interpret(screen, x)))); + ok_flat(matches!(expr.head()?, Some("when")).then(||{ + when(state.namespace(&expr.nth(1)?)?.unwrap(), thunk).draw(output) + })) + }); +} /// Only render when condition is true. /// @@ -15,10 +32,11 @@ fn_kw_layout!(kw_when |state, output, expr| { /// # } /// ``` pub const fn when <'a, T: Screen> (condition: bool, item: impl Draw) -> impl Draw { - draw(move|to: &mut T|if condition { item.draw(to) } else { Ok(Default::default()) }) + When(condition, item) } fn_kw_layout!(kw_either |state, output, expr| { + #[cfg(feature = "prof")] profiling::scope!("kw_layout"); let thunk_a = draw(move|screen|ok_flat(expr.nth(2)?.map(|x|state.interpret(screen, x)))); let thunk_b = draw(move|screen|ok_flat(expr.nth(3)?.map(|x|state.interpret(screen, x)))); ok_flat(matches!(expr.head()?, Some("either")).then(||{ @@ -36,5 +54,8 @@ fn_kw_layout!(kw_either |state, output, expr| { pub const fn either <'a, T: Screen> ( condition: bool, a: impl Draw, b: impl Draw ) -> impl Draw { - draw(move|to: &mut T|if condition { a.draw(to) } else { b.draw(to) }) + draw(move|to: &mut T|{ + #[cfg(feature = "prof")] profiling::scope!("either"); + if condition { a.draw(to) } else { b.draw(to) } + }) } diff --git a/src/lib.rs b/src/lib.rs index e4efc8d..d68e831 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,9 +68,32 @@ mod deps; pub use self::deps::*; } /// Implement [`Debug`] in bulk. -#[macro_export] macro_rules! impl_debug (($($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 })* -}); +#[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 ( @@ -280,7 +303,7 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } } /// Run something with the client. pub fn with_client (&self, op: impl FnOnce(&Client)->T) -> T { - match &*self.0.read().unwrap() { + match &*self.0.try_read().unwrap() { Inert => panic!("jack client not activated"), Inactive(client) => op(client), Activating => panic!("jack client has not finished activation"), @@ -293,14 +316,14 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } 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.write().unwrap(), &mut state); + 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.write().unwrap()).handle(event) + 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] @@ -308,14 +331,14 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } // 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 Ok(mut app) = app.write() { + 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.write().unwrap() = Active( + *client_state.try_write().unwrap() = Active( client.activate_async(notify, process)? ); } else { @@ -445,7 +468,7 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } fn callback ( state: &Arc>, client: &Client, scope: &ProcessScope ) -> Control where Self: Sized { - if let Ok(mut state) = state.write() { + if let Some(mut state) = state.try_write() { state.process(client, scope) } else { Control::Quit @@ -612,10 +635,10 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } for connect in self.connections().iter() { match &connect.name { Some(Exact(name)) => { - *connect.status.write().unwrap() = self.connect_exact(name)?; + *connect.status.try_write().unwrap() = self.connect_exact(name)?; }, Some(RegExp(re)) => { - *connect.status.write().unwrap() = self.connect_regexp(re, connect.scope)?; + *connect.status.try_write().unwrap() = self.connect_regexp(re, connect.scope)?; }, _ => {}, }; @@ -922,7 +945,7 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } 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.write().unwrap(), message); + update_keys(&mut*self.held.try_write().unwrap(), message); } } /// Write a chunk of MIDI data from the output buffer to the output port. @@ -1079,7 +1102,7 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } pub fn info (&self) -> Arc { format!(" ({}) {} {}", { - let status = self.status.read().unwrap(); + let status = self.status.try_read().unwrap(); let mut ok = 0; for (_, _, state) in status.iter() { if *state == Connected { @@ -1167,6 +1190,8 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } use crate::time::PerfModel; #[derive(Debug)] pub struct Task { + /// Human-friendly name + pub name: Arc, /// Exit flag. pub exit: Arc, /// Performance counter. @@ -1177,30 +1202,37 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } impl Task { /// Spawn a TUI thread that runs `callt least one, then repeats until `exit`. - pub fn new (exit: Arc, mut call: F) -> Result - where F: FnMut(&PerfModel)->() + Send + Sync + 'static - { + 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("tengri task".into()).spawn(move || { + 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() + })?.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 ( - exit: Arc, time: Duration, mut call: F + name: Option>, + exit: Arc, + time: Duration, + mut call: F ) -> Result where F: FnMut(&PerfModel)->() + Send + Sync + 'static { - Self::new(exit, move |perf| { + Self::new(name, exit, move |perf| { let _ = call(perf); sleep(time); }) @@ -1209,11 +1241,14 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } /// Spawn a thread that uses [crossterm::event::poll] /// to run `call` every `time` msec. #[cfg(feature = "term")] pub fn new_poll ( - exit: Arc, time: Duration, mut call: F + name: Option>, + exit: Arc, + time: Duration, + mut call: F ) -> Result where F: FnMut(&PerfModel)->() + Send + Sync + 'static { - Self::new(exit, move |perf| { + Self::new(name, exit, move |perf| { if poll(time).is_ok() { let _ = call(perf); } @@ -1366,7 +1401,19 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } } } - impl<'a, S: Screen, D: Draw> Draw for Option { + 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) } @@ -1378,12 +1425,6 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } //} //} - //impl> Draw for Arc { - //fn draw (&self, __: &mut T) -> Perhaps> { - //todo!() - //} - //} - impl<'a, T: Screen, V: Draw> Draw for &V { fn draw (&self, to: &mut T) -> Drawn { (*self).draw(to) diff --git a/src/term.rs b/src/term.rs index f4b739b..8b19db3 100644 --- a/src/term.rs +++ b/src/term.rs @@ -162,11 +162,11 @@ impl Tui { ) -> Result { let exited = exited.clone(); let state = state.clone(); - Task::new_poll(exited.clone(), poll, move |_| { + Task::new_poll(Some("tengri input"), exited.clone(), poll, move |_| { let event = read().unwrap(); if Exit::is(&event) { exited.store(true, Relaxed); - } else if let Err(e) = state.write().unwrap().apply(&TuiEvent(event)) { + } else if let Err(e) = state.write().apply(&TuiEvent(event)) { panic!("{e}") } }) @@ -268,15 +268,15 @@ impl Tui { let Size { width, height } = backend.size().expect("get size failed"); let mut prev = Tui::new(width, height); let mut next = Tui::new(width, height); - Ok(Task::new_sleep(exited.clone(), sleep, move |perf| { + Ok(Task::new_sleep(Some("tengri output"), exited.clone(), sleep, move |perf| { let Size { width, height } = backend.size().expect("get size failed"); - if let Ok(state) = state.try_read() { + if let Some(state) = state.try_read() { prev.resize(&mut backend, width, height); state.draw(&mut next).expect("draw failed"); // TODO draw error prev.redraw(&mut backend, &mut next); } - let timer = format!("{:>3.3}ms", perf.used.load(Relaxed)); - prev.blit(&timer, 0, 0, Some(Style::default())); + //let timer = format!("{:>3.3}ms", perf.used.load(Relaxed)); + //prev.blit(&timer, 0, 0, Some(Style::default())); })?) } /// Draw TUI content or its error message.