pub mod event;
pub mod handle;
use std::sync::OnceLock;
use async_task::Task;
use event_source::emit;
use futures_lite::Future;
use instant::Duration;
use scoped_tls_hkt::scoped_thread_local;
use winit::{
error::EventLoopError,
event::Event,
event_loop::{ControlFlow, EventLoopBuilder, EventLoopWindowTarget},
};
use crate::{device, resumed, suspended, timer::UpdateState, window};
use self::{event::ExecutorEvent, handle::ExecutorHandle};
pub type EventLoopTarget = EventLoopWindowTarget<ExecutorEvent>;
static HANDLE: OnceLock<ExecutorHandle> = OnceLock::new();
pub fn executor_handle() -> &'static ExecutorHandle {
HANDLE.get().expect("Executor is not started")
}
scoped_thread_local!(static EL_TARGET: EventLoopTarget);
pub fn with_eventloop_target<R>(func: impl FnOnce(&EventLoopTarget) -> R) -> R {
EL_TARGET.with(func)
}
#[derive(Debug)]
struct Executor {
_main: Task<()>,
handle: &'static ExecutorHandle,
}
impl Executor {
fn on_event(&mut self, event: Event<ExecutorEvent>, target: &EventLoopTarget) {
EL_TARGET.set(target, move || match event {
Event::UserEvent(ExecutorEvent::Wake) => {}
Event::UserEvent(ExecutorEvent::PollTask(runnable)) => {
runnable.run();
}
Event::UserEvent(ExecutorEvent::Exit) => target.exit(),
Event::DeviceEvent { device_id, event } => {
emit!(device(), (device_id, &event));
}
Event::WindowEvent {
window_id,
mut event,
} => {
emit!(window(), (window_id, &mut event));
}
Event::Resumed => {
emit!(resumed(), ());
}
Event::Suspended => {
emit!(suspended(), ());
}
Event::AboutToWait => {
if let UpdateState::WaitTimeout(next_delay) = self.handle.timer.update_next() {
target.set_control_flow(ControlFlow::wait_duration(Duration::from_millis(
next_delay.get(),
)));
} else if target.control_flow() == ControlFlow::Poll {
target.set_control_flow(ControlFlow::Wait);
}
}
_ => {}
});
}
}
pub fn run(main: impl Future<Output = ()>) -> Result<(), EventLoopError> {
let event_loop = EventLoopBuilder::with_user_event().build()?;
let handle = {
if HANDLE.set(ExecutorHandle::new(&event_loop)).is_err() {
panic!("This cannot be happen");
}
HANDLE.get().unwrap()
};
let (runnable, task) = {
let proxy = event_loop.create_proxy();
let main = async move {
main.await;
let _ = proxy.send_event(ExecutorEvent::Exit);
};
unsafe { handle.spawn_raw_unchecked(main) }
};
let mut executor = Executor {
_main: task,
handle,
};
EL_TARGET.set(&event_loop, move || runnable.run());
event_loop.run(move |event, target| executor.on_event(event, target))
}