Skip to main content

aeth_window/
manager.rs

1use crate::access_winit_active_event_loop::AccessWinitActiveEventLoop;
2use crate::window::{Window, Windows};
3use aeth_event::{Pub, Sub, new_pubsub};
4use aeth_task::foreground;
5use futures::channel::oneshot;
6use std::cell::RefCell;
7use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
8use std::rc::{Rc, Weak};
9use winit::error::RequestError;
10use winit::event::{DeviceEvent as WinitDeviceEvent, DeviceId};
11use winit::event_loop::ActiveEventLoop;
12use winit::window::WindowAttributes;
13
14type ActiveEventLoopJob = Box<dyn FnOnce(&dyn ActiveEventLoop)>;
15
16/// Wake-up event generated by [`EventLoopProxy.wake_up()`].
17///
18/// [`EventLoopProxy.wake_up()`]: winit::event_loop::EventLoopProxy::wake_up
19#[derive(Clone)]
20pub struct WakeUpEvent;
21
22/// Bundled winit device event with device id.
23#[derive(Clone)]
24pub struct DeviceEvent {
25    pub event: WinitDeviceEvent,
26    pub device_id: Option<DeviceId>,
27}
28
29pub(crate) struct ManagerInner {
30    active_event_loop_jobs: RefCell<Vec<ActiveEventLoopJob>>,
31    windows: Rc<RefCell<Windows>>,
32    draining_foreground_loopback: RefCell<bool>,
33    wakeup_event_pub: Pub<WakeUpEvent>,
34    wakeup_event_sub: Sub<WakeUpEvent>,
35    device_event_pub: Pub<DeviceEvent>,
36    device_event_sub: Sub<DeviceEvent>,
37}
38
39impl ManagerInner {
40    pub(crate) fn new(windows: Rc<RefCell<Windows>>) -> Self {
41        let (wakeup_event_pub, wakeup_event_sub) = new_pubsub();
42        let (device_event_pub, device_event_sub) = new_pubsub();
43        Self {
44            active_event_loop_jobs: RefCell::new(Vec::new()),
45            windows,
46            draining_foreground_loopback: RefCell::new(false),
47            wakeup_event_pub,
48            wakeup_event_sub,
49            device_event_pub,
50            device_event_sub,
51        }
52    }
53
54    pub(crate) fn is_draining_foreground_loopback(&self) -> bool {
55        *self.draining_foreground_loopback.borrow()
56    }
57
58    pub(crate) fn wakeup_event_pub(&self) -> Pub<WakeUpEvent> {
59        self.wakeup_event_pub.clone()
60    }
61
62    pub(crate) fn device_event_pub(&self) -> Pub<DeviceEvent> {
63        self.device_event_pub.clone()
64    }
65
66    pub(crate) fn drain_active_event_loop_jobs(&self, event_loop: &dyn ActiveEventLoop) -> bool {
67        let mut target = Vec::new();
68        std::mem::swap(&mut target, &mut self.active_event_loop_jobs.borrow_mut());
69        let processed = target.len() > 0;
70        for job in target.drain(..) {
71            job(event_loop)
72        }
73        processed
74    }
75
76    async fn run_with_active_event_loop<F, T>(&self, f: F) -> T
77    where
78        F: FnOnce(&dyn ActiveEventLoop) -> T + 'static,
79        T: 'static,
80    {
81        let (sender, receiver) = oneshot::channel();
82        self.active_event_loop_jobs
83            .borrow_mut()
84            .push(Box::new(move |v| {
85                let _ = sender.send(catch_unwind(AssertUnwindSafe(move || f(v))));
86            }));
87        match receiver.await.expect("Execution cancelled.") {
88            Ok(value) => value,
89            Err(payload) => resume_unwind(payload),
90        }
91    }
92}
93
94/// Manager of the window subsystem.
95///
96/// This manager is accessible from (and only from) the
97/// foreground thread. It provides the interfaces to create
98/// the window, winit objects and access active event loop.
99pub struct Manager {
100    inner: Weak<ManagerInner>,
101}
102
103impl Manager {
104    fn must_upgrade_manager(&self) -> Rc<ManagerInner> {
105        self.inner
106            .upgrade()
107            .expect("Window manager has been destroyed")
108    }
109
110    /// Set whether the event loop will drain the foreground
111    /// loopback tasks.
112    ///
113    /// If set, the event loop will call
114    /// `aeth::task::drain_foreground_loopback` automatically
115    /// whenever the foreground tasks can run. Otherwise the
116    /// application must invoke the draining function manually.
117    pub fn set_draining_foreground_loopback(&self, value: bool) {
118        let inner = self.must_upgrade_manager();
119        *inner.draining_foreground_loopback.borrow_mut() = value;
120    }
121
122    /// Create a managed window instance.
123    ///
124    /// Please notice you should always call this function
125    /// instead of grabbing the active event loop and invoke
126    /// `active_event_loop.create_window` manually, otherwise
127    /// there's no way to deliver window event to your window.
128    /// See `aeth::window::Window` for more detail.
129    pub async fn create_window(&self, attrs: WindowAttributes) -> Result<Window, RequestError> {
130        let inner = self.must_upgrade_manager();
131        let window = inner
132            .run_with_active_event_loop(|v| v.create_window(attrs))
133            .await?;
134        Ok(Windows::allocate(&inner.windows, window))
135    }
136
137    /// Obtain the wakeup event subscriber.
138    pub fn wakeup_event_sub(&self) -> Sub<WakeUpEvent> {
139        self.must_upgrade_manager().wakeup_event_sub.clone()
140    }
141
142    /// Obtain the device event subscriber.
143    pub fn device_event_sub(&self) -> Sub<DeviceEvent> {
144        self.must_upgrade_manager().device_event_sub.clone()
145    }
146}
147
148impl AccessWinitActiveEventLoop for Manager {
149    async fn run_with_active_event_loop<F, T>(&self, f: F) -> T
150    where
151        F: FnOnce(&dyn ActiveEventLoop) -> T + 'static,
152        T: 'static,
153    {
154        let inner = self.must_upgrade_manager();
155        inner.run_with_active_event_loop(f).await
156    }
157}
158
159thread_local! {
160    pub(crate) static MANAGER: RefCell<Option<Weak<ManagerInner>>> = RefCell::new(None);
161}
162
163/// Obtain the window manager.
164///
165/// This function is made async deliberately to ensure it's
166/// called within the async task runtime.
167pub async fn manager() -> Manager {
168    foreground::assert();
169    MANAGER.with_borrow(|v| {
170        let manager = v.as_ref().expect("Window manager is not initialized");
171        Manager {
172            inner: manager.clone(),
173        }
174    })
175}