From 69db1a4332179c1591e313e3a6579d40acec7841 Mon Sep 17 00:00:00 2001 From: Kevin Hake Date: Tue, 24 Mar 2026 16:46:11 -0700 Subject: [PATCH 1/8] Upgrade ImGui from v1.73 to v1.92.6 --- CMakeLists.txt | 6 +++--- libs/imgui | 2 +- src/rwe/ImGuiContext.cpp | 2 +- src/rwe/ImGuiContext.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e3732229..61017755b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -207,8 +207,8 @@ file(GLOB IMGUI_SOURCE_FILES ) set(IMGUI_IMPL_FILES - ${PROJECT_SOURCE_DIR}/libs/imgui/examples/imgui_impl_sdl.cpp - ${PROJECT_SOURCE_DIR}/libs/imgui/examples/imgui_impl_opengl3.cpp + ${PROJECT_SOURCE_DIR}/libs/imgui/backends/imgui_impl_sdl2.cpp + ${PROJECT_SOURCE_DIR}/libs/imgui/backends/imgui_impl_opengl3.cpp ) set(SOURCE_FILES @@ -738,7 +738,7 @@ target_include_directories(librwe PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) # for gene target_link_libraries(librwe nlohmann_json::nlohmann_json) target_include_directories(librwe PUBLIC ${PROJECT_SOURCE_DIR}/libs/imgui) -target_include_directories(librwe PUBLIC ${PROJECT_SOURCE_DIR}/libs/imgui/examples) +target_include_directories(librwe PUBLIC ${PROJECT_SOURCE_DIR}/libs/imgui/backends) add_custom_command(TARGET librwe PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory "${PROJECT_SOURCE_DIR}/shaders" "$/shaders") diff --git a/libs/imgui b/libs/imgui index 4e56de757..6ded5230d 160000 --- a/libs/imgui +++ b/libs/imgui @@ -1 +1 @@ -Subproject commit 4e56de757c76c9d713d4f05a0f6adf82d1aac068 +Subproject commit 6ded5230d043aa32c755e65c910c2af5002fb9f9 diff --git a/src/rwe/ImGuiContext.cpp b/src/rwe/ImGuiContext.cpp index 2d304c752..f91dd6b42 100644 --- a/src/rwe/ImGuiContext.cpp +++ b/src/rwe/ImGuiContext.cpp @@ -56,7 +56,7 @@ namespace rwe void ImGuiContext::newFrame(SDL_Window* window) { ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplSDL2_NewFrame(window); + ImGui_ImplSDL2_NewFrame(); ImGui::NewFrame(); } diff --git a/src/rwe/ImGuiContext.h b/src/rwe/ImGuiContext.h index edd048466..6f173687e 100644 --- a/src/rwe/ImGuiContext.h +++ b/src/rwe/ImGuiContext.h @@ -2,7 +2,7 @@ #include #include -#include +#include #include namespace rwe From cfe8e245f16b737c41cc71f77024fd13599a4fab Mon Sep 17 00:00:00 2001 From: Kevin Hake Date: Tue, 24 Mar 2026 15:27:29 -0700 Subject: [PATCH 2/8] Add SDL3 and SDL3_mixer submodules --- .gitmodules | 6 ++++++ libs/sdl3 | 1 + libs/sdl3_mixer | 1 + 3 files changed, 8 insertions(+) create mode 160000 libs/sdl3 create mode 160000 libs/sdl3_mixer diff --git a/.gitmodules b/.gitmodules index c0f5384d2..c38ba6965 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,9 @@ [submodule "libs/asio"] path = libs/asio url = https://github.com/chriskohlhoff/asio.git +[submodule "libs/sdl3"] + path = libs/sdl3 + url = https://github.com/libsdl-org/SDL.git +[submodule "libs/sdl3_mixer"] + path = libs/sdl3_mixer + url = https://github.com/libsdl-org/SDL_mixer.git diff --git a/libs/sdl3 b/libs/sdl3 new file mode 160000 index 000000000..683181b47 --- /dev/null +++ b/libs/sdl3 @@ -0,0 +1 @@ +Subproject commit 683181b47cfabd293e3ea409f838915b8297a4fd diff --git a/libs/sdl3_mixer b/libs/sdl3_mixer new file mode 160000 index 000000000..cedfeef30 --- /dev/null +++ b/libs/sdl3_mixer @@ -0,0 +1 @@ +Subproject commit cedfeef30e93db35eee6b25759117da63f8e5a4f From 0572b76a49110da442528240d5767cc26361fb3a Mon Sep 17 00:00:00 2001 From: Kevin Hake Date: Tue, 24 Mar 2026 16:04:20 -0700 Subject: [PATCH 3/8] Switch from system SDL2 to SDL3/SDL3_mixer submodules (static) --- CMakeLists.txt | 120 ++++++------------------------------------------- devbox.json | 13 ++++-- devbox.lock | 74 +++++++++++++----------------- 3 files changed, 54 insertions(+), 153 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 61017755b..6f9232521 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,15 @@ set(JSON_BuildTests OFF CACHE INTERNAL "") set(JSON_Install OFF CACHE INTERNAL "") add_subdirectory("libs/json") +set(SDL_STATIC ON CACHE BOOL "" FORCE) +set(SDL_SHARED OFF CACHE BOOL "" FORCE) +set(SDL_TEST_LIBRARY OFF CACHE BOOL "" FORCE) +add_subdirectory("libs/sdl3") + +set(SDL3MIXER_VENDORED ON CACHE BOOL "" FORCE) +set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) +add_subdirectory("libs/sdl3_mixer") + function(find_windows_dll var dll) if(WIN32) find_program(${var} ${dll}) @@ -100,17 +109,7 @@ if(MSVC) set(GLEW_LIBRARIES "${CMAKE_SOURCE_DIR}/libs/_msvc/glew-2.1.0/lib/Release/x64/glew32.lib") set(GLEW_INCLUDE_DIRS "libs/_msvc/glew-2.1.0/include") - set(SDL2_DLL "${CMAKE_SOURCE_DIR}/libs/_msvc/SDL2-2.0.7/lib/x64/SDL2.dll") - set(SDL2_LIBRARY "${CMAKE_SOURCE_DIR}/libs/_msvc/SDL2-2.0.7/lib/x64/SDL2.lib;${CMAKE_SOURCE_DIR}/libs/_msvc/SDL2-2.0.7/lib/x64/SDL2main.lib") - set(SDL2_INCLUDE_DIR "libs/_msvc/SDL2-2.0.7/include") - - set(SDL2_IMAGE_DLL "${CMAKE_SOURCE_DIR}/libs/_msvc/SDL2_image-2.0.2/lib/x64/SDL2_image.dll") - set(SDL2_IMAGE_LIBRARIES "${CMAKE_SOURCE_DIR}/libs/_msvc/SDL2_image-2.0.2/lib/x64/SDL2_image.lib") - set(SDL2_IMAGE_INCLUDE_DIRS "libs/_msvc/SDL2_image-2.0.2/include") - - set(SDL2_MIXER_DLL "${CMAKE_SOURCE_DIR}/libs/_msvc/SDL2_mixer-2.0.2/lib/x64/SDL2_mixer.dll") - set(SDL2_MIXER_LIBRARIES "${CMAKE_SOURCE_DIR}/libs/_msvc/SDL2_mixer-2.0.2/lib/x64/SDL2_mixer.lib") - set(SDL2_MIXER_INCLUDE_DIRS "libs/_msvc/SDL2_mixer-2.0.2/include") + # SDL3 and SDL3_mixer built from submodules above set(ZLIB_DLL "${CMAKE_SOURCE_DIR}/libs/_msvc/zlib-1.2.11/lib/zlib1.dll") set(ZLIB_LIBRARIES "${CMAKE_SOURCE_DIR}/libs/_msvc/zlib-1.2.11/lib/zdll.lib") @@ -120,15 +119,6 @@ if(MSVC) set(PNG_LIBRARIES "${CMAKE_SOURCE_DIR}/libs/_msvc/lpng1634/lib/libpng16-16.lib") set(PNG_INCLUDE_DIRS "libs/_msvc/lpng1634/include") - set(FLAC_DLL "${CMAKE_SOURCE_DIR}/libs/_msvc/extras/libFLAC-8.dll") - set(JPEG_DLL "${CMAKE_SOURCE_DIR}/libs/_msvc/extras/libjpeg-9.dll") - set(MPG_DLL "${CMAKE_SOURCE_DIR}/libs/_msvc/extras/libmpg123-0.dll") - set(OGG_DLL "${CMAKE_SOURCE_DIR}/libs/_msvc/extras/libogg-0.dll") - set(TIFF_DLL "${CMAKE_SOURCE_DIR}/libs/_msvc/extras/libtiff-5.dll") - set(LZMA_DLL "LZMA_DLL-NOTFOUND") # bundled libtiff doesn't depend on liblzma - set(VORBIS_DLL "${CMAKE_SOURCE_DIR}/libs/_msvc/extras/libvorbis-0.dll") - set(VORBISFILE_DLL "${CMAKE_SOURCE_DIR}/libs/_msvc/extras/libvorbisfile-3.dll") - set(WEBP_DLL "${CMAKE_SOURCE_DIR}/libs/_msvc/extras/libwebp-7.dll") else() if(EXISTS "${CMAKE_SOURCE_DIR}/libs/_protobuf-install/lib/libprotobuf.a") set(Protobuf_LIBRARY "${CMAKE_SOURCE_DIR}/libs/_protobuf-install/lib/libprotobuf.a") @@ -147,52 +137,13 @@ else() find_package(GLEW REQUIRED) find_windows_dll(GLEW_DLL "glew32.dll") - find_package(SDL2 REQUIRED) - find_windows_dll(SDL2_DLL "SDL2.dll") - find_package(SDL2_image REQUIRED) - find_windows_dll(SDL2_IMAGE_DLL "SDL2_image.dll") - find_package(SDL2_mixer REQUIRED) - find_windows_dll(SDL2_MIXER_DLL "SDL2_mixer.dll") + # SDL3 and SDL3_mixer built from submodules above find_package(ZLIB REQUIRED) find_windows_dll(ZLIB_DLL "zlib1.dll") find_package(PNG REQUIRED) find_windows_dll(PNG_DLL "libpng16-16.dll") - find_windows_dll(FLAC_DLL "libFLAC.dll") - find_windows_dll(JBIG_DLL "libjbig-0.dll") - find_windows_dll(LERC_DLL "libLerc.dll") - find_windows_dll(SHARPYUV_DLL "libsharpyuv-0.dll") - find_windows_dll(JPEG_DLL "libjpeg-8.dll") - find_windows_dll(MPG_DLL "libmpg123-0.dll") - find_windows_dll(OGG_DLL "libogg-0.dll") - find_windows_dll(TIFF_DLL "libtiff-6.dll") - find_windows_dll(LZMA_DLL "liblzma-5.dll") - find_windows_dll(VORBIS_DLL "libvorbis-0.dll") - find_windows_dll(VORBISFILE_DLL "libvorbisfile-3.dll") - find_windows_dll(WEBP_DLL "libwebp-7.dll") - find_windows_dll(ZSTD_DLL "libzstd.dll") - find_windows_dll(MODPLUG_DLL "libmodplug-1.dll") - find_windows_dll(OPUSFILE_DLL "libopusfile-0.dll") - find_windows_dll(OPUS_DLL "libopus-0.dll") - find_windows_dll(READLINE_DLL "libreadline8.dll") - find_windows_dll(SNDFILE_DLL "libsndfile-1.dll") - find_windows_dll(VORBISENC_DLL "libvorbisenc-2.dll") - find_windows_dll(TERMCAP_DLL "libtermcap-0.dll") - find_windows_dll(GLIB2_DLL "libglib-2.0-0.dll") - find_windows_dll(GMODULE2_DLL "libgmodule-2.0-0.dll") - find_windows_dll(STDC++_DLL "libstdc++-6.dll") - find_windows_dll(GCC_S_SEH_DLL "libgcc_s_seh-1.dll") - find_windows_dll(WINPTHREAD_DLL "libwinpthread-1.dll") - find_windows_dll(INTL_DLL "libintl-8.dll") - find_windows_dll(ICONV_DLL "libiconv-2.dll") - find_windows_dll(DEFLATE_DLL "libdeflate.dll") - find_windows_dll(JXL_DLL "libjxl.dll") - find_windows_dll(BROTLIENC_DLL "libbrotlienc.dll") - find_windows_dll(BROTLIDEC_DLL "libbrotlidec.dll") - find_windows_dll(BROTLICOMMON_DLL "libbrotlicommon.dll") - find_windows_dll(HWY_DLL "libhwy.dll") - find_windows_dll(LCMS2_DLL "liblcms2-2.dll") endif() set(Protobuf_USE_STATIC_LIBS ON) @@ -662,17 +613,8 @@ else() target_include_directories(librwe PUBLIC ${GLEW_INCLUDE_DIRS}) endif() -target_copy_file(librwe ${SDL2_DLL}) -target_link_libraries(librwe ${SDL2_LIBRARY}) -target_include_directories(librwe PUBLIC ${SDL2_INCLUDE_DIR}) - -target_copy_file(librwe ${SDL2_IMAGE_DLL}) -target_link_libraries(librwe ${SDL2_IMAGE_LIBRARIES}) -target_include_directories(librwe PUBLIC ${SDL2_IMAGE_INCLUDE_DIRS}) - -target_copy_file(librwe ${SDL2_MIXER_DLL}) -target_link_libraries(librwe ${SDL2_MIXER_LIBRARIES}) -target_include_directories(librwe PUBLIC ${SDL2_MIXER_INCLUDE_DIRS}) +target_link_libraries(librwe SDL3::SDL3-static) +target_link_libraries(librwe SDL3_mixer::SDL3_mixer) target_copy_file(librwe ${ZLIB_DLL}) target_link_libraries(librwe ${ZLIB_LIBRARIES}) @@ -680,42 +622,6 @@ target_include_directories(librwe PUBLIC ${ZLIB_INCLUDE_DIRS}) target_copy_file(librwe ${PNG_DLL}) -target_copy_file(librwe ${FLAC_DLL}) -target_copy_file(librwe ${JPEG_DLL}) -target_copy_file(librwe ${MPG_DLL}) -target_copy_file(librwe ${OGG_DLL}) -target_copy_file(librwe ${TIFF_DLL}) -if(WIN32 AND NOT MSVC) - target_copy_file(librwe ${LZMA_DLL}) # MSYS2 libtiff depends on liblzma - target_copy_file(librwe ${ZSTD_DLL}) # MSYS2 libtiff depends on zstd - target_copy_file(librwe ${DEFLATE_DLL}) # MSYS2 libtiff depends on deflate - target_copy_file(librwe ${JXL_DLL}) # MSYS2 SDL2_image depends on jxl - target_copy_file(librwe ${BROTLIENC_DLL}) - target_copy_file(librwe ${BROTLIDEC_DLL}) - target_copy_file(librwe ${BROTLICOMMON_DLL}) - target_copy_file(librwe ${HWY_DLL}) - target_copy_file(librwe ${LCMS2_DLL}) - target_copy_file(librwe ${MODPLUG_DLL}) # MSYS2 SDL2_mixer depends on modplug - target_copy_file(librwe ${OPUSFILE_DLL}) # MSYS2 SDL2_mixer depends on opusfile - target_copy_file(librwe ${JBIG_DLL}) # MSYS2 SDL2_image depends on jbig - target_copy_file(librwe ${LERC_DLL}) # MSYS2 SDL2_image depends on Lerc - target_copy_file(librwe ${SHARPYUV_DLL}) # MSYS2 SDL2_image libwebp depends on sharpyuv - target_copy_file(librwe ${OPUS_DLL}) - target_copy_file(librwe ${READLINE_DLL}) - target_copy_file(librwe ${SNDFILE_DLL}) - target_copy_file(librwe ${VORBISENC_DLL}) - target_copy_file(librwe ${TERMCAP_DLL}) - target_copy_file(librwe ${GLIB2_DLL}) - target_copy_file(librwe ${GMODULE2_DLL}) - target_copy_file(librwe ${STDC++_DLL}) - target_copy_file(librwe ${GCC_S_SEH_DLL}) - target_copy_file(librwe ${WINPTHREAD_DLL}) - target_copy_file(librwe ${INTL_DLL}) - target_copy_file(librwe ${ICONV_DLL}) -endif() -target_copy_file(librwe ${VORBIS_DLL}) -target_copy_file(librwe ${VORBISFILE_DLL}) -target_copy_file(librwe ${WEBP_DLL}) if(TARGET protobuf::libprotobuf) message(STATUS "Using protobuf::libprotobuf imported target") diff --git a/devbox.json b/devbox.json index 716747b18..c550ca340 100644 --- a/devbox.json +++ b/devbox.json @@ -3,15 +3,20 @@ "packages": [ "cmake", "protobuf", - "SDL2", - "SDL2_image", - "SDL2_mixer", "glew", "libpng", "zlib", "pkg-config", "git", - "libX11.dev" + "libX11.dev", + "libxext", + "libXi", + "libXfixes", + "libxrandr", + "libXScrnSaver", + "libXcursor", + "libXtst", + "libxcb" ], "shell": { "init_hook": [ diff --git a/devbox.lock b/devbox.lock index 805ff46e9..23d824077 100644 --- a/devbox.lock +++ b/devbox.lock @@ -1,48 +1,6 @@ { "lockfile_version": "1", "packages": { - "SDL2": { - "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#SDL2", - "source": "nixpkg", - "systems": { - "x86_64-darwin": { - "outputs": [ - { - "path": "/nix/store/b6c8nbfs0awmdblgjaajsrvwd0m47r3p-sdl2-compat-2.32.62", - "default": true - } - ] - } - } - }, - "SDL2_image": { - "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#SDL2_image", - "source": "nixpkg", - "systems": { - "x86_64-darwin": { - "outputs": [ - { - "path": "/nix/store/9kkyzjl728ijx8bvy95vvj0b0c2c8zp6-SDL2_image-2.8.8", - "default": true - } - ] - } - } - }, - "SDL2_mixer": { - "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#SDL2_mixer", - "source": "nixpkg", - "systems": { - "x86_64-darwin": { - "outputs": [ - { - "path": "/nix/store/n0ij8v60ck36rni9gfia25cgn0sjf4ia-SDL2_mixer-2.8.1", - "default": true - } - ] - } - } - }, "cmake": { "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#cmake", "source": "nixpkg", @@ -84,6 +42,26 @@ "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#libX11.dev", "source": "nixpkg" }, + "libXScrnSaver": { + "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#libXScrnSaver", + "source": "nixpkg" + }, + "libXcursor": { + "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#libXcursor", + "source": "nixpkg" + }, + "libXfixes": { + "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#libXfixes", + "source": "nixpkg" + }, + "libXi": { + "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#libXi", + "source": "nixpkg" + }, + "libXtst": { + "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#libXtst", + "source": "nixpkg" + }, "libpng": { "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#libpng", "source": "nixpkg", @@ -103,6 +81,18 @@ } } }, + "libxcb": { + "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#libxcb", + "source": "nixpkg" + }, + "libxext": { + "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#libxext", + "source": "nixpkg" + }, + "libxrandr": { + "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#libxrandr", + "source": "nixpkg" + }, "pkg-config": { "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#pkg-config", "source": "nixpkg", From 809817b74853f250a99ebdf044ce93e5a6b437a2 Mon Sep 17 00:00:00 2001 From: Kevin Hake Date: Tue, 24 Mar 2026 17:25:53 -0700 Subject: [PATCH 4/8] Migrate source code from SDL2 to SDL3 API (visual build, no audio) Audio stubbed out pending SDL3_mixer migration. --- CMakeLists.txt | 4 +-- src/bridge.cpp | 26 +++++++------- src/main.cpp | 30 ++++++---------- src/rwe/AudioService.cpp | 49 +++++++++++++++++++++++++- src/rwe/AudioService.h | 6 +++- src/rwe/CursorService.cpp | 8 +++-- src/rwe/ImGuiContext.cpp | 16 ++++----- src/rwe/ImGuiContext.h | 2 +- src/rwe/MainMenuScene.cpp | 4 +-- src/rwe/MainMenuScene.h | 2 +- src/rwe/game/GameScene.cpp | 56 +++++++++++++++--------------- src/rwe/game/GameScene.h | 4 +-- src/rwe/render/GraphicsContext.h | 2 +- src/rwe/render/OpenGlVersion.h | 2 +- src/rwe/scene/Scene.h | 6 ++-- src/rwe/scene/SceneManager.cpp | 35 +++++++++---------- src/rwe/sdl/SdlContext.h | 57 +++++++++++++++++-------------- src/rwe/sdl/SdlContextManager.cpp | 5 --- src/rwe/sdl/SdlContextManager.h | 3 -- src/rwe/sdl/SdlMixerContext.h | 25 ++++++++++++-- 20 files changed, 201 insertions(+), 141 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f9232521..dcd090aaf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,7 +158,7 @@ file(GLOB IMGUI_SOURCE_FILES ) set(IMGUI_IMPL_FILES - ${PROJECT_SOURCE_DIR}/libs/imgui/backends/imgui_impl_sdl2.cpp + ${PROJECT_SOURCE_DIR}/libs/imgui/backends/imgui_impl_sdl3.cpp ${PROJECT_SOURCE_DIR}/libs/imgui/backends/imgui_impl_opengl3.cpp ) @@ -440,8 +440,6 @@ set(SOURCE_FILES src/rwe/sdl/SdlContextManager.cpp src/rwe/sdl/SdlContextManager.h src/rwe/sdl/SdlException.h - src/rwe/sdl/SdlImageContext.h - src/rwe/sdl/SdlImageException.h src/rwe/sdl/SdlMixerContext.h src/rwe/sdl/SdlMixerException.h src/rwe/sim/Energy.cpp diff --git a/src/bridge.cpp b/src/bridge.cpp index 9d3db6e64..168404d60 100644 --- a/src/bridge.cpp +++ b/src/bridge.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -174,24 +174,26 @@ std::optional getMinimap(rwe::CompositeVirtualFileSystem& vfs, cons std::optional>> getVideoModes() { - if (SDL_Init(SDL_INIT_VIDEO)) + if (!SDL_Init(SDL_INIT_VIDEO)) { return std::nullopt; } std::vector> modeList; - auto displayModes = SDL_GetNumDisplayModes(0); - for (int i = 0; i < displayModes; ++i) + auto primaryDisplay = SDL_GetPrimaryDisplay(); + int count = 0; + auto** modes = SDL_GetFullscreenDisplayModes(primaryDisplay, &count); + if (modes) { - SDL_DisplayMode mode; - if (SDL_GetDisplayMode(0, i, &mode) != 0) + for (int i = 0; i < count; ++i) { - continue; - } - if (mode.format != SDL_PIXELFORMAT_RGB888 || mode.refresh_rate != 60) - { - continue; + const auto* mode = modes[i]; + if (mode->format != SDL_PIXELFORMAT_XRGB8888 || mode->refresh_rate != 60.0f) + { + continue; + } + modeList.emplace_back(mode->w, mode->h); } - modeList.emplace_back(mode.w, mode.h); + SDL_free(modes); } SDL_Quit(); return modeList; diff --git a/src/main.cpp b/src/main.cpp index dbbb841f0..bec0bcab0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -85,19 +85,19 @@ namespace rwe << requiredVersion.version.minorVersion << ", " << getOpenGlProfileName(requiredVersion.profile) << " profile"; - if (sdlContext->glSetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, requiredVersion.version.majorVersion) != 0) + if (!sdlContext->glSetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, requiredVersion.version.majorVersion)) { return Err(SDL_GetError()); } - if (sdlContext->glSetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, requiredVersion.version.minorVersion) != 0) + if (!sdlContext->glSetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, requiredVersion.version.minorVersion)) { return Err(SDL_GetError()); } - if (sdlContext->glSetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, getSdlProfileMask(requiredVersion.profile)) != 0) + if (!sdlContext->glSetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, getSdlProfileMask(requiredVersion.profile))) { return Err(SDL_GetError()); } - if (sdlContext->glSetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG) != 0) + if (!sdlContext->glSetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG)) { return Err(SDL_GetError()); } @@ -135,15 +135,13 @@ namespace rwe auto sdlContext = sdlManager.getSdlContext(); // require a stencil buffer of some kind - if (sdlContext->glSetAttribute(SDL_GL_STENCIL_SIZE, 1) != 0) + if (!sdlContext->glSetAttribute(SDL_GL_STENCIL_SIZE, 1)) { throw std::runtime_error(SDL_GetError()); } auto window = sdlContext->createWindow( "RWE", - SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, desiredWindowWidth, desiredWindowHeight, SDL_WINDOW_OPENGL | (fullscreen ? SDL_WINDOW_FULLSCREEN : 0)); @@ -154,25 +152,19 @@ namespace rwe if (fullscreen) { - SDL_DisplayMode desiredMode; - desiredMode.w = desiredWindowWidth; - desiredMode.h = desiredWindowHeight; - desiredMode.refresh_rate = 0; - desiredMode.format = 0; - desiredMode.driverdata = 0; SDL_DisplayMode targetMode; - auto displayIndex = sdlContext->getWindowDisplayIndex(window.get()); - if (displayIndex < 0) + auto displayID = sdlContext->getWindowDisplayIndex(window.get()); + if (displayID == 0) { throw std::runtime_error(SDL_GetError()); } - if (sdlContext->getClosestDisplayMode(displayIndex, &desiredMode, &targetMode) == nullptr) + if (!sdlContext->getClosestDisplayMode(displayID, desiredWindowWidth, desiredWindowHeight, 0.0f, &targetMode)) { throw std::runtime_error(SDL_GetError()); } - if (sdlContext->setWindowDisplayMode(window.get(), &targetMode) != 0) + if (!sdlContext->setWindowDisplayMode(window.get(), &targetMode)) { throw std::runtime_error(SDL_GetError()); } @@ -182,7 +174,7 @@ namespace rwe // Prevent the mouse from leaving the window. // We rely on nudging the edges of the screen to pan the camera, // so this is necessary for the game to work. - sdlContext->setWindowGrab(window.get(), SDL_TRUE); + sdlContext->setWindowGrab(window.get(), true); int windowWidth; int windowHeight; @@ -293,7 +285,7 @@ namespace rwe cursors[*CursorType::Green] = textureService.getGafEntry("anims/CURSORS.GAF", "cursorgrn"); CursorService cursor(sdlContext, &timeService, cursors); - sdlContext->showCursor(SDL_DISABLE); + sdlContext->hideCursor(); SceneManager sceneManager(sdlContext, window.get(), &graphics, &timeService, &imGuiContext, &cursor, &globalConfig, UiRenderService(&graphics, &shaders, &viewport), &viewport); diff --git a/src/rwe/AudioService.cpp b/src/rwe/AudioService.cpp index b81f75832..5ec2944ce 100644 --- a/src/rwe/AudioService.cpp +++ b/src/rwe/AudioService.cpp @@ -1,8 +1,8 @@ #include "AudioService.h" -#include namespace rwe { +#if 0 // TODO: SDL3_mixer migration AudioService::AudioService( SdlContext* sdlContext, SdlMixerContext* sdlMixerContext, @@ -83,6 +83,53 @@ namespace rwe { sdlMixerContext->volume(channel, volume); } +#else + AudioService::AudioService( + SdlContext* sdlContext, + SdlMixerContext* sdlMixerContext, + AbstractVirtualFileSystem* fileSystem) + : sdlContext(sdlContext), + sdlMixerContext(sdlMixerContext), + fileSystem(fileSystem) + { + } + + AudioService::LoopToken AudioService::loopSound(const SoundHandle& /*sound*/) + { + return LoopToken(); + } + + int AudioService::playSound(const SoundHandle& /*sound*/) + { + return -1; + } + + std::optional AudioService::loadSound(const std::string& /*soundName*/) + { + return std::nullopt; + } + + void AudioService::reserveChannels(unsigned int /*count*/) + { + } + + void AudioService::haltChannel(int /*channel*/) + { + } + + void AudioService::playSoundIfFree(const AudioService::SoundHandle& /*sound*/, unsigned int /*channel*/) + { + } + + Observable& AudioService::getChannelFinished() + { + return channelFinished; + } + + void AudioService::setVolume(int /*channel*/, int /*volume*/) + { + } +#endif AudioService::LoopToken::LoopToken(AudioService* audioService, int channel, const AudioService::SoundHandle& sound) : audioService(audioService), channel(channel), sound(sound) diff --git a/src/rwe/AudioService.h b/src/rwe/AudioService.h index e931f5783..70bb61211 100644 --- a/src/rwe/AudioService.h +++ b/src/rwe/AudioService.h @@ -13,8 +13,13 @@ namespace rwe class AudioService { public: +#if 0 // TODO: SDL3_mixer migration using Sound = Mix_Chunk; using SoundHandle = std::shared_ptr; +#else + struct Sound {}; + using SoundHandle = std::shared_ptr; +#endif class LoopToken { @@ -40,7 +45,6 @@ namespace rwe AbstractVirtualFileSystem* fileSystem; std::unordered_map> soundBank; Subject channelFinished; - std::function channelFinishedCallback = std::bind(&Subject::next, &channelFinished, std::placeholders::_1); public: AudioService(SdlContext* sdlContext, SdlMixerContext* sdlMixerContext, AbstractVirtualFileSystem* fileSystem); diff --git a/src/rwe/CursorService.cpp b/src/rwe/CursorService.cpp index 7fe1bc958..740c56bd1 100644 --- a/src/rwe/CursorService.cpp +++ b/src/rwe/CursorService.cpp @@ -31,10 +31,12 @@ namespace rwe void CursorService::render(UiRenderService& renderer) const { - int x; - int y; + float fx; + float fy; - sdlContext->getMouseState(&x, &y); + sdlContext->getMouseState(&fx, &fy); + int x = static_cast(fx); + int y = static_cast(fy); auto timeInMillis = timeService->getTicks(); const auto& frames = currentCursor->sprites; diff --git a/src/rwe/ImGuiContext.cpp b/src/rwe/ImGuiContext.cpp index f91dd6b42..4c91822cc 100644 --- a/src/rwe/ImGuiContext.cpp +++ b/src/rwe/ImGuiContext.cpp @@ -1,12 +1,12 @@ #include "ImGuiContext.h" -#include +#include namespace rwe { ImGuiContext::~ImGuiContext() { ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplSDL2_Shutdown(); + ImGui_ImplSDL3_Shutdown(); ImGui::DestroyContext(); } @@ -14,7 +14,7 @@ namespace rwe { if (io.WantCaptureKeyboard) { - if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP || event.type == SDL_TEXTINPUT) + if (event.type == SDL_EVENT_KEY_DOWN || event.type == SDL_EVENT_KEY_UP || event.type == SDL_EVENT_TEXT_INPUT) { return true; } @@ -22,7 +22,7 @@ namespace rwe if (io.WantCaptureMouse) { - if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEMOTION || event.type == SDL_MOUSEWHEEL) + if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN || event.type == SDL_EVENT_MOUSE_BUTTON_UP || event.type == SDL_EVENT_MOUSE_MOTION || event.type == SDL_EVENT_MOUSE_WHEEL) { return true; } @@ -35,7 +35,7 @@ namespace rwe { if (wantsEvent(*io, event)) { - ImGui_ImplSDL2_ProcessEvent(&event); + ImGui_ImplSDL3_ProcessEvent(&event); return true; } return false; @@ -49,14 +49,14 @@ namespace rwe io->IniFilename = this->iniPath.data(); io->ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; ImGui::StyleColorsDark(); - ImGui_ImplSDL2_InitForOpenGL(window, glContext); - ImGui_ImplOpenGL3_Init("#version 130"); + ImGui_ImplSDL3_InitForOpenGL(window, glContext); + ImGui_ImplOpenGL3_Init("#version 150"); } void ImGuiContext::newFrame(SDL_Window* window) { ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplSDL2_NewFrame(); + ImGui_ImplSDL3_NewFrame(); ImGui::NewFrame(); } diff --git a/src/rwe/ImGuiContext.h b/src/rwe/ImGuiContext.h index 6f173687e..3d35fb181 100644 --- a/src/rwe/ImGuiContext.h +++ b/src/rwe/ImGuiContext.h @@ -2,7 +2,7 @@ #include #include -#include +#include #include namespace rwe diff --git a/src/rwe/MainMenuScene.cpp b/src/rwe/MainMenuScene.cpp index a4f9f76ce..c8a6d0f97 100644 --- a/src/rwe/MainMenuScene.cpp +++ b/src/rwe/MainMenuScene.cpp @@ -155,9 +155,9 @@ namespace rwe topPanel().mouseWheel(event); } - void MainMenuScene::onKeyDown(const SDL_Keysym& keysym) + void MainMenuScene::onKeyDown(const SDL_KeyboardEvent& keysym) { - topPanel().keyDown(KeyEvent(keysym.sym)); + topPanel().keyDown(KeyEvent(keysym.key)); } void MainMenuScene::goToMainMenu() diff --git a/src/rwe/MainMenuScene.h b/src/rwe/MainMenuScene.h index ca95b3e64..7cf56437c 100644 --- a/src/rwe/MainMenuScene.h +++ b/src/rwe/MainMenuScene.h @@ -55,7 +55,7 @@ namespace rwe void onMouseWheel(MouseWheelEvent event) override; - void onKeyDown(const SDL_Keysym& keysym) override; + void onKeyDown(const SDL_KeyboardEvent& keysym) override; void update(int millisecondsElapsed) override; diff --git a/src/rwe/game/GameScene.cpp b/src/rwe/game/GameScene.cpp index fd7d24371..d152f82f1 100644 --- a/src/rwe/game/GameScene.cpp +++ b/src/rwe/game/GameScene.cpp @@ -1198,47 +1198,47 @@ namespace rwe ImGui::End(); } - void GameScene::onKeyDown(const SDL_Keysym& keysym) + void GameScene::onKeyDown(const SDL_KeyboardEvent& keysym) { - currentPanel->keyDown(KeyEvent(keysym.sym)); + currentPanel->keyDown(KeyEvent(keysym.key)); - if (keysym.sym == SDLK_UP) + if (keysym.key == SDLK_UP) { up = true; } - else if (keysym.sym == SDLK_DOWN) + else if (keysym.key == SDLK_DOWN) { down = true; } - else if (keysym.sym == SDLK_LEFT) + else if (keysym.key == SDLK_LEFT) { left = true; } - else if (keysym.sym == SDLK_RIGHT) + else if (keysym.key == SDLK_RIGHT) { right = true; } - else if (keysym.sym == SDLK_LCTRL) + else if (keysym.key == SDLK_LCTRL) { leftCtrlDown = true; } - else if (keysym.sym == SDLK_RCTRL) + else if (keysym.key == SDLK_RCTRL) { rightCtrlDown = true; } - else if (keysym.sym == SDLK_LSHIFT) + else if (keysym.key == SDLK_LSHIFT) { leftShiftDown = true; } - else if (keysym.sym == SDLK_RSHIFT) + else if (keysym.key == SDLK_RSHIFT) { rightShiftDown = true; } - else if (keysym.sym == SDLK_ESCAPE) + else if (keysym.key == SDLK_ESCAPE) { handleEscapeDown(); } - else if (keysym.sym == SDLK_F10) + else if (keysym.key == SDLK_F10) { showDebugWindow = true; } @@ -1246,11 +1246,11 @@ namespace rwe { healthBarsVisible = !healthBarsVisible; } - else if (keysym.sym == SDLK_t) + else if (keysym.key == SDLK_T) { startTrack(); } - else if (keysym.sym == SDLK_c) + else if (keysym.key == SDLK_C) { if (isCtrlDown()) { @@ -1281,39 +1281,39 @@ namespace rwe } } - void GameScene::onKeyUp(const SDL_Keysym& keysym) + void GameScene::onKeyUp(const SDL_KeyboardEvent& keysym) { - currentPanel->keyUp(KeyEvent(keysym.sym)); + currentPanel->keyUp(KeyEvent(keysym.key)); - if (keysym.sym == SDLK_UP) + if (keysym.key == SDLK_UP) { up = false; } - else if (keysym.sym == SDLK_DOWN) + else if (keysym.key == SDLK_DOWN) { down = false; } - else if (keysym.sym == SDLK_LEFT) + else if (keysym.key == SDLK_LEFT) { left = false; } - else if (keysym.sym == SDLK_RIGHT) + else if (keysym.key == SDLK_RIGHT) { right = false; } - else if (keysym.sym == SDLK_LCTRL) + else if (keysym.key == SDLK_LCTRL) { leftCtrlDown = false; } - else if (keysym.sym == SDLK_RCTRL) + else if (keysym.key == SDLK_RCTRL) { rightCtrlDown = false; } - else if (keysym.sym == SDLK_LSHIFT) + else if (keysym.key == SDLK_LSHIFT) { leftShiftDown = false; } - else if (keysym.sym == SDLK_RSHIFT) + else if (keysym.key == SDLK_RSHIFT) { rightShiftDown = false; } @@ -2445,10 +2445,10 @@ namespace rwe Point GameScene::getMousePosition() const { - int x; - int y; - sceneContext.sdl->getMouseState(&x, &y); - return Point(x, y); + float fx; + float fy; + sceneContext.sdl->getMouseState(&fx, &fy); + return Point(static_cast(fx), static_cast(fy)); } std::optional GameScene::getFirstCollidingUnit(const Ray3f& ray) const diff --git a/src/rwe/game/GameScene.h b/src/rwe/game/GameScene.h index 6bde0ff1e..e1f55389b 100644 --- a/src/rwe/game/GameScene.h +++ b/src/rwe/game/GameScene.h @@ -332,9 +332,9 @@ namespace rwe void render() override; - void onKeyDown(const SDL_Keysym& keysym) override; + void onKeyDown(const SDL_KeyboardEvent& keysym) override; - void onKeyUp(const SDL_Keysym& keysym) override; + void onKeyUp(const SDL_KeyboardEvent& keysym) override; void onMouseDown(MouseButtonEvent event) override; diff --git a/src/rwe/render/GraphicsContext.h b/src/rwe/render/GraphicsContext.h index 4f5c7dfef..1ee3ba4b7 100644 --- a/src/rwe/render/GraphicsContext.h +++ b/src/rwe/render/GraphicsContext.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include #include diff --git a/src/rwe/render/OpenGlVersion.h b/src/rwe/render/OpenGlVersion.h index 5847cc2e2..eccaf8577 100644 --- a/src/rwe/render/OpenGlVersion.h +++ b/src/rwe/render/OpenGlVersion.h @@ -21,7 +21,7 @@ namespace rwe throw std::logic_error("Unknown profile"); } - SDL_GLprofile getSdlProfileMask(OpenGlProfile profile) + SDL_GLProfile getSdlProfileMask(OpenGlProfile profile) { switch (profile) { diff --git a/src/rwe/scene/Scene.h b/src/rwe/scene/Scene.h index f93e9b398..71dfcb183 100644 --- a/src/rwe/scene/Scene.h +++ b/src/rwe/scene/Scene.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include namespace rwe @@ -14,9 +14,9 @@ namespace rwe virtual void render() {} - virtual void onKeyDown(const SDL_Keysym& /*key*/) {} + virtual void onKeyDown(const SDL_KeyboardEvent& /*key*/) {} - virtual void onKeyUp(const SDL_Keysym& /*key*/) {} + virtual void onKeyUp(const SDL_KeyboardEvent& /*key*/) {} virtual void onMouseDown(MouseButtonEvent /*event*/) {} diff --git a/src/rwe/scene/SceneManager.cpp b/src/rwe/scene/SceneManager.cpp index fbcb96924..1f8cf24c3 100644 --- a/src/rwe/scene/SceneManager.cpp +++ b/src/rwe/scene/SceneManager.cpp @@ -50,13 +50,13 @@ namespace rwe { switch (event.type) { - case SDL_KEYDOWN: - currentScene.onKeyDown(event.key.keysym); + case SDL_EVENT_KEY_DOWN: + currentScene.onKeyDown(event.key); break; - case SDL_KEYUP: - currentScene.onKeyUp(event.key.keysym); + case SDL_EVENT_KEY_UP: + currentScene.onKeyUp(event.key); break; - case SDL_MOUSEBUTTONDOWN: + case SDL_EVENT_MOUSE_BUTTON_DOWN: { auto button = convertSdlMouseButton(event.button.button); if (!button) @@ -68,7 +68,7 @@ namespace rwe currentScene.onMouseDown(e); break; } - case SDL_MOUSEBUTTONUP: + case SDL_EVENT_MOUSE_BUTTON_UP: { auto button = convertSdlMouseButton(event.button.button); if (!button) @@ -80,13 +80,13 @@ namespace rwe currentScene.onMouseUp(e); break; } - case SDL_MOUSEMOTION: + case SDL_EVENT_MOUSE_MOTION: { MouseMoveEvent e(event.motion.x, event.motion.y); currentScene.onMouseMove(e); break; } - case SDL_MOUSEWHEEL: + case SDL_EVENT_MOUSE_WHEEL: { MouseWheelEvent e(event.wheel.x, event.wheel.y); currentScene.onMouseWheel(e); @@ -120,24 +120,21 @@ namespace rwe continue; } - if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_F11) + if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_F11) { showDebugWindow = true; continue; } - if (event.type == SDL_QUIT) + if (event.type == SDL_EVENT_QUIT) { return; } - if (event.type == SDL_WINDOWEVENT) + if (event.type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED && event.window.windowID == sdl->getWindowId(window)) { - if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED && event.window.windowID == sdl->getWindowId(window)) - { - viewport->setDimensions(event.window.data1, event.window.data2); - continue; - } + viewport->setDimensions(event.window.data1, event.window.data2); + continue; } dispatchToScene(event, *currentScene); @@ -165,7 +162,7 @@ namespace rwe if (!imGuiContext->io->WantCaptureMouse) { - sdl->showCursor(SDL_FALSE); + sdl->hideCursor(); cursorService->render(uiRenderService); } @@ -201,12 +198,12 @@ namespace rwe ImGui::Separator(); if (ImGui::Button("Grab Mouse")) { - sdl->setWindowGrab(window, SDL_TRUE); + sdl->setWindowGrab(window, true); } ImGui::SameLine(); if (ImGui::Button("Ungrab Mouse")) { - sdl->setWindowGrab(window, SDL_FALSE); + sdl->setWindowGrab(window, false); } ImGui::Separator(); if (ImGui::Button("Show Demo Window")) diff --git a/src/rwe/sdl/SdlContext.h b/src/rwe/sdl/SdlContext.h index 13e6d7521..e21dfe7cd 100644 --- a/src/rwe/sdl/SdlContext.h +++ b/src/rwe/sdl/SdlContext.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -16,20 +16,20 @@ namespace rwe struct GlContextDeleter { - void operator()(SDL_GLContext context) { SDL_GL_DeleteContext(context); } + void operator()(SDL_GLContextState* context) { SDL_GL_DestroyContext(context); } }; struct RWopsDeleter { - void operator()(SDL_RWops* rw) { SDL_RWclose(rw); } + void operator()(SDL_IOStream* rw) { SDL_CloseIO(rw); } }; - using GlContextUniquePtr = std::unique_ptr; + using GlContextUniquePtr = std::unique_ptr; private: SdlContext() { - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) != 0) + if (!SDL_Init(SDL_INIT_VIDEO)) // TODO: re-add SDL_INIT_AUDIO after SDL3_mixer migration { throw rwe::SDLException(SDL_GetError()); } @@ -41,9 +41,9 @@ namespace rwe } public: - std::unique_ptr createWindow(const char* title, int x, int y, int w, int h, Uint32 flags) + std::unique_ptr createWindow(const char* title, int w, int h, SDL_WindowFlags flags) { - return std::unique_ptr(SDL_CreateWindow(title, x, y, w, h, flags)); + return std::unique_ptr(SDL_CreateWindow(title, w, h, flags)); } GlContextUniquePtr glCreateContext(SDL_Window* window) @@ -51,12 +51,12 @@ namespace rwe return GlContextUniquePtr(SDL_GL_CreateContext(window)); } - std::unique_ptr rwFromConstMem(const void* mem, int size) + std::unique_ptr rwFromConstMem(const void* mem, size_t size) { - return std::unique_ptr(SDL_RWFromConstMem(mem, size)); + return std::unique_ptr(SDL_IOFromConstMem(mem, size)); }; - Uint32 getTicks() + Uint64 getTicks() { return SDL_GetTicks(); } @@ -73,25 +73,30 @@ namespace rwe bool pollEvent(SDL_Event* event) { - return SDL_PollEvent(event) == 1; + return SDL_PollEvent(event); } - Uint32 getMouseState(int* x, int* y) + SDL_MouseButtonFlags getMouseState(float* x, float* y) { return SDL_GetMouseState(x, y); } - int showCursor(int toggle) + void showCursor() { - return SDL_ShowCursor(toggle); + SDL_ShowCursor(); } - int glSetAttribute(SDL_GLattr attr, int value) + void hideCursor() + { + SDL_HideCursor(); + } + + bool glSetAttribute(SDL_GLAttr attr, int value) { return SDL_GL_SetAttribute(attr, value); } - int glSetSwapInterval(int interval) + bool glSetSwapInterval(int interval) { return SDL_GL_SetSwapInterval(interval); } @@ -101,32 +106,32 @@ namespace rwe SDL_GetWindowSize(window, w, h); } - int getWindowDisplayIndex(SDL_Window* window) + SDL_DisplayID getWindowDisplayIndex(SDL_Window* window) { - return SDL_GetWindowDisplayIndex(window); + return SDL_GetDisplayForWindow(window); } - SDL_DisplayMode* getClosestDisplayMode(int displayIndex, const SDL_DisplayMode* mode, SDL_DisplayMode* closest) + bool getClosestDisplayMode(SDL_DisplayID displayID, int w, int h, float refreshRate, SDL_DisplayMode* closest) { - return SDL_GetClosestDisplayMode(displayIndex, mode, closest); + return SDL_GetClosestFullscreenDisplayMode(displayID, w, h, refreshRate, false, closest); } - int setWindowDisplayMode(SDL_Window* window, const SDL_DisplayMode* mode) + bool setWindowDisplayMode(SDL_Window* window, const SDL_DisplayMode* mode) { - return SDL_SetWindowDisplayMode(window, mode); + return SDL_SetWindowFullscreenMode(window, mode); } - void setWindowSize(SDL_Window* window, int width, int height) + bool setWindowSize(SDL_Window* window, int width, int height) { return SDL_SetWindowSize(window, width, height); } - void setWindowGrab(SDL_Window* window, SDL_bool grabbed) + void setWindowGrab(SDL_Window* window, bool grabbed) { - SDL_SetWindowGrab(window, grabbed); + SDL_SetWindowMouseGrab(window, grabbed); } - Uint32 getWindowId(SDL_Window* window) + SDL_WindowID getWindowId(SDL_Window* window) { return SDL_GetWindowID(window); } diff --git a/src/rwe/sdl/SdlContextManager.cpp b/src/rwe/sdl/SdlContextManager.cpp index 1e100b202..6baf632a7 100644 --- a/src/rwe/sdl/SdlContextManager.cpp +++ b/src/rwe/sdl/SdlContextManager.cpp @@ -17,9 +17,4 @@ namespace rwe return &sdlMixerContext; } - const SdlImageContext* SdlContextManager::getSdlImageContext() const - { - return &sdlImageContext; - } - } diff --git a/src/rwe/sdl/SdlContextManager.h b/src/rwe/sdl/SdlContextManager.h index 29463b1c0..72f7e9b04 100644 --- a/src/rwe/sdl/SdlContextManager.h +++ b/src/rwe/sdl/SdlContextManager.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include namespace rwe @@ -22,11 +21,9 @@ namespace rwe const rwe::SdlContext* getSdlContext() const; rwe::SdlContext* getSdlContext(); rwe::SdlMixerContext* getSdlMixerContext(); - const SdlImageContext* getSdlImageContext() const; private: rwe::SdlContext sdlContext; rwe::SdlMixerContext sdlMixerContext; - SdlImageContext sdlImageContext; }; } diff --git a/src/rwe/sdl/SdlMixerContext.h b/src/rwe/sdl/SdlMixerContext.h index 5303fbba1..e0dc132ac 100644 --- a/src/rwe/sdl/SdlMixerContext.h +++ b/src/rwe/sdl/SdlMixerContext.h @@ -1,6 +1,7 @@ #pragma once -#include +#if 0 // TODO: SDL3_mixer migration +#include #include #include #include @@ -38,7 +39,7 @@ namespace rwe void operator()(Mix_Chunk* chunk) { Mix_FreeChunk(chunk); } }; - std::unique_ptr loadWavRw(SDL_RWops* src) + std::unique_ptr loadWavRw(SDL_IOStream* src) { return std::unique_ptr(Mix_LoadWAV_RW(src, 0)); }; @@ -86,3 +87,23 @@ namespace rwe } }; } +#else +#include +#include + +namespace rwe +{ + class SdlMixerContext + { + private: + SdlMixerContext() = default; + SdlMixerContext(const SdlMixerContext&) = delete; + ~SdlMixerContext() = default; + + friend class SdlContextManager; + + public: + void allocateChannels(int /*numChans*/) {} + }; +} +#endif From 45eb5f03d96ca52f53b9614dfcb1fd9e3ab939dc Mon Sep 17 00:00:00 2001 From: Kevin Hake Date: Tue, 24 Mar 2026 18:31:16 -0700 Subject: [PATCH 5/8] Migrate audio from SDL2_mixer to SDL3_mixer track-based API - Rewrite SdlMixerContext with MIX_Mixer, MIX_Track, MIX_Audio wrappers - Adapt AudioService from channel-based to track-pool model - Add audio/video dev packages to devbox for SDL3 backend support - Remove dead SdlImageContext/SdlImageException files --- devbox.json | 9 +- devbox.lock | 76 +++++++++++++++ src/main.cpp | 6 +- src/rwe/AudioService.cpp | 167 ++++++++++++++++++++------------ src/rwe/AudioService.h | 20 ++-- src/rwe/sdl/SdlContext.h | 2 +- src/rwe/sdl/SdlImageContext.h | 29 ------ src/rwe/sdl/SdlImageException.h | 12 --- src/rwe/sdl/SdlMixerContext.h | 99 +++++++++---------- 9 files changed, 251 insertions(+), 169 deletions(-) delete mode 100644 src/rwe/sdl/SdlImageContext.h delete mode 100644 src/rwe/sdl/SdlImageException.h diff --git a/devbox.json b/devbox.json index c550ca340..62bec6bfe 100644 --- a/devbox.json +++ b/devbox.json @@ -16,7 +16,14 @@ "libXScrnSaver", "libXcursor", "libXtst", - "libxcb" + "libxcb", + "alsa-lib", + "pipewire", + "pulseaudio", + "wayland", + "wayland-protocols", + "libxkbcommon", + "libdecor" ], "shell": { "init_hook": [ diff --git a/devbox.lock b/devbox.lock index 23d824077..3481a66b7 100644 --- a/devbox.lock +++ b/devbox.lock @@ -1,6 +1,10 @@ { "lockfile_version": "1", "packages": { + "alsa-lib": { + "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#alsa-lib", + "source": "nixpkg" + }, "cmake": { "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#cmake", "source": "nixpkg", @@ -15,6 +19,54 @@ } } }, + "coccinelle@latest": { + "last_modified": "2026-03-21T07:29:51Z", + "resolved": "github:NixOS/nixpkgs/09061f748ee21f68a089cd5d91ec1859cd93d0be#coccinelle", + "source": "devbox-search", + "version": "1.3.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/7q083y67w82v535a8mhrg6lpvwzf6ykr-coccinelle-1.3.0", + "default": true + } + ], + "store_path": "/nix/store/7q083y67w82v535a8mhrg6lpvwzf6ykr-coccinelle-1.3.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/234l2555h53914ypxf16z7s1jjxb80l3-coccinelle-1.3.0", + "default": true + } + ], + "store_path": "/nix/store/234l2555h53914ypxf16z7s1jjxb80l3-coccinelle-1.3.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/yvgc02g1ivr3nbbincm8j8apbgmic9zl-coccinelle-1.3.0", + "default": true + } + ], + "store_path": "/nix/store/yvgc02g1ivr3nbbincm8j8apbgmic9zl-coccinelle-1.3.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/gw7w5wvp7nlf1xj32a7qx6lcibpamagx-coccinelle-1.3.0", + "default": true + } + ], + "store_path": "/nix/store/gw7w5wvp7nlf1xj32a7qx6lcibpamagx-coccinelle-1.3.0" + } + } + }, "git": { "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#git", "source": "nixpkg" @@ -62,6 +114,10 @@ "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#libXtst", "source": "nixpkg" }, + "libdecor": { + "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#libdecor", + "source": "nixpkg" + }, "libpng": { "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#libpng", "source": "nixpkg", @@ -89,10 +145,18 @@ "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#libxext", "source": "nixpkg" }, + "libxkbcommon": { + "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#libxkbcommon", + "source": "nixpkg" + }, "libxrandr": { "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#libxrandr", "source": "nixpkg" }, + "pipewire": { + "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#pipewire", + "source": "nixpkg" + }, "pkg-config": { "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#pkg-config", "source": "nixpkg", @@ -126,6 +190,18 @@ } } }, + "pulseaudio": { + "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#pulseaudio", + "source": "nixpkg" + }, + "wayland": { + "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#wayland", + "source": "nixpkg" + }, + "wayland-protocols": { + "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#wayland-protocols", + "source": "nixpkg" + }, "zlib": { "resolved": "github:NixOS/nixpkgs/724cf38d99ba81fbb4a347081db93e2e3a9bc2ae?narHash=sha256-MpAKyXfJRDTgRU33Hja%2BG%2B3h9ywLAJJNRq4Pjbb4dQs%3D#zlib", "source": "nixpkg", diff --git a/src/main.cpp b/src/main.cpp index bec0bcab0..84dab3d5a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -128,10 +128,6 @@ namespace rwe LOG_INFO << "Initializing SDL"; SdlContextManager sdlManager; - // Set a reasonable number of audio channels - // so that we always hear unit sounds. - sdlManager.getSdlMixerContext()->allocateChannels(256); - auto sdlContext = sdlManager.getSdlContext(); // require a stencil buffer of some kind @@ -261,6 +257,8 @@ namespace rwe TextureService textureService(&graphics, &vfs, &*palette); AudioService audioService(sdlContext, sdlManager.getSdlMixerContext(), &vfs); + // Allocate a pool of tracks for sound playback + audioService.allocateTracks(256); // load sound definitions LOG_INFO << "Loading global sound definitions"; diff --git a/src/rwe/AudioService.cpp b/src/rwe/AudioService.cpp index 5ec2944ce..973d4157e 100644 --- a/src/rwe/AudioService.cpp +++ b/src/rwe/AudioService.cpp @@ -2,7 +2,6 @@ namespace rwe { -#if 0 // TODO: SDL3_mixer migration AudioService::AudioService( SdlContext* sdlContext, SdlMixerContext* sdlMixerContext, @@ -11,30 +10,99 @@ namespace rwe sdlMixerContext(sdlMixerContext), fileSystem(fileSystem) { - this->sdlMixerContext->channelFinished(std::bind(&Subject::next, &channelFinished, std::placeholders::_1)); + } + + void AudioService::allocateTracks(unsigned int count) + { + tracks.reserve(count); + for (unsigned int i = tracks.size(); i < count; ++i) + { + auto track = sdlMixerContext->createTrack(); + if (!track) + { + throw std::runtime_error("Failed to create mixer track"); + } + tracks.push_back(std::move(track)); + setupTrackCallback(i); + } + } + + void AudioService::setupTrackCallback(int trackIndex) + { + sdlMixerContext->setTrackStoppedCallback( + tracks[trackIndex].get(), + [](void* userdata, MIX_Track* track) + { + auto* self = static_cast(userdata); + for (unsigned int i = 0; i < self->tracks.size(); ++i) + { + if (self->tracks[i].get() == track) + { + self->channelFinished.next(static_cast(i)); + break; + } + } + }, + this); + } + + int AudioService::findFreeTrack() + { + // Search unreserved tracks for a free one + for (unsigned int i = reservedCount; i < tracks.size(); ++i) + { + if (!sdlMixerContext->trackPlaying(tracks[i].get())) + { + return static_cast(i); + } + } + return -1; } AudioService::LoopToken AudioService::loopSound(const SoundHandle& sound) { - auto channel = sdlMixerContext->playChannel(-1, sound.get(), -1); + int channel = findFreeTrack(); + if (channel == -1) + { + return LoopToken(); + } + + auto* track = tracks[channel].get(); + sdlMixerContext->setTrackAudio(track, sound.get()); + sdlMixerContext->setTrackGain(track, defaultGain); + + auto props = SDL_CreateProperties(); + SDL_SetNumberProperty(props, MIX_PROP_PLAY_LOOPS_NUMBER, -1); + sdlMixerContext->playTrack(track, props); + SDL_DestroyProperties(props); + return LoopToken(this, channel, sound); } int AudioService::playSound(const SoundHandle& sound) { - return sdlMixerContext->playChannel(-1, sound.get(), 0); + int channel = findFreeTrack(); + if (channel == -1) + { + return -1; + } + + auto* track = tracks[channel].get(); + sdlMixerContext->setTrackAudio(track, sound.get()); + sdlMixerContext->setTrackGain(track, defaultGain); + sdlMixerContext->playTrack(track); + + return channel; } std::optional AudioService::loadSound(const std::string& soundName) { - auto soundIter = soundBank.find(soundName); if (soundIter != soundBank.end()) { return soundIter->second; } - auto bytes = fileSystem->readFile("sounds/" + soundName + ".WAV"); if (!bytes) { @@ -42,8 +110,13 @@ namespace rwe } auto rwOps = sdlContext->rwFromConstMem(bytes->data(), bytes->size()); - std::shared_ptr sound(sdlMixerContext->loadWavRw(rwOps.get())); - sdlMixerContext->volumeChunk(sound.get(), MIX_MAX_VOLUME / 4); + auto audio = sdlMixerContext->loadAudioIO(rwOps.get(), true, false); + if (!audio) + { + return std::nullopt; + } + + std::shared_ptr sound(audio.release(), [](MIX_Audio* a) { MIX_DestroyAudio(a); }); soundBank[soundName] = sound; return sound; @@ -51,74 +124,37 @@ namespace rwe void AudioService::reserveChannels(unsigned int count) { - auto num = sdlMixerContext->reserveChannels(count); - assert(num >= 0); - if (static_cast(num) < count) + if (count > tracks.size()) { - throw std::runtime_error("Failed to reserve audio channels"); + allocateTracks(count); } + reservedCount = count; } void AudioService::haltChannel(int channel) { - sdlMixerContext->haltChannel(channel); + if (channel >= 0 && static_cast(channel) < tracks.size()) + { + sdlMixerContext->stopTrack(tracks[channel].get()); + } } void AudioService::playSoundIfFree(const AudioService::SoundHandle& sound, unsigned int channel) { - if (sdlMixerContext->playing(channel)) + if (channel >= tracks.size()) { return; } - sdlMixerContext->playChannel(channel, sound.get(), 0); - } - - Observable& AudioService::getChannelFinished() - { - return channelFinished; - } - - void AudioService::setVolume(int channel, int volume) - { - sdlMixerContext->volume(channel, volume); - } -#else - AudioService::AudioService( - SdlContext* sdlContext, - SdlMixerContext* sdlMixerContext, - AbstractVirtualFileSystem* fileSystem) - : sdlContext(sdlContext), - sdlMixerContext(sdlMixerContext), - fileSystem(fileSystem) - { - } - - AudioService::LoopToken AudioService::loopSound(const SoundHandle& /*sound*/) - { - return LoopToken(); - } - - int AudioService::playSound(const SoundHandle& /*sound*/) - { - return -1; - } - - std::optional AudioService::loadSound(const std::string& /*soundName*/) - { - return std::nullopt; - } - - void AudioService::reserveChannels(unsigned int /*count*/) - { - } - - void AudioService::haltChannel(int /*channel*/) - { - } + if (sdlMixerContext->trackPlaying(tracks[channel].get())) + { + return; + } - void AudioService::playSoundIfFree(const AudioService::SoundHandle& /*sound*/, unsigned int /*channel*/) - { + auto* track = tracks[channel].get(); + sdlMixerContext->setTrackAudio(track, sound.get()); + sdlMixerContext->setTrackGain(track, defaultGain); + sdlMixerContext->playTrack(track); } Observable& AudioService::getChannelFinished() @@ -126,10 +162,15 @@ namespace rwe return channelFinished; } - void AudioService::setVolume(int /*channel*/, int /*volume*/) + void AudioService::setVolume(int channel, int volume) { + if (channel >= 0 && static_cast(channel) < tracks.size()) + { + // Old scale: 0-128 (MIX_MAX_VOLUME). New scale: 0.0-1.0 + float gain = static_cast(volume) / 128.0f; + sdlMixerContext->setTrackGain(tracks[channel].get(), gain); + } } -#endif AudioService::LoopToken::LoopToken(AudioService* audioService, int channel, const AudioService::SoundHandle& sound) : audioService(audioService), channel(channel), sound(sound) diff --git a/src/rwe/AudioService.h b/src/rwe/AudioService.h index 70bb61211..c1e7e2226 100644 --- a/src/rwe/AudioService.h +++ b/src/rwe/AudioService.h @@ -7,19 +7,15 @@ #include #include #include +#include namespace rwe { class AudioService { public: -#if 0 // TODO: SDL3_mixer migration - using Sound = Mix_Chunk; + using Sound = MIX_Audio; using SoundHandle = std::shared_ptr; -#else - struct Sound {}; - using SoundHandle = std::shared_ptr; -#endif class LoopToken { @@ -46,6 +42,14 @@ namespace rwe std::unordered_map> soundBank; Subject channelFinished; + // Track pool: maps channel indices to MIX_Track pointers. + // Tracks 0..reservedCount-1 are "reserved" (used by playSoundIfFree). + std::vector tracks; + unsigned int reservedCount{0}; + + // Default gain applied to sounds on load (equivalent to old MIX_MAX_VOLUME/4) + static constexpr float defaultGain = 0.25f; + public: AudioService(SdlContext* sdlContext, SdlMixerContext* sdlMixerContext, AbstractVirtualFileSystem* fileSystem); AudioService(const AudioService&) = delete; @@ -53,6 +57,8 @@ namespace rwe AudioService& operator=(const AudioService&) = delete; AudioService& operator=(AudioService&&) = delete; + void allocateTracks(unsigned int count); + LoopToken loopSound(const SoundHandle& sound); int playSound(const SoundHandle& sound); @@ -69,5 +75,7 @@ namespace rwe private: void haltChannel(int channel); + int findFreeTrack(); + void setupTrackCallback(int trackIndex); }; } diff --git a/src/rwe/sdl/SdlContext.h b/src/rwe/sdl/SdlContext.h index e21dfe7cd..83be10173 100644 --- a/src/rwe/sdl/SdlContext.h +++ b/src/rwe/sdl/SdlContext.h @@ -29,7 +29,7 @@ namespace rwe private: SdlContext() { - if (!SDL_Init(SDL_INIT_VIDEO)) // TODO: re-add SDL_INIT_AUDIO after SDL3_mixer migration + if (!SDL_Init(SDL_INIT_VIDEO)) { throw rwe::SDLException(SDL_GetError()); } diff --git a/src/rwe/sdl/SdlImageContext.h b/src/rwe/sdl/SdlImageContext.h deleted file mode 100644 index 4574c770c..000000000 --- a/src/rwe/sdl/SdlImageContext.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include -#include - -namespace rwe -{ - class SdlImageContext - { - private: - SdlImageContext() - { - - int flags = IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF; - if ((IMG_Init(flags) & flags) != flags) - { - throw rwe::SDLImageException(IMG_GetError()); - } - } - SdlImageContext(const SdlImageContext&) = delete; - ~SdlImageContext() - { - - IMG_Quit(); - } - - friend class SdlContextManager; - }; -} diff --git a/src/rwe/sdl/SdlImageException.h b/src/rwe/sdl/SdlImageException.h deleted file mode 100644 index 7b437f902..000000000 --- a/src/rwe/sdl/SdlImageException.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -namespace rwe -{ - class SDLImageException : public std::runtime_error - { - public: - explicit SDLImageException(const char* sdlError) : std::runtime_error(sdlError) {} - }; -} diff --git a/src/rwe/sdl/SdlMixerContext.h b/src/rwe/sdl/SdlMixerContext.h index e0dc132ac..47142f3a8 100644 --- a/src/rwe/sdl/SdlMixerContext.h +++ b/src/rwe/sdl/SdlMixerContext.h @@ -1,6 +1,5 @@ #pragma once -#if 0 // TODO: SDL3_mixer migration #include #include #include @@ -10,100 +9,94 @@ namespace rwe { class SdlMixerContext { + public: + struct AudioDeleter + { + void operator()(MIX_Audio* audio) { MIX_DestroyAudio(audio); } + }; + + struct TrackDeleter + { + void operator()(MIX_Track* track) { MIX_DestroyTrack(track); } + }; + + using AudioPtr = std::unique_ptr; + using TrackPtr = std::unique_ptr; + private: + MIX_Mixer* mixer; + SdlMixerContext() { - int flags = 0; - if ((Mix_Init(flags) & flags) != flags) + if (!MIX_Init()) { - throw SDLMixerException(Mix_GetError()); + throw SDLMixerException("MIX_Init failed"); } - if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024) != 0) + mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, nullptr); + if (!mixer) { - throw SDLMixerException(Mix_GetError()); + MIX_Quit(); + throw SDLMixerException(SDL_GetError()); } } + SdlMixerContext(const SdlMixerContext&) = delete; + ~SdlMixerContext() { - Mix_CloseAudio(); - Mix_Quit(); + MIX_DestroyMixer(mixer); + MIX_Quit(); } friend class SdlContextManager; public: - struct MixChunkDeleter - { - void operator()(Mix_Chunk* chunk) { Mix_FreeChunk(chunk); } - }; + MIX_Mixer* getMixer() { return mixer; } - std::unique_ptr loadWavRw(SDL_IOStream* src) + AudioPtr loadAudioIO(SDL_IOStream* io, bool predecode, bool closeio) { - return std::unique_ptr(Mix_LoadWAV_RW(src, 0)); - }; + return AudioPtr(MIX_LoadAudio_IO(mixer, io, predecode, closeio)); + } - void allocateChannels(int numChans) + TrackPtr createTrack() { - Mix_AllocateChannels(numChans); + return TrackPtr(MIX_CreateTrack(mixer)); } - int playChannel(int channel, Mix_Chunk* chunk, int loops) + bool setTrackAudio(MIX_Track* track, MIX_Audio* audio) { - return Mix_PlayChannel(channel, chunk, loops); + return MIX_SetTrackAudio(track, audio); } - void haltChannel(int channel) + bool playTrack(MIX_Track* track, SDL_PropertiesID options = 0) { - Mix_HaltChannel(channel); + return MIX_PlayTrack(track, options); } - int volumeChunk(Mix_Chunk* chunk, int volume) + bool stopTrack(MIX_Track* track, Sint64 fadeOutFrames = 0) { - return Mix_VolumeChunk(chunk, volume); + return MIX_StopTrack(track, fadeOutFrames); } - int reserveChannels(int num) + bool trackPlaying(MIX_Track* track) { - return Mix_ReserveChannels(num); + return MIX_TrackPlaying(track); } - int playing(int channel) + bool setTrackGain(MIX_Track* track, float gain) { - return Mix_Playing(channel); + return MIX_SetTrackGain(track, gain); } - void channelFinished(std::function f) + bool setTrackStoppedCallback(MIX_Track* track, MIX_TrackStoppedCallback cb, void* userdata) { - static auto cachedF = f; - void (*callback)(int) = [](int channel) { cachedF(channel); }; - return Mix_ChannelFinished(callback); + return MIX_SetTrackStoppedCallback(track, cb, userdata); } - int volume(int channel, int volume) + bool playAudio(MIX_Audio* audio) { - return Mix_Volume(channel, volume); + return MIX_PlayAudio(mixer, audio); } }; } -#else -#include -#include - -namespace rwe -{ - class SdlMixerContext - { - private: - SdlMixerContext() = default; - SdlMixerContext(const SdlMixerContext&) = delete; - ~SdlMixerContext() = default; - - friend class SdlContextManager; - - public: - void allocateChannels(int /*numChans*/) {} - }; -} -#endif From 32b12dab7ac00a1cfb8d38776180507530dc59fa Mon Sep 17 00:00:00 2001 From: Kevin Hake Date: Tue, 24 Mar 2026 18:35:52 -0700 Subject: [PATCH 6/8] Update CI dependencies for SDL3 submodule build --- .github/workflows/build.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e18eb2846..c86e489d4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,12 +39,16 @@ jobs: g++-14 clang-18 ccache - libsdl2-dev - libsdl2-image-dev - libsdl2-mixer-dev libglew-dev zlib1g-dev libpng-dev + libasound2-dev + libpulse-dev + libpipewire-0.3-dev + libwayland-dev + wayland-protocols + libxkbcommon-dev + libdecor-0-dev - name: restore ccache uses: actions/cache@v5 with: @@ -198,15 +202,9 @@ jobs: make unzip mingw-w64-x86_64-toolchain - mingw-w64-x86_64-SDL2 - mingw-w64-x86_64-SDL2_image - mingw-w64-x86_64-SDL2_mixer mingw-w64-x86_64-glew - mingw-w64-x86_64-smpeg2 - mingw-w64-x86_64-libmodplug mingw-w64-x86_64-zlib mingw-w64-x86_64-libpng - mingw-w64-x86_64-readline mingw-w64-x86_64-ccache - name: restore ccache uses: actions/cache@v5 From a527cb7f46cdadd9d857187fd0dc7fb1a758245a Mon Sep 17 00:00:00 2001 From: Kevin Hake Date: Wed, 25 Mar 2026 03:30:07 -0700 Subject: [PATCH 7/8] Fix CI build failures for clang PCH and MinGW EGL - Set POSITION_INDEPENDENT_CODE on rwe_test to match librwe's PCH - Disable SDL_OPENGLES (unused, and MinGW lacks EGL headers) --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index dcd090aaf..106ca9568 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,7 @@ add_subdirectory("libs/json") set(SDL_STATIC ON CACHE BOOL "" FORCE) set(SDL_SHARED OFF CACHE BOOL "" FORCE) set(SDL_TEST_LIBRARY OFF CACHE BOOL "" FORCE) +set(SDL_OPENGLES OFF CACHE BOOL "" FORCE) add_subdirectory("libs/sdl3") set(SDL3MIXER_VENDORED ON CACHE BOOL "" FORCE) @@ -771,6 +772,9 @@ add_executable(rwe_test ${TEST_FILES}) target_link_libraries(rwe_test Catch2::Catch2WithMain) target_link_libraries(rwe_test rapidcheck_catch) target_link_libraries(rwe_test librwe) +# Don't reuse librwe's PCH for rwe_test: librwe is a static library (-fPIC) +# but rwe_test is an executable (-fPIE), and clang's PCH encodes this setting. +set_target_properties(rwe_test PROPERTIES POSITION_INDEPENDENT_CODE ON) target_precompile_headers(rwe_test REUSE_FROM librwe) add_test(NAME rwe_test COMMAND rwe_test) From b2d8a314b14142d8873400160768777532b1dcab Mon Sep 17 00:00:00 2001 From: Kevin Hake Date: Wed, 25 Mar 2026 16:09:23 -0700 Subject: [PATCH 8/8] Reset CMAKE_POSITION_INDEPENDENT_CODE after SDL3_mixer --- CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 106ca9568..4a6a91b72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,7 +56,11 @@ add_subdirectory("libs/sdl3") set(SDL3MIXER_VENDORED ON CACHE BOOL "" FORCE) set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) +# SDL3_mixer's CMakeLists sets CMAKE_POSITION_INDEPENDENT_CODE ON in the cache +# via option(), which leaks into all targets. We don't need PIC for our static +# libraries or executables, so force it off after SDL3_mixer is configured. add_subdirectory("libs/sdl3_mixer") +set(CMAKE_POSITION_INDEPENDENT_CODE OFF CACHE BOOL "" FORCE) function(find_windows_dll var dll) if(WIN32) @@ -772,9 +776,6 @@ add_executable(rwe_test ${TEST_FILES}) target_link_libraries(rwe_test Catch2::Catch2WithMain) target_link_libraries(rwe_test rapidcheck_catch) target_link_libraries(rwe_test librwe) -# Don't reuse librwe's PCH for rwe_test: librwe is a static library (-fPIC) -# but rwe_test is an executable (-fPIE), and clang's PCH encodes this setting. -set_target_properties(rwe_test PROPERTIES POSITION_INDEPENDENT_CODE ON) target_precompile_headers(rwe_test REUSE_FROM librwe) add_test(NAME rwe_test COMMAND rwe_test)