1 | use std::time::Duration; |
2 | |
3 | use crate::{ |
4 | event::Event, |
5 | event_loop::{EventLoop, EventLoopWindowTarget}, |
6 | }; |
7 | |
8 | /// The return status for `pump_events` |
9 | pub 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 |
17 | pub 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 | |
180 | impl<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 | |