Use winit
like the async
runtime you've always wanted.
winit
is actually asynchronous, contrary to popular belief; it's just not
async
. It uses an event loop to handle events, which is an good fit for some
cases but not others. The maintainers of winit
have referred to this type of
event loop as "poor man's async
"; a system that is not async
but is still asynchronous.
This crate builds an async
interface on top of this event loop.
Consider the following winit
program, which creates a window and prints the
size of the window when it is resized:
use winit::event::{Event, WindowEvent};
use winit::event_loop::EventLoop;
use winit::window::Window;
fn main2(evl: EventLoop<()>) {
let mut window = None;
evl.run(move |event, elwt| {
match event {
Event::Resumed => {
// Application is active; create a window.
window = Some(Window::new(elwt).unwrap());
},
Event::Suspended => {
// Application is inactive; destroy the window.
window = None;
},
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => {
// Window is closed; exit the application.
elwt.exit();
},
WindowEvent::Resized(size) => {
println!("{:?}", size);
}
_ => {},
},
_ => {},
}
});
}
fn main() {
# return;
let evl = EventLoop::new().unwrap();
main2(evl);
}
This strategy is a bit long winded. Now, compare against the equivalent
async-winit
program:
use async_winit::event_loop::EventLoop;
use async_winit::window::Window;
use async_winit::ThreadUnsafe;
use futures_lite::prelude::*;
fn main2(evl: EventLoop<ThreadUnsafe>) {
let window_target = evl.window_target().clone();
evl.block_on(async move {
loop {
// Wait for the application to be active.
window_target.resumed().await;
// Create a window.
let window = Window::<ThreadUnsafe>::new().await.unwrap();
// Print the size of the window when it is resized.
let print_size = async {
window
.resized()
.wait()
.for_each(|size| {
println!("{:?}", size);
})
.await;
true
};
// Wait until the window is closed.
let close = async {
window.close_requested().wait().await;
println!("Close");
true
};
// Wait until the application is suspended.
let suspend = async {
window_target.suspended().wait().await;
false
};
// Run all of these at once.
let needs_exit = print_size.or(close).or(suspend).await;
// If we need to exit, exit. Otherwise, loop again, destroying the window.
if needs_exit {
window_target.exit().await;
} else {
drop(window);
}
}
});
}
fn main() {
# return;
let evl = EventLoop::new();
main2(evl);
}
In my opinion, the flatter async
style is much easier to read and understand.
Your mileage may vary.
- In many cases it may make more sense to think of a program as an
async
task, rather than an event loop. - You don't need to tie everything to the
EventLoopWindowTarget
;Window::new()
and other functions take no parameters and can be called from anywhere as long as anEventLoop
is running somewhere. - You can use the
async
ecosystem to its full potential here.
- There is a not insignificant amount of overhead involved in using
async-winit
. This is becauseasync-winit
is built on top ofwinit
, which is built on top ofwinit
's event loop. This means thatasync-winit
has to convert betweenasync
andwinit
's event loop, which is not free. async-winit
is not as low level aswinit
. This means that you can't do everything that you can do withwinit
.- For instance, data cannot be shared mutable between individual tasks. This
can be easily worked around with
RefCell
in simple cases, but still requires additional thought for shared state.
- For instance, data cannot be shared mutable between individual tasks. This
can be easily worked around with
async-winit
was created by John Nunley (@notgull).
This project is heavily based on async-io
by Stjepan Glavina et al, as well
as winit
by Pierre Kreiger et al.
async-winit
is free software: you can redistribute it and/or modify it under
the terms of one of the following licenses:
- GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
- Mozilla Public License as published by the Mozilla Foundation, version 2.
async-winit
is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License or the Mozilla
Public License for more details.
You should have received a copy of the GNU Lesser General Public License and the
Mozilla Public License along with async-winit
. If not, see
https://www.gnu.org/licenses/.