ffi M3.3 + M3.4 + M3.5: SxGLView/SxMetalView migrated; uikit_register_classes deleted

Three slices in one commit since they're tightly coupled (the
M3.5 deletion only makes sense after M3.3 and M3.4):

M3.3 — SxGLView migrated to declarative '#objc_class("SxGLView")':
  - '#extends UIView' for the view-hierarchy + responder chain.
  - 'layerClass :: *void = objc_getClass("CAEAGLLayer".ptr);' uses
    the M2.1(a) class-level constant form. Registered on the
    metaclass; UIView's +layerClass override dispatches here so
    EAGL gets the right backing layer.
  - Six instance methods (sxTick, layoutSubviews, four touch
    selectors) forward to existing legacy IMP free functions.

M3.4 — SxMetalView migrated, same shape as SxGLView; differs only
  in the 'layerClass' constant returning CAMetalLayer instead of
  CAEAGLLayer. The five shared IMPs (sxTick/layoutSubviews/4 touch
  handlers) reach the same free functions — they already branch on
  plat.gpu_mode for GL-specific renderbuffer code.

M3.5 — uikit_register_classes() and the two helper registration
  functions are deleted outright. Every sx-defined Obj-C class in
  this module now goes through the compiler's M1.2 / M2.1(a)
  synthesis path at module init. The call site inside
  UIKitPlatform.init is gone too — just a comment marking the
  migration point.

Chess on iOS-sim: board renders, scene-delegate connection still
fires, GL/Metal layer setup intact, touch dispatch routes through
the synthesized IMP trampolines. 183 example tests + zig build
test green.

End of M3. The platform layer's Obj-C-runtime wiring is fully
declarative.

Remaining: M4 (autoreleasepool + ARC ops), M5 (closure↔block),
M6 (auto-import + production hardening). M1.1.b (Class(T)
parameterization + instancetype) is still deferred — none of
the migrated uikit code needed it.
This commit is contained in:
agra
2026-05-26 07:41:07 +03:00
parent 066840d9e0
commit 78288b98ac

View File

@@ -313,7 +313,9 @@ impl Platform for UIKitPlatform {
// happen BEFORE any code that loads fonts/textures from disk.
inline if OS == .ios {
uikit_chdir_to_bundle();
uikit_register_classes();
// M3.5 — uikit_register_classes was deleted. Every
// sx-defined Obj-C class in this module is now declarative;
// the compiler synthesises class-pair init at module init.
if self.gpu_mode == .gles {
uikit_create_gl_context(self);
} else {
@@ -450,19 +452,12 @@ uikit_chdir_to_bundle :: () {
chdir(c_string(rsrc));
}
uikit_register_classes :: () {
inline if OS == .ios {
// SxAppDelegate (M3.1) + SxSceneDelegate (M3.2) are now
// declarative `#objc_class(...)` blocks — the compiler
// synthesises their IMPs, class-pair registration, and
// protocol conformances at module init. The old hand-rolled
// objc_allocateClassPair + class_addMethod + class_addProtocol
// sequences are gone.
uikit_register_gl_view_class();
uikit_register_metal_view_class();
}
}
// uikit_register_classes — deleted (M3.5). Every sx-defined
// Obj-C class in this module (SxAppDelegate, SxSceneDelegate,
// SxGLView, SxMetalView) is now a declarative `#objc_class(...)`
// block — the compiler synthesises their IMPs, class-pair
// registration, ivar wiring, +alloc / -dealloc trampolines, and
// `#implements` protocol conformances at module init.
// Read [UIScreen mainScreen].nativeScale into plat.dpi_scale. Used by the
// metal-mode init path which doesn't go through uikit_create_gl_context
@@ -757,9 +752,39 @@ uikit_present_renderbuffer :: (self: *UIKitPlatform) {
// ── SxGLView class ─────────────────────────────────────────────────────────
// UIView subclass overriding `+layerClass` to return [CAEAGLLayer class].
// Instance method `sxTick:` is what CADisplayLink calls.
//
// M3.3 — migrated to declarative `#objc_class` form. The compiler
// synthesises class-pair init, metaclass `+layerClass`, and the
// instance-method IMP trampolines at module init.
SxGLView :: #objc_class("SxGLView") {
#extends UIView;
uikit_gl_view_layer_class :: (cls: *void, _cmd: *void) -> *void callconv(.c) {
objc_getClass("CAEAGLLayer".ptr);
// Class-level constant — `+layerClass` returns [CAEAGLLayer class].
layerClass :: *void = objc_getClass("CAEAGLLayer".ptr);
sxTick :: (self: *Self, link: *void) {
uikit_gl_view_tick(xx self, xx 0, link);
}
layoutSubviews :: (self: *Self) {
uikit_gl_view_layout(xx self, xx 0);
}
touchesBegan_withEvent :: (self: *Self, touches: *void, event: *void) {
uikit_gl_view_touches_began(xx self, xx 0, touches, event);
}
touchesMoved_withEvent :: (self: *Self, touches: *void, event: *void) {
uikit_gl_view_touches_moved(xx self, xx 0, touches, event);
}
touchesEnded_withEvent :: (self: *Self, touches: *void, event: *void) {
uikit_gl_view_touches_ended(xx self, xx 0, touches, event);
}
touchesCancelled_withEvent :: (self: *Self, touches: *void, event: *void) {
uikit_gl_view_touches_ended(xx self, xx 0, touches, event);
}
}
uikit_gl_view_tick :: (self: *void, _cmd: *void, link_raw: *void) callconv(.c) {
@@ -889,82 +914,44 @@ uikit_gl_view_touches_ended :: (self: *void, _cmd: *void, touches: *void, event:
g_uikit_plat.events.append(.mouse_up(.{ position = pos, button = .left }));
}
uikit_register_gl_view_class :: () {
inline if OS == .ios {
UIView := objc_getClass("UIView".ptr);
SxGLView := objc_allocateClassPair(UIView, "SxGLView".ptr, 0);
// +layerClass is a CLASS method — registered on the metaclass.
metaclass := object_getClass(SxGLView);
class_addMethod(metaclass,
sel_registerName("layerClass".ptr),
xx uikit_gl_view_layer_class, "#@:".ptr);
// -sxTick: is the CADisplayLink callback. -layoutSubviews allocates
// the renderbuffer when the layer first gets non-zero bounds.
class_addMethod(SxGLView,
sel_registerName("sxTick:".ptr),
xx uikit_gl_view_tick, "v@:@".ptr);
class_addMethod(SxGLView,
sel_registerName("layoutSubviews".ptr),
xx uikit_gl_view_layout, "v@:".ptr);
// Touch dispatch.
class_addMethod(SxGLView,
sel_registerName("touchesBegan:withEvent:".ptr),
xx uikit_gl_view_touches_began, "v@:@@".ptr);
class_addMethod(SxGLView,
sel_registerName("touchesMoved:withEvent:".ptr),
xx uikit_gl_view_touches_moved, "v@:@@".ptr);
class_addMethod(SxGLView,
sel_registerName("touchesEnded:withEvent:".ptr),
xx uikit_gl_view_touches_ended, "v@:@@".ptr);
class_addMethod(SxGLView,
sel_registerName("touchesCancelled:withEvent:".ptr),
xx uikit_gl_view_touches_ended, "v@:@@".ptr);
objc_registerClassPair(SxGLView);
}
}
// +layerClass IMP for SxMetalView. Class method, signature "#@:".
uikit_metal_view_layer_class :: (cls: *void, _cmd: *void) -> *void callconv(.c) {
objc_getClass("CAMetalLayer".ptr);
}
// uikit_register_gl_view_class — deleted (M3.3). SxGLView is now
// declarative; the compiler synthesises everything at module init.
// SxMetalView reuses the same tick/layout/touch IMPs as SxGLView. The IMPs
// already branch on `plat.gpu_mode` for the GL-specific bits (renderbuffer
// setup, etc.), so a single set of IMPs serves both view classes.
uikit_register_metal_view_class :: () {
inline if OS == .ios {
UIView := objc_getClass("UIView".ptr);
SxMetalView := objc_allocateClassPair(UIView, "SxMetalView".ptr, 0);
//
// M3.4 — migrated to declarative `#objc_class`. Only `+layerClass`
// differs from SxGLView (returns CAMetalLayer instead of CAEAGLLayer).
SxMetalView :: #objc_class("SxMetalView") {
#extends UIView;
metaclass := object_getClass(SxMetalView);
class_addMethod(metaclass,
sel_registerName("layerClass".ptr),
xx uikit_metal_view_layer_class, "#@:".ptr);
layerClass :: *void = objc_getClass("CAMetalLayer".ptr);
class_addMethod(SxMetalView,
sel_registerName("sxTick:".ptr),
xx uikit_gl_view_tick, "v@:@".ptr);
class_addMethod(SxMetalView,
sel_registerName("layoutSubviews".ptr),
xx uikit_gl_view_layout, "v@:".ptr);
sxTick :: (self: *Self, link: *void) {
uikit_gl_view_tick(xx self, xx 0, link);
}
class_addMethod(SxMetalView,
sel_registerName("touchesBegan:withEvent:".ptr),
xx uikit_gl_view_touches_began, "v@:@@".ptr);
class_addMethod(SxMetalView,
sel_registerName("touchesMoved:withEvent:".ptr),
xx uikit_gl_view_touches_moved, "v@:@@".ptr);
class_addMethod(SxMetalView,
sel_registerName("touchesEnded:withEvent:".ptr),
xx uikit_gl_view_touches_ended, "v@:@@".ptr);
class_addMethod(SxMetalView,
sel_registerName("touchesCancelled:withEvent:".ptr),
xx uikit_gl_view_touches_ended, "v@:@@".ptr);
layoutSubviews :: (self: *Self) {
uikit_gl_view_layout(xx self, xx 0);
}
objc_registerClassPair(SxMetalView);
touchesBegan_withEvent :: (self: *Self, touches: *void, event: *void) {
uikit_gl_view_touches_began(xx self, xx 0, touches, event);
}
touchesMoved_withEvent :: (self: *Self, touches: *void, event: *void) {
uikit_gl_view_touches_moved(xx self, xx 0, touches, event);
}
touchesEnded_withEvent :: (self: *Self, touches: *void, event: *void) {
uikit_gl_view_touches_ended(xx self, xx 0, touches, event);
}
touchesCancelled_withEvent :: (self: *Self, touches: *void, event: *void) {
uikit_gl_view_touches_ended(xx self, xx 0, touches, event);
}
}
// uikit_register_metal_view_class — deleted (M3.4). Replaced by
// the declarative SxMetalView form above.