ffi M3.2: SxSceneDelegate migrated + #implements protocol conformance

Migrates SxSceneDelegate from the hand-rolled
objc_allocateClassPair + class_addMethod + class_addProtocol
sequence to the declarative form:

  SxSceneDelegate :: #objc_class("SxSceneDelegate") {
      #extends UIResponder;
      #implements UISceneDelegate;
      #implements UIWindowSceneDelegate;

      scene_willConnectToSession_options :: (self, scene, session, options) { ... }
      window    :: (self) -> *void { ... }
      setWindow :: (self, w) { ... }
  }

emit_llvm now honors '#implements' in the class-pair init
constructor — for each #implements ProtocolAlias on the cache
entry's AST, emit before objc_registerClassPair:

  proto = objc_getProtocol("ProtocolName")
  class_addProtocol(cls, proto)

iOS checks 'class_conformsToProtocol' when instantiating scene
delegates; without the conformance the runtime silently rejects
the class and a default scene with no delegate gets created
instead. The protocol-getter returns null on dead-strip /
runtime mismatch (rare but possible) — the runtime treats
class_addProtocol(cls, null) as a no-op, so no explicit null
check needed.

Method bodies forward to the existing legacy free IMP functions
(uikit_scene_will_connect, uikit_window_getter,
uikit_window_setter) so we don't have to inline the scene-
connect setup logic (~80 lines).

uikit_register_classes is now tiny — just the two remaining
view-class helpers (M3.3 SxGLView + M3.4 SxMetalView). M3.5
deletes the function entirely once those land.

Chess on iOS-sim: board renders, scene delegate fires, touch
events route correctly. 183 example tests + zig build test
green.
This commit is contained in:
agra
2026-05-26 07:37:14 +03:00
parent 66f84f67b8
commit 066840d9e0
3 changed files with 56 additions and 43 deletions

View File

@@ -197,6 +197,30 @@ SxAppDelegate :: #objc_class("SxAppDelegate") {
init :: (self: *SxAppDelegate) -> *SxAppDelegate;
}
// SxSceneDelegate — iOS 13+ scene-based lifecycle delegate.
// UIApplicationSceneManifest names this in Info.plist; iOS
// instantiates it via scene-session connection. Replaces the M3.2
// hand-rolled registration. Two `#implements` declarations
// formally conform to the scene-delegate protocols — iOS rejects
// the class otherwise.
SxSceneDelegate :: #objc_class("SxSceneDelegate") {
#extends UIResponder;
#implements UISceneDelegate;
#implements UIWindowSceneDelegate;
scene_willConnectToSession_options :: (self: *Self, scene: *void, session: *void, options: *void) {
uikit_scene_will_connect(xx self, xx 0, scene, session, options);
}
window :: (self: *Self) -> *void {
return uikit_window_getter(xx self, xx 0);
}
setWindow :: (self: *Self, w: *void) {
uikit_window_setter(xx self, xx 0, w);
}
}
// 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;
@@ -428,49 +452,12 @@ uikit_chdir_to_bundle :: () {
uikit_register_classes :: () {
inline if OS == .ios {
// SxAppDelegate is now declared as `#objc_class("SxAppDelegate")`
// (M3.1) — the compiler synthesises its IMPs and class-pair
// registration at module init. The old hand-rolled
// objc_allocateClassPair + class_addMethod sequence is gone.
UIResponder_cls := objc_getClass("UIResponder".ptr);
// SxSceneDelegate handles the per-scene UI setup. iOS 13+ scene-based
// lifecycle: didFinishLaunching is too early for the window — the
// UIWindowScene doesn't connect until `scene:willConnectTo:options:`.
// The class is named in Info.plist's UIApplicationSceneManifest →
// UISceneDelegateClassName.
SxSceneDelegate := objc_allocateClassPair(UIResponder_cls, "SxSceneDelegate".ptr, 0);
class_addMethod(SxSceneDelegate,
sel_registerName("scene:willConnectToSession:options:".ptr),
xx uikit_scene_will_connect, "v@:@@@".ptr);
class_addMethod(SxSceneDelegate,
sel_registerName("window".ptr),
xx uikit_window_getter, "@@:".ptr);
class_addMethod(SxSceneDelegate,
sel_registerName("setWindow:".ptr),
xx uikit_window_setter, "v@:@".ptr);
// Formal protocol conformance is required for UISceneDelegate
// (iOS checks -[cls conformsToProtocol:@protocol(UISceneDelegate)]
// before instantiating; without it the class is silently rejected
// with "does not conform to the UISceneDelegate protocol" in the
// log and a default scene with no delegate is created instead).
// Add the protocol BEFORE registerClassPair — the runtime locks
// the class layout after registration.
UISceneDelegateProto := objc_getProtocol("UISceneDelegate".ptr);
UIWindowSceneDelegateProto := objc_getProtocol("UIWindowSceneDelegate".ptr);
if UISceneDelegateProto != null {
class_addProtocol(SxSceneDelegate, UISceneDelegateProto);
} else {
NSLog(ns_string("[sx] WARN: UISceneDelegate protocol not found (dead-stripped)\n".ptr));
}
if UIWindowSceneDelegateProto != null {
class_addProtocol(SxSceneDelegate, UIWindowSceneDelegateProto);
}
objc_registerClassPair(SxSceneDelegate);
// 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();