winit/event_loop.rs
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//! [`wake_up`][EventLoopProxy::wake_up] method. Then during handling the wake up
7//! you can poll your event sources.
8//!
9//! See the root-level documentation for information on how to create and use an event loop to
10//! handle events.
11use std::fmt;
12use std::marker::PhantomData;
13#[cfg(any(x11_platform, wayland_platform))]
14use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
15use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
16#[cfg(not(web_platform))]
17use std::time::{Duration, Instant};
18
19#[cfg(web_platform)]
20use web_time::{Duration, Instant};
21
22use crate::application::ApplicationHandler;
23use crate::error::{EventLoopError, RequestError};
24use crate::monitor::MonitorHandle;
25use crate::platform_impl;
26use crate::utils::AsAny;
27use crate::window::{CustomCursor, CustomCursorSource, Theme, Window, WindowAttributes};
28
29/// Provides a way to retrieve events from the system and from the windows that were registered to
30/// the events loop.
31///
32/// An `EventLoop` can be seen more or less as a "context". Calling [`EventLoop::new`]
33/// initializes everything that will be required to create windows. For example on Linux creating
34/// an event loop opens a connection to the X or Wayland server.
35///
36/// To wake up an `EventLoop` from a another thread, see the [`EventLoopProxy`] docs.
37///
38/// Note that this cannot be shared across threads (due to platform-dependant logic
39/// forbidding it), as such it is neither [`Send`] nor [`Sync`]. If you need cross-thread access,
40/// the [`Window`] created from this _can_ be sent to an other thread, and the
41/// [`EventLoopProxy`] allows you to wake up an `EventLoop` from another thread.
42///
43/// [`Window`]: crate::window::Window
44pub struct EventLoop {
45 pub(crate) event_loop: platform_impl::EventLoop,
46 pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync
47}
48
49/// Object that allows building the event loop.
50///
51/// This is used to make specifying options that affect the whole application
52/// easier. But note that constructing multiple event loops is not supported.
53///
54/// This can be created using [`EventLoop::builder`].
55#[derive(Default, PartialEq, Eq, Hash)]
56pub struct EventLoopBuilder {
57 pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes,
58}
59
60static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
61
62impl EventLoopBuilder {
63 /// Builds a new event loop.
64 ///
65 /// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread,
66 /// and only once per application.***
67 ///
68 /// Calling this function will result in display backend initialisation.
69 ///
70 /// ## Panics
71 ///
72 /// Attempting to create the event loop off the main thread will panic. This
73 /// restriction isn't strictly necessary on all platforms, but is imposed to
74 /// eliminate any nasty surprises when porting to platforms that require it.
75 /// `EventLoopBuilderExt::any_thread` functions are exposed in the relevant
76 /// [`platform`] module if the target platform supports creating an event
77 /// loop on any thread.
78 ///
79 /// ## Platform-specific
80 ///
81 /// - **Wayland/X11:** to prevent running under `Wayland` or `X11` unset `WAYLAND_DISPLAY` or
82 /// `DISPLAY` respectively when building the event loop.
83 /// - **Android:** must be configured with an `AndroidApp` from `android_main()` by calling
84 /// [`.with_android_app(app)`] before calling `.build()`, otherwise it'll panic.
85 ///
86 /// [`platform`]: crate::platform
87 #[cfg_attr(
88 android_platform,
89 doc = "[`.with_android_app(app)`]: \
90 crate::platform::android::EventLoopBuilderExtAndroid::with_android_app"
91 )]
92 #[cfg_attr(
93 not(android_platform),
94 doc = "[`.with_android_app(app)`]: #only-available-on-android"
95 )]
96 #[inline]
97 pub fn build(&mut self) -> Result<EventLoop, EventLoopError> {
98 let _span = tracing::debug_span!("winit::EventLoopBuilder::build").entered();
99
100 if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) {
101 return Err(EventLoopError::RecreationAttempt);
102 }
103
104 // Certain platforms accept a mutable reference in their API.
105 #[allow(clippy::unnecessary_mut_passed)]
106 Ok(EventLoop {
107 event_loop: platform_impl::EventLoop::new(&mut self.platform_specific)?,
108 _marker: PhantomData,
109 })
110 }
111
112 #[cfg(web_platform)]
113 pub(crate) fn allow_event_loop_recreation() {
114 EVENT_LOOP_CREATED.store(false, Ordering::Relaxed);
115 }
116}
117
118impl fmt::Debug for EventLoopBuilder {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 f.debug_struct("EventLoopBuilder").finish_non_exhaustive()
121 }
122}
123
124impl fmt::Debug for EventLoop {
125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 f.debug_struct("EventLoop").finish_non_exhaustive()
127 }
128}
129
130/// Set through [`ActiveEventLoop::set_control_flow()`].
131///
132/// Indicates the desired behavior of the event loop after [`about_to_wait`] is called.
133///
134/// Defaults to [`Wait`].
135///
136/// [`Wait`]: Self::Wait
137/// [`about_to_wait`]: crate::application::ApplicationHandler::about_to_wait
138#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
139pub enum ControlFlow {
140 /// When the current loop iteration finishes, immediately begin a new iteration regardless of
141 /// whether or not new events are available to process.
142 Poll,
143
144 /// When the current loop iteration finishes, suspend the thread until another event arrives.
145 #[default]
146 Wait,
147
148 /// When the current loop iteration finishes, suspend the thread until either another event
149 /// arrives or the given time is reached.
150 ///
151 /// Useful for implementing efficient timers. Applications which want to render at the
152 /// display's native refresh rate should instead use [`Poll`] and the VSync functionality
153 /// of a graphics API to reduce odds of missed frames.
154 ///
155 /// [`Poll`]: Self::Poll
156 WaitUntil(Instant),
157}
158
159impl ControlFlow {
160 /// Creates a [`ControlFlow`] that waits until a timeout has expired.
161 ///
162 /// In most cases, this is set to [`WaitUntil`]. However, if the timeout overflows, it is
163 /// instead set to [`Wait`].
164 ///
165 /// [`WaitUntil`]: Self::WaitUntil
166 /// [`Wait`]: Self::Wait
167 pub fn wait_duration(timeout: Duration) -> Self {
168 match Instant::now().checked_add(timeout) {
169 Some(instant) => Self::WaitUntil(instant),
170 None => Self::Wait,
171 }
172 }
173}
174
175impl EventLoop {
176 /// Create the event loop.
177 ///
178 /// This is an alias of `EventLoop::builder().build()`.
179 #[inline]
180 pub fn new() -> Result<EventLoop, EventLoopError> {
181 Self::builder().build()
182 }
183
184 /// Start building a new event loop.
185 ///
186 /// This returns an [`EventLoopBuilder`], to allow configuring the event loop before creation.
187 ///
188 /// To get the actual event loop, call [`build`][EventLoopBuilder::build] on that.
189 #[inline]
190 pub fn builder() -> EventLoopBuilder {
191 EventLoopBuilder { platform_specific: Default::default() }
192 }
193}
194
195impl EventLoop {
196 /// Run the application with the event loop on the calling thread.
197 ///
198 /// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
199 ///
200 /// ## Platform-specific
201 ///
202 /// - **iOS:** Will never return to the caller and so values not passed to this function will
203 /// *not* be dropped before the process exits.
204 /// - **Web:** Will _act_ as if it never returns to the caller by throwing a Javascript
205 /// exception (that Rust doesn't see) that will also mean that the rest of the function is
206 /// never executed and any values not passed to this function will *not* be dropped.
207 ///
208 /// Web applications are recommended to use
209 #[cfg_attr(
210 any(web_platform, docsrs),
211 doc = " [`EventLoopExtWeb::spawn_app()`][crate::platform::web::EventLoopExtWeb::spawn_app()]"
212 )]
213 #[cfg_attr(not(any(web_platform, docsrs)), doc = " `EventLoopExtWeb::spawn_app()`")]
214 /// [^1] instead of [`run_app()`] to avoid the need for the Javascript exception trick, and to
215 /// make it clearer that the event loop runs asynchronously (via the browser's own,
216 /// internal, event loop) and doesn't block the current thread of execution like it does
217 /// on other platforms.
218 ///
219 /// This function won't be available with `target_feature = "exception-handling"`.
220 ///
221 /// [^1]: `spawn_app()` is only available on the Web platform.
222 ///
223 /// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
224 /// [`run_app()`]: Self::run_app()
225 #[inline]
226 #[cfg(not(all(web_platform, target_feature = "exception-handling")))]
227 pub fn run_app<A: ApplicationHandler>(self, app: A) -> Result<(), EventLoopError> {
228 self.event_loop.run_app(app)
229 }
230
231 /// Creates an [`EventLoopProxy`] that can be used to dispatch user events
232 /// to the main event loop, possibly from another thread.
233 pub fn create_proxy(&self) -> EventLoopProxy {
234 self.event_loop.window_target().create_proxy()
235 }
236
237 /// Gets a persistent reference to the underlying platform display.
238 ///
239 /// See the [`OwnedDisplayHandle`] type for more information.
240 pub fn owned_display_handle(&self) -> OwnedDisplayHandle {
241 self.event_loop.window_target().owned_display_handle()
242 }
243
244 /// Change if or when [`DeviceEvent`]s are captured.
245 ///
246 /// See [`ActiveEventLoop::listen_device_events`] for details.
247 ///
248 /// [`DeviceEvent`]: crate::event::DeviceEvent
249 pub fn listen_device_events(&self, allowed: DeviceEvents) {
250 let _span = tracing::debug_span!(
251 "winit::EventLoop::listen_device_events",
252 allowed = ?allowed
253 )
254 .entered();
255 self.event_loop.window_target().listen_device_events(allowed)
256 }
257
258 /// Sets the [`ControlFlow`].
259 pub fn set_control_flow(&self, control_flow: ControlFlow) {
260 self.event_loop.window_target().set_control_flow(control_flow);
261 }
262
263 /// Create custom cursor.
264 ///
265 /// ## Platform-specific
266 ///
267 /// **iOS / Android / Orbital:** Unsupported.
268 pub fn create_custom_cursor(
269 &self,
270 custom_cursor: CustomCursorSource,
271 ) -> Result<CustomCursor, RequestError> {
272 self.event_loop.window_target().create_custom_cursor(custom_cursor)
273 }
274}
275
276#[cfg(feature = "rwh_06")]
277impl rwh_06::HasDisplayHandle for EventLoop {
278 fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
279 rwh_06::HasDisplayHandle::display_handle(self.event_loop.window_target().rwh_06_handle())
280 }
281}
282
283#[cfg(any(x11_platform, wayland_platform))]
284impl AsFd for EventLoop {
285 /// Get the underlying [EventLoop]'s `fd` which you can register
286 /// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
287 /// loop must be polled with the [`pump_app_events`] API.
288 ///
289 /// [`calloop`]: https://crates.io/crates/calloop
290 /// [`mio`]: https://crates.io/crates/mio
291 /// [`pump_app_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_app_events
292 fn as_fd(&self) -> BorrowedFd<'_> {
293 self.event_loop.as_fd()
294 }
295}
296
297#[cfg(any(x11_platform, wayland_platform))]
298impl AsRawFd for EventLoop {
299 /// Get the underlying [EventLoop]'s raw `fd` which you can register
300 /// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
301 /// loop must be polled with the [`pump_app_events`] API.
302 ///
303 /// [`calloop`]: https://crates.io/crates/calloop
304 /// [`mio`]: https://crates.io/crates/mio
305 /// [`pump_app_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_app_events
306 fn as_raw_fd(&self) -> RawFd {
307 self.event_loop.as_raw_fd()
308 }
309}
310
311pub trait ActiveEventLoop: AsAny {
312 /// Creates an [`EventLoopProxy`] that can be used to dispatch user events
313 /// to the main event loop, possibly from another thread.
314 fn create_proxy(&self) -> EventLoopProxy;
315
316 /// Create the window.
317 ///
318 /// Possible causes of error include denied permission, incompatible system, and lack of memory.
319 ///
320 /// ## Platform-specific
321 ///
322 /// - **Web:** The window is created but not inserted into the Web page automatically. Please
323 /// see the Web platform module for more information.
324 fn create_window(
325 &self,
326 window_attributes: WindowAttributes,
327 ) -> Result<Box<dyn Window>, RequestError>;
328
329 /// Create custom cursor.
330 ///
331 /// ## Platform-specific
332 ///
333 /// **iOS / Android / Orbital:** Unsupported.
334 fn create_custom_cursor(
335 &self,
336 custom_cursor: CustomCursorSource,
337 ) -> Result<CustomCursor, RequestError>;
338
339 /// Returns the list of all the monitors available on the system.
340 ///
341 /// ## Platform-specific
342 ///
343 /// **Web:** Only returns the current monitor without
344 #[cfg_attr(
345 any(web_platform, docsrs),
346 doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
347 )]
348 #[cfg_attr(not(any(web_platform, docsrs)), doc = "detailed monitor permissions.")]
349 fn available_monitors(&self) -> Box<dyn Iterator<Item = MonitorHandle>>;
350
351 /// Returns the primary monitor of the system.
352 ///
353 /// Returns `None` if it can't identify any monitor as a primary one.
354 ///
355 /// ## Platform-specific
356 ///
357 /// - **Wayland:** Always returns `None`.
358 /// - **Web:** Always returns `None` without
359 #[cfg_attr(
360 any(web_platform, docsrs),
361 doc = " [detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
362 )]
363 #[cfg_attr(not(any(web_platform, docsrs)), doc = " detailed monitor permissions.")]
364 fn primary_monitor(&self) -> Option<MonitorHandle>;
365
366 /// Change if or when [`DeviceEvent`]s are captured.
367 ///
368 /// Since the [`DeviceEvent`] capture can lead to high CPU usage for unfocused windows, winit
369 /// will ignore them by default for unfocused windows on Linux/BSD. This method allows changing
370 /// this at runtime to explicitly capture them again.
371 ///
372 /// ## Platform-specific
373 ///
374 /// - **Wayland / macOS / iOS / Android / Orbital:** Unsupported.
375 ///
376 /// [`DeviceEvent`]: crate::event::DeviceEvent
377 fn listen_device_events(&self, allowed: DeviceEvents);
378
379 /// Returns the current system theme.
380 ///
381 /// Returns `None` if it cannot be determined on the current platform.
382 ///
383 /// ## Platform-specific
384 ///
385 /// - **iOS / Android / Wayland / x11 / Orbital:** Unsupported.
386 fn system_theme(&self) -> Option<Theme>;
387
388 /// Sets the [`ControlFlow`].
389 fn set_control_flow(&self, control_flow: ControlFlow);
390
391 /// Gets the current [`ControlFlow`].
392 fn control_flow(&self) -> ControlFlow;
393
394 /// This exits the event loop.
395 ///
396 /// See [`exiting`][crate::application::ApplicationHandler::exiting].
397 fn exit(&self);
398
399 /// Returns if the [`EventLoop`] is about to stop.
400 ///
401 /// See [`exit()`][Self::exit].
402 fn exiting(&self) -> bool;
403
404 /// Gets a persistent reference to the underlying platform display.
405 ///
406 /// See the [`OwnedDisplayHandle`] type for more information.
407 fn owned_display_handle(&self) -> OwnedDisplayHandle;
408
409 /// Get the raw-window-handle handle.
410 #[cfg(feature = "rwh_06")]
411 fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle;
412}
413
414#[cfg(feature = "rwh_06")]
415impl rwh_06::HasDisplayHandle for dyn ActiveEventLoop + '_ {
416 fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
417 self.rwh_06_handle().display_handle()
418 }
419}
420
421/// A proxy for the underlying display handle.
422///
423/// The purpose of this type is to provide a cheaply clonable handle to the underlying
424/// display handle. This is often used by graphics APIs to connect to the underlying APIs.
425/// It is difficult to keep a handle to the [`EventLoop`] type or the [`ActiveEventLoop`]
426/// type. In contrast, this type involves no lifetimes and can be persisted for as long as
427/// needed.
428///
429/// For all platforms, this is one of the following:
430///
431/// - A zero-sized type that is likely optimized out.
432/// - A reference-counted pointer to the underlying type.
433#[derive(Clone, PartialEq, Eq)]
434pub struct OwnedDisplayHandle {
435 #[cfg_attr(not(feature = "rwh_06"), allow(dead_code))]
436 pub(crate) platform: platform_impl::OwnedDisplayHandle,
437}
438
439impl fmt::Debug for OwnedDisplayHandle {
440 #[inline]
441 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
442 f.debug_struct("OwnedDisplayHandle").finish_non_exhaustive()
443 }
444}
445
446#[cfg(feature = "rwh_06")]
447impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
448 #[inline]
449 fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
450 let raw = self.platform.raw_display_handle_rwh_06()?;
451
452 // SAFETY: The underlying display handle should be safe.
453 let handle = unsafe { rwh_06::DisplayHandle::borrow_raw(raw) };
454
455 Ok(handle)
456 }
457}
458
459/// Control the [`EventLoop`], possibly from a different thread, without referencing it directly.
460#[derive(Clone)]
461pub struct EventLoopProxy {
462 pub(crate) event_loop_proxy: platform_impl::EventLoopProxy,
463}
464
465impl EventLoopProxy {
466 /// Wake up the [`EventLoop`], resulting in [`ApplicationHandler::proxy_wake_up()`] being
467 /// called.
468 ///
469 /// Calls to this method are coalesced into a single call to [`proxy_wake_up`], see the
470 /// documentation on that for details.
471 ///
472 /// If the event loop is no longer running, this is a no-op.
473 ///
474 /// [`proxy_wake_up`]: ApplicationHandler::proxy_wake_up
475 ///
476 /// # Platform-specific
477 ///
478 /// - **Windows**: The wake-up may be ignored under high contention, see [#3687].
479 ///
480 /// [#3687]: https://github.com/rust-windowing/winit/pull/3687
481 pub fn wake_up(&self) {
482 self.event_loop_proxy.wake_up();
483 }
484}
485
486impl fmt::Debug for EventLoopProxy {
487 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
488 f.debug_struct("ActiveEventLoop").finish_non_exhaustive()
489 }
490}
491
492/// Control when device events are captured.
493#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
494#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
495pub enum DeviceEvents {
496 /// Report device events regardless of window focus.
497 Always,
498 /// Only capture device events while the window is focused.
499 #[default]
500 WhenFocused,
501 /// Never capture device events.
502 Never,
503}
504
505/// A unique identifier of the winit's async request.
506///
507/// This could be used to identify the async request once it's done
508/// and a specific action must be taken.
509///
510/// One of the handling scenarios could be to maintain a working list
511/// containing [`AsyncRequestSerial`] and some closure associated with it.
512/// Then once event is arriving the working list is being traversed and a job
513/// executed and removed from the list.
514#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
515pub struct AsyncRequestSerial {
516 serial: usize,
517}
518
519impl AsyncRequestSerial {
520 // TODO(kchibisov): Remove `cfg` when the clipboard will be added.
521 #[allow(dead_code)]
522 pub(crate) fn get() -> Self {
523 static CURRENT_SERIAL: AtomicUsize = AtomicUsize::new(0);
524 // NOTE: We rely on wrap around here, while the user may just request
525 // in the loop usize::MAX times that's issue is considered on them.
526 let serial = CURRENT_SERIAL.fetch_add(1, Ordering::Relaxed);
527 Self { serial }
528 }
529}