winit-async 0.4.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::run_ready_tasks;
use std::fmt::Debug;
use std::mem;
use winit::application::ApplicationHandler;
use winit::event::{DeviceEvent, DeviceId, StartCause, WindowEvent};
use winit::event_loop::ActiveEventLoop;
use winit::window::WindowId;

#[cfg(not(target_arch = "wasm32"))]
pub mod native;
#[cfg(target_arch = "wasm32")]
pub mod web;

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

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

    const fn is_suspended(self) -> bool {
        match self {
            Self::Suspended => true,
            Self::Resumed | Self::Undefined => false,
        }
    }
}

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::Undefined,
        }
    }

    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, event_loop: &ActiveEventLoop) {
        self.set_event_loop(event_loop);
        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, event_loop: &ActiveEventLoop) {
        self.set_event_loop(event_loop);
        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,
        event_loop: &ActiveEventLoop,
        device_id: DeviceId,
        event: DeviceEvent,
    ) {
        self.set_event_loop(event_loop);
        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,
        event_loop: &ActiveEventLoop,
        window_id: WindowId,
        event: WindowEvent,
    ) {
        self.set_event_loop(event_loop);
        if let Some(tx) = self.window_events.get(&window_id) {
            tx.send(event).unwrap();
        }
    }
}

struct AsyncApplication;

impl ApplicationHandler for AsyncApplication {
    fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
        if cause == StartCause::Init {
            runtime().set_event_loop(event_loop);
            run_ready_tasks();
        }
    }

    fn suspended(&mut self, event_loop: &ActiveEventLoop) {
        runtime().suspended(event_loop);
        run_ready_tasks();
    }

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

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

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

    fn user_event(&mut self, event_loop: &ActiveEventLoop, _: ()) {
        runtime().set_event_loop(event_loop);
        run_ready_tasks();
    }
}