wip: refactored core

This commit is contained in:
🪞👃🪞 2024-09-04 23:51:35 +03:00
parent 461c60d6b3
commit c033a5618b
9 changed files with 75 additions and 67 deletions

View file

@ -1,11 +1,11 @@
use crate::*; use crate::*;
pub enum Collected<'a, T, U> { pub enum Collected<'a, T, U> {
Box(Box<dyn Render<'a, T, U> + 'a>), Box(Box<dyn Render<T, U> + 'a>),
Ref(&'a (dyn Render<'a, T, U> + 'a)), Ref(&'a (dyn Render<T, U> + 'a)),
} }
impl<'a, T, U> Render<'a, T, U> for Collected<'a, T, U> { impl<'a, T, U> Render<T, U> for Collected<'a, T, U> {
fn render (&self, to: &'a mut T) -> Perhaps<U> { fn render (&self, to: &mut T) -> Perhaps<U> {
match self { match self {
Self::Box(item) => (*item).render(to), Self::Box(item) => (*item).render(to),
Self::Ref(item) => (*item).render(to), Self::Ref(item) => (*item).render(to),
@ -21,20 +21,20 @@ impl<'a, T, U> Collection<'a, T, U> {
} }
} }
pub trait Collect<'a, T, U> { pub trait Collect<'a, T, U> {
fn add_box (self, item: Box<dyn Render<'a, T, U> + 'a>) -> Self; fn add_box (self, item: Box<dyn Render<T, U> + 'a>) -> Self;
fn add_ref (self, item: &'a dyn Render<'a, T, U>) -> Self; fn add_ref (self, item: &'a dyn Render<T, U>) -> Self;
fn add <R: Render<'a, T, U> + Sized + 'a> (self, item: R) -> Self fn add <R: Render<T, U> + Sized + 'a> (self, item: R) -> Self
where Self: Sized where Self: Sized
{ {
self.add_box(Box::new(item)) self.add_box(Box::new(item))
} }
} }
impl<'a, T, U> Collect<'a, T, U> for Collection<'a, T, U> { impl<'a, T, U> Collect<'a, T, U> for Collection<'a, T, U> {
fn add_box (mut self, item: Box<dyn Render<'a, T, U> + 'a>) -> Self { fn add_box (mut self, item: Box<dyn Render<T, U> + 'a>) -> Self {
self.0.push(Collected::Box(item)); self.0.push(Collected::Box(item));
self self
} }
fn add_ref (mut self, item: &'a dyn Render<'a, T, U>) -> Self { fn add_ref (mut self, item: &'a dyn Render<T, U>) -> Self {
self.0.push(Collected::Ref(item)); self.0.push(Collected::Ref(item));
self self
} }

View file

@ -2,17 +2,17 @@ use crate::*;
/// Handle input /// Handle input
pub trait Handle<T, U>: Send + Sync { pub trait Handle<T, U>: Send + Sync {
fn handle (&mut self, context: &mut T) -> Perhaps<U>; fn handle (&mut self, context: &T) -> Perhaps<U>;
} }
impl<H, T, U> Handle<T, U> for &mut H where H: Handle<T, U> { impl<H, T, U> Handle<T, U> for &mut H where H: Handle<T, U> {
fn handle (&mut self, context: &mut T) -> Perhaps<U> { fn handle (&mut self, context: &T) -> Perhaps<U> {
(*self).handle(context) (*self).handle(context)
} }
} }
impl<H, T, U> Handle<T, U> for Option<H> where H: Handle<T, U> { impl<H, T, U> Handle<T, U> for Option<H> where H: Handle<T, U> {
fn handle (&mut self, context: &mut T) -> Perhaps<U> { fn handle (&mut self, context: &T) -> Perhaps<U> {
if let Some(ref mut handle) = self { if let Some(ref mut handle) = self {
handle.handle(context) handle.handle(context)
} else { } else {
@ -22,25 +22,25 @@ impl<H, T, U> Handle<T, U> for Option<H> where H: Handle<T, U> {
} }
impl<H, T, U> Handle<T, U> for Mutex<H> where H: Handle<T, U> { impl<H, T, U> Handle<T, U> for Mutex<H> where H: Handle<T, U> {
fn handle (&mut self, context: &mut T) -> Perhaps<U> { fn handle (&mut self, context: &T) -> Perhaps<U> {
self.lock().unwrap().handle(context) self.lock().unwrap().handle(context)
} }
} }
impl<H, T, U> Handle<T, U> for Arc<Mutex<H>> where H: Handle<T, U> { impl<H, T, U> Handle<T, U> for Arc<Mutex<H>> where H: Handle<T, U> {
fn handle (&mut self, context: &mut T) -> Perhaps<U> { fn handle (&mut self, context: &T) -> Perhaps<U> {
self.lock().unwrap().handle(context) self.lock().unwrap().handle(context)
} }
} }
impl<H, T, U> Handle<T, U> for RwLock<H> where H: Handle<T, U> { impl<H, T, U> Handle<T, U> for RwLock<H> where H: Handle<T, U> {
fn handle (&mut self, context: &mut T) -> Perhaps<U> { fn handle (&mut self, context: &T) -> Perhaps<U> {
self.write().unwrap().handle(context) self.write().unwrap().handle(context)
} }
} }
impl<H, T, U> Handle<T, U> for Arc<RwLock<H>> where H: Handle<T, U> { impl<H, T, U> Handle<T, U> for Arc<RwLock<H>> where H: Handle<T, U> {
fn handle (&mut self, context: &mut T) -> Perhaps<U> { fn handle (&mut self, context: &T) -> Perhaps<U> {
self.write().unwrap().handle(context) self.write().unwrap().handle(context)
} }
} }

View file

@ -1,18 +1,18 @@
use crate::{*, jack::*}; use crate::{*, jack::*};
/// A UI component that may be associated with a JACK client by the `Jack` factory. /// A UI component that may be associated with a JACK client by the `Jack` factory.
pub trait Device<'a, T, U>: Render<'a, T, U> + Handle + Process + Send + Sync { pub trait Device<E: Engine>: Component<E> + Process {
/// Perform type erasure for collecting heterogeneous devices. /// Perform type erasure for collecting heterogeneous devices.
fn boxed (self) -> Box<dyn Device<'a, T, U>> where Self: Sized + 'static { fn boxed (self) -> Box<dyn Device<E>> where Self: Sized + 'static {
Box::new(self) Box::new(self)
} }
} }
/// All things that implement the required traits can be treated as `Device`. /// All things that implement the required traits can be treated as `Device`.
impl<'a, D, T, U> Device<'a, T, U> for D where D: Render<'a, T, U> + Handle + Process {} impl<D, E: Engine> Device<E> for D where D: Component<E> + Process {}
impl<'a, T, U> Render<'a, T, U> for Box<dyn Device<'a, T, U>> { impl<E: Engine> Render<E, E::Rendered> for Box<dyn Device<E>> {
fn render (&self, to: &'a mut T) -> Result<Option<U>, Box<dyn std::error::Error>> { fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
(**self).render(to) (**self).render(to)
} }
} }
@ -98,8 +98,8 @@ pub trait Process {
} }
/// Just run thing with JACK. Returns the activated client. /// Just run thing with JACK. Returns the activated client.
pub fn jack_run <T> (name: &str, app: &Arc<RwLock<T>>) -> Usually<DynamicAsyncClient> pub fn jack_run <T, E: Engine> (name: &str, app: &Arc<RwLock<T>>) -> Usually<DynamicAsyncClient>
where T: Handle + Process + Send + Sync + 'static where T: Handle<E, E::Handled> + Process + Send + Sync + 'static
{ {
let options = ClientOptions::NO_START_SERVER; let options = ClientOptions::NO_START_SERVER;
let (client, _status) = Client::new(name, options)?; let (client, _status) = Client::new(name, options)?;
@ -143,12 +143,11 @@ impl Jack {
)?.0, )?.0,
}) })
} }
pub fn run <'a: 'static, D, T, U> ( pub fn run <'a: 'static, D, E> (
self, state: impl FnOnce(JackPorts)->Box<D> self, state: impl FnOnce(JackPorts)->Box<D>
) -> Usually<JackDevice<'a, T, U>> ) -> Usually<JackDevice<E>>
where D: Device<'a, T, U> + Process + Sized + 'static, where D: Device<E> + Sized + 'static,
T: 'static, E: Engine + 'static,
U: 'static
{ {
let owned_ports = JackPorts { let owned_ports = JackPorts {
audio_ins: register_ports(&self.client, self.audio_ins, AudioIn)?, audio_ins: register_ports(&self.client, self.audio_ins, AudioIn)?,
@ -164,7 +163,7 @@ impl Jack {
.map(|p|Ok(p.name()?)).collect::<Usually<Vec<_>>>()?; .map(|p|Ok(p.name()?)).collect::<Usually<Vec<_>>>()?;
let audio_ins = owned_ports.audio_ins.values() let audio_ins = owned_ports.audio_ins.values()
.map(|p|Ok(p.name()?)).collect::<Usually<Vec<_>>>()?; .map(|p|Ok(p.name()?)).collect::<Usually<Vec<_>>>()?;
let state = Arc::new(RwLock::new(state(owned_ports) as Box<dyn Device<T, U>>)); let state = Arc::new(RwLock::new(state(owned_ports) as Box<dyn Device<E>>));
let client = self.client.activate_async( let client = self.client.activate_async(
Notifications(Box::new({ Notifications(Box::new({
let _state = state.clone(); let _state = state.clone();

View file

@ -1,31 +1,31 @@
use crate::{*, jack::*}; use crate::{*, jack::*};
/// A [Device] bound to a JACK client and a set of ports. /// A [Device] bound to a JACK client and a set of ports.
pub struct JackDevice<'a, T, U> { pub struct JackDevice<E: Engine> {
/// The active JACK client of this device. /// The active JACK client of this device.
pub client: DynamicAsyncClient, pub client: DynamicAsyncClient,
/// The device state, encapsulated for sharing between threads. /// The device state, encapsulated for sharing between threads.
pub state: Arc<RwLock<Box<dyn Device<'a, T, U>>>>, pub state: Arc<RwLock<Box<dyn Device<E>>>>,
/// Unowned copies of the device's JACK ports, for connecting to the device. /// Unowned copies of the device's JACK ports, for connecting to the device.
/// The "real" readable/writable `Port`s are owned by the `state`. /// The "real" readable/writable `Port`s are owned by the `state`.
pub ports: UnownedJackPorts, pub ports: UnownedJackPorts,
} }
impl<'a, T, U> std::fmt::Debug for JackDevice<'a, T, U> { impl<E: Engine> std::fmt::Debug for JackDevice<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("JackDevice").field("ports", &self.ports).finish() f.debug_struct("JackDevice").field("ports", &self.ports).finish()
} }
} }
impl<'a, T, U> Render<'a, T, U> for JackDevice<'a, T, U> { impl<E: Engine> Render<E, E::Rendered> for JackDevice<E> {
fn render (&self, to: &'a mut T) -> Perhaps<U> { fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
self.state.read().unwrap().render(to) self.state.read().unwrap().render(to)
} }
} }
impl<'a, T, U> Handle for JackDevice<'a, T, U> { impl<E: Engine> Handle<E, E::Handled> for JackDevice<E> {
fn handle (&mut self, event: &AppEvent) -> Usually<bool> { fn handle (&mut self, from: &E) -> Perhaps<E::Handled> {
self.state.write().unwrap().handle(event) self.state.write().unwrap().handle(from)
} }
} }
impl<'a, T, U> Ports for JackDevice<'a, T, U> { impl<E: Engine> Ports for JackDevice<E> {
fn audio_ins (&self) -> Usually<Vec<&Port<Unowned>>> { fn audio_ins (&self) -> Usually<Vec<&Port<Unowned>>> {
Ok(self.ports.audio_ins.values().collect()) Ok(self.ports.audio_ins.values().collect())
} }
@ -39,13 +39,13 @@ impl<'a, T, U> Ports for JackDevice<'a, T, U> {
Ok(self.ports.midi_outs.values().collect()) Ok(self.ports.midi_outs.values().collect())
} }
} }
impl<'a, T, U> JackDevice<'a, T, U> { impl<E: Engine> JackDevice<E> {
/// Returns a locked mutex of the state's contents. /// Returns a locked mutex of the state's contents.
pub fn state (&self) -> LockResult<RwLockReadGuard<Box<dyn Device<'a, T, U>>>> { pub fn state (&self) -> LockResult<RwLockReadGuard<Box<dyn Device<E>>>> {
self.state.read() self.state.read()
} }
/// Returns a locked mutex of the state's contents. /// Returns a locked mutex of the state's contents.
pub fn state_mut (&self) -> LockResult<RwLockWriteGuard<Box<dyn Device<'a, T, U>>>> { pub fn state_mut (&self) -> LockResult<RwLockWriteGuard<Box<dyn Device<E>>>> {
self.state.write() self.state.write()
} }
pub fn connect_midi_in (&self, index: usize, port: &Port<Unowned>) -> Usually<()> { pub fn connect_midi_in (&self, index: usize, port: &Port<Unowned>) -> Usually<()> {

View file

@ -27,6 +27,7 @@ use better_panic::{Settings, Verbosity};
} }
submod! { submod! {
collect
component component
edn edn
engine engine
@ -42,7 +43,6 @@ submod! {
render render
render_axis render_axis
render_buffer render_buffer
render_collect
render_layered render_layered
render_split render_split
time time

View file

@ -2,7 +2,7 @@ use crate::*;
/// Render to output. /// Render to output.
pub trait Render<T, U>: Send + Sync { pub trait Render<T, U>: Send + Sync {
fn render (&self, context: &mut T) -> Perhaps<U>; fn render (&self, to: &mut T) -> Perhaps<U>;
} }
/// Options can be rendered optionally. /// Options can be rendered optionally.

View file

@ -20,7 +20,7 @@ impl<'a, T, U> Collect<'a, T, U> for Layered<'a, T, U> {
} }
impl<'a> Render<TuiContext, Rect> for Layered<'a, TuiContext, Rect> { impl<'a> Render<TuiContext, Rect> for Layered<'a, TuiContext, Rect> {
fn render (&self, to: &mut impl TuiTarget) -> Perhaps<Rect> { fn render (&self, to: &mut TuiContext) -> Perhaps<Rect> {
let area = to.area(); let area = to.area();
for layer in self.0.0.iter() { for layer in self.0.0.iter() {
layer.render(to)?; layer.render(to)?;

View file

@ -44,24 +44,24 @@ impl<'a, T, U> Split<'a, T, U> {
} }
impl<'a, T, U> Collect<'a, T, U> for Split<'a, T, U> { impl<'a, T, U> Collect<'a, T, U> for Split<'a, T, U> {
fn add_box (mut self, item: Box<dyn Render<'a, T, U> + 'a>) -> Self { fn add_box (mut self, item: Box<dyn Render<T, U> + 'a>) -> Self {
self.items = self.items.add_box(item); self.items = self.items.add_box(item);
self self
} }
fn add_ref (mut self, item: &'a dyn Render<'a, T, U>) -> Self { fn add_ref (mut self, item: &'a dyn Render<T, U>) -> Self {
self.items = self.items.add_ref(item); self.items = self.items.add_ref(item);
self self
} }
} }
impl<'a> Render<'a, TuiOutput<'a>, Rect> for Split<'a, TuiOutput<'a>, Rect> { impl<'a> Render<TuiContext, Rect> for Split<'a, TuiContext, Rect> {
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> { fn render (&self, to: &mut TuiContext) -> Perhaps<Rect> {
Ok(None)//Rect::default())//Some(self.render_areas(to)?.0)) Ok(None)//Rect::default())//Some(self.render_areas(to)?.0))
} }
} }
impl<'a, 'b: 'a> Split<'a, TuiOutput<'a>, Rect> { impl<'a> Split<'a, TuiContext, Rect> {
pub fn render_areas (&'a self, to: &mut TuiOutput<'a>) -> Usually<(Rect, Vec<Rect>)> { pub fn render_areas (&self, to: &mut TuiContext) -> Usually<(Rect, Vec<Rect>)> {
Ok((Rect::default(), vec![])) Ok((Rect::default(), vec![]))
//let Rect { mut x, mut y, mut width, mut height } = to.area; //let Rect { mut x, mut y, mut width, mut height } = to.area;
//let TuiOutput { buffer, area } = to; //let TuiOutput { buffer, area } = to;

View file

@ -34,24 +34,22 @@ impl Engine for TuiContext {
fn teardown (&mut self) -> Usually<()> { fn teardown (&mut self) -> Usually<()> {
terminal_teardown() terminal_teardown()
} }
fn handle (&mut self, state: &mut impl Handle<Self, bool>) -> Usually<()> { fn handle (&self, state: &mut impl Handle<Self, bool>) -> Usually<()> {
if ::crossterm::event::poll(self.poll).is_ok() { if ::crossterm::event::poll(self.poll).is_ok() {
let event = ::crossterm::event::read().unwrap(); let event = ::crossterm::event::read().unwrap();
if let Event::Key(KeyEvent { if let Event::Key(KeyEvent {
code: KeyCode::Char('c'), modifiers: KeyModifiers::CONTROL, .. code: KeyCode::Char('c'), modifiers: KeyModifiers::CONTROL, ..
}) = event { }) = event {
self.exited.store(true, Ordering::Relaxed); self.exited.store(true, Ordering::Relaxed);
} else if let Err(e) = state.handle(&mut self) { } else if let Err(e) = state.handle(self) {
panic!("{e}") panic!("{e}")
} }
} }
Ok(()) Ok(())
} }
fn render (&mut self, state: &impl Render<Self, Rect>) -> Usually<()> { fn render (&mut self, state: &impl Render<Self, Rect>) -> Usually<()> {
if let Ok(state) = state.try_read() { state.render(self).expect("render failed");
state.render(&mut self).expect("render failed"); Ok(())
}
std::thread::sleep(self.sleep)
} }
} }
impl TuiContext { impl TuiContext {
@ -61,7 +59,7 @@ impl TuiContext {
) -> Usually<Arc<RwLock<R>>> { ) -> Usually<Arc<RwLock<R>>> {
let backend = CrosstermBackend::new(stdout()); let backend = CrosstermBackend::new(stdout());
let area = backend.size()?; let area = backend.size()?;
let engine = Arc::new(Self { let engine = Arc::new(RwLock::new(Self {
sleep: Duration::from_millis(20), sleep: Duration::from_millis(20),
poll: Duration::from_millis(100), poll: Duration::from_millis(100),
exited: Arc::new(AtomicBool::new(false)), exited: Arc::new(AtomicBool::new(false)),
@ -69,25 +67,30 @@ impl TuiContext {
buffers: [Buffer::empty(area), Buffer::empty(area)], buffers: [Buffer::empty(area), Buffer::empty(area)],
backend, backend,
area, area,
}); }));
let _input_thread = { let _input_thread = {
let engine = engine.clone(); let engine = engine.clone();
let state = state.clone(); let state = state.clone();
spawn(move || loop { spawn(move || loop {
let engine = engine.read().unwrap();
if engine.exited() { if engine.exited() {
break break
} }
engine.handle(&mut state).expect("handle failed"); engine.handle(&mut *state.write().unwrap()).expect("handle failed");
}) })
}; };
let main_thread = { let main_thread = {
let engine = engine.clone(); let engine = engine.clone();
let state = state.clone(); let state = state.clone();
spawn(move || loop { spawn(move || loop {
let mut engine = engine.write().unwrap();
if engine.exited() { if engine.exited() {
break break
} }
engine.render(&mut state).expect("render failed"); if let Ok(state) = state.try_read() {
engine.render(&*state).expect("render failed");
}
std::thread::sleep(engine.sleep);
}) })
}; };
main_thread.join().expect("main thread failed"); main_thread.join().expect("main thread failed");
@ -248,15 +251,17 @@ pub trait BorderStyle {
const W: &'static str = ""; const W: &'static str = "";
#[inline] #[inline]
fn draw <'a> (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> { fn draw <'a> (&self, to: &mut TuiContext) -> Perhaps<Rect> {
self.draw_horizontal(to.buffer, to.area, None)?; self.draw_horizontal(to, None)?;
self.draw_vertical(to.buffer, to.area, None)?; self.draw_vertical(to, None)?;
self.draw_corners(to.buffer, to.area, None)?; self.draw_corners(to, None)?;
Ok(Some(to.area)) Ok(Some(to.area))
} }
#[inline] #[inline]
fn draw_horizontal (&self, buf: &mut Buffer, area: Rect, style: Option<Style>) -> Usually<Rect> { fn draw_horizontal (&self, to: &mut TuiContext, style: Option<Style>) -> Usually<Rect> {
let area = to.area();
let buf = to.buffer();
let style = style.or_else(||self.style_horizontal()); let style = style.or_else(||self.style_horizontal());
for x in area.x..(area.x+area.width).saturating_sub(1) { for x in area.x..(area.x+area.width).saturating_sub(1) {
self.draw_north(buf, x, area.y, style)?; self.draw_north(buf, x, area.y, style)?;
@ -274,7 +279,9 @@ pub trait BorderStyle {
} }
#[inline] #[inline]
fn draw_vertical (&self, buf: &mut Buffer, area: Rect, style: Option<Style>) -> Usually<Rect> { fn draw_vertical (&self, to: &mut TuiContext, style: Option<Style>) -> Usually<Rect> {
let area = to.area();
let buf = to.buffer();
let style = style.or_else(||self.style_vertical()); let style = style.or_else(||self.style_vertical());
for y in area.y..(area.y+area.height).saturating_sub(1) { for y in area.y..(area.y+area.height).saturating_sub(1) {
Self::W.blit(buf, area.x, y, style)?; Self::W.blit(buf, area.x, y, style)?;
@ -284,7 +291,9 @@ pub trait BorderStyle {
} }
#[inline] #[inline]
fn draw_corners (&self, buf: &mut Buffer, area: Rect, style: Option<Style>) -> Usually<Rect> { fn draw_corners (&self, to: &mut TuiContext, style: Option<Style>) -> Usually<Rect> {
let area = to.area();
let buf = to.buffer();
let style = style.or_else(||self.style_corners()); let style = style.or_else(||self.style_corners());
if area.width > 0 && area.height > 0 { if area.width > 0 && area.height > 0 {
Self::NW.blit(buf, area.x, area.y, style)?; Self::NW.blit(buf, area.x, area.y, style)?;