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}