1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
//! This is a [`winit`](https://lib.rs/crates/winit) utility which abstracts away
//! winit's event-loop inversion of control.
//! 
//! ## Rationale
//! 
//! Winit necessarily hijacks the main thread due to platform constraints,
//! creating a "don't call us, we'll call you" situation. Inversions of control
//! have some undesirable properties, including:
//! 
//! - It's difficult to add inversion of control to a program after the fact, as
//!   it tends to fundamentally affect the program's architecture.
//! - For the above reason, it's difficult to write programs which are generic
//!   between inversions-of-control, or to modify a program from using one
//!   inversion-of-control framework to using a different framework.
//! - It's tricky to use several inversions of control simultaneously. For example,
//!   it would be difficult to combine tokio with winit without creating additional
//!   abstractions.
//! 
//! ## Solution
//! 
//! This library spawns your code on a second thread (a "simulated main thread"),
//! hijacks the real main thread with winit's event loop, and provides your code
//! handles to communicate with the main event loop. This allows you to write your
//! program as you would any other program, treating winit's event loop as an
//! iterator of events and a handle with which to create windows and ask about the
//! system. When the simulated main thread exits, it triggers the event loop to
//! exit, shutting down the process, just like if it were the real main thread.
//! 
//! ## Handling of Control Flow
//! 
//! ### Blockers
//! 
//! The simulated main thread receives winit `Event`s through an `EventReceiver`.
//! In these events, the user event type is a `Blocker`. This is a concurrency
//! structure emitted by the main thread which blocks the event loop from
//! processing further winit events until the `Blocker` is dropped. This is a way
//! to synchronize the event loop with the simulated main thread to some extent,
//! such as to synchronize the presenting of images.
//! 
//! Whenever the event loop encounters a `RedrawRequested` event, it immediately
//! emits a `Blocker`, and thus will not proceed until the simulated main thread
//! receives and drops that `Blocker`.
//! 
//! ### `ControlFlow`
//! 
//! This library keeps the winit event loop in the `ControlFlow::Wait` state.
//! Therefore, if you want to redraw a window in a loop, you should call
//! `Window::request_redraw` after every draw.
//! 
//! ## Example
//! 
//! ### Without `winit-main`:
//! 
//! ```rust,no_run
//! use winit::{
//!     event::{Event, WindowEvent},
//!     event_loop::{ControlFlow, EventLoop},
//!     window::WindowBuilder,
//! };
//! 
//! fn main() {
//!     let event_loop = EventLoop::new();
//!     let window = WindowBuilder::new().build(&event_loop).unwrap();
//! 
//!     event_loop.run(move |event, _, control_flow| {
//!         *control_flow = ControlFlow::Wait;
//! 
//!         if matches!(
//!             event,
//!             Event::WindowEvent {
//!                 event: WindowEvent::CloseRequested,
//!                 window_id,
//!             } if window_id == window.id()
//!         ) {
//!             *control_flow = ControlFlow::Exit;
//!         }
//!     });
//! }
//! ```
//! 
//! ### With `winit-main` (no proc macro):
//!
//! ```rust,no_run
//! use winit_main::reexports::{
//!     event::{Event, WindowEvent},
//!     window::WindowAttributes,
//! };
//! 
//! fn main() {
//!     winit_main::run(|event_loop, events| {
//!         let window = event_loop
//!             .create_window(WindowAttributes::default())
//!             .unwrap();
//! 
//!         for event in events.iter() {
//!             if matches!(
//!                 event,
//!                 Event::WindowEvent {
//!                     event: WindowEvent::CloseRequested,
//!                     window_id,
//!                 } if window_id == window.id()
//!             ) {
//!                 break;
//!             }
//!         }
//!     });
//! }
//! ```
//! 
//! ### With `winit-main` (with proc macro):
//! 
//! ```rust,compile_fail
//! use winit_main::{
//!     reexports::{
//!         event::{Event, WindowEvent},
//!         window::WindowAttributes,
//!     },
//!     EventLoopHandle,
//!     EventReceiver,
//! };
//! 
//! 
//! #[winit_main::main]
//! fn main(event_loop: EventLoopHandle, events: EventReceiver) {
//!     let window = event_loop
//!         .create_window(WindowAttributes::default())
//!         .unwrap();
//! 
//!     for event in events.iter() {
//!         if matches!(
//!             event,
//!             Event::WindowEvent {
//!                 event: WindowEvent::CloseRequested,
//!                 window_id,
//!             } if window_id == window.id()
//!         ) {
//!             break;
//!         }
//!     }
//! }
//! ```

use std::{
    sync::mpsc,
    thread,
    time::Duration,
    iter,
    panic::{
        catch_unwind,
        AssertUnwindSafe,
    },
};
use winit::{
    event_loop::{
        EventLoop,
        EventLoopProxy,
        ControlFlow,
    },
    event::Event,
    monitor::MonitorHandle,
    window::{
        WindowAttributes,
        Window,
    },
    error::OsError,
};
use crate::request::{
    Request,
    RequestMessage,
    RequestCallback,
    GetAvailableMonitors,
    GetPrimaryMonitor,
    CreateWindow,
};

#[cfg(feature = "proc")]
pub use winit_main_proc::main;


mod request;


/// Re-exports of `winit` modules.
///
/// Re-exports all `winit` modules except `winit::event_loop`.
pub mod reexports {
    // re-export everthing except `event_loop`
    pub use winit::{
        dpi,
        error,
        event,
        monitor,
        platform,
        window,
    };
}

/// Message sent from the simulated main thread to the event loop.
enum Message {
    /// Request for some function to be evaluated in the context of the event
    /// loop and the response sent back to the sender. Sent by
    /// `EventLoopHandle`.
    Request(RequestMessage),
    /// Request for the event loop, and therefore the entire process, to exit.
    /// Sent when the simulated main thread's user function exits.
    Exit,
    /// Unblock the event loop from its currently blocked state. Sent to the
    /// event loop once, no more and no less, after and only after the event
    /// loop sends out a `Blocked` user event. 
    Unblock,
}


/// Handle for sending requests to the main event loop and receiving responses.
#[derive(Clone)]
pub struct EventLoopHandle {
    // use this to wake the event loop up and trigger it to process messages
    wake_sender: EventLoopProxy<()>,
    // use this to actually send the message
    msg_send: mpsc::Sender<Message>,
}

fn sleep_forever() -> ! {
    loop {
        thread::sleep(Duration::new(u64::MAX, 1_000_000_000 - 1));
    }
}

impl EventLoopHandle {
    /// Send a request, wait for a response.
    fn request_wait<R>(&self, request: R) -> R::Response
    where
        R: Request,
        RequestMessage: From<RequestCallback<R>>,
    {
        // pair the request with a channel for the response to return on
        let (send_response, recv_response) = mpsc::channel();
        let request = RequestMessage::from(RequestCallback {
            request,
            callback: send_response,
        });

        // send the request
        let _ = self.msg_send.send(Message::Request(request));
        // trigger the event loop to wake up and process the request
        let _ = self.wake_sender.send_event(());

        // wait for the response
        match recv_response.recv() {
            Ok(response) => response,
            Err(mpsc::RecvError) => sleep_forever(),
        }
    }

    /// The list of all monitors available on the system. 
    ///
    /// Equivalent to
    /// `winit::event_loop::EventLoopWindowTarget::available_monitors`.
    pub fn available_monitors(&self) -> Vec<MonitorHandle> {
        self.request_wait(GetAvailableMonitors)
    }

    /// The primary monitor of the system.
    /// 
    /// Equivalent to
    /// `winit::event_loop::EventLoopWindowTarget::primary_monitor`.
    pub fn primary_monitor(&self) -> Option<MonitorHandle> {
        self.request_wait(GetPrimaryMonitor)
    }

    /// Attempt to create a new window.
    ///
    /// Equivalent to `winit::window::WindowBuilder::build`.
    pub fn create_window(&self, attributes: WindowAttributes) -> Result<Window, OsError> {
        self.request_wait(CreateWindow(attributes))
    }
}

/// Concurrency structure, emitted as a user event immediately after certain 
/// other events are emitted, which blocks the event loop until this `Blocker`
/// is dropped.
pub struct Blocker(mpsc::Sender<Message>);

impl Drop for Blocker {
    fn drop(&mut self) {
        let _ = self.0.send(Message::Unblock);
    }
}

impl Blocker {
    /// Unblock the event loop. This is only to facilitate readability, since 
    /// `Blocker` unblocks the event loop when dropped.
    pub fn unblock(self) {
        drop(self)
    }
}


/// Handle for receiving events from the main event loop.
///
/// Unlike a raw `std::sync::mpsc::Receiver`, this never returns error on
/// disconnection, because disconnection can only occur for a brief moment
/// between the main event loop beginning to shut down, and the process as a 
/// whole exiting. Therefore, when this receives a disconnection error from
/// the underlying receiver, it enters an infinite sleep cycle as it waits for
/// the OS to kill the process. 
pub struct EventReceiver(mpsc::Receiver<Event<'static, Blocker>>);

impl EventReceiver {
    /// Receive an event, blocking until one is available. 
    pub fn recv(&self) -> Event<'static, Blocker> {
        match self.0.recv() {
            Ok(event) => event,
            Err(mpsc::RecvError) => sleep_forever(),
        }
    }

    /// Attempt to receive an event, blocking until one is available, or the
    /// `timeout` duration has passed.
    pub fn recv_timeout(&self, timeout: Duration) -> Option<Event<'static, Blocker>> {
        match self.0.recv_timeout(timeout) {
            Ok(event) => Some(event),
            Err(mpsc::RecvTimeoutError::Timeout) => None,
            Err(mpsc::RecvTimeoutError::Disconnected) => sleep_forever(),
        }
    }

    /// Try to receive an event immediately, never blocking.
    pub fn try_recv(&self) -> Option<Event<'static, Blocker>> {
        match self.0.try_recv() {
            Ok(event) => Some(event),
            Err(mpsc::TryRecvError::Empty) => None,
            Err(mpsc::TryRecvError::Disconnected) => sleep_forever(),
        }
    }

    /// Iterator form of `self.recv()`. Blocking iterator that never ends.
    pub fn iter<'a>(&'a self) -> impl Iterator<Item=Event<'static, Blocker>> + 'a {
        iter::from_fn(move || Some(self.recv()))
    }

    /// Iterator form of `self.try_recv()`. Non-blocking iterator that drains
    /// the events currently in the queue. 
    pub fn try_iter<'a>(&'a self) -> impl Iterator<Item=Event<'static, Blocker>> + 'a {
        iter::from_fn(move || self.try_recv())
    }
}


/// Hijack the main thread with a winit event loop, and spawn a new thread with
/// callbacks to communicate with the main thread.
/// 
/// When the new thread, the "simulated main thread" exits, the event loop will
/// also exit loop. This is this is the primary abstraction of this crate, as
/// it abstracts away `winit`'s inversion of control, and allows `winit` to be
/// used more like any other library.
pub fn run<F>(f: F) -> !
where
    F: FnOnce(EventLoopHandle, EventReceiver) + Send + 'static
{
    // create event loop
    let event_loop = EventLoop::with_user_event();

    // create queues
    let (event_send, event_recv) = mpsc::channel();
    let (msg_send, msg_recv) = mpsc::channel();
    let msg_send_1 = msg_send;
    let msg_send_2 = msg_send_1.clone();
    let msg_send_3 = msg_send_1.clone();
    let wake_sender_1 = event_loop.create_proxy();
    let wake_sender_2 = event_loop.create_proxy();

    // spawn simulated main thread    
    thread::spawn(move || {
        let handle = EventLoopHandle {
            wake_sender: wake_sender_1,
            msg_send: msg_send_1,
        };
        let receiver = EventReceiver(event_recv);

        // run the user code
        let _ = catch_unwind(AssertUnwindSafe(move || f(handle, receiver)));

        // send the exit message to the event loop
        let _ = msg_send_2.send(Message::Exit);
        // wake up the event loop
        let _ = wake_sender_2.send_event(());
    });

    // enter event loop
    event_loop.run(move |event, window_target, control_flow| {
        *control_flow = ControlFlow::Wait;

        let event = match event.to_static() {
            Some(event) => event,
            None => return, // TODO: what if user wants the static event?
        };

        match event.map_nonuser_event() {
            Ok(nonuser_event) => {
                // send out event
                let triggers_block = matches!(
                    &nonuser_event,
                    &Event::RedrawRequested(_)
                );
                
                let _ = event_send.send(nonuser_event);

                if triggers_block {
                    // maybe send out a blocker, then block on it
                    let blocker = Blocker(msg_send_3.clone());
                    let _ = event_send.send(Event::UserEvent(blocker));

                    // we must still process messages while blocked blocked, or
                    // it would likely cause deadlock
                    'block: for msg in msg_recv.iter() {
                        match msg {
                            Message::Request(request) => {
                                request.run_respond(window_target);
                            }
                            Message::Unblock => {
                                break 'block;
                            },
                            Message::Exit => {
                                *control_flow = ControlFlow::Exit;
                            }
                        };
                    }
                }
            }
            Err(Event::UserEvent(())) => {
                // process messages
                // the user event is sent to wake us up and trigger us to 
                // process messages after a message is sent
                for msg in msg_recv.try_iter() {
                    match msg {
                        Message::Request(request) => {
                            request.run_respond(window_target);
                        }
                        Message::Unblock => unreachable!("not blocked"),
                        Message::Exit => {
                            *control_flow = ControlFlow::Exit;
                        }
                    };
                }
            }
            Err(_) => unreachable!(),
        };
    });
}