pub mod signal;
#[cfg(target_arch = "wasm32")]
mod wasm;
use std::{
sync::atomic::Ordering,
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
};
use async_component_core::{context::ComponentStream, AsyncComponent};
use futures::{Stream, StreamExt};
use winit::{
event::Event,
event_loop::{ControlFlow, EventLoop},
};
use crate::WinitComponent;
use ref_extended::ref_extended;
use self::signal::WinitSignal;
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct ExecutorPollEvent;
#[derive(Debug)]
pub struct WinitExecutor {
event_loop: Option<EventLoop<ExecutorPollEvent>>,
state_signal: WinitSignal,
}
impl WinitExecutor {
pub fn new(event_loop: EventLoop<ExecutorPollEvent>) -> Self {
let state_signal = WinitSignal::new(event_loop.create_proxy());
Self {
event_loop: Some(event_loop),
state_signal,
}
}
fn poll_stream(&'static self, mut stream: impl Stream + Unpin) -> Poll<()> {
if self
.state_signal
.scheduled
.compare_exchange(true, false, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
{
if let Poll::Ready(Some(_)) =
stream.poll_next_unpin(&mut Context::from_waker(&unsafe {
Waker::from_raw(create_raw_waker(&self.state_signal))
}))
{
self.state_signal.scheduled.store(true, Ordering::Release);
}
Poll::Ready(())
} else {
Poll::Pending
}
}
pub fn run<C: AsyncComponent + WinitComponent + 'static>(
mut self,
func: impl FnOnce() -> C,
) -> ! {
let event_loop = self.event_loop.take().unwrap();
let mut stream = ComponentStream::new(func);
let executor = self;
ref_extended!(|&executor| event_loop.run(move |event, _, control_flow| {
let mut stream = stream.enter();
match event {
Event::MainEventsCleared => {
stream
.component_mut()
.on_event(&mut Event::MainEventsCleared, control_flow);
if let ControlFlow::ExitWithCode(_) = control_flow {
return;
}
match executor.poll_stream(&mut stream) {
Poll::Ready(_) => {
control_flow.set_poll();
}
Poll::Pending => {
control_flow.set_wait();
}
}
}
Event::UserEvent(_) => {}
_ => {
stream
.component_mut()
.on_event(&mut event.map_nonuser_event().unwrap(), control_flow);
}
}
}))
}
}
fn create_raw_waker(signal: &'static WinitSignal) -> RawWaker {
unsafe fn waker_clone(this: *const ()) -> RawWaker {
create_raw_waker(&*(this as *const WinitSignal))
}
unsafe fn waker_wake(this: *const ()) {
let this = &*(this as *const WinitSignal);
this.wake_by_ref();
}
unsafe fn waker_wake_by_ref(this: *const ()) {
let this = &*(this as *const WinitSignal);
this.wake_by_ref();
}
unsafe fn waker_drop(_: *const ()) {}
RawWaker::new(
signal as *const _ as *const (),
&RawWakerVTable::new(waker_clone, waker_wake, waker_wake_by_ref, waker_drop),
)
}