winit_main/
lib.rs

1//! This is a [`winit`](https://lib.rs/crates/winit) utility which abstracts away
2//! winit's event-loop inversion of control.
3//! 
4//! ## Rationale
5//! 
6//! Winit necessarily hijacks the main thread due to platform constraints,
7//! creating a "don't call us, we'll call you" situation. Inversions of control
8//! have some undesirable properties, including:
9//! 
10//! - It's difficult to add inversion of control to a program after the fact, as
11//!   it tends to fundamentally affect the program's architecture.
12//! - For the above reason, it's difficult to write programs which are generic
13//!   between inversions-of-control, or to modify a program from using one
14//!   inversion-of-control framework to using a different framework.
15//! - It's tricky to use several inversions of control simultaneously. For example,
16//!   it would be difficult to combine tokio with winit without creating additional
17//!   abstractions.
18//! 
19//! ## Solution
20//! 
21//! This library spawns your code on a second thread (a "simulated main thread"),
22//! hijacks the real main thread with winit's event loop, and provides your code
23//! handles to communicate with the main event loop. This allows you to write your
24//! program as you would any other program, treating winit's event loop as an
25//! iterator of events and a handle with which to create windows and ask about the
26//! system. When the simulated main thread exits, it triggers the event loop to
27//! exit, shutting down the process, just like if it were the real main thread.
28//! 
29//! ## Handling of Control Flow
30//! 
31//! ### Blockers
32//! 
33//! The simulated main thread receives winit `Event`s through an `EventReceiver`.
34//! In these events, the user event type is a `Blocker`. This is a concurrency
35//! structure emitted by the main thread which blocks the event loop from
36//! processing further winit events until the `Blocker` is dropped. This is a way
37//! to synchronize the event loop with the simulated main thread to some extent,
38//! such as to synchronize the presenting of images.
39//! 
40//! Whenever the event loop encounters a `RedrawRequested` event, it immediately
41//! emits a `Blocker`, and thus will not proceed until the simulated main thread
42//! receives and drops that `Blocker`.
43//! 
44//! ### `ControlFlow`
45//! 
46//! This library keeps the winit event loop in the `ControlFlow::Wait` state.
47//! Therefore, if you want to redraw a window in a loop, you should call
48//! `Window::request_redraw` after every draw.
49//! 
50//! ## Example
51//! 
52//! ### Without `winit-main`:
53//! 
54//! ```rust,no_run
55//! use winit::{
56//!     event::{Event, WindowEvent},
57//!     event_loop::{ControlFlow, EventLoop},
58//!     window::WindowBuilder,
59//! };
60//! 
61//! fn main() {
62//!     let event_loop = EventLoop::new();
63//!     let window = WindowBuilder::new().build(&event_loop).unwrap();
64//! 
65//!     event_loop.run(move |event, _, control_flow| {
66//!         *control_flow = ControlFlow::Wait;
67//! 
68//!         if matches!(
69//!             event,
70//!             Event::WindowEvent {
71//!                 event: WindowEvent::CloseRequested,
72//!                 window_id,
73//!             } if window_id == window.id()
74//!         ) {
75//!             *control_flow = ControlFlow::Exit;
76//!         }
77//!     });
78//! }
79//! ```
80//! 
81//! ### With `winit-main` (no proc macro):
82//!
83//! ```rust,no_run
84//! use winit_main::reexports::{
85//!     event::{Event, WindowEvent},
86//!     window::WindowAttributes,
87//! };
88//! 
89//! fn main() {
90//!     winit_main::run(|event_loop, events| {
91//!         let window = event_loop
92//!             .create_window(WindowAttributes::default())
93//!             .unwrap();
94//! 
95//!         for event in events.iter() {
96//!             if matches!(
97//!                 event,
98//!                 Event::WindowEvent {
99//!                     event: WindowEvent::CloseRequested,
100//!                     window_id,
101//!                 } if window_id == window.id()
102//!             ) {
103//!                 break;
104//!             }
105//!         }
106//!     });
107//! }
108//! ```
109//! 
110//! ### With `winit-main` (with proc macro):
111//! 
112//! ```rust,compile_fail
113//! use winit_main::{
114//!     reexports::{
115//!         event::{Event, WindowEvent},
116//!         window::WindowAttributes,
117//!     },
118//!     EventLoopHandle,
119//!     EventReceiver,
120//! };
121//! 
122//! 
123//! #[winit_main::main]
124//! fn main(event_loop: EventLoopHandle, events: EventReceiver) {
125//!     let window = event_loop
126//!         .create_window(WindowAttributes::default())
127//!         .unwrap();
128//! 
129//!     for event in events.iter() {
130//!         if matches!(
131//!             event,
132//!             Event::WindowEvent {
133//!                 event: WindowEvent::CloseRequested,
134//!                 window_id,
135//!             } if window_id == window.id()
136//!         ) {
137//!             break;
138//!         }
139//!     }
140//! }
141//! ```
142
143use std::{
144    sync::mpsc,
145    thread,
146    time::Duration,
147    iter,
148    panic::{
149        catch_unwind,
150        AssertUnwindSafe,
151    },
152};
153use winit::{
154    event_loop::{
155        EventLoop,
156        EventLoopProxy,
157        ControlFlow,
158    },
159    event::Event,
160    monitor::MonitorHandle,
161    window::{
162        WindowAttributes,
163        Window,
164    },
165    error::OsError,
166};
167use crate::request::{
168    Request,
169    RequestMessage,
170    RequestCallback,
171    GetAvailableMonitors,
172    GetPrimaryMonitor,
173    CreateWindow,
174};
175
176#[cfg(feature = "proc")]
177pub use winit_main_proc::main;
178
179
180mod request;
181
182
183/// Re-exports of `winit` modules.
184///
185/// Re-exports all `winit` modules except `winit::event_loop`.
186pub mod reexports {
187    // re-export everthing except `event_loop`
188    pub use winit::{
189        dpi,
190        error,
191        event,
192        monitor,
193        platform,
194        window,
195    };
196}
197
198/// Message sent from the simulated main thread to the event loop.
199enum Message {
200    /// Request for some function to be evaluated in the context of the event
201    /// loop and the response sent back to the sender. Sent by
202    /// `EventLoopHandle`.
203    Request(RequestMessage),
204    /// Request for the event loop, and therefore the entire process, to exit.
205    /// Sent when the simulated main thread's user function exits.
206    Exit,
207    /// Unblock the event loop from its currently blocked state. Sent to the
208    /// event loop once, no more and no less, after and only after the event
209    /// loop sends out a `Blocked` user event. 
210    Unblock,
211}
212
213
214/// Handle for sending requests to the main event loop and receiving responses.
215#[derive(Clone)]
216pub struct EventLoopHandle {
217    // use this to wake the event loop up and trigger it to process messages
218    wake_sender: EventLoopProxy<()>,
219    // use this to actually send the message
220    msg_send: mpsc::Sender<Message>,
221}
222
223fn sleep_forever() -> ! {
224    loop {
225        thread::sleep(Duration::new(u64::MAX, 1_000_000_000 - 1));
226    }
227}
228
229impl EventLoopHandle {
230    /// Send a request, wait for a response.
231    fn request_wait<R>(&self, request: R) -> R::Response
232    where
233        R: Request,
234        RequestMessage: From<RequestCallback<R>>,
235    {
236        // pair the request with a channel for the response to return on
237        let (send_response, recv_response) = mpsc::channel();
238        let request = RequestMessage::from(RequestCallback {
239            request,
240            callback: send_response,
241        });
242
243        // send the request
244        let _ = self.msg_send.send(Message::Request(request));
245        // trigger the event loop to wake up and process the request
246        let _ = self.wake_sender.send_event(());
247
248        // wait for the response
249        match recv_response.recv() {
250            Ok(response) => response,
251            Err(mpsc::RecvError) => sleep_forever(),
252        }
253    }
254
255    /// The list of all monitors available on the system. 
256    ///
257    /// Equivalent to
258    /// `winit::event_loop::EventLoopWindowTarget::available_monitors`.
259    pub fn available_monitors(&self) -> Vec<MonitorHandle> {
260        self.request_wait(GetAvailableMonitors)
261    }
262
263    /// The primary monitor of the system.
264    /// 
265    /// Equivalent to
266    /// `winit::event_loop::EventLoopWindowTarget::primary_monitor`.
267    pub fn primary_monitor(&self) -> Option<MonitorHandle> {
268        self.request_wait(GetPrimaryMonitor)
269    }
270
271    /// Attempt to create a new window.
272    ///
273    /// Equivalent to `winit::window::WindowBuilder::build`.
274    pub fn create_window(&self, attributes: WindowAttributes) -> Result<Window, OsError> {
275        self.request_wait(CreateWindow(attributes))
276    }
277}
278
279/// Concurrency structure, emitted as a user event immediately after certain 
280/// other events are emitted, which blocks the event loop until this `Blocker`
281/// is dropped.
282pub struct Blocker(mpsc::Sender<Message>);
283
284impl Drop for Blocker {
285    fn drop(&mut self) {
286        let _ = self.0.send(Message::Unblock);
287    }
288}
289
290impl Blocker {
291    /// Unblock the event loop. This is only to facilitate readability, since 
292    /// `Blocker` unblocks the event loop when dropped.
293    pub fn unblock(self) {
294        drop(self)
295    }
296}
297
298
299/// Handle for receiving events from the main event loop.
300///
301/// Unlike a raw `std::sync::mpsc::Receiver`, this never returns error on
302/// disconnection, because disconnection can only occur for a brief moment
303/// between the main event loop beginning to shut down, and the process as a 
304/// whole exiting. Therefore, when this receives a disconnection error from
305/// the underlying receiver, it enters an infinite sleep cycle as it waits for
306/// the OS to kill the process. 
307pub struct EventReceiver(mpsc::Receiver<Event<'static, Blocker>>);
308
309impl EventReceiver {
310    /// Receive an event, blocking until one is available. 
311    pub fn recv(&self) -> Event<'static, Blocker> {
312        match self.0.recv() {
313            Ok(event) => event,
314            Err(mpsc::RecvError) => sleep_forever(),
315        }
316    }
317
318    /// Attempt to receive an event, blocking until one is available, or the
319    /// `timeout` duration has passed.
320    pub fn recv_timeout(&self, timeout: Duration) -> Option<Event<'static, Blocker>> {
321        match self.0.recv_timeout(timeout) {
322            Ok(event) => Some(event),
323            Err(mpsc::RecvTimeoutError::Timeout) => None,
324            Err(mpsc::RecvTimeoutError::Disconnected) => sleep_forever(),
325        }
326    }
327
328    /// Try to receive an event immediately, never blocking.
329    pub fn try_recv(&self) -> Option<Event<'static, Blocker>> {
330        match self.0.try_recv() {
331            Ok(event) => Some(event),
332            Err(mpsc::TryRecvError::Empty) => None,
333            Err(mpsc::TryRecvError::Disconnected) => sleep_forever(),
334        }
335    }
336
337    /// Iterator form of `self.recv()`. Blocking iterator that never ends.
338    pub fn iter<'a>(&'a self) -> impl Iterator<Item=Event<'static, Blocker>> + 'a {
339        iter::from_fn(move || Some(self.recv()))
340    }
341
342    /// Iterator form of `self.try_recv()`. Non-blocking iterator that drains
343    /// the events currently in the queue. 
344    pub fn try_iter<'a>(&'a self) -> impl Iterator<Item=Event<'static, Blocker>> + 'a {
345        iter::from_fn(move || self.try_recv())
346    }
347}
348
349
350/// Hijack the main thread with a winit event loop, and spawn a new thread with
351/// callbacks to communicate with the main thread.
352/// 
353/// When the new thread, the "simulated main thread" exits, the event loop will
354/// also exit loop. This is this is the primary abstraction of this crate, as
355/// it abstracts away `winit`'s inversion of control, and allows `winit` to be
356/// used more like any other library.
357pub fn run<F>(f: F) -> !
358where
359    F: FnOnce(EventLoopHandle, EventReceiver) + Send + 'static
360{
361    // create event loop
362    let event_loop = EventLoop::with_user_event();
363
364    // create queues
365    let (event_send, event_recv) = mpsc::channel();
366    let (msg_send, msg_recv) = mpsc::channel();
367    let msg_send_1 = msg_send;
368    let msg_send_2 = msg_send_1.clone();
369    let msg_send_3 = msg_send_1.clone();
370    let wake_sender_1 = event_loop.create_proxy();
371    let wake_sender_2 = event_loop.create_proxy();
372
373    // spawn simulated main thread    
374    thread::spawn(move || {
375        let handle = EventLoopHandle {
376            wake_sender: wake_sender_1,
377            msg_send: msg_send_1,
378        };
379        let receiver = EventReceiver(event_recv);
380
381        // run the user code
382        let _ = catch_unwind(AssertUnwindSafe(move || f(handle, receiver)));
383
384        // send the exit message to the event loop
385        let _ = msg_send_2.send(Message::Exit);
386        // wake up the event loop
387        let _ = wake_sender_2.send_event(());
388    });
389
390    // enter event loop
391    event_loop.run(move |event, window_target, control_flow| {
392        *control_flow = ControlFlow::Wait;
393
394        let event = match event.to_static() {
395            Some(event) => event,
396            None => return, // TODO: what if user wants the static event?
397        };
398
399        match event.map_nonuser_event() {
400            Ok(nonuser_event) => {
401                // send out event
402                let triggers_block = matches!(
403                    &nonuser_event,
404                    &Event::RedrawRequested(_)
405                );
406                
407                let _ = event_send.send(nonuser_event);
408
409                if triggers_block {
410                    // maybe send out a blocker, then block on it
411                    let blocker = Blocker(msg_send_3.clone());
412                    let _ = event_send.send(Event::UserEvent(blocker));
413
414                    // we must still process messages while blocked blocked, or
415                    // it would likely cause deadlock
416                    'block: for msg in msg_recv.iter() {
417                        match msg {
418                            Message::Request(request) => {
419                                request.run_respond(window_target);
420                            }
421                            Message::Unblock => {
422                                break 'block;
423                            },
424                            Message::Exit => {
425                                *control_flow = ControlFlow::Exit;
426                            }
427                        };
428                    }
429                }
430            }
431            Err(Event::UserEvent(())) => {
432                // process messages
433                // the user event is sent to wake us up and trigger us to 
434                // process messages after a message is sent
435                for msg in msg_recv.try_iter() {
436                    match msg {
437                        Message::Request(request) => {
438                            request.run_respond(window_target);
439                        }
440                        Message::Unblock => unreachable!("not blocked"),
441                        Message::Exit => {
442                            *control_flow = ControlFlow::Exit;
443                        }
444                    };
445                }
446            }
447            Err(_) => unreachable!(),
448        };
449    });
450}