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}