These libraries are experimental. APIs may change without notice. Generated from source with koruc 0.1.7 on 10/5/2026.
Metal
@korulang/metal@0.0.1Metal + AppKit window for Koru — pure objc_msgSend, phantom window lifecycle, frame-loop-as-effect-branch
metal/index.kz · 7 tors
@korulang/metal — a Metal window for Koru (v0: open + clear + close) · 23 more lines
@korulang/metal — a Metal window for Koru (v0: open + clear + close)
Tracer for the native-app assimilation path: can Koru drive an
Objective-C-flavored API with no shim? Everything below goes through
objc_msgSend typed casts — no .m file, no wrapper library, just the
ObjC runtime's C surface plus the few genuinely-C entry points
(MTLCreateSystemDefaultDevice, CFStringCreateWithCString).
The C footguns this compiles away, same bet as raylib/index.kz:
1. A window you open and never render is leaked Cocoa state — here it is
a KORU030 outright: `frames` is the only route to <active!>, and
<active!> is the only state window.close accepts.
2. Rendering without a drawable/descriptor bracket is a silent no-op —
here every draw call borrows a *Frame<frame> that only exists inside
the `! frame` effect-branch body: the pass descriptor lives between
the engine's bracket, so drawing outside a frame is not expressible.
3. Forgetting to end/present a command buffer hangs the swap — the
engine owns that bracket: endEncoding + presentDrawable + commit are
emitted around the frame body, so the user cannot omit them.
House style follows raylib/index.kz and sqlite3/index.kz: `<state!>` grants
an obligation, `<!state>` consumes it, bare `<state>` borrows.
Phantom lifecycles
Derived from the phantom labels in the declarations below — state! issues an
obligation the compiler will chase, !state discharges it, a bare state holds it without moving it. Nothing here is hand-drawn.
Window 2 states opened!active!Frame 1 state frame!~pub tor window.open { width: i32, height: i32, title: string }
| win *Window<opened!>
| err string// The frame loop — the `opened -> active` transition, same bracket shape as
// raylib's: the engine acquires the drawable, builds the render pass, hands
// the user a *Frame borrow for pass configuration, then ends, presents and
// commits around the body. `! frame` fires once per frame; `count` bounds
// the loop so tests halt.
//
// Callbacks as effect branches, the vaxis shape: ObjC IMPs cannot call Koru
// arms, so they push into the mailbox while sendEvent runs; the loop drains
// it here — the splice site where arms are callable — and fires the
// optional `! ?` arms. Unbound optional arms are generated no-ops. `! close`
// fires when the window closed (isVisible polled — no delegate needed; the
// default close button just closes); quit() breaks the loop.
~pub tor frames { win: *Window<!opened|!active>, count: ?i32 }
! frame *Frame<frame!>
! ?key-down koru/metal:KeyData
! ?mouse-down koru/metal:MouseData
! ?close
| done *Window<active!>// No-op consumer of the per-frame borrow — auto-inserted release, mirror of
// raylib's release.frame.
~pub tor release.frame { f: *Frame<!frame> }~pub tor window.close { win: *Window<!active> }// Clear color — a WINDOW property, not a draw call. Metal reads the pass
// descriptor's clearColor at encoder creation, so a `draw.clear` inside the
// frame body would be a silent no-op by construction. Setting it here, as a
// borrow (same shape as raylib's window.fps), keeps the semantic honest: the
// stored color is applied to every frame's attachment.
~pub tor window.clear.color { win: *Window<opened|active>, r: f32, g: f32, b: f32 }// Drawing — every draw call borrows the frame handle, so it can only appear
// inside a `! frame` body, where it records into the live render encoder.
~pub tor draw.triangle { f: *Frame<frame> }quit
index.kz:453// Request the frames loop to exit at the next iteration — same shape as
// vaxis:quit. Sets a module flag; frames checks it after mailbox dispatch.
~pub tor quit {}