End-to-end on iOS sim: UIKitPlatform boots an SxAppDelegate, installs
an SxGLView (UIView subclass overriding +layerClass to return
CAEAGLLayer) as the root view controller's view, sets the drawable
properties (EAGLColorFormatRGBA8, non-retained backing — looked up by
dlsym so pointer-identity-checked constants match), creates an
EAGLContext (GLES3), and registers a CADisplayLink that invokes the
user's frame closure on every vsync. end_frame presents the
renderbuffer via [EAGLContext presentRenderbuffer:].
The renderbuffer is allocated lazily in -[SxGLView layoutSubviews] once
the layer has its real on-screen bounds — allocating earlier (e.g. in
didFinishLaunching) failed with INCOMPLETE_ATTACHMENT because the
SxGLView's frame was still zero at that point. Setting the SxGLView
as the VC's `view` (via setView:) lets the standard VC layout pipeline
size it to the window without us having to read CGRect struct returns
from objc_msgSend.
EAGL drawableProperties dict keys/values are dlsym'd from OpenGLES —
the framework checks them by pointer identity, so synthesized NSString
literals with the same contents don't work.
examples/66-uikit-platform.sx — runnable smoke test that cycles the
screen color (red → green → blue every 30 frames) so you can confirm
the display-link tick and present pipeline.
modules/opengl.sx gains glGenFramebuffers, glGenRenderbuffers,
glBindFramebuffer, glBindRenderbuffer, glFramebufferRenderbuffer,
glGetRenderbufferParameteriv, glCheckFramebufferStatus — needed for
the iOS GLES FBO-to-renderbuffer setup. They're wired into load_gl
so SDL and the iOS dlsym loader both pick them up.
Compiles cleanly on macOS / WASM / iOS-sim. Non-iOS targets never
reference the unresolved UIKit/QuartzCore/OpenGLES symbols because
every Obj-C touch lives inside `inline if OS == .ios`.
Game's iOS path still goes through SDL3 for now. Touch events + game
wire-up + keyboard observer = next steps.
- 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.
First commit of the Phase 8 platform abstraction (see current/PLATFORM_PLAN.md):
- `library/modules/platform/types.sx` — `FrameContext` (viewport_w/h,
pixel_w/h, dpi_scale, delta_time) and `KeyboardState` (visible, height
+ `zero()`). `EdgeInsets`/`Point`/`Size` and `Event` are reused from
`modules/ui/types.sx` / `modules/ui/events.sx`.
- `library/modules/platform/api.sx` — `Platform :: protocol { init,
run_frame_loop, poll_events, begin_frame, end_frame, safe_insets,
keyboard, show_keyboard, hide_keyboard, shutdown }`. Protocol bodies
omit `self` (matches the `View`/`Allocator` convention).
- `run_frame_loop` takes `Closure()` so backends own the run loop:
SDL drives a `while !quit`, UIKit hands it to a `CADisplayLink` tick,
Emscripten hands it to `emscripten_set_main_loop`.
No backend yet. Regression suite still 50/50; game build still green.
- `-F <dir>` CLI flag adds Apple framework search paths (parallel to `-L`).
- `TargetConfig.framework_paths` flows into the iOS link line (`-F<dir>`).
- iOS link adds `-Wl,-rpath,@executable_path/Frameworks` so embedded
frameworks resolve at runtime.
- `createBundle` now takes the framework list; for each one it locates
`<name>.framework` in the `-F` paths and `cp -R`s it into
`<bundle>.app/Frameworks/`.
- `c_import.compileCToObjects` forwards `-target`/`-isysroot` to clang so
`#c_import` works under cross-compile (was using host clang implicitly).
- iOS SDK is auto-discovered once at startup and shared by both the C
compile and the link paths.
- `SX_DEBUG_LINK=1` prints the resolved link argv.
- `library/modules/sdl3.sx`: drop `#library "SDL3"` — linking is now
per-target (build.sx handles `-lSDL3` on macOS, `-framework SDL3` on iOS).
20 files (~3,830 lines): view protocol, layout, renderer, glyph cache,
fonts, gestures, animation, scroll, stacks, modifiers, etc.
Internal imports rewritten from "ui/..." to "modules/ui/...".
Consumers now `#import "modules/ui"` from any project; no symlink
hacks needed. Verified by compiling game/main.sx without its local
ui/ — resolves via the Phase 6 stdlib fallback.
- examples/modules/ -> library/modules/ (top-level, no more
symlink hacks in consumer projects)
- compiler discovers stdlib via _NSGetExecutablePath / readlink
/proc/self/exe; searches dev layout (../../library), install
layout (../library), and alongside-binary fallback
- SX_STDLIB_PATH env var overrides for tests / dev convenience
- SX_DEBUG_STDLIB env var dumps the discovery results
- build.zig installs library/ alongside the binary
- Compilation gains stdlib_paths field threaded through resolveImports
- 50 tests pass; consumer projects can now build from any cwd