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