floem_winit/platform/
pump_events.rs

1use std::time::Duration;
2
3use crate::{
4    event::Event,
5    event_loop::{EventLoop, EventLoopWindowTarget},
6};
7
8/// The return status for `pump_events`
9pub enum PumpStatus {
10    /// Continue running external loop.
11    Continue,
12    /// Exit external loop.
13    Exit(i32),
14}
15
16/// Additional methods on [`EventLoop`] for pumping events within an external event loop
17pub trait EventLoopExtPumpEvents {
18    /// A type provided by the user that can be passed through [`Event::UserEvent`].
19    type UserEvent;
20
21    /// Pump the `EventLoop` to check for and dispatch pending events.
22    ///
23    /// This API is designed to enable applications to integrate Winit into an
24    /// external event loop, for platforms that can support this.
25    ///
26    /// The given `timeout` limits how long it may block waiting for new events.
27    ///
28    /// Passing a `timeout` of `Some(Duration::ZERO)` would ensure your external
29    /// event loop is never blocked but you would likely need to consider how
30    /// to throttle your own external loop.
31    ///
32    /// Passing a `timeout` of `None` means that it may wait indefinitely for new
33    /// events before returning control back to the external loop.
34    ///
35    /// ## Example
36    ///
37    /// ```rust,no_run
38    /// # // Copied from examples/window_pump_events.rs
39    /// # #[cfg(any(
40    /// #     windows_platform,
41    /// #     macos_platform,
42    /// #     x11_platform,
43    /// #     wayland_platform,
44    /// #     android_platform,
45    /// # ))]
46    /// fn main() -> std::process::ExitCode {
47    /// #     use std::{process::ExitCode, thread::sleep, time::Duration};
48    /// #
49    /// #     use simple_logger::SimpleLogger;
50    /// #     use winit::{
51    /// #         event::{Event, WindowEvent},
52    /// #         event_loop::EventLoop,
53    /// #         platform::pump_events::{EventLoopExtPumpEvents, PumpStatus},
54    /// #         window::WindowBuilder,
55    /// #     };
56    ///     let mut event_loop = EventLoop::new().unwrap();
57    /// #
58    /// #   SimpleLogger::new().init().unwrap();
59    ///     let window = WindowBuilder::new()
60    ///         .with_title("A fantastic window!")
61    ///         .build(&event_loop)
62    ///         .unwrap();
63    ///
64    ///     'main: loop {
65    ///         let timeout = Some(Duration::ZERO);
66    ///         let status = event_loop.pump_events(timeout, |event, elwt| {
67    /// #            if let Event::WindowEvent { event, .. } = &event {
68    /// #                // Print only Window events to reduce noise
69    /// #                println!("{event:?}");
70    /// #            }
71    /// #
72    ///             match event {
73    ///                 Event::WindowEvent {
74    ///                     event: WindowEvent::CloseRequested,
75    ///                     window_id,
76    ///                 } if window_id == window.id() => elwt.exit(),
77    ///                 Event::AboutToWait => {
78    ///                     window.request_redraw();
79    ///                 }
80    ///                 _ => (),
81    ///             }
82    ///         });
83    ///         if let PumpStatus::Exit(exit_code) = status {
84    ///             break 'main ExitCode::from(exit_code as u8);
85    ///         }
86    ///
87    ///         // Sleep for 1/60 second to simulate application work
88    ///         //
89    ///         // Since `pump_events` doesn't block it will be important to
90    ///         // throttle the loop in the app somehow.
91    ///         println!("Update()");
92    ///         sleep(Duration::from_millis(16));
93    ///     }
94    /// }
95    /// ```
96    ///
97    /// **Note:** This is not a portable API, and its usage involves a number of
98    /// caveats and trade offs that should be considered before using this API!
99    ///
100    /// You almost certainly shouldn't use this API, unless you absolutely know it's
101    /// the only practical option you have.
102    ///
103    /// ## Synchronous events
104    ///
105    /// Some events _must_ only be handled synchronously via the closure that
106    /// is passed to Winit so that the handler will also be synchronized with
107    /// the window system and operating system.
108    ///
109    /// This is because some events are driven by a window system callback
110    /// where the window systems expects the application to have handled the
111    /// event before returning.
112    ///
113    /// **These events can not be buffered and handled outside of the closure
114    /// passed to Winit.**
115    ///
116    /// As a general rule it is not recommended to ever buffer events to handle
117    /// them outside of the closure passed to Winit since it's difficult to
118    /// provide guarantees about which events are safe to buffer across all
119    /// operating systems.
120    ///
121    /// Notable events that will certainly create portability problems if
122    /// buffered and handled outside of Winit include:
123    /// - `RedrawRequested` events, used to schedule rendering.
124    ///
125    ///     macOS for example uses a `drawRect` callback to drive rendering
126    /// within applications and expects rendering to be finished before
127    /// the `drawRect` callback returns.
128    ///
129    ///     For portability it's strongly recommended that applications should
130    /// keep their rendering inside the closure provided to Winit.
131    /// - Any lifecycle events, such as `Suspended` / `Resumed`.
132    ///
133    ///     The handling of these events needs to be synchronized with the
134    /// operating system and it would never be appropriate to buffer a
135    /// notification that your application has been suspended or resumed and
136    /// then handled that later since there would always be a chance that
137    /// other lifecycle events occur while the event is buffered.
138    ///
139    /// ## Supported Platforms
140    /// - Windows
141    /// - Linux
142    /// - MacOS
143    /// - Android
144    ///
145    /// ## Unsupported Platforms
146    /// - **Web:**  This API is fundamentally incompatible with the event-based way in which
147    /// Web browsers work because it's not possible to have a long-running external
148    /// loop that would block the browser and there is nothing that can be
149    /// polled to ask for new new events. Events are delivered via callbacks based
150    /// on an event loop that is internal to the browser itself.
151    /// - **iOS:** It's not possible to stop and start an `NSApplication` repeatedly on iOS so
152    /// there's no way to support the same approach to polling as on MacOS.
153    ///
154    /// ## Platform-specific
155    /// - **Windows**: The implementation will use `PeekMessage` when checking for
156    ///   window messages to avoid blocking your external event loop.
157    ///
158    /// - **MacOS**: The implementation works in terms of stopping the global `NSApp`
159    ///   whenever the application `RunLoop` indicates that it is preparing to block
160    ///   and wait for new events.
161    ///
162    ///   This is very different to the polling APIs that are available on other
163    ///   platforms (the lower level polling primitives on MacOS are private
164    ///   implementation details for `NSApp` which aren't accessible to application
165    ///   developers)
166    ///
167    ///   It's likely this will be less efficient than polling on other OSs and
168    ///   it also means the `NSApp` is stopped while outside of the Winit
169    ///   event loop - and that's observable (for example to crates like `rfd`)
170    ///   because the `NSApp` is global state.
171    ///
172    ///   If you render outside of Winit you are likely to see window resizing artifacts
173    ///   since MacOS expects applications to render synchronously during any `drawRect`
174    ///   callback.
175    fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
176    where
177        F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>);
178}
179
180impl<T> EventLoopExtPumpEvents for EventLoop<T> {
181    type UserEvent = T;
182
183    fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
184    where
185        F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>),
186    {
187        self.event_loop.pump_events(timeout, event_handler)
188    }
189}