Skip to content

Split SDL3 into SDL3.Raw and SDL3.Wrapped#16

Open
klukaszek wants to merge 3 commits into
mainfrom
raw-wrapped-migration
Open

Split SDL3 into SDL3.Raw and SDL3.Wrapped#16
klukaszek wants to merge 3 commits into
mainfrom
raw-wrapped-migration

Conversation

@klukaszek

@klukaszek klukaszek commented Mar 13, 2026

Copy link
Copy Markdown
Owner

Summary

This PR restructures the library around explicit SDL3.Raw.* and SDL3.Wrapped.* layers.

The main goals are:

  • Preserve a direct raw SDL binding surface
  • Provide a higher-level wrapped surface for the default SDL3.* modules
  • Move the public API toward MonadIO where appropriate without forcing users to think in Haskell-specific resource vocabulary
  • Keep SDL concepts and naming as the primary mental model

What Changed

  • Added a full SDL3.Raw.* module tree for low-level FFI-shaped bindings.
  • Added a full SDL3.Wrapped.* module tree for the higher-level/default API.
  • Kept SDL3.* modules as compatibility-facing modules that re-export the wrapped layer.
  • Added top-level SDL3.Raw as an umbrella re-export for migration users who want the raw layer directly.
  • Removed stale DISTRIBUTION.md references from the repo.

API / Behavior Notes

This is not just a directory move. Some modules needed real follow-up design work during the split.

Examples:

  • Surface no longer defaults to exposing raw surface pointers through the wrapped API.
  • Callback-heavy modules like Hints, Events, System, Mouse, Tray, and Audio now manage callback lifetime more deliberately in the wrapped layer.
  • Modules like IOStream, Process, Storage, Camera, Gamepad, Haptic, AsyncIO, and Mutex were normalized so wrapped code does not default to Ptr SDL... handles for owned SDL objects.
  • Init got a callback cleanup fix for sdlRunOnMainThread.
  • Hints and Events had callback-removal correctness issues fixed as part of the split.
  • Tray and Surface integration was updated so tray icons use the wrapped surface API cleanly.

Examples

The examples were updated to keep building and to use the new wrapped API surface where needed, especially around GPU/surface usage.

Verification

Verified during this work with:

  • cabal build lib:sdl3
  • full example builds
  • targeted cabal run smoke tests including wrapped non-GPU and GPU examples
  • a standalone SDL3.Raw import smoke test compiled through Cabal

Follow-Up

This is intentionally being left in limbo for review because it is a large structural PR with broad API impact. I experimented with both Claude and Codex while working on this over the course of a bit. Any criticisms, concerns, and comments are welcome, as I was not even sure if I originally wanted to release this branch.

Things still worth a follow-up:

  • Whether SDL3.Raw should be documented more explicitly in the README
  • Whether some remaining low-level wrapped entry points should get additional helper overloads
  • Documentation! The goal was to originally keep everything as close to the original SDL API so the user could just be pointed to the SDL documentation, but this is beginning to look like a pipe-dream.
  • Fixing BindingChecker. Not updated to verify Raw bindings yet.

klukaszek and others added 2 commits March 13, 2026 15:05
* Made audio streams type-safe

* Fix Wrapped.Audio phantom-type integration and example compatibility

- Export format value constructors so sdlAudioSpec is usable at term level.
- Fix stream device functions to unwrap phantom-typed SDLAudioStream.
- Fix sizeOf hardcoded to Float in VS.Vector SDLAudioStreamPut instance.
- Change Raw callback setters to accept FunPtr directly for proper lifetime management.
- Add separate Get/Put callback tracking IORefs to prevent cross-orphaning.
- Fix polymorphic streamCallbackRefs unsafePerformIO footgun by using monomorphic Raw types.
- Add SomeSDLAudioSpec existential for runtime-discovered formats.
- Add sdlGetAudioStreamDataBytes and sdlPutAudioStreamDataBytes escape hatches.
- Wrap sdlMixAudio, sdlGetAudioFormatName, sdlGetSilenceValueForFormat with phantom types.
- Fix sdlOpenAudioDeviceStream callback ambiguity when passing Nothing.
- Update AudioExample and WAVExample to compile and run with new API.
- Optimize VS.Vector instances for SDLAudioStreamGet and SDLAudioStreamPut by using new raw pointer functions (sdlGetAudioStreamDataPtr, sdlPutAudioStreamDataPtr), achieving zero-copy with unsafeFreeze.
- Fix memory management in Raw.Audio by using Stdinc.free (SDL_free) for buffers allocated by SDL.
- Make sdlMixAudio safe by requiring mutable destination buffers (VSM.IOVector) and using explicit scoping for type-safe sample sizes.

---------

Co-authored-by: Kyle Lukaszek <kylelukaszek@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Change Request: Use the MonadIO typeclass instead of straight IO

2 participants