Port from SDL2 -> SDL3

First stab, very choppy sound
This commit is contained in:
Adrian Conlon committed 2026-08-21 22:34:24 +01:00
1 parent e131d33f6f
commit 24a28db043
11 files changed
+158 -158

No files matched your search

+10 -10
View File
@@ -5,11 +5,11 @@
#include <memory>
#include <string>
#include <SDL.h>
#include <SDL3/SDL.h>
#include <Device.h>
#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> gameController(int which) const;
[[nodiscard]] std::shared_ptr<GameController> 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<SDL_Renderer> renderer() const noexcept { return m_renderer; }
[[nodiscard]] std::shared_ptr<SDL_Texture> bitmapTexture() const noexcept { return m_bitmapTexture; }
[[nodiscard]] std::shared_ptr<SDL_PixelFormat> 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<SDL_Window> m_window;
std::shared_ptr<SDL_Renderer> m_renderer;
std::shared_ptr<SDL_Texture> m_bitmapTexture;
std::shared_ptr<SDL_PixelFormat> 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;
+9 -9
View File
@@ -2,7 +2,7 @@
#include <memory>
#include <SDL.h>
#include <SDL3/SDL.h>
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<SDL_GameController> m_gameController;
std::shared_ptr<SDL_Gamepad> m_gamepad;
void open();
void close() noexcept;
std::shared_ptr<SDL_Haptic> m_hapticController;
std::shared_ptr<SDL_Haptic> m_haptic;
bool m_hapticRumbleSupported = false;
void openHapticController();
void closeHapticController() noexcept;
void openHaptic();
void closeHaptic() noexcept;
};
}
-21
View File
@@ -1,21 +0,0 @@
#pragma once
#include <string>
#include <stdexcept>
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);
}
};
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include <string>
#include <stdexcept>
#include <sstream>
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);
}
};
}
+56 -68
View File
@@ -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<GameController> Game::gameController(const int which) const {
std::shared_ptr<GameController> 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<GameController> 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;
}
+30 -27
View File
@@ -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());
}
}
+6 -6
View File
@@ -68,19 +68,19 @@
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>..\..\inc;C:\libraries\SDL2-2.32.10\include;$(IncludePath)</IncludePath>
<IncludePath>..\..\inc;C:\libraries\SDL3-3.4.14\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>..\..\inc;C:\libraries\SDL2-2.32.10\include;$(IncludePath)</IncludePath>
<IncludePath>..\..\inc;C:\libraries\SDL3-3.4.14\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>..\..\inc;C:\libraries\SDL2-2.32.10\include;$(IncludePath)</IncludePath>
<IncludePath>..\..\inc;C:\libraries\SDL3-3.4.14\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>..\..\inc;C:\libraries\SDL2-2.32.10\include;$(IncludePath)</IncludePath>
<IncludePath>..\..\inc;C:\libraries\SDL3-3.4.14\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
@@ -163,12 +163,12 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="SDLWrapper.cpp" />
<ClCompile Include="Wrapper.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\inc\Game.h" />
<ClInclude Include="..\inc\GameController.h" />
<ClInclude Include="..\inc\SDLWrapper.h" />
<ClInclude Include="..\inc\Wrapper.h" />
<ClInclude Include="stdafx.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+2 -2
View File
@@ -17,7 +17,7 @@
<ClCompile Include="GameController.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SDLWrapper.cpp">
<ClCompile Include="Wrapper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="stdafx.cpp">
@@ -31,7 +31,7 @@
<ClInclude Include="..\inc\GameController.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\SDLWrapper.h">
<ClInclude Include="..\inc\Wrapper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="stdafx.h">
-14
View File
@@ -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();
}
+15
View File
@@ -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();
}
+2 -1
View File
@@ -7,13 +7,14 @@
#ifndef STDAFX_H
#define STDAFX_H
#include <cassert>
#include <cstdint>
#include <map>
#include <memory>
#include <stdexcept>
#include <string>
#include <SDL.h>
#include <SDL3/SDL.h>
#include <Device.h>