ffi 1.31: uikit_scene_will_connect_ios via #objc_call
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.
This commit is contained in:
@@ -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));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user