Skip to content

AppKit: Launch app inside EventLoop::new#4534

Open
madsmtm wants to merge 1 commit intomasterfrom
madsmtm/launch-in-new
Open

AppKit: Launch app inside EventLoop::new#4534
madsmtm wants to merge 1 commit intomasterfrom
madsmtm/launch-in-new

Conversation

@madsmtm
Copy link
Copy Markdown
Member

@madsmtm madsmtm commented Mar 19, 2026

This moves the application launch from the beginning of EventLoop::run_app to EventLoop::new, which:

  • Feels more consistent, we now emit new_events(StartCause::Init) at the beginning of run_app_on_demand instead of conditionally depending on if the application has launched or not.
  • Would allow us to bring back event_loop.create_window in some form on macOS, since the application would now be launched between EventLoop::new and EventLoop::run_app. See Deprecate window creation with stale event loop #3447 for why we removed it originally.

This differs semantically from iOS where we must still launch the application in EventLoop::run_app, but iOS is the special snowflake here, the other desktop platforms' are semantically launched after EventLoop::new. Doing it this way also matches what GLFW's init function does.

  • Tested on all platforms changed
    • macOS 15.7.3
    • macOS 10.12.7
  • Added an entry to the changelog module if knowledge of this change could be valuable to users
  • Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
  • Created or updated an example program if it would help users understand this functionality

This moves the application launch from the beginning of
`EventLoop::run_app` to `EventLoop::new`, which:
- Feels more consistent, we now emit `new_events(StartCause::Init)` at
  the beginning of `run_app_on_demand` instead of conditionally
  depending on if the application has launched or not.
- Would allow us to bring back `event_loop.create_window` in some form
  on macOS, since the application would now be launched between
  `EventLoop::new` and `EventLoop::run_app`.

This differs semantically from iOS where we must still launch the
application in `EventLoop::run_app`, but iOS is the special snowflake
here, the other desktop platforms' are semantically launched after
`EventLoop::new`.

Doing it this way also matches what GLFW's init function does.
@madsmtm madsmtm added S - enhancement Wouldn't this be the coolest? DS - appkit Affects the AppKit/macOS backend labels Mar 19, 2026
Comment on lines +249 to +275
// Run `finishLaunching` just in case it works.
app.finishLaunching();
// Now _ideally_, calling `finishLaunching` should be enough for the application to, you
// know, launch (create the a dock icon etc.), but unfortunately, this doesn't happen for
// various godforsaken reasons... The only way to make the application properly launch is by
// calling `NSApplication::run`.
//
// So we check if the application hasn't finished launching, and if it hasn't, we run it
// once to finish it.
//
// This is _very_ important, there's a _lot_ of weird and subtle state that requires that
// the application is launched properly, including window creation, the menu bar,
// activation, see:
// - https://github.com/rust-windowing/winit/pull/1903
// - https://github.com/rust-windowing/winit/pull/1922
// - https://github.com/rust-windowing/winit/issues/2238
// - https://github.com/rust-windowing/winit/issues/2051
// - https://github.com/rust-windowing/winit/issues/2087
// - https://developer.apple.com/forums/thread/772169
//
// This approach is similar to what other cross-platform windowing libraries do (except that
// we do it without a delegate to allow users to override that):
// - GLFW delegate: https://github.com/glfw/glfw/blob/3.4/src/cocoa_init.m#L439-L443
// - GLFW launch: https://github.com/glfw/glfw/blob/3.4/src/cocoa_init.m#L634-L635
// - FLTK delegate: https://github.com/fltk/fltk/blob/release-1.4.4/src/Fl_cocoa.mm#L1604-L1607
// - FLTK launch: https://github.com/fltk/fltk/blob/release-1.4.4/src/Fl_cocoa.mm#L1903-L1919
// - Stackoverflow issue: https://stackoverflow.com/questions/48020222/how-to-make-nsapp-run-not-block/67626393#67626393
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a bit furious (at both myself and Apple) about this, I've been oblivious to this for years (even though I vividly recall starting at GLFW's initialization code for what must've been hours, and concluding "they must just not have fixed this issue"), it's only ~half a year ago that I connected the dots and found out that you actually can launch the application!

Had I known this way back then, I wouldn't have pushed so hard for removing Window::new / event_loop.create_window :/

(Those are still problematic on iOS, but that's a secondary citizen that needs special care, so I'd be more fine with excluding that from such considerations).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Window::new requires event loop anyway, so it's irrelevant where it is.

@madsmtm
Copy link
Copy Markdown
Member Author

madsmtm commented Mar 19, 2026

CC @kchibisov @daxpedda with this, it might make sense to reintroduce a desktop/web-specific EventLoop::create_window? Or maybe even in the future re-merge EventLoop and ActiveEventLoop?

impl EventLoop {
    #[cfg(not(any(android_platform, ios_platform)))]
    pub fn create_window_desktop(
        &self,
        window_attributes: WindowAttributes,
    ) -> Result<Box<dyn Window>, RequestError> {
        self.event_loop.window_target().create_window(window_attributes)
    }
}

I suspect that would solve a lot of the issues users have in #3626 and #3877, most people don't need their thing to run on Android and iOS.

@madsmtm madsmtm added the C - nominated Nominated for discussion in the next meeting label Mar 19, 2026
@madsmtm madsmtm mentioned this pull request Mar 20, 2026
25 tasks
@kchibisov
Copy link
Copy Markdown
Member

CC @kchibisov @daxpedda with this, it might make sense to reintroduce a desktop/web-specific EventLoop::create_window? Or maybe even in the future re-merge EventLoop and ActiveEventLoop?

As per discussion, I'd be fine if you return Surface trait and return it instead

impl EventLoop {
    #[cfg(not(any(android_platform, ios_platform)))]
    pub fn create_surface(
        &self,
        window_attributes: WindowAttributes,
    ) -> Result<Box<dyn Surface>, RequestError> {
        self.event_loop.window_target().create_window(window_attributes)
    }
}

pub trait Surface {
    fn window_handle();
    fn surface_size() -> 
}

There're some APIs in the fly #4264 .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C - nominated Nominated for discussion in the next meeting DS - appkit Affects the AppKit/macOS backend S - enhancement Wouldn't this be the coolest?

Development

Successfully merging this pull request may close these issues.

2 participants