From b3558c327429772df1a53b6e132b68998fa87abf Mon Sep 17 00:00:00 2001 From: agra Date: Tue, 19 May 2026 20:57:09 +0300 Subject: [PATCH] ffi 1.31: uikit_scene_will_connect_ios via #objc_call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The biggest Phase 1D cluster: the iOS scene-lifecycle entry that runs at every launch. UIWindow alloc/init, UIViewController alloc/init, GL view alloc/init/install, root-view-controller wiring, layer access + setOpaque:, EAGL drawable-properties dictionary build, screen/nativeScale DPI scaling, makeKeyAndVisible, UITextField subview install, CADisplayLink construct + addToRunLoop. Every return shape this file uses (void, *void, f64) and every arg shape (BOOL via `xx 0`/`xx 1`, multi-arg selectors `displayLinkWithTarget:selector:` and `setObject:forKey:`) is exercised by this single launch. Net -44 lines on this commit (104 → 60). Also drops a stale `EAGLContext := objc_getClass(...)` decl that wasn't referenced inside this function — EAGL context creation lives in uikit_create_gl_context (already migrated in 1.29). uikit.sx is now 868 lines (-69 cumulative across Phase 1D). iOS-sim chess regression smoke: app launches cleanly, board renders with status-bar clearance, sharp DPI scaling, compositor working, display-link tick driving frames. Every part of the migrated function is on the launch path and all of it succeeds. --- library/modules/platform/uikit.sx | 104 +++++++++--------------------- 1 file changed, 30 insertions(+), 74 deletions(-) diff --git a/library/modules/platform/uikit.sx b/library/modules/platform/uikit.sx index acc9f52..665b0b7 100644 --- a/library/modules/platform/uikit.sx +++ b/library/modules/platform/uikit.sx @@ -496,77 +496,37 @@ uikit_scene_will_connect_ios :: (delegate: *void, scene: *void) { UIViewController := objc_getClass("UIViewController".ptr); SxGLView := objc_getClass("SxGLView".ptr); SxMetalView := objc_getClass("SxMetalView".ptr); - EAGLContext := objc_getClass("EAGLContext".ptr); CADisplayLink := objc_getClass("CADisplayLink".ptr); NSRunLoop := objc_getClass("NSRunLoop".ptr); - sel_alloc := sel_registerName("alloc".ptr); - sel_init := sel_registerName("init".ptr); - sel_init_with_scene := sel_registerName("initWithWindowScene:".ptr); - sel_init_with_frame := sel_registerName("initWithFrame:".ptr); - sel_view := sel_registerName("view".ptr); - sel_set_root_vc := sel_registerName("setRootViewController:".ptr); - sel_make_key_visible := sel_registerName("makeKeyAndVisible".ptr); - sel_add_subview := sel_registerName("addSubview:".ptr); - sel_set_frame := sel_registerName("setFrame:".ptr); - sel_bounds := sel_registerName("bounds".ptr); - sel_set_autoresizing := sel_registerName("setAutoresizingMask:".ptr); - sel_init_with_api := sel_registerName("initWithAPI:".ptr); - sel_set_current_ctx := sel_registerName("setCurrentContext:".ptr); - sel_layer := sel_registerName("layer".ptr); - sel_set_content_scale := sel_registerName("setContentScaleFactor:".ptr); - sel_screen := sel_registerName("screen".ptr); - sel_native_scale := sel_registerName("nativeScale".ptr); - sel_link_with_target := sel_registerName("displayLinkWithTarget:selector:".ptr); - sel_add_to_runloop := sel_registerName("addToRunLoop:forMode:".ptr); - sel_current_runloop := sel_registerName("currentRunLoop".ptr); - sel_tick := sel_registerName("sxTick:".ptr); - - msg_o : (*void, *void) -> *void = xx objc_msgSend; - msg_v : (*void, *void) -> void = xx objc_msgSend; - msg_oo : (*void, *void, *void) -> void = xx objc_msgSend; - msg_ooo : (*void, *void, *void) -> *void = xx objc_msgSend; - msg_oso : (*void, *void, *void, *void) -> *void = xx objc_msgSend; - msg_oi32 : (*void, *void, s32) -> *void = xx objc_msgSend; - msg_oou64 : (*void, *void, u64) -> void = xx objc_msgSend; - // CGFloat-returning msgSend. CGFloat is `double` on 64-bit Apple — reading - // it as f32 reads the low 32 bits of `d0` which isn't a valid float - // representation of the underlying double, so the value comes back as 0. - msg_d : (*void, *void) -> f64 = xx objc_msgSend; - msg_odbl : (*void, *void, f64) -> void = xx objc_msgSend; - - win_raw := msg_o(UIWindow, sel_alloc); - plat.window = msg_ooo(win_raw, sel_init_with_scene, scene); + win_raw := #objc_call(*void)(UIWindow, "alloc"); + plat.window = #objc_call(*void)(win_raw, "initWithWindowScene:", scene); // Make the scene delegate own the window so iOS retains it. Per the // scene-based lifecycle, the scene delegate is expected to provide the // UIWindow via -window/-setWindow:. - sel_set_window := sel_registerName("setWindow:".ptr); - msg_oo(delegate, sel_set_window, plat.window); + #objc_call(void)(delegate, "setWindow:", plat.window); - vc_raw := msg_o(UIViewController, sel_alloc); - plat.root_vc = msg_o(vc_raw, sel_init); + vc_raw := #objc_call(*void)(UIViewController, "alloc"); + plat.root_vc = #objc_call(*void)(vc_raw, "init"); // Allocate either SxGLView or SxMetalView based on gpu_mode and install // it as the VC's view. The view's +layerClass override gives us the // right CAEAGLLayer / CAMetalLayer subclass. Setting it BEFORE // setRootViewController avoids the VC lazy-loading a default view. view_class := if plat.gpu_mode == .gles then SxGLView else SxMetalView; - glv_raw := msg_o(view_class, sel_alloc); - plat.gl_view = msg_o(glv_raw, sel_init); - sel_set_view := sel_registerName("setView:".ptr); - msg_oo(plat.root_vc, sel_set_view, plat.gl_view); + glv_raw := #objc_call(*void)(view_class, "alloc"); + plat.gl_view = #objc_call(*void)(glv_raw, "init"); + #objc_call(void)(plat.root_vc, "setView:", plat.gl_view); - msg_oo(plat.window, sel_set_root_vc, plat.root_vc); + #objc_call(void)(plat.window, "setRootViewController:", plat.root_vc); - plat.gl_layer = msg_o(plat.gl_view, sel_layer); + plat.gl_layer = #objc_call(*void)(plat.gl_view, "layer"); // Mark the layer opaque (no compositor blend). Required for EAGL + // recommended for Metal (CAMetalLayer.opaque defaults to YES but doesn't // hurt to be explicit). - sel_set_opaque := sel_registerName("setOpaque:".ptr); - msg_obool : (*void, *void, u8) -> void = xx objc_msgSend; - msg_obool(plat.gl_layer, sel_set_opaque, 1); + #objc_call(void)(plat.gl_layer, "setOpaque:", xx 1); if plat.gpu_mode == .gles { // EAGL drawable properties dict required by @@ -575,13 +535,8 @@ uikit_scene_will_connect_ios :: (delegate: *void, scene: *void) { // allocation silently fails and the framebuffer reports INCOMPLETE. NSMutableDictionary := objc_getClass("NSMutableDictionary".ptr); NSNumber := objc_getClass("NSNumber".ptr); - sel_dictionary := sel_registerName("dictionary".ptr); - sel_set_obj_for_key := sel_registerName("setObject:forKey:".ptr); - sel_number_bool := sel_registerName("numberWithBool:".ptr); - sel_set_drawable := sel_registerName("setDrawableProperties:".ptr); - msg_oio : (*void, *void, u8) -> *void = xx objc_msgSend; - ns_no := msg_oio(NSNumber, sel_number_bool, 0); + ns_no := #objc_call(*void)(NSNumber, "numberWithBool:", xx 0); // The EAGL dict keys/values must be the framework-provided NSString // constants (pointer identity is checked) — dlsym them from OpenGLES. @@ -589,11 +544,10 @@ uikit_scene_will_connect_ios :: (delegate: *void, scene: *void) { colorformat_key := uikit_extern_nsstring("kEAGLDrawablePropertyColorFormat".ptr); rgba8_value := uikit_extern_nsstring("kEAGLColorFormatRGBA8".ptr); - dict := msg_o(NSMutableDictionary, sel_dictionary); - msg_o3 : (*void, *void, *void, *void) -> void = xx objc_msgSend; - msg_o3(dict, sel_set_obj_for_key, ns_no, retained_key); - msg_o3(dict, sel_set_obj_for_key, rgba8_value, colorformat_key); - msg_oo(plat.gl_layer, sel_set_drawable, dict); + dict := #objc_call(*void)(NSMutableDictionary, "dictionary"); + #objc_call(void)(dict, "setObject:forKey:", ns_no, retained_key); + #objc_call(void)(dict, "setObject:forKey:", rgba8_value, colorformat_key); + #objc_call(void)(plat.gl_layer, "setDrawableProperties:", dict); } // EAGLContext + load_gl were already done in uikit_create_gl_context() @@ -603,35 +557,37 @@ uikit_scene_will_connect_ios :: (delegate: *void, scene: *void) { // Match the layer's drawable scale to the screen's native scale so we get // pixel-accurate rendering on retina displays. CGFloat is `double` on // 64-bit Apple platforms; reading as f32 would clobber the value. - screen := msg_o(plat.window, sel_screen); - scale := msg_d(screen, sel_native_scale); + screen := #objc_call(*void)(plat.window, "screen"); + scale := #objc_call(f64)(screen, "nativeScale"); plat.dpi_scale = xx scale; - msg_odbl(plat.gl_view, sel_set_content_scale, scale); + #objc_call(void)(plat.gl_view, "setContentScaleFactor:", scale); // Renderbuffer is allocated lazily in -[SxGLView layoutSubviews] once // the layer has its real on-screen bounds. makeKeyAndVisible triggers // a layout pass; layoutSubviews calls uikit_setup_renderbuffer. - msg_v(plat.window, sel_make_key_visible); + #objc_call(void)(plat.window, "makeKeyAndVisible"); // Hidden UITextField as the firstResponder source for show_keyboard / // hide_keyboard. Lives as a subview of the GL view so it's in the // responder chain but is sized 0×0 so it can't be tapped. UITextField := objc_getClass("UITextField".ptr); - sel_add_subview := sel_registerName("addSubview:".ptr); - tf_raw := msg_o(UITextField, sel_alloc); - plat.text_field = msg_o(tf_raw, sel_init); - msg_oo(plat.gl_view, sel_add_subview, plat.text_field); + tf_raw := #objc_call(*void)(UITextField, "alloc"); + plat.text_field = #objc_call(*void)(tf_raw, "init"); + #objc_call(void)(plat.gl_view, "addSubview:", plat.text_field); // (Keyboard observer is registered in didFinishLaunching via // uikit_subscribe_keyboard_notifications — it's app-level, not scene- // level, so it doesn't belong here.) - // CADisplayLink: vsync-driven tick into our SxGLView. - plat.display_link = msg_oso(CADisplayLink, sel_link_with_target, plat.gl_view, sel_tick); - runloop := msg_o(NSRunLoop, sel_current_runloop); + // CADisplayLink: vsync-driven tick into our SxGLView. The second arg is + // 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); - msg_oso(plat.display_link, sel_add_to_runloop, runloop, mode); + #objc_call(void)(plat.display_link, "addToRunLoop:forMode:", runloop, mode); NSLog(ns_string("[sx] UIKitPlatform booted\n".ptr)); }