winit_block_on/
lib.rs

1// SPDX-License-Identifier: BSL-1.0 OR Apache-2.0
2//               Copyright John Nunley, 2023.
3// Distributed under the Boost Software License, Version 1.0 or the Apache
4//                 License, Version 2.0.
5//       (See accompanying file LICENSE or copy at
6//         https://www.boost.org/LICENSE_1_0.txt)
7
8//! A simple wrapper around `winit` that allows one to block on a future using the `winit` event loop.
9//!
10//! `winit` does not support `async` programming by default. This crate provides a small workaround
11//! that allows one to block on a future using the `winit` event loop.
12//!
13//! ## Examples
14//!
15//! ```no_run
16//! use winit::event::{Event, WindowEvent};
17//! use winit::event_loop::{ControlFlow, EventLoopBuilder};
18//! use winit::window::WindowBuilder;
19//! use winit_block_on::prelude::*;
20//!
21//! use std::future::pending;
22//! use std::time::Duration;
23//!
24//! // Create an event loop.
25//! let event_loop = EventLoopBuilder::new_block_on().build();
26//!
27//! // Create a window inside the event loop.
28//! let window = WindowBuilder::new().build(&event_loop).unwrap();
29//!
30//! // Create a proxy that can be used to send events to the event loop.
31//! let proxy = event_loop.create_proxy();
32//!
33//! // Block on the future indefinitely.
34//! event_loop.block_on(
35//!     move |event, _, control_flow| {
36//!         match event {
37//!             Event::UserEvent(()) => control_flow.set_exit(),
38//!             Event::WindowEvent {
39//!                 event: WindowEvent::CloseRequested,
40//!                 window_id
41//!             } if window_id == window.id() => control_flow.set_exit(),
42//!             _ => {}
43//!         }
44//!     },
45//!     async move {
46//!         // Wait for one second.
47//!         async_io::Timer::after(Duration::from_secs(1)).await;
48//!
49//!         // Tell the event loop to close.
50//!         proxy.send_event(()).unwrap();
51//!     }
52//! )
53//! ```
54//!
55//! This is a contrived example, since `control_flow.set_wait_deadline()` can do the same thing. See
56//! the `networking` example for a more complex and in-depth example of combining `async` with `winit`.
57//!
58//! ## Limitations
59//!
60//! In both cases, the user event `T` needs to be `Send` and `'static`. This is because the event loop
61//! proxy needs to be put into a `Waker`. In addition, if you are not using `run_return`, the future
62//! needs to be `'static`. Both of these limitations could be removed if `block_on` were integrated
63//! directly into `winit`.
64
65#![forbid(unsafe_code)]
66
67mod run_return;
68pub use run_return::*;
69
70use std::convert::Infallible;
71use std::future::Future;
72use std::sync::{Arc, Mutex};
73use std::task::{Context, Wake, Waker};
74
75use winit::event::Event;
76use winit::event_loop::{
77    ControlFlow, EventLoop, EventLoopBuilder, EventLoopProxy, EventLoopWindowTarget as Elwt,
78};
79
80/// Import all relevant traits for this crate.
81pub mod prelude {
82    pub use super::{EventLoopBuilderExt, EventLoopExt};
83}
84
85/// An extension trait for `EventLoop` that allows one to block on a future.
86pub trait EventLoopExt {
87    type User;
88
89    /// Block on the provided future indefinitely.
90    fn block_on<F, Fut>(self, handler: F, fut: Fut) -> !
91    where
92        F: FnMut(Event<'_, Self::User>, &Elwt<Signal<Self::User>>, &mut ControlFlow) + 'static,
93        Fut: Future<Output = Infallible> + 'static;
94}
95
96impl<T: Send + 'static> EventLoopExt for EventLoop<Signal<T>> {
97    type User = T;
98
99    fn block_on<F, Fut>(self, mut handler: F, fut: Fut) -> !
100    where
101        F: FnMut(Event<'_, Self::User>, &Elwt<Signal<Self::User>>, &mut ControlFlow) + 'static,
102        Fut: Future<Output = Infallible> + 'static,
103    {
104        // We need to pin the future on the heap, since the callback needs to be movable.
105        let mut fut = Box::pin(fut);
106        let mut ready = true;
107
108        // Create a waker that will wake up the event loop.
109        let waker = make_proxy_waker(&self);
110
111        self.run(move |event, target, control_flow| {
112            // If we got a wakeup signal, process it.
113            match event {
114                Event::UserEvent(Signal(Inner::Wakeup)) => {
115                    // Make sure the future is ready to wake up.
116                    ready = true;
117                }
118
119                Event::UserEvent(Signal(Inner::User(user))) => {
120                    // Forward the user event to the inner callback.
121                    let event = Event::UserEvent(user);
122                    handler(event, target, control_flow);
123                }
124
125                Event::RedrawEventsCleared => {
126                    // The handler may be interested in this event.
127                    handler(Event::RedrawEventsCleared, target, control_flow);
128
129                    // Since we are no longer blocking any events, we can process the future.
130                    if ready {
131                        ready = false;
132
133                        // Eat the unused poll warning, it should never be ready anyways.
134                        let _ = fut.as_mut().poll(&mut Context::from_waker(&waker));
135                    }
136                }
137
138                event => {
139                    // This is another type of event, so forward it to the inner callback.
140                    let event: Event<T> =
141                        event.map_nonuser_event().unwrap_or_else(|_| unreachable!());
142                    handler(event, target, control_flow);
143                }
144            }
145        })
146    }
147}
148
149fn make_proxy_waker<T: Send + 'static>(evl: &EventLoop<Signal<T>>) -> Waker {
150    let proxy = evl.create_proxy();
151    Waker::from(Arc::new(ProxyWaker(Mutex::new(proxy))))
152}
153
154struct ProxyWaker<T: 'static>(Mutex<EventLoopProxy<Signal<T>>>);
155
156impl<T> Wake for ProxyWaker<T> {
157    fn wake(self: Arc<Self>) {
158        self.0
159            .lock()
160            .unwrap()
161            .send_event(Signal(Inner::Wakeup))
162            .ok();
163    }
164
165    fn wake_by_ref(self: &Arc<Self>) {
166        self.0
167            .lock()
168            .unwrap()
169            .send_event(Signal(Inner::Wakeup))
170            .ok();
171    }
172}
173
174/// An extension trait for `EventLoopBuilder` that allows one to construct an
175/// event loop that can block on the future.
176pub trait EventLoopBuilderExt {
177    /// Starts building a new async event loop.
178    fn new_block_on() -> Self;
179}
180
181impl<T> EventLoopBuilderExt for EventLoopBuilder<Signal<T>> {
182    fn new_block_on() -> Self {
183        Self::with_user_event()
184    }
185}
186
187/// The signal used to notify the event loop that it should wake up.
188pub struct Signal<T>(Inner<T>);
189
190impl<T> From<T> for Signal<T> {
191    fn from(t: T) -> Self {
192        Self(Inner::User(t))
193    }
194}
195
196enum Inner<T> {
197    User(T),
198    Wakeup,
199}