1 | use crate::{ |
2 | error::EventLoopError, |
3 | event::Event, |
4 | event_loop::{EventLoop, EventLoopWindowTarget}, |
5 | }; |
6 | |
7 | #[cfg (doc)] |
8 | use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window}; |
9 | |
10 | /// Additional methods on [`EventLoop`] to return control flow to the caller. |
11 | pub trait EventLoopExtRunOnDemand { |
12 | /// A type provided by the user that can be passed through [`Event::UserEvent`]. |
13 | type UserEvent; |
14 | |
15 | /// Runs the event loop in the calling thread and calls the given `event_handler` closure |
16 | /// to dispatch any window system events. |
17 | /// |
18 | /// Unlike [`EventLoop::run`], this function accepts non-`'static` (i.e. non-`move`) closures |
19 | /// and it is possible to return control back to the caller without |
20 | /// consuming the `EventLoop` (by using [`exit()`]) and |
21 | /// so the event loop can be re-run after it has exit. |
22 | /// |
23 | /// It's expected that each run of the loop will be for orthogonal instantiations of your |
24 | /// Winit application, but internally each instantiation may re-use some common window |
25 | /// system resources, such as a display server connection. |
26 | /// |
27 | /// This API is not designed to run an event loop in bursts that you can exit from and return |
28 | /// to while maintaining the full state of your application. (If you need something like this |
29 | /// you can look at the [`EventLoopExtPumpEvents::pump_events()`] API) |
30 | /// |
31 | /// Each time `run_on_demand` is called the `event_handler` can expect to receive a |
32 | /// `NewEvents(Init)` and `Resumed` event (even on platforms that have no suspend/resume |
33 | /// lifecycle) - which can be used to consistently initialize application state. |
34 | /// |
35 | /// See the [`set_control_flow()`] docs on how to change the event loop's behavior. |
36 | /// |
37 | /// # Caveats |
38 | /// - This extension isn't available on all platforms, since it's not always possible to return |
39 | /// to the caller (specifically this is impossible on iOS and Web - though with the Web |
40 | /// backend it is possible to use `EventLoopExtWebSys::spawn()`[^1] more than once instead). |
41 | /// - No [`Window`] state can be carried between separate runs of the event loop. |
42 | /// |
43 | /// You are strongly encouraged to use [`EventLoop::run()`] for portability, unless you specifically need |
44 | /// the ability to re-run a single event loop more than once |
45 | /// |
46 | /// # Supported Platforms |
47 | /// - Windows |
48 | /// - Linux |
49 | /// - macOS |
50 | /// - Android |
51 | /// |
52 | /// # Unsupported Platforms |
53 | /// - **Web:** This API is fundamentally incompatible with the event-based way in which |
54 | /// Web browsers work because it's not possible to have a long-running external |
55 | /// loop that would block the browser and there is nothing that can be |
56 | /// polled to ask for new events. Events are delivered via callbacks based |
57 | /// on an event loop that is internal to the browser itself. |
58 | /// - **iOS:** It's not possible to stop and start an `NSApplication` repeatedly on iOS. |
59 | /// |
60 | #[cfg_attr ( |
61 | not(wasm_platform), |
62 | doc = "[^1]: `spawn()` is only available on `wasm` platforms." |
63 | )] |
64 | /// |
65 | /// [`exit()`]: EventLoopWindowTarget::exit() |
66 | /// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow() |
67 | fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError> |
68 | where |
69 | F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>); |
70 | } |
71 | |
72 | impl<T> EventLoopExtRunOnDemand for EventLoop<T> { |
73 | type UserEvent = T; |
74 | |
75 | fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError> |
76 | where |
77 | F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>), |
78 | { |
79 | self.event_loop.window_target().clear_exit(); |
80 | self.event_loop.run_on_demand(callback:event_handler) |
81 | } |
82 | } |
83 | |
84 | impl<T> EventLoopWindowTarget<T> { |
85 | /// Clear exit status. |
86 | pub(crate) fn clear_exit(&self) { |
87 | self.p.clear_exit() |
88 | } |
89 | } |
90 | |