147 lines
5.0 KiB
Rust
147 lines
5.0 KiB
Rust
use std::{
|
|
io,
|
|
pin::Pin,
|
|
sync::{
|
|
atomic::{AtomicBool, Ordering},
|
|
mpsc::{self, SyncSender},
|
|
Arc,
|
|
},
|
|
task::{Context, Poll},
|
|
thread,
|
|
time::Duration,
|
|
};
|
|
|
|
use futures_core::stream::Stream;
|
|
|
|
use crate::event::{
|
|
filter::EventFilter, lock_internal_event_reader, poll_internal, read_internal, sys::Waker,
|
|
Event, InternalEvent,
|
|
};
|
|
|
|
/// A stream of `Result<Event>`.
|
|
///
|
|
/// **This type is not available by default. You have to use the `event-stream` feature flag
|
|
/// to make it available.**
|
|
///
|
|
/// It implements the [Stream](futures_core::stream::Stream)
|
|
/// trait and allows you to receive [`Event`]s with [`async-std`](https://crates.io/crates/async-std)
|
|
/// or [`tokio`](https://crates.io/crates/tokio) crates.
|
|
///
|
|
/// Check the [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) folder to see how to use
|
|
/// it (`event-stream-*`).
|
|
#[derive(Debug)]
|
|
pub struct EventStream {
|
|
poll_internal_waker: Waker,
|
|
stream_wake_task_executed: Arc<AtomicBool>,
|
|
stream_wake_task_should_shutdown: Arc<AtomicBool>,
|
|
task_sender: SyncSender<Task>,
|
|
}
|
|
|
|
impl Default for EventStream {
|
|
fn default() -> Self {
|
|
let (task_sender, receiver) = mpsc::sync_channel::<Task>(1);
|
|
|
|
thread::spawn(move || {
|
|
while let Ok(task) = receiver.recv() {
|
|
loop {
|
|
if let Ok(true) = poll_internal(None, &EventFilter) {
|
|
break;
|
|
}
|
|
|
|
if task.stream_wake_task_should_shutdown.load(Ordering::SeqCst) {
|
|
break;
|
|
}
|
|
}
|
|
task.stream_wake_task_executed
|
|
.store(false, Ordering::SeqCst);
|
|
task.stream_waker.wake();
|
|
}
|
|
});
|
|
|
|
EventStream {
|
|
poll_internal_waker: lock_internal_event_reader().waker(),
|
|
stream_wake_task_executed: Arc::new(AtomicBool::new(false)),
|
|
stream_wake_task_should_shutdown: Arc::new(AtomicBool::new(false)),
|
|
task_sender,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl EventStream {
|
|
/// Constructs a new instance of `EventStream`.
|
|
pub fn new() -> EventStream {
|
|
EventStream::default()
|
|
}
|
|
}
|
|
|
|
struct Task {
|
|
stream_waker: std::task::Waker,
|
|
stream_wake_task_executed: Arc<AtomicBool>,
|
|
stream_wake_task_should_shutdown: Arc<AtomicBool>,
|
|
}
|
|
|
|
// Note to future me
|
|
//
|
|
// We need two wakers in order to implement EventStream correctly.
|
|
//
|
|
// 1. futures::Stream waker
|
|
//
|
|
// Stream::poll_next can return Poll::Pending which means that there's no
|
|
// event available. We are going to spawn a thread with the
|
|
// poll_internal(None, &EventFilter) call. This call blocks until an
|
|
// event is available and then we have to wake up the executor with notification
|
|
// that the task can be resumed.
|
|
//
|
|
// 2. poll_internal waker
|
|
//
|
|
// There's no event available, Poll::Pending was returned, stream waker thread
|
|
// is up and sitting in the poll_internal. User wants to drop the EventStream.
|
|
// We have to wake up the poll_internal (force it to return Ok(false)) and quit
|
|
// the thread before we drop.
|
|
impl Stream for EventStream {
|
|
type Item = io::Result<Event>;
|
|
|
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
|
let result = match poll_internal(Some(Duration::from_secs(0)), &EventFilter) {
|
|
Ok(true) => match read_internal(&EventFilter) {
|
|
Ok(InternalEvent::Event(event)) => Poll::Ready(Some(Ok(event))),
|
|
Err(e) => Poll::Ready(Some(Err(e))),
|
|
#[cfg(unix)]
|
|
_ => unreachable!(),
|
|
},
|
|
Ok(false) => {
|
|
if !self
|
|
.stream_wake_task_executed
|
|
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
|
|
// https://github.com/rust-lang/rust/issues/80486#issuecomment-752244166
|
|
.unwrap_or_else(|x| x)
|
|
{
|
|
let stream_waker = cx.waker().clone();
|
|
let stream_wake_task_executed = self.stream_wake_task_executed.clone();
|
|
let stream_wake_task_should_shutdown =
|
|
self.stream_wake_task_should_shutdown.clone();
|
|
|
|
stream_wake_task_should_shutdown.store(false, Ordering::SeqCst);
|
|
|
|
let _ = self.task_sender.send(Task {
|
|
stream_waker,
|
|
stream_wake_task_executed,
|
|
stream_wake_task_should_shutdown,
|
|
});
|
|
}
|
|
Poll::Pending
|
|
}
|
|
Err(e) => Poll::Ready(Some(Err(e))),
|
|
};
|
|
result
|
|
}
|
|
}
|
|
|
|
impl Drop for EventStream {
|
|
fn drop(&mut self) {
|
|
self.stream_wake_task_should_shutdown
|
|
.store(true, Ordering::SeqCst);
|
|
let _ = self.poll_internal_waker.wake();
|
|
}
|
|
}
|