How to integrate winit 0.30 with async

⚓ Rust    📅 2025-09-08    👤 surdeus    👁️ 2      

surdeus

I've a frame-rate (FPS) for a system which I want to run manually using one of my tickers. (I've one that uses window.setTimeout (web) / tokio::time::ticker (native) and other that uses window.requestAnimationFrame (web) / tokio::time::ticker (native).) (These are also important because these tickers provide the elapsed delta.)

Project entry points are async (whether they wrap everything with an infinite loop or not relies on if it's a native platform versus the web).

On web, bootstrap occurs slightly different, using wasm-bindgen-futures instead of Tokio. On other platforms, bootstrap uses Tokio (with the Android case a bit more wrappy using the android_activity crate).

Here's an example taken from the winit documentation doing event handling:

use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
use winit::window::{Window, WindowId};

#[derive(Default)]
struct App {
    window: Option<Window>,
}

impl ApplicationHandler for App {
    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
        self.window = Some(event_loop.create_window(Window::default_attributes()).unwrap());
    }

    fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) {
        match event {
            WindowEvent::CloseRequested => {
                println!("The close button was pressed; stopping");
                event_loop.exit();
            },
            WindowEvent::RedrawRequested => {
                // Redraw the application.
                //
                // It's preferable for applications that do not render continuously to render in
                // this event rather than in AboutToWait, since rendering in here allows
                // the program to gracefully handle redraws requested by the OS.

                // Draw.

                // Queue a RedrawRequested event.
                //
                // You only need to call this if you've determined that you need to redraw in
                // applications which do not always need to. Applications that redraw continuously
                // can render here instead.
                self.window.as_ref().unwrap().request_redraw();
            }
            _ => (),
        }
    }
}

fn main() {
    let event_loop = EventLoop::new().unwrap();

    // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
    // dispatched any events. This is ideal for games and similar applications.
    event_loop.set_control_flow(ControlFlow::Poll);

    // ControlFlow::Wait pauses the event loop if no events are available to process.
    // This is ideal for non-game applications that only update in response to user
    // input, and uses significantly less power/CPU time than ControlFlow::Poll.
    event_loop.set_control_flow(ControlFlow::Wait);

    let mut app = App::default();
    event_loop.run_app(&mut app);
}

Seems fine, but my entry point is async, so it'd conflict with winit's EventLoop (and I have a specific frame rate to target, as well). The system (rendering graphics, handling user input, executing Entity-Component-System patterns perhaps) might not be async itself though, I guess?

Also, by what everything indicates, for web the EventLoop should use .spawn_app(...) rather than .run_app(...).

2 posts - 2 participants

Read full topic

🏷️ Rust_feed