// macOS/AppKit F15 demo — popup with "grab" (../../../features/f15-popup.md).
// macOS has no popup grab: a context menu built from raw windows is a borderless
// NSWindow (NSWindowStyleMaskBorderless) raised to NSPopUpMenuWindowLevel, and
// "dismiss on outside click" must be assembled from events the popup never
// receives. This demo implements and compares BOTH dismissal mechanisms:
// (a) +[NSEvent addLocalMonitorForEventsMatchingMask:handler:] (in-app events,
// block callback) and the global variant (other apps' events — needs
// nothing for mouse, Accessibility for keys; what each actually sees under
// an SSH-launched locked session is logged);
// (b) the key-window route: canBecomeKeyWindow override (borderless windows
// refuse key status by default — also required for Esc/keyDown!) +
// windowDidResignKey as the dismissal signal.
// Plus: 3 items with tracking-area hover highlight, Esc via keyDown →
// interpretKeyEvents: → cancelOperation:, app-computed clamping near the
// bottom-right screen edge (and whether constrainFrameRect:toScreen: auto-clamps
// a borderless window — the override logs every call), and a one-level submenu
// as a child window (addChildWindow:ordered: — stacking + move-with-parent).
//
// All clicks/keys are synthetic (A[ssh], locked console): CGEvent-wrapped
// right-click per the f06/f07 recipes, plain NSEvent left-clicks/keys, dispatched
// via [NSApp postEvent:] (so local monitors see them) or [window sendEvent:]
// (fallback: direct method call — every route taken is logged).
//
// Modes: WSI_AUTO_EXIT=1 = bounded scripted run; without it the window stays up
// for a manual (Tier C) session. Headless-safe: prints `SKIP:` and exits 0.
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;
import objc.autorelease : autoreleasepool_push, autoreleasepool_pop; // objective-d
import objc.block : Block, (error) _error_block;
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);
int int app.CGWindowLevelForKey(int key) nothrow @nogcCGWindowLevelForKey(int (parameter) int keykey);
void* void* app.CGEventCreateMouseEvent(void* source, uint type, app.NSPoint pos, uint button) nothrow @nogcCGEventCreateMouseEvent(void* (parameter) void* sourcesource, uint (parameter) uint typetype, (struct) app.NSPointNSPoint (parameter) app.NSPoint pospos, uint (parameter) uint buttonbutton);
void void app.CFRelease(void* obj) nothrow @nogcCFRelease(void* (parameter) void* objobj);
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.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;
enum uint (constant) uint app.kCGEventRightMouseDown = 3ukCGEventRightMouseDown = 3;
enum uint (constant) uint app.kCGMouseButtonRight = 1ukCGMouseButtonRight = 1;
// CGWindowLevelKey values (CGWindowLevel.h).
enum : int
{
(enum value) app.kCGNormalWindowLevelKey = 4kCGNormalWindowLevelKey = 4,
(enum value) app.kCGFloatingWindowLevelKey = 5kCGFloatingWindowLevelKey = 5,
(enum value) app.kCGPopUpMenuWindowLevelKey = 11kCGPopUpMenuWindowLevelKey = 11,
(enum value) app.kCGScreenSaverWindowLevelKey = 13kCGScreenSaverWindowLevelKey = 13,
(enum value) app.kCGMaximumWindowLevelKey = 14kCGMaximumWindowLevelKey = 14,
}
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* lockedlocked = void* app.CFDictionaryGetValue(void* dict, void* key) nothrow @nogcCFDictionaryGetValue((local variable) void* dictdict, (local variable) void* kLockedkLocked);
instr("session", "screen_locked=%d",
locked !is null && CFBooleanGetValue(locked) ? 1 : 0);
void app.CFRelease(void* obj) nothrow @nogcCFRelease((local variable) void* kLockedkLocked);
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:");
}
extern class (class) app.NSArrayNSArray : (class) app.NSObjectNSObject
{
static (class) app.NSArrayNSArray app.NSArray app.NSArray.arrayWithObject(app.NSObject o)arrayWithObject((class) app.NSObjectNSObject (parameter) app.NSObject oo) @selector("arrayWithObject:");
}
extern class (class) app.NSNotificationNSNotification : (class) app.NSObjectNSObject
{
(class) app.NSObjectNSObject app.NSObject app.NSNotification.object()object() @selector("object");
}
extern class (class) app.NSEventNSEvent : (class) app.NSObjectNSObject
{
static (class) app.NSEventNSEvent app.NSEvent app.NSEvent.mouseEventWithType(long type, app.NSPoint location, ulong modifierFlags, double timestamp, long windowNumber, app.NSObject context, long eventNumber, long clickCount, double pressure)mouseEventWithType(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, long (parameter) long eventNumbereventNumber,
long (parameter) long clickCountclickCount, double (parameter) double pressurepressure)
@selector("mouseEventWithType:location:modifierFlags:timestamp:windowNumber:context:eventNumber:clickCount:pressure:");
static (class) app.NSEventNSEvent app.NSEvent app.NSEvent.keyEventWithType(long type, app.NSPoint location, ulong modifierFlags, double timestamp, long windowNumber, app.NSObject context, app.NSString characters, app.NSString charactersIgnoringModifiers, bool isARepeat, ushort keyCode)keyEventWithType(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, (class) app.NSStringNSString (parameter) app.NSString characterscharacters,
(class) app.NSStringNSString (parameter) app.NSString charactersIgnoringModifierscharactersIgnoringModifiers, bool (parameter) bool isARepeatisARepeat, ushort (parameter) ushort keyCodekeyCode)
@selector("keyEventWithType:location:modifierFlags:timestamp:windowNumber:context:characters:charactersIgnoringModifiers:isARepeat:keyCode:");
static (class) app.NSEventNSEvent app.NSEvent app.NSEvent.eventWithCGEvent(void* cgEvent)eventWithCGEvent(void* (parameter) void* cgEventcgEvent) @selector("eventWithCGEvent:");
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:");
static (class) app.NSObjectNSObject app.NSEvent.addLocalMonitorForEventsMatchingMaskaddLocalMonitorForEventsMatchingMask(ulong mask,
Block!(void*, void*)* handler)
@selector("addLocalMonitorForEventsMatchingMask:handler:");
static (class) app.NSObjectNSObject app.NSEvent.addGlobalMonitorForEventsMatchingMaskaddGlobalMonitorForEventsMatchingMask(ulong mask,
Block!(void, void*)* handler)
@selector("addGlobalMonitorForEventsMatchingMask:handler:");
static void void app.NSEvent.removeMonitor(app.NSObject monitor)removeMonitor((class) app.NSObjectNSObject (parameter) app.NSObject monitormonitor) @selector("removeMonitor:");
long long app.NSEvent.type()type() @selector("type");
(struct) app.NSPointNSPoint app.NSPoint app.NSEvent.locationInWindow()locationInWindow() @selector("locationInWindow");
long long app.NSEvent.windowNumber()windowNumber() @selector("windowNumber");
ushort ushort app.NSEvent.keyCode()keyCode() @selector("keyCode");
}
extern class (class) app.NSTimerNSTimer : (class) app.NSObjectNSObject
{
static (class) app.NSTimerNSTimer app.NSTimer.scheduledTimerWithTimeIntervalscheduledTimerWithTimeInterval(double interval, (class) app.NSObjectNSObject (parameter) NSObject targettarget,
SEL sel, (class) app.NSObjectNSObject (parameter) NSObject userInfouserInfo, 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:");
void void app.NSApplication.sendEvent(app.NSEvent event)sendEvent((class) app.NSEventNSEvent (parameter) app.NSEvent eventevent) @selector("sendEvent:");
}
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.NSTrackingAreaNSTrackingArea : (class) app.NSObjectNSObject
{
static (class) app.NSTrackingAreaNSTrackingArea app.NSTrackingArea app.NSTrackingArea.alloc()alloc() @selector("alloc");
(class) app.NSTrackingAreaNSTrackingArea app.NSTrackingArea app.NSTrackingArea.initWithRect(app.NSRect rect, ulong options, app.NSObject owner, app.NSObject userInfo)initWithRect((struct) app.NSRectNSRect (parameter) app.NSRect rectrect, ulong (parameter) ulong optionsoptions, (class) app.NSObjectNSObject (parameter) app.NSObject ownerowner,
(class) app.NSObjectNSObject (parameter) app.NSObject userInfouserInfo) @selector("initWithRect:options:owner:userInfo:");
}
extern class (class) app.NSViewNSView : (class) app.NSObjectNSObject
{
(struct) app.NSRectNSRect app.NSRect app.NSView.bounds()bounds() @selector("bounds");
void void app.NSView.setNeedsDisplay(bool flag)setNeedsDisplay(bool (parameter) bool flagflag) @selector("setNeedsDisplay:");
void void app.NSView.addTrackingArea(app.NSTrackingArea area)addTrackingArea((class) app.NSTrackingAreaNSTrackingArea (parameter) app.NSTrackingArea areaarea) @selector("addTrackingArea:");
void void app.NSView.interpretKeyEvents(app.NSArray events)interpretKeyEvents((class) app.NSArrayNSArray (parameter) app.NSArray eventsevents) @selector("interpretKeyEvents:");
bool bool app.NSView.acceptsFirstResponder()acceptsFirstResponder() @selector("acceptsFirstResponder");
}
extern class (class) app.NSWindowNSWindow : (class) app.NSObjectNSObject
{
// (alloc is declared on the leaf classes — a base declaration clashes with
// the covariant leaf override, per the scaffold recipe.)
(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:");
bool bool app.NSWindow.makeFirstResponder(app.NSObject responder)makeFirstResponder((class) app.NSObjectNSObject (parameter) app.NSObject responderresponder) @selector("makeFirstResponder:");
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:");
void void app.NSWindow.sendEvent(app.NSEvent event)sendEvent((class) app.NSEventNSEvent (parameter) app.NSEvent eventevent) @selector("sendEvent:");
long long app.NSWindow.windowNumber()windowNumber() @selector("windowNumber");
(struct) app.NSRectNSRect app.NSRect app.NSWindow.frame()frame() @selector("frame");
void void app.NSWindow.setFrameOrigin(app.NSPoint p)setFrameOrigin((struct) app.NSPointNSPoint (parameter) app.NSPoint pp) @selector("setFrameOrigin:");
(struct) app.NSRectNSRect app.NSRect app.NSWindow.convertRectToScreen(app.NSRect r)convertRectToScreen((struct) app.NSRectNSRect (parameter) app.NSRect rr) @selector("convertRectToScreen:");
long long app.NSWindow.level()level() @selector("level");
void void app.NSWindow.setLevel(long level)setLevel(long (parameter) long levellevel) @selector("setLevel:");
bool bool app.NSWindow.isKeyWindow()isKeyWindow() @selector("isKeyWindow");
bool bool app.NSWindow.isVisible()isVisible() @selector("isVisible");
void void app.NSWindow.addChildWindow(app.NSWindow child, long ordered)addChildWindow((class) app.NSWindowNSWindow (parameter) app.NSWindow childchild, long (parameter) long orderedordered) @selector("addChildWindow:ordered:");
void void app.NSWindow.removeChildWindow(app.NSWindow child)removeChildWindow((class) app.NSWindowNSWindow (parameter) app.NSWindow childchild) @selector("removeChildWindow:");
(class) app.NSWindowNSWindow app.NSWindow app.NSWindow.parentWindow()parentWindow() @selector("parentWindow");
bool bool app.NSWindow.canBecomeKeyWindow()canBecomeKeyWindow() @selector("canBecomeKeyWindow");
(struct) app.NSRectNSRect app.NSRect app.NSWindow.constrainFrameRect(app.NSRect frameRect, app.NSObject screen)constrainFrameRect((struct) app.NSRectNSRect (parameter) app.NSRect frameRectframeRect, (class) app.NSObjectNSObject (parameter) app.NSObject screenscreen)
@selector("constrainFrameRect:toScreen:");
}
// A plain leaf so the main window can be alloc'd (see the note in NSWindow).
class (class) app.PlainWindowPlainWindow : (class) app.NSWindowNSWindow
{
static (class) app.PlainWindowPlainWindow app.PlainWindow app.PlainWindow.alloc()alloc() @selector("alloc");
}
// The borderless popup window. canBecomeKeyWindow=YES is mechanism (b)'s enabler
// AND the prerequisite for receiving keyDown (Esc); the constrainFrameRect
// override logs whether AppKit ever asks to clamp a borderless window.
class (class) app.PopupWindowPopupWindow : (class) app.NSWindowNSWindow
{
static (class) app.PopupWindowPopupWindow app.PopupWindow app.PopupWindow.alloc()alloc() @selector("alloc");
override bool bool app.PopupWindow.canBecomeKeyWindow()canBecomeKeyWindow() @selector("canBecomeKeyWindow")
{
return true;
}
override (struct) app.NSRectNSRect app.NSRect app.PopupWindow.constrainFrameRect(app.NSRect frameRect, app.NSObject screen)constrainFrameRect((struct) app.NSRectNSRect (parameter) app.NSRect frameRectframeRect, (class) app.NSObjectNSObject (parameter) app.NSObject screenscreen)
@selector("constrainFrameRect:toScreen:")
{
immutable _error_ rr = super.super.constrainFrameRectconstrainFrameRect(frameRect, screen);
instr("constrain_frame_rect", "in=(%.0f,%.0f %.0fx%.0f) out=(%.0f,%.0f %.0fx%.0f) screen_nil=%d",
frameRect.frameRect.originorigin.frameRect.origin.xx, frameRect.frameRect.originorigin.frameRect.origin.yy, frameRect.frameRect.sizesize.frameRect.size.widthwidth, frameRect.frameRect.sizesize.frameRect.size.heightheight,
r.r.originorigin.r.origin.xx, r.r.originorigin.r.origin.yy, r.r.sizesize.r.size.widthwidth, r.r.sizesize.r.size.heightheight, screen is null ? 1 : 0);
return r;
}
}
// The menu surface: 3 items, hover highlight via tracking areas, item click,
// Esc via keyDown → interpretKeyEvents: → cancelOperation:.
class (class) app.MenuViewMenuView : (class) app.NSViewNSView
{
static (class) app.MenuViewMenuView app.MenuView app.MenuView.alloc()alloc() @selector("alloc");
(class) app.MenuViewMenuView app.MenuView app.MenuView.initWithFrame(app.NSRect frame)initWithFrame((struct) app.NSRectNSRect (parameter) app.NSRect frameframe) @selector("initWithFrame:");
override bool bool app.MenuView.acceptsFirstResponder()acceptsFirstResponder() @selector("acceptsFirstResponder")
{
return true;
}
void void app.MenuView.drawRect(app.NSRect dirtyRect)drawRect((struct) app.NSRectNSRect (parameter) app.NSRect dirtyRectdirtyRect) @selector("drawRect:")
{
void* _error_ ctxctx = NSGraphicsContext.NSGraphicsContext.currentContextcurrentContext().NSGraphicsContext.currentContext().CGContextCGContext();
immutable _error_ bb = this.this.boundsbounds();
CGContextSetRGBFillColor(ctx, 0.22, 0.22, 0.24, 1);
CGContextFillRect(ctx, b);
foreach ((parameter) ii; 0 .. kItems)
{
if (i == g_hover)
CGContextSetRGBFillColor(ctx, 0.30, 0.42, 0.65, 1); // hover highlight
else
CGContextSetRGBFillColor(ctx, 0.27, 0.27, 0.30, 1);
immutable _error_ rr = itemRect(b, i);
CGContextFillRect(ctx, NSRect(NSPoint(r.r.originorigin.r.origin.xx + 2, r.r.originorigin.r.origin.yy + 1),
NSSize(r.r.sizesize.r.size.widthwidth - 4, r.r.sizesize.r.size.heightheight - 2)));
}
}
void void app.MenuView.mouseEntered(app.NSEvent e)mouseEntered((class) app.NSEventNSEvent (parameter) app.NSEvent ee) @selector("mouseEntered:")
{
immutable _error_ ii = itemAt(e.e.locationInWindowlocationInWindow());
instr("tracking", "event=mouseEntered item=%d", i);
setHover(i);
}
void void app.MenuView.mouseExited(app.NSEvent e)mouseExited((class) app.NSEventNSEvent (parameter) app.NSEvent ee) @selector("mouseExited:")
{
instr("tracking", "event=mouseExited item=%d", g_hover);
setHover(-1);
}
void void app.MenuView.mouseMoved(app.NSEvent e)mouseMoved((class) app.NSEventNSEvent (parameter) app.NSEvent ee) @selector("mouseMoved:")
{
++g_movesSeen;
}
void void app.MenuView.mouseDown(app.NSEvent e)mouseDown((class) app.NSEventNSEvent (parameter) app.NSEvent ee) @selector("mouseDown:")
{
++g_itemClicks;
immutable _error_ pp = e.e.locationInWindowlocationInWindow();
immutable _error_ ii = itemAt(p);
instr("popup_item", "event=mouseDown item=%d loc=(%.0f,%.0f)", i, p.p.xx, p.p.yy);
if (i == 1)
openSubmenu(); // item 1 = "Submenu >" — child-window test
else if (i >= 0)
dismissPopup("item_click");
}
void void app.MenuView.keyDown(app.NSEvent e)keyDown((class) app.NSEventNSEvent (parameter) app.NSEvent ee) @selector("keyDown:")
{
instr("key", "event=keyDown keycode=%u routing=interpretKeyEvents", e.e.keyCodekeyCode());
g_escViaCancel = false;
this.this.interpretKeyEventsinterpretKeyEvents(NSArray.NSArray.arrayWithObjectarrayWithObject(e));
if (!g_escViaCancel && e.e.keyCodekeyCode() == 53)
{
// Fallback path if the text system did not map Esc to cancelOperation:.
instr("key", "esc_route=keyCode_fallback");
dismissPopup("esc_keydown");
}
}
// Esc lands here when routed through the text system (doCommandBySelector:).
void void app.MenuView.cancelOperation(app.NSObject sender)cancelOperation((class) app.NSObjectNSObject (parameter) app.NSObject sendersender) @selector("cancelOperation:")
{
g_escViaCancel = true;
instr("key", "esc_route=cancelOperation");
dismissPopup("esc_cancelOperation");
}
}
// Mechanism (b): the popup's delegate — losing key status means "clicked elsewhere".
class (class) app.PopupDelegatePopupDelegate : (class) app.NSObjectNSObject
{
static (class) app.PopupDelegatePopupDelegate app.PopupDelegate app.PopupDelegate.alloc()alloc() @selector("alloc");
(class) app.PopupDelegatePopupDelegate app.PopupDelegate app.PopupDelegate.init()init() @selector("init");
void void app.PopupDelegate.windowDidResignKey(app.NSNotification n)windowDidResignKey((class) app.NSNotificationNSNotification (parameter) app.NSNotification nn) @selector("windowDidResignKey:")
{
instr("popup_focus", "event=windowDidResignKey armed=%d", g_resignArmed ? 1 : 0);
if (g_resignArmed && g_popup !is null)
dismissPopup("resign_key");
}
}
// The main window's content view: right-click target.
class (class) app.MainViewMainView : (class) app.NSViewNSView
{
static (class) app.MainViewMainView app.MainView app.MainView.alloc()alloc() @selector("alloc");
(class) app.MainViewMainView app.MainView app.MainView.initWithFrame(app.NSRect frame)initWithFrame((struct) app.NSRectNSRect (parameter) app.NSRect frameframe) @selector("initWithFrame:");
void void app.MainView.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.13, 0.16, 0.13, 1);
CGContextFillRect(ctx, this.this.boundsbounds());
}
void void app.MainView.rightMouseDown(app.NSEvent e)rightMouseDown((class) app.NSEventNSEvent (parameter) app.NSEvent ee) @selector("rightMouseDown:")
{
immutable _error_ pp = e.e.locationInWindowlocationInWindow();
// Anchor = the click point in screen coords; popup opens below-right.
immutable _error_ spsp = g_win.g_win.convertRectToScreenconvertRectToScreen(NSRect(p, NSSize(1, 1))).g_win.convertRectToScreen(NSRect(p, NSSize(1, 1))).originorigin;
instr("right_click", "loc_window=(%.0f,%.0f) anchor_screen=(%.0f,%.0f)",
p.p.xx, p.p.yy, sp.sp.xx, sp.sp.yy);
openPopup(sp, "right_click");
}
void void app.MainView.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.NSWindowStyleMaskBorderless = 0LUNSWindowStyleMaskBorderless = 0,
(enum value) app.NSWindowStyleMaskTitled = 1LUNSWindowStyleMaskTitled = 1,
(enum value) app.NSWindowStyleMaskClosable = 2LUNSWindowStyleMaskClosable = 2,
(enum value) app.NSWindowStyleMaskResizable = 8LUNSWindowStyleMaskResizable = 8,
}
enum ulong (constant) ulong app.NSBackingStoreBuffered = 2LUNSBackingStoreBuffered = 2;
enum : long
{
(enum value) app.NSEventTypeLeftMouseDown = 1LNSEventTypeLeftMouseDown = 1,
(enum value) app.NSEventTypeRightMouseDown = 3LNSEventTypeRightMouseDown = 3,
(enum value) app.NSEventTypeMouseMoved = 5LNSEventTypeMouseMoved = 5,
(enum value) app.NSEventTypeKeyDown = 10LNSEventTypeKeyDown = 10,
(enum value) app.NSEventTypeApplicationDefined = 15LNSEventTypeApplicationDefined = 15,
}
enum ulong (constant) ulong app.NSEventMaskLeftMouseDown = 2LUNSEventMaskLeftMouseDown = 1UL << (enum value) app.NSEventTypeLeftMouseDown = 1LNSEventTypeLeftMouseDown;
enum ulong (constant) ulong app.NSEventMaskRightMouseDown = 8LUNSEventMaskRightMouseDown = 1UL << (enum value) app.NSEventTypeRightMouseDown = 3LNSEventTypeRightMouseDown;
enum long (constant) long app.NSWindowAbove = 1LNSWindowAbove = 1;
// NSPopUpMenuWindowLevel == kCGPopUpMenuWindowLevel (read at runtime, expected 101).
enum ulong (constant) ulong app.kTrackingOptions = 129LUkTrackingOptions = 0x01 /*MouseEnteredAndExited*/ | 0x80 /*ActiveAlways*/ ;
enum int (constant) int app.kItems = 3kItems = 3;
enum double (constant) double app.kItemH = 28.0kItemH = 28, (constant) double app.kMenuW = 160.0kMenuW = 160;
enum double (constant) double app.kMenuH = 84.0kMenuH = (constant) int app.kItems = 3kItems * (constant) double app.kItemH = 28.0kItemH;
// --- 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; // main window
__gshared (class) app.PopupWindowPopupWindow (__gshared global) app.PopupWindow app.g_popupg_popup; // the open popup (null when closed)
__gshared (class) app.PopupWindowPopupWindow (__gshared global) app.PopupWindow app.g_submenug_submenu; // child window (null when closed)
__gshared (class) app.MenuViewMenuView (__gshared global) app.MenuView app.g_menuViewg_menuView;
__gshared (class) app.PopupDelegatePopupDelegate (__gshared global) app.PopupDelegate app.g_popupDlgg_popupDlg;
__gshared (class) app.MainViewMainView (__gshared global) app.MainView app.g_mainViewg_mainView;
__gshared long (__gshared global) long app.g_winNumg_winNum;
__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_hoverg_hover = -1;
__gshared int (__gshared global) int app.g_movesSeeng_movesSeen;
__gshared bool (__gshared global) bool app.g_escViaCancelg_escViaCancel;
__gshared bool (__gshared global) bool app.g_resignArmedg_resignArmed; // mechanism (b) active for the current popup
__gshared (class) app.NSObjectNSObject (__gshared global) app.NSObject app.g_localMong_localMon, (__gshared global) app.NSObject app.g_globalMong_globalMon;
__gshared Block!(void*, void*) _error_ app.g_localBlockg_localBlock;
__gshared Block!(void, void*) _error_ app.g_globalBlockg_globalBlock;
__gshared int (__gshared global) int app.g_localSeeng_localSeen, (__gshared global) int app.g_globalSeeng_globalSeen;
__gshared int (__gshared global) int app.g_opensg_opens;
__gshared int (__gshared global) int app.g_itemClicksg_itemClicks;
(struct) app.NSRectNSRect app.NSRect app.itemRect(app.NSRect b, int i)itemRect((struct) app.NSRectNSRect (parameter) app.NSRect bb, int (parameter) int ii)
{
return NSRect(NSPoint(0, b.b.sizesize.b.size.heightheight - (i + 1) * kItemH), NSSize(b.b.sizesize.b.size.widthwidth, kItemH));
}
int int app.itemAt(app.NSPoint p)itemAt((struct) app.NSPointNSPoint (parameter) app.NSPoint pp)
{
if (p.p.xx < 0 || p.p.xx >= kMenuW || p.p.yy < 0 || p.p.yy >= kMenuH)
return -1;
immutable _error_ ii = cast(int) ((kMenuH - p.p.yy) / kItemH);
return i >= 0 && i < kItems ? i : -1;
}
void void app.setHover(int i)setHover(int (parameter) int ii)
{
if (g_hover == i)
return;
g_hover = i;
instr("hover", "item=%d", i);
if (g_menuView !is null)
g_menuView.g_menuView.setNeedsDisplaysetNeedsDisplay(true);
}
double double app.screenHeight()screenHeight()
{
return CGDisplayBounds(CGMainDisplayID()).CGDisplayBounds(CGMainDisplayID()).sizesize.CGDisplayBounds(CGMainDisplayID()).size.heightheight;
}
// --- Event monitors (mechanism a) ------------------------------------------------------
// Called from the local-monitor block (via an attribute-erasing cast — the body
// is ObjC message sends, which never GC-allocate or throw here).
void void app.onLocalEvent(void* ev)onLocalEvent(void* (parameter) void* evev)
{
++g_localSeen;
(unresolved type) NSEventNSEvent _error_ ee = cast((unresolved type) NSEventNSEvent) ev;
immutable _error_ wnwn = e.e.windowNumberwindowNumber();
immutable _error_ popupWnpopupWn = g_popup !is null ? g_popup.g_popup.windowNumberwindowNumber() : -1;
immutable _error_ subWnsubWn = g_submenu !is null ? g_submenu.g_submenu.windowNumberwindowNumber() : -1;
immutable _error_ outsideoutside = wn != popupWn && wn != subWn;
instr("monitor", "scope=local type=%ld win=%ld outside_popup=%d", e.e.typetype(), wn,
outside ? 1 : 0);
if (e.e.typetype() == NSEventTypeLeftMouseDown && outside && g_popup !is null)
dismissPopup("outside_click_local_monitor");
}
void void app.onGlobalEvent(void* ev)onGlobalEvent(void* (parameter) void* evev)
{
++g_globalSeen;
instr("monitor", "scope=global type=%ld", (cast((unresolved type) NSEventNSEvent) ev).(cast(NSEvent)ev).typetype());
}
void void app.installMonitors()installMonitors()
{
g_localBlock = block((void* (parameter) void* evev) {
(cast(void function(void*) nothrow @nogc) &onLocalEvent)(ev);
return ev; // pass the event through to normal dispatch
});
g_globalBlock = block((void* (parameter) void* evev) {
(cast(void function(void*) nothrow @nogc) &onGlobalEvent)(ev);
});
g_localMon = NSEvent.NSEvent.addLocalMonitorForEventsMatchingMaskaddLocalMonitorForEventsMatchingMask(
NSEventMaskLeftMouseDown | NSEventMaskRightMouseDown, &g_localBlock);
g_globalMon = NSEvent.NSEvent.addGlobalMonitorForEventsMatchingMaskaddGlobalMonitorForEventsMatchingMask(
NSEventMaskLeftMouseDown | NSEventMaskRightMouseDown, &g_globalBlock);
instr("monitor", "installed local=%d global=%d note=global_sees_only_other_apps_no_a11y_needed_for_mouse",
g_localMon !is null ? 1 : 0, g_globalMon !is null ? 1 : 0);
}
void void app.removeMonitors()removeMonitors()
{
if (g_localMon !is null)
NSEvent.NSEvent.removeMonitorremoveMonitor(g_localMon);
if (g_globalMon !is null)
NSEvent.NSEvent.removeMonitorremoveMonitor(g_globalMon);
g_localMon = g_globalMon = null;
}
// --- Popup open / dismiss ----------------------------------------------------------------
// Opens the popup below-right of `anchor` (AppKit screen coords, y-up), with
// app-computed clamping to the screen rect — placement ownership on macOS is
// 100% the app's; nothing repositions a borderless window for you.
(struct) app.NSRectNSRect app.NSRect app.clampToScreen(app.NSRect r, app.NSPoint anchor)clampToScreen((struct) app.NSRectNSRect (parameter) app.NSRect rr, (struct) app.NSPointNSPoint (parameter) app.NSPoint anchoranchor)
{
immutable _error_ shsh = screenHeight();
immutable _error_ swsw = CGDisplayBounds(CGMainDisplayID()).CGDisplayBounds(CGMainDisplayID()).sizesize.CGDisplayBounds(CGMainDisplayID()).size.widthwidth;
auto _error_ placedplaced = r;
if (placed.placed.originorigin.placed.origin.xx + r.r.sizesize.r.size.widthwidth > sw)
placed.placed.originorigin.placed.origin.xx = sw - r.r.sizesize.r.size.widthwidth; // slide left off the right edge
if (placed.placed.originorigin.placed.origin.yy < 0)
placed.placed.originorigin.placed.origin.yy = anchor.anchor.yy; // flip above the anchor off the bottom edge
if (placed.placed.originorigin.placed.origin.yy + r.r.sizesize.r.size.heightheight > sh)
placed.placed.originorigin.placed.origin.yy = sh - r.r.sizesize.r.size.heightheight;
return placed;
}
// `doClamp=false` places the popup at the raw (possibly off-screen) frame so the
// edge test can measure whether anything else (AppKit, WindowServer) clamps it.
void void app.openPopup(app.NSPoint anchor, const(char)* why, bool doClamp = true)openPopup((struct) app.NSPointNSPoint (parameter) app.NSPoint anchoranchor, const(char)* (parameter) const(char)* whywhy, bool (parameter) bool doClampdoClamp = true)
{
if (g_popup !is null)
dismissPopup("reopen");
++g_opens;
immutable _error_ dbdb = CGDisplayBounds(CGMainDisplayID());
// Desired: top-left corner at the anchor → frame origin (bottom-left, y-up).
auto _error_ wantwant = NSRect(NSPoint(anchor.anchor.xx, anchor.anchor.yy - kMenuH), NSSize(kMenuW, kMenuH));
auto _error_ placedplaced = doClamp ? clampToScreen(want, anchor) : want;
instr("popup_open", "cause=%s anchor=(%.0f,%.0f) gravity=bottom_right want=(%.0f,%.0f %.0fx%.0f) placed=(%.0f,%.0f %.0fx%.0f) app_clamp=%d screen=%.0fx%.0f",
why, anchor.anchor.xx, anchor.anchor.yy, want.want.originorigin.want.origin.xx, want.want.originorigin.want.origin.yy, want.want.sizesize.want.size.widthwidth,
want.want.sizesize.want.size.heightheight, placed.placed.originorigin.placed.origin.xx, placed.placed.originorigin.placed.origin.yy, placed.placed.sizesize.placed.size.widthwidth,
placed.placed.sizesize.placed.size.heightheight, doClamp ? 1 : 0, db.db.sizesize.db.size.widthwidth, db.db.sizesize.db.size.heightheight);
g_popup = cast((unresolved type) PopupWindowPopupWindow) PopupWindow.PopupWindow.allocalloc().PopupWindow.alloc().initWithContentRectinitWithContentRect(placed,
NSWindowStyleMaskBorderless, NSBackingStoreBuffered, false);
g_popup.g_popup.setReleasedWhenClosedsetReleasedWhenClosed(false);
g_popup.g_popup.setLevelsetLevel(CGWindowLevelForKey(kCGPopUpMenuWindowLevelKey));
g_menuView = MenuView.MenuView.allocalloc().MenuView.alloc().initWithFrameinitWithFrame(NSRect(NSPoint(0, 0), placed.placed.sizesize));
g_popup.g_popup.setContentViewsetContentView(g_menuView);
g_popup.g_popup.setDelegatesetDelegate(g_popupDlg);
foreach ((parameter) ii; 0 .. kItems)
g_menuView.g_menuView.addTrackingAreaaddTrackingArea(NSTrackingArea.NSTrackingArea.allocalloc().NSTrackingArea.alloc().initWithRectinitWithRect(
itemRect(NSRect(NSPoint(0, 0), placed.placed.sizesize), i), kTrackingOptions,
g_menuView, null));
g_hover = -1;
g_popup.g_popup.makeKeyAndOrderFrontmakeKeyAndOrderFront(null); // key status: mechanism (b) + Esc delivery
g_popup.g_popup.makeFirstRespondermakeFirstResponder(g_menuView);
g_resignArmed = false; // armed explicitly by the resign-key script step
installMonitors();
immutable _error_ ff = g_popup.g_popup.frameframe();
instr("popup_placed", "rect=(%.0f,%.0f %.0fx%.0f) level=%ld is_key=%d can_become_key=%d",
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_popup.g_popup.levellevel(),
g_popup.g_popup.isKeyWindowisKeyWindow() ? 1 : 0, g_popup.g_popup.canBecomeKeyWindowcanBecomeKeyWindow() ? 1 : 0);
}
void void app.openSubmenu()openSubmenu()
{
immutable _error_ pfpf = g_popup.g_popup.frameframe();
// One level deep: to the right of item 1, top-aligned with it.
auto _error_ rectrect = NSRect(NSPoint(pf.pf.originorigin.pf.origin.xx + kMenuW - 4,
pf.pf.originorigin.pf.origin.yy + kMenuH - 2 * kItemH), NSSize(kMenuW, 2 * kItemH));
g_submenu = cast((unresolved type) PopupWindowPopupWindow) PopupWindow.PopupWindow.allocalloc().PopupWindow.alloc().initWithContentRectinitWithContentRect(rect,
NSWindowStyleMaskBorderless, NSBackingStoreBuffered, false);
g_submenu.g_submenu.setReleasedWhenClosedsetReleasedWhenClosed(false);
auto _error_ vv = MenuView.MenuView.allocalloc().MenuView.alloc().initWithFrameinitWithFrame(NSRect(NSPoint(0, 0), rect.rect.sizesize));
g_submenu.g_submenu.setContentViewsetContentView(v);
g_popup.g_popup.addChildWindowaddChildWindow(g_submenu, NSWindowAbove);
g_submenu.g_submenu.orderFrontorderFront(null);
immutable _error_ ff = g_submenu.g_submenu.frameframe();
instr("submenu_open", "rect=(%.0f,%.0f %.0fx%.0f) parent_win=%ld child_win=%ld level_parent=%ld level_child=%ld ordered=NSWindowAbove",
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_popup.g_popup.windowNumberwindowNumber(), g_submenu.g_submenu.windowNumberwindowNumber(), g_popup.g_popup.levellevel(),
g_submenu.g_submenu.levellevel());
}
void void app.dismissPopup(const(char)* cause)dismissPopup(const(char)* (parameter) const(char)* causecause)
{
if (g_popup is null)
return;
instr("popup_dismiss", "cause=%s", cause);
g_resignArmed = false;
if (g_submenu !is null)
{
g_popup.g_popup.removeChildWindowremoveChildWindow(g_submenu);
g_submenu.g_submenu.orderOutorderOut(null);
g_submenu = null;
}
removeMonitors();
g_popup.g_popup.setDelegatesetDelegate(null);
g_popup.g_popup.orderOutorderOut(null);
g_popup = null;
g_menuView = null;
}
// --- Synthetic input ------------------------------------------------------------------------
// Right-click, CGEvent-backed (the f06/f07 "route 2 object, route 1 dispatch"
// hybrid): build a CGEvent, wrap via eventWithCGEvent:, dispatch in-process.
void void app.injectRightClick(app.NSPoint viewPt)injectRightClick((struct) app.NSPointNSPoint (parameter) app.NSPoint viewPtviewPt)
{
immutable _error_ spsp = g_win.g_win.convertRectToScreenconvertRectToScreen(NSRect(viewPt, NSSize(1, 1))).g_win.convertRectToScreen(NSRect(viewPt, NSSize(1, 1))).originorigin;
immutable _error_ cgPtcgPt = NSPoint(sp.sp.xx, screenHeight() - sp.sp.yy); // CG is y-down
void* _error_ cgcg = CGEventCreateMouseEvent(null, kCGEventRightMouseDown, cgPt,
kCGMouseButtonRight);
(unresolved type) NSEventNSEvent _error_ ee = NSEvent.NSEvent.eventWithCGEventeventWithCGEvent(cg);
instr("inject", "kind=right_click route=cgevent_wrap view=(%.0f,%.0f) cg=(%.0f,%.0f) wrapped_win=%ld",
viewPt.viewPt.xx, viewPt.viewPt.yy, cgPt.cgPt.xx, cgPt.cgPt.yy, e !is null ? e.e.windowNumberwindowNumber() : -1);
// A wrapped CGEvent carries no window; sendEvent on the target window routes
// by responder chain, falling back to the direct method call if undelivered.
if (e !is null)
g_win.g_win.sendEventsendEvent(e);
if (g_popup is null)
{
instr("inject", "kind=right_click result=sendEvent_not_delivered fallback=direct_call");
(unresolved type) NSEventNSEvent _error_ plainplain = NSEvent.NSEvent.mouseEventWithTypemouseEventWithType(NSEventTypeRightMouseDown, viewPt,
0, 0, g_winNum, null, 0, 1, 0);
g_mainView.g_mainView.rightMouseDownrightMouseDown(plain);
}
CFRelease(cg);
}
// Plain left-click at a window-local point, posted through the app queue so the
// local monitor (which hooks the app's dispatch) gets to see it.
void void app.postLeftClick(app.NSWindow target, app.NSPoint pt, const(char)* label)postLeftClick((class) app.NSWindowNSWindow (parameter) app.NSWindow targettarget, (struct) app.NSPointNSPoint (parameter) app.NSPoint ptpt, const(char)* (parameter) const(char)* labellabel)
{
(unresolved type) NSEventNSEvent _error_ ee = NSEvent.NSEvent.mouseEventWithTypemouseEventWithType(NSEventTypeLeftMouseDown, pt, 0, 0,
target.target.windowNumberwindowNumber(), null, 0, 1, 0);
instr("inject", "kind=left_click label=%s win=%ld loc=(%.0f,%.0f) route=postEvent",
label, target.target.windowNumberwindowNumber(), pt.pt.xx, pt.pt.yy);
g_app.g_app.postEventpostEvent(e, false);
}
void void app.injectEsc()injectEsc()
{
(unresolved type) NSStringNSString _error_ escesc = NSString.NSString.allocalloc().NSString.alloc().initWithUTF8StringinitWithUTF8String("\x1b");
(unresolved type) NSEventNSEvent _error_ ee = NSEvent.NSEvent.keyEventWithTypekeyEventWithType(NSEventTypeKeyDown, NSPoint(0, 0), 0, 0,
g_popup.g_popup.windowNumberwindowNumber(), null, esc, esc, false, 53);
instr("inject", "kind=esc keycode=53 win=%ld is_key=%d route=sendEvent",
g_popup.g_popup.windowNumberwindowNumber(), g_popup.g_popup.isKeyWindowisKeyWindow() ? 1 : 0);
g_popup.g_popup.sendEventsendEvent(e);
}
// --- The scripted run --------------------------------------------------------------------------
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);
}
void void app.onStep()onStep()
{
if (!g_auto)
return;
switch (g_step)
{
case 0: // The window-level ladder this demo slots into.
instr("levels", "normal=%d floating=%d popup_menu=%d screensaver=%d maximum=%d",
CGWindowLevelForKey(kCGNormalWindowLevelKey),
CGWindowLevelForKey(kCGFloatingWindowLevelKey),
CGWindowLevelForKey(kCGPopUpMenuWindowLevelKey),
CGWindowLevelForKey(kCGScreenSaverWindowLevelKey),
CGWindowLevelForKey(kCGMaximumWindowLevelKey));
break;
case 1: // Open via synthetic right-click.
injectRightClick(NSPoint(240, 160));
break;
case 2: // Hover: do synthetic moves reach tracking areas? (cursor-driven — no)
{
if (g_popup is null)
break;
(unresolved type) NSEventNSEvent _error_ mvmv = NSEvent.NSEvent.mouseEventWithTypemouseEventWithType(NSEventTypeMouseMoved,
NSPoint(kMenuW / 2, kMenuH - kItemH / 2), 0, 0,
g_popup.g_popup.windowNumberwindowNumber(), null, 0, 0, 0);
immutable _error_ beforebefore = g_movesSeen;
g_popup.g_popup.sendEventsendEvent(mv);
instr("tracking", "probe=synthetic_mouseMoved delivered_to_view=%d entered_fired=%d note=tracking_areas_are_cursor_driven",
g_movesSeen - before, g_hover >= 0 ? 1 : 0);
if (g_hover < 0)
setHover(0); // drive the highlight directly for the visual path
}
break;
case 3: // Click item 1 → submenu (child window).
if (g_popup !is null)
{
(unresolved type) NSEventNSEvent _error_ ee = NSEvent.NSEvent.mouseEventWithTypemouseEventWithType(NSEventTypeLeftMouseDown,
NSPoint(kMenuW / 2, kMenuH - kItemH - kItemH / 2), 0, 0,
g_popup.g_popup.windowNumberwindowNumber(), null, 0, 1, 0);
immutable _error_ beforebefore = g_itemClicks;
g_popup.g_popup.sendEventsendEvent(e);
if (g_itemClicks == before)
{
// Same f10 finding: [window sendEvent:] drops synthetic mouse
// events under the locked session — call the handler directly.
instr("inject", "kind=item_click result=sendEvent_not_delivered fallback=direct_call");
g_menuView.g_menuView.mouseDownmouseDown(e);
}
}
break;
case 4: // Move-with-parent proof.
if (g_popup !is null && g_submenu !is null)
{
immutable _error_ beforebefore = g_submenu.g_submenu.frameframe();
immutable _error_ pfpf = g_popup.g_popup.frameframe();
g_popup.g_popup.setFrameOriginsetFrameOrigin(NSPoint(pf.pf.originorigin.pf.origin.xx + 24, pf.pf.originorigin.pf.origin.yy + 24));
immutable _error_ afterafter = g_submenu.g_submenu.frameframe();
instr("child_move", "parent_moved=(+24,+24) child_before=(%.0f,%.0f) child_after=(%.0f,%.0f) parent_of_child=%ld",
before.before.originorigin.before.origin.xx, before.before.originorigin.before.origin.yy, after.after.originorigin.after.origin.xx, after.after.originorigin.after.origin.yy,
g_submenu.g_submenu.parentWindowparentWindow() !is null
? g_submenu.g_submenu.parentWindowparentWindow().g_submenu.parentWindow().windowNumberwindowNumber() : -1);
}
break;
case 5: // Outside click → local monitor dismissal (mechanism a).
postLeftClick(g_win, NSPoint(50, 50), "outside_click");
break;
case 6: // Reopen near the bottom-right screen edge, UNclamped: who fixes it?
{
instr("monitor", "after_outside_click popup_open=%d local_seen=%d global_seen=%d",
g_popup !is null ? 1 : 0, g_localSeen, g_globalSeen);
immutable _error_ dbdb = CGDisplayBounds(CGMainDisplayID());
openPopup(NSPoint(db.db.sizesize.db.size.widthwidth - 30, 40), "edge_test_bottom_right", false);
}
break;
case 7: // Nobody clamped it — apply the app-side math and read back.
if (g_popup !is null)
{
immutable _error_ beforebefore = g_popup.g_popup.frameframe();
immutable _error_ dbdb = CGDisplayBounds(CGMainDisplayID());
immutable _error_ anchoranchor = NSPoint(db.db.sizesize.db.size.widthwidth - 30, 40);
immutable _error_ targettarget = clampToScreen(NSRect(NSPoint(anchor.anchor.xx, anchor.anchor.yy - kMenuH),
NSSize(kMenuW, kMenuH)), anchor);
g_popup.g_popup.setFrameOriginsetFrameOrigin(target.target.originorigin);
immutable _error_ afterafter = g_popup.g_popup.frameframe();
instr("edge_clamp", "offscreen_frame=(%.0f,%.0f) app_clamped_to=(%.0f,%.0f) readback=(%.0f,%.0f) owner=app",
before.before.originorigin.before.origin.xx, before.before.originorigin.before.origin.yy, target.target.originorigin.target.origin.xx, target.target.originorigin.target.origin.yy,
after.after.originorigin.after.origin.xx, after.after.originorigin.after.origin.yy);
}
break;
case 8: // Esc → keyDown → cancelOperation: (needs key status on borderless!).
if (g_popup !is null)
injectEsc();
break;
case 9: // Reopen, then arm mechanism (b) and steal key status.
openPopup(NSPoint(400, 400), "resign_key_test");
g_resignArmed = true;
break;
case 10:
if (g_popup !is null)
{
instr("popup_focus", "action=makeKeyAndOrderFront_main popup_is_key=%d",
g_popup.g_popup.isKeyWindowisKeyWindow() ? 1 : 0);
if (!g_popup.g_popup.isKeyWindowisKeyWindow())
instr("popup_focus", "mechanism_b=untestable_headless note=no_window_gets_key_status_in_locked_session tier_c=resign_key_dismissal");
g_win.g_win.makeKeyAndOrderFrontmakeKeyAndOrderFront(null); // → popup windowDidResignKey → dismiss
}
break;
case 11:
instr("summary", "opens=%d popup_open=%d item_clicks=%d local_seen=%d global_seen=%d",
g_opens, g_popup !is null ? 1 : 0, g_itemClicks, g_localSeen, g_globalSeen);
dismissPopup("shutdown");
stopApp();
break;
default:
stopApp();
break;
}
++g_step;
}
int int D main()main()
{
g_auto = getenv("WSI_AUTO_EXIT") !is null;
instrInit("APPKIT_F15");
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_ contentRectcontentRect = NSRect(NSPoint(120, 120), NSSize(480, 320));
g_win = PlainWindow.PlainWindow.allocalloc().PlainWindow.alloc().initWithContentRectinitWithContentRect(contentRect,
NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable,
NSBackingStoreBuffered, false);
g_win.g_win.setTitlesetTitle(NSString.NSString.allocalloc().NSString.alloc().initWithUTF8StringinitWithUTF8String("wsi-f15-popup"));
g_win.g_win.setReleasedWhenClosedsetReleasedWhenClosed(false);
g_mainView = MainView.MainView.allocalloc().MainView.alloc().initWithFrameinitWithFrame(NSRect(NSPoint(0, 0), contentRect.contentRect.sizesize));
g_win.g_win.setContentViewsetContentView(g_mainView);
g_win.g_win.makeKeyAndOrderFrontmakeKeyAndOrderFront(null);
g_app.g_app.activateIgnoringOtherAppsactivateIgnoringOtherApps(true);
g_winNum = g_win.g_win.windowNumberwindowNumber();
g_popupDlg = PopupDelegate.PopupDelegate.allocalloc().PopupDelegate.alloc().initinit();
instr("window_created", "win_num=%ld", g_winNum);
instr("step", "name=script_timer_start interval_ms=250");
NSTimer.NSTimer.scheduledTimerWithTimeIntervalscheduledTimerWithTimeInterval(0.25, g_mainView, SEL.register("tick:"), null, true);
instr("step", "name=NSApp_run");
g_app.g_app.runrun();
instr("loop_exit", "steps=%d opens=%d local_seen=%d global_seen=%d moves_seen=%d",
g_step, g_opens, g_localSeen, g_globalSeen, g_movesSeen);
return 0;
}