// macOS/AppKit F07 demo — IME / text input via NSTextInputClient
// (../../../features/f07-text-input.md).
// F06 proved the boundary: WITHOUT NSTextInputClient, interpretKeyEvents: routes to
// the legacy single-arg insertText: and a synthetic dead key passes through verbatim
// (´ then e, never é). This demo implements the FULL NSTextInputClient protocol on a
// custom view and shows the boundary flipping: the same synthetic option-e + e pair
// now produces setMarkedText:"´" (composition pending, rendered inline with an
// underline) followed by insertText:"é" replacing the marked text.
//
// The view keeps a single-line editor model (committed UTF-16 buffer + inline marked
// text + caret) and logs EVERY protocol callback with its arguments — the call
// choreography is the deliverable. Conformance is attached at runtime
// (class_addProtocol with the AppKit-registered NSTextInputClient protocol), and the
// demo logs conformsToProtocol: before/after plus -[NSView inputContext] for a
// conforming vs a plain view (the "bespoke view exposes no inputContext" trap).
//
// Choreographed under WSI_AUTO_EXIT=1 (synthetic NSEvents through [window sendEvent:]
// -> keyDown: -> interpretKeyEvents:, the f06 route 1):
// a. plain text "hi" -> which insertText variant fires now?
// b. option-e then e -> dead-key composition through marked text
// c. option-e then Esc -> cancel mid-composition (unmarkText? cancelOperation:?)
// d. option-e then focus loss -> marked-text fate on resignFirstResponder
// e. insertText:"X" range={0,2} -> direct replacementRange semantics on committed text
// f. firstRectForCharacterRange at both ends -> the caret/candidate-window anchor math
// Real CJK (Pinyin) input needs an unlocked session + enabled IME -> Tier C script in
// ../../f07-text-input.md.
//
// 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 instrument : instr, instrInit;
import objc.autorelease : autoreleasepool_push, autoreleasepool_pop; // objective-d
import objc.rt : SEL, Class, Protocol;
extern (C) nothrow @nogc
{
uint uint app.CGMainDisplayID() nothrow @nogcCGMainDisplayID();
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);
// Route 2 (cgevent_wrap): a CGEvent created against a private event source gives
// the NSEvent a real HID-style event backing (and the source owns dead-key state),
// WITHOUT posting through the WindowServer tap (which f06 showed is not routed to
// a locked, non-frontmost app). The CGEvent is wrapped in-process via
// +[NSEvent eventWithCGEvent:] and dispatched straight to the view.
void* void* app.CGEventSourceCreate(int stateID) nothrow @nogcCGEventSourceCreate(int (parameter) int stateIDstateID);
void* void* app.CGEventCreateKeyboardEvent(void* source, ushort vk, bool keyDown) nothrow @nogcCGEventCreateKeyboardEvent(void* (parameter) void* sourcesource, ushort (parameter) ushort vkvk, bool (parameter) bool keyDownkeyDown);
void void app.CGEventSetFlags(void* event, ulong flags) nothrow @nogcCGEventSetFlags(void* (parameter) void* eventevent, ulong (parameter) ulong flagsflags);
void void app.CFRelease(void* o) nothrow @nogcCFRelease(void* (parameter) void* oo);
}
enum int (constant) int app.kCGEventSourceStatePrivate = -1kCGEventSourceStatePrivate = -1;
enum ulong (constant) ulong app.kCGEventFlagMaskAlternate = 524288LUkCGEventFlagMaskAlternate = 1UL << 19;
// --- Cocoa geometry / NSRange ----------------------------------------------------
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) struct (struct) app.NSRangeNSRange
{
ulong (field) ulong app.NSRange.locationlocation, (field) ulong app.NSRange.lengthlength; // NSUInteger
}
enum ulong (constant) ulong app.NSNotFound = 9223372036854775807LUNSNotFound = 0x7FFF_FFFF_FFFF_FFFF; // NSIntegerMax
// --- AppKit class declarations ---------------------------------------------------
extern (Objective-C):
extern class (class) app.NSObjectNSObject
{
bool app.NSObject.respondsToSelectorrespondsToSelector(SEL (parameter) SEL selsel) @selector("respondsToSelector:");
Class app.NSObject.objcClassobjcClass() @selector("class");
}
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:");
(class) app.NSStringNSString app.NSString app.NSString.initWithCharacters(const(ushort)* chars, ulong length)initWithCharacters(const(ushort)* (parameter) const(ushort)* charschars, ulong (parameter) ulong lengthlength)
@selector("initWithCharacters:length:");
const(char)* const(char)* app.NSString.UTF8String()UTF8String() @selector("UTF8String");
ulong ulong app.NSString.length()length() @selector("length");
ushort ushort app.NSString.characterAtIndex(ulong index)characterAtIndex(ulong (parameter) ulong indexindex) @selector("characterAtIndex:");
}
extern class (class) app.NSAttributedStringNSAttributedString : (class) app.NSObjectNSObject
{
static (class) app.NSAttributedStringNSAttributedString app.NSAttributedString app.NSAttributedString.alloc()alloc() @selector("alloc");
(class) app.NSAttributedStringNSAttributedString app.NSAttributedString app.NSAttributedString.initWithString(app.NSString s)initWithString((class) app.NSStringNSString (parameter) app.NSString ss) @selector("initWithString:");
(class) app.NSStringNSString app.NSString app.NSAttributedString.string()string() @selector("string");
}
extern class (class) app.NSArrayNSArray : (class) app.NSObjectNSObject
{
static (class) app.NSArrayNSArray app.NSArray app.NSArray.array()array() @selector("array");
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.NSResponderNSResponder : (class) app.NSObjectNSObject
{
void void app.NSResponder.interpretKeyEvents(app.NSArray events)interpretKeyEvents((class) app.NSArrayNSArray (parameter) app.NSArray eventsevents) @selector("interpretKeyEvents:");
bool bool app.NSResponder.becomeFirstResponder()becomeFirstResponder() @selector("becomeFirstResponder");
bool bool app.NSResponder.resignFirstResponder()resignFirstResponder() @selector("resignFirstResponder");
}
extern class (class) app.NSEventNSEvent : (class) app.NSObjectNSObject
{
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.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.NSEventNSEvent app.NSEvent app.NSEvent.eventWithCGEvent(void* cgEvent)eventWithCGEvent(void* (parameter) void* cgEventcgEvent) @selector("eventWithCGEvent:");
ushort ushort app.NSEvent.keyCode()keyCode() @selector("keyCode");
long long app.NSEvent.type()type() @selector("type");
(class) app.NSStringNSString app.NSString app.NSEvent.characters()characters() @selector("characters");
ulong ulong app.NSEvent.modifierFlags()modifierFlags() @selector("modifierFlags");
}
extern class (class) app.NSTimerNSTimer : (class) app.NSObjectNSObject
{
static (class) app.NSTimerNSTimer app.NSTimer.scheduledTimerWithTimeIntervalscheduledTimerWithTimeInterval(double interval, (class) app.NSObjectNSObject (parameter) NSObject targettarget,
SEL (parameter) SEL selsel, (class) app.NSObjectNSObject userInfo, 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.NSTextInputContextNSTextInputContext : (class) app.NSObjectNSObject
{
void void app.NSTextInputContext.activate()activate() @selector("activate");
bool bool app.NSTextInputContext.handleEvent(app.NSEvent e)handleEvent((class) app.NSEventNSEvent (parameter) app.NSEvent ee) @selector("handleEvent:");
(class) app.NSStringNSString app.NSString app.NSTextInputContext.selectedKeyboardInputSource()selectedKeyboardInputSource() @selector("selectedKeyboardInputSource");
}
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.activateIgnoringOtherApps(bool flag)activateIgnoringOtherApps(bool (parameter) bool flagflag) @selector("activateIgnoringOtherApps:");
void void app.NSApplication.run()run() @selector("run");
void void app.NSApplication.stop(app.NSObject sender)stop((class) app.NSObjectNSObject (parameter) app.NSObject sendersender) @selector("stop:");
void void app.NSApplication.postEvent(app.NSEvent event, bool atStart)postEvent((class) app.NSEventNSEvent (parameter) app.NSEvent eventevent, bool (parameter) bool atStartatStart) @selector("postEvent:atStart:");
}
extern class (class) app.NSViewNSView : (class) app.NSResponderNSResponder
{
bool bool app.NSView.acceptsFirstResponder()acceptsFirstResponder() @selector("acceptsFirstResponder");
(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:");
(struct) app.NSRectNSRect app.NSRect app.NSView.convertRect(app.NSRect rect, app.NSView view)convertRect((struct) app.NSRectNSRect (parameter) app.NSRect rectrect, (class) app.NSViewNSView (parameter) app.NSView viewview) @selector("convertRect:toView:");
(struct) app.NSPointNSPoint app.NSPoint app.NSView.convertPoint(app.NSPoint point, app.NSView view)convertPoint((struct) app.NSPointNSPoint (parameter) app.NSPoint pointpoint, (class) app.NSViewNSView (parameter) app.NSView viewview) @selector("convertPoint:fromView:");
(class) app.NSTextInputContextNSTextInputContext app.NSTextInputContext app.NSView.inputContext()inputContext() @selector("inputContext");
}
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:");
bool bool app.NSWindow.makeFirstResponder(app.NSObject responder)makeFirstResponder((class) app.NSObjectNSObject (parameter) app.NSObject responderresponder) @selector("makeFirstResponder:");
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.convertRectToScreen(app.NSRect rect)convertRectToScreen((struct) app.NSRectNSRect (parameter) app.NSRect rectrect) @selector("convertRectToScreen:");
(struct) app.NSRectNSRect app.NSRect app.NSWindow.convertRectFromScreen(app.NSRect rect)convertRectFromScreen((struct) app.NSRectNSRect (parameter) app.NSRect rectrect) @selector("convertRectFromScreen:");
}
// A focusable view WITHOUT NSTextInputClient: the focus-loss target, and the negative
// control for -[NSView inputContext] (a bespoke view that never told AppKit it can
// accept text input gets no input context at all — the Uno/sokol trap from the survey).
class (class) app.PlainViewPlainView : (class) app.NSViewNSView
{
static (class) app.PlainViewPlainView app.PlainView app.PlainView.alloc()alloc() @selector("alloc");
(class) app.PlainViewPlainView app.PlainView app.PlainView.initWithFrame(app.NSRect frame)initWithFrame((struct) app.NSRectNSRect (parameter) app.NSRect frameframe) @selector("initWithFrame:");
override bool bool app.PlainView.acceptsFirstResponder()acceptsFirstResponder() @selector("acceptsFirstResponder")
{
return true;
}
}
// The editor view. Bodied methods implement NSTextInputClient (conformance metadata is
// attached at runtime in main — see attachProtocol); the editor model lives in module
// globals (g_text/g_marked/g_caret).
class (class) app.TextViewTextView : (class) app.NSViewNSView
{
static (class) app.TextViewTextView app.TextView app.TextView.alloc()alloc() @selector("alloc");
(class) app.TextViewTextView app.TextView app.TextView.initWithFrame(app.NSRect frame)initWithFrame((struct) app.NSRectNSRect (parameter) app.NSRect frameframe) @selector("initWithFrame:");
override bool bool app.TextView.acceptsFirstResponder()acceptsFirstResponder() @selector("acceptsFirstResponder")
{
return true;
}
override bool bool app.TextView.becomeFirstResponder()becomeFirstResponder() @selector("becomeFirstResponder")
{
instr("focus", "state=become");
return super.super.becomeFirstResponderbecomeFirstResponder();
}
override bool bool app.TextView.resignFirstResponder()resignFirstResponder() @selector("resignFirstResponder")
{
instr("focus", "state=resign has_marked=%d", g_hasMarked ? 1 : 0);
immutable _error_ rr = super.super.resignFirstResponderresignFirstResponder();
logState("after_resign");
return r;
}
void void app.TextView.keyDown(app.NSEvent e)keyDown((class) app.NSEventNSEvent (parameter) app.NSEvent ee) @selector("keyDown:")
{
(unresolved type) NSStringNSString _error_ txttxt = e.e.characterscharacters();
instr("key", "code=%u text=%s", e.e.keyCodekeyCode(), txt !is null ? txt.txt.UTF8StringUTF8String() : "");
if (g_useIke) // WSI_USE_IKE=1: the NSResponder-convenience route
{
this.this.interpretKeyEventsinterpretKeyEvents(NSArray.NSArray.arrayWithObjectarrayWithObject(e));
instr("interpret_key_events", "returned code=%u", e.e.keyCodekeyCode());
return;
}
// The real-app pattern (interpretKeyEvents: does the same internally): offer
// the event to the input context; only if it declines run the key bindings.
(unresolved type) NSTextInputContextNSTextInputContext _error_ ctxctx = this.this.inputContextinputContext();
immutable _error_ handledhandled = ctx !is null && ctx.ctx.handleEventhandleEvent(e);
instr("handle_event", "ctx=%s handled=%d",
ctx !is null ? "non-nil".ptr : "nil".ptr, handled ? 1 : 0);
if (!handled)
this.this.interpretKeyEventsinterpretKeyEvents(NSArray.NSArray.arrayWithObjectarrayWithObject(e));
}
// --- NSTextInputClient -------------------------------------------------------
void void app.TextView.insertTextRR(app.NSObject s, app.NSRange repl)insertTextRR((class) app.NSObjectNSObject (parameter) app.NSObject ss, (struct) app.NSRangeNSRange (parameter) app.NSRange replrepl) @selector("insertText:replacementRange:")
{
char[48] _error_ rbrb;
(unresolved type) NSStringNSString _error_ strstr = stringOf(s);
instr("tic_insert_text", "text=%s class=%s repl=%s",
u8(str), s !is null ? s.s.objcClassobjcClass().name : "<nil>", rs(repl, rb));
(unresolved type) size_tsize_t _error_ locloc = g_caret, _error_ deldel = 0;
if (g_hasMarked)
{
loc = g_markedLoc; // insert replaces the pending marked text
clearMarked();
}
else if (repl.repl.locationlocation != NSNotFound)
{
loc = cast((unresolved type) size_tsize_t) repl.repl.locationlocation;
del = cast((unresolved type) size_tsize_t) repl.repl.lengthlength;
}
spliceCommitted(loc, del, str);
g_caret = loc + nsLen(str);
logState("insert");
this.this.setNeedsDisplaysetNeedsDisplay(true);
}
// Legacy pre-NSTextInputClient sink — with conformance attached this must go dead
// (in f06, without conformance, ALL text landed here).
void void app.TextView.insertTextLegacy(app.NSObject s)insertTextLegacy((class) app.NSObjectNSObject (parameter) app.NSObject ss) @selector("insertText:")
{
instr("insert_text_legacy", "text=%s", u8(stringOf(s)));
}
void void app.TextView.setMarkedTextSR(app.NSObject s, app.NSRange sel, app.NSRange repl)setMarkedTextSR((class) app.NSObjectNSObject (parameter) app.NSObject ss, (struct) app.NSRangeNSRange (parameter) app.NSRange selsel, (struct) app.NSRangeNSRange (parameter) app.NSRange replrepl)
@selector("setMarkedText:selectedRange:replacementRange:")
{
char[48] _error_ rb1rb1, _error_ rb2rb2;
(unresolved type) NSStringNSString _error_ strstr = stringOf(s);
instr("tic_set_marked_text", "text=%s class=%s sel=%s repl=%s",
u8(str), s !is null ? s.s.objcClassobjcClass().name : "<nil>", rs(sel, rb1), rs(repl, rb2));
if (!g_hasMarked)
g_markedLoc = repl.repl.locationlocation != NSNotFound ? cast((unresolved type) size_tsize_t) repl.repl.locationlocation : g_caret;
g_markedLen = nsLen(str);
foreach ((parameter) ii; 0 .. g_markedLen)
g_marked[i] = str.str.characterAtIndexcharacterAtIndex(i);
g_hasMarked = g_markedLen > 0; // an empty string unmarks per the protocol docs
g_selInMarked = sel;
logState("set_marked");
this.this.setNeedsDisplaysetNeedsDisplay(true);
}
void void app.TextView.unmarkText()unmarkText() @selector("unmarkText")
{
instr("tic_unmark_text", "marked_len=%zu note=committing_pending_text", g_markedLen);
// Per the protocol: accept the marked text as if it had been inserted normally.
if (g_hasMarked)
{
spliceUtf16(g_markedLoc, 0, g_marked[0 .. g_markedLen]);
g_caret = g_markedLoc + g_markedLen;
clearMarked();
}
logState("unmark");
this.this.setNeedsDisplaysetNeedsDisplay(true);
}
(struct) app.NSRangeNSRange app.NSRange app.TextView.selectedRange()selectedRange() @selector("selectedRange")
{
(unresolved type) NSRangeNSRange _error_ rr = g_hasMarked
? NSRange(g_markedLoc + g_selInMarked.g_selInMarked.locationlocation, g_selInMarked.g_selInMarked.lengthlength)
: NSRange(g_caret, 0);
char[48] _error_ rbrb;
instr("tic_selected_range", "-> %s", rs(r, rb));
return r;
}
(struct) app.NSRangeNSRange app.NSRange app.TextView.markedRange()markedRange() @selector("markedRange")
{
(unresolved type) NSRangeNSRange _error_ rr = g_hasMarked ? NSRange(g_markedLoc, g_markedLen) : NSRange(NSNotFound, 0);
char[48] _error_ rbrb;
instr("tic_marked_range", "-> %s", rs(r, rb));
return r;
}
bool bool app.TextView.hasMarkedText()hasMarkedText() @selector("hasMarkedText")
{
instr("tic_has_marked_text", "-> %d", g_hasMarked ? 1 : 0);
return g_hasMarked;
}
(class) app.NSAttributedStringNSAttributedString app.NSAttributedString app.TextView.attributedSubstring(app.NSRange range, app.NSRange* actual)attributedSubstring((struct) app.NSRangeNSRange (parameter) app.NSRange rangerange, (struct) app.NSRangeNSRange* (parameter) app.NSRange* actualactual)
@selector("attributedSubstringForProposedRange:actualRange:")
{
immutable _error_ totaltotal = docLen();
(unresolved type) size_tsize_t _error_ locloc = range.range.locationlocation > total ? total : cast((unresolved type) size_tsize_t) range.range.locationlocation;
(unresolved type) size_tsize_t _error_ lenlen = loc + range.range.lengthlength > total ? total - loc : cast((unresolved type) size_tsize_t) range.range.lengthlength;
ushort[256] _error_ tmptmp;
foreach ((parameter) ii; 0 .. len)
tmp[i] = docCharAt(loc + i);
if (actual !is null)
*actual = NSRange(loc, len);
char[48] _error_ rbrb;
instr("tic_attributed_substring", "range=%s actual={%zu,%zu}", rs(range, rb), loc, len);
(unresolved type) NSStringNSString _error_ subsub = NSString.NSString.allocalloc().NSString.alloc().initWithCharactersinitWithCharacters(tmp.ptr, len);
return NSAttributedString.NSAttributedString.allocalloc().NSAttributedString.alloc().initWithStringinitWithString(sub);
}
(class) app.NSArrayNSArray app.NSArray app.TextView.validAttributesForMarkedText()validAttributesForMarkedText() @selector("validAttributesForMarkedText")
{
instr("tic_valid_attributes", "-> empty");
return NSArray.NSArray.arrayarray();
}
// The candidate-window anchor: the IME asks for the on-screen rect of a character
// range; the answer must be in SCREEN coordinates. Logged on every call — this is
// how a candidate window follows the caret.
(struct) app.NSRectNSRect app.NSRect app.TextView.firstRect(app.NSRange range, app.NSRange* actual)firstRect((struct) app.NSRangeNSRange (parameter) app.NSRange rangerange, (struct) app.NSRangeNSRange* (parameter) app.NSRange* actualactual)
@selector("firstRectForCharacterRange:actualRange:")
{
immutable _error_ totaltotal = docLen();
(unresolved type) size_tsize_t _error_ locloc = range.range.locationlocation == NSNotFound || range.range.locationlocation > total
? g_caret : cast((unresolved type) size_tsize_t) range.range.locationlocation;
immutable _error_ lenlen = range.range.lengthlength == 0 ? 1 : cast((unresolved type) size_tsize_t) range.range.lengthlength;
immutable _error_ viewRectviewRect = NSRect(NSPoint(textX + cellW * loc, baseY),
NSSize(cellW * len, cellH));
immutable _error_ winRectwinRect = this.this.convertRectconvertRect(viewRect, null);
immutable _error_ scrscr = g_win.g_win.convertRectToScreenconvertRectToScreen(winRect);
if (actual !is null)
*actual = NSRange(loc, range.range.lengthlength);
char[48] _error_ rbrb;
instr("tic_first_rect", "range=%s view=(%.0f,%.0f %.0fx%.0f) screen=(%.0f,%.0f %.0fx%.0f)",
rs(range, rb), viewRect.viewRect.originorigin.viewRect.origin.xx, viewRect.viewRect.originorigin.viewRect.origin.yy,
viewRect.viewRect.sizesize.viewRect.size.widthwidth, viewRect.viewRect.sizesize.viewRect.size.heightheight, scr.scr.originorigin.scr.origin.xx, scr.scr.originorigin.scr.origin.yy,
scr.scr.sizesize.scr.size.widthwidth, scr.scr.sizesize.scr.size.heightheight);
return scr;
}
ulong ulong app.TextView.characterIndexForPoint(app.NSPoint screenPoint)characterIndexForPoint((struct) app.NSPointNSPoint (parameter) app.NSPoint screenPointscreenPoint) @selector("characterIndexForPoint:")
{
immutable _error_ winRectwinRect = g_win.g_win.convertRectFromScreenconvertRectFromScreen(
NSRect(screenPoint, NSSize(0, 0)));
immutable _error_ vpvp = this.this.convertPointconvertPoint(winRect.winRect.originorigin, null);
immutable double _error_ relrel = (vp.vp.xx - textX) / cellW;
immutable _error_ totaltotal = docLen();
(unresolved type) size_tsize_t _error_ idxidx = rel < 0 ? 0 : cast((unresolved type) size_tsize_t) rel;
if (idx > total)
idx = total;
instr("tic_character_index", "screen=(%.0f,%.0f) view_x=%.1f -> %zu",
screenPoint.screenPoint.xx, screenPoint.screenPoint.yy, vp.vp.xx, idx);
return idx;
}
void app.TextView.doCommandBySelectordoCommandBySelector(SEL (parameter) SEL selsel) @selector("doCommandBySelector:")
{
instr("do_command", "selector=%s has_marked=%d", sel.name, g_hasMarked ? 1 : 0);
}
// --- rendering: committed cells gray, marked cells blue with an underline -----
// (Text is rendered as colored character cells, not glyphs — the locked-screen
// A[ssh] session composites nothing anyway; the marked/committed distinction and
// the underline are what F07 requires to be visible in the model.)
void void app.TextView.drawRect(app.NSRect dirty)drawRect((struct) app.NSRectNSRect (parameter) app.NSRect dirtydirty) @selector("drawRect:")
{
void* _error_ ctxctx = NSGraphicsContext.NSGraphicsContext.currentContextcurrentContext().NSGraphicsContext.currentContext().CGContextCGContext();
CGContextSetRGBFillColor(ctx, 0.12, 0.12, 0.14, 1);
CGContextFillRect(ctx, this.this.boundsbounds());
immutable _error_ totaltotal = docLen();
foreach ((parameter) ii; 0 .. total)
{
immutable _error_ markedmarked = g_hasMarked && i >= g_markedLoc && i < g_markedLoc + g_markedLen;
immutable _error_ xx = textX + cellW * i;
if (marked)
{
CGContextSetRGBFillColor(ctx, 0.35, 0.55, 0.95, 1); // marked: blue cell
CGContextFillRect(ctx, NSRect(NSPoint(x, baseY), NSSize(cellW - 1, cellH)));
CGContextSetRGBFillColor(ctx, 1, 1, 1, 1); // + 2 pt underline
CGContextFillRect(ctx, NSRect(NSPoint(x, baseY - 3), NSSize(cellW - 1, 2)));
}
else
{
CGContextSetRGBFillColor(ctx, 0.7, 0.7, 0.7, 1); // committed: gray cell
CGContextFillRect(ctx, NSRect(NSPoint(x, baseY), NSSize(cellW - 1, cellH)));
}
}
// caret: 1 pt vertical bar at the insertion point (display position)
immutable _error_ caretDispcaretDisp = g_hasMarked ? g_markedLoc + g_markedLen : g_caret;
CGContextSetRGBFillColor(ctx, 1, 0.85, 0.3, 1);
CGContextFillRect(ctx, NSRect(NSPoint(textX + cellW * caretDisp, baseY - 1),
NSSize(1, cellH + 2)));
}
void void app.TextView.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.NSEventTypeKeyDown = 10LNSEventTypeKeyDown = 10;
enum long (constant) long app.NSEventTypeKeyUp = 11LNSEventTypeKeyUp = 11;
enum long (constant) long app.NSEventTypeApplicationDefined = 15LNSEventTypeApplicationDefined = 15;
enum ulong (constant) ulong app.NSEventModifierFlagOption = 524288LUNSEventModifierFlagOption = 1UL << 19;
// Single-line editor cell metrics (points; also the firstRect/characterIndex math).
enum double (constant) double app.textX = 10.0textX = 10, (constant) double app.baseY = 20.0baseY = 20, (constant) double app.cellW = 9.0cellW = 9, (constant) double app.cellH = 16.0cellH = 16;
// --- 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.TextViewTextView (__gshared global) app.TextView app.g_viewg_view;
__gshared (class) app.PlainViewPlainView (__gshared global) app.PlainView app.g_plaing_plain;
__gshared long (__gshared global) long app.g_winNumg_winNum;
__gshared int (__gshared global) int app.g_stepg_step;
__gshared void* (__gshared global) void* app.g_cgSrcg_cgSrc; // private CGEventSource: owns dead-key state for route 2
__gshared bool (__gshared global) bool app.g_useIkeg_useIke; // WSI_USE_IKE=1: interpretKeyEvents: instead of handleEvent:
__gshared ushort[512] (__gshared global) ushort[512] app.g_textg_text; // committed text, UTF-16 units
__gshared (alias) object.size_t = ulongsize_t (__gshared global) ulong app.g_textLeng_textLen;
__gshared (alias) object.size_t = ulongsize_t (__gshared global) ulong app.g_caretg_caret;
__gshared ushort[64] (__gshared global) ushort[64] app.g_markedg_marked; // pending (pre-edit) text, rendered inline at g_markedLoc
__gshared (alias) object.size_t = ulongsize_t (__gshared global) ulong app.g_markedLocg_markedLoc, (__gshared global) ulong app.g_markedLeng_markedLen;
__gshared bool (__gshared global) bool app.g_hasMarkedg_hasMarked;
__gshared (struct) app.NSRangeNSRange (__gshared global) app.NSRange app.g_selInMarkedg_selInMarked;
(alias) object.size_t = ulongsize_t ulong app.nsLen(app.NSString s)nsLen((class) app.NSStringNSString (parameter) app.NSString ss)
{
return s is null ? 0 : cast((unresolved type) size_tsize_t) s.s.lengthlength();
}
const(char)* const(char)* app.u8(app.NSString s)u8((class) app.NSStringNSString (parameter) app.NSString ss)
{
return s is null ? "<nil>" : s.s.UTF8StringUTF8String();
}
// The `id` params of insertText:/setMarkedText: are NSString OR NSAttributedString.
(class) app.NSStringNSString app.NSString app.stringOf(app.NSObject s)stringOf((class) app.NSObjectNSObject (parameter) app.NSObject ss)
{
if (s is null)
return null;
if (s.s.respondsToSelectorrespondsToSelector(SEL.register("string")))
return (cast((unresolved type) NSAttributedStringNSAttributedString) s).(cast(NSAttributedString)s).stringstring();
return cast((unresolved type) NSStringNSString) s;
}
const(char)* const(char)* app.rs(in app.NSRange r, char[] buf)rs(in (struct) app.NSRangeNSRange (parameter) const(app.NSRange) rr, char[] (parameter) char[] bufbuf)
{
if (r.r.locationlocation == NSNotFound)
snprintf(buf.ptr, buf.buf.lengthlength, "{NSNotFound,%llu}", r.r.lengthlength);
else
snprintf(buf.ptr, buf.buf.lengthlength, "{%llu,%llu}", r.r.locationlocation, r.r.lengthlength);
return buf.ptr;
}
void void app.clearMarked()clearMarked()
{
g_hasMarked = false;
g_markedLen = 0;
g_selInMarked = NSRange(0, 0);
}
void void app.spliceUtf16(ulong loc, ulong del, scope const(ushort)[] ins)spliceUtf16((alias) object.size_t = ulongsize_t (parameter) ulong locloc, (alias) object.size_t = ulongsize_t (parameter) ulong deldel, scope const(ushort)[] (parameter) const(ushort)[] insins)
{
if (loc > g_textLen)
loc = g_textLen;
if (loc + del > g_textLen)
del = g_textLen - loc;
immutable _error_ newLennewLen = g_textLen - del + ins.ins.lengthlength;
if (newLen > g_text.g_text.lengthlength)
return;
// shift the tail (forward iteration for a left shift, reverse for a right shift)
if (ins.ins.lengthlength < del)
foreach ((parameter) ii; 0 .. g_textLen - loc - del)
g_text[loc + ins.ins.lengthlength + i] = g_text[loc + del + i];
else if (ins.ins.lengthlength > del)
foreach_reverse ((parameter) ii; 0 .. g_textLen - loc - del)
g_text[loc + ins.ins.lengthlength + i] = g_text[loc + del + i];
foreach ((parameter) ii, (parameter) uu; ins)
g_text[loc + i] = u;
g_textLen = newLen;
}
void void app.spliceCommitted(ulong loc, ulong del, app.NSString s)spliceCommitted((alias) object.size_t = ulongsize_t (parameter) ulong locloc, (alias) object.size_t = ulongsize_t (parameter) ulong deldel, (class) app.NSStringNSString (parameter) app.NSString ss)
{
ushort[128] _error_ tmptmp;
immutable _error_ nn = nsLen(s) > tmp.tmp.lengthlength ? tmp.tmp.lengthlength : nsLen(s);
foreach ((parameter) ii; 0 .. n)
tmp[i] = s.s.characterAtIndexcharacterAtIndex(i);
spliceUtf16(loc, del, tmp[0 .. n]);
}
(alias) object.size_t = ulongsize_t ulong app.docLen()docLen()
{
return g_textLen + (g_hasMarked ? g_markedLen : 0);
}
ushort ushort app.docCharAt(ulong i)docCharAt((alias) object.size_t = ulongsize_t (parameter) ulong ii)
{
if (!g_hasMarked || i < g_markedLoc)
return g_text[i];
if (i < g_markedLoc + g_markedLen)
return g_marked[i - g_markedLoc];
return g_text[i - g_markedLen];
}
// BMP-only UTF-16 -> UTF-8 (enough for ASCII/é/´); writes a NUL-terminated C string.
void void app.utf16ToUtf8(const(ushort)* s, ulong n, char* o) nothrow @nogcutf16ToUtf8(const(ushort)* (parameter) const(ushort)* ss, (alias) object.size_t = ulongsize_t (parameter) ulong nn, char* (parameter) char* oo) nothrow @nogc
{
(unresolved type) size_tsize_t _error_ jj = 0;
foreach ((parameter) ii; 0 .. n)
{
immutable uint _error_ cc = s[i];
if (c < 0x80)
o[j++] = cast(char) c;
else if (c < 0x800)
{
o[j++] = cast(char)(0xC0 | (c >> 6));
o[j++] = cast(char)(0x80 | (c & 0x3F));
}
else
{
o[j++] = cast(char)(0xE0 | (c >> 12));
o[j++] = cast(char)(0x80 | ((c >> 6) & 0x3F));
o[j++] = cast(char)(0x80 | (c & 0x3F));
}
}
o[j] = 0;
}
void void app.logState(const(char)* at)logState(const(char)* (parameter) const(char)* atat)
{
char[1600] _error_ cbcb;
char[256] _error_ mbmb;
utf16ToUtf8(g_text.ptr, g_textLen, cb.ptr);
utf16ToUtf8(g_marked.ptr, g_markedLen, mb.ptr);
instr("state", "at=%s text=\"%s\" caret=%zu marked=\"%s\" marked_at=%zu",
at, cb.ptr, g_caret, mb.ptr, g_markedLoc);
}
// --- Synthetic key injection (f06 route 1) ----------------------------------------
struct (struct) app.KeySpecKeySpec
{
ushort (field) ushort app.KeySpec.codecode;
ulong (field) ulong app.KeySpec.flagsflags;
(alias) object.string = stringstring (field) string app.KeySpec.charschars;
(alias) object.string = stringstring (field) string app.KeySpec.charsNoModcharsNoMod;
(alias) object.string = stringstring (field) string app.KeySpec.labellabel;
}
immutable (struct) app.KeySpecKeySpec (immutable global) immutable(app.KeySpec) app.optEoptE = (struct) app.KeySpecKeySpec(14, (constant) ulong app.NSEventModifierFlagOption = 524288LUNSEventModifierFlagOption, "´", "e", "opt_e_deadacute");
void void app.inject(in app.KeySpec k)inject(in (struct) app.KeySpecKeySpec (parameter) const(app.KeySpec) kk)
{
instr("inject", "route=nsevent label=%.*s code=%u",
cast(int) k.k.labellabel.k.label.lengthlength, k.k.labellabel.ptr, k.k.codecode);
(unresolved type) NSStringNSString _error_ chch = NSString.NSString.allocalloc().NSString.alloc().initWithUTF8StringinitWithUTF8String(k.k.charschars.ptr);
(unresolved type) NSStringNSString _error_ cmcm = NSString.NSString.allocalloc().NSString.alloc().initWithUTF8StringinitWithUTF8String(k.k.charsNoModcharsNoMod.ptr);
g_win.g_win.sendEventsendEvent(NSEvent.NSEvent.keyEventWithTypekeyEventWithType(NSEventTypeKeyDown, NSPoint(0, 0), k.k.flagsflags,
0, g_winNum, null, ch, cm, false, k.k.codecode));
g_win.g_win.sendEventsendEvent(NSEvent.NSEvent.keyEventWithTypekeyEventWithType(NSEventTypeKeyUp, NSPoint(0, 0), k.k.flagsflags,
0, g_winNum, null, ch, cm, false, k.k.codecode));
}
// Route 2: CGEvent-backed NSEvent, dispatched in-process to the view's keyDown:
// (never posted to the WindowServer). The shared private source keeps dead-key state
// across calls, and the wrapped NSEvent carries a real event ref for TSM.
void void app.injectCG(ushort code, ulong flags, const(char)* label)injectCG(ushort (parameter) ushort codecode, ulong (parameter) ulong flagsflags, const(char)* (parameter) const(char)* labellabel)
{
void* _error_ downdown = CGEventCreateKeyboardEvent(g_cgSrc, code, true);
if (down is null)
{
instr("inject", "route=cgevent_wrap label=%s result=create_failed", label);
return;
}
if (flags != 0)
CGEventSetFlags(down, flags);
(unresolved type) NSEventNSEvent _error_ ee = NSEvent.NSEvent.eventWithCGEventeventWithCGEvent(down);
(unresolved type) NSStringNSString _error_ txttxt = e !is null ? e.e.characterscharacters() : null;
instr("inject", "route=cgevent_wrap label=%s code=%u wrapped_type=%ld wrapped_text=%s",
label, code, e !is null ? e.e.typetype() : -1, txt !is null ? txt.txt.UTF8StringUTF8String() : "<nil>");
if (e !is null)
g_view.g_view.keyDownkeyDown(e);
CFRelease(down);
}
void void app.stopApp()stopApp()
{
instr("step", "name=NSApp_stop");
g_app.g_app.stopstop(null);
(unresolved type) NSEventNSEvent _error_ evev = NSEvent.NSEvent.otherEventWithTypeotherEventWithType(NSEventTypeApplicationDefined,
NSPoint(0, 0), 0, 0, 0, null, 0, 0, 0);
g_app.g_app.postEventpostEvent(ev, true); // stop: is only checked after an event dispatch
}
void void app.onStep()onStep()
{
switch (g_step)
{
case 0: // a. plain text — which insertText variant fires WITH conformance?
inject(KeySpec(4, 0, "h", "h", "letter_h"));
break;
case 1:
inject(KeySpec(34, 0, "i", "i", "letter_i"));
break;
case 2: // b1. dead key as a plain synthetic NSEvent (the f06 verbatim path)
inject(optE);
break;
case 3:
inject(KeySpec(14, 0, "e", "e", "letter_e_after_dead"));
break;
case 4: // b2. dead key as a CGEvent-backed NSEvent: does TSM compose now?
injectCG(14, kCGEventFlagMaskAlternate, "opt_e_deadacute");
break;
case 5:
injectCG(14, 0, "letter_e_compose");
break;
case 6: // c. cancel mid-composition with Esc
injectCG(14, kCGEventFlagMaskAlternate, "opt_e_deadacute");
break;
case 7:
injectCG(53, 0, "escape_mid_composition");
break;
case 8: // d. focus loss mid-composition
injectCG(14, kCGEventFlagMaskAlternate, "opt_e_deadacute");
break;
case 9:
instr("step", "name=focus_steal target=PlainView has_marked=%d", g_hasMarked ? 1 : 0);
g_win.g_win.makeFirstRespondermakeFirstResponder(g_plain);
break;
case 10:
instr("step", "name=focus_restore target=TextView");
g_win.g_win.makeFirstRespondermakeFirstResponder(g_view);
break;
case 11: // e. replacementRange semantics: replace committed chars {0,2} directly
instr("step", "name=direct_insert_replacement text=X repl={0,2}");
g_view.g_view.insertTextRRinsertTextRR(NSString.NSString.allocalloc().NSString.alloc().initWithUTF8StringinitWithUTF8String("X"), NSRange(0, 2));
break;
case 12: // f. the candidate-anchor math at both ends of the line
{
instr("step", "name=direct_first_rect at=start_and_caret");
(unresolved type) NSRangeNSRange _error_ actualactual;
g_view.g_view.firstRectfirstRect(NSRange(0, 1), &actual);
immutable _error_ rr = g_view.g_view.firstRectfirstRect(NSRange(g_caret, 1), &actual);
g_view.g_view.characterIndexForPointcharacterIndexForPoint(r.r.originorigin); // round-trip the anchor point
break;
}
default:
stopApp();
break;
}
++g_step;
}
extern (Objective-C) class (class) app.StepTargetStepTarget : (class) app.NSObjectNSObject
{
static (class) app.StepTargetStepTarget app.StepTarget app.StepTarget.alloc()alloc() @selector("alloc");
(class) app.StepTargetStepTarget app.StepTarget app.StepTarget.init()init() @selector("init");
void void app.StepTarget.step(app.NSTimer t)step((class) app.NSTimerNSTimer (parameter) app.NSTimer tt) @selector("step:")
{
onStep();
}
}
// Attach the AppKit-registered NSTextInputClient protocol to the D-defined class at
// runtime. AppKit gates the whole IME path on conformsToProtocol: — implementing the
// methods alone is not enough for -[NSView inputContext] to create an input context.
void void app.attachProtocol(app.TextView probe)attachProtocol((class) app.TextViewTextView (parameter) app.TextView probeprobe)
{
Class _error_ clscls = Class.lookup("TextView");
if (cls.ptr is null)
cls = probe.probe.objcClassobjcClass(); // D may emit a mangled runtime name; ask the instance
Protocol _error_ protoproto = Protocol.get("NSTextInputClient");
instr("protocol_attach", "class=%s protocol_found=%d conforms_before=%d",
cls.name, proto.ptr !is null ? 1 : 0, cls.conformsTo(proto) ? 1 : 0);
if (proto.ptr is null)
return;
immutable _error_ addedadded = cls.addProtocol(proto);
instr("protocol_attach", "added=%d conforms_after=%d", added ? 1 : 0,
cls.conformsTo(proto) ? 1 : 0);
}
int int D main()main()
{
immutable _error_ autoExitautoExit = getenv("WSI_AUTO_EXIT") !is null;
g_useIke = getenv("WSI_USE_IKE") !is null;
immutable _error_ noActivatenoActivate = getenv("WSI_NO_ACTIVATE") !is null;
instrInit("APPKIT_F07");
instr("init_start", "auto_exit=%d use_ike=%d no_activate=%d",
autoExit ? 1 : 0, g_useIke ? 1 : 0, noActivate ? 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);
immutable _error_ contentRectcontentRect = NSRect(NSPoint(120, 120), NSSize(480, 120));
g_win = NSWindow.NSWindow.allocalloc().NSWindow.alloc().initWithContentRectinitWithContentRect(contentRect,
NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable,
NSBackingStoreBuffered, false);
g_win.g_win.setTitlesetTitle(NSString.NSString.allocalloc().NSString.alloc().initWithUTF8StringinitWithUTF8String("wsi-f07-text-input"));
g_view = TextView.TextView.allocalloc().TextView.alloc().initWithFrameinitWithFrame(contentRect);
g_plain = PlainView.PlainView.allocalloc().PlainView.alloc().initWithFrameinitWithFrame(NSRect(NSPoint(0, 0), NSSize(10, 10)));
// Conformance dance BEFORE first-responder setup, so the input context is created
// against a conforming class.
attachProtocol(g_view);
g_win.g_win.setContentViewsetContentView(g_view);
g_win.g_win.setDelegatesetDelegate(g_view);
g_win.g_win.makeKeyAndOrderFrontmakeKeyAndOrderFront(null);
g_app.g_app.activateIgnoringOtherAppsactivateIgnoringOtherApps(true);
g_win.g_win.makeFirstRespondermakeFirstResponder(g_view);
g_winNum = g_win.g_win.windowNumberwindowNumber();
// The inputContext split: conforming view gets one, plain view does not.
(unresolved type) NSTextInputContextNSTextInputContext _error_ ctxctx = g_view.g_view.inputContextinputContext();
instr("input_context", "view=TextView ctx=%s",
ctx !is null ? "non-nil".ptr : "nil".ptr);
instr("input_context", "view=PlainView ctx=%s",
g_plain.g_plain.inputContextinputContext() !is null ? "non-nil".ptr : "nil".ptr);
if (ctx !is null)
{
(unresolved type) NSStringNSString _error_ srcsrc = ctx.ctx.selectedKeyboardInputSourceselectedKeyboardInputSource();
instr("input_context", "selected_source=%s",
src !is null ? src.src.UTF8StringUTF8String() : "<nil>");
if (!noActivate)
{
ctx.ctx.activateactivate(); // force TSM activation (normally done on app activation)
src = ctx.ctx.selectedKeyboardInputSourceselectedKeyboardInputSource();
instr("input_context", "after_activate selected_source=%s",
src !is null ? src.src.UTF8StringUTF8String() : "<nil>");
}
}
instr("window_created", "win_num=%ld first_responder=TextView", g_winNum);
g_cgSrc = CGEventSourceCreate(kCGEventSourceStatePrivate);
instr("cg_source", "state=private created=%d", g_cgSrc !is null ? 1 : 0);
if (autoExit)
{
(unresolved type) StepTargetStepTarget _error_ targettarget = StepTarget.StepTarget.allocalloc().StepTarget.alloc().initinit();
instr("step", "name=script_timer_start interval_ms=250");
NSTimer.NSTimer.scheduledTimerWithTimeIntervalscheduledTimerWithTimeInterval(0.25, target, SEL.register("step:"),
null, true);
}
instr("step", "name=NSApp_run");
g_app.g_app.runrun();
instr("loop_exit", "steps=%d", g_step);
return 0;
}