From 24a28db04320d6d06edf2b4aa2ba2172f21c80c2 Mon Sep 17 00:00:00 2001 From: Adrian Conlon Date: Fri, 21 Aug 2026 22:34:24 +0100 Subject: [PATCH] Port from SDL2 -> SDL3 First stab, very choppy sound --- Gaming/inc/Game.h | 20 ++--- Gaming/inc/GameController.h | 18 ++--- Gaming/inc/SDLWrapper.h | 21 ----- Gaming/inc/Wrapper.h | 28 +++++++ Gaming/src/Game.cpp | 124 ++++++++++++++---------------- Gaming/src/GameController.cpp | 57 +++++++------- Gaming/src/Gaming.vcxproj | 12 +-- Gaming/src/Gaming.vcxproj.filters | 4 +- Gaming/src/SDLWrapper.cpp | 14 ---- Gaming/src/Wrapper.cpp | 15 ++++ Gaming/src/stdafx.h | 3 +- 11 files changed, 158 insertions(+), 158 deletions(-) delete mode 100644 Gaming/inc/SDLWrapper.h create mode 100644 Gaming/inc/Wrapper.h delete mode 100644 Gaming/src/SDLWrapper.cpp create mode 100644 Gaming/src/Wrapper.cpp diff --git a/Gaming/inc/Game.h b/Gaming/inc/Game.h index 36224fc..67ba475 100644 --- a/Gaming/inc/Game.h +++ b/Gaming/inc/Game.h @@ -5,11 +5,11 @@ #include #include -#include +#include #include -#include "SDLWrapper.h" +#include "Wrapper.h" class Configuration; @@ -52,7 +52,7 @@ namespace Gaming { void removeJoystick(SDL_Event& e); virtual void updateTexture(); - virtual void copyTexture(); + virtual void renderTexture(); virtual void displayTexture(); [[nodiscard]] virtual const uint32_t* pixels() const = 0; @@ -63,12 +63,12 @@ namespace Gaming { virtual bool handleJoyButtonDown(SDL_JoyButtonEvent event); virtual bool handleJoyButtonUp(SDL_JoyButtonEvent event); - virtual bool handleControllerButtonDown(SDL_ControllerButtonEvent event); - virtual bool handleControllerButtonUp(SDL_ControllerButtonEvent event); + virtual bool handleGamepadButtonDown(SDL_GamepadButtonEvent event); + virtual bool handleGamepadButtonUp(SDL_GamepadButtonEvent event); void toggleFullscreen(); - [[nodiscard]] std::shared_ptr gameController(int which) const; + [[nodiscard]] std::shared_ptr gamepad(int which) const; [[nodiscard]] int mappedController(const SDL_JoystickID which) const; [[nodiscard]] int chooseControllerIndex(int who) const; @@ -76,17 +76,17 @@ namespace Gaming { [[nodiscard]] std::shared_ptr renderer() const noexcept { return m_renderer; } [[nodiscard]] std::shared_ptr bitmapTexture() const noexcept { return m_bitmapTexture; } - [[nodiscard]] std::shared_ptr pixelFormat() const noexcept { return m_pixelFormat; } + [[nodiscard]] const SDL_PixelFormatDetails* pixelFormat() const noexcept { return m_pixelFormat; } private: - SDLWrapper m_wrapper; + Wrapper m_wrapper; std::shared_ptr m_window; std::shared_ptr m_renderer; std::shared_ptr m_bitmapTexture; - std::shared_ptr m_pixelFormat; + const SDL_PixelFormatDetails* m_pixelFormat = nullptr; - Uint32 m_pixelType = SDL_PIXELFORMAT_ARGB8888; + SDL_PixelFormat m_pixelType = SDL_PIXELFORMAT_ARGB8888; bool m_vsync = false; Uint64 m_performanceFrequency = 0; diff --git a/Gaming/inc/GameController.h b/Gaming/inc/GameController.h index 2de7a8f..87c97e1 100644 --- a/Gaming/inc/GameController.h +++ b/Gaming/inc/GameController.h @@ -2,7 +2,7 @@ #include -#include +#include namespace Gaming { class GameController final { @@ -13,26 +13,26 @@ namespace Gaming { void startRumble() noexcept; void stopRumble() noexcept; - [[nodiscard]] static auto buildJoystickId(SDL_GameController* controller) noexcept { - auto joystick = ::SDL_GameControllerGetJoystick(controller); - return ::SDL_JoystickInstanceID(joystick); + [[nodiscard]] static auto buildJoystickId(SDL_Gamepad* gamepad) noexcept { + auto joystick = ::SDL_GetGamepadJoystick(gamepad); + return ::SDL_GetJoystickID(joystick); } [[nodiscard]] auto getJoystickId() const noexcept { - return buildJoystickId(m_gameController.get()); + return buildJoystickId(m_gamepad.get()); } private: int m_index; - std::shared_ptr m_gameController; + std::shared_ptr m_gamepad; void open(); void close() noexcept; - std::shared_ptr m_hapticController; + std::shared_ptr m_haptic; bool m_hapticRumbleSupported = false; - void openHapticController(); - void closeHapticController() noexcept; + void openHaptic(); + void closeHaptic() noexcept; }; } \ No newline at end of file diff --git a/Gaming/inc/SDLWrapper.h b/Gaming/inc/SDLWrapper.h deleted file mode 100644 index e1ef29a..0000000 --- a/Gaming/inc/SDLWrapper.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include - -namespace Gaming { - class SDLWrapper final { - public: - SDLWrapper(bool verbose = false); - ~SDLWrapper(); - - static void throwSDLException(std::string failure) { - throw std::runtime_error(failure + ::SDL_GetError()); - } - - static void verifySDLCall(int returned, std::string failure) { - if (returned < 0) - throwSDLException(failure); - } - }; -} \ No newline at end of file diff --git a/Gaming/inc/Wrapper.h b/Gaming/inc/Wrapper.h new file mode 100644 index 0000000..96c7fd2 --- /dev/null +++ b/Gaming/inc/Wrapper.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include + +namespace Gaming { + class Wrapper final { + public: + Wrapper(bool verbose = false); + ~Wrapper(); + + static void throwException(const std::string& failure) { + std::ostringstream output; + output << "SDL: " << failure << ": " << ::SDL_GetError(); + throw std::runtime_error(output.str()); + } + + static void maybeThrowException(bool success, const std::string& failure) { + if (!success) + throwException(failure); + } + + static void maybeThrowException(void* handle, const std::string& failure) { + maybeThrowException(handle != nullptr, failure); + } + }; +} \ No newline at end of file diff --git a/Gaming/src/Game.cpp b/Gaming/src/Game.cpp index 7893501..a5d092d 100644 --- a/Gaming/src/Game.cpp +++ b/Gaming/src/Game.cpp @@ -32,48 +32,38 @@ void Game::raisePOWER() noexcept { m_window.reset(::SDL_CreateWindow( title().c_str(), - SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, windowWidth(), windowHeight(), - SDL_WINDOW_SHOWN), ::SDL_DestroyWindow); - if (m_window == nullptr) - SDLWrapper::throwSDLException("Unable to create window: "); + 0), ::SDL_DestroyWindow); + Wrapper::maybeThrowException(m_window.get(), "Unable to create window"); - ::SDL_DisplayMode mode; - SDLWrapper::verifySDLCall( - ::SDL_GetWindowDisplayMode(m_window.get(), &mode), - "Unable to obtain window information: "); + const auto displayID = SDL_GetDisplayForWindow(m_window.get()); + const auto* mode = ::SDL_GetCurrentDisplayMode(displayID); + Wrapper::maybeThrowException(mode, "Unable to obtain window information"); + + m_renderer.reset(::SDL_CreateRenderer( + m_window.get(), + nullptr), ::SDL_DestroyRenderer); + Wrapper::maybeThrowException(m_renderer.get(), "Unable to create renderer: "); - Uint32 rendererFlags = 0; m_vsync = useVsync(); if (m_vsync) { - const auto required = fps(); - if (std::abs(required - mode.refresh_rate) < 0.001) { - rendererFlags |= SDL_RENDERER_PRESENTVSYNC; - ::SDL_Log("Attempting to use SDL_RENDERER_PRESENTVSYNC"); + assert(mode != nullptr); + const auto difference = fps() - mode->refresh_rate; + m_vsync = std::abs(difference) < 0.001; + if (m_vsync) { + SDL_Log("Attempting to configure renderer VSYNC"); + m_vsync = SDL_SetRenderVSync(m_renderer.get(), 1); + if (!m_vsync) { + const auto error = SDL_GetError(); + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unable to set render VSYNC (%s)", error); + } } else { - m_vsync = false; - ::SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Display refresh rate is incompatible with required rate (%f)", required); - } - } - m_renderer.reset(::SDL_CreateRenderer(m_window.get(), -1, rendererFlags), ::SDL_DestroyRenderer); - if (m_renderer == nullptr) - SDLWrapper::throwSDLException("Unable to create renderer: "); - - ::SDL_RendererInfo info; - SDLWrapper::verifySDLCall( - ::SDL_GetRendererInfo(m_renderer.get(), &info), - "Unable to obtain renderer information"); - - if (m_vsync) { - if ((info.flags & SDL_RENDERER_PRESENTVSYNC) == 0) { - ::SDL_LogWarn(::SDL_LOG_CATEGORY_APPLICATION, "Renderer does not support VSYNC, reverting to timed delay loop."); - m_vsync = false; + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Display refresh rate is incompatible with required rate (%f)", fps()); } } - m_pixelFormat.reset(::SDL_AllocFormat(m_pixelType), ::SDL_FreeFormat); - if (m_pixelFormat == nullptr) - SDLWrapper::throwSDLException("Unable to allocate pixel format: "); + m_pixelFormat = SDL_GetPixelFormatDetails(m_pixelType); + Wrapper::maybeThrowException(m_pixelFormat, "Unable to obtain pixel format details"); configureBackground(); createBitmapTexture(); @@ -83,15 +73,13 @@ void Game::raisePOWER() noexcept { } void Game::configureBackground() const { - SDLWrapper::verifySDLCall( - ::SDL_SetRenderDrawColor(m_renderer.get(), 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE), - "Unable to set render draw colour"); + const auto success = ::SDL_SetRenderDrawColor(m_renderer.get(), 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE); + Wrapper::maybeThrowException(success, "Unable to set render draw colour"); } void Game::createBitmapTexture() { m_bitmapTexture.reset(::SDL_CreateTexture(m_renderer.get(), m_pixelType, SDL_TEXTUREACCESS_STREAMING, rasterWidth(), rasterHeight()), ::SDL_DestroyTexture); - if (m_bitmapTexture == nullptr) - SDLWrapper::throwSDLException("Unable to create bitmap texture"); + Wrapper::maybeThrowException(m_bitmapTexture.get(), "Unable to create bitmap texture"); } void Game::runLoop() { @@ -113,31 +101,31 @@ void Game::handleEvents() { ::SDL_Event e; while (::SDL_PollEvent(&e)) { switch (e.type) { - case SDL_QUIT: + case SDL_EVENT_QUIT: lowerPOWER(); break; - case SDL_KEYDOWN: - handleKeyDown(e.key.keysym.sym); + case SDL_EVENT_KEY_DOWN: + handleKeyDown(e.key.key); break; - case SDL_KEYUP: - handleKeyUp(e.key.keysym.sym); + case SDL_EVENT_KEY_UP: + handleKeyUp(e.key.key); break; - case SDL_JOYBUTTONDOWN: + case SDL_EVENT_JOYSTICK_BUTTON_DOWN: handleJoyButtonDown(e.jbutton); break; - case SDL_JOYBUTTONUP: + case SDL_EVENT_JOYSTICK_BUTTON_UP: handleJoyButtonUp(e.jbutton); break; - case SDL_CONTROLLERBUTTONDOWN: - handleControllerButtonDown(e.cbutton); + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: + handleGamepadButtonDown(e.gbutton); break; - case SDL_CONTROLLERBUTTONUP: - handleControllerButtonUp(e.cbutton); + case SDL_EVENT_GAMEPAD_BUTTON_UP: + handleGamepadButtonUp(e.gbutton); break; - case SDL_JOYDEVICEADDED: + case SDL_EVENT_JOYSTICK_ADDED: addJoystick(e); break; - case SDL_JOYDEVICEREMOVED: + case SDL_EVENT_JOYSTICK_REMOVED: removeJoystick(e); break; } @@ -146,7 +134,7 @@ void Game::handleEvents() { void Game::draw() { updateTexture(); - copyTexture(); + renderTexture(); displayTexture(); } @@ -203,7 +191,7 @@ void Game::addJoystick(SDL_Event& e) { SDL_Log("Joystick device %d added (%zd controllers)", which, m_gameControllers.size()); } -std::shared_ptr Game::gameController(const int which) const { +std::shared_ptr Game::gamepad(const int which) const { const auto i = m_gameControllers.find(which); if (i == m_gameControllers.cend()) throw std::runtime_error("Unknown controller"); @@ -239,27 +227,27 @@ std::shared_ptr Game::chooseController(const int who) const { } void Game::updateTexture() { - SDLWrapper::verifySDLCall( - ::SDL_UpdateTexture(m_bitmapTexture.get(), nullptr, pixels(), displayWidth() * sizeof(Uint32)), - "Unable to update texture: "); + const auto success = ::SDL_UpdateTexture(m_bitmapTexture.get(), nullptr, pixels(), displayWidth() * sizeof(Uint32)); + Wrapper::maybeThrowException(success, "Unable to update texture"); } -void Game::copyTexture() { - SDLWrapper::verifySDLCall( - ::SDL_RenderCopy(m_renderer.get(), m_bitmapTexture.get(), nullptr, nullptr), - "Unable to copy texture to renderer: "); +void Game::renderTexture() { + const auto success = ::SDL_RenderTexture(m_renderer.get(), m_bitmapTexture.get(), nullptr, nullptr); + Wrapper::maybeThrowException(success, "Unable to render texture"); } void Game::displayTexture() { - ::SDL_RenderPresent(m_renderer.get()); + const auto success = ::SDL_RenderPresent(m_renderer.get()); + Wrapper::maybeThrowException(success, "Unable to present render to screen"); + } void Game::toggleFullscreen() { - auto wasFullscreen = ::SDL_GetWindowFlags(m_window.get()) & SDL_WINDOW_FULLSCREEN_DESKTOP; - SDLWrapper::verifySDLCall( - ::SDL_SetWindowFullscreen(m_window.get(), wasFullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP), - "Failed to modify the window full screen setting: "); - ::SDL_ShowCursor(wasFullscreen ? 1 : 0); + const auto wasFullscreen = (SDL_GetWindowFlags(m_window.get()) & SDL_WINDOW_FULLSCREEN) != 0; + auto success = SDL_SetWindowFullscreen(m_window.get(), !wasFullscreen); + Wrapper::maybeThrowException(success, "Failed to toggle window full screen setting"); + success = wasFullscreen ? SDL_ShowCursor() : SDL_HideCursor(); + Wrapper::maybeThrowException(success, "Failed to toggle cursor show/hide"); } bool Game::handleKeyDown(SDL_Keycode key) { @@ -292,11 +280,11 @@ bool Game::handleJoyButtonUp(SDL_JoyButtonEvent event) { return false; } -bool Game::handleControllerButtonDown(SDL_ControllerButtonEvent event) { +bool Game::handleGamepadButtonDown(SDL_GamepadButtonEvent event) { return false; } -bool Game::handleControllerButtonUp(SDL_ControllerButtonEvent event) { +bool Game::handleGamepadButtonUp(SDL_GamepadButtonEvent event) { return false; } diff --git a/Gaming/src/GameController.cpp b/Gaming/src/GameController.cpp index 9303e15..61035ab 100644 --- a/Gaming/src/GameController.cpp +++ b/Gaming/src/GameController.cpp @@ -14,48 +14,51 @@ namespace Gaming { } void GameController::open() { - SDL_assert(::SDL_NumJoysticks() > 0); - if (::SDL_IsGameController(m_index)) { - m_gameController.reset(::SDL_GameControllerOpen(m_index), ::SDL_GameControllerClose); - if (m_gameController == nullptr) - SDLWrapper::throwSDLException("Unable to open game controller: "); - openHapticController(); - auto name = ::SDL_GameControllerName(m_gameController.get()); - ::SDL_Log("Game controller name: %s", name); + + int count; + auto* joysticks = SDL_GetJoysticks(&count); + Wrapper::maybeThrowException(joysticks, "Unable to obtain joystick information"); + ::SDL_free(joysticks); + assert(count > 0); + assert(m_index < count); + + if (::SDL_IsGamepad(m_index)) { + m_gamepad.reset(::SDL_OpenGamepad(m_index), ::SDL_CloseGamepad); + Wrapper::maybeThrowException(m_gamepad.get(), "Unable to open gamepad"); + openHaptic(); + const auto* name = ::SDL_GetGamepadName(m_gamepad.get()); + Wrapper::maybeThrowException(name, "Unable to obtain gamepad name"); + ::SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "Game controller name: %s", name); } else { - ::SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Joystick is not a game controller!!"); + ::SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Joystick is not a game controller"); } } - void GameController::openHapticController() { - m_hapticController.reset(::SDL_HapticOpen(m_index), ::SDL_HapticClose); - if (m_hapticController == nullptr) - SDLWrapper::throwSDLException("Unable to open haptic controller: "); - SDLWrapper::verifySDLCall(::SDL_HapticRumbleInit(m_hapticController.get()), "Unable to initialise haptic controller: "); - m_hapticRumbleSupported = ::SDL_HapticRumbleSupported(m_hapticController.get()) != SDL_FALSE; + void GameController::openHaptic() { + m_haptic.reset(::SDL_OpenHaptic(m_index), ::SDL_CloseHaptic); + Wrapper::maybeThrowException(m_haptic.get(), "Unable to open haptic gamepad"); + const auto success = ::SDL_InitHapticRumble(m_haptic.get()); + Wrapper::maybeThrowException(success, "Unable to initialise haptic gamepad"); + m_hapticRumbleSupported = ::SDL_HapticRumbleSupported(m_haptic.get()); } - void GameController::closeHapticController() noexcept { - m_hapticController.reset(); + void GameController::closeHaptic() noexcept { + m_haptic.reset(); m_hapticRumbleSupported = false; } void GameController::close() noexcept { - m_gameController.reset(); - closeHapticController(); + m_gamepad.reset(); + closeHaptic(); } void GameController::startRumble() noexcept { - if (m_hapticRumbleSupported) { - if (::SDL_HapticRumblePlay(m_hapticController.get(), 1.0, 1000) < 0) - ::SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unable to start haptic rumble: %s", ::SDL_GetError()); - } + if (m_hapticRumbleSupported && !::SDL_PlayHapticRumble(m_haptic.get(), 1.0, 1000)) + ::SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Unable to start haptic rumble: %s", ::SDL_GetError()); } void GameController::stopRumble() noexcept { - if (m_hapticRumbleSupported) { - if (::SDL_HapticRumbleStop(m_hapticController.get()) < 0) - ::SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unable to stop haptic rumble: %s", ::SDL_GetError()); - } + if (m_hapticRumbleSupported && !::SDL_StopHapticRumble(m_haptic.get())) + ::SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Unable to stop haptic rumble: %s", ::SDL_GetError()); } } \ No newline at end of file diff --git a/Gaming/src/Gaming.vcxproj b/Gaming/src/Gaming.vcxproj index c3d88ff..09e0354 100644 --- a/Gaming/src/Gaming.vcxproj +++ b/Gaming/src/Gaming.vcxproj @@ -68,19 +68,19 @@ false - ..\..\inc;C:\libraries\SDL2-2.32.10\include;$(IncludePath) + ..\..\inc;C:\libraries\SDL3-3.4.14\include;$(IncludePath) true - ..\..\inc;C:\libraries\SDL2-2.32.10\include;$(IncludePath) + ..\..\inc;C:\libraries\SDL3-3.4.14\include;$(IncludePath) true - ..\..\inc;C:\libraries\SDL2-2.32.10\include;$(IncludePath) + ..\..\inc;C:\libraries\SDL3-3.4.14\include;$(IncludePath) false - ..\..\inc;C:\libraries\SDL2-2.32.10\include;$(IncludePath) + ..\..\inc;C:\libraries\SDL3-3.4.14\include;$(IncludePath) @@ -163,12 +163,12 @@ Create Create - + - + diff --git a/Gaming/src/Gaming.vcxproj.filters b/Gaming/src/Gaming.vcxproj.filters index 60dd8c9..d5a1921 100644 --- a/Gaming/src/Gaming.vcxproj.filters +++ b/Gaming/src/Gaming.vcxproj.filters @@ -17,7 +17,7 @@ Source Files - + Source Files @@ -31,7 +31,7 @@ Header Files - + Header Files diff --git a/Gaming/src/SDLWrapper.cpp b/Gaming/src/SDLWrapper.cpp deleted file mode 100644 index 4f6407e..0000000 --- a/Gaming/src/SDLWrapper.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "stdafx.h" -#include "../inc/SDLWrapper.h" - -using namespace Gaming; - -SDLWrapper::SDLWrapper(bool verbose /* = false */) { - verifySDLCall(::SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC), "Failed to initialise SDL: "); - const auto priority = verbose ? SDL_LogPriority::SDL_LOG_PRIORITY_VERBOSE : SDL_LogPriority::SDL_LOG_PRIORITY_ERROR; - SDL_LogSetAllPriority(priority); -} - -SDLWrapper::~SDLWrapper() { - ::SDL_Quit(); -} \ No newline at end of file diff --git a/Gaming/src/Wrapper.cpp b/Gaming/src/Wrapper.cpp new file mode 100644 index 0000000..f6d44c9 --- /dev/null +++ b/Gaming/src/Wrapper.cpp @@ -0,0 +1,15 @@ +#include "stdafx.h" +#include "../inc/Wrapper.h" + +using namespace Gaming; + +Wrapper::Wrapper(bool verbose /* = false */) { + const auto success = ::SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC); + maybeThrowException(success, "Unable to initialise SDL library"); + const auto priority = verbose ? SDL_LogPriority::SDL_LOG_PRIORITY_VERBOSE : SDL_LogPriority::SDL_LOG_PRIORITY_ERROR; + ::SDL_SetLogPriorities(priority); +} + +Wrapper::~Wrapper() { + ::SDL_Quit(); +} \ No newline at end of file diff --git a/Gaming/src/stdafx.h b/Gaming/src/stdafx.h index 4109c92..da96968 100644 --- a/Gaming/src/stdafx.h +++ b/Gaming/src/stdafx.h @@ -7,13 +7,14 @@ #ifndef STDAFX_H #define STDAFX_H +#include #include #include #include #include #include -#include +#include #include