The opaque Pixels type wrapped a void* pointing into an SDL_Surface's
pixel buffer, but nothing tied the Pixels lifetime to the surface. If
the GC collected the surface first, the Pixels pointer would dangle,
causing use-after-free.
Replace with two safe alternatives:
- updateTexture now takes a ByteArray (Lean-owned, GC-managed buffer)
plus pitch, matching how Haskell's sdl2 uses ByteString and Rust's
rust-sdl2 uses &[u8]. This is faithful to SDL_UpdateTexture's general
void* interface but requires copying pixels into a ByteArray first.
- updateTextureFromSurface is a new zero-copy convenience that takes an
SDLSurface directly. The C side extracts both pixels and pitch from
the surface internally. Because the surface is borrowed (@&) for the
call's duration, the GC cannot collect it mid-use. This is not
strictly faithful to SDL_UpdateTexture's C API, but is the common
case and avoids an unnecessary memcpy.
Also removes: Pixels type, Pixels.nonemptyType, sdl_pixels_external_class,
sdl_Surface_get_pixels, and the pixels_external_class registration in
sdl_init.
WebcamApp.lean is updated to use updateTextureFromSurface.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The old packing was:
X << 32 | Y << 16 | buttons
where X and Y were full 32-bit values. Y's upper 16 bits (bits 32-47)
overlapped with X (bits 32-63), and buttons (bits 0-31) overlapped with
Y (bits 16-47). This corrupted X whenever Y >= 65536, and corrupted Y
whenever buttons had any bits set above bit 15.
The Lean unpacking side already assumed a non-overlapping layout:
X = bits 63:32, Y = bits 31:16 (masked to 16), buttons = bits 15:0
Now the C side explicitly masks Y and buttons to 16 bits before packing,
matching the Lean expectations. This means Y is limited to [-32768,
32767] which is adequate for screen coordinates and typical relative
mode deltas.
Also fix the SDLIO error type in sdl_set_relative_mouse_mode (same
lean_mk_io_user_error issue fixed in the earlier commit).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sdl_camera_finalizer was a no-op, so SDL cameras opened with
SDL_OpenCamera were never closed. Now calls SDL_CloseCamera.
sdl_camera_spec_finalizer was a no-op, so the SDL_CameraSpec structs
allocated with malloc in sdl_get_camera_format were never freed. Now
calls free().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SDLIO is defined as EIO SDLError, where SDLError is a single-field
structure wrapping String. At runtime, SDLError is represented as just
a String (newtype erasure). Therefore, error returns from C functions
with SDLIO return types should use lean_mk_string(...) directly.
Several functions incorrectly wrapped the error string in
lean_mk_io_user_error(), which creates an IO.Error constructor. When
Lean tried to pattern-match this as SDLError (a String), it would read
the wrong memory layout, causing potential crashes on any error path.
Affected functions: sdl_create_window, sdl_create_window_and_renderer,
sdl_set_render_draw_color, sdl_set_render_draw_color_float,
sdl_render_clear, sdl_render_fill_rect, sdl_create_texture_from_surface
(one of two error paths), sdl_set_track_audio, sdl_play_track.
sdl_render_present is left unchanged because it returns IO Unit (not
SDLIO), so lean_mk_io_user_error is correct there.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two related issues with Lean's Float type being 8 bytes (double), not 4:
1. sdl_render_texture_rect read SDLFRect scalar fields at byte offsets
0,4,8,12 but lean_ctor_get_float reads 8-byte doubles, so the correct
offsets are 0,8,16,24. The old offsets caused each read to straddle
two adjacent fields, producing garbage values for y/w/h.
2. sdl_set_render_draw_color_float declared its parameters as float (4
bytes) but Lean passes Float as double (8 bytes) at the ABI level.
On x86-64, Lean puts doubles in XMM registers and the C function
interpreted the wrong bits—e.g. double 1.0 (0x3FF0000000000000) has
lower 32 bits 0x00000000, so all colors read as 0.
Both functions now use double to match Lean's Float ABI, with explicit
(float) casts where SDL3 APIs expect float.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>