Handling window creation in winit crate

I'm a new to Rust and I'm working on a Rust GUI application and have a handler with a resumed method that's intended to be called when the application becomes active (similar to applicationDidBecomeActive(_:) in iOS). Currently, I'm creating the main application window within this resumed method:

struct App {
    window: Option<Window>,
}

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

    // ... other methods
}

My concern is that the resumed method might be called multiple times during the application's lifecycle (e.g., when returning from the background). This could lead to the creation of multiple Window instances even if one already exists.
What are the common or recommended approaches in Rust GUI development to handle this scenario and ensure that a new window isn't created on every resumed event if one is already present?

They’re both the same[1]:

On iOS, the Resumed event is emitted in response to an applicationDidBecomeActive callback […]

(docs)

Note that resumed/suspended only really exist on Android, iOS, and Web platforms. (Winit sends a fake Resumed event on all platforms right after the event loop starts to make sure all applications run their initialization code in ApplicationHandler::resumed).

The docs for ApplicationHandler::resumed definitely validate this concern:

Considering that the implementation of Suspended and Resumed events may be internally driven by multiple platform-specific events, and that there may be subtle differences across platforms with how these internal events are delivered, it’s recommended that applications be able to gracefully handle redundant (i.e. back-to-back) Suspended or Resumed events.

To fix this, you can just not create a window if self.window.is_some(). (Edit: do not destroy the window in suspended() like I said in the previous version of this post. In suspended(), you need to destroy all other render surfaces like wgpu::Surface, but not the window).

This thread might have just revealed a bug in some code I’ve written, where I don’t remember handling being resumed multiple times... and I was destroying the window in suspended()... :sweat_smile:


  1. on iOS where applicationDidBecomeActive exists ↩︎

1 Like

Is that the right action? I can see where it feels symmetric, but the documentation doesn't say that windows are invalidated, just render surfaces. Destroying and later recreating the window would discard the user's window management choices, and on some platforms, might even stop resuming from happening at all (because there is no longer a window for the user to switch to).

3 Likes