taao 0.1.3

A crate that bridges tao and async rust, and also supports winit.
Documentation
use std::future::Future;
use std::pin::Pin;
use std::sync::{
    Arc,
    atomic::{AtomicBool, Ordering},
};
use std::task::{Context, Poll, Wake, Waker};

#[cfg(feature = "event")]
use async_channel::Receiver;
use winit::application::ApplicationHandler;
#[cfg(feature = "event")]
use winit::event::Event as WinitEvent;
use winit::event::{DeviceEvent, StartCause, WindowEvent};
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
use winit::platform::run_on_demand::EventLoopExtRunOnDemand;
use winit::window::WindowId;

use crate::BlockOnError;

struct EventLoopWake {
    proxy: winit::event_loop::EventLoopProxy<()>,
    notified: AtomicBool,
}

impl Wake for EventLoopWake {
    fn wake(self: Arc<Self>) {
        self.wake_by_ref();
    }

    fn wake_by_ref(self: &Arc<Self>) {
        if !self.notified.swap(true, Ordering::AcqRel)
            && let Err(error) = self.proxy.send_event(())
        {
            #[cfg(feature = "tracing")]
            tracing::trace!(target: "taao::block_on", %error, "winit wake notification was rejected");
            #[cfg(not(feature = "tracing"))]
            let _ = error;
        }
    }
}

struct Driver<F: Future> {
    future: Pin<Box<F>>,
    output: Option<F::Output>,
    wake: Arc<EventLoopWake>,
    waker: Waker,
    #[cfg(feature = "event")]
    events: Option<crate::event::EventQueueSender<crate::event::EventKind>>,
    #[cfg(feature = "event")]
    operations: Option<Receiver<crate::event::winit::Operation>>,
}

impl<F: Future> Driver<F> {
    #[cfg(feature = "event")]
    fn send(&self, event: WinitEvent<()>) {
        if let Some(sender) = &self.events {
            sender.send(crate::event::EventKind::Winit(event));
        }
    }

    #[cfg(feature = "event")]
    fn process_operations(&self, event_loop: &ActiveEventLoop) {
        if let Some(operations) = &self.operations {
            while let Ok(operation) = operations.try_recv() {
                operation(event_loop);
            }
        }
    }

    fn poll(&mut self, event_loop: &ActiveEventLoop) {
        if self.output.is_some() {
            return;
        }

        self.wake.notified.store(false, Ordering::Release);
        let mut context = Context::from_waker(&self.waker);
        if let Poll::Ready(output) = self.future.as_mut().poll(&mut context) {
            self.output = Some(output);
            event_loop.exit();
            #[cfg(feature = "tracing")]
            tracing::debug!(target: "taao::block_on", backend = "winit", "future completed");
        }
    }

    fn after_callback(&mut self, event_loop: &ActiveEventLoop) {
        #[cfg(feature = "event")]
        self.process_operations(event_loop);
        self.poll(event_loop);
    }
}

impl<F: Future> ApplicationHandler<()> for Driver<F> {
    fn new_events(&mut self, event_loop: &ActiveEventLoop, _cause: StartCause) {
        #[cfg(feature = "event")]
        self.send(WinitEvent::NewEvents(_cause));
        self.after_callback(event_loop);
    }

    fn window_event(
        &mut self,
        event_loop: &ActiveEventLoop,
        _window_id: WindowId,
        _event: WindowEvent,
    ) {
        #[cfg(feature = "event")]
        self.send(WinitEvent::WindowEvent {
            window_id: _window_id,
            event: _event,
        });
        self.after_callback(event_loop);
    }

    fn device_event(
        &mut self,
        event_loop: &ActiveEventLoop,
        _device_id: winit::event::DeviceId,
        _event: DeviceEvent,
    ) {
        #[cfg(feature = "event")]
        self.send(WinitEvent::DeviceEvent {
            device_id: _device_id,
            event: _event,
        });
        self.after_callback(event_loop);
    }

    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
        #[cfg(feature = "event")]
        self.send(WinitEvent::Resumed);
        self.after_callback(event_loop);
    }

    fn user_event(&mut self, event_loop: &ActiveEventLoop, (): ()) {
        self.after_callback(event_loop);
    }

    fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
        #[cfg(feature = "event")]
        self.send(WinitEvent::AboutToWait);
        self.after_callback(event_loop);
        if self.output.is_none() {
            event_loop.set_control_flow(ControlFlow::Wait);
        }
    }

    fn suspended(&mut self, event_loop: &ActiveEventLoop) {
        #[cfg(feature = "event")]
        self.send(WinitEvent::Suspended);
        self.after_callback(event_loop);
    }

    fn memory_warning(&mut self, event_loop: &ActiveEventLoop) {
        #[cfg(feature = "event")]
        self.send(WinitEvent::MemoryWarning);
        self.after_callback(event_loop);
    }
}

#[cfg(feature = "event")]
type EventSink = Option<crate::event::EventQueueSender<crate::event::EventKind>>;
#[cfg(not(feature = "event"))]
type EventSink = EventFeatureDisabled;

#[cfg(not(feature = "event"))]
struct EventFeatureDisabled;

#[cfg(feature = "event")]
type Operations = Option<Receiver<crate::event::winit::Operation>>;
#[cfg(not(feature = "event"))]
type Operations = EventFeatureDisabled;

fn empty_event_sink() -> EventSink {
    #[cfg(feature = "event")]
    {
        None
    }
    #[cfg(not(feature = "event"))]
    {
        EventFeatureDisabled
    }
}

fn empty_operations() -> Operations {
    #[cfg(feature = "event")]
    {
        None
    }
    #[cfg(not(feature = "event"))]
    {
        EventFeatureDisabled
    }
}

pub(crate) fn block_on<F>(
    event_loop: &mut EventLoop<()>,
    future: F,
) -> Result<F::Output, BlockOnError>
where
    F: Future,
{
    run(event_loop, future, empty_event_sink(), empty_operations())
}

#[cfg(feature = "event")]
pub(crate) fn block_on_with_events<F, Output>(
    event_loop: &mut EventLoop<()>,
    handler: F,
) -> Result<Output, BlockOnError>
where
    F: std::ops::AsyncFnOnce(crate::event::Event) -> Output,
{
    let proxy = event_loop.create_proxy();
    let (event, events, operations) = crate::event::winit::Event::new(proxy);
    let future = async move { handler(event).await };
    run(event_loop, future, Some(events), Some(operations))
}

fn run<F>(
    event_loop: &mut EventLoop<()>,
    future: F,
    _events: EventSink,
    _operations: Operations,
) -> Result<F::Output, BlockOnError>
where
    F: Future,
{
    #[cfg(feature = "tracing")]
    tracing::debug!(target: "taao::block_on", backend = "winit", "starting event-loop future");
    let wake = Arc::new(EventLoopWake {
        proxy: event_loop.create_proxy(),
        notified: AtomicBool::new(true),
    });
    let waker = Waker::from(Arc::clone(&wake));
    let mut driver = Driver {
        future: Box::pin(future),
        output: None,
        wake,
        waker,
        #[cfg(feature = "event")]
        events: _events,
        #[cfg(feature = "event")]
        operations: _operations,
    };

    if let Err(error) = event_loop.run_app_on_demand(&mut driver) {
        #[cfg(feature = "tracing")]
        tracing::error!(target: "taao::block_on", backend = "winit", %error, "failed to run event loop");
        return Err(BlockOnError::Backend(error.to_string()));
    }

    match driver.output {
        Some(output) => Ok(output),
        None => {
            #[cfg(feature = "tracing")]
            tracing::error!(target: "taao::block_on", backend = "winit", "event loop exited before the future completed");
            Err(BlockOnError::EventLoopExited)
        }
    }
}