What works on iOS sim now:
- pure-UIKit boot via UIApplicationMain (no SDL3 on iOS)
- SxGLView (CAEAGLLayer) + EAGLContext(GLES3) + CADisplayLink
- GLES3 shader path in modules/ui/renderer.sx (was wasm-only; now
wasm-OR-ios)
- UITouch -> ui.Event translation (mouse_down/moved/up) on touchesBegan/
Moved/Ended/Cancelled. Verified by tapping the chess board: the
expected pawn highlights and its legal moves show as green dots.
- chdir to NSBundle.mainBundle.resourcePath inside UIKitPlatform.init so
the game's relative fopen("assets/...") calls resolve.
Required restructuring to fix four problems discovered along the way:
1. GL context + load_gl must happen BEFORE UIApplicationMain so the
game's pipeline.init (which compiles shaders) doesn't crash on null
function pointers. Pulled EAGLContext creation + load_gl out of
didFinishLaunching: into UIKitPlatform.init via uikit_create_gl_context.
2. UIScreen.nativeScale returns CGFloat (=double on 64-bit Apple).
Reading it through a `(*void, *void) -> f32` msgSend signature
clobbers the value to 0 — the upper 32 bits of d0 land where the f32
reads from. Replaced msg_f with msg_d returning f64 (and added
msg_odbl for setContentScaleFactor: which takes CGFloat).
3. `xx <f64-call-result>` directly assigned to an f32 field through a
sema path lowers as `sitofp` (integer→float) on the double — LLVM
verification rejects it. Workaround: hoist into an `f64` local first.
4. The renderer was selecting the GLSL 330 core shader on every non-wasm
target, including iOS GLES3 where it silently fails to compile and
no quads render. Added OS == .ios to the GLES branch.
Game changes:
- main.sx: g_plat is now a boxed `Platform` (not concrete *SdlPlatform).
Backend chosen per-target via `inline if OS == .ios { ... }`. The
ESC-to-stop handling is OS-guarded (mobile apps don't quit on key
press, and SDL_Keycode references would force-link SDL on iOS).
- build.sx: iOS no longer adds SDL3; it adds UIKit + OpenGLES +
QuartzCore instead.
- delta_time and viewport dims are now mirrored to free globals so the
dock subsystem (`g_dock_delta_time = @g_delta_time`) and build_ui
layout decisions don't need a pointer through the boxed protocol.
Other:
- Added `stop()` to the Platform protocol (no-op on UIKitPlatform).
- examples/66-uikit-platform.sx updated: taps advance the clear color
(red → green → blue) — smoke test for the touch IMP wiring.
- shutdown() on UIKitPlatform is a no-op (mobile apps don't tear down).
Outstanding for next session:
- The Dynamic Island notch overlaps the top of the board because we
haven't read UIView.safeAreaInsets yet (CGRect/UIEdgeInsets struct
returns require a different msgSend ABI than we currently express).
- Keyboard observer (UIKeyboardWillChangeFrameNotification + animation
duration) — the load-bearing iOS feature.
- Real-device codesigning workflow for the new build.
Two more sx compiler bugs to file out of this work:
- xx(f64 call result) → f32 emits sitofp (problem #3 above).
- Inline `#import` inside `inline if` fails to parse (we worked around
by importing both backends unconditionally; the unused-backend's
Obj-C calls are gated by `inline if OS == .ios`).
- library/modules/platform/sdl3.sx: SdlPlatform impl wrapping SDL3 init,
GL context, event pump, swap. run_frame_loop owns the loop: while loop
on desktop, emscripten_set_main_loop on WASM. Registers an event-watch
that re-invokes the frame closure during macOS modal resize-drag so
content keeps rendering at the new size. safe_insets / keyboard /
show_keyboard / hide_keyboard are no-ops (these targets have no soft
keyboard).
Two compiler bug repros uncovered during the refactor:
- examples/issue-0020.sx: global `Foo = .{}` zero-initializes, ignoring
struct field defaults. Local `Foo = .{}` correctly applies defaults.
Workaround: set fields explicitly in an init method or heap-allocate
the value.
- examples/issue-0021.sx: an enclosing function's return type bleeds
into `xx`'s target type inside an `if-then-else` expression on the
RHS of a struct-field assignment. The same expression in a `-> void`
function produces the right value; in a `-> bool` function it
silently produces 0. Bit the SX Chess game's dpi_scale calc inside
`SdlPlatform.init` (returns bool), making all text labels render
invisibly on retina. Workaround: hoist each `xx` cast into its own
f32 local.
Regression gate: 50/50 examples pass, macOS chess game runs at ~2700fps
(close to the pre-refactor 2900 baseline), WASM build still emits a
working .html/.js/.wasm/.data quad.