diff --git a/current/CHECKPOINT-FFI.md b/current/CHECKPOINT-FFI.md index 1679b16..ccdbdcc 100644 --- a/current/CHECKPOINT-FFI.md +++ b/current/CHECKPOINT-FFI.md @@ -560,9 +560,17 @@ declared as `#foreign #objc_class` blocks. The 4-keyword `addObserver:selector:name:object:` selector derives cleanly from the underscore-separated sx name (`addObserver_selector_name_object`). +Phase 3.2 C3 landed: RunLoop + display-timing cluster. NSRunLoop +(`currentRunLoop`) and CADisplayLink +(`displayLinkWithTarget_selector`, `addToRunLoop_forMode`, +`targetTimestamp`, `duration`) declared as `#foreign #objc_class` +blocks. The `link` parameter on the `sxTick:` callback is now cast +to `*CADisplayLink` at function entry so subsequent method calls +type-check. + Open work: -- **Phase 3 step 3.2 — C3..C5** — uikit.sx migration, one cluster - per commit, chess regression after each. +- **Phase 3 step 3.2 — C4..C5** — uikit.sx migration, two clusters + remaining (UIKit chrome, view tree + GL). test for the default-mangling table. Escape hatch for selectors that don't fit the underscore-split rule (e.g. `tableView_ numberOfRowsInSection_` with an asymmetric keyword count). diff --git a/library/modules/platform/uikit.sx b/library/modules/platform/uikit.sx index ac3eaab..714d14a 100644 --- a/library/modules/platform/uikit.sx +++ b/library/modules/platform/uikit.sx @@ -100,6 +100,19 @@ NSNotificationCenter :: #foreign #objc_class("NSNotificationCenter") { addObserver_selector_name_object :: (self: *Self, observer: *void, sel: *void, name: *void, obj: *void); } +// ── RunLoop + display timing (Phase 3.2 C3) ──────────────────────────── + +NSRunLoop :: #foreign #objc_class("NSRunLoop") { + currentRunLoop :: () -> *NSRunLoop; +} + +CADisplayLink :: #foreign #objc_class("CADisplayLink") { + displayLinkWithTarget_selector :: (target: *void, sel: *void) -> *CADisplayLink; + addToRunLoop_forMode :: (self: *Self, runloop: *NSRunLoop, mode: *void); + targetTimestamp :: (self: *Self) -> f64; + duration :: (self: *Self) -> f64; +} + // GLenum constants for renderbuffer/framebuffer setup that aren't in opengl.sx's // loader path (they live on the framework's symbol table directly). GL_RENDERBUFFER :u32: 0x8D41; @@ -534,8 +547,9 @@ uikit_scene_will_connect_ios :: (delegate: *void, scene: *void) { UIViewController := objc_getClass("UIViewController".ptr); SxGLView := objc_getClass("SxGLView".ptr); SxMetalView := objc_getClass("SxMetalView".ptr); - CADisplayLink := objc_getClass("CADisplayLink".ptr); - NSRunLoop := objc_getClass("NSRunLoop".ptr); + // CADisplayLink and NSRunLoop class objects come from the + // declarative `#objc_class` slots populated by emit_llvm's + // __sx_objc_class_init constructor — no local objc_getClass needed. win_raw := #objc_call(*void)(UIWindow, "alloc"); plat.window = #objc_call(*void)(win_raw, "initWithWindowScene:", scene); @@ -619,10 +633,11 @@ uikit_scene_will_connect_ios :: (delegate: *void, scene: *void) { // a SEL value (not a dispatch selector), so it still goes through // sel_registerName. sel_tick := sel_registerName("sxTick:".ptr); - plat.display_link = #objc_call(*void)(CADisplayLink, "displayLinkWithTarget:selector:", plat.gl_view, sel_tick); - runloop := #objc_call(*void)(NSRunLoop, "currentRunLoop"); - mode := ns_string("kCFRunLoopDefaultMode".ptr); - #objc_call(void)(plat.display_link, "addToRunLoop:forMode:", runloop, mode); + link := CADisplayLink.displayLinkWithTarget_selector(plat.gl_view, sel_tick); + plat.display_link = xx link; + runloop := NSRunLoop.currentRunLoop(); + mode_ns := ns_string("kCFRunLoopDefaultMode".ptr); + link.addToRunLoop_forMode(runloop, mode_ns); NSLog(ns_string("[sx] UIKitPlatform booted\n".ptr)); } @@ -682,7 +697,8 @@ uikit_gl_view_layer_class :: (cls: *void, _cmd: *void) -> *void callconv(.c) { objc_getClass("CAEAGLLayer".ptr); } -uikit_gl_view_tick :: (self: *void, _cmd: *void, link: *void) callconv(.c) { +uikit_gl_view_tick :: (self: *void, _cmd: *void, link_raw: *void) callconv(.c) { + link : *CADisplayLink = xx link_raw; if g_uikit_plat == null { return; } plat := g_uikit_plat; @@ -696,7 +712,7 @@ uikit_gl_view_tick :: (self: *void, _cmd: *void, link: *void) callconv(.c) { // and keyboardLayoutGuide — none eliminated the lag without // cascade-breaking the GL view's frame. if plat.kb_animating { - target_ts := #objc_call(f64)(link, "targetTimestamp"); + target_ts := link.targetTimestamp(); elapsed := target_ts - plat.kb_anim_start; // Negative elapsed can happen if the just-fired willChangeFrame // set kb_anim_start to a wall time AFTER the tick already @@ -723,11 +739,11 @@ uikit_gl_view_tick :: (self: *void, _cmd: *void, link: *void) callconv(.c) { if !plat.has_frame_closure { return; } if !plat.gl_initialized { return; } - dur_d := #objc_call(f64)(link, "duration"); + dur_d := link.duration(); plat.delta_time = xx dur_d; // Stash the targetTimestamp so begin_frame can hand it down to the // game in FrameContext for Metal presentDrawable:atTime:. - plat.last_target_ts = #objc_call(f64)(link, "targetTimestamp"); + plat.last_target_ts = link.targetTimestamp(); fn := plat.frame_closure; fn();