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}