AppKit F14 — Window state & vetoable close
Per the F14 feature spec, the deliverable is the ordered delegate + notification sequence for every state transition — zoom:, miniaturize:/deminiaturize:, toggleFullScreen:, orderOut:/orderFront: — plus the close contract: windowShouldClose: as the first-class veto, and the proof that close skips the delegate ask while performClose: honors it. The program is ./examples/f14-window-state/app.d (with the shared instrument.d logger), built on the scaffold recipe: one pure-D NSObject subclass is simultaneously the NSWindowDelegate (every method logs delegate m=…) and a catch-all NSNotificationCenter observer (addObserver:selector:name:nil object:window logs note name=…) — interleaving the two by timestamp yields the choreography. A phase= tag names the request being serviced; NSWindowDidUpdateNotification (one per run-loop pass) is counted and suppressed.
Last reviewed: June 11, 2026
All run findings are A[ssh]: built and executed on mac-bsn (aarch64-darwin, macOS 26.3.1, LDC 1.41.0) over SSH with the console session locked — the demo reads the lock state itself via CGSessionCopyCurrentDictionary and logged session screen_locked=1 on_console=1. The lock shapes two results honestly reported below: fullscreen fails (windowDidFailToEnterFullScreen) and no window ever becomes key (zero focus events in the entire run), so the miniaturize-focus question and the real Space transition are Tier C.
| Measurement | Value |
|---|---|
zoom: (programmatic maximize) | synchronous, animated — blocks ~356 ms; bracketed by live-resize delegate calls |
zoom: ask order | willUseStandardFrame → (willResize on restore only) → shouldZoom:toFrame: → live-resize |
miniaturize: | asynchronous — returns immediately; Will→DidMiniaturize gap 1.55 s (locked session) |
deminiaturize: | asynchronous, ~0.53 s; isMiniaturized readback still 1 right after the call |
toggleFullScreen: | fails under the locked session — DidFailToEnterFullScreen 69 ms after WillEnter |
collectionBehavior prerequisite | default 0x0; demo sets FullScreenPrimary (1<<7) before toggling |
orderOut:/orderFront: | synchronous isVisible flip; notifications only, no delegate methods |
| Veto contract | windowShouldClose: returning NO honored by performClose: (window stays, flag cleared) |
close vs performClose: | close never asks — windowWillClose fired with no windowShouldClose: (proved on aux) |
| Focus events | 0 key/main transitions in the whole run — locked session grants no key status (Tier C) |
| Exit | clean 0 (loop_exit steps=14 suppressed_didUpdate_notes=8) |
Zoom (maximize) — a synchronous, animated, delegate-mediated resize A[ssh]
The full enter choreography, verbatim (timestamps µs):
497036 APPKIT_F14 state_request kind=zoom api=zoom: zoomed_before=0
497102 APPKIT_F14 delegate m=windowWillUseStandardFrame default=(0,0 1728x1084) phase=zoom_on
497161 APPKIT_F14 delegate m=windowShouldZoom toFrame=(0,0 1728x1084) phase=zoom_on answer=YES
497469 APPKIT_F14 delegate m=windowWillStartLiveResize phase=zoom_on
497508 APPKIT_F14 note name=NSWindowWillStartLiveResizeNotification phase=zoom_on
853249 APPKIT_F14 delegate m=windowDidResize frame=(0,0 1728x1084) phase=zoom_on
853333 APPKIT_F14 note name=NSWindowDidResizeNotification phase=zoom_on
853472 APPKIT_F14 delegate m=windowDidEndLiveResize phase=zoom_on
853522 APPKIT_F14 note name=NSWindowDidEndLiveResizeNotification phase=zoom_on
854132 APPKIT_F14 state_changed tag=after_zoom_on frame=(0,0 1728x1084) zoomed=1 …zoom:blocks for the animation (~356 ms betweenWillStartLiveResizeandDidResize): thestate_changedreadback runs in the same timer tick, immediately after the call returns, and already sees the final frame andisZoomed=1. F02 proved a plainsetFrame:display:is one synchronous unanimated change;zoom:is the animated sibling — and unlikesetFrame:it does run the live-resize bracket (windowWillStartLiveResize/windowDidEndLiveResize), which the scaffold had only ever seen reserved for interactive drags.- The ask order is delegate-first:
windowWillUseStandardFrame:defaultFrame:(proposing the 1728×1084 visible frame) thenwindowShouldZoom:toFrame:— a second veto point this demo answersYES. Only onewindowDidResizearrives for the whole animation, at the end; per-frame resize callbacks do not fire. - Asymmetry: zooming off (restore) inserts a
windowWillResize:toSize:480x348betweenwillUseStandardFrameandshouldZoom; zooming on never consultedwindowWillResizeat all. isZoomedis not free: everyisZoomedreadback re-invokeswindowWillUseStandardFrame:(AppKit computes the standard frame to compare) — that is why straywillUseStandardFramelines precede everystate_changedprobe in the log. A delegate that does work in that method pays it on everyisZoomedquery.- Delegate method and matching notification are back-to-back, delegate first (
windowDidResizeat 853249,NSWindowDidResizeNotificationat 853333) — the delegate is a notification observer under the hood, registered first.
Miniaturize / deminiaturize — fire-and-return, settle later A[ssh]
1294836 APPKIT_F14 state_request kind=minimize api=miniaturize:
1295328 APPKIT_F14 state_changed tag=after_miniaturize frame=(120,120 480x348) zoomed=0 mini=0 visible=1 …
1298922 APPKIT_F14 delegate m=windowWillMiniaturize phase=miniaturize
1298973 APPKIT_F14 note name=NSWindowWillMiniaturizeNotification phase=miniaturize
1299007 APPKIT_F14 note name=NSWindowWillOrderOffScreenNotification phase=miniaturize
2850934 APPKIT_F14 delegate m=windowDidMiniaturize miniaturized=1 phase=miniaturize
2850988 APPKIT_F14 note name=NSWindowDidMiniaturizeNotification phase=miniaturize
2851084 APPKIT_F14 note name=NSWindowDidOrderOffScreenNotification phase=miniaturizeminiaturize:returns before anything happens: the synchronous readback still saysmini=0 visible=1. The transition then runs asynchronously —WillMiniaturize+WillOrderOffScreen~4 ms later, then a 1.55 s gap (the Dock genie animation grinding under a locked, non-composited session) beforeDidMiniaturize→DidOrderOffScreen. The same shape applies todeminiaturize:(readback right after the call:mini=1 visible=0; settled ~0.53 s later via_NSWindowWillBecomeVisible→DidDeminiaturize→_NSWindowDidBecomeVisible). Contrast withzoom:: zoom is synchronous, miniaturize is not — a framework cannot treat "state request" as one shape on this platform.- Where does focus go? Unanswerable at
A[ssh]: the run produced zerodidBecomeKey/didResignKey/didBecomeMainevents because a locked session never grants key status to begin with (key=0in everystate_changedline, occlusion stuck at0x2000— note theNSWindowOcclusionStateVisiblebit0x2is never set, matching the scaffold's "registered but not composited" sidecar evidence). ThewindowDidResignKey-on-miniaturize question is Tier C.
Fullscreen — the transition that needs a real session A[ssh]
collectionBehavior defaults to 0x0; the demo sets NSWindowCollectionBehaviorFullScreenPrimary (1<<7) first. The locked-session result, verbatim:
3690159 APPKIT_F14 state_request kind=fullscreen api=toggleFullScreen:
3691133 APPKIT_F14 note name=NSWindowWillSnapshotForFullScreenNotification phase=fullscreen_enter
3714678 APPKIT_F14 delegate m=windowWillEnterFullScreen phase=fullscreen_enter
3714802 APPKIT_F14 delegate m=windowWillStartLiveResize phase=fullscreen_enter
3722494 APPKIT_F14 delegate m=windowWillResize toSize=1728x1084 phase=fullscreen_enter
3723618 APPKIT_F14 delegate m=windowDidResize frame=(0,0 1728x1084) phase=fullscreen_enter
3726151 APPKIT_F14 delegate m=windowDidEndLiveResize phase=fullscreen_enter
3770834 APPKIT_F14 delegate m=windowWillStartLiveResize phase=fullscreen_enter
3772877 APPKIT_F14 delegate m=windowDidResize frame=(120,120 480x348) phase=fullscreen_enter
3783898 APPKIT_F14 note name=NSWindowDidFailToEnterFullScreenNotification phase=fullscreen_enter
3783918 APPKIT_F14 delegate m=windowDidFailToEnterFullScreen phase=fullscreen_enter- The transition starts — snapshot notification,
WillEnterFullScreen, a live-resize to the full 1728×1084 — then AppKit resizes the window back to 480×348 and reportswindowDidFailToEnterFullScreen:(69 ms afterWillEnter; ~94 ms request-to-fail).windowDidEnterFullScreennever fires;styleMasknever gainsNSWindowStyleMaskFullScreen; the demo's 40-tick wait seesentered=0 failed=1. A Space transition needs a logged-in console — under SSH/locked, the failure path is the finding, and the delegate gets a first-class callback for it (note the unusual order: the notification preceded the delegate method here, the only place the demo observed that inversion). - The exit toggle was then skipped (
skipped=not_fullscreen— the demo checksstyleMaskrather than blindly toggling, which would have entered fullscreen and failed again). Duration of a real enter/exit: Tier C.
orderOut / orderFront — visibility is not a delegate affair A[ssh]
orderOut: flips isVisible synchronously (readback in the same tick: visible=0) and emits only notifications — NSWindowWillOrderOffScreenNotification → NSWindowDidOrderOffScreenNotification (plus the private _NSDisplayLinkInfoProviderDidUpdate); no public NSWindowDelegate method exists for ordering. makeKeyAndOrderFront: mirrors it with the private_NSWindowWillBecomeVisible / _NSWindowDidBecomeVisible pair — the catch-all observer is the only way this demo could see the show transition at all. (And the "makeKey" half did nothing: locked session, no key status.)
The close contract — a first-class veto, with a trapdoor A[ssh]
4295134 APPKIT_F14 close_probe api=close win=aux expect=no_windowShouldClose
4295338 APPKIT_F14 delegate m=windowWillClose win=aux phase=aux_close_direct
4493714 APPKIT_F14 close_probe api=performClose win=main dirty=1 expect=veto
4494857 APPKIT_F14 close_requested veto=1 win=main phase=veto_close_1 mechanism=windowShouldClose_NO
4601204 APPKIT_F14 close_probe after=performClose still_visible=1 dirty=0
4696897 APPKIT_F14 close_probe api=performClose win=main dirty=0 expect=close
4697343 APPKIT_F14 close_requested veto=0 win=main phase=veto_close_2
4697405 APPKIT_F14 delegate m=windowWillClose win=main phase=veto_close_2
4697500 APPKIT_F14 note name=NSWindowWillCloseNotification phase=veto_close_2windowShouldClose:returningNOis the veto, and it sticks: with the dirty flag set,performClose:producedclose_requested veto=1, nowindowWillClose, no notifications,still_visible=1. The once-veto cleared the flag; the secondperformClose:ran the full teardown (windowShouldClose→windowWillClose→NSWindowWillCloseNotification→ order-off-screen →NSWindowDidCloseNotification). Same return-value shape as Win32'sWM_CLOSEhandler; the opposite of X11/Wayland, where the close request is purely advisory and "veto" is just ignoring it.closeskips the ask. On the aux window,closefiredwindowWillClosewithout ever callingwindowShouldClose:— exactly as theperformClose:docs imply (it isperformClose:that asks the delegate "for approval";closejust closes). A framework exposing "request close" must route throughperformClose:(or ask the delegate itself); callingclosesilently bypasses every dirty-document guard.- The demo set
setReleasedWhenClosed:NOso post-close readbacks stay legal — with the defaultYES, a closed window is a dangling object.
Surprises
zoom:is a 356 ms blocking call that runs the live-resize bracket — programmatic maximize animates even though programmaticsetFrame:(F02) does not, and the app's thread is captive for the duration.isZoomedcalls back into the delegate (windowWillUseStandardFrame:) on every query.- The three "state" verbs have three different temporal shapes: zoom synchronous-animated, miniaturize fire-and-settle-later (1.55 s), fullscreen asynchronous with an explicit failure callback.
- Fullscreen fails cleanly under a locked session —
windowDidFailToEnterFullScreenexists precisely for "the environment said no", and it arrived after AppKit had already resized the window full-size and back. - Ordering (
orderOut:/orderFront:) has no delegate API — only notifications, two of them private (_NSWindowWillBecomeVisible). - Delegate-before-notification held everywhere except
DidFailToEnterFullScreen, where the notification landed first. - A locked session grants key status to no one:
makeKeyAndOrderFront:succeeded at ordering but the whole 4.7 s run contains zero focus events.
Sources
- This demo —
./examples/f14-window-state/app.d,./examples/f14-window-state/instrument.d; the scaffold findings (subclassing recipe,stop:idiom, locked-session WindowServer evidence); the F02 resize findings. - Feature spec — F14 window state.
- Apple Developer documentation (Wayback-pinned, bot-hostile host):
NSWindowDelegate,zoom:,isZoomed,windowShouldZoom:toFrame:,windowWillUseStandardFrame:defaultFrame:,miniaturize:,toggleFullScreen:,collectionBehavior,windowDidFailToEnterFullScreen:,windowShouldClose:,performClose:,close,orderOut:,occlusionState.