ae7497e18f
The idea that redraw events are dispatched with a specific ordering that makes it possible to specifically report when we have finished dispatching redraw events isn't portable and the way in which we dispatched RedrawEventsCleared was inconsistent across backends. More generally speaking, there is no inherent relationship between redrawing and event loop iterations. An event loop may wake up at any frequency depending on what sources of input events are being listened to but redrawing is generally throttled and in some way synchronized with the display frequency. Similarly there's no inherent relationship between a single event loop iteration and the dispatching of any specific kind of "main" event. An event loop wakes up when there are events to read (e.g. input events or responses from a display server / compositor) and goes back to waiting when there's nothing else to read. There isn't really a special kind of "main" event that is dispatched in order with respect to other events. What we can do more portably is emit an event when the event loop is about to block and wait for new events. In practice this is very similar to how MainEventsCleared was implemented except it wasn't the very last event previously since redraw events could be dispatched afterwards. The main backend where we don't strictly know when we're going to wait for events is Web (since the real event loop is internal to the browser). For now we emulate AboutToWait on Web similar to how MainEventsCleared was dispatched. In practice most applications almost certainly shouldn't care about AboutToWait because the frequency of event loop iterations is essentially arbitrary and usually irrelevant. |
||
---|---|---|
.cargo | ||
.github | ||
docs/res | ||
examples | ||
run-wasm | ||
src | ||
tests | ||
.gitattributes | ||
.gitignore | ||
.gitmodules | ||
build.rs | ||
Cargo.toml | ||
CHANGELOG.md | ||
clippy.toml | ||
CONTRIBUTING.md | ||
deny.toml | ||
FEATURES.md | ||
HALL_OF_CHAMPIONS.md | ||
LICENSE | ||
README.md | ||
rustfmt.toml |
winit - Cross-platform window creation and management in Rust
[dependencies]
winit = "0.29.0-beta.0"
Documentation
For features within the scope of winit, see FEATURES.md.
For features outside the scope of winit, see Missing features provided by other crates in the wiki.
Contact Us
Join us in any of these:
Usage
Winit is a window creation and management library. It can create windows and lets you handle events (for example: the window being resized, a key being pressed, a mouse movement, etc.) produced by window.
Winit is designed to be a low-level brick in a hierarchy of libraries. Consequently, in order to show something on the window you need to use the platform-specific getters provided by winit, or another library.
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;
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
_ => (),
}
});
}
Winit is only officially supported on the latest stable version of the Rust compiler.
Cargo Features
Winit provides the following features, which can be enabled in your Cargo.toml
file:
serde
: Enables serialization/deserialization of certain types with Serde.x11
(enabled by default): On Unix platform, compiles with the X11 backendwayland
(enabled by default): On Unix platform, compiles with the Wayland backendmint
: Enables mint (math interoperability standard types) conversions.
Platform-specific usage
Wayland
Note that windows don't appear on Wayland until you draw/present to them.
WebAssembly
To run the web example: cargo run-wasm --example web
Winit supports compiling to the wasm32-unknown-unknown
target with web-sys
.
On the web platform, a Winit window is backed by a <canvas>
element. You can
either provide Winit with a <canvas>
element, or let Winit
create a <canvas>
element which you can then retrieve and
insert it into the DOM yourself.
For example code using Winit with WebAssembly, check out the web example. For information on using Rust on WebAssembly, check out the Rust and WebAssembly book.
Android
The Android backend builds on (and exposes types from) the ndk
crate.
Native Android applications need some form of "glue" crate that is responsible for defining the main entry point for your Rust application as well as tracking various life-cycle events and synchronizing with the main JVM thread.
Winit uses the android-activity as a
glue crate (prior to 0.28
it used
ndk-glue).
The version of the glue crate that your application depends on must match the version that Winit depends on because the glue crate is responsible for your application's main entrypoint. If Cargo resolves multiple versions they will clash.
winit
glue compatibility table:
winit | ndk-glue |
---|---|
0.28 | android-activity = "0.4" |
0.27 | ndk-glue = "0.7" |
0.26 | ndk-glue = "0.5" |
0.25 | ndk-glue = "0.3" |
0.24 | ndk-glue = "0.2" |
The recommended way to avoid a conflict with the glue version is to avoid explicitly
depending on the android-activity
crate, and instead consume the API that
is re-exported by Winit under winit::platform::android::activity::*
Running on an Android device needs a dynamic system library, add this to Cargo.toml:
[lib]
name = "main"
crate-type = ["cdylib"]
All Android applications are based on an Activity
subclass and the
android-activity
crate is designed to support different choices for this base
class. Your application must specify the base class it needs via a feature flag:
Base Class | Feature Flag | Notes |
---|---|---|
NativeActivity |
android-native-activity |
Built-in to Android - it is possible to use without compiling any Java or Kotlin code. Java or Kotlin code may be needed to subclass NativeActivity to access some platform features. It does not derive from the AndroidAppCompat base class. |
GameActivity |
android-game-activity |
Derives from AndroidAppCompat which is a defacto standard Activity base class that helps support a wider range of Android versions. Requires a build system that can compile Java or Kotlin and fetch Android dependencies from a Maven repository (or link with an embedded release of GameActivity ) |
For example, add this to Cargo.toml:
winit = { version = "0.28", features = [ "android-native-activity" ] }
[target.'cfg(target_os = "android")'.dependencies]
android_logger = "0.11.0"
And, for example, define an entry point for your library like this:
#[cfg(target_os = "android")]
use winit::platform::android::activity::AndroidApp;
#[cfg(target_os = "android")]
#[no_mangle]
fn android_main(app: AndroidApp) {
use winit::platform::android::EventLoopBuilderExtAndroid;
android_logger::init_once(android_logger::Config::default().with_min_level(log::Level::Trace));
let event_loop = EventLoopBuilder::with_user_event()
.with_android_app(app)
.build();
_main(event_loop);
}
For more details, refer to these android-activity
example applications.
Converting from ndk-glue
to android-activity
If your application is currently based on NativeActivity
via the ndk-glue
crate and building with cargo apk
then the minimal changes would be:
- Remove
ndk-glue
from yourCargo.toml
- Enable the
"android-native-activity"
feature for Winit:winit = { version = "0.28", features = [ "android-native-activity" ] }
- Add an
android_main
entrypoint (as above), instead of using the '[ndk_glue::main]
proc macro fromndk-macros
(optionally add a dependency onandroid_logger
and initialize logging as above). - Pass a clone of the
AndroidApp
that your application receives to Winit when building your event loop (as shown above).
MacOS
A lot of functionality expects the application to be ready before you start doing anything; this includes creating windows, fetching monitors, drawing, and so on, see issues #2238, #2051 and #2087.
If you encounter problems, you should try doing your initialization inside
Event::NewEvents(StartCause::Init)
.
iOS
Similar to macOS, iOS's main UIApplicationMain
does some init work that's required
by all UI related code, see issue #1705. You should consider creating your windows
inside Event::NewEvents(StartCause::Init)
.
Redox OS
Redox OS has some functionality not present yet, that will be implemented when its orbital display server provides it.