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, callback:event_handler)
188 }
189}
190