Crate winit_main[][src]

Expand description

This is a 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 Events 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:

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):

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):

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;
        }
    }
}

Modules

Re-exports of winit modules.

Structs

Concurrency structure, emitted as a user event immediately after certain other events are emitted, which blocks the event loop until this Blocker is dropped.

Handle for sending requests to the main event loop and receiving responses.

Handle for receiving events from the main event loop.

Functions

Hijack the main thread with a winit event loop, and spawn a new thread with callbacks to communicate with the main thread.