// macOS/AppKit F14 demo — window state transitions & the vetoable close
// (../../../features/f14-window-state.md).
// The deliverable is the ORDERED delegate-method + notification sequence for each
// 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).
//
// Instrumentation: every NSWindowDelegate method the demo implements logs
// `delegate m=<method>` and a catch-all NSNotificationCenter observer
// (addObserver:selector:name:nil object:window) logs `note name=<NSWindow…>` —
// interleaving the two by timestamp yields the choreography per transition.
// A `phase=` tag carried on every line names the request being serviced.
// NSWindowDidUpdateNotification fires once per run-loop pass and would flood the
// log; it is counted and suppressed.
//
// A[ssh]: built/run on mac-bsn over SSH; the console-session lock state is read
// from CGSessionCopyCurrentDictionary (CGSSessionScreenIsLocked) and logged so
// findings can be labelled honestly (miniaturize/fullscreen behave differently
// when no one is watching).
//
// Modes: WSI_AUTO_EXIT=1 = bounded scripted run (the sequence below); without it
// the window stays up for a manual (Tier C) session. Headless-safe: prints
// `SKIP:` and exits 0 when there is no window server.
module (module) appapp;
import (package) corecore.(module) core.attributeThis module contains UDA's (User Defined Attributes) either used in
the runtime or special UDA's recognized by compiler.
Attribute Name, Linkage, Description
gnuAbiTag, C++,
Declares an ABI tag on a C++ symbol.
mustuse,,
Ensures that values of a struct or union type are not discarded.
optional, Objective-C,
Makes an Objective-C interface method optional.
selector, Objective-C,
Attaches an Objective-C selector to a method.
standalone,,
Marks a shared module constructor as not depending on any
other module constructor being run first.
weak,,
Specifies that a global symbol should be emitted with weak linkage.
Source
core/attribute.d
attribute : selector;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.stdioD header file for C99 <stdio.h>
pubs.opengroup.org/onlinepubs/009695399/basedefs/stdio.h.html, stdio.h
Source
core/stdc/stdio.d
stdio : (alias) app.printf = int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.stdlibD header file for C99.
pubs.opengroup.org/onlinepubs/009695399/basedefs/stdlib.h.html, stdlib.h
Source
core/stdc/stdlib.d
stdlib : (alias) app.getenv = char* core.stdc.stdlib.getenv(scope const(char*) name) nothrow @nogcgetenv;
import instrument : instr, instrInit, instrNowUs;
import objc.autorelease : autoreleasepool_push, autoreleasepool_pop; // objective-d
import objc.rt : SEL;
// --- Geometry ----------------------------------------------------------------------
extern (C) struct (struct) app.NSPointNSPoint
{
double (field) double app.NSPoint.xx, (field) double app.NSPoint.yy;
}
extern (C) struct (struct) app.NSSizeNSSize
{
double (field) double app.NSSize.widthwidth, (field) double app.NSSize.heightheight;
}
extern (C) struct (struct) app.NSRectNSRect
{
(struct) app.NSPointNSPoint (field) app.NSPoint app.NSRect.originorigin;
(struct) app.NSSizeNSSize (field) app.NSSize app.NSRect.sizesize;
}
// --- CoreGraphics / CoreFoundation C API -------------------------------------------
extern (C) nothrow @nogc
{
uint uint app.CGMainDisplayID() nothrow @nogcCGMainDisplayID();
(struct) app.NSRectNSRect app.NSRect app.CGDisplayBounds(uint display) nothrow @nogcCGDisplayBounds(uint (parameter) uint displaydisplay);
// Session dictionary: carries CGSSessionScreenIsLocked when the console is locked.
void* void* app.CGSessionCopyCurrentDictionary() nothrow @nogcCGSessionCopyCurrentDictionary();
void* void* app.CFStringCreateWithCString(void* alloc, const(char)* s, uint encoding) nothrow @nogcCFStringCreateWithCString(void* (parameter) void* allocalloc, const(char)* (parameter) const(char)* ss, uint (parameter) uint encodingencoding);
void* void* app.CFDictionaryGetValue(void* dict, void* key) nothrow @nogcCFDictionaryGetValue(void* (parameter) void* dictdict, void* (parameter) void* keykey);
ubyte ubyte app.CFBooleanGetValue(void* b) nothrow @nogcCFBooleanGetValue(void* (parameter) void* bb);
void void app.CFRelease(void* obj) nothrow @nogcCFRelease(void* (parameter) void* objobj);
void void app.CGContextSetRGBFillColor(void* ctx, double r, double g, double b, double a) nothrow @nogcCGContextSetRGBFillColor(void* (parameter) void* ctxctx, double (parameter) double rr, double (parameter) double gg, double (parameter) double bb, double (parameter) double aa);
void void app.CGContextFillRect(void* ctx, app.NSRect rect) nothrow @nogcCGContextFillRect(void* (parameter) void* ctxctx, (struct) app.NSRectNSRect (parameter) app.NSRect rectrect);
}
enum uint (constant) uint app.kCFStringEncodingUTF8 = 134217984ukCFStringEncodingUTF8 = 0x08000100;
// Logs lock state via the CGSSessionScreenIsLocked key (absent when unlocked).
void void app.logSessionLockState()logSessionLockState()
{
void* (local variable) void* dictdict = void* app.CGSessionCopyCurrentDictionary() nothrow @nogcCGSessionCopyCurrentDictionary();
if ((local variable) void* dictdict is null)
{
instr("session", "lock_state=no_session_dictionary");
return;
}
void* (local variable) void* kLockedkLocked = void* app.CFStringCreateWithCString(void* alloc, const(char)* s, uint encoding) nothrow @nogcCFStringCreateWithCString(null, "CGSSessionScreenIsLocked",
(constant) uint app.kCFStringEncodingUTF8 = 134217984ukCFStringEncodingUTF8);
void* (local variable) void* kConsolekConsole = void* app.CFStringCreateWithCString(void* alloc, const(char)* s, uint encoding) nothrow @nogcCFStringCreateWithCString(null, "kCGSSessionOnConsoleKey",
(constant) uint app.kCFStringEncodingUTF8 = 134217984ukCFStringEncodingUTF8);
void* (local variable) void* lockedlocked = void* app.CFDictionaryGetValue(void* dict, void* key) nothrow @nogcCFDictionaryGetValue((local variable) void* dictdict, (local variable) void* kLockedkLocked);
void* (local variable) void* consoleconsole = void* app.CFDictionaryGetValue(void* dict, void* key) nothrow @nogcCFDictionaryGetValue((local variable) void* dictdict, (local variable) void* kConsolekConsole);
instr("session", "screen_locked=%d on_console=%d",
locked !is null && CFBooleanGetValue(locked) ? 1 : 0,
console !is null && CFBooleanGetValue(console) ? 1 : 0);
void app.CFRelease(void* obj) nothrow @nogcCFRelease((local variable) void* kLockedkLocked);
void app.CFRelease(void* obj) nothrow @nogcCFRelease((local variable) void* kConsolekConsole);
void app.CFRelease(void* obj) nothrow @nogcCFRelease((local variable) void* dictdict);
}
// --- AppKit class declarations -----------------------------------------------------
extern (Objective-C):
extern class (class) app.NSObjectNSObject
{
}
extern class (class) app.NSStringNSString : (class) app.NSObjectNSObject
{
static (class) app.NSStringNSString app.NSString app.NSString.alloc()alloc() @selector("alloc");
(class) app.NSStringNSString app.NSString app.NSString.initWithUTF8String(const(char)* s)initWithUTF8String(const(char)* (parameter) const(char)* ss) @selector("initWithUTF8String:");
const(char)* const(char)* app.NSString.UTF8String()UTF8String() @selector("UTF8String");
}
extern class (class) app.NSNotificationNSNotification : (class) app.NSObjectNSObject
{
(class) app.NSStringNSString app.NSString app.NSNotification.name()name() @selector("name");
(class) app.NSObjectNSObject app.NSObject app.NSNotification.object()object() @selector("object");
}
extern class (class) app.NSNotificationCenterNSNotificationCenter : (class) app.NSObjectNSObject
{
static (class) app.NSNotificationCenterNSNotificationCenter app.NSNotificationCenter app.NSNotificationCenter.defaultCenter()defaultCenter() @selector("defaultCenter");
void app.NSNotificationCenter.addObserveraddObserver((class) app.NSObjectNSObject observer, SEL sel, (class) app.NSStringNSString (parameter) NSString namename, (class) app.NSObjectNSObject (parameter) NSObject objobj)
@selector("addObserver:selector:name:object:");
}
extern class (class) app.NSEventNSEvent : (class) app.NSObjectNSObject
{
static (class) app.NSEventNSEvent app.NSEvent app.NSEvent.otherEventWithType(long type, app.NSPoint location, ulong modifierFlags, double timestamp, long windowNumber, app.NSObject context, short subtype, long data1, long data2)otherEventWithType(long (parameter) long typetype, (struct) app.NSPointNSPoint (parameter) app.NSPoint locationlocation, ulong (parameter) ulong modifierFlagsmodifierFlags,
double (parameter) double timestamptimestamp, long (parameter) long windowNumberwindowNumber, (class) app.NSObjectNSObject (parameter) app.NSObject contextcontext, short (parameter) short subtypesubtype,
long (parameter) long data1data1, long (parameter) long data2data2)
@selector("otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:");
}
extern class (class) app.NSTimerNSTimer : (class) app.NSObjectNSObject
{
static (class) app.NSTimerNSTimer app.NSTimer.scheduledTimerWithTimeIntervalscheduledTimerWithTimeInterval(double interval, (class) app.NSObjectNSObject target,
SEL sel, (class) app.NSObjectNSObject userInfo, bool repeats)
@selector("scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:");
}
extern class (class) app.NSApplicationNSApplication : (class) app.NSObjectNSObject
{
static (class) app.NSApplicationNSApplication app.NSApplication app.NSApplication.sharedApplication()sharedApplication() @selector("sharedApplication");
void void app.NSApplication.setActivationPolicy(long policy)setActivationPolicy(long (parameter) long policypolicy) @selector("setActivationPolicy:");
void void app.NSApplication.activateIgnoringOtherApps(bool flag)activateIgnoringOtherApps(bool (parameter) bool flagflag) @selector("activateIgnoringOtherApps:");
void void app.NSApplication.run()run() @selector("run");
void void app.NSApplication.stop(app.NSObject sender)stop((class) app.NSObjectNSObject (parameter) app.NSObject sendersender) @selector("stop:");
void void app.NSApplication.postEvent(app.NSEvent event, bool atStart)postEvent((class) app.NSEventNSEvent (parameter) app.NSEvent eventevent, bool (parameter) bool atStartatStart) @selector("postEvent:atStart:");
}
extern class (class) app.NSGraphicsContextNSGraphicsContext : (class) app.NSObjectNSObject
{
static (class) app.NSGraphicsContextNSGraphicsContext app.NSGraphicsContext app.NSGraphicsContext.currentContext()currentContext() @selector("currentContext");
void* void* app.NSGraphicsContext.CGContext()CGContext() @selector("CGContext");
}
extern class (class) app.NSViewNSView : (class) app.NSObjectNSObject
{
(struct) app.NSRectNSRect app.NSRect app.NSView.bounds()bounds() @selector("bounds");
}
extern class (class) app.NSWindowNSWindow : (class) app.NSObjectNSObject
{
static (class) app.NSWindowNSWindow app.NSWindow app.NSWindow.alloc()alloc() @selector("alloc");
(class) app.NSWindowNSWindow app.NSWindow app.NSWindow.initWithContentRect(app.NSRect contentRect, ulong styleMask, ulong backing, bool defer)initWithContentRect((struct) app.NSRectNSRect (parameter) app.NSRect contentRectcontentRect, ulong (parameter) ulong styleMaskstyleMask,
ulong (parameter) ulong backingbacking, bool (parameter) bool deferdefer) @selector("initWithContentRect:styleMask:backing:defer:");
void void app.NSWindow.setTitle(app.NSString title)setTitle((class) app.NSStringNSString (parameter) app.NSString titletitle) @selector("setTitle:");
void void app.NSWindow.setContentView(app.NSObject view)setContentView((class) app.NSObjectNSObject (parameter) app.NSObject viewview) @selector("setContentView:");
void void app.NSWindow.setDelegate(app.NSObject d)setDelegate((class) app.NSObjectNSObject (parameter) app.NSObject dd) @selector("setDelegate:");
void void app.NSWindow.setReleasedWhenClosed(bool b)setReleasedWhenClosed(bool (parameter) bool bb) @selector("setReleasedWhenClosed:");
void void app.NSWindow.makeKeyAndOrderFront(app.NSObject sender)makeKeyAndOrderFront((class) app.NSObjectNSObject (parameter) app.NSObject sendersender) @selector("makeKeyAndOrderFront:");
void void app.NSWindow.orderOut(app.NSObject sender)orderOut((class) app.NSObjectNSObject (parameter) app.NSObject sendersender) @selector("orderOut:");
void void app.NSWindow.orderFront(app.NSObject sender)orderFront((class) app.NSObjectNSObject (parameter) app.NSObject sendersender) @selector("orderFront:");
long long app.NSWindow.windowNumber()windowNumber() @selector("windowNumber");
(struct) app.NSRectNSRect app.NSRect app.NSWindow.frame()frame() @selector("frame");
// State requests + readbacks.
void void app.NSWindow.zoom(app.NSObject sender)zoom((class) app.NSObjectNSObject (parameter) app.NSObject sendersender) @selector("zoom:");
bool bool app.NSWindow.isZoomed()isZoomed() @selector("isZoomed");
void void app.NSWindow.miniaturize(app.NSObject sender)miniaturize((class) app.NSObjectNSObject (parameter) app.NSObject sendersender) @selector("miniaturize:");
void void app.NSWindow.deminiaturize(app.NSObject sender)deminiaturize((class) app.NSObjectNSObject (parameter) app.NSObject sendersender) @selector("deminiaturize:");
bool bool app.NSWindow.isMiniaturized()isMiniaturized() @selector("isMiniaturized");
void void app.NSWindow.toggleFullScreen(app.NSObject sender)toggleFullScreen((class) app.NSObjectNSObject (parameter) app.NSObject sendersender) @selector("toggleFullScreen:");
ulong ulong app.NSWindow.styleMask()styleMask() @selector("styleMask");
ulong ulong app.NSWindow.collectionBehavior()collectionBehavior() @selector("collectionBehavior");
void void app.NSWindow.setCollectionBehavior(ulong b)setCollectionBehavior(ulong (parameter) ulong bb) @selector("setCollectionBehavior:");
bool bool app.NSWindow.isVisible()isVisible() @selector("isVisible");
bool bool app.NSWindow.isKeyWindow()isKeyWindow() @selector("isKeyWindow");
ulong ulong app.NSWindow.occlusionState()occlusionState() @selector("occlusionState");
// The close pair under test.
void void app.NSWindow.performClose(app.NSObject sender)performClose((class) app.NSObjectNSObject (parameter) app.NSObject sendersender) @selector("performClose:");
void void app.NSWindow.close()close() @selector("close");
}
// The content view — a flat fill so state changes have something to composite.
class (class) app.StateViewStateView : (class) app.NSViewNSView
{
static (class) app.StateViewStateView app.StateView app.StateView.alloc()alloc() @selector("alloc");
(class) app.StateViewStateView app.StateView app.StateView.initWithFrame(app.NSRect frame)initWithFrame((struct) app.NSRectNSRect (parameter) app.NSRect frameframe) @selector("initWithFrame:");
void void app.StateView.drawRect(app.NSRect dirtyRect)drawRect((struct) app.NSRectNSRect (parameter) app.NSRect dirtyRectdirtyRect) @selector("drawRect:")
{
void* _error_ ctxctx = NSGraphicsContext.NSGraphicsContext.currentContextcurrentContext().NSGraphicsContext.currentContext().CGContextCGContext();
CGContextSetRGBFillColor(ctx, 0.15, 0.25, 0.35, 1);
CGContextFillRect(ctx, this.this.boundsbounds());
}
}
// The NSWindowDelegate + catch-all notification observer + script driver, in one
// pure-D NSObject subclass (selector-matched methods need no protocol declaration).
class (class) app.StateDelegateStateDelegate : (class) app.NSObjectNSObject
{
static (class) app.StateDelegateStateDelegate app.StateDelegate app.StateDelegate.alloc()alloc() @selector("alloc");
(class) app.StateDelegateStateDelegate app.StateDelegate app.StateDelegate.init()init() @selector("init");
// --- Close contract ------------------------------------------------------------
bool bool app.StateDelegate.windowShouldClose(app.NSWindow sender)windowShouldClose((class) app.NSWindowNSWindow (parameter) app.NSWindow sendersender) @selector("windowShouldClose:")
{
immutable _error_ isMainisMain = sender is g_win;
if (isMain && g_dirty)
{
g_dirty = false; // once-veto: the dirty flag is consumed
instr("close_requested", "veto=1 win=%s phase=%s mechanism=windowShouldClose_NO",
winLabel(sender), g_phase);
return false;
}
instr("close_requested", "veto=0 win=%s phase=%s", winLabel(sender), g_phase);
return true;
}
void void app.StateDelegate.windowWillClose(app.NSNotification n)windowWillClose((class) app.NSNotificationNSNotification (parameter) app.NSNotification nn) @selector("windowWillClose:")
{
instr("delegate", "m=windowWillClose win=%s phase=%s",
winLabel(cast((unresolved type) NSWindowNSWindow) n.n.objectobject()), g_phase);
if (cast((unresolved type) NSWindowNSWindow) n.n.objectobject() is g_win)
stopApp(); // second close request → willClose → terminate
}
// --- Zoom (maximize) choreography ------------------------------------------------
(struct) app.NSRectNSRect app.NSRect app.StateDelegate.windowWillUseStandardFrame(app.NSWindow w, app.NSRect frame)windowWillUseStandardFrame((class) app.NSWindowNSWindow (parameter) app.NSWindow ww, (struct) app.NSRectNSRect (parameter) app.NSRect frameframe)
@selector("windowWillUseStandardFrame:defaultFrame:")
{
instr("delegate", "m=windowWillUseStandardFrame default=(%.0f,%.0f %.0fx%.0f) phase=%s",
frame.frame.originorigin.frame.origin.xx, frame.frame.originorigin.frame.origin.yy, frame.frame.sizesize.frame.size.widthwidth, frame.frame.sizesize.frame.size.heightheight, g_phase);
return frame;
}
bool bool app.StateDelegate.windowShouldZoom(app.NSWindow w, app.NSRect frame)windowShouldZoom((class) app.NSWindowNSWindow (parameter) app.NSWindow ww, (struct) app.NSRectNSRect (parameter) app.NSRect frameframe)
@selector("windowShouldZoom:toFrame:")
{
instr("delegate", "m=windowShouldZoom toFrame=(%.0f,%.0f %.0fx%.0f) phase=%s answer=YES",
frame.frame.originorigin.frame.origin.xx, frame.frame.originorigin.frame.origin.yy, frame.frame.sizesize.frame.size.widthwidth, frame.frame.sizesize.frame.size.heightheight, g_phase);
return true;
}
(struct) app.NSSizeNSSize app.NSSize app.StateDelegate.windowWillResize(app.NSWindow w, app.NSSize size)windowWillResize((class) app.NSWindowNSWindow (parameter) app.NSWindow ww, (struct) app.NSSizeNSSize (parameter) app.NSSize sizesize)
@selector("windowWillResize:toSize:")
{
instr("delegate", "m=windowWillResize toSize=%.0fx%.0f phase=%s",
size.size.widthwidth, size.size.heightheight, g_phase);
return size;
}
void void app.StateDelegate.windowDidResize(app.NSNotification n)windowDidResize((class) app.NSNotificationNSNotification (parameter) app.NSNotification nn) @selector("windowDidResize:")
{
immutable _error_ ff = g_win.g_win.frameframe();
instr("delegate", "m=windowDidResize frame=(%.0f,%.0f %.0fx%.0f) phase=%s",
f.f.originorigin.f.origin.xx, f.f.originorigin.f.origin.yy, f.f.sizesize.f.size.widthwidth, f.f.sizesize.f.size.heightheight, g_phase);
}
void void app.StateDelegate.windowDidMove(app.NSNotification n)windowDidMove((class) app.NSNotificationNSNotification (parameter) app.NSNotification nn) @selector("windowDidMove:")
{
instr("delegate", "m=windowDidMove phase=%s", g_phase);
}
void void app.StateDelegate.windowWillStartLiveResize(app.NSNotification n)windowWillStartLiveResize((class) app.NSNotificationNSNotification (parameter) app.NSNotification nn) @selector("windowWillStartLiveResize:")
{
instr("delegate", "m=windowWillStartLiveResize phase=%s", g_phase);
}
void void app.StateDelegate.windowDidEndLiveResize(app.NSNotification n)windowDidEndLiveResize((class) app.NSNotificationNSNotification (parameter) app.NSNotification nn) @selector("windowDidEndLiveResize:")
{
instr("delegate", "m=windowDidEndLiveResize phase=%s", g_phase);
}
// --- Miniaturize ------------------------------------------------------------------
void void app.StateDelegate.windowWillMiniaturize(app.NSNotification n)windowWillMiniaturize((class) app.NSNotificationNSNotification (parameter) app.NSNotification nn) @selector("windowWillMiniaturize:")
{
instr("delegate", "m=windowWillMiniaturize phase=%s", g_phase);
}
void void app.StateDelegate.windowDidMiniaturize(app.NSNotification n)windowDidMiniaturize((class) app.NSNotificationNSNotification (parameter) app.NSNotification nn) @selector("windowDidMiniaturize:")
{
instr("delegate", "m=windowDidMiniaturize miniaturized=%d phase=%s",
g_win.g_win.isMiniaturizedisMiniaturized() ? 1 : 0, g_phase);
}
void void app.StateDelegate.windowDidDeminiaturize(app.NSNotification n)windowDidDeminiaturize((class) app.NSNotificationNSNotification (parameter) app.NSNotification nn) @selector("windowDidDeminiaturize:")
{
instr("delegate", "m=windowDidDeminiaturize phase=%s", g_phase);
}
// --- Focus / key status -------------------------------------------------------------
void void app.StateDelegate.windowDidBecomeKey(app.NSNotification n)windowDidBecomeKey((class) app.NSNotificationNSNotification (parameter) app.NSNotification nn) @selector("windowDidBecomeKey:")
{
instr("focus", "state=in win=%s phase=%s reason=didBecomeKey",
winLabel(cast((unresolved type) NSWindowNSWindow) n.n.objectobject()), g_phase);
}
void void app.StateDelegate.windowDidResignKey(app.NSNotification n)windowDidResignKey((class) app.NSNotificationNSNotification (parameter) app.NSNotification nn) @selector("windowDidResignKey:")
{
instr("focus", "state=out win=%s phase=%s reason=didResignKey",
winLabel(cast((unresolved type) NSWindowNSWindow) n.n.objectobject()), g_phase);
}
void void app.StateDelegate.windowDidBecomeMain(app.NSNotification n)windowDidBecomeMain((class) app.NSNotificationNSNotification (parameter) app.NSNotification nn) @selector("windowDidBecomeMain:")
{
instr("focus", "state=main_in win=%s phase=%s", winLabel(cast((unresolved type) NSWindowNSWindow) n.n.objectobject()), g_phase);
}
void void app.StateDelegate.windowDidResignMain(app.NSNotification n)windowDidResignMain((class) app.NSNotificationNSNotification (parameter) app.NSNotification nn) @selector("windowDidResignMain:")
{
instr("focus", "state=main_out win=%s phase=%s", winLabel(cast((unresolved type) NSWindowNSWindow) n.n.objectobject()), g_phase);
}
// --- Fullscreen (Space transition) ----------------------------------------------------
void void app.StateDelegate.windowWillEnterFullScreen(app.NSNotification n)windowWillEnterFullScreen((class) app.NSNotificationNSNotification (parameter) app.NSNotification nn) @selector("windowWillEnterFullScreen:")
{
g_fsT0 = instrNowUs();
instr("delegate", "m=windowWillEnterFullScreen phase=%s", g_phase);
}
void void app.StateDelegate.windowDidEnterFullScreen(app.NSNotification n)windowDidEnterFullScreen((class) app.NSNotificationNSNotification (parameter) app.NSNotification nn) @selector("windowDidEnterFullScreen:")
{
g_fsEntered = true;
instr("delegate", "m=windowDidEnterFullScreen dur_ms=%.1f styleMask=0x%llx phase=%s",
(instrNowUs() - g_fsT0) / 1000.0, g_win.g_win.styleMaskstyleMask(), g_phase);
}
void void app.StateDelegate.windowWillExitFullScreen(app.NSNotification n)windowWillExitFullScreen((class) app.NSNotificationNSNotification (parameter) app.NSNotification nn) @selector("windowWillExitFullScreen:")
{
g_fsT0 = instrNowUs();
instr("delegate", "m=windowWillExitFullScreen phase=%s", g_phase);
}
void void app.StateDelegate.windowDidExitFullScreen(app.NSNotification n)windowDidExitFullScreen((class) app.NSNotificationNSNotification (parameter) app.NSNotification nn) @selector("windowDidExitFullScreen:")
{
g_fsExited = true;
instr("delegate", "m=windowDidExitFullScreen dur_ms=%.1f styleMask=0x%llx phase=%s",
(instrNowUs() - g_fsT0) / 1000.0, g_win.g_win.styleMaskstyleMask(), g_phase);
}
void void app.StateDelegate.windowDidFailToEnterFullScreen(app.NSWindow w)windowDidFailToEnterFullScreen((class) app.NSWindowNSWindow (parameter) app.NSWindow ww)
@selector("windowDidFailToEnterFullScreen:")
{
g_fsFailed = true;
instr("delegate", "m=windowDidFailToEnterFullScreen phase=%s", g_phase);
}
// --- Catch-all notification observer + script timer -------------------------------------
void void app.StateDelegate.onNote(app.NSNotification n)onNote((class) app.NSNotificationNSNotification (parameter) app.NSNotification nn) @selector("onNote:")
{
const(char)* _error_ namename = n.n.namename().n.name().UTF8StringUTF8String();
// NSWindowDidUpdateNotification fires once per run-loop drawing pass — count, don't log.
if (streq(name, "NSWindowDidUpdateNotification"))
{
++g_updates;
return;
}
instr("note", "name=%s phase=%s", name, g_phase);
}
void void app.StateDelegate.tick(app.NSTimer t)tick((class) app.NSTimerNSTimer (parameter) app.NSTimer tt) @selector("tick:")
{
onStep();
}
}
extern (D): // back to D linkage
enum (constant) int app.NSApplicationActivationPolicyRegular = 0NSApplicationActivationPolicyRegular = 0;
enum : ulong
{
(enum value) app.NSWindowStyleMaskTitled = 1LUNSWindowStyleMaskTitled = 1,
(enum value) app.NSWindowStyleMaskClosable = 2LUNSWindowStyleMaskClosable = 2,
(enum value) app.NSWindowStyleMaskMiniaturizable = 4LUNSWindowStyleMaskMiniaturizable = 4,
(enum value) app.NSWindowStyleMaskResizable = 8LUNSWindowStyleMaskResizable = 8,
(enum value) app.NSWindowStyleMaskFullScreen = 16384LUNSWindowStyleMaskFullScreen = 1 << 14,
}
enum ulong (constant) ulong app.NSBackingStoreBuffered = 2LUNSBackingStoreBuffered = 2;
enum long (constant) long app.NSEventTypeApplicationDefined = 15LNSEventTypeApplicationDefined = 15;
enum ulong (constant) ulong app.NSWindowCollectionBehaviorFullScreenPrimary = 128LUNSWindowCollectionBehaviorFullScreenPrimary = 1 << 7;
// --- Demo state -----------------------------------------------------------------------
__gshared (class) app.NSApplicationNSApplication (__gshared global) app.NSApplication app.g_appg_app;
__gshared (class) app.NSWindowNSWindow (__gshared global) app.NSWindow app.g_wing_win; // the main window under test
__gshared (class) app.NSWindowNSWindow (__gshared global) app.NSWindow app.g_auxg_aux; // throwaway window for the close-vs-performClose proof
__gshared (class) app.StateDelegateStateDelegate (__gshared global) app.StateDelegate app.g_dlgg_dlg;
__gshared bool (__gshared global) bool app.g_autog_auto;
__gshared int (__gshared global) int app.g_stepg_step;
__gshared int (__gshared global) int app.g_waitTicksg_waitTicks; // budget for async (fullscreen) waits
__gshared const(char)* (__gshared global) const(char)* app.g_phaseg_phase = "init"; // request currently being serviced
__gshared bool (__gshared global) bool app.g_dirtyg_dirty; // the vetoable-close dirty flag
__gshared bool (__gshared global) bool app.g_fsEnteredg_fsEntered, (__gshared global) bool app.g_fsExitedg_fsExited, (__gshared global) bool app.g_fsFailedg_fsFailed;
__gshared ulong (__gshared global) ulong app.g_fsT0g_fsT0;
__gshared int (__gshared global) int app.g_updatesg_updates; // suppressed NSWindowDidUpdateNotification count
bool bool app.streq(const(char)* a, const(char)* b) nothrow @nogcstreq(const(char)* (parameter) const(char)* aa, const(char)* (parameter) const(char)* bb) nothrow @nogc
{
import core.stdc.string : strcmp;
return a !is null && b !is null && strcmp(a, b) == 0;
}
const(char)* const(char)* app.winLabel(app.NSWindow w)winLabel((class) app.NSWindowNSWindow (parameter) app.NSWindow ww)
{
if (w is g_win)
return "main";
if (w is g_aux)
return "aux";
return "other";
}
void void app.logState(const(char)* tag)logState(const(char)* (parameter) const(char)* tagtag)
{
immutable _error_ ff = g_win.g_win.frameframe();
instr("state_changed", "tag=%s frame=(%.0f,%.0f %.0fx%.0f) zoomed=%d mini=%d visible=%d key=%d occlusion=0x%llx fullscreen=%d",
tag, f.f.originorigin.f.origin.xx, f.f.originorigin.f.origin.yy, f.f.sizesize.f.size.widthwidth, f.f.sizesize.f.size.heightheight,
g_win.g_win.isZoomedisZoomed() ? 1 : 0, g_win.g_win.isMiniaturizedisMiniaturized() ? 1 : 0,
g_win.g_win.isVisibleisVisible() ? 1 : 0, g_win.g_win.isKeyWindowisKeyWindow() ? 1 : 0,
g_win.g_win.occlusionStateocclusionState(),
(g_win.g_win.styleMaskstyleMask() & NSWindowStyleMaskFullScreen) ? 1 : 0);
}
void void app.stopApp()stopApp()
{
instr("step", "name=NSApp_stop");
g_app.g_app.stopstop(null);
(unresolved type) NSEventNSEvent _error_ evev = NSEvent.NSEvent.otherEventWithTypeotherEventWithType(NSEventTypeApplicationDefined,
NSPoint(0, 0), 0, 0, 0, null, 0, 0, 0);
g_app.g_app.postEventpostEvent(ev, true); // stop: is only checked after an event dispatch
}
// --- The scripted run --------------------------------------------------------------------
void void app.onStep()onStep()
{
if (!g_auto)
return;
switch (g_step)
{
case 0: // Baseline.
g_phase = "baseline";
logState("baseline");
instr("collection_behavior", "before=0x%llx", g_win.g_win.collectionBehaviorcollectionBehavior());
g_win.g_win.setCollectionBehaviorsetCollectionBehavior(NSWindowCollectionBehaviorFullScreenPrimary);
instr("collection_behavior", "after=0x%llx note=FullScreenPrimary_prerequisite_for_toggleFullScreen",
g_win.g_win.collectionBehaviorcollectionBehavior());
break;
case 1: // Zoom on (programmatic maximize).
g_phase = "zoom_on";
instr("state_request", "kind=zoom api=zoom: zoomed_before=%d", g_win.g_win.isZoomedisZoomed() ? 1 : 0);
g_win.g_win.zoomzoom(null);
logState("after_zoom_on");
break;
case 2: // Zoom off (restore).
g_phase = "zoom_off";
instr("state_request", "kind=zoom_restore api=zoom: zoomed_before=%d",
g_win.g_win.isZoomedisZoomed() ? 1 : 0);
g_win.g_win.zoomzoom(null);
logState("after_zoom_off");
break;
case 3: // Miniaturize.
g_phase = "miniaturize";
instr("state_request", "kind=minimize api=miniaturize:");
g_win.g_win.miniaturizeminiaturize(null);
logState("after_miniaturize");
break;
case 4: // Deminiaturize.
g_phase = "deminiaturize";
instr("state_request", "kind=restore api=deminiaturize:");
g_win.g_win.deminiaturizedeminiaturize(null);
logState("after_deminiaturize");
break;
case 5: // orderOut (hide without state).
g_phase = "order_out";
instr("state_request", "kind=hide api=orderOut:");
g_win.g_win.orderOutorderOut(null);
logState("after_order_out");
break;
case 6: // orderFront + makeKey restore.
g_phase = "order_front";
instr("state_request", "kind=show api=makeKeyAndOrderFront:");
g_win.g_win.makeKeyAndOrderFrontmakeKeyAndOrderFront(null);
logState("after_order_front");
break;
case 7: // Fullscreen enter — async Space transition; wait for didEnter.
g_phase = "fullscreen_enter";
instr("state_request", "kind=fullscreen api=toggleFullScreen:");
g_fsEntered = g_fsFailed = false;
g_waitTicks = 40; // 8 s budget at 200 ms ticks
g_win.g_win.toggleFullScreentoggleFullScreen(null);
break;
case 8: // Wait for the enter transition to settle (or time out).
if (!g_fsEntered && !g_fsFailed && --g_waitTicks > 0)
return; // stay on this step
instr("state_settled", "kind=fullscreen entered=%d failed=%d timed_out=%d",
g_fsEntered ? 1 : 0, g_fsFailed ? 1 : 0, g_waitTicks <= 0 ? 1 : 0);
logState("after_fullscreen_enter");
break;
case 9: // Fullscreen exit.
g_phase = "fullscreen_exit";
if (g_win.g_win.styleMaskstyleMask() & NSWindowStyleMaskFullScreen)
{
instr("state_request", "kind=fullscreen_exit api=toggleFullScreen:");
g_fsExited = false;
g_waitTicks = 40;
g_win.g_win.toggleFullScreentoggleFullScreen(null);
}
else
{
instr("state_request", "kind=fullscreen_exit skipped=not_fullscreen");
g_step += 1; // skip the wait step too
}
break;
case 10:
if (!g_fsExited && --g_waitTicks > 0)
return;
instr("state_settled", "kind=fullscreen_exit exited=%d timed_out=%d",
g_fsExited ? 1 : 0, g_waitTicks <= 0 ? 1 : 0);
logState("after_fullscreen_exit");
break;
case 11: // Proof: -close skips windowShouldClose:; -performClose: asks first.
g_phase = "aux_close_direct";
instr("close_probe", "api=close win=aux expect=no_windowShouldClose");
g_aux.g_aux.closeclose(); // delegate's windowShouldClose must NOT fire; willClose must
break;
case 12: // Vetoable close, attempt 1: dirty → veto.
g_phase = "veto_close_1";
g_dirty = true;
instr("close_probe", "api=performClose win=main dirty=1 expect=veto");
g_win.g_win.performCloseperformClose(null);
instr("close_probe", "after=performClose still_visible=%d dirty=%d",
g_win.g_win.isVisibleisVisible() ? 1 : 0, g_dirty ? 1 : 0);
break;
case 13: // Attempt 2: flag consumed → close proceeds → willClose stops the app.
g_phase = "veto_close_2";
instr("close_probe", "api=performClose win=main dirty=0 expect=close");
g_win.g_win.performCloseperformClose(null);
break;
default:
// Safety net if windowWillClose never fired.
instr("close_probe", "result=window_did_not_close visible=%d", g_win.g_win.isVisibleisVisible() ? 1 : 0);
stopApp();
break;
}
++g_step;
}
int int D main()main()
{
g_auto = getenv("WSI_AUTO_EXIT") !is null;
instrInit("APPKIT_F14");
instr("init_start", "auto_exit=%d", g_auto ? 1 : 0);
if (CGMainDisplayID() == 0)
{
printf("SKIP: no macOS window server (CGMainDisplayID == 0)\n");
return 0;
}
logSessionLockState();
auto _error_ poolpool = autoreleasepool_push();
scope (exit)
autoreleasepool_pop(pool);
g_app = NSApplication.NSApplication.sharedApplicationsharedApplication();
g_app.g_app.setActivationPolicysetActivationPolicy(NSApplicationActivationPolicyRegular);
immutable _error_ maskmask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable
| NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable;
g_win = NSWindow.NSWindow.allocalloc().NSWindow.alloc().initWithContentRectinitWithContentRect(
NSRect(NSPoint(120, 120), NSSize(480, 320)), mask, NSBackingStoreBuffered, false);
g_win.g_win.setTitlesetTitle(NSString.NSString.allocalloc().NSString.alloc().initWithUTF8StringinitWithUTF8String("wsi-f14-window-state"));
g_win.g_win.setReleasedWhenClosedsetReleasedWhenClosed(false); // we read isVisible after close
(unresolved type) StateViewStateView _error_ viewview = StateView.StateView.allocalloc().StateView.alloc().initWithFrameinitWithFrame(NSRect(NSPoint(0, 0), NSSize(480, 320)));
g_win.g_win.setContentViewsetContentView(view);
g_aux = NSWindow.NSWindow.allocalloc().NSWindow.alloc().initWithContentRectinitWithContentRect(
NSRect(NSPoint(700, 120), NSSize(200, 120)), mask, NSBackingStoreBuffered, false);
g_aux.g_aux.setTitlesetTitle(NSString.NSString.allocalloc().NSString.alloc().initWithUTF8StringinitWithUTF8String("wsi-f14-aux"));
g_aux.g_aux.setReleasedWhenClosedsetReleasedWhenClosed(false);
g_dlg = StateDelegate.StateDelegate.allocalloc().StateDelegate.alloc().initinit();
g_win.g_win.setDelegatesetDelegate(g_dlg);
g_aux.g_aux.setDelegatesetDelegate(g_dlg);
// Catch-all observer: every notification whose object is the main window.
NSNotificationCenter.NSNotificationCenter.defaultCenterdefaultCenter().NSNotificationCenter.defaultCenter().addObserveraddObserver(g_dlg, SEL.register("onNote:"),
null, g_win);
g_aux.g_aux.orderFrontorderFront(null);
g_win.g_win.makeKeyAndOrderFrontmakeKeyAndOrderFront(null);
g_app.g_app.activateIgnoringOtherAppsactivateIgnoringOtherApps(true);
instr("window_created", "main_win=%ld aux_win=%ld styleMask=0x%llx",
g_win.g_win.windowNumberwindowNumber(), g_aux.g_aux.windowNumberwindowNumber(), g_win.g_win.styleMaskstyleMask());
instr("step", "name=script_timer_start interval_ms=200");
NSTimer.NSTimer.scheduledTimerWithTimeIntervalscheduledTimerWithTimeInterval(0.2, g_dlg, SEL.register("tick:"), null, true);
instr("step", "name=NSApp_run");
g_app.g_app.runrun();
instr("loop_exit", "steps=%d suppressed_didUpdate_notes=%d", g_step, g_updates);
return 0;
}