Repository navigation
sdl: ship SDL3 alongside SDL2 - #27
edumeneses wants to merge 3 commits into
Conversation
|
one thing to check with SDL: that it doesn't break the SDL API shipped by emscripten, I don't know if they updated to SDL3 too |
|
Checked. Short version: this PR can't touch the emscripten SDL, and emscripten did add SDL3 — but its port is marked experimental, which makes it a real question for the libossia half rather than this one. This PR doesn't go near WASMThe WASM SDK never built SDL at all — there is no
This PR only adds Emscripten has SDL3, flagged experimental
So the capability is there and joystick specifically is fine. The friction is the experimental warning, the 3.4.2-vs-3.4.14 skew, and the threads variant. Where it actually bites
I'd lean on (1) — browser gamepads come from the Gamepad API either way and the emscripten joystick driver is enabled in the SDL3 port's build config — but that's your call and it belongs in the libossia PR, not here. Nothing about it changes this diff. Worth noting the side-by-side layout helps here specifically: because the SDK keeps shipping SDL2 2.32.10 (matching emscripten's sdl2 port exactly), you can move native to SDL3 and leave the WASM leg on SDL2 for a while if the experimental port turns out to be a problem. |
|
Opened #28 to track removing the transitional SDL2 build once libossia is ported, so the side-by-side layout doesn't quietly become permanent. |
|
@jcelerier one decision needed from you before the libossia side starts — flagging it here rather than guessing, since it changes the shape of that PR. Edu and I went through the options internally first; this is the one we couldn't settle without you.
Our read is (1): browser gamepads come from the Gamepad API either way, the experimental warning looks like caution about API coverage we don't use (video/render/GPU) rather than about joystick, and (3)'s dual-API code is the thing we'd least like to live with. But you know what the wasm build actually needs from pthreads and how much you want to lean on an experimental emscripten port, so it's your call. Happy to go with (2) if you'd rather keep the WASM leg conservative — score's browser build losing gamepad input is a much smaller loss than the native releases staying on SDL2. Written by Claude (Anthropic), acting as Edu's assistant — Edu has reviewed the analysis above. |
|
note that for SDL2 it's not part of the WASM sdk we build because it's already a built-in part of the emscripten SDK |
|
in any case the best would be to do a complete WASM build with this sdk and enforce SDL3, and verify that it works |
|
CI verdict from the first run, and a fix pushed for the two real failures.
|
|
The libossia + score halves are up as drafts, so the prerequisite for your plan on #28 is in place:
Two findings that matter for this PR: The SDK's avendish needs no PR. Its bare Still open on your side: the full WASM build with SDL3 enforced. That's the one thing I can't verify from here, and it's your explicit gate. Once it passes I'll flip this PR to modify Written by Claude (Anthropic), acting as Edu's assistant. |
Points the SDK prefix paths at $OSSIA_SDK/SDL3 (see ossia/sdk#27), renames the ossia::sdl2 link target to ossia::sdl3, moves the Emscripten link option to -sUSE_SDL=3, and bumps libossia to the branch carrying the API port. The libossia bump also picks up the 13 commits of master the submodule was behind; none of them touch SDL. avendish is deliberately untouched. avendish.ossia.cmake and avendish.standalone.cmake do reference a bare SDL2 link target, but every one of those references sits behind either the `if(1)` short-circuit at the top of avendish.ossia.cmake or an `if(TARGET ossia::ossia)` that score's build does not enter -- no link.txt in a configured score build mentions SDL2, and the only libSDL2.so left in build.ninja comes from system ffmpeg's avdevice pkg-config ldflags, which is unrelated. Verified on linux-x86_64 against SDL3 3.4.14: JoystickDevice.cpp, JoystickProtocolFactory.cpp, JoystickProtocolSettingsWidget.cpp, JoystickSpecificSettingsSerialization.cpp and score_plugin_audio.cpp (which is what pulls in Audio/SDLInterface.hpp) all compile clean under this build's own flags and warnings. joystick_info keeps its index-based API, so the enumeration loops in JoystickDevice and JoystickProtocolFactory and the (id, index) pair in JoystickSpecificSettings needed no change and existing save files keep loading. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TTUi76cnUWh7dGZC64LqbZ
|
Correction to something I asserted earlier in this PR and in #28, since it was load-bearing for the side-by-side argument. I said score's deployment builds hard-fail when the function(score_assert_feature name)
if(NOT "${name}" IN_LIST "${SCORE_FEATURES_LIST}")
list(APPEND SCORE_MISSING_FEATURES "${name}") # no PARENT_SCOPE
endif()
endfunction()Two problems: What this changes: dropping SDL2 before libossia is ported loses the joystick protocol and the SDL audio engine from the releases silently rather than failing the build. Still a reason not to swap it out from under un-ported consumers, but a weaker one than I gave. Pushed ee21443 to fix the comment in I have not touched One more thing the port surfaced, in ossia/score#2176: the prefix-path change only covers $OSSIA_SDK consumers. The flatpak had no SDL module at all and was taking SDL2 from the Written by Claude (Anthropic), acting as Edu's assistant. |
Moves every SDL2 call site to SDL3 and switches find_package(SDL2) to SDL3. ossia::sdl2 becomes ossia::sdl3, and the SDK prefix path moves from $OSSIA_SDK/SDL2 to $OSSIA_SDK/SDL3 to match ossia/sdk#27. Most of it is upstream's rename table (SDL_oldnames.h, which is also what build-scripts/rename_symbols.py applies): SDL_JoystickOpen -> SDL_OpenJoystick, SDL_GameControllerHasAxis -> SDL_GamepadHasAxis, SDL_CONTROLLER* -> SDL_GAMEPAD*, the SDL_Controller*Event structs -> SDL_Gamepad*Event and their union members caxis/cbutton/ctouchpad/csensor -> gaxis/gbutton/gtouchpad/gsensor, and the event enums to SDL_EVENT_*. Four things needed more than a rename: Device indices are gone. SDL2 enumerated joysticks as 0..SDL_NumJoysticks()-1 and mapped an index to an identity with SDL_JoystickGetDeviceInstanceID; SDL3 has only SDL_GetJoysticks(), returning an owned array of stable SDL_JoystickID. joystick_info keeps its index-based signatures on purpose: an index into that array plays exactly the role SDL2's device index did, and is just as transient (valid until the device list changes), so score's enumeration loop and the (id, index) pair it serialises into save files keep working untouched. The new sdl_joystick_ids RAII helper owns the array; index 0 maps to SDL3's invalid id of 0, which joystick_info translates back to the -1 sentinel its callers expect. game_controller_protocol no longer needs to remember the index at all, since SDL_GetGamepadTypeForID takes the id, so m_joystick_index is gone. Booleans changed shape. SDL_Init and the Has*/SetSensorEnabled family return bool rather than an int status, so `< 0`, `>= 0` and `== SDL_TRUE` tests are dropped; button events carry `down` instead of `state == SDL_PRESSED`. The rumble capability queries were replaced by properties: SDL_GameControllerHasRumble/HasRumbleTriggers become SDL_GetBooleanProperty on SDL_GetGamepadProperties with SDL_PROP_GAMEPAD_CAP_RUMBLE_BOOLEAN and SDL_PROP_GAMEPAD_CAP_TRIGGER_RUMBLE_BOOLEAN. The audio engine is a real rewrite. SDL3 has no pull callback filling a fixed-size buffer: SDL_OpenAudioDeviceStream hands us a stream and asks for `additional_amount` bytes, which varies. The callback now services that request in whole blocks of effective_buffer_size frames and pushes each through SDL_PutAudioStreamData, with SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES set to the requested block size so the device buffer matches. Overshooting the request by at most one block is fine - the stream buffers it. SDL_AudioSpec lost `samples`, `callback` and `userdata`, so effective_buffer_size is now what we asked for rather than what the device reported. The interleave scratch buffers are preallocated members instead of per-callback alloca, which the fixed block size now makes possible. Status comes from SDL_AudioStreamDevicePaused instead of SDL_GetAudioDeviceStatus. Two guards were dropped. The SDL_VERSION_ATLEAST(2, 26, 0) fallback for SDL_SENSOR_GYRO_R is dead now that SDL3 is the floor. And sdl_protocol.hpp no longer includes SDL_config.h to test SDL_AUDIO_DISABLED: SDL3 does not install SDL_build_config.h, and it does not need to - verified that a -DSDL_AUDIO=0 SDL3 build still exports SDL_OpenAudioDeviceStream, SDL_PutAudioStreamData, SDL_ResumeAudioStreamDevice, SDL_DestroyAudioStream and SDL_AudioStreamDevicePaused, so the engine compiles and links either way and an audio-less build simply fails SDL_Init(SDL_INIT_AUDIO) at runtime. WASM moves to `-s USE_SDL=3`. Emscripten's sdl3 port exists at the EMSDK_VERSION the SDK pins and has SDL_JOYSTICK_EMSCRIPTEN and SDL_AUDIO_DRIVER_EMSCRIPTEN enabled, so both backends stay live in the browser; it does still warn that the port is experimental. Verified on linux-x86_64: game_controller_protocol.cpp, joystick_protocol.cpp and audio_engine.cpp compile clean against SDL3 3.4.14 under score's own flags and warnings, and audio_engine.cpp really does compile the SDL engine (the preprocessed TU contains class sdl_protocol and the stream API, with no SDL2 call left). A standalone harness mirroring the two rewritten paths confirms the behaviour: SDL_Init(JOYSTICK|GAMEPAD) succeeds, enumeration and GUID lookup work, an out-of-range index yields id 0, and the audio callback produced exactly 77 blocks of 256 frames with the expected paused -> resumed transition. No gamepad was attached to the test machine, so rumble, sensors and touchpad input are compile- and API-verified but not exercised against real hardware. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TTUi76cnUWh7dGZC64LqbZ
Points the SDK prefix paths at $OSSIA_SDK/SDL3 (see ossia/sdk#27), renames the ossia::sdl2 link target to ossia::sdl3, moves the Emscripten link option to -sUSE_SDL=3, and bumps libossia to the branch carrying the API port. The libossia bump also picks up the 13 commits of master the submodule was behind; none of them touch SDL. avendish is deliberately untouched. avendish.ossia.cmake and avendish.standalone.cmake do reference a bare SDL2 link target, but every one of those references sits behind either the `if(1)` short-circuit at the top of avendish.ossia.cmake or an `if(TARGET ossia::ossia)` that score's build does not enter -- no link.txt in a configured score build mentions SDL2, and the only libSDL2.so left in build.ninja comes from system ffmpeg's avdevice pkg-config ldflags, which is unrelated. Verified on linux-x86_64 against SDL3 3.4.14: JoystickDevice.cpp, JoystickProtocolFactory.cpp, JoystickProtocolSettingsWidget.cpp, JoystickSpecificSettingsSerialization.cpp and score_plugin_audio.cpp (which is what pulls in Audio/SDLInterface.hpp) all compile clean under this build's own flags and warnings. joystick_info keeps its index-based API, so the enumeration loops in JoystickDevice and JoystickProtocolFactory and the (id, index) pair in JoystickSpecificSettings needed no change and existing save files keep loading. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TTUi76cnUWh7dGZC64LqbZ
HOLD_PRS lists "<pr>=<owner/repo>#<n>" pairs: the PR stays out of the build until <n> is merged, then goes in by itself. The release body lists the held PRs and what they wait for. ossia#2176 (SDL3) is held on ossia/sdk#27: the SDK the AppImage builds against has no SDL3 before it, so libossia would turn SDL off and the AppImage would lose the joystick protocol and the SDL audio engine without failing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lc7tYGHRn6TmaxMEfBTswW
Adds an SDL3 3.4.14 build to every platform that already builds SDL2, installed
to $INSTALL_PREFIX/SDL3 so it sits next to the existing $INSTALL_PREFIX/SDL2.
SDL3's ABI has been stable since 3.2.0 (Jan 2025) and the breaking API churn
predates it, so there is nothing left to wait for on the upstream side.
SDL2 stays because the change CANNOT be contained to this repo otherwise.
libossia is what actually consumes it, and it is SDL2-only in three places:
the joystick/game-controller protocols and the SDL audio engine use the SDL2
spellings throughout (SDL_JoystickOpen, SDL_NumJoysticks, SDL_GameController*,
SDL_OpenAudioDevice's callback form -- all renamed or replaced in SDL3),
cmake/deps/sdl.cmake does find_package(SDL2 CONFIG) and exports ossia::sdl2,
and the SDK prefix paths in OssiaOptions.cmake / score's CMakeLists.txt point
at $OSSIA_SDK/SDL2. Replacing SDL2 in place would silently flip
OSSIA_ENABLE_SDL to FALSE, which score's score_assert_feature(sdl) turns into a
hard failure on any deployment build. Side-by-side keeps every sdkNN tag
consumable by today's score while that port happens in libossia; removing SDL2
afterwards is then a mechanical delete of SDL_VERSION and the sdl.sh scripts.
The flag sets are the SDL2 ones translated to SDL3:
- SDL_EVENTS, SDL_FILE, SDL_ATOMIC, SDL_THREADS, SDL_TIMERS, SDL_LOADSO,
SDL_CPUINFO, SDL_FILESYSTEM, SDL_LIBSAMPLERATE, SDL_DLOPEN and SDL_SYSTEM
are no longer options -- SDL3 always builds those.
- SDL_STATIC_PIC is gone; CMAKE_POSITION_INDEPENDENT_CODE replaces it.
- SDL_UNIX_CONSOLE_BUILD is required on Linux: since we build with both
SDL_X11=0 and SDL_WAYLAND=0, SDL3 otherwise stops at configure time with
"SDL could not find X11 or Wayland development libraries on your system".
- SDL_HIDAPI/SDL_HIDAPI_JOYSTICK are explicit. They are what makes the extra
gamepad surface (PS4/PS5 rumble, gyro/accel sensors, touchpad fingers)
actually reachable, which is the point of the upgrade.
- SDL_TESTS defaults to ON for a top-level SDL3 project, so it and
SDL_TEST_LIBRARY/SDL_EXAMPLES/SDL_INSTALL_CPACK are turned off.
- The macOS build no longer symlinks a framework Resources dir into place:
SDL_FRAMEWORK defaults to OFF in SDL3 and the install lands a normal
lib/cmake/SDL3 tree. It is passed explicitly all the same.
- SDL_DYNAMIC_API=0 moves from -DCMAKE_C_FLAGS to $CFLAGS/$CXXFLAGS. Passing
CMAKE_C_FLAGS on the command line makes cmake ignore the environment, which
dropped every flag common.sh sets -- including macOS's -arch, so the
cross-built x86_64 leg was getting host-arch objects.
- The IBUS_FOUND -> 0 sed is not carried over: SDL3 guards that block behind
SDL_IBUS, which we already set to 0.
SDL3_VERSION is deliberately outside the core-hash allowlist in
.github/actions/core-hash, so this rotates the media stage only and every
published core keeps being reused.
Verified on linux-x86_64 end to end: clone-sdl3.sh fetches and patches the
tarball, the static lib builds and installs, find_package(SDL3 CONFIG) resolves
from the prefix, SDL3::SDL3-static links, and the result runs SDL_Init with
JOYSTICK|GAMEPAD|HAPTIC|SENSOR against a video- and audio-less build. Confirmed
the dynapi indirection is compiled out (no SDL_InitDynamicAPI), there are no
absolute 32-bit relocations, and SDL_RumbleGamepad /
SDL_SetGamepadSensorEnabled / SDL_GetGamepadTouchpadFinger are all present.
AArch64/ gets an sdl3.sh for consistency but is not in CI (its common.sh is a
dangling symlink into a developer's home). MSVC/sdl.ps1 is left alone: it pins
Visual Studio 2017 and SDL 2.0.9 and is not wired into any workflow.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TTUi76cnUWh7dGZC64LqbZ
Both macOS legs failed in CI, and both trace back to this branch passing $CFLAGS through to the SDL3 build instead of overriding CMAKE_C_FLAGS the way sdl.sh does. Overriding it made cmake ignore the environment, which is what had been hiding macOS/common.sh's flags from SDL all along. arm64: -ffast-math reaches the compiler, and SDL_audiotypecvt.c guards a `#pragma STDC FENV_ACCESS ON` behind __aarch64__ + clang >= 12, so SDL3-3.4.14/src/audio/SDL_audiotypecvt.c:541:14: error: '#pragma STDC FENV_ACCESS ON' is illegal when precise is disabled -fno-fast-math restores precise FP for this build only. Nothing in SDL wants fast-math semantics; the flag comes from common.sh for the DSP libraries. x86_64: CPUFLAGS carries `-arch x86_64 -arch x86_64h` for the fat build, and SDL3 (unlike SDL2) compiles through a precompiled header, so clang: error: cannot use 'precompiled-header' output with multiple -arch options CMAKE_DISABLE_PRECOMPILE_HEADERS turns the PCH off. This is unavoidable for a fat build regardless of how the arch reaches the compiler -- routing it through CMAKE_OSX_ARCHITECTURES instead still emits both -arch flags on one clang invocation. Keeping the passthrough rather than reverting to the CMAKE_C_FLAGS override is deliberate: the override silently dropped -arch too, so the x86_64 leg was building host-arch objects. Fat objects are the point of that leg. The linux legs and windows-arm64 are unaffected -- neither common.sh sets -ffast-math or a second -arch -- and both were already green. windows-x86_64's failure is unrelated to SDL3 and to this branch: curl could not reach www.libsdl.org for the SDL2 tarball ("Failed to connect to www.libsdl.org:443 after 21089 ms"), so the pre-existing common/clone-sdl.sh step died. windows-arm64 pulled the same tarball fine in the same run. clone-sdl3.sh fetches from github.com, which did not flake. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TTUi76cnUWh7dGZC64LqbZ
The comment claimed score's deployment builds hard-assert the sdl feature.
They do not, in practice: score_assert_feature does list(APPEND
SCORE_MISSING_FEATURES ...) inside a function() with no PARENT_SCOPE, so the
variable its FATAL_ERROR tests is always empty, and it also passes a
dereferenced "${SCORE_FEATURES_LIST}" to IN_LIST, which expects the list's
name. Dropping SDL2 before libossia is ported therefore loses the joystick
protocol and the SDL audio engine from the releases silently rather than loudly,
which is a weaker argument for shipping both SDLs but still an argument.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TTUi76cnUWh7dGZC64LqbZ
ee21443 to
a068e62
Compare
|
This is Edu's Claude assistant, posting on Edu's behalf. Rebased onto master (50 commits behind). Same three commits. The only conflict was This is what ossia/score#2176 and ossia/libossia#922 wait on: without SDL3 in the SDK, libossia turns SDL off and the joystick protocol and the SDL audio engine silently drop out of the release builds. |
Moves every SDL2 call site to SDL3 and switches find_package(SDL2) to SDL3. ossia::sdl2 becomes ossia::sdl3, and the SDK prefix path moves from $OSSIA_SDK/SDL2 to $OSSIA_SDK/SDL3 to match ossia/sdk#27. Most of it is upstream's rename table (SDL_oldnames.h, which is also what build-scripts/rename_symbols.py applies): SDL_JoystickOpen -> SDL_OpenJoystick, SDL_GameControllerHasAxis -> SDL_GamepadHasAxis, SDL_CONTROLLER* -> SDL_GAMEPAD*, the SDL_Controller*Event structs -> SDL_Gamepad*Event and their union members caxis/cbutton/ctouchpad/csensor -> gaxis/gbutton/gtouchpad/gsensor, and the event enums to SDL_EVENT_*. Four things needed more than a rename: Device indices are gone. SDL2 enumerated joysticks as 0..SDL_NumJoysticks()-1 and mapped an index to an identity with SDL_JoystickGetDeviceInstanceID; SDL3 has only SDL_GetJoysticks(), returning an owned array of stable SDL_JoystickID. joystick_info keeps its index-based signatures on purpose: an index into that array plays exactly the role SDL2's device index did, and is just as transient (valid until the device list changes), so score's enumeration loop and the (id, index) pair it serialises into save files keep working untouched. The new sdl_joystick_ids RAII helper owns the array; index 0 maps to SDL3's invalid id of 0, which joystick_info translates back to the -1 sentinel its callers expect. game_controller_protocol no longer needs to remember the index at all, since SDL_GetGamepadTypeForID takes the id, so m_joystick_index is gone. Booleans changed shape. SDL_Init and the Has*/SetSensorEnabled family return bool rather than an int status, so `< 0`, `>= 0` and `== SDL_TRUE` tests are dropped; button events carry `down` instead of `state == SDL_PRESSED`. The rumble capability queries were replaced by properties: SDL_GameControllerHasRumble/HasRumbleTriggers become SDL_GetBooleanProperty on SDL_GetGamepadProperties with SDL_PROP_GAMEPAD_CAP_RUMBLE_BOOLEAN and SDL_PROP_GAMEPAD_CAP_TRIGGER_RUMBLE_BOOLEAN. The audio engine is a real rewrite. SDL3 has no pull callback filling a fixed-size buffer: SDL_OpenAudioDeviceStream hands us a stream and asks for `additional_amount` bytes, which varies. The callback now services that request in whole blocks of effective_buffer_size frames and pushes each through SDL_PutAudioStreamData, with SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES set to the requested block size so the device buffer matches. Overshooting the request by at most one block is fine - the stream buffers it. SDL_AudioSpec lost `samples`, `callback` and `userdata`, so effective_buffer_size is now what we asked for rather than what the device reported. The interleave scratch buffers are preallocated members instead of per-callback alloca, which the fixed block size now makes possible. Status comes from SDL_AudioStreamDevicePaused instead of SDL_GetAudioDeviceStatus. Two guards were dropped. The SDL_VERSION_ATLEAST(2, 26, 0) fallback for SDL_SENSOR_GYRO_R is dead now that SDL3 is the floor. And sdl_protocol.hpp no longer includes SDL_config.h to test SDL_AUDIO_DISABLED: SDL3 does not install SDL_build_config.h, and it does not need to - verified that a -DSDL_AUDIO=0 SDL3 build still exports SDL_OpenAudioDeviceStream, SDL_PutAudioStreamData, SDL_ResumeAudioStreamDevice, SDL_DestroyAudioStream and SDL_AudioStreamDevicePaused, so the engine compiles and links either way and an audio-less build simply fails SDL_Init(SDL_INIT_AUDIO) at runtime. WASM moves to `-s USE_SDL=3`. Emscripten's sdl3 port exists at the EMSDK_VERSION the SDK pins and has SDL_JOYSTICK_EMSCRIPTEN and SDL_AUDIO_DRIVER_EMSCRIPTEN enabled, so both backends stay live in the browser; it does still warn that the port is experimental. Verified on linux-x86_64: game_controller_protocol.cpp, joystick_protocol.cpp and audio_engine.cpp compile clean against SDL3 3.4.14 under score's own flags and warnings, and audio_engine.cpp really does compile the SDL engine (the preprocessed TU contains class sdl_protocol and the stream API, with no SDL2 call left). A standalone harness mirroring the two rewritten paths confirms the behaviour: SDL_Init(JOYSTICK|GAMEPAD) succeeds, enumeration and GUID lookup work, an out-of-range index yields id 0, and the audio callback produced exactly 77 blocks of 256 frames with the expected paused -> resumed transition. No gamepad was attached to the test machine, so rumble, sensors and touchpad input are compile- and API-verified but not exercised against real hardware. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TTUi76cnUWh7dGZC64LqbZ
Points the SDK prefix paths at $OSSIA_SDK/SDL3 (see ossia/sdk#27), renames the ossia::sdl2 link target to ossia::sdl3, moves the Emscripten link option to -sUSE_SDL=3, and bumps libossia to the branch carrying the API port. The libossia bump also picks up the 13 commits of master the submodule was behind; none of them touch SDL. avendish is deliberately untouched. avendish.ossia.cmake and avendish.standalone.cmake do reference a bare SDL2 link target, but every one of those references sits behind either the `if(1)` short-circuit at the top of avendish.ossia.cmake or an `if(TARGET ossia::ossia)` that score's build does not enter -- no link.txt in a configured score build mentions SDL2, and the only libSDL2.so left in build.ninja comes from system ffmpeg's avdevice pkg-config ldflags, which is unrelated. Verified on linux-x86_64 against SDL3 3.4.14: JoystickDevice.cpp, JoystickProtocolFactory.cpp, JoystickProtocolSettingsWidget.cpp, JoystickSpecificSettingsSerialization.cpp and score_plugin_audio.cpp (which is what pulls in Audio/SDLInterface.hpp) all compile clean under this build's own flags and warnings. joystick_info keeps its index-based API, so the enumeration loops in JoystickDevice and JoystickProtocolFactory and the (id, index) pair in JoystickSpecificSettings needed no change and existing save files keep loading. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TTUi76cnUWh7dGZC64LqbZ
Closes #6.
Adds an SDL3 3.4.14 build to every platform that already builds SDL2, installed to
$INSTALL_PREFIX/SDL3next to the existing$INSTALL_PREFIX/SDL2. This unblocks the gamepad work — the releases are no longer stuck on SDL2.SDL3's ABI has been stable since 3.2.0 (Jan 2025); the breaking API churn all predates it and upstream is now on its 15th stable release. Nothing left to wait for.
Why SDL2 stays
I looked at whether this could be contained to this repo. It can't be, if SDL2 is replaced. libossia is the actual consumer and it is SDL2-only in three separate places:
src/ossia/protocols/joystick/{joystick,game_controller}_protocol.cpp,joystick_manager.hppSDL_JoystickOpen,SDL_NumJoysticks,SDL_JoystickGetDeviceInstanceID, the wholeSDL_GameController*family,SDL_CONTROLLER*event enums. All renamed or restructured in SDL3.src/ossia/audio/sdl_protocol.hppSDL_OpenAudioDevice's callback form +SDL_GetAudioDeviceStatus, replaced by audio streams in SDL3.cmake/deps/sdl.cmake,cmake/OssiaOptions.cmake(and score'sCMakeLists.txt)find_package(SDL2 CONFIG)→ossia::sdl2, and prefix paths pointing at$OSSIA_SDK/SDL2.Swapping SDL2 out from under that would silently flip
OSSIA_ENABLE_SDLtoFALSE, and score'sscore_assert_feature(sdl)turns that into a hard failure on any deployment build. Side-by-side keeps everysdkNNtag consumable by today's score while the port lands in libossia; dropping SDL2 afterwards is then a mechanical delete ofSDL_VERSIONand the foursdl.shscripts.Happy to switch this to a hard replace if you'd rather sequence it the other way — it's a one-line change to each
CMAKE_INSTALL_PREFIX.Flag translation
The SDL2 flag sets carried over to SDL3, with the differences that matter:
SDL_EVENTS,SDL_FILE,SDL_ATOMIC,SDL_THREADS,SDL_TIMERS,SDL_LOADSO,SDL_CPUINFO,SDL_FILESYSTEM,SDL_LIBSAMPLERATE,SDL_DLOPEN,SDL_SYSTEM.SDL_STATIC_PIC→CMAKE_POSITION_INDEPENDENT_CODE.SDL_UNIX_CONSOLE_BUILDis required on Linux. Since we build with bothSDL_X11=0andSDL_WAYLAND=0, SDL3 otherwise hard-stops at configure:SDL could not find X11 or Wayland development libraries on your system.SDL_HIDAPI/SDL_HIDAPI_JOYSTICKset explicitly — these are what make the extra gamepad surface (PS4/PS5 rumble, gyro/accel, touchpad fingers) reachable at all.SDL_TESTSdefaults ON for a top-level SDL3 project, so it plusSDL_TEST_LIBRARY/SDL_EXAMPLES/SDL_INSTALL_CPACKare turned off.Resources→cmakesymlink:SDL_FRAMEWORKdefaults OFF in SDL3 and the install lands a normallib/cmake/SDL3tree.IBUS_FOUND -> 0sed isn't carried over — SDL3 guards that block behindSDL_IBUS, already 0.SDL_DYNAMIC_API=0moved from-DCMAKE_C_FLAGSinto$CFLAGS/$CXXFLAGS. PassingCMAKE_C_FLAGSon the command line makes cmake ignore the environment, which dropped everythingcommon.shsets — including macOS's-arch, so the cross-built x86_64 leg was getting host-arch objects. Worth fixing inmacOS/sdl.shtoo, but I left SDL2 untouched here.Core hash
SDL3_VERSIONsits outside the allowlist in.github/actions/core-hash, so this rotates the media stage only — verified the hash is byte-identical to master:Testing
Ran
Linux/sdl3.shend to end on linux-x86_64 in a container-shaped harness (/image+ bind-mountedcommon/, so the./common/clone-sdl3.shand../common/versions.shsource paths are exercised as they are in CI):clone-sdl3.shfetches and patches the tarball; the#error Nopedeletion is load-bearing — without it the build fails onSDL_dynapi.cand the PCH.find_package(SDL3 REQUIRED CONFIG)resolves from the prefix,SDL3::SDL3-staticlinks, and the binary runsSDL_Init(JOYSTICK|GAMEPAD|HAPTIC|SENSOR)successfully against a video-less, audio-less build →SDL 3.4.14, joysticks: 0.hidapi linux virtual.SDL_InitDynamicAPI), zero absolute 32-bit relocations, andSDL_RumbleGamepad/SDL_SetGamepadSensorEnabled/SDL_GetGamepadTouchpadFingerall present in the archive.bash -nclean on all ten touched scripts.macOS and Windows are unverified locally — that's what this PR's CI run is for.
Not touched
AArch64/gets ansdl3.shfor consistency but isn't in CI (itscommon.shis a dangling symlink into/home/jcelerier/...).MSVC/sdl.ps1left alone — pins Visual Studio 2017 and SDL 2.0.9, not wired into any workflow.-sUSE_SDL=2port. Moving that to--use-port=sdl3is a libossia-side change.Follow-up
The libossia port is the other half of this. I can take it next: ~130 SDL references across 6 files, plus
deps/sdl.cmakeand the prefix paths inOssiaOptions.cmake/ score'sCMakeLists.txt. Mostly mechanical renames; the audio engine's callback → stream conversion is the only real rewrite.🤖 Generated with Claude Code
https://claude.ai/code/session_01TTUi76cnUWh7dGZC64LqbZ