floem_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//! [`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.
10use std::marker::PhantomData;
11use std::ops::Deref;
12#[cfg(any(x11_platform, wayland_platform))]
13use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
14use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
15use std::{error, fmt};
16
17#[cfg(not(wasm_platform))]
18use std::time::{Duration, Instant};
19#[cfg(wasm_platform)]
20use web_time::{Duration, Instant};
21
22use crate::error::EventLoopError;
23use 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
40pub 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`.
51pub 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)]
61pub struct EventLoopBuilder<T: 'static> {
62    pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes,
63    _p: PhantomData<T>,
64}
65
66impl EventLoopBuilder<()> {
67    /// Start building a new event loop.
68    #[inline]
69    pub fn new() -> Self {
70        Self::with_user_event()
71    }
72}
73
74static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
75
76impl<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
139impl<T> fmt::Debug for EventLoop<T> {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        f.pad("EventLoop { .. }")
142    }
143}
144
145impl<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)]
159pub 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
179impl 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(timeout) {
189            Some(instant) => Self::WaitUntil(instant),
190            None => Self::Wait,
191        }
192    }
193}
194
195impl 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
205impl<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")]
261impl<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")]
268unsafe 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))]
276impl<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))]
290impl<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
303impl<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
310impl<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")]
376impl<T> rwh_06::HasDisplayHandle for EventLoopWindowTarget<T> {
377    fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
378        let raw = 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")]
385unsafe 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`].
393pub struct EventLoopProxy<T: 'static> {
394    event_loop_proxy: platform_impl::EventLoopProxy<T>,
395}
396
397impl<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
405impl<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
418impl<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)]
429pub struct EventLoopClosed<T>(pub T);
430
431impl<T> fmt::Display for EventLoopClosed<T> {
432    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
433        f.write_str("Tried to wake up a closed `EventLoop`")
434    }
435}
436
437impl<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)]
441pub 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)]
461pub struct AsyncRequestSerial {
462    serial: u64,
463}
464
465impl 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: AtomicU64 = AtomicU64::new(0);
470        // NOTE: we rely on wrap around here, while the user may just request
471        // in the loop u64::MAX times that's issue is considered on them.
472        let serial = CURRENT_SERIAL.fetch_add(1, Ordering::Relaxed);
473        Self { serial }
474    }
475}