ratatui_kit/terminal/
mod.rs1use futures::{Stream, StreamExt, stream::BoxStream};
2use ratatui::buffer::Buffer;
3use std::{
4 collections::VecDeque,
5 fmt::Debug,
6 io,
7 sync::{Arc, Mutex, Weak},
8 task::{Poll, Waker},
9};
10
11mod cross_terminal;
12pub use cross_terminal::CrossTerminal;
13
14pub trait TerminalImpl: Send {
15 type Event: Clone + Debug;
16 fn event_stream(&mut self) -> io::Result<BoxStream<'static, Self::Event>>;
17 fn received_ctrl_c(event: Self::Event) -> bool;
18 fn draw<F>(&mut self, f: F) -> io::Result<()>
19 where
20 F: FnOnce(&mut ratatui::Frame);
21
22 fn insert_before<F>(&mut self, height: u16, draw_fn: F) -> io::Result<()>
23 where
24 F: FnOnce(&mut Buffer);
25}
26
27struct TerminalEventsInner<T> {
33 pending: VecDeque<T>,
34 waker: Option<Waker>,
35}
36
37pub struct TerminalEvents<T> {
40 inner: Arc<Mutex<TerminalEventsInner<T>>>,
41}
42
43impl<T> Stream for TerminalEvents<T> {
45 type Item = T;
46 fn poll_next(
47 self: std::pin::Pin<&mut Self>,
48 cx: &mut std::task::Context<'_>,
49 ) -> Poll<Option<Self::Item>> {
50 let mut inner = self.inner.lock().unwrap();
51 if let Some(event) = inner.pending.pop_front() {
52 Poll::Ready(Some(event)) } else {
54 inner.waker = Some(cx.waker().clone()); Poll::Pending
56 }
57 }
58}
59
60pub struct Terminal<T = CrossTerminal>
67where
68 T: TerminalImpl,
69{
70 inner: Box<T>,
71 event_stream: BoxStream<'static, T::Event>,
72 subscribers: Vec<Weak<Mutex<TerminalEventsInner<T::Event>>>>,
73 received_ctrl_c: bool,
74}
75
76impl<T> Terminal<T>
77where
78 T: TerminalImpl,
79{
80 pub fn new(inner: T) -> io::Result<Self> {
81 let mut inner = Box::new(inner);
82 Ok(Self {
83 event_stream: inner.event_stream()?,
84 subscribers: Vec::new(),
85 received_ctrl_c: false,
86 inner,
87 })
88 }
89
90 pub fn received_ctrl_c(&self) -> bool {
91 self.received_ctrl_c
92 }
93
94 pub fn draw<F>(&mut self, f: F) -> io::Result<()>
95 where
96 F: FnOnce(&mut ratatui::Frame),
97 {
98 self.inner.draw(f)
99 }
100
101 pub fn insert_before<F>(&mut self, height: u16, draw_fn: F) -> io::Result<()>
102 where
103 F: FnOnce(&mut Buffer),
104 {
105 self.inner.insert_before(height, draw_fn)
106 }
107
108 pub fn events(&mut self) -> io::Result<TerminalEvents<T::Event>> {
110 let inner = Arc::new(Mutex::new(TerminalEventsInner {
112 pending: VecDeque::new(),
113 waker: None,
114 }));
115
116 self.subscribers.push(Arc::downgrade(&inner));
118
119 Ok(TerminalEvents { inner })
120 }
121
122 pub async fn wait(&mut self) {
124 while let Some(event) = self.event_stream.next().await {
125 self.received_ctrl_c = T::received_ctrl_c(event.clone());
127 if self.received_ctrl_c {
128 return; }
130
131 self.subscribers.retain(|subscriber| {
133 if let Some(subscriber) = subscriber.upgrade() {
134 let mut subscriber = subscriber.lock().unwrap();
135 subscriber.pending.push_back(event.clone());
137
138 if let Some(waker) = subscriber.waker.take() {
140 waker.wake(); }
142
143 true } else {
145 false }
147 });
148 }
149 }
150}