aeth-window 0.0.4

The window subsystem for aeth-rs
Documentation
use crate::access_winit_active_event_loop::AccessWinitActiveEventLoop;
use crate::window::{Window, Windows};
use aeth_event::{Pub, Sub, new_pubsub};
use aeth_task::foreground;
use futures::channel::oneshot;
use std::cell::RefCell;
use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
use std::rc::{Rc, Weak};
use winit::error::RequestError;
use winit::event::{DeviceEvent as WinitDeviceEvent, DeviceId};
use winit::event_loop::ActiveEventLoop;
use winit::window::WindowAttributes;

type ActiveEventLoopJob = Box<dyn FnOnce(&dyn ActiveEventLoop)>;

/// Wake-up event generated by [`EventLoopProxy.wake_up()`].
///
/// [`EventLoopProxy.wake_up()`]: winit::event_loop::EventLoopProxy::wake_up
#[derive(Clone)]
pub struct WakeUpEvent;

/// Bundled winit device event with device id.
#[derive(Clone)]
pub struct DeviceEvent {
    pub event: WinitDeviceEvent,
    pub device_id: Option<DeviceId>,
}

pub(crate) struct ManagerInner {
    active_event_loop_jobs: RefCell<Vec<ActiveEventLoopJob>>,
    windows: Rc<RefCell<Windows>>,
    draining_foreground_loopback: RefCell<bool>,
    wakeup_event_pub: Pub<WakeUpEvent>,
    wakeup_event_sub: Sub<WakeUpEvent>,
    device_event_pub: Pub<DeviceEvent>,
    device_event_sub: Sub<DeviceEvent>,
}

impl ManagerInner {
    pub(crate) fn new(windows: Rc<RefCell<Windows>>) -> Self {
        let (wakeup_event_pub, wakeup_event_sub) = new_pubsub();
        let (device_event_pub, device_event_sub) = new_pubsub();
        Self {
            active_event_loop_jobs: RefCell::new(Vec::new()),
            windows,
            draining_foreground_loopback: RefCell::new(false),
            wakeup_event_pub,
            wakeup_event_sub,
            device_event_pub,
            device_event_sub,
        }
    }

    pub(crate) fn is_draining_foreground_loopback(&self) -> bool {
        *self.draining_foreground_loopback.borrow()
    }

    pub(crate) fn wakeup_event_pub(&self) -> Pub<WakeUpEvent> {
        self.wakeup_event_pub.clone()
    }

    pub(crate) fn device_event_pub(&self) -> Pub<DeviceEvent> {
        self.device_event_pub.clone()
    }

    pub(crate) fn drain_active_event_loop_jobs(&self, event_loop: &dyn ActiveEventLoop) -> bool {
        let mut target = Vec::new();
        std::mem::swap(&mut target, &mut self.active_event_loop_jobs.borrow_mut());
        let processed = target.len() > 0;
        for job in target.drain(..) {
            job(event_loop)
        }
        processed
    }

    async fn run_with_active_event_loop<F, T>(&self, f: F) -> T
    where
        F: FnOnce(&dyn ActiveEventLoop) -> T + 'static,
        T: 'static,
    {
        let (sender, receiver) = oneshot::channel();
        self.active_event_loop_jobs
            .borrow_mut()
            .push(Box::new(move |v| {
                let _ = sender.send(catch_unwind(AssertUnwindSafe(move || f(v))));
            }));
        match receiver.await.expect("Execution cancelled.") {
            Ok(value) => value,
            Err(payload) => resume_unwind(payload),
        }
    }
}

/// Manager of the window subsystem.
///
/// This manager is accessible from (and only from) the
/// foreground thread. It provides the interfaces to create
/// the window, winit objects and access active event loop.
pub struct Manager {
    inner: Weak<ManagerInner>,
}

impl Manager {
    fn must_upgrade_manager(&self) -> Rc<ManagerInner> {
        self.inner
            .upgrade()
            .expect("Window manager has been destroyed")
    }

    /// Set whether the event loop will drain the foreground
    /// loopback tasks.
    ///
    /// If set, the event loop will call
    /// `aeth::task::drain_foreground_loopback` automatically
    /// whenever the foreground tasks can run. Otherwise the
    /// application must invoke the draining function manually.
    pub fn set_draining_foreground_loopback(&self, value: bool) {
        let inner = self.must_upgrade_manager();
        *inner.draining_foreground_loopback.borrow_mut() = value;
    }

    /// Create a managed window instance.
    ///
    /// Please notice you should always call this function
    /// instead of grabbing the active event loop and invoke
    /// `active_event_loop.create_window` manually, otherwise
    /// there's no way to deliver window event to your window.
    /// See `aeth::window::Window` for more detail.
    pub async fn create_window(&self, attrs: WindowAttributes) -> Result<Window, RequestError> {
        let inner = self.must_upgrade_manager();
        let window = inner
            .run_with_active_event_loop(|v| v.create_window(attrs))
            .await?;
        Ok(Windows::allocate(&inner.windows, window))
    }

    /// Obtain the wakeup event subscriber.
    pub fn wakeup_event_sub(&self) -> Sub<WakeUpEvent> {
        self.must_upgrade_manager().wakeup_event_sub.clone()
    }

    /// Obtain the device event subscriber.
    pub fn device_event_sub(&self) -> Sub<DeviceEvent> {
        self.must_upgrade_manager().device_event_sub.clone()
    }
}

impl AccessWinitActiveEventLoop for Manager {
    async fn run_with_active_event_loop<F, T>(&self, f: F) -> T
    where
        F: FnOnce(&dyn ActiveEventLoop) -> T + 'static,
        T: 'static,
    {
        let inner = self.must_upgrade_manager();
        inner.run_with_active_event_loop(f).await
    }
}

thread_local! {
    pub(crate) static MANAGER: RefCell<Option<Weak<ManagerInner>>> = RefCell::new(None);
}

/// Obtain the window manager.
///
/// This function is made async deliberately to ensure it's
/// called within the async task runtime.
pub async fn manager() -> Manager {
    foreground::assert();
    MANAGER.with_borrow(|v| {
        let manager = v.as_ref().expect("Window manager is not initialized");
        Manager {
            inner: manager.clone(),
        }
    })
}