Unify fullscreen around AppKit native transition#38
Merged
Conversation
Native fullscreen via the green traffic-light button bypassed the get_offset() pillarbox math because the gate keyed on a flag that only the custom Cmd+F path ever set. The 320x240 Spectrum image was stretched to fit whatever shape the display happened to be. Drop the gate. get_offset() runs every frame against [self bounds] and the texture's native dimensions. Windowed mode is a no-op: contentAspectRatio keeps the view at 4:3 and the function returns zero margins, so the GL quad is still drawn corner-to-corner.
Set NSWindowCollectionBehaviorFullScreenPrimary on the main window in awakeFromNib so the green traffic-light button acts as a real fullscreen toggle, with the standard outward-arrows hover icon and the slide-into-its-own-Space animation. Without this opt-in the green button is a zoom widget. OR into the existing collection behavior rather than overwriting so any flags AppKit set during NIB instantiation are preserved.
The Fullscreen menu item targeted DisplayOpenGLView's custom borderless-overlay action. Retarget it to toggleFullScreen: on the main NSWindow so both gestures invoke the same AppKit transition. Direct-target the menu item (XIB id 877) rather than relying on First Responder, so Cmd+F continues to toggle the emulator window even when a pinned utility window holds key. Rename the item to "Enter Full Screen". AppKit's NSWindow auto- toggles the title to "Exit Full Screen" while the window is fullscreen via validateMenuItem:. Drop the now-orphaned -fullscreen: wrapper on FuseController. No XIB targets it and no code calls it.
The Esc-intercept in keyDown: forced the custom borderless overlay toggle when fullscreen was active. With AppKit native fullscreen landing, exit is handled by Cmd+F, Cmd+Ctrl+F, and the green button on hover — Esc on the emulator's keyboard maps to Caps Shift + 1 (EDIT) and should reach the Spectrum like any other key. Forwarding to proxy_emulator unconditionally also restores Esc as the mouse-grab release trigger in fullscreen (input.c uses it).
DisplayOpenGLView's custom -fullscreen: IBAction allocated a borderless NSWindow, reparented the GL view into it via setContentView:, and tracked the swap with fullscreenWindow / windowedWindow ivars. AppKit's native fullscreen now handles all of this. Drop the IBAction, the ivars, the proxy_emulator forwarder, and the Emulator-side stub that flipped settings_current.full_screen. settings_current.full_screen is now read-only — the remaining readers are removed in follow-up commits.
Thirteen IBActions wrapped their bodies in if( !settings_current.full_screen ) and silently no-op'd when the custom borderless overlay was active. The overlay covered the OS chrome, so an Open or Preferences panel underneath it would have been invisible — the guard was defensive code for that constraint. With AppKit native fullscreen the menu bar auto-hides on hover- reveal and panels render over the fullscreen Space the way macOS handles them for any fullscreen app. The guards no longer protect anything; they only prevent the user from doing what Apple's HIG explicitly directs apps to allow: > Don't make people exit full-screen mode to open files, import > images, save files, or perform similar interactions. Remove the wrappers from open:, save_as:, quit:, help:, showRollbackPane:, showTapeBrowserPane:, showKeyboardPane: (and its accidental doubled inner check), showLoadBinaryPane:, showSaveBinaryPane:, showPokeFinderPane:, showPokeMemoryPane:, showMemoryBrowserPane:, and showPreferencesPane:. showMemoryBrowserPane: had a pre-existing structural quirk — the nil-check + alloc was inside the guard but the showWindow: call was outside, so in custom fullscreen it silently messaged a nil controller. The simplification puts both inside the same block, matching every other pane action.
quit: was calling performClose: on the key window and relying on applicationShouldTerminateAfterLastWindowClosed: YES to chain that into a real quit. AppKit treats an explicitly closed window as a user-discard signal and skips restoration for it, so the app would relaunch windowed even when the user quit while fullscreen and the system NSQuitAlwaysKeepsWindows setting allowed restoration. terminate: is the canonical macOS Cmd+Q. It captures restorable state for visible windows before the app exits. terminate: also sends close (not performClose:) to each window during shutdown, which would otherwise bypass windowShouldClose: — where the "Exit Fuse?" confirm and the unsaved-media check live. Move that logic into a new applicationShouldTerminate: on FuseController so it runs on every quit path, and shrink windowShouldClose: to a shim that calls [NSApp terminate:self] and returns NO. Cmd+Q and the red traffic-light close button now re-enter the same handler, so the prompt lives in one place and never double-fires.
ui_error_specific swallowed every error when settings_current.full_screen was set, returning 0 without ever calling aqua_verror. That made runtime errors during fullscreen play invisible: RZX desync, disk read failures on a mounted image, tape malformed-block during playback of a tape mounted before fullscreen, Spectranet socket failures, sound device hot-unplug — all silently dropped. macOS native fullscreen overlays alerts on the fullscreen Space the way it handles any other fullscreen app, so the original concern (panel hidden under the borderless overlay) no longer applies. Distraction-free is the OS's job via the menu-bar auto-hide; a malfunction is not the happy path.
cocoadisplay_resize_window read settings_current.full_screen twice to skip the resize when the window is fullscreen — once on the caller's thread (worker or main) and again after dispatching to main. The authoritative fullscreen state lives in NSWindow's styleMask, which is main-thread-only. Drop the pre-dispatch check and let the main-thread block be the sole gate. The cost is one extra cheap dispatch when the resize is going to be skipped anyway; the win is one fewer read of a mirrored flag whose only purpose was thread-safe access from worker code.
Adding a child window without NSWindowCollectionBehaviorFullScreenAuxiliary to a fullscreen parent forces AppKit to demote the parent out of fullscreen so the two can coexist on the regular desktop. Result: opening Preferences from fullscreen kicked the emulator out of fullscreen rather than overlaying Preferences on the fullscreen Space. OR the auxiliary flag into the child's collection behavior in pinAsChildOf:. The bit is harmless when the parent is windowed and gives every utility window the right Space-sharing semantics for free, since they all reach the fullscreen state through this single choke point.
A user who entered fullscreen with the cursor grabbed used to regain it on the way out. The behavior went with the custom-fullscreen IBAction; restore it under the AppKit transition by adding a windowDidExitFullScreen: hook on DisplayOpenGLView (the window's existing delegate). The entry side is intentionally not symmetric. Auto-grabbing on fullscreen entry would hide the cursor and block the menu-bar-on- hover reveal that exposes Open, Preferences, and the rest of the menu in fullscreen.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problems solved
Cmd+Fentered fullscreen via the custom borderless-window path, but every keypress inside it produced a UI beep and was otherwise ignored.Cmd+F: it used AppKit's native transition and never setsettings_current.full_screen, so the renderer skipped letterboxing and the emulated display was stretched instead of pillarboxed.Caps Shift+1(EDIT) and blocking mouse-grab release.Cmd+QinvokedperformClose:on the key window instead ofNSApp terminate:, so the exit-confirm and unsaved-media check could be bypassed from fullscreen.Known issues
NSOpenPanel / NSSavePanel (e.g.
Cmd+Oto open a file) are slow to appear in fullscreen (~3–5 s first open vs ~0.5 s windowed). The bottleneck is inside AppKit's powerbox/Space negotiation. This path was previously unreachable from fullscreen, so it is not a regression; needs a separate fix.