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() {}