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::marker::PhantomData;
12#[cfg(any(x11_platform, wayland_platform))]
13use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
14
15use rwh_06::{DisplayHandle, HandleError, HasDisplayHandle};
16pub use winit_core::event_loop::*;
17
18use crate::application::ApplicationHandler;
19use crate::cursor::{CustomCursor, CustomCursorSource};
20use crate::error::{EventLoopError, RequestError};
21use crate::platform_impl;
22
23/// Provides a way to retrieve events from the system and from the windows that were registered to
24/// the events loop.
25///
26/// An `EventLoop` can be seen more or less as a "context". Calling [`EventLoop::new`]
27/// initializes everything that will be required to create windows. For example on Linux creating
28/// an event loop opens a connection to the X or Wayland server.
29///
30/// To wake up an `EventLoop` from a another thread, see the [`EventLoopProxy`] docs.
31///
32/// Note that this cannot be shared across threads (due to platform-dependant logic
33/// forbidding it), as such it is neither [`Send`] nor [`Sync`]. If you need cross-thread access,
34/// the [`Window`] created from this _can_ be sent to an other thread, and the
35/// [`EventLoopProxy`] allows you to wake up an `EventLoop` from another thread.
36///
37/// [`Window`]: crate::window::Window
38#[derive(Debug)]
39pub struct EventLoop {
40    pub(crate) event_loop: platform_impl::EventLoop,
41    pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync
42}
43
44/// Object that allows building the event loop.
45///
46/// This is used to make specifying options that affect the whole application
47/// easier. But note that constructing multiple event loops is not supported.
48///
49/// This can be created using [`EventLoop::builder`].
50#[derive(Default, Debug, PartialEq, Eq, Hash)]
51pub struct EventLoopBuilder {
52    pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes,
53}
54
55impl EventLoopBuilder {
56    /// Builds a new event loop.
57    ///
58    /// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread,
59    /// and only once per application.***
60    ///
61    /// Calling this function will result in display backend initialisation.
62    ///
63    /// ## Panics
64    ///
65    /// Attempting to create the event loop off the main thread will panic. This
66    /// restriction isn't strictly necessary on all platforms, but is imposed to
67    /// eliminate any nasty surprises when porting to platforms that require it.
68    /// `EventLoopBuilderExt::with_any_thread` functions are exposed in the relevant
69    /// [`platform`] module if the target platform supports creating an event
70    /// loop on any thread.
71    ///
72    /// ## Platform-specific
73    ///
74    /// - **Wayland/X11:** to prevent running under `Wayland` or `X11` unset `WAYLAND_DISPLAY` or
75    ///   `DISPLAY` respectively when building the event loop.
76    /// - **Android:** must be configured with an `AndroidApp` from `android_main()` by calling
77    ///   [`.with_android_app(app)`] before calling `.build()`, otherwise it'll panic.
78    ///
79    /// [`platform`]: crate::platform
80    #[cfg_attr(
81        android_platform,
82        doc = "[`.with_android_app(app)`]: \
83               crate::platform::android::EventLoopBuilderExtAndroid::with_android_app"
84    )]
85    #[cfg_attr(
86        not(android_platform),
87        doc = "[`.with_android_app(app)`]: #only-available-on-android"
88    )]
89    #[inline]
90    pub fn build(&mut self) -> Result<EventLoop, EventLoopError> {
91        let _span = tracing::debug_span!("winit::EventLoopBuilder::build").entered();
92
93        // Certain platforms accept a mutable reference in their API.
94        #[allow(clippy::unnecessary_mut_passed)]
95        Ok(EventLoop {
96            event_loop: platform_impl::EventLoop::new(&mut self.platform_specific)?,
97            _marker: PhantomData,
98        })
99    }
100}
101
102impl EventLoop {
103    /// Create the event loop.
104    ///
105    /// This is an alias of `EventLoop::builder().build()`.
106    #[inline]
107    pub fn new() -> Result<EventLoop, EventLoopError> {
108        Self::builder().build()
109    }
110
111    /// Start building a new event loop.
112    ///
113    /// This returns an [`EventLoopBuilder`], to allow configuring the event loop before creation.
114    ///
115    /// To get the actual event loop, call [`build`][EventLoopBuilder::build] on that.
116    #[inline]
117    pub fn builder() -> EventLoopBuilder {
118        EventLoopBuilder { platform_specific: Default::default() }
119    }
120
121    /// Run the event loop with the given application on the calling thread.
122    ///
123    /// The `app` is dropped when the event loop is shut down.
124    ///
125    /// ## Event loop flow
126    ///
127    /// This function internally handles the different parts of a traditional event-handling loop.
128    /// You can imagine this method as being implemented like this:
129    ///
130    /// ```rust,ignore
131    /// let mut start_cause = StartCause::Init;
132    ///
133    /// // Run the event loop.
134    /// while !event_loop.exiting() {
135    ///     // Wake up.
136    ///     app.new_events(event_loop, start_cause);
137    ///
138    ///     // Indicate that surfaces can now safely be created.
139    ///     if start_cause == StartCause::Init {
140    ///         app.can_create_surfaces(event_loop);
141    ///     }
142    ///
143    ///     // Handle proxy wake-up event.
144    ///     if event_loop.proxy_wake_up_set() {
145    ///         event_loop.proxy_wake_up_clear();
146    ///         app.proxy_wake_up(event_loop);
147    ///     }
148    ///
149    ///     // Handle actions done by the user / system such as moving the cursor, resizing the
150    ///     // window, changing the window theme, etc.
151    ///     for event in event_loop.events() {
152    ///         match event {
153    ///             window event => app.window_event(event_loop, window_id, event),
154    ///             device event => app.device_event(event_loop, device_id, event),
155    ///         }
156    ///     }
157    ///
158    ///     // Handle redraws.
159    ///     for window_id in event_loop.pending_redraws() {
160    ///         app.window_event(event_loop, window_id, WindowEvent::RedrawRequested);
161    ///     }
162    ///
163    ///     // Done handling events, wait until we're woken up again.
164    ///     app.about_to_wait(event_loop);
165    ///     start_cause = event_loop.wait_if_necessary();
166    /// }
167    ///
168    /// // Finished running, drop application state.
169    /// drop(app);
170    /// ```
171    ///
172    /// This is of course a very coarse-grained overview, and leaves out timing details like
173    /// [`ControlFlow::WaitUntil`] and life-cycle methods like [`ApplicationHandler::resumed`], but
174    /// it should give you an idea of how things fit together.
175    ///
176    /// ## Returns
177    ///
178    /// The semantics of this function can be a bit confusing, because the way different platforms
179    /// control their event loop varies significantly.
180    ///
181    /// On most platforms (Android, macOS, Orbital, X11, Wayland, Windows), this blocks the caller,
182    /// runs the event loop internally, and then returns once [`ActiveEventLoop::exit`] is called.
183    /// See [`run_app_on_demand`] for more detailed semantics.
184    ///
185    /// On iOS, this will register the application handler, and then call [`UIApplicationMain`]
186    /// (which is the only way to run the system event loop), which never returns to the caller
187    /// (the process instead exits after the handler has been dropped). See also
188    /// [`run_app_never_return`].
189    ///
190    /// On the web, this works by registering the application handler, and then immediately
191    /// returning to the caller. This is necessary because WebAssembly (and JavaScript) is always
192    /// executed in the context of the browser's own (internal) event loop, and thus we need to
193    /// return to avoid blocking that and allow events to later be delivered asynchronously. See
194    /// also [`register_app`].
195    ///
196    /// If you call this function inside `fn main`, you usually do not need to think about these
197    /// details.
198    ///
199    /// [`UIApplicationMain`]: https://developer.apple.com/documentation/uikit/uiapplicationmain(_:_:_:_:)-1yub7?language=objc
200    /// [`run_app_on_demand`]: crate::event_loop::run_on_demand::EventLoopExtRunOnDemand::run_app_on_demand
201    /// [`run_app_never_return`]: crate::event_loop::never_return::EventLoopExtNeverReturn::run_app_never_return
202    /// [`register_app`]: crate::event_loop::register::EventLoopExtRegister::register_app
203    ///
204    /// ## Static
205    ///
206    /// To alleviate the issues noted above, this function requires that you pass in a `'static`
207    /// handler, to ensure that any state your application uses will be alive as long as the
208    /// application is running.
209    ///
210    /// To be clear, you should avoid doing e.g. `event_loop.run_app(&mut app)?`, and prefer
211    /// `event_loop.run_app(app)?` instead.
212    ///
213    /// If this requirement is prohibitive for you, consider using [`run_app_on_demand`] instead
214    /// (though note that this is not available on iOS and web).
215    #[inline]
216    #[allow(unused_mut)]
217    pub fn run_app<A: ApplicationHandler + 'static>(
218        mut self,
219        mut app: A,
220    ) -> Result<(), EventLoopError> {
221        #[cfg(any(
222            windows_platform,
223            macos_platform,
224            android_platform,
225            orbital_platform,
226            x11_platform,
227            wayland_platform,
228        ))]
229        {
230            let result = self.event_loop.run_app_on_demand(&mut app);
231            // SAFETY: unsure that the state is dropped before the exit from the event loop.
232            drop(app);
233            result
234        }
235        #[cfg(web_platform)]
236        {
237            self.event_loop.register_app(app);
238            Ok(())
239        }
240        #[cfg(ios_platform)]
241        {
242            self.event_loop.run_app_never_return(app)
243        }
244    }
245
246    /// Creates an [`EventLoopProxy`] that can be used to dispatch user events
247    /// to the main event loop, possibly from another thread.
248    pub fn create_proxy(&self) -> EventLoopProxy {
249        self.event_loop.window_target().create_proxy()
250    }
251
252    /// Gets a persistent reference to the underlying platform display.
253    ///
254    /// See the [`OwnedDisplayHandle`] type for more information.
255    pub fn owned_display_handle(&self) -> OwnedDisplayHandle {
256        self.event_loop.window_target().owned_display_handle()
257    }
258
259    /// Change if or when [`DeviceEvent`]s are captured.
260    ///
261    /// See [`ActiveEventLoop::listen_device_events`] for details.
262    ///
263    /// [`DeviceEvent`]: crate::event::DeviceEvent
264    pub fn listen_device_events(&self, allowed: DeviceEvents) {
265        let _span = tracing::debug_span!(
266            "winit::EventLoop::listen_device_events",
267            allowed = ?allowed
268        )
269        .entered();
270        self.event_loop.window_target().listen_device_events(allowed)
271    }
272
273    /// Sets the [`ControlFlow`].
274    pub fn set_control_flow(&self, control_flow: ControlFlow) {
275        self.event_loop.window_target().set_control_flow(control_flow);
276    }
277
278    /// Create custom cursor.
279    ///
280    /// ## Platform-specific
281    ///
282    /// **iOS / Android / Orbital:** Unsupported.
283    pub fn create_custom_cursor(
284        &self,
285        custom_cursor: CustomCursorSource,
286    ) -> Result<CustomCursor, RequestError> {
287        self.event_loop.window_target().create_custom_cursor(custom_cursor)
288    }
289}
290
291impl HasDisplayHandle for EventLoop {
292    fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
293        HasDisplayHandle::display_handle(self.event_loop.window_target().rwh_06_handle())
294    }
295}
296
297#[cfg(any(x11_platform, wayland_platform))]
298impl AsFd for EventLoop {
299    /// Get the underlying [EventLoop]'s `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::event_loop::pump_events::EventLoopExtPumpEvents::pump_app_events
306    fn as_fd(&self) -> BorrowedFd<'_> {
307        self.event_loop.as_fd()
308    }
309}
310
311#[cfg(any(x11_platform, wayland_platform))]
312impl AsRawFd for EventLoop {
313    /// Get the underlying [EventLoop]'s raw `fd` which you can register
314    /// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
315    /// loop must be polled with the [`pump_app_events`] API.
316    ///
317    /// [`calloop`]: https://crates.io/crates/calloop
318    /// [`mio`]: https://crates.io/crates/mio
319    /// [`pump_app_events`]: crate::event_loop::pump_events::EventLoopExtPumpEvents::pump_app_events
320    fn as_raw_fd(&self) -> RawFd {
321        self.event_loop.as_raw_fd()
322    }
323}
324
325#[cfg(any(
326    windows_platform,
327    macos_platform,
328    android_platform,
329    x11_platform,
330    wayland_platform,
331    docsrs,
332))]
333impl winit_core::event_loop::pump_events::EventLoopExtPumpEvents for EventLoop {
334    fn pump_app_events<A: ApplicationHandler>(
335        &mut self,
336        timeout: Option<std::time::Duration>,
337        app: A,
338    ) -> winit_core::event_loop::pump_events::PumpStatus {
339        self.event_loop.pump_app_events(timeout, app)
340    }
341}
342
343#[allow(unused_imports)]
344#[cfg(any(
345    windows_platform,
346    macos_platform,
347    android_platform,
348    orbital_platform,
349    x11_platform,
350    wayland_platform,
351    docsrs,
352))]
353impl winit_core::event_loop::run_on_demand::EventLoopExtRunOnDemand for EventLoop {
354    fn run_app_on_demand<A: ApplicationHandler>(&mut self, app: A) -> Result<(), EventLoopError> {
355        self.event_loop.run_app_on_demand(app)
356    }
357}
358
359#[cfg(any(web_platform, docsrs))]
360impl winit_core::event_loop::register::EventLoopExtRegister for EventLoop {
361    fn register_app<A: ApplicationHandler + 'static>(self, app: A) {
362        self.event_loop.register_app(app)
363    }
364}
365
366#[cfg(android_platform)]
367impl winit_android::EventLoopExtAndroid for EventLoop {
368    fn android_app(&self) -> &winit_android::activity::AndroidApp {
369        &self.event_loop.android_app
370    }
371}
372
373#[cfg(android_platform)]
374impl winit_android::EventLoopBuilderExtAndroid for EventLoopBuilder {
375    fn with_android_app(&mut self, app: winit_android::activity::AndroidApp) -> &mut Self {
376        self.platform_specific.android_app = Some(app);
377        self
378    }
379
380    fn handle_volume_keys(&mut self) -> &mut Self {
381        self.platform_specific.ignore_volume_keys = false;
382        self
383    }
384}
385
386#[cfg(macos_platform)]
387impl winit_appkit::EventLoopBuilderExtMacOS for EventLoopBuilder {
388    #[inline]
389    fn with_activation_policy(
390        &mut self,
391        activation_policy: winit_appkit::ActivationPolicy,
392    ) -> &mut Self {
393        self.platform_specific.activation_policy = Some(activation_policy);
394        self
395    }
396
397    #[inline]
398    fn with_default_menu(&mut self, enable: bool) -> &mut Self {
399        self.platform_specific.default_menu = enable;
400        self
401    }
402
403    #[inline]
404    fn with_activate_ignoring_other_apps(&mut self, ignore: bool) -> &mut Self {
405        self.platform_specific.activate_ignoring_other_apps = ignore;
406        self
407    }
408}
409
410#[cfg(wayland_platform)]
411impl winit_wayland::EventLoopExtWayland for EventLoop {
412    #[inline]
413    fn is_wayland(&self) -> bool {
414        self.event_loop.is_wayland()
415    }
416}
417
418#[cfg(wayland_platform)]
419impl winit_wayland::EventLoopBuilderExtWayland for EventLoopBuilder {
420    #[inline]
421    fn with_wayland(&mut self) -> &mut Self {
422        self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::Wayland);
423        self
424    }
425
426    #[inline]
427    fn with_any_thread(&mut self, any_thread: bool) -> &mut Self {
428        self.platform_specific.any_thread = any_thread;
429        self
430    }
431}
432
433#[cfg(web_platform)]
434impl winit_web::EventLoopExtWeb for EventLoop {
435    fn set_poll_strategy(&self, strategy: winit_web::PollStrategy) {
436        self.event_loop.set_poll_strategy(strategy);
437    }
438
439    fn poll_strategy(&self) -> winit_web::PollStrategy {
440        self.event_loop.poll_strategy()
441    }
442
443    fn set_wait_until_strategy(&self, strategy: winit_web::WaitUntilStrategy) {
444        self.event_loop.set_wait_until_strategy(strategy);
445    }
446
447    fn wait_until_strategy(&self) -> winit_web::WaitUntilStrategy {
448        self.event_loop.wait_until_strategy()
449    }
450
451    fn has_multiple_screens(&self) -> Result<bool, winit_core::error::NotSupportedError> {
452        self.event_loop.has_multiple_screens()
453    }
454
455    fn request_detailed_monitor_permission(&self) -> winit_web::MonitorPermissionFuture {
456        self.event_loop.request_detailed_monitor_permission()
457    }
458
459    fn has_detailed_monitor_permission(&self) -> winit_web::HasMonitorPermissionFuture {
460        self.event_loop.has_detailed_monitor_permission()
461    }
462}
463
464#[cfg(windows_platform)]
465impl winit_win32::EventLoopBuilderExtWindows for EventLoopBuilder {
466    #[inline]
467    fn with_any_thread(&mut self, any_thread: bool) -> &mut Self {
468        self.platform_specific.any_thread = any_thread;
469        self
470    }
471
472    #[inline]
473    fn with_dpi_aware(&mut self, dpi_aware: bool) -> &mut Self {
474        self.platform_specific.dpi_aware = dpi_aware;
475        self
476    }
477
478    #[inline]
479    fn with_msg_hook<F>(&mut self, callback: F) -> &mut Self
480    where
481        F: FnMut(*const core::ffi::c_void) -> bool + 'static,
482    {
483        self.platform_specific.msg_hook = Some(Box::new(callback));
484        self
485    }
486}
487
488#[cfg(x11_platform)]
489impl winit_x11::EventLoopExtX11 for EventLoop {
490    #[inline]
491    fn is_x11(&self) -> bool {
492        !self.event_loop.is_wayland()
493    }
494}
495
496#[cfg(x11_platform)]
497impl winit_x11::EventLoopBuilderExtX11 for EventLoopBuilder {
498    #[inline]
499    fn with_x11(&mut self) -> &mut Self {
500        self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::X);
501        self
502    }
503
504    #[inline]
505    fn with_any_thread(&mut self, any_thread: bool) -> &mut Self {
506        self.platform_specific.any_thread = any_thread;
507        self
508    }
509}
510
511/// ```compile_error
512/// use winit::event_loop::run_on_demand::EventLoopExtRunOnDemand;
513/// use winit::event_loop::EventLoop;
514///
515/// let mut event_loop = EventLoop::new().unwrap();
516/// event_loop.run_app_on_demand(|_, _| {
517///     // Attempt to run the event loop re-entrantly; this must fail.
518///     event_loop.run_app_on_demand(|_, _| {});
519/// });
520/// ```
521#[allow(dead_code)]
522fn test_run_on_demand_cannot_access_event_loop() {}