use std::pin::Pin;
use std::task::{Context, Poll};
use async_channel::{Receiver, Sender};
use futures_lite::Stream;
use winit::event_loop::{ActiveEventLoop, EventLoopProxy};
use super::{DEFAULT_EVENT_CAPACITY, EventQueue, EventQueueSender, SubmitError};
#[derive(Debug)]
pub enum EventKind {
Winit(winit::event::Event<()>),
}
pub(crate) type Operation = Box<dyn FnOnce(&ActiveEventLoop) + Send>;
pub struct Event {
events: EventQueue<EventKind>,
submitter: EventSubmitter,
}
impl Event {
pub(crate) fn new(
proxy: EventLoopProxy<()>,
) -> (Self, EventQueueSender<EventKind>, Receiver<Operation>) {
let (events, event_sender) = EventQueue::bounded(DEFAULT_EVENT_CAPACITY);
let (operation_sender, operation_receiver) = async_channel::bounded(64);
(
Self {
events,
submitter: EventSubmitter {
operations: operation_sender,
proxy,
},
},
event_sender,
operation_receiver,
)
}
pub fn submitter(&self) -> EventSubmitter {
self.submitter.clone()
}
pub fn dropped_events(&self) -> usize {
self.events.dropped_events()
}
}
pub trait Submitter {
fn submit<R: Send + 'static>(
&self,
operation: impl FnOnce(&ActiveEventLoop) -> R + Send + 'static,
) -> impl std::future::Future<Output = Result<R, SubmitError>> + Send;
}
impl Submitter for Event {
async fn submit<R: Send + 'static>(
&self,
operation: impl FnOnce(&ActiveEventLoop) -> R + Send + 'static,
) -> Result<R, SubmitError> {
self.submitter.submit(operation).await
}
}
#[derive(Clone)]
pub struct EventSubmitter {
operations: Sender<Operation>,
proxy: EventLoopProxy<()>,
}
impl Submitter for EventSubmitter {
async fn submit<R: Send + 'static>(
&self,
operation: impl FnOnce(&ActiveEventLoop) -> R + Send + 'static,
) -> Result<R, SubmitError> {
let (reply_sender, reply_receiver) = async_channel::bounded(1);
if self
.operations
.send(Box::new(move |event_loop| {
#[cfg(feature = "tracing")]
tracing::trace!(target: "taao::event", backend = "winit", "executing submitted operation");
let _ = reply_sender.try_send(operation(event_loop));
}))
.await
.is_err()
{
#[cfg(feature = "tracing")]
tracing::error!(target: "taao::event", backend = "winit", "failed to enqueue submitted operation");
return Err(SubmitError);
}
#[cfg(feature = "tracing")]
tracing::trace!(target: "taao::event", backend = "winit", "submitted operation queued");
if let Err(error) = self.proxy.send_event(()) {
#[cfg(feature = "tracing")]
tracing::error!(target: "taao::event", backend = "winit", %error, "failed to wake event loop for submitted operation");
#[cfg(not(feature = "tracing"))]
let _ = error;
return Err(SubmitError);
}
reply_receiver.recv().await.map_err(|_| SubmitError)
}
}
impl Stream for Event {
type Item = EventKind;
fn poll_next(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Option<Self::Item>> {
Pin::new(&mut self.events).poll_next(context)
}
}