// macOS/AppKit F12 demo — cursors (../../../features/f12-cursors.md).
// The server composites the cursor on macOS; the app's job is only to SAY which
// cursor — via NSCursor standard cursors set from cursorUpdate: (driven by
// NSTrackingArea with the NSTrackingCursorUpdate option), the legacy
// addCursorRect:cursor: mechanism, or a custom NSCursor initWithImage:hotSpot:.
// Built on the scaffold/f08 recipe; run findings are A[ssh] (locked console — no
// pointer motion possible, so cursorUpdate: is driven directly per zone and the
// on-screen pixels are Tier C).
//
// What it logs:
// - the standard-cursor VOCABULARY: every public NSCursor class getter probed via
// class_getClassMethod (available=0/1), including the macOS 15 additions
// (columnResize/rowResize/zoomIn/zoomOut/frameResizeCursorFromPosition:
// inDirections:) and the never-public diagonal-resize names — the vocabulary gap
// vs Win32/X11 is the deliverable;
// - a 3x3 hover-zone grid of NSTrackingAreas (options MouseEnteredAndExited |
// CursorUpdate | ActiveInKeyWindow — CursorUpdate is documented as NOT supported
// with ActiveAlways), each install logged, plus updateTrackingAreas and the
// legacy resetCursorRects/addCursorRect:cursor: path;
// - cursor_set per zone driven through cursorUpdate: directly (locked session =
// no real pointer), with each cursor's image size, rep count and rep pixel
// widths (the built-in 1x+2x HiDPI pairs);
// - the eight resize directions: the soft-deprecated pre-15 six
// (left/right/leftRight/up/down/upDown — no public diagonals before macOS 15)
// then frameResizeCursorFromPosition:inDirections: for all four edges + four
// corners via objc_msgSend;
// - one custom cursor: a CPU-drawn 24x24 ARGB bullseye NSBitmapImageRep plus a
// 48x48 2x rep in the same NSImage (AppKit picks the rep by screen scale at
// display time), hotspot (12,12), set + read back;
// - push/pop vs set: the cursor-stack probe (what currentCursor reports after
// each push/pop/set, including set-under-a-stack);
// - NSCursor.hide/unhide pairing (the F10 visibility connection);
// - a synthetic CGEventPost mouse-move probe: does an in-process posted move
// reach the tracking areas in a locked session? (counters say at exit).
//
// Modes: WSI_AUTO_EXIT=1 = bounded scripted run; without it the window stays up for
// a manual (Tier C) hover 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, (alias) app.snprintf = int core.stdc.stdio.snprintf(scope char* s, ulong n, scope const(char*) format, scope const ...) nothrow @nogcsnprintf;
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 (package) corecore.(package) core.stdcstdc.(module) core.stdc.stringD header file for C99.
pubs.opengroup.org/onlinepubs/009695399/basedefs/string.h.html, string.h
Source
core/stdc/string.d
string : (alias) app.memcpy = void* core.stdc.string.memcpy(return scope void* s1, scope const(void*) s2, ulong n) pure nothrow @nogcmemcpy;
import instrument : instr, instrInit;
import objc.autorelease : autoreleasepool_push, autoreleasepool_pop; // objective-d
import objc.rt : SEL, Class, Method;
// Dynamic dispatch for the macOS-15 availability-gated NSCursor getters: resolve
// objc_msgSend via dlsym (objc.rt's own declaration is private) and cast it to the
// exact signature — the Apple-documented calling pattern for objc_msgSend.
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(module) core.sys.posix.dlfcnD header file for POSIX.
dlfcn : (alias) app.dlsym = void* core.sys.posix.dlfcn.dlsym(void*, scope const(char*)) nothrow @nogcdlsym;
enum (constant) void* app.RTLD_DEFAULT = cast(void*)cast(size_t)18446744073709551614LURTLD_DEFAULT = cast(void*) -2; // dlfcn.h, macOS
alias (alias) app.MsgSend0 = extern (C) void* function(void*, void*) nothrow @nogcMsgSend0 = extern (C) void* function(void*, void*) nothrow @nogc;
alias (alias) app.MsgSend2 = extern (C) void* function(void*, void*, ulong, ulong) nothrow @nogcMsgSend2 = extern (C) void* function(void*, void*, ulong, ulong) nothrow @nogc;
void* void* app.msgSendAddr()msgSendAddr()
{
__gshared void* (__gshared global) void* app.msgSendAddr.addraddr;
if ((__gshared global) void* app.msgSendAddr.addraddr is null)
(__gshared global) void* app.msgSendAddr.addraddr = void* core.sys.posix.dlfcn.dlsym(void*, scope const(char*)) nothrow @nogcdlsym((constant) void* app.RTLD_DEFAULT = cast(void*)cast(size_t)18446744073709551614LURTLD_DEFAULT, "objc_msgSend");
return (__gshared global) void* app.msgSendAddr.addraddr;
}
// --- C-level declarations ---------------------------------------------------------
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;
}
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);
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);
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.CGEventPost(uint tap, void* event) nothrow @nogcCGEventPost(uint (parameter) uint taptap, void* (parameter) void* eventevent);
void void app.CFRelease(void* obj) nothrow @nogcCFRelease(void* (parameter) void* objobj);
}
enum uint (constant) uint app.kCGEventMouseMoved = 5ukCGEventMouseMoved = 5;
enum uint (constant) uint app.kCGSessionEventTap = 1ukCGSessionEventTap = 1;
// NSTrackingAreaOptions (NSTrackingArea.h). Note the header on ActiveAlways:
// "Not supported for NSTrackingCursorUpdate."
enum : ulong
{
(enum value) app.NSTrackingMouseEnteredAndExited = 1LUNSTrackingMouseEnteredAndExited = 0x01,
(enum value) app.NSTrackingCursorUpdate = 4LUNSTrackingCursorUpdate = 0x04,
(enum value) app.NSTrackingActiveInKeyWindow = 32LUNSTrackingActiveInKeyWindow = 0x20,
}
// NSCursorFrameResizePosition (NSCursor.h, macOS 15): edges are bits, corners ORs.
enum : ulong
{
(enum value) app.frpTop = 1LUfrpTop = 1 << 0,
(enum value) app.frpLeft = 2LUfrpLeft = 1 << 1,
(enum value) app.frpBottom = 4LUfrpBottom = 1 << 2,
(enum value) app.frpRight = 8LUfrpRight = 1 << 3,
}
enum ulong (constant) ulong app.frameResizeDirectionsAll = 3LUframeResizeDirectionsAll = 0b11; // Inward | Outward
// --- 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
{
ulong ulong app.NSArray.count()count() @selector("count");
(class) app.NSObjectNSObject app.NSObject app.NSArray.objectAtIndex(ulong index)objectAtIndex(ulong (parameter) ulong indexindex) @selector("objectAtIndex:");
}
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:");
(struct) app.NSPointNSPoint app.NSPoint app.NSEvent.locationInWindow()locationInWindow() @selector("locationInWindow");
}
extern class (class) app.NSTimerNSTimer : (class) app.NSObjectNSObject
{
static (class) app.NSTimerNSTimer app.NSTimer.scheduledTimerWithTimeIntervalscheduledTimerWithTimeInterval(double interval, (class) app.NSObjectNSObject target,
SEL (parameter) SEL selsel, (class) app.NSObjectNSObject (parameter) NSObject userInfouserInfo, bool repeats)
@selector("scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:");
}
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.NSImageRepNSImageRep : (class) app.NSObjectNSObject
{
long long app.NSImageRep.pixelsWide()pixelsWide() @selector("pixelsWide");
long long app.NSImageRep.pixelsHigh()pixelsHigh() @selector("pixelsHigh");
}
extern class (class) app.NSBitmapImageRepNSBitmapImageRep : (class) app.NSImageRepNSImageRep
{
static (class) app.NSBitmapImageRepNSBitmapImageRep app.NSBitmapImageRep app.NSBitmapImageRep.alloc()alloc() @selector("alloc");
(class) app.NSBitmapImageRepNSBitmapImageRep app.NSBitmapImageRep app.NSBitmapImageRep.initWithBitmapDataPlanes(ubyte** planes, long pixelsWide, long pixelsHigh, long bitsPerSample, long samplesPerPixel, bool hasAlpha, bool isPlanar, app.NSString colorSpaceName, long bytesPerRow, long bitsPerPixel)initWithBitmapDataPlanes(ubyte** (parameter) ubyte** planesplanes, long (parameter) long pixelsWidepixelsWide,
long (parameter) long pixelsHighpixelsHigh, long (parameter) long bitsPerSamplebitsPerSample, long (parameter) long samplesPerPixelsamplesPerPixel, bool (parameter) bool hasAlphahasAlpha,
bool (parameter) bool isPlanarisPlanar, (class) app.NSStringNSString (parameter) app.NSString colorSpaceNamecolorSpaceName, long (parameter) long bytesPerRowbytesPerRow, long (parameter) long bitsPerPixelbitsPerPixel)
@selector("initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bytesPerRow:bitsPerPixel:");
ubyte* ubyte* app.NSBitmapImageRep.bitmapData()bitmapData() @selector("bitmapData");
void void app.NSBitmapImageRep.setSize(app.NSSize size)setSize((struct) app.NSSizeNSSize (parameter) app.NSSize sizesize) @selector("setSize:");
}
extern class (class) app.NSImageNSImage : (class) app.NSObjectNSObject
{
static (class) app.NSImageNSImage app.NSImage app.NSImage.alloc()alloc() @selector("alloc");
(class) app.NSImageNSImage app.NSImage app.NSImage.initWithSize(app.NSSize size)initWithSize((struct) app.NSSizeNSSize (parameter) app.NSSize sizesize) @selector("initWithSize:");
void void app.NSImage.addRepresentation(app.NSImageRep rep)addRepresentation((class) app.NSImageRepNSImageRep (parameter) app.NSImageRep reprep) @selector("addRepresentation:");
(struct) app.NSSizeNSSize app.NSSize app.NSImage.size()size() @selector("size");
(class) app.NSArrayNSArray app.NSArray app.NSImage.representations()representations() @selector("representations");
}
extern class (class) app.NSCursorNSCursor : (class) app.NSObjectNSObject
{
static (class) app.NSCursorNSCursor app.NSCursor app.NSCursor.alloc()alloc() @selector("alloc");
(class) app.NSCursorNSCursor app.NSCursor app.NSCursor.initWithImage(app.NSImage image, app.NSPoint hotSpot)initWithImage((class) app.NSImageNSImage (parameter) app.NSImage imageimage, (struct) app.NSPointNSPoint (parameter) app.NSPoint hotSpothotSpot)
@selector("initWithImage:hotSpot:");
// The pre-macOS-15 public vocabulary (resize* are soft-deprecated since 15).
static (class) app.NSCursorNSCursor app.NSCursor app.NSCursor.arrowCursor()arrowCursor() @selector("arrowCursor");
static (class) app.NSCursorNSCursor app.NSCursor app.NSCursor.IBeamCursor()IBeamCursor() @selector("IBeamCursor");
static (class) app.NSCursorNSCursor app.NSCursor app.NSCursor.crosshairCursor()crosshairCursor() @selector("crosshairCursor");
static (class) app.NSCursorNSCursor app.NSCursor app.NSCursor.pointingHandCursor()pointingHandCursor() @selector("pointingHandCursor");
static (class) app.NSCursorNSCursor app.NSCursor app.NSCursor.openHandCursor()openHandCursor() @selector("openHandCursor");
static (class) app.NSCursorNSCursor app.NSCursor app.NSCursor.resizeLeftCursor()resizeLeftCursor() @selector("resizeLeftCursor");
static (class) app.NSCursorNSCursor app.NSCursor app.NSCursor.resizeRightCursor()resizeRightCursor() @selector("resizeRightCursor");
static (class) app.NSCursorNSCursor app.NSCursor app.NSCursor.resizeLeftRightCursor()resizeLeftRightCursor() @selector("resizeLeftRightCursor");
static (class) app.NSCursorNSCursor app.NSCursor app.NSCursor.resizeUpCursor()resizeUpCursor() @selector("resizeUpCursor");
static (class) app.NSCursorNSCursor app.NSCursor app.NSCursor.resizeDownCursor()resizeDownCursor() @selector("resizeDownCursor");
static (class) app.NSCursorNSCursor app.NSCursor app.NSCursor.resizeUpDownCursor()resizeUpDownCursor() @selector("resizeUpDownCursor");
static (class) app.NSCursorNSCursor app.NSCursor app.NSCursor.currentCursor()currentCursor() @selector("currentCursor");
static void void app.NSCursor.hide()hide() @selector("hide");
static void void app.NSCursor.unhide()unhide() @selector("unhide");
void void app.NSCursor.set()set() @selector("set");
void void app.NSCursor.push()push() @selector("push");
void void app.NSCursor.pop()pop() @selector("pop");
(class) app.NSImageNSImage app.NSImage app.NSCursor.image()image() @selector("image");
(struct) app.NSPointNSPoint app.NSPoint app.NSCursor.hotSpot()hotSpot() @selector("hotSpot");
}
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.NSResponderNSResponder : (class) app.NSObjectNSObject
{
}
extern class (class) app.NSApplicationNSApplication : (class) app.NSResponderNSResponder
{
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.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.NSViewNSView : (class) app.NSResponderNSResponder
{
(struct) app.NSRectNSRect app.NSRect app.NSView.bounds()bounds() @selector("bounds");
void void app.NSView.addTrackingArea(app.NSTrackingArea area)addTrackingArea((class) app.NSTrackingAreaNSTrackingArea (parameter) app.NSTrackingArea areaarea) @selector("addTrackingArea:");
(class) app.NSArrayNSArray app.NSArray app.NSView.trackingAreas()trackingAreas() @selector("trackingAreas");
void void app.NSView.addCursorRect(app.NSRect rect, app.NSCursor cursor)addCursorRect((struct) app.NSRectNSRect (parameter) app.NSRect rectrect, (class) app.NSCursorNSCursor (parameter) app.NSCursor cursorcursor) @selector("addCursorRect:cursor:");
void void app.NSView.updateTrackingAreas()updateTrackingAreas() @selector("updateTrackingAreas");
}
extern class (class) app.NSWindowNSWindow : (class) app.NSResponderNSResponder
{
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.NSView view)setContentView((class) app.NSViewNSView (parameter) app.NSView viewview) @selector("setContentView:");
void void app.NSWindow.setDelegate(app.NSObject anObject)setDelegate((class) app.NSObjectNSObject (parameter) app.NSObject anObjectanObject) @selector("setDelegate:");
void void app.NSWindow.makeKeyAndOrderFront(app.NSObject sender)makeKeyAndOrderFront((class) app.NSObjectNSObject (parameter) app.NSObject sendersender) @selector("makeKeyAndOrderFront:");
void void app.NSWindow.invalidateCursorRectsForView(app.NSView view)invalidateCursorRectsForView((class) app.NSViewNSView (parameter) app.NSView viewview)
@selector("invalidateCursorRectsForView:");
bool bool app.NSWindow.areCursorRectsEnabled()areCursorRectsEnabled() @selector("areCursorRectsEnabled");
(struct) app.NSRectNSRect app.NSRect app.NSWindow.frame()frame() @selector("frame");
}
// The instrumented view: 3x3 zone grid, tracking areas, cursorUpdate: routing.
class (class) app.ZoneViewZoneView : (class) app.NSViewNSView
{
static (class) app.ZoneViewZoneView app.ZoneView app.ZoneView.alloc()alloc() @selector("alloc");
(class) app.ZoneViewZoneView app.ZoneView app.ZoneView.initWithFrame(app.NSRect frame)initWithFrame((struct) app.NSRectNSRect (parameter) app.NSRect frameframe) @selector("initWithFrame:");
void void app.ZoneView.drawRect(app.NSRect dirtyRect)drawRect((struct) app.NSRectNSRect (parameter) app.NSRect dirtyRectdirtyRect) @selector("drawRect:")
{
// Checkerboard the 9 zones (the Tier-C visual aid; cheap flat fills).
void* _error_ ctxctx = NSGraphicsContext.NSGraphicsContext.currentContextcurrentContext().NSGraphicsContext.currentContext().CGContextCGContext();
immutable _error_ bb = this.this.boundsbounds();
foreach ((parameter) zz; 0 .. 9)
{
immutable _error_ shadeshade = 0.25 + 0.08 * z;
CGContextSetRGBFillColor(ctx, shade, shade, shade, 1);
CGContextFillRect(ctx, zoneRect(b, z));
}
}
void void app.ZoneView.tick(app.NSTimer timer)tick((class) app.NSTimerNSTimer (parameter) app.NSTimer timertimer) @selector("tick:")
{
onTick(this);
}
// The modern path: AppKit sends cursorUpdate: when the pointer enters a tracking
// area registered with NSTrackingCursorUpdate. Locked session = no pointer, so
// the schedule calls this directly with e=null and g_forcedZone set.
void void app.ZoneView.cursorUpdate(app.NSEvent e)cursorUpdate((class) app.NSEventNSEvent (parameter) app.NSEvent ee) @selector("cursorUpdate:")
{
++g_cursorUpdates;
int _error_ zonezone = g_forcedZone;
if (e !is null)
{
immutable _error_ pp = e.e.locationInWindowlocationInWindow();
zone = zoneAt(this.this.boundsbounds(), p);
}
instr("cursor_update", "n=%d zone=%d source=%s", g_cursorUpdates, zone,
e !is null ? "event".ptr : "driven".ptr);
if (zone >= 0 && zone < 9)
setZoneCursor(zone);
}
void void app.ZoneView.mouseEntered(app.NSEvent e)mouseEntered((class) app.NSEventNSEvent (parameter) app.NSEvent ee) @selector("mouseEntered:")
{
++g_mouseEnters;
instr("mouse_entered", "n=%d", g_mouseEnters);
}
void void app.ZoneView.mouseExited(app.NSEvent e)mouseExited((class) app.NSEventNSEvent (parameter) app.NSEvent ee) @selector("mouseExited:")
{
++g_mouseExits;
instr("mouse_exited", "n=%d", g_mouseExits);
}
// AppKit calls this when tracking areas need recomputation (resize, scroll, …).
override void void app.ZoneView.updateTrackingAreas()updateTrackingAreas() @selector("updateTrackingAreas")
{
++g_updateTrackingCalls;
instr("update_tracking_areas", "n=%d count=%llu", g_updateTrackingCalls,
this.this.trackingAreastrackingAreas().this.trackingAreas().countcount());
super.super.updateTrackingAreasupdateTrackingAreas();
}
// The legacy mechanism (pre-NSTrackingArea, still alive): AppKit asks the view
// to re-declare its cursor rects; one addCursorRect:cursor: per region.
void void app.ZoneView.resetCursorRects()resetCursorRects() @selector("resetCursorRects")
{
++g_resetCursorRects;
this.this.addCursorRectaddCursorRect(this.this.boundsbounds(), NSCursor.NSCursor.crosshairCursorcrosshairCursor());
instr("reset_cursor_rects", "n=%d mechanism=addCursorRect_cursor legacy=1",
g_resetCursorRects);
}
void void app.ZoneView.windowWillClose(app.NSObject notification)windowWillClose((class) app.NSObjectNSObject (parameter) app.NSObject notificationnotification) @selector("windowWillClose:")
{
instr("close_requested");
g_app.g_app.stopstop(null);
}
}
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.NSWindowStyleMaskResizable = 8LUNSWindowStyleMaskResizable = 8,
}
enum ulong (constant) ulong app.NSBackingStoreBuffered = 2LUNSBackingStoreBuffered = 2;
enum long (constant) long app.NSEventTypeApplicationDefined = 15LNSEventTypeApplicationDefined = 15;
// --- 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;
__gshared (class) app.ZoneViewZoneView (__gshared global) app.ZoneView app.g_viewg_view;
__gshared (class) app.NSCursorNSCursor (__gshared global) app.NSCursor app.g_customCursorg_customCursor;
__gshared int (__gshared global) int app.g_tickg_tick, (__gshared global) int app.g_cursorUpdatesg_cursorUpdates, (__gshared global) int app.g_mouseEntersg_mouseEnters, (__gshared global) int app.g_mouseExitsg_mouseExits;
__gshared int (__gshared global) int app.g_updateTrackingCallsg_updateTrackingCalls, (__gshared global) int app.g_resetCursorRectsg_resetCursorRects, (__gshared global) int app.g_forcedZoneg_forcedZone = -1;
__gshared bool (__gshared global) bool app.g_autog_auto, (__gshared global) bool app.g_stopRequestedg_stopRequested;
// Name registry so currentCursor() pointers are loggable by name.
__gshared void*[40] (__gshared global) void*[40] app.g_curPtrsg_curPtrs;
__gshared const(char)*[40] (__gshared global) const(char)*[40] app.g_curNamesg_curNames;
__gshared int (__gshared global) int app.g_curCountg_curCount;
// Schedule (ticks of a ~16 ms timer).
enum (constant) int app.zoneDriveStartTick = 5zoneDriveStartTick = 5; // zones 0..8 on ticks 5..13
enum (constant) int app.legacyResizeTick = 16legacyResizeTick = 16;
enum (constant) int app.macos15Tick = 18macos15Tick = 18;
enum (constant) int app.invalidateRectsTick = 20invalidateRectsTick = 20;
enum (constant) int app.cgEventTick = 24cgEventTick = 24;
enum (constant) int app.syntheticResultTick = 38syntheticResultTick = 38;
enum (constant) int app.autoExitTick = 45autoExitTick = 45;
(class) app.NSStringNSString app.NSString app.nsstr(const(char)* s)nsstr(const(char)* (parameter) const(char)* ss)
{
return NSString.NSString.allocalloc().NSString.alloc().initWithUTF8StringinitWithUTF8String(s);
}
(struct) app.NSRectNSRect app.NSRect app.zoneRect(app.NSRect b, int z)zoneRect((struct) app.NSRectNSRect (parameter) app.NSRect bb, int (parameter) int zz)
{
immutable _error_ ww = b.b.sizesize.b.size.widthwidth / 3, _error_ hh = b.b.sizesize.b.size.heightheight / 3;
return NSRect(NSPoint(b.b.originorigin.b.origin.xx + (z % 3) * w, b.b.originorigin.b.origin.yy + (z / 3) * h),
NSSize(w, h));
}
int int app.zoneAt(app.NSRect b, app.NSPoint p)zoneAt((struct) app.NSRectNSRect (parameter) app.NSRect bb, (struct) app.NSPointNSPoint (parameter) app.NSPoint pp)
{
if (b.b.sizesize.b.size.widthwidth <= 0 || b.b.sizesize.b.size.heightheight <= 0)
return -1;
immutable _error_ cxcx = cast(int) (3 * (p.p.xx - b.b.originorigin.b.origin.xx) / b.b.sizesize.b.size.widthwidth);
immutable _error_ cycy = cast(int) (3 * (p.p.yy - b.b.originorigin.b.origin.yy) / b.b.sizesize.b.size.heightheight);
return cx < 0 || cx > 2 || cy < 0 || cy > 2 ? -1 : cy * 3 + cx;
}
void void app.registerCursor(app.NSCursor c, const(char)* name)registerCursor((class) app.NSCursorNSCursor (parameter) app.NSCursor cc, const(char)* (parameter) const(char)* namename)
{
if (c is null || g_curCount >= g_curPtrs.length)
return;
g_curPtrs[g_curCount] = cast(void*) c;
g_curNames[g_curCount] = name;
++g_curCount;
}
const(char)* const(char)* app.cursorName(app.NSCursor c)cursorName((class) app.NSCursorNSCursor (parameter) app.NSCursor cc)
{
foreach ((parameter) ii; 0 .. g_curCount)
if (g_curPtrs[i] is cast(void*) c)
return g_curNames[i];
return c is null ? "nil".ptr : "unregistered".ptr;
}
// One cursor_set log line, with the image facts (size in points, reps, rep pixel
// widths — standard cursors ship 1x+2x pairs; that is the HiDPI mechanism).
void void app.setAndLog(app.NSCursor c, const(char)* name, const(char)* path)setAndLog((class) app.NSCursorNSCursor (parameter) app.NSCursor cc, const(char)* (parameter) const(char)* namename, const(char)* (parameter) const(char)* pathpath)
{
if (c is null)
{
instr("cursor_set", "name=%s path=%s skipped=nil", name, path);
return;
}
registerCursor(c, name);
c.c.setset();
(unresolved type) NSImageNSImage _error_ imgimg = c.c.imageimage();
immutable _error_ szsz = img !is null ? img.img.sizesize() : NSSize(0, 0);
immutable _error_ hothot = c.c.hotSpothotSpot();
char[64] _error_ repsreps = void;
repsSummary(img, reps[]);
instr("cursor_set", "name=%s path=%s size_pt=%.0fx%.0f hotspot=(%.0f,%.0f) reps=%s",
name, path, sz.sz.widthwidth, sz.sz.heightheight, hot.hot.xx, hot.hot.yy, reps.ptr);
}
// "n:px1,px2,…" — rep count and per-rep pixel width.
void void app.repsSummary(app.NSImage img, char[] buf)repsSummary((class) app.NSImageNSImage (parameter) app.NSImage imgimg, char[] (parameter) char[] bufbuf)
{
buf[0] = '0';
buf[1] = 0;
if (img is null)
return;
(unresolved type) NSArrayNSArray _error_ rsrs = img.img.representationsrepresentations();
immutable _error_ nn = rs.rs.countcount();
size_t _error_ offoff = snprintf(buf.ptr, buf.length, "%llu:", n);
foreach ((parameter) ii; 0 .. n)
{
auto _error_ rr = cast((unresolved type) NSImageRepNSImageRep) rs.rs.objectAtIndexobjectAtIndex(i);
off += snprintf(buf.ptr + off, buf.length - off, i == 0 ? "%ld" : ",%ld",
r.r.pixelsWidepixelsWide());
}
}
// --- The standard-cursor vocabulary probe -------------------------------------------
void void app.probeVocabulary()probeVocabulary()
{
// Public class getters (pre-15 + the macOS 15 additions), the parameterized
// 15.0 selectors, and the diagonal names that never existed publicly.
static struct (struct) ProbeProbe
{
(unresolved type) stringstring _error_ selsel;
(unresolved type) stringstring _error_ notenote;
}
static immutable (unresolved type) ProbeProbe[] Probe[] probesprobes = [
{"arrowCursor", "10.0"},
{"IBeamCursor", "10.0"},
{"IBeamCursorForVerticalLayout", "10.7"},
{"crosshairCursor", "10.0"},
{"pointingHandCursor", "10.0"},
{"closedHandCursor", "10.0"},
{"openHandCursor", "10.0"},
{"operationNotAllowedCursor", "10.5"},
{"dragLinkCursor", "10.6"},
{"dragCopyCursor", "10.6"},
{"contextualMenuCursor", "10.6"},
{"disappearingItemCursor", "10.0"},
{"resizeLeftCursor", "10.0_soft_deprecated_15.0"},
{"resizeRightCursor", "10.0_soft_deprecated_15.0"},
{"resizeLeftRightCursor", "10.0_soft_deprecated_15.0"},
{"resizeUpCursor", "10.0_soft_deprecated_15.0"},
{"resizeDownCursor", "10.0_soft_deprecated_15.0"},
{"resizeUpDownCursor", "10.0_soft_deprecated_15.0"},
{"zoomInCursor", "15.0"},
{"zoomOutCursor", "15.0"},
{"columnResizeCursor", "15.0"},
{"rowResizeCursor", "15.0"},
{"columnResizeCursorInDirections:", "15.0"},
{"rowResizeCursorInDirections:", "15.0"},
{"frameResizeCursorFromPosition:inDirections:", "15.0"},
{"currentCursor", "10.0"},
{"currentSystemCursor", "10.6_deprecated"},
// The gap: no public diagonal resize cursors existed before macOS 15.
{"resizeNorthWestSouthEastCursor", "never_public"},
{"resizeNorthEastSouthWestCursor", "never_public"},
{"_windowResizeNorthWestSouthEastCursor", "private"},
];
Class _error_ clscls = Class.lookup("NSCursor");
foreach ((parameter) pp; probes)
instr("vocab", "sel=%s available=%d note=%s", p.p.selsel.ptr,
cls.getClassMethod(SEL.register(p.p.selsel.ptr)).ptr !is null ? 1 : 0,
p.p.notenote.ptr);
}
// --- The custom bullseye cursor ------------------------------------------------------
// CPU-paint an RGBA bullseye: alternating dark/light rings, transparent corners.
void void app.paintBullseye(ubyte* px, int n)paintBullseye(ubyte* (parameter) ubyte* pxpx, int (parameter) int nn)
{
immutable _error_ cc = (n - 1) / 2.0;
foreach ((parameter) yy; 0 .. n)
foreach ((parameter) xx; 0 .. n)
{
immutable _error_ dxdx = x - c, _error_ dydy = y - c;
immutable _error_ rr = (dx * dx + dy * dy) ^^ 0.5;
ubyte* _error_ pp = px + 4 * (y * n + x);
if (r > c) // outside: fully transparent
{
p[0] = p[1] = p[2] = p[3] = 0;
continue;
}
immutable _error_ ringring = cast(int) (r * 8 / n); // 0..3 rings
immutable _error_ darkdark = (ring & 1) == 0;
p[0] = dark ? 16 : 240; // R (premultiplied; alpha=255 so identical)
p[1] = dark ? 16 : 64; // G
p[2] = dark ? 16 : 32; // B
p[3] = 255; // A
}
}
(class) app.NSBitmapImageRepNSBitmapImageRep app.NSBitmapImageRep app.makeRep(int px, double sizePt)makeRep(int (parameter) int pxpx, double (parameter) double sizePtsizePt)
{
auto _error_ reprep = NSBitmapImageRep.NSBitmapImageRep.allocalloc().NSBitmapImageRep.alloc().initWithBitmapDataPlanesinitWithBitmapDataPlanes(null, px, px, 8, 4,
true, false, nsstr("NSDeviceRGBColorSpace"), px * 4, 32);
paintBullseye(rep.rep.bitmapDatabitmapData(), px);
rep.rep.setSizesetSize(NSSize(sizePt, sizePt)); // pt size != px size on the 2x rep
return rep;
}
(class) app.NSCursorNSCursor app.NSCursor app.buildCustomCursor()buildCustomCursor()
{
(unresolved type) NSImageNSImage _error_ imgimg = NSImage.NSImage.allocalloc().NSImage.alloc().initWithSizeinitWithSize(NSSize(24, 24));
img.img.addRepresentationaddRepresentation(makeRep(24, 24)); // 1x
img.img.addRepresentationaddRepresentation(makeRep(48, 24)); // 2x — AppKit picks by screen scale
(unresolved type) NSCursorNSCursor _error_ curcur = NSCursor.NSCursor.allocalloc().NSCursor.alloc().initWithImageinitWithImage(img, NSPoint(12, 12));
char[64] _error_ repsreps = void;
repsSummary(img, reps[]);
immutable _error_ hothot = cur.cur.hotSpothotSpot();
instr("custom_cursor", "image_pt=%.0fx%.0f reps=%s hotspot=(%.0f,%.0f) "
~ "note=hotspot_in_flipped_image_coords", img.img.sizesize().img.size().widthwidth,
img.img.sizesize().img.size().heightheight, reps.ptr, hot.hot.xx, hot.hot.yy);
return cur;
}
// --- Probes -------------------------------------------------------------------------
void void app.logCurrent(const(char)* after)logCurrent(const(char)* (parameter) const(char)* afterafter)
{
(unresolved type) NSCursorNSCursor _error_ curcur = NSCursor.NSCursor.currentCursorcurrentCursor();
instr("cursor_stack", "after=%s current=%s", after, cursorName(cur));
}
// push/pop vs set: what does the app-side stack report?
void void app.stackProbe()stackProbe()
{
(unresolved type) NSCursorNSCursor _error_ arrowarrow = NSCursor.NSCursor.arrowCursorarrowCursor();
(unresolved type) NSCursorNSCursor _error_ ibeamibeam = NSCursor.NSCursor.IBeamCursorIBeamCursor();
(unresolved type) NSCursorNSCursor _error_ crosscross = NSCursor.NSCursor.crosshairCursorcrosshairCursor();
(unresolved type) NSCursorNSCursor _error_ handhand = NSCursor.NSCursor.pointingHandCursorpointingHandCursor();
registerCursor(arrow, "arrow");
registerCursor(ibeam, "iBeam");
registerCursor(cross, "crosshair");
registerCursor(hand, "pointingHand");
instr("step", "name=stack_probe");
arrow.arrow.setset();
logCurrent("arrow.set");
ibeam.ibeam.pushpush();
logCurrent("iBeam.push");
cross.cross.pushpush();
logCurrent("crosshair.push");
hand.hand.setset(); // set while two pushes are outstanding: replace top? bypass?
logCurrent("pointingHand.set_under_stack");
cross.cross.poppop();
logCurrent("pop_1");
ibeam.ibeam.poppop();
logCurrent("pop_2");
arrow.arrow.setset();
logCurrent("arrow.set_final");
// The F10 connection: hide/unhide is a balanced counter, independent of which
// cursor is set — a capture/lock mode must pair them or leak invisibility.
NSCursor.NSCursor.hidehide();
NSCursor.NSCursor.unhideunhide();
instr("cursor_visibility", "hide_unhide=balanced note=f10_lock_pairing");
}
// Zone -> cursor mapping (zones 0..8, row-major from bottom-left in view coords).
void void app.setZoneCursor(int zone)setZoneCursor(int (parameter) int zonezone)
{
switch (zone)
{
case 0:
setAndLog(NSCursor.NSCursor.arrowCursorarrowCursor(), "arrow", "standard");
break;
case 1:
setAndLog(NSCursor.NSCursor.IBeamCursorIBeamCursor(), "iBeam", "standard");
break;
case 2:
setAndLog(NSCursor.NSCursor.pointingHandCursorpointingHandCursor(), "pointingHand", "standard");
break;
case 3:
setAndLog(NSCursor.NSCursor.crosshairCursorcrosshairCursor(), "crosshair", "standard");
break;
case 4:
setAndLog(NSCursor.NSCursor.openHandCursoropenHandCursor(), "openHand", "standard");
break;
case 5:
setAndLog(NSCursor.NSCursor.resizeLeftRightCursorresizeLeftRightCursor(), "resizeLeftRight",
"standard_soft_deprecated");
break;
case 6:
setAndLog(NSCursor.NSCursor.resizeUpDownCursorresizeUpDownCursor(), "resizeUpDown",
"standard_soft_deprecated");
break;
case 7:
setAndLog(macos15Cursor("columnResizeCursor"), "columnResize", "macos15");
break;
case 8:
setAndLog(g_customCursor, "customBullseye", "initWithImage_hotSpot");
break;
default:
break;
}
}
// Availability-gated class getter via the runtime (returns null when absent).
(class) app.NSCursorNSCursor app.NSCursor app.macos15Cursor(const(char)* sel)macos15Cursor(const(char)* (parameter) const(char)* selsel)
{
Class _error_ clscls = Class.lookup("NSCursor");
auto _error_ ss = SEL.register(sel);
if (cls.getClassMethod(s).ptr is null)
return null;
auto _error_ send0send0 = cast((unresolved type) MsgSend0MsgSend0) msgSendAddr();
return cast((unresolved type) NSCursorNSCursor) send0(cls.ptr, s.ptr);
}
// The eight resize directions, both vocabularies.
void void app.resizeSequence()resizeSequence()
{
instr("step", "name=legacy_resize_six note=no_public_diagonals_before_macos15");
setAndLog(NSCursor.NSCursor.resizeLeftCursorresizeLeftCursor(), "resizeLeft", "standard_soft_deprecated");
setAndLog(NSCursor.NSCursor.resizeRightCursorresizeRightCursor(), "resizeRight", "standard_soft_deprecated");
setAndLog(NSCursor.NSCursor.resizeLeftRightCursorresizeLeftRightCursor(), "resizeLeftRight",
"standard_soft_deprecated");
setAndLog(NSCursor.NSCursor.resizeUpCursorresizeUpCursor(), "resizeUp", "standard_soft_deprecated");
setAndLog(NSCursor.NSCursor.resizeDownCursorresizeDownCursor(), "resizeDown", "standard_soft_deprecated");
setAndLog(NSCursor.NSCursor.resizeUpDownCursorresizeUpDownCursor(), "resizeUpDown",
"standard_soft_deprecated");
}
void void app.frameResizeSequence()frameResizeSequence()
{
Class _error_ clscls = Class.lookup("NSCursor");
auto _error_ ss = SEL.register("frameResizeCursorFromPosition:inDirections:");
if (cls.getClassMethod(s).ptr is null)
{
instr("step", "name=frame_resize_eight available=0");
return;
}
instr("step", "name=frame_resize_eight available=1");
static immutable (unresolved type) stringstring[8] string[8] namesnames = ["top", "left", "bottom", "right", "topLeft",
"topRight", "bottomLeft", "bottomRight"];
static immutable ulong[8] ulong[8] pospos = [frpTop, frpLeft, frpBottom, frpRight,
frpTop | frpLeft, frpTop | frpRight, frpBottom | frpLeft,
frpBottom | frpRight];
foreach ((parameter) ii; 0 .. 8)
{
auto _error_ send2send2 = cast((unresolved type) MsgSend2MsgSend2) msgSendAddr();
auto _error_ cc = cast((unresolved type) NSCursorNSCursor) send2(cls.ptr, s.ptr, pos[i], frameResizeDirectionsAll);
char[32] _error_ nmnm = void;
snprintf(nm.ptr, nm.length, "frameResize.%s", names[i].ptr);
setAndLog(c, nm.ptr, "macos15_frame_resize");
}
}
// Synthetic pointer motion: post a CGEvent mouse-move into zone centers. In-process
// posting needs no accessibility grant, but a locked session has no live pointer
// pipeline — counters at syntheticResultTick give the honest answer.
void void app.cgEventProbe()cgEventProbe()
{
immutable _error_ wfwf = g_win.g_win.frameframe(); // AppKit global coords: bottom-left origin, y-up
immutable _error_ dbdb = CGDisplayBounds(CGMainDisplayID()); // CG: top-left origin
// Global CG point of the window-content center.
immutable _error_ gxgx = wf.wf.originorigin.wf.origin.xx + wf.wf.sizesize.wf.size.widthwidth / 2;
immutable _error_ gygy = db.db.sizesize.db.size.heightheight - (wf.wf.originorigin.wf.origin.yy + wf.wf.sizesize.wf.size.heightheight / 2);
instr("step", "name=cg_event_probe target_global=(%.0f,%.0f) tap=session", gx, gy);
foreach ((parameter) ii; 0 .. 2)
{
void* _error_ evev = CGEventCreateMouseEvent(null, kCGEventMouseMoved,
NSPoint(gx + i * 40, gy), 0);
CGEventPost(kCGSessionEventTap, ev);
CFRelease(ev);
}
instr("cg_event_posted", "moves=2");
}
void void app.installTrackingAreas(app.ZoneView view)installTrackingAreas((class) app.ZoneViewZoneView (parameter) app.ZoneView viewview)
{
immutable _error_ bb = view.view.boundsbounds();
immutable _error_ optsopts = NSTrackingMouseEnteredAndExited | NSTrackingCursorUpdate
| NSTrackingActiveInKeyWindow;
foreach ((parameter) zz; 0 .. 9)
{
immutable _error_ rr = zoneRect(b, z);
auto _error_ areaarea = NSTrackingArea.NSTrackingArea.allocalloc().NSTrackingArea.alloc().initWithRectinitWithRect(r, opts, view, null);
view.view.addTrackingAreaaddTrackingArea(area);
instr("tracking_install", "zone=%d rect=(%.0f,%.0f %.0fx%.0f) options=0x%llx",
z, r.r.originorigin.r.origin.xx, r.r.originorigin.r.origin.yy, r.r.sizesize.r.size.widthwidth, r.r.sizesize.r.size.heightheight, opts);
}
instr("tracking_areas", "count=%llu note=cursor_update_incompatible_with_active_always",
view.view.trackingAreastrackingAreas().view.trackingAreas().countcount());
}
void void app.onTick(app.ZoneView view)onTick((class) app.ZoneViewZoneView (parameter) app.ZoneView viewview)
{
++g_tick;
if (!g_auto)
return; // interactive mode: keep the window up for a manual hover session
if (g_tick >= zoneDriveStartTick && g_tick < zoneDriveStartTick + 9)
{
g_forcedZone = g_tick - zoneDriveStartTick;
view.view.cursorUpdatecursorUpdate(null); // drive the cursorUpdate: logic directly (no pointer)
g_forcedZone = -1;
}
else if (g_tick == legacyResizeTick)
resizeSequence();
else if (g_tick == macos15Tick)
frameResizeSequence();
else if (g_tick == invalidateRectsTick)
{
instr("step", "name=invalidateCursorRectsForView rects_enabled=%d",
g_win.g_win.areCursorRectsEnabledareCursorRectsEnabled() ? 1 : 0);
g_win.g_win.invalidateCursorRectsForViewinvalidateCursorRectsForView(view);
}
else if (g_tick == cgEventTick)
cgEventProbe();
else if (g_tick == syntheticResultTick)
instr("synthetic_result", "cursor_updates_from_events=%d mouse_entered=%d "
~ "mouse_exited=%d reset_cursor_rects=%d note=locked_session",
g_cursorUpdates - 9, g_mouseEnters, g_mouseExits, g_resetCursorRects);
else if (g_tick >= autoExitTick && !g_stopRequested)
{
g_stopRequested = true;
instr("step", "name=NSApp_stop tick=%d", g_tick);
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: needs an event dispatch to take effect
}
}
int int D main()main()
{
g_auto = getenv("WSI_AUTO_EXIT") !is null;
instrInit("APPKIT_F12");
instr("init_start", "auto_exit=%d", g_auto ? 1 : 0);
if (CGMainDisplayID() == 0)
{
printf("SKIP: no macOS window server (CGMainDisplayID == 0)\n");
return 0;
}
auto _error_ poolpool = autoreleasepool_push();
scope (exit)
autoreleasepool_pop(pool);
g_app = NSApplication.NSApplication.sharedApplicationsharedApplication();
g_app.g_app.setActivationPolicysetActivationPolicy(NSApplicationActivationPolicyRegular);
probeVocabulary();
immutable _error_ contentRectcontentRect = NSRect(NSPoint(120, 120), NSSize(480, 320));
g_win = NSWindow.NSWindow.allocalloc().NSWindow.alloc().initWithContentRectinitWithContentRect(contentRect,
NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable,
NSBackingStoreBuffered, false);
g_win.g_win.setTitlesetTitle(nsstr("wsi-f12-cursors"));
g_view = ZoneView.ZoneView.allocalloc().ZoneView.alloc().initWithFrameinitWithFrame(contentRect);
g_win.g_win.setContentViewsetContentView(g_view);
g_win.g_win.setDelegatesetDelegate(g_view);
installTrackingAreas(g_view);
g_win.g_win.makeKeyAndOrderFrontmakeKeyAndOrderFront(null);
g_customCursor = buildCustomCursor();
registerCursor(g_customCursor, "customBullseye");
stackProbe();
instr("step", "name=NSTimer_scheduledTimerWithTimeInterval interval_ms=16");
NSTimer.NSTimer.scheduledTimerWithTimeIntervalscheduledTimerWithTimeInterval(0.016, g_view, SEL.register("tick:"), null,
true);
instr("step", "name=NSApp_run");
g_app.g_app.runrun();
instr("loop_exit", "ticks=%d cursor_updates=%d mouse_entered=%d mouse_exited=%d "
~ "update_tracking=%d reset_rects=%d",
g_tick, g_cursorUpdates, g_mouseEnters, g_mouseExits, g_updateTrackingCalls,
g_resetCursorRects);
return 0;
}