async_winit/
event_loop.rs

1/*
2
3`async-winit` is free software: you can redistribute it and/or modify it under the terms of one of
4the following licenses:
5
6* GNU Lesser General Public License as published by the Free Software Foundation, either
7  version 3 of the License, or (at your option) any later version.
8* Mozilla Public License as published by the Mozilla Foundation, version 2.
9
10`async-winit` is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
11the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
12Public License and the Patron License for more details.
13
14You should have received a copy of the GNU Lesser General Public License and the Mozilla
15Public License along with `async-winit`. If not, see <https://www.gnu.org/licenses/>.
16
17*/
18
19//! The [`EventLoop`] and associated structures.
20//!
21//! There are three main differences between [`EventLoop`]s here and in [`winit`]:
22//!
23//! - Instead of `run` or `run_return`, there are `block_on` and `block_on_return`, which take a future
24//!   and run it to completion. Eent handling is done through the [`Handler`] structures instead.
25//! - Methods on [`EventLoop`] and [`EventLoopWindowTarget`] are `async`.
26//! - There is no `EventLoopProxy` type, since it is now obsolete with `async` blocks. Instead,
27//!   consider using an async channel to communicate with the event loop.
28//!
29//! ```no_run
30//! use async_winit::event_loop::EventLoop;
31//! use async_winit::ThreadUnsafe;
32//!
33//! struct MyCustomType;
34//!
35//! let (sender, receiver) = async_channel::unbounded();
36//!
37//! EventLoop::<ThreadUnsafe>::new().block_on(async move {
38//!     // Wait for a message from the channel.
39//!     let message = receiver.recv().await.unwrap();
40//! #   futures_lite::future::pending().await
41//! });
42//!
43//! // In another thread, send a message to the event loop.
44//! # futures_lite::future::block_on(async move {
45//! sender.send(MyCustomType).await.unwrap();
46//! # });
47//! ```
48//!
49//! [`Handler`]: crate::Handler
50
51use crate::handler::Handler;
52use crate::reactor::{EventLoopOp, Reactor};
53use crate::sync::ThreadSafety;
54use crate::DefaultThreadSafety;
55
56use std::convert::Infallible;
57use std::fmt;
58use std::future::Future;
59use std::ops;
60
61use raw_window_handle::{HasRawDisplayHandle, RawDisplayHandle};
62use winit::event_loop::EventLoopProxy;
63
64#[doc(inline)]
65pub use winit::event_loop::{ControlFlow, DeviceEventFilter, EventLoopClosed};
66
67/// Used to indicate that we need to wake up the event loop.
68///
69/// This is a ZST used by the underlying event loop to wake up the event loop. It is not used
70/// directly by the user.
71///
72/// It is public because it is used by the [`Filter`] type. Generally, you don't need to use it.
73///
74/// [`Filter`]: crate::filter::Filter
75#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
76pub struct Wakeup {
77    pub(crate) _private: (),
78}
79
80/// Provides a way to retrieve events from the system and from the windows that were registered to
81/// the events loop.
82///
83/// The [`EventLoop`] is a "context" for the GUI system. More specifically, it represents a connection
84/// to the underlying GUI system. The [`EventLoop`] is the main object that you will use to drive
85/// the program. Most `async` functions in `async-winit` rely on the [`EventLoop`] to be currently
86/// running.
87///
88/// The [`EventLoop`] itself is `!Send` and `!Sync` due to underlying platform restrictions. However,
89/// [`EventLoopWindowTarget`]` and [`Window`] are both not only `Send` and `Sync`, but also cheaply
90/// clonable. This means that you can create a window on one thread, and then send it to another
91/// thread to be used.
92///
93/// [`Window`]: crate::window::Window
94pub struct EventLoop<TS: ThreadSafety = DefaultThreadSafety> {
95    /// The underlying event loop.
96    pub(crate) inner: winit::event_loop::EventLoop<Wakeup>,
97
98    /// The window target.
99    window_target: EventLoopWindowTarget<TS>,
100}
101
102impl<TS: ThreadSafety> fmt::Debug for EventLoop<TS> {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        f.write_str("EventLoop { .. }")
105    }
106}
107
108/// A reference to the `EventLoop` that allows the user access to the underlying display connections.
109///
110/// Unlike in `winit`, this type is cheaply clonable. It is not actually used that often, since most of
111/// its previous use cases don't directly require the window target to be passed in. However, it is
112/// still useful for some things, like indicating the need to exit the application or getting
113/// available monitors.
114pub struct EventLoopWindowTarget<TS: ThreadSafety = DefaultThreadSafety> {
115    /// The associated reactor, cached for convenience.
116    reactor: TS::Rc<Reactor<TS>>,
117
118    /// The event loop proxy.
119    proxy: EventLoopProxy<Wakeup>,
120
121    /// The raw display handle.
122    raw_display_handle: RawDisplayHandle,
123
124    /// Is this using wayland?
125    #[cfg(any(x11_platform, wayland_platform))]
126    pub(crate) is_wayland: bool,
127}
128
129impl<TS: ThreadSafety> fmt::Debug for EventLoopWindowTarget<TS> {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        f.write_str("EventLoopWindowTarget { .. }")
132    }
133}
134
135impl<TS: ThreadSafety> Clone for EventLoopWindowTarget<TS> {
136    fn clone(&self) -> Self {
137        Self {
138            reactor: self.reactor.clone(),
139            proxy: self.proxy.clone(),
140            raw_display_handle: self.raw_display_handle,
141            #[cfg(any(x11_platform, wayland_platform))]
142            is_wayland: self.is_wayland,
143        }
144    }
145}
146
147/// Object that allows for building the [`EventLoop`].
148///
149/// This specifies options that affect the whole application, like the current Android app or whether
150/// to use the Wayland backend. You cannot create more than one [`EventLoop`] per application.
151pub struct EventLoopBuilder {
152    /// The underlying builder.
153    pub(crate) inner: winit::event_loop::EventLoopBuilder<Wakeup>,
154}
155
156impl fmt::Debug for EventLoopBuilder {
157    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158        f.write_str("EventLoopBuilder { .. }")
159    }
160}
161
162impl EventLoopBuilder {
163    /// Create a new [`EventLoopBuilder`].
164    pub fn new() -> Self {
165        Self {
166            inner: winit::event_loop::EventLoopBuilder::with_user_event(),
167        }
168    }
169
170    /// Builds a new event loop.
171    ///
172    /// In general, this function must be called on the same thread that `main()` is being run inside of.
173    /// This can be circumvented in some cases using platform specific options. See the [`platform`]
174    /// module for more information. Attempting to violate this property or create more than one event
175    /// loop per application will result in a panic.
176    ///
177    /// This function results in platform-specific backend initialization.
178    ///
179    /// [`platform`]: crate::platform
180    pub fn build<TS: ThreadSafety>(&mut self) -> EventLoop<TS> {
181        let inner = self.inner.build();
182        EventLoop {
183            window_target: EventLoopWindowTarget {
184                reactor: Reactor::<TS>::get(),
185                proxy: inner.create_proxy(),
186                raw_display_handle: inner.raw_display_handle(),
187                #[cfg(any(x11_platform, wayland_platform))]
188                is_wayland: {
189                    cfg_if::cfg_if! {
190                        if #[cfg(feature = "x11")] {
191                            use winit::platform::x11::EventLoopWindowTargetExtX11;
192                            !inner.is_x11()
193                        } else if #[cfg(feature = "wayland")] {
194                            use winit::platform::wayland::EventLoopWindowTargetExtWayland;
195                            inner.is_wayland()
196                        } else {
197                            false
198                        }
199                    }
200                },
201            },
202            inner,
203        }
204    }
205}
206
207impl Default for EventLoopBuilder {
208    fn default() -> Self {
209        Self::new()
210    }
211}
212
213unsafe impl<TS: ThreadSafety> HasRawDisplayHandle for EventLoop<TS> {
214    fn raw_display_handle(&self) -> RawDisplayHandle {
215        self.window_target.raw_display_handle
216    }
217}
218
219impl<TS: ThreadSafety> EventLoop<TS> {
220    /// Alias for [`EventLoopBuilder::new().build()`].
221    ///
222    /// [`EventLoopBuilder::new().build()`]: EventLoopBuilder::build
223    #[inline]
224    pub fn new() -> EventLoop<TS> {
225        EventLoopBuilder::new().build()
226    }
227}
228
229impl<TS: ThreadSafety> Default for EventLoop<TS> {
230    #[inline]
231    fn default() -> Self {
232        Self::new()
233    }
234}
235
236impl<TS: ThreadSafety> EventLoopWindowTarget<TS> {
237    /// Request that the event loop exit as soon as possible.
238    #[inline]
239    pub fn set_exit(&self) {
240        self.reactor.request_exit(0);
241    }
242
243    /// Request that we exit as soon as possible with the given exit code.
244    #[inline]
245    pub fn set_exit_with_code(&self, code: i32) {
246        self.reactor.request_exit(code);
247    }
248
249    /// Exit the program.
250    #[inline]
251    pub async fn exit(&self) -> ! {
252        self.set_exit();
253        futures_lite::future::pending().await
254    }
255
256    /// Exit the program with the given exit code.
257    #[inline]
258    pub async fn exit_with_code(&self, code: i32) -> ! {
259        self.set_exit_with_code(code);
260        futures_lite::future::pending().await
261    }
262
263    /// Get the handler for the `Resumed` event.
264    #[inline]
265    pub fn resumed(&self) -> &Handler<(), TS> {
266        &self.reactor.evl_registration.resumed
267    }
268
269    /// Get the handler for the `Suspended` event.
270    #[inline]
271    pub fn suspended(&self) -> &Handler<(), TS> {
272        &self.reactor.evl_registration.suspended
273    }
274
275    /// Get the primary monitor.
276    #[inline]
277    pub async fn primary_monitor(&self) -> Option<winit::monitor::MonitorHandle> {
278        let (tx, rx) = crate::oneoff::oneoff();
279        self.reactor
280            .push_event_loop_op(EventLoopOp::PrimaryMonitor(tx))
281            .await;
282        rx.recv().await
283    }
284
285    /// Get the available monitors.
286    #[inline]
287    pub async fn available_monitors(&self) -> impl Iterator<Item = winit::monitor::MonitorHandle> {
288        let (tx, rx) = crate::oneoff::oneoff();
289        self.reactor
290            .push_event_loop_op(EventLoopOp::AvailableMonitors(tx))
291            .await;
292        rx.recv().await.into_iter()
293    }
294
295    /// Set the device event filter.
296    #[inline]
297    pub async fn set_device_event_filter(&self, filter: DeviceEventFilter) {
298        let (tx, rx) = crate::oneoff::oneoff();
299        self.reactor
300            .push_event_loop_op(EventLoopOp::SetDeviceFilter { filter, waker: tx })
301            .await;
302
303        // Wait for the filter to be set.
304        rx.recv().await;
305    }
306}
307
308unsafe impl<TS: ThreadSafety> HasRawDisplayHandle for EventLoopWindowTarget<TS> {
309    fn raw_display_handle(&self) -> RawDisplayHandle {
310        self.raw_display_handle
311    }
312}
313
314impl<TS: ThreadSafety + 'static> EventLoop<TS> {
315    /// Manually get a reference to the event loop's window target.
316    #[inline]
317    pub fn window_target(&self) -> &EventLoopWindowTarget<TS> {
318        &self.window_target
319    }
320
321    /// Block on a future forever.
322    #[inline]
323    pub fn block_on(self, future: impl Future<Output = Infallible> + 'static) -> ! {
324        let inner = self.inner;
325
326        let mut future = Box::pin(future);
327        let mut filter = crate::filter::Filter::<TS>::new(&inner);
328
329        inner.run(move |event, elwt, flow| {
330            filter.handle_event(future.as_mut(), event, elwt, flow);
331        })
332    }
333}
334
335impl<TS: ThreadSafety> ops::Deref for EventLoop<TS> {
336    type Target = EventLoopWindowTarget<TS>;
337
338    #[inline]
339    fn deref(&self) -> &Self::Target {
340        &self.window_target
341    }
342}
343
344impl<TS: ThreadSafety> ops::DerefMut for EventLoop<TS> {
345    #[inline]
346    fn deref_mut(&mut self) -> &mut Self::Target {
347        &mut self.window_target
348    }
349}