Go to file
Robert Bragg 420840278b Windows: Implement EventLoopExtPumpEvents and EventLoopExtRunOnDemand
A surprising amount of work was required to enable these extensions
on Windows.

I had originally assumed that pump_events was going to be very similar
to run except would use PeekMessageW instead of GetMessageW to avoid
blocking the external loop but I found the Windows backend broke
several assumptions I had.

Overall I think these changes can hopefully be considered a quite a
significant simplification (I think it's a net deletion of a fair amount
of code) and I think it also helps bring it into slightly closer alignment
with other backends too

Key changes:
- I have removed the `wait_thread` that was a fairly fiddly way of handling
  `ControlFlow::WaitUntil` timeouts in favor of using `SetTimer` which works
  with the same messages picked up by `GetMessage` and `PeekMessage`.
- I have removed the ordering guarantees between `MainEventsCleared`,
  `RedrawRequested` and `RedrawEventsCleared` events due to the complexity in
  maintaining this artificial ordering, which is already not supported
  consistently across backends anyway (in particular this ordering already
  isn't compatible with how MacOS / iOS work).
- `RedrawRequested` events are now directly dispatched via `WM_PAINT` messages
  - comparable to how `RedrawRequested` is dispatched via `drawRect` in the
  MacOS backend.
- I have re-worked how `NewEvents`, `MainEventsCleared`, and `RedrawEventsCleared`
  get dispatched to be more in line with the MacOS backend and also more in line
  with how we have recently discussed defining them for all platforms.

  `NewEvents` is conceptually delivered when the event loop "wakes up" and
  `MainEventsCleared` gets dispatched when the event loop is about to ask the
  OS to wait for new events.

  This is a more portable model, and is already how these events work in the
  MacOS backend.

  `RedrawEventsCleared` are just delivered after `MainEventsCleared` but this
  event no longer has a useful meaning.

Probably the most controversial thing here is that this "breaks" the ordering
rules for redraw event handling, but since my changes interacted with how the
order is maintained I was very reluctant to figure out how to continue
maintaining something that we have recently been discussing changing:

https://github.com/rust-windowing/winit/issues/2640.

Additionally, since the MacOS backend already doesn't strictly maintain this
order it's somewhat academic to see this as a breakage if Winit applications
can't really rely on it already.

This updates the documentation for `request_redraw()` to reflect that we
no longer guarantee that `RedrawRequested` events must be dispatched
after `MainEventsCleared`.
2023-07-28 03:04:32 +04:00
.cargo Improve web example (#2115) 2022-02-25 12:57:46 +01:00
.github Update remaining actions to v3 2023-06-03 17:00:55 +03:00
docs/res Overhaul the Keyboard API 2023-05-28 21:02:59 +03:00
examples Add platform::startup_notify for Wayland/X11 2023-07-20 13:16:51 +00:00
run-wasm Update cargo-run-wasm (#2509) 2022-10-09 04:49:19 +02:00
src Windows: Implement EventLoopExtPumpEvents and EventLoopExtRunOnDemand 2023-07-28 03:04:32 +04:00
tests On Web, implement Send and Sync where appropriate (#2834) 2023-06-05 02:44:54 +02:00
.gitattributes Update changelog guidelines to prevent conflicts from blocking PRs (#2145) 2022-01-23 13:55:33 +01:00
.gitignore Add WindowExtMacOS::{set_,}option_as_alt 2023-01-31 12:35:49 +03:00
.gitmodules Forward porting (#966) 2019-06-23 02:39:26 -04:00
build.rs Correctly detect that we don't support Emscripten (#2971) 2023-07-22 18:31:40 +02:00
Cargo.toml Replace libc with rustix in some modules 2023-07-22 09:32:27 +00:00
CHANGELOG.md On Windows, add drag_resize_window method support (#2966) 2023-07-21 20:01:56 +02:00
clippy.toml Add Fullscreen API compatibility for Safari (#2948) 2023-07-11 00:34:02 +02:00
CONTRIBUTING.md Bump MSRV to 1.64 2023-03-08 19:34:10 +03:00
deny.toml Update xkbcommon-dl to 0.4.0 2023-06-28 14:48:35 +04:00
FEATURES.md On Windows, add drag_resize_window method support (#2966) 2023-07-21 20:01:56 +02:00
HALL_OF_CHAMPIONS.md Add CODEOWNERS file (#2420) 2022-08-11 16:13:56 +02:00
LICENSE Initial commit 2014-07-27 11:41:26 +02:00
README.md Bump version on master 2023-07-01 00:10:02 +04:00
rustfmt.toml Re-format on stable rustfmt (#974) 2019-06-24 12:14:55 -04:00

winit - Cross-platform window creation and management in Rust

Crates.io Docs.rs CI Status

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

Matrix Libera.Chat

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 backend
  • wayland (enabled by default): On Unix platform, compiles with the Wayland backend
  • mint: 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:

  1. Remove ndk-glue from your Cargo.toml
  2. Enable the "android-native-activity" feature for Winit: winit = { version = "0.28", features = [ "android-native-activity" ] }
  3. Add an android_main entrypoint (as above), instead of using the '[ndk_glue::main] proc macro from ndk-macros (optionally add a dependency on android_logger and initialize logging as above).
  4. 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.