use futures::{Stream, StreamExt, stream::BoxStream};
use ratatui::buffer::Buffer;
use std::{
collections::VecDeque,
fmt::Debug,
io,
sync::{Arc, Mutex, Weak},
task::{Poll, Waker},
};
mod cross_terminal;
pub use cross_terminal::CrossTerminal;
pub trait TerminalImpl: Send {
type Event: Clone + Debug;
fn event_stream(&mut self) -> io::Result<BoxStream<'static, Self::Event>>;
fn received_ctrl_c(event: Self::Event) -> bool;
fn draw<F>(&mut self, f: F) -> io::Result<()>
where
F: FnOnce(&mut ratatui::Frame);
fn insert_before<F>(&mut self, height: u16, draw_fn: F) -> io::Result<()>
where
F: FnOnce(&mut Buffer);
}
struct TerminalEventsInner<T> {
pending: VecDeque<T>,
waker: Option<Waker>,
}
pub struct TerminalEvents<T> {
inner: Arc<Mutex<TerminalEventsInner<T>>>,
}
impl<T> Stream for TerminalEvents<T> {
type Item = T;
fn poll_next(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> Poll<Option<Self::Item>> {
let mut inner = self.inner.lock().unwrap();
if let Some(event) = inner.pending.pop_front() {
Poll::Ready(Some(event)) } else {
inner.waker = Some(cx.waker().clone()); Poll::Pending
}
}
}
pub struct Terminal<T = CrossTerminal>
where
T: TerminalImpl,
{
inner: Box<T>,
event_stream: BoxStream<'static, T::Event>,
subscribers: Vec<Weak<Mutex<TerminalEventsInner<T::Event>>>>,
received_ctrl_c: bool,
}
impl<T> Terminal<T>
where
T: TerminalImpl,
{
pub fn new(inner: T) -> io::Result<Self> {
let mut inner = Box::new(inner);
Ok(Self {
event_stream: inner.event_stream()?,
subscribers: Vec::new(),
received_ctrl_c: false,
inner,
})
}
pub fn received_ctrl_c(&self) -> bool {
self.received_ctrl_c
}
pub fn draw<F>(&mut self, f: F) -> io::Result<()>
where
F: FnOnce(&mut ratatui::Frame),
{
self.inner.draw(f)
}
pub fn insert_before<F>(&mut self, height: u16, draw_fn: F) -> io::Result<()>
where
F: FnOnce(&mut Buffer),
{
self.inner.insert_before(height, draw_fn)
}
pub fn events(&mut self) -> io::Result<TerminalEvents<T::Event>> {
let inner = Arc::new(Mutex::new(TerminalEventsInner {
pending: VecDeque::new(),
waker: None,
}));
self.subscribers.push(Arc::downgrade(&inner));
Ok(TerminalEvents { inner })
}
pub async fn wait(&mut self) {
while let Some(event) = self.event_stream.next().await {
self.received_ctrl_c = T::received_ctrl_c(event.clone());
if self.received_ctrl_c {
return; }
self.subscribers.retain(|subscriber| {
if let Some(subscriber) = subscriber.upgrade() {
let mut subscriber = subscriber.lock().unwrap();
subscriber.pending.push_back(event.clone());
if let Some(waker) = subscriber.waker.take() {
waker.wake(); }
true } else {
false }
});
}
}
}