winit-async 0.1.0

An async runtime based on the winit event loop.
Documentation
use crate::map::Map;
use crate::tls::runtime;
use async_local_channel::{spsc, watch};
use async_local_executor::{Executor, JoinHandle, TaskHandle, spawn_local};
use std::fmt::Debug;
use std::mem;
use winit::application::ApplicationHandler;
use winit::error::EventLoopError;
use winit::event::{DeviceEvent, DeviceId, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::window::WindowId;

#[derive(Debug, Default, Clone, Copy)]
enum State {
    #[default]
    Suspended,
    Resumed,
}

impl State {
    const fn is_resumed(self) -> bool {
        match self {
            State::Suspended => false,
            State::Resumed => true,
        }
    }

    const fn is_suspended(self) -> bool {
        !self.is_resumed()
    }
}

struct ChannelPair<T, R> {
    tx: T,
    rx: R,
}

impl<T, R> ChannelPair<T, R> {
    fn new((tx, rx): (T, R)) -> Self {
        Self { tx, rx }
    }
}

pub struct Runtime {
    event_loop: Option<&'static ActiveEventLoop>,
    resumed: ChannelPair<watch::Sender<()>, watch::InactiveReceiver<()>>,
    suspended: ChannelPair<watch::Sender<()>, watch::InactiveReceiver<()>>,
    window_events: Map<WindowId, spsc::Sender<WindowEvent>>,
    device_events: Option<spsc::Sender<(DeviceId, DeviceEvent)>>,
    state: State,
}

impl Runtime {
    fn new() -> Self {
        Self {
            event_loop: None,
            resumed: ChannelPair::new(watch::channel()),
            suspended: ChannelPair::new(watch::channel()),
            window_events: Map::new(),
            device_events: None,
            state: State::Suspended,
        }
    }

    pub fn event_loop(&self) -> &ActiveEventLoop {
        self.event_loop.unwrap()
    }

    fn set_event_loop(&mut self, event_loop: &ActiveEventLoop) {
        self.event_loop = Some(unsafe { mem::transmute(event_loop) });
    }

    fn resumed(&mut self) {
        self.state = State::Resumed;
        self.resumed.tx.send(()).unwrap();
    }

    pub fn resumed_rx(&mut self) -> Option<watch::Receiver<()>> {
        if self.state.is_resumed() {
            None
        } else {
            Some(self.resumed.rx.clone().activate())
        }
    }

    fn suspended(&mut self) {
        self.state = State::Suspended;
        self.suspended.tx.send(()).unwrap();
    }

    pub fn suspended_rx(&mut self) -> Option<watch::Receiver<()>> {
        if self.state.is_suspended() {
            None
        } else {
            Some(self.suspended.rx.clone().activate())
        }
    }

    pub fn device_event_receiver(&mut self) -> Option<spsc::Receiver<(DeviceId, DeviceEvent)>> {
        if self.device_events.is_some() {
            return None;
        }
        let (tx, rx) = spsc::channel();
        assert!(self.device_events.replace(tx).is_none());
        Some(rx.activate())
    }

    pub fn device_event_receiver_dropped(&mut self) {
        self.device_events = None;
    }

    pub fn window_event_receiver(
        &mut self,
        window_id: WindowId,
    ) -> Option<spsc::Receiver<WindowEvent>> {
        if self.window_events.contains_key(&window_id) {
            return None;
        }
        let (tx, rx) = spsc::channel();
        self.window_events.push(window_id, tx);
        Some(rx.activate())
    }

    fn device_event(&mut self, device_id: DeviceId, event: DeviceEvent) {
        if let Some(tx) = self.device_events.as_ref() {
            tx.send((device_id, event)).unwrap();
        }
    }

    pub fn window_event_receiver_dropped(&mut self, window_id: WindowId) {
        self.window_events.remove(&window_id);
    }

    fn window_event(&mut self, window_id: WindowId, event: WindowEvent) {
        if let Some(tx) = self.window_events.get(&window_id) {
            tx.send(event).unwrap();
        }
    }
}

struct RuntimeProxy;

impl ApplicationHandler<TaskHandle> for RuntimeProxy {
    fn suspended(&mut self, _: &ActiveEventLoop) {
        runtime().suspended();
    }

    fn resumed(&mut self, _: &ActiveEventLoop) {
        runtime().resumed();
    }

    fn window_event(&mut self, _: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
        runtime().window_event(window_id, event);
    }

    fn device_event(&mut self, _: &ActiveEventLoop, device_id: DeviceId, event: DeviceEvent) {
        runtime().device_event(device_id, event);
    }

    fn user_event(&mut self, event_loop: &ActiveEventLoop, task_handle: TaskHandle) {
        runtime().set_event_loop(event_loop);
        task_handle.tick();
    }
}

pub fn run<E, F>(future: F) -> Result<JoinHandle<Result<(), E>>, EventLoopError>
where
    F: IntoFuture<Output = Result<(), E>> + 'static,
{
    let event_loop = EventLoop::with_user_event().build()?;
    let proxy = event_loop.create_proxy();
    let mut ex = Executor::new(move |task| proxy.send_event(task).unwrap());
    ex.run_in(|| {
        let handle = spawn_local(future);
        let mut runtime = Runtime::new();
        runtime
            .run_in(move || event_loop.run_app(&mut RuntimeProxy))
            .map(|()| handle)
    })
}