| 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 | |