use crate::{*, time::*, lang::*}; use ::std::{thread::JoinHandle, time::Duration}; #[cfg(feature = "term")] use ::crossterm::event::poll; #[derive(Clone)] pub struct Exit(Arc); impl Exit { pub fn run (run: impl Fn(Self)->Usually) -> Usually { run(Self(Arc::new(AtomicBool::new(false)))) } } #[derive(Debug)] pub struct Thread { /// Exit flag. pub exit: Arc, /// Performance counter. pub perf: Arc, /// Use this to wait for the thread to finish. pub join: JoinHandle<()>, } impl Thread { /// Spawn a TUI thread that runs `callt least one, then repeats until `exit`. pub fn new (exit: Arc, call: F) -> Result where F: Fn(&PerfModel)->() + Send + Sync + 'static { let perf = Arc::new(PerfModel::default()); Ok(Self { exit: exit.clone(), perf: perf.clone(), join: std::thread::Builder::new().name("tengri tui output".into()).spawn(move || { while !exit.fetch_and(true, Relaxed) { let _ = perf.cycle(&call); } })?.into() }) } /// 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, call: F ) -> Result where F: Fn(&PerfModel)->() + Send + Sync + 'static { Self::new(exit, move |perf| { let _ = call(perf); std::thread::sleep(time); }) } /// 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, call: F ) -> Result where F: Fn(&PerfModel)->() + Send + Sync + 'static { Self::new(exit, move |perf| { if poll(time).is_ok() { let _ = call(perf); } }) } pub fn join (self) -> Result<(), Box> { self.join.join() } } /// Define an enum containing commands, and implement [Command] trait for over given `State`. #[macro_export] macro_rules! def_command ( ($Command:ident: |$state:ident: $State:ty| { // FIXME: support attrs (docstrings) $($Variant:ident$({$($arg:ident:$Arg:ty),+ $(,)?})?=>$body:expr),* $(,)? })=>{ #[derive(Debug)] pub enum $Command { // FIXME: support attrs (docstrings) $($Variant $({ $($arg: $Arg),* })?),* } impl ::tengri::Command<$State> for $Command { fn execute (&self, $state: &mut $State) -> Perhaps { match self { $(Self::$Variant $({ $($arg),* })? => $body,)* _ => unimplemented!("Command<{}>: {self:?}", stringify!($State)), } } } }); /// Implement [Handle] for given `State` and `handler`. #[macro_export] macro_rules! 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 } } } }