Skip to content

Fix native child viewports aborting fullscreen on macOS#8286

Open
smoe wants to merge 2 commits into
emilk:mainfrom
smoe:macos-fullscreen-auxiliary-viewports
Open

Fix native child viewports aborting fullscreen on macOS#8286
smoe wants to merge 2 commits into
emilk:mainfrom
smoe:macos-fullscreen-auxiliary-viewports

Conversation

@smoe

@smoe smoe commented Jul 3, 2026

Copy link
Copy Markdown

Problem

Opening a native child viewport (show_viewport_deferred / show_viewport_immediate) while the root window is in macOS native fullscreen makes AppKit renegotiate the active Space: the child flickers, can get pulled into Split View tiling — ending up unintentionally fullscreen itself — and the root's fullscreen state can be aborted. Reproduced with both the wgpu and glow backends; window state captured via AppKit before the fix:

window="Root window"     fullscreen=true   on_active_space=true
window="Child viewport"  fullscreen=true   on_active_space=true   <- pulled into Split View
+ one stale ghost window left behind

Fix

AppKit's mechanism for windows accompanying a fullscreen window (palettes, inspectors) is NSWindowCollectionBehaviorFullScreenAuxiliary. winit 0.30 doesn't expose it, and it must be set before the window is first ordered on screen — but winit shows windows during creation. So egui-winit now creates affected windows hidden, sets the collection behavior via objc2-app-kit, and then orders them front (respecting the builder's visible/active). Same pattern as the existing iOS safe_area.rs polyfill; I have proposed the API upstream (rust-windowing/winit#4614) so most of the new macos.rs module can be deleted when egui moves to winit 0.31.

New API: ViewportBuilder::with_fullscreen_auxiliary (macOS only):

  • true: always mark the window as fullscreen-auxiliary,
  • false: never,
  • unset (default): auto — mark it if and only if the window is created while the app has a fullscreen window on the active Space, and the viewport doesn't itself request fullscreen. So child viewports "just work" from a fullscreen root, with no behavior change in any other situation.

Trade-off: an auxiliary window cannot itself enter native fullscreen. ViewportCommand::Fullscreen(true) / SetMonitor therefore clear the flag before toggling, so runtime fullscreen requests on child viewports keep working.

After the fix (same repro):

window="Root window"     fullscreen=true   on_active_space=true   <- stays fullscreen
window="Child viewport"  fullscreen=false  on_active_space=true   <- floats on the fullscreen Space

Testing

  • Test app driving the full sequence (fullscreen root → open deferred child → inspect NSWindow state) on macOS 15 / Darwin 25.5.0, run against both wgpu and glow backends: root stays fullscreen, child shows on the same Space with the auxiliary bit set.
  • Regression check with a windowed root: predicate is false, the child window is created visible by winit exactly as before (no auxiliary bit, no hidden-then-show).
  • cargo test -p egui -p egui-winit, clippy (debug + release, all features), cargo doc -D warnings, lint.py, cargo fmt all pass.

Notes

smoe and others added 2 commits July 3, 2026 23:19
When a native child viewport was opened while the root window was in
native fullscreen, macOS renegotiated the active Space: the new window
flickered, could get pulled into Split View tiling (ending up
unintentionally fullscreen itself), and could abort the root's
fullscreen state (emilk#8259).

Fix this by marking such windows as
NSWindowCollectionBehaviorFullScreenAuxiliary, which lets them be shown
on the same Space as a fullscreen window. Since winit 0.30 does not
expose the collection behavior, and it must be set before the window is
first ordered on screen, the window is created hidden, marked via
objc2-app-kit, and then shown.

- New `ViewportBuilder::with_fullscreen_auxiliary` (macOS only):
  `true` = always auxiliary, `false` = never, unset = auto (auxiliary
  iff created while the app has a fullscreen window on the active Space
  and the viewport does not itself request fullscreen).
- `ViewportCommand::Fullscreen(true)` / `SetMonitor` clear the flag
  first, since auxiliary windows cannot enter native fullscreen.

Verified on macOS with both the wgpu and glow backends: the root stays
fullscreen and the child shows on top of it; behavior with a windowed
root is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

Preview is being built...

Preview will be available at https://egui-pr-preview.github.io/pr/8286-macos-fullscreen-auxiliary-viewports

View snapshot changes at kitdiff

@smoe smoe marked this pull request as ready for review July 3, 2026 22:30
@yay

yay commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Related: This PR handles native child-window creation on an existing fullscreen Space, while #8280 addresses viewport lifecycle and rendering failures during native fullscreen transitions.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

macOS: native child viewport flickers/aborts when opened from fullscreen or maximized root window

2 participants