ffi 3.2 C5: migrate uikit.sx view tree + GL drawables cluster

Final Phase 3.2 cluster. CALayer / CAEAGLLayer / EAGLContext declared
as `#foreign #objc_class` blocks, plus `setContentScaleFactor` added
to UIView and `-layer` now returns `*CALayer` (was opaque `*void`).

Classes declared:

- CALayer      → setOpaque (instance)
- CAEAGLLayer  → setDrawableProperties (instance)
- EAGLContext  → alloc (class), initWithAPI (instance),
                 setCurrentContext (class — takes EAGLContext arg),
                 renderbufferStorage_fromDrawable (instance),
                 presentRenderbuffer (instance)
- UIView       → +setContentScaleFactor (existing decl extended)

The C5 group sits above UIView in the file so the `-layer` return type
`*CALayer` forward-resolves cleanly.

Migration sites in uikit.sx:

- `uikit_create_gl_context` → `EAGLContext.alloc().initWithAPI(api)`
  + `EAGLContext.setCurrentContext(ctx)`.
- `uikit_setup_renderbuffer` → cast `*EAGLContext` and
  `gl_ctx.renderbufferStorage_fromDrawable(target, layer)`.
- `uikit_present_renderbuffer` → same cast + `presentRenderbuffer(target)`.
- Scene-connect bring-up: `gl_layer.setOpaque(1)`,
  `eagl_layer.setDrawableProperties(...)`,
  `gl_view.setContentScaleFactor(scale)`.

One more `objc_getClass(...)` lookup (EAGLContext) retired — the class
slot comes from the declarative binding via `__sx_objc_class_init`.

**Phase 3.2 complete.** Five clusters migrated (C1: Foundation
utility; C2: Notifications + Bundle; C3: RunLoop + display timing;
C4: UIKit chrome; C5: view tree + GL drawables). 8 foreign Cocoa
classes declared; ~30 `#objc_call(T)(...)` sites rewritten to
`recv.method(args)` / `Cls.method(args)`; 6 `objc_getClass`
lookups retired. Sx-defined classes (SxAppDelegate, SxSceneDelegate,
SxGLView, SxMetalView) and a handful of foreign sites that exercise
delegate/protocol surfaces stay on the explicit `#objc_call` form
pending Phase 3.7's class synthesis.

167/167 example tests; chess clean on macOS / iOS sim / Android via
`tools/verify-step.sh`.
This commit is contained in:
agra
2026-05-25 17:58:55 +03:00
parent 5b4969f9be
commit bd3033dc5a
2 changed files with 74 additions and 36 deletions

View File

@@ -600,27 +600,46 @@ matching the expected output.
Phase 3.2 C4 landed: UIKit chrome cluster migrated. Six classes
declared (UIScreen, UIView, UIWindow, UIViewController, UITextField
— plus the existing C1/C2/C3 classes already in place). Migration
sites: `show_keyboard` / `hide_keyboard` use `tf.becomeFirstResponder()`
/ `tf.resignFirstResponder()`; `uikit_refresh_safe_insets` uses
`gl_view.safeAreaInsets()`; `uikit_read_screen_scale` and the GL-
context bring-up both use `UIScreen.mainScreen().nativeScale()`;
keyboard-frame callback uses `win.screen().bounds()`; the scene-
connect bring-up chains `UIWindow.alloc().initWithWindowScene(scene)`
and `UIViewController.alloc().init()` then `vc.setView(...)`,
`win.setRootViewController(...)`, `gl_view.layer()`,
`UITextField.alloc().init()`, `gl_view.addSubview(...)`,
`win.makeKeyAndVisible()`. Three `objc_getClass(...)` calls (UIWindow,
UIViewController, UITextField) are gone — the class slots come from
the declarative bindings via `__sx_objc_class_init`. C4 is the
cluster that triggered issue-0043; with the fix in, the chained
dispatch resolves correctly under lazy lowering. 167/167 tests +
chess clean on macOS / iOS sim / Android.
— plus the existing C1/C2/C3 classes already in place). Three
`objc_getClass(...)` calls (UIWindow, UIViewController, UITextField)
are gone — the class slots come from the declarative bindings via
`__sx_objc_class_init`. C4 is the cluster that triggered
issue-0043; with the fix in, the chained dispatch resolves
correctly under lazy lowering.
Phase 3.2 C5 landed: view tree + GL drawables cluster migrated.
CALayer (`setOpaque`), CAEAGLLayer (`setDrawableProperties`), and
EAGLContext (`alloc`, `initWithAPI`, `setCurrentContext`,
`renderbufferStorage_fromDrawable`, `presentRenderbuffer`) declared.
UIView gained `setContentScaleFactor` and `layer` now returns
`*CALayer` (was opaque `*void`). Migration sites:
`uikit_create_gl_context` uses `EAGLContext.alloc().initWithAPI(...)`
then `EAGLContext.setCurrentContext(ctx)`;
`uikit_setup_renderbuffer` uses
`gl_ctx.renderbufferStorage_fromDrawable(...)`;
`uikit_present_renderbuffer` uses `gl_ctx.presentRenderbuffer(...)`;
the scene-connect bring-up uses `gl_layer.setOpaque(1)`,
`eagl_layer.setDrawableProperties(...)`, and
`gl_view.setContentScaleFactor(scale)`. One more `objc_getClass`
(EAGLContext) gone. 167/167 + chess clean on macOS / iOS sim /
Android.
**Phase 3.2 complete.** Surface summary:
- `#selector("explicit:")` override (parts A1+A2).
- Locked-in golden mangling-table test (part B).
- Five uikit.sx clusters migrated to declarative `#objc_class`
(parts C1..C5) — 8 foreign Cocoa classes declared, 30+
`#objc_call` call sites rewritten to `recv.method(args)` /
`Cls.method(args)` form. 6 redundant `objc_getClass(...)` lookups
retired. Sx-defined classes (SxAppDelegate, SxSceneDelegate,
SxGLView, SxMetalView) and a handful of foreign sites that
exercise less common paths (e.g. `objc_call(void)(delegate,
"setWindow:", ...)` from UIWindowSceneDelegate protocol) stay on
the explicit `#objc_call` form pending Phase 3.7's class-synthesis
work.
Open work:
- **Phase 3 step 3.2 — C5** — uikit.sx migration (view tree + GL
drawables: CAEAGLLayer, EAGLContext, plus any remaining
CAMetalLayer / NSTimer sites).
- **Phase 3 step 3.3** — `property name: Type` synthesizes
`inst.name``[inst name]` getter and `inst.name = x`
`[inst setName: x]` setter. `#setter("...")` overrides the setter

View File

@@ -113,6 +113,25 @@ CADisplayLink :: #foreign #objc_class("CADisplayLink") {
duration :: (self: *Self) -> f64;
}
// ── View tree + GL drawables (Phase 3.2 C5) ────────────────────────────
// (Declared before UIView so `layer :: (...) -> *CALayer` can reference it.)
CALayer :: #foreign #objc_class("CALayer") {
setOpaque :: (self: *Self, opaque: s8);
}
CAEAGLLayer :: #foreign #objc_class("CAEAGLLayer") {
setDrawableProperties :: (self: *Self, props: *void);
}
EAGLContext :: #foreign #objc_class("EAGLContext") {
alloc :: () -> *EAGLContext;
initWithAPI :: (self: *Self, api: s32) -> *EAGLContext;
setCurrentContext :: (ctx: *EAGLContext);
renderbufferStorage_fromDrawable :: (self: *Self, target: u32, drawable: *void) -> s8;
presentRenderbuffer :: (self: *Self, target: u32) -> s8;
}
// ── UIKit chrome (Phase 3.2 C4) ────────────────────────────────────────
UIScreen :: #foreign #objc_class("UIScreen") {
@@ -122,11 +141,10 @@ UIScreen :: #foreign #objc_class("UIScreen") {
}
UIView :: #foreign #objc_class("UIView") {
safeAreaInsets :: (self: *Self) -> UIEdgeInsets;
addSubview :: (self: *Self, view: *void);
// `-layer` returns a CALayer*; declared as opaque pointer for now
// (CALayer's own declarative binding is in the C5 cluster).
layer :: (self: *Self) -> *void;
safeAreaInsets :: (self: *Self) -> UIEdgeInsets;
addSubview :: (self: *Self, view: *void);
layer :: (self: *Self) -> *CALayer;
setContentScaleFactor :: (self: *Self, scale: f64);
}
UIWindow :: #foreign #objc_class("UIWindow") {
@@ -526,17 +544,14 @@ uikit_keyboard_will_change_frame :: (self: *void, _cmd: *void, notification: *vo
uikit_create_gl_context :: (plat: *UIKitPlatform) {
inline if OS != .ios { return; }
EAGLContext := objc_getClass("EAGLContext".ptr);
// UIScreen class slot comes from the declarative #objc_class binding.
// Read the screen scale up-front so callers can size font caches and
// textures with the right DPI before the window even exists.
screen := UIScreen.mainScreen();
plat.dpi_scale = xx screen.nativeScale();
ctx_raw := #objc_call(*void)(EAGLContext, "alloc");
plat.gl_ctx = #objc_call(*void)(ctx_raw, "initWithAPI:", EAGL_API_GLES3);
#objc_call(void)(EAGLContext, "setCurrentContext:", plat.gl_ctx);
ctx := EAGLContext.alloc().initWithAPI(EAGL_API_GLES3);
plat.gl_ctx = xx ctx;
EAGLContext.setCurrentContext(ctx);
load_gl(@ios_gl_proc);
}
@@ -611,12 +626,13 @@ uikit_scene_will_connect_ios :: (delegate: *void, scene: *void) {
win.setRootViewController(plat.root_vc);
gl_view : *UIView = xx plat.gl_view;
plat.gl_layer = gl_view.layer();
gl_layer := gl_view.layer();
plat.gl_layer = xx gl_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).
#objc_call(void)(plat.gl_layer, "setOpaque:", xx 1);
gl_layer.setOpaque(1);
if plat.gpu_mode == .gles {
// EAGL drawable properties dict required by
@@ -634,7 +650,8 @@ uikit_scene_will_connect_ios :: (delegate: *void, scene: *void) {
dict := NSMutableDictionary.dictionary();
dict.setObject_forKey(xx ns_no, retained_key);
dict.setObject_forKey(rgba8_value, colorformat_key);
#objc_call(void)(plat.gl_layer, "setDrawableProperties:", xx dict);
eagl_layer : *CAEAGLLayer = xx gl_layer;
eagl_layer.setDrawableProperties(xx dict);
}
// EAGLContext + load_gl were already done in uikit_create_gl_context()
@@ -647,7 +664,7 @@ uikit_scene_will_connect_ios :: (delegate: *void, scene: *void) {
screen2 := win.screen();
scale := screen2.nativeScale();
plat.dpi_scale = xx scale;
#objc_call(void)(plat.gl_view, "setContentScaleFactor:", scale);
gl_view.setContentScaleFactor(scale);
// Renderbuffer is allocated lazily in -[SxGLView layoutSubviews] once
// the layer has its real on-screen bounds. makeKeyAndVisible triggers
@@ -691,7 +708,8 @@ uikit_setup_renderbuffer :: (plat: *UIKitPlatform) {
glBindRenderbuffer(GL_RENDERBUFFER, plat.color_renderbuffer);
#objc_call(bool)(plat.gl_ctx, "renderbufferStorage:fromDrawable:", GL_RENDERBUFFER, plat.gl_layer);
gl_ctx : *EAGLContext = xx plat.gl_ctx;
gl_ctx.renderbufferStorage_fromDrawable(GL_RENDERBUFFER, plat.gl_layer);
glBindFramebuffer(GL_FRAMEBUFFER, plat.framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, plat.color_renderbuffer);
@@ -723,7 +741,8 @@ uikit_setup_renderbuffer :: (plat: *UIKitPlatform) {
uikit_present_renderbuffer :: (self: *UIKitPlatform) {
inline if OS != .ios { return; }
glBindRenderbuffer(GL_RENDERBUFFER, self.color_renderbuffer);
#objc_call(bool)(self.gl_ctx, "presentRenderbuffer:", GL_RENDERBUFFER);
gl_ctx : *EAGLContext = xx self.gl_ctx;
gl_ctx.presentRenderbuffer(GL_RENDERBUFFER);
}
// ── SxGLView class ─────────────────────────────────────────────────────────