winit_block_on/
run_return.rs

1// SPDX-License-Identifier: BSL-1.0 OR Apache-2.0
2//               Copyright John Nunley, 2023.
3// Distributed under the Boost Software License, Version 1.0 or the Apache
4//                 License, Version 2.0.
5//       (See accompanying file LICENSE or copy at
6//         https://www.boost.org/LICENSE_1_0.txt)
7
8#![cfg(any(
9    target_os = "windows",
10    target_os = "macos",
11    target_os = "android",
12    target_os = "linux",
13    target_os = "dragonfly",
14    target_os = "freebsd",
15    target_os = "netbsd",
16    target_os = "openbsd",
17))]
18
19use super::{make_proxy_waker, Inner, Signal};
20
21use std::future::Future;
22use std::task::{Context, Poll};
23
24use winit::event::Event;
25use winit::event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget as Elwt};
26use winit::platform::run_return::EventLoopExtRunReturn as _;
27
28/// An extension trait for `EventLoop` that allows one to block on a non-infinite future.
29pub trait EventLoopRunReturnExt {
30    type User;
31
32    /// Block on the provided future until either the loop exits or the future returns a value.
33    fn block_on_return<F, Fut>(&mut self, handler: F, fut: Fut) -> BlockOnReturnResult<Fut::Output>
34    where
35        F: FnMut(Event<'_, Self::User>, &Elwt<Signal<Self::User>>, &mut ControlFlow),
36        Fut: Future;
37}
38
39impl<T: Send + 'static> EventLoopRunReturnExt for EventLoop<Signal<T>> {
40    type User = T;
41
42    fn block_on_return<F, Fut>(
43        &mut self,
44        mut handler: F,
45        fut: Fut,
46    ) -> BlockOnReturnResult<Fut::Output>
47    where
48        F: FnMut(Event<'_, Self::User>, &Elwt<Signal<Self::User>>, &mut ControlFlow),
49        Fut: Future,
50    {
51        // We need to pin the future on the heap, since the callback needs to be movable.
52        let mut ready = true;
53        pin_utils::pin_mut!(fut);
54
55        // Create a waker that will wake up the event loop.
56        let waker = make_proxy_waker(&self);
57
58        // The output of the future.
59        let mut output = None;
60
61        self.run_return({
62            let output = &mut output;
63
64            move |event, target, control_flow| {
65                match event {
66                    Event::UserEvent(Signal(Inner::Wakeup)) => {
67                        // Make sure the future is ready to wake up.
68                        ready = true;
69                    }
70
71                    Event::UserEvent(Signal(Inner::User(user))) => {
72                        // Forward the user event to the callback.
73                        handler(Event::UserEvent(user), target, control_flow);
74                    }
75
76                    event @ Event::RedrawEventsCleared | event @ Event::LoopDestroyed => {
77                        // The handler may be interested in this event.
78                        let event = event.map_nonuser_event().unwrap_or_else(|_| unreachable!());
79                        handler(event, target, control_flow);
80
81                        // If the future is ready to be polled, poll it.
82                        if ready {
83                            ready = false;
84
85                            // Poll the future.
86                            if let Poll::Ready(res) =
87                                fut.as_mut().poll(&mut Context::from_waker(&waker))
88                            {
89                                // The future returned a value.
90                                *output = Some(res);
91
92                                // Request to exit the loop.
93                                control_flow.set_exit();
94                            }
95                        }
96                    }
97
98                    event => {
99                        // Forward the event to the inner callback.
100                        let event = event.map_nonuser_event().unwrap_or_else(|_| unreachable!());
101                        handler(event, target, control_flow);
102                    }
103                }
104            }
105        });
106
107        match output {
108            Some(output) => BlockOnReturnResult::Value(output),
109            None => BlockOnReturnResult::Exit,
110        }
111    }
112}
113
114/// Either one option or another.
115#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
116pub enum BlockOnReturnResult<T> {
117    /// The future returned a value.
118    Value(T),
119
120    /// The loop exited.
121    Exit,
122}