1 | //! The [`EventLoop`] struct and assorted supporting types, including |
2 | //! [`ControlFlow`]. |
3 | //! |
4 | //! If you want to send custom events to the event loop, use |
5 | //! [`EventLoop::create_proxy`] to acquire an [`EventLoopProxy`] and call its |
6 | //! [`send_event`](`EventLoopProxy::send_event`) method. |
7 | //! |
8 | //! See the root-level documentation for information on how to create and use an event loop to |
9 | //! handle events. |
10 | use std::marker::PhantomData; |
11 | use std::ops::Deref; |
12 | #[cfg (any(x11_platform, wayland_platform))] |
13 | use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; |
14 | use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; |
15 | use std::{error, fmt}; |
16 | |
17 | #[cfg (not(wasm_platform))] |
18 | use std::time::{Duration, Instant}; |
19 | #[cfg (wasm_platform)] |
20 | use web_time::{Duration, Instant}; |
21 | |
22 | use crate::error::EventLoopError; |
23 | use crate::{event::Event, monitor::MonitorHandle, platform_impl}; |
24 | |
25 | /// Provides a way to retrieve events from the system and from the windows that were registered to |
26 | /// the events loop. |
27 | /// |
28 | /// An `EventLoop` can be seen more or less as a "context". Calling [`EventLoop::new`] |
29 | /// initializes everything that will be required to create windows. For example on Linux creating |
30 | /// an event loop opens a connection to the X or Wayland server. |
31 | /// |
32 | /// To wake up an `EventLoop` from a another thread, see the [`EventLoopProxy`] docs. |
33 | /// |
34 | /// Note that this cannot be shared across threads (due to platform-dependant logic |
35 | /// forbidding it), as such it is neither [`Send`] nor [`Sync`]. If you need cross-thread access, the |
36 | /// [`Window`] created from this _can_ be sent to an other thread, and the |
37 | /// [`EventLoopProxy`] allows you to wake up an `EventLoop` from another thread. |
38 | /// |
39 | /// [`Window`]: crate::window::Window |
40 | pub struct EventLoop<T: 'static> { |
41 | pub(crate) event_loop: platform_impl::EventLoop<T>, |
42 | pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync |
43 | } |
44 | |
45 | /// Target that associates windows with an [`EventLoop`]. |
46 | /// |
47 | /// This type exists to allow you to create new windows while Winit executes |
48 | /// your callback. [`EventLoop`] will coerce into this type (`impl<T> Deref for |
49 | /// EventLoop<T>`), so functions that take this as a parameter can also take |
50 | /// `&EventLoop`. |
51 | pub struct EventLoopWindowTarget<T: 'static> { |
52 | pub(crate) p: platform_impl::EventLoopWindowTarget<T>, |
53 | pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync |
54 | } |
55 | |
56 | /// Object that allows building the event loop. |
57 | /// |
58 | /// This is used to make specifying options that affect the whole application |
59 | /// easier. But note that constructing multiple event loops is not supported. |
60 | #[derive (Default)] |
61 | pub struct EventLoopBuilder<T: 'static> { |
62 | pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes, |
63 | _p: PhantomData<T>, |
64 | } |
65 | |
66 | impl EventLoopBuilder<()> { |
67 | /// Start building a new event loop. |
68 | #[inline ] |
69 | pub fn new() -> Self { |
70 | Self::with_user_event() |
71 | } |
72 | } |
73 | |
74 | static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false); |
75 | |
76 | impl<T> EventLoopBuilder<T> { |
77 | /// Start building a new event loop, with the given type as the user event |
78 | /// type. |
79 | #[inline ] |
80 | pub fn with_user_event() -> Self { |
81 | Self { |
82 | platform_specific: Default::default(), |
83 | _p: PhantomData, |
84 | } |
85 | } |
86 | |
87 | /// Builds a new event loop. |
88 | /// |
89 | /// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread, |
90 | /// and only once per application.*** |
91 | /// |
92 | /// Calling this function will result in display backend initialisation. |
93 | /// |
94 | /// ## Panics |
95 | /// |
96 | /// Attempting to create the event loop off the main thread will panic. This |
97 | /// restriction isn't strictly necessary on all platforms, but is imposed to |
98 | /// eliminate any nasty surprises when porting to platforms that require it. |
99 | /// `EventLoopBuilderExt::any_thread` functions are exposed in the relevant |
100 | /// [`platform`] module if the target platform supports creating an event |
101 | /// loop on any thread. |
102 | /// |
103 | /// ## Platform-specific |
104 | /// |
105 | /// - **Wayland/X11:** to prevent running under `Wayland` or `X11` unset `WAYLAND_DISPLAY` |
106 | /// or `DISPLAY` respectively when building the event loop. |
107 | /// - **Android:** must be configured with an `AndroidApp` from `android_main()` by calling |
108 | /// [`.with_android_app(app)`] before calling `.build()`, otherwise it'll panic. |
109 | /// |
110 | /// [`platform`]: crate::platform |
111 | #[cfg_attr ( |
112 | android, |
113 | doc = "[`.with_android_app(app)`]: crate::platform::android::EventLoopBuilderExtAndroid::with_android_app" |
114 | )] |
115 | #[cfg_attr ( |
116 | not(android), |
117 | doc = "[`.with_android_app(app)`]: #only-available-on-android" |
118 | )] |
119 | #[inline ] |
120 | pub fn build(&mut self) -> Result<EventLoop<T>, EventLoopError> { |
121 | if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) { |
122 | return Err(EventLoopError::RecreationAttempt); |
123 | } |
124 | |
125 | // Certain platforms accept a mutable reference in their API. |
126 | #[allow (clippy::unnecessary_mut_passed)] |
127 | Ok(EventLoop { |
128 | event_loop: platform_impl::EventLoop::new(&mut self.platform_specific)?, |
129 | _marker: PhantomData, |
130 | }) |
131 | } |
132 | |
133 | #[cfg (wasm_platform)] |
134 | pub(crate) fn allow_event_loop_recreation() { |
135 | EVENT_LOOP_CREATED.store(false, Ordering::Relaxed); |
136 | } |
137 | } |
138 | |
139 | impl<T> fmt::Debug for EventLoop<T> { |
140 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
141 | f.pad("EventLoop { .. }" ) |
142 | } |
143 | } |
144 | |
145 | impl<T> fmt::Debug for EventLoopWindowTarget<T> { |
146 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
147 | f.pad("EventLoopWindowTarget { .. }" ) |
148 | } |
149 | } |
150 | |
151 | /// Set through [`EventLoopWindowTarget::set_control_flow()`]. |
152 | /// |
153 | /// Indicates the desired behavior of the event loop after [`Event::AboutToWait`] is emitted. |
154 | /// |
155 | /// Defaults to [`Wait`]. |
156 | /// |
157 | /// [`Wait`]: Self::Wait |
158 | #[derive (Copy, Clone, Debug, Default, PartialEq, Eq)] |
159 | pub enum ControlFlow { |
160 | /// When the current loop iteration finishes, immediately begin a new iteration regardless of |
161 | /// whether or not new events are available to process. |
162 | Poll, |
163 | |
164 | /// When the current loop iteration finishes, suspend the thread until another event arrives. |
165 | #[default] |
166 | Wait, |
167 | |
168 | /// When the current loop iteration finishes, suspend the thread until either another event |
169 | /// arrives or the given time is reached. |
170 | /// |
171 | /// Useful for implementing efficient timers. Applications which want to render at the display's |
172 | /// native refresh rate should instead use [`Poll`] and the VSync functionality of a graphics API |
173 | /// to reduce odds of missed frames. |
174 | /// |
175 | /// [`Poll`]: Self::Poll |
176 | WaitUntil(Instant), |
177 | } |
178 | |
179 | impl ControlFlow { |
180 | /// Creates a [`ControlFlow`] that waits until a timeout has expired. |
181 | /// |
182 | /// In most cases, this is set to [`WaitUntil`]. However, if the timeout overflows, it is |
183 | /// instead set to [`Wait`]. |
184 | /// |
185 | /// [`WaitUntil`]: Self::WaitUntil |
186 | /// [`Wait`]: Self::Wait |
187 | pub fn wait_duration(timeout: Duration) -> Self { |
188 | match Instant::now().checked_add(duration:timeout) { |
189 | Some(instant: Instant) => Self::WaitUntil(instant), |
190 | None => Self::Wait, |
191 | } |
192 | } |
193 | } |
194 | |
195 | impl EventLoop<()> { |
196 | /// Alias for [`EventLoopBuilder::new().build()`]. |
197 | /// |
198 | /// [`EventLoopBuilder::new().build()`]: EventLoopBuilder::build |
199 | #[inline ] |
200 | pub fn new() -> Result<EventLoop<()>, EventLoopError> { |
201 | EventLoopBuilder::new().build() |
202 | } |
203 | } |
204 | |
205 | impl<T> EventLoop<T> { |
206 | #[deprecated = "Use `EventLoopBuilder::<T>::with_user_event().build()` instead." ] |
207 | pub fn with_user_event() -> Result<EventLoop<T>, EventLoopError> { |
208 | EventLoopBuilder::<T>::with_user_event().build() |
209 | } |
210 | |
211 | /// Runs the event loop in the calling thread and calls the given `event_handler` closure |
212 | /// to dispatch any pending events. |
213 | /// |
214 | /// Since the closure is `'static`, it must be a `move` closure if it needs to |
215 | /// access any data from the calling context. |
216 | /// |
217 | /// See the [`set_control_flow()`] docs on how to change the event loop's behavior. |
218 | /// |
219 | /// ## Platform-specific |
220 | /// |
221 | /// - **iOS:** Will never return to the caller and so values not passed to this function will |
222 | /// *not* be dropped before the process exits. |
223 | /// - **Web:** Will _act_ as if it never returns to the caller by throwing a Javascript exception |
224 | /// (that Rust doesn't see) that will also mean that the rest of the function is never executed |
225 | /// and any values not passed to this function will *not* be dropped. |
226 | /// |
227 | /// Web applications are recommended to use |
228 | #[cfg_attr ( |
229 | wasm_platform, |
230 | doc = "[`EventLoopExtWebSys::spawn()`][crate::platform::web::EventLoopExtWebSys::spawn()]" |
231 | )] |
232 | #[cfg_attr (not(wasm_platform), doc = "`EventLoopExtWebSys::spawn()`" )] |
233 | /// [^1] instead of [`run()`] to avoid the need |
234 | /// for the Javascript exception trick, and to make it clearer that the event loop runs |
235 | /// asynchronously (via the browser's own, internal, event loop) and doesn't block the |
236 | /// current thread of execution like it does on other platforms. |
237 | /// |
238 | /// This function won't be available with `target_feature = "exception-handling"`. |
239 | /// |
240 | /// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow() |
241 | /// [`run()`]: Self::run() |
242 | /// [^1]: `EventLoopExtWebSys::spawn()` is only available on WASM. |
243 | #[inline ] |
244 | #[cfg (not(all(wasm_platform, target_feature = "exception-handling" )))] |
245 | pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError> |
246 | where |
247 | F: FnMut(Event<T>, &EventLoopWindowTarget<T>), |
248 | { |
249 | self.event_loop.run(event_handler) |
250 | } |
251 | |
252 | /// Creates an [`EventLoopProxy`] that can be used to dispatch user events to the main event loop. |
253 | pub fn create_proxy(&self) -> EventLoopProxy<T> { |
254 | EventLoopProxy { |
255 | event_loop_proxy: self.event_loop.create_proxy(), |
256 | } |
257 | } |
258 | } |
259 | |
260 | #[cfg (feature = "rwh_06" )] |
261 | impl<T> rwh_06::HasDisplayHandle for EventLoop<T> { |
262 | fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> { |
263 | rwh_06::HasDisplayHandle::display_handle(&**self) |
264 | } |
265 | } |
266 | |
267 | #[cfg (feature = "rwh_05" )] |
268 | unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoop<T> { |
269 | /// Returns a [`rwh_05::RawDisplayHandle`] for the event loop. |
270 | fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle { |
271 | rwh_05::HasRawDisplayHandle::raw_display_handle(&**self) |
272 | } |
273 | } |
274 | |
275 | #[cfg (any(x11_platform, wayland_platform))] |
276 | impl<T> AsFd for EventLoop<T> { |
277 | /// Get the underlying [EventLoop]'s `fd` which you can register |
278 | /// into other event loop, like [`calloop`] or [`mio`]. When doing so, the |
279 | /// loop must be polled with the [`pump_events`] API. |
280 | /// |
281 | /// [`calloop`]: https://crates.io/crates/calloop |
282 | /// [`mio`]: https://crates.io/crates/mio |
283 | /// [`pump_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_events |
284 | fn as_fd(&self) -> BorrowedFd<'_> { |
285 | self.event_loop.as_fd() |
286 | } |
287 | } |
288 | |
289 | #[cfg (any(x11_platform, wayland_platform))] |
290 | impl<T> AsRawFd for EventLoop<T> { |
291 | /// Get the underlying [EventLoop]'s raw `fd` which you can register |
292 | /// into other event loop, like [`calloop`] or [`mio`]. When doing so, the |
293 | /// loop must be polled with the [`pump_events`] API. |
294 | /// |
295 | /// [`calloop`]: https://crates.io/crates/calloop |
296 | /// [`mio`]: https://crates.io/crates/mio |
297 | /// [`pump_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_events |
298 | fn as_raw_fd(&self) -> RawFd { |
299 | self.event_loop.as_raw_fd() |
300 | } |
301 | } |
302 | |
303 | impl<T> Deref for EventLoop<T> { |
304 | type Target = EventLoopWindowTarget<T>; |
305 | fn deref(&self) -> &EventLoopWindowTarget<T> { |
306 | self.event_loop.window_target() |
307 | } |
308 | } |
309 | |
310 | impl<T> EventLoopWindowTarget<T> { |
311 | /// Returns the list of all the monitors available on the system. |
312 | #[inline ] |
313 | pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> { |
314 | #[allow (clippy::useless_conversion)] // false positive on some platforms |
315 | self.p |
316 | .available_monitors() |
317 | .into_iter() |
318 | .map(|inner| MonitorHandle { inner }) |
319 | } |
320 | |
321 | /// Returns the primary monitor of the system. |
322 | /// |
323 | /// Returns `None` if it can't identify any monitor as a primary one. |
324 | /// |
325 | /// ## Platform-specific |
326 | /// |
327 | /// **Wayland / Web:** Always returns `None`. |
328 | #[inline ] |
329 | pub fn primary_monitor(&self) -> Option<MonitorHandle> { |
330 | self.p |
331 | .primary_monitor() |
332 | .map(|inner| MonitorHandle { inner }) |
333 | } |
334 | |
335 | /// Change if or when [`DeviceEvent`]s are captured. |
336 | /// |
337 | /// Since the [`DeviceEvent`] capture can lead to high CPU usage for unfocused windows, winit |
338 | /// will ignore them by default for unfocused windows on Linux/BSD. This method allows changing |
339 | /// this at runtime to explicitly capture them again. |
340 | /// |
341 | /// ## Platform-specific |
342 | /// |
343 | /// - **Wayland / macOS / iOS / Android / Orbital:** Unsupported. |
344 | /// |
345 | /// [`DeviceEvent`]: crate::event::DeviceEvent |
346 | pub fn listen_device_events(&self, allowed: DeviceEvents) { |
347 | self.p.listen_device_events(allowed); |
348 | } |
349 | |
350 | /// Sets the [`ControlFlow`]. |
351 | pub fn set_control_flow(&self, control_flow: ControlFlow) { |
352 | self.p.set_control_flow(control_flow) |
353 | } |
354 | |
355 | /// Gets the current [`ControlFlow`]. |
356 | pub fn control_flow(&self) -> ControlFlow { |
357 | self.p.control_flow() |
358 | } |
359 | |
360 | /// This exits the event loop. |
361 | /// |
362 | /// See [`LoopExiting`](Event::LoopExiting). |
363 | pub fn exit(&self) { |
364 | self.p.exit() |
365 | } |
366 | |
367 | /// Returns if the [`EventLoop`] is about to stop. |
368 | /// |
369 | /// See [`exit()`](Self::exit). |
370 | pub fn exiting(&self) -> bool { |
371 | self.p.exiting() |
372 | } |
373 | } |
374 | |
375 | #[cfg (feature = "rwh_06" )] |
376 | impl<T> rwh_06::HasDisplayHandle for EventLoopWindowTarget<T> { |
377 | fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> { |
378 | let raw: RawDisplayHandle = self.p.raw_display_handle_rwh_06()?; |
379 | // SAFETY: The display will never be deallocated while the event loop is alive. |
380 | Ok(unsafe { rwh_06::DisplayHandle::borrow_raw(raw) }) |
381 | } |
382 | } |
383 | |
384 | #[cfg (feature = "rwh_05" )] |
385 | unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoopWindowTarget<T> { |
386 | /// Returns a [`rwh_05::RawDisplayHandle`] for the event loop. |
387 | fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle { |
388 | self.p.raw_display_handle_rwh_05() |
389 | } |
390 | } |
391 | |
392 | /// Used to send custom events to [`EventLoop`]. |
393 | pub struct EventLoopProxy<T: 'static> { |
394 | event_loop_proxy: platform_impl::EventLoopProxy<T>, |
395 | } |
396 | |
397 | impl<T: 'static> Clone for EventLoopProxy<T> { |
398 | fn clone(&self) -> Self { |
399 | Self { |
400 | event_loop_proxy: self.event_loop_proxy.clone(), |
401 | } |
402 | } |
403 | } |
404 | |
405 | impl<T: 'static> EventLoopProxy<T> { |
406 | /// Send an event to the [`EventLoop`] from which this proxy was created. This emits a |
407 | /// `UserEvent(event)` event in the event loop, where `event` is the value passed to this |
408 | /// function. |
409 | /// |
410 | /// Returns an `Err` if the associated [`EventLoop`] no longer exists. |
411 | /// |
412 | /// [`UserEvent(event)`]: Event::UserEvent |
413 | pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> { |
414 | self.event_loop_proxy.send_event(event) |
415 | } |
416 | } |
417 | |
418 | impl<T: 'static> fmt::Debug for EventLoopProxy<T> { |
419 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
420 | f.pad("EventLoopProxy { .. }" ) |
421 | } |
422 | } |
423 | |
424 | /// The error that is returned when an [`EventLoopProxy`] attempts to wake up an [`EventLoop`] that |
425 | /// no longer exists. |
426 | /// |
427 | /// Contains the original event given to [`EventLoopProxy::send_event`]. |
428 | #[derive (Copy, Clone, Debug, PartialEq, Eq, Hash)] |
429 | pub struct EventLoopClosed<T>(pub T); |
430 | |
431 | impl<T> fmt::Display for EventLoopClosed<T> { |
432 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
433 | f.write_str(data:"Tried to wake up a closed `EventLoop`" ) |
434 | } |
435 | } |
436 | |
437 | impl<T: fmt::Debug> error::Error for EventLoopClosed<T> {} |
438 | |
439 | /// Control when device events are captured. |
440 | #[derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] |
441 | pub enum DeviceEvents { |
442 | /// Report device events regardless of window focus. |
443 | Always, |
444 | /// Only capture device events while the window is focused. |
445 | #[default] |
446 | WhenFocused, |
447 | /// Never capture device events. |
448 | Never, |
449 | } |
450 | |
451 | /// A unique identifier of the winit's async request. |
452 | /// |
453 | /// This could be used to identify the async request once it's done |
454 | /// and a specific action must be taken. |
455 | /// |
456 | /// One of the handling scenarious could be to maintain a working list |
457 | /// containing [`AsyncRequestSerial`] and some closure associated with it. |
458 | /// Then once event is arriving the working list is being traversed and a job |
459 | /// executed and removed from the list. |
460 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
461 | pub struct AsyncRequestSerial { |
462 | serial: usize, |
463 | } |
464 | |
465 | impl AsyncRequestSerial { |
466 | // TODO(kchibisov): Remove `cfg` when the clipboard will be added. |
467 | #[allow (dead_code)] |
468 | pub(crate) fn get() -> Self { |
469 | static CURRENT_SERIAL: AtomicUsize = AtomicUsize::new(0); |
470 | // NOTE: We rely on wrap around here, while the user may just request |
471 | // in the loop usize::MAX times that's issue is considered on them. |
472 | let serial: usize = CURRENT_SERIAL.fetch_add(val:1, order:Ordering::Relaxed); |
473 | Self { serial } |
474 | } |
475 | } |
476 | |