From 5f0e37a00b26449c8b3af5ed590aef143e892ed7 Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Wed, 18 Dec 2024 20:00:43 +0100 Subject: [PATCH] Port to SDL3 (WIP) --- CMakeLists.txt | 38 ++--- src/audio/sdl/engine.cpp | 53 +++--- src/sdl/controller/SdlController.cpp | 107 ++++++------ src/sdl/controller/SdlController.hpp | 4 +- src/sdl/other.cpp | 8 +- src/sdl/sdl.cpp | 5 +- src/sdl/sdl.hpp | 2 +- src/sdl/window.cpp | 234 ++++++++++++++------------- src/sdl/windowimpl.hpp | 2 +- 9 files changed, 230 insertions(+), 223 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ec334943..63e590f5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,9 +107,9 @@ elseif (UNIX) target_link_libraries(jngl PRIVATE $<$,$,9.0>>:stdc++fs>) else() # Windows - set(JNGL_SDL2_DEFAULT OFF) + set(JNGL_SDL_DEFAULT OFF) if(WINDOWS_STORE) - set(JNGL_SDL2_DEFAULT ON) + set(JNGL_SDL_DEFAULT ON) FetchContent_Declare( angle @@ -125,10 +125,10 @@ else() # Windows set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_OLD} CACHE BOOL "" FORCE) endif() endif() - option(JNGL_SDL2 "Use SDL2 instead of WinAPI" ${JNGL_SDL2_DEFAULT}) + option(JNGL_SDL "Use SDL3 instead of WinAPI" ${JNGL_SDL_DEFAULT}) target_compile_definitions(jngl PRIVATE _WIN32_WINNT=0x602 _SILENCE_ALL_CXX20_DEPRECATION_WARNINGS UNICODE) - if(JNGL_SDL2) + if(JNGL_SDL) file(GLOB SRC src/sdl/*.cpp) if(WINDOWS_STORE) target_sources(jngl PRIVATE src/xinput/XinputController.cpp) @@ -213,9 +213,9 @@ else() endif() if(UNIX AND NOT IOS AND NOT EMSCRIPTEN) find_package(PkgConfig REQUIRED) - pkg_check_modules(SDL2 REQUIRED sdl2) - target_include_directories(jngl PRIVATE ${SDL2_INCLUDE_DIRS}) - target_link_libraries(jngl PRIVATE ${SDL2_LINK_LIBRARIES}) + pkg_check_modules(SDL3 REQUIRED sdl3) + target_include_directories(jngl PRIVATE ${SDL3_INCLUDE_DIRS}) + target_link_libraries(jngl PRIVATE ${SDL3_LINK_LIBRARIES}) endif() if(NOT IOS AND NOT EMSCRIPTEN) @@ -237,17 +237,17 @@ else() if(MSVC) target_compile_definitions(jngl PUBLIC _USE_MATH_DEFINES) - if(JNGL_SDL2) - CPMAddPackage(NAME sdl2 - URL https://www.libsdl.org/release/SDL2-2.28.4.zip - URL_HASH SHA1=0d9e0da0e935c4f0524cfd16d47d38b6045b9573 + if(JNGL_SDL) + CPMAddPackage(NAME sdl3 + URL https://www.libsdl.org/release/SDL3-3.1.6.zip + URL_HASH SHA1=b16ad311975378dcc87b11fc02c84eaa1f028b38 OPTIONS "SDL_SENSOR OFF" # doesn't work with UWP ) else() - CPMAddPackage(NAME sdl2 - URL https://www.libsdl.org/release/SDL2-2.28.4.zip - URL_HASH SHA1=0d9e0da0e935c4f0524cfd16d47d38b6045b9573 + CPMAddPackage(NAME sdl3 + URL https://www.libsdl.org/release/SDL3-3.1.6.zip + URL_HASH SHA1=b16ad311975378dcc87b11fc02c84eaa1f028b38 OPTIONS "SDL_ATOMIC OFF" "SDL_CPUINFO ON" @@ -264,10 +264,10 @@ else() "SDL_TIMERS OFF" ) endif() - target_link_libraries(jngl PRIVATE SDL2-static) + target_link_libraries(jngl PRIVATE SDL3-static) if(WINDOWS_STORE) target_compile_definitions(jngl PUBLIC JNGL_UWP) - target_include_directories(jngl PUBLIC ${sdl2_SOURCE_DIR}/include ${sdl2_BINARY_DIR}/include) # TODO: This should be PRIVATE + target_include_directories(jngl PUBLIC ${sdl3_SOURCE_DIR}/include ${sdl3_BINARY_DIR}/include) # TODO: This should be PRIVATE target_link_options(jngl PUBLIC $,/defaultlib:vccorlibd.lib /defaultlib:msvcrtd.lib,/defaultlib:vccorlib.lib /defaultlib:msvcrt.lib>) endif() elseif(IOS) @@ -309,9 +309,9 @@ else() find_package(PkgConfig) if(PkgConfig_FOUND) - pkg_check_modules(SDL2 REQUIRED sdl2) - target_include_directories(jngl PRIVATE ${SDL2_INCLUDE_DIRS}) - target_link_libraries(jngl PRIVATE ${SDL2_LINK_LIBRARIES}) + pkg_check_modules(SDL3 REQUIRED sdl3) + target_include_directories(jngl PRIVATE ${SDL3_INCLUDE_DIRS}) + target_link_libraries(jngl PRIVATE ${SDL3_LINK_LIBRARIES}) if(JNGL_VIDEO) if(APPLE) # On macOS we'll build theora from source, since Homebrew's version crashes due diff --git a/src/audio/sdl/engine.cpp b/src/audio/sdl/engine.cpp index 7197f3bb6..ec2131385 100644 --- a/src/audio/sdl/engine.cpp +++ b/src/audio/sdl/engine.cpp @@ -9,7 +9,7 @@ #include "../Stream.hpp" #include "../constants.hpp" -#include +#include #include #include @@ -78,61 +78,54 @@ struct engine::Impl { struct SdlImpl : public Backend { std::shared_ptr sdl_init; - SDL_AudioDeviceID device; + SDL_AudioStream* device = nullptr; std::vector buffer; std::shared_ptr output; explicit SdlImpl(std::shared_ptr output) : output(std::move(output)) { - if (SDL_Init(SDL_INIT_AUDIO) < 0) { + if (!SDL_Init(SDL_INIT_AUDIO)) { throw std::runtime_error(SDL_GetError()); } - SDL_AudioSpec desired, obtained; - desired.freq = frequency; - desired.channels = 2; - desired.format = AUDIO_S16SYS; -#ifdef __EMSCRIPTEN__ - desired.samples = 2048; -#else - desired.samples = 256; -#endif - desired.callback = &callback; - desired.userdata = this; - if (device = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, 0); device == 0) { + SDL_AudioSpec spec; + SDL_zero(spec); + spec.freq = frequency; + spec.channels = 2; + spec.format = SDL_AUDIO_F32; + if (device = + SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, &callback, this); + device == 0) { throw std::runtime_error(SDL_GetError()); } internal::debug("Initialized audio: {} channels, {} Hz, {} samples", - static_cast(obtained.channels), obtained.freq, obtained.samples); + static_cast(spec.channels), spec.freq, spec.freq); - buffer.resize(obtained.samples * obtained.channels); - SDL_PauseAudioDevice(device, 0); + SDL_ResumeAudioStreamDevice(device); } ~SdlImpl() override { - SDL_CloseAudioDevice(device); + SDL_DestroyAudioStream(device); } - static void callback(void* userdata, std::uint8_t* dst_u8, int len) { - static std::string const profiler_str = "audio"; - // prof::profiler prof(profiler_str); - + static void callback(void* userdata, SDL_AudioStream* stream, int additionalAmount, int totalAmount) { auto self = static_cast(userdata); - std::int16_t* dst = reinterpret_cast(dst_u8); - std::size_t const size = len / 2; + self->buffer.resize(additionalAmount); + std::size_t const size = additionalAmount; std::size_t read = 0; read = self->output->read(self->buffer.data(), size); std::fill(self->buffer.data() + read, self->buffer.data() + size, 0.f); - for (auto s : self->buffer) { - *dst++ = static_cast( - std::max(std::min((65535.f * s - 1.f) / 2.f, 32767.f), -32768.f)); - } + SDL_PutAudioStreamData(stream, self->buffer.data(), additionalAmount * sizeof(float)); } void setPause(bool pause) override { - SDL_PauseAudioDevice(device, pause ? 1 : 0); + if (pause) { + SDL_PauseAudioStreamDevice(device); + } else { + SDL_ResumeAudioStreamDevice(device); + } } }; }; diff --git a/src/sdl/controller/SdlController.cpp b/src/sdl/controller/SdlController.cpp index 73f525b69..e00b52c93 100644 --- a/src/sdl/controller/SdlController.cpp +++ b/src/sdl/controller/SdlController.cpp @@ -10,16 +10,16 @@ namespace jngl { -SdlController::SdlController(SDL_Joystick* const handle, const int index) -: handle(handle), haptic(SDL_HapticOpenFromJoystick(handle)) { +SdlController::SdlController(SDL_Joystick* const handle, const SDL_JoystickID joystickId) +: handle(handle), haptic(SDL_OpenHapticFromJoystick(handle)) { if (haptic) { - if (SDL_HapticRumbleInit(haptic) < 0) { + if (!SDL_InitHapticRumble(haptic)) { internal::error(SDL_GetError()); } } else { internal::error(SDL_GetError()); } - switch (SDL_JoystickNumButtons(handle)) { + switch (SDL_GetNumJoystickButtons(handle)) { case 11: model = Model::XBOX_WIRED; break; @@ -30,13 +30,13 @@ SdlController::SdlController(SDL_Joystick* const handle, const int index) model = Model::XBOX; break; default: { - if (SDL_IsGameController(index)) { - gameController = SDL_GameControllerOpen(index); + if (SDL_IsGamepad(joystickId)) { + gameController = SDL_OpenGamepad(joystickId); if (gameController) { this->handle = nullptr; - SDL_JoystickClose(handle); + SDL_CloseJoystick(handle); } else { - internal::warn("SDL_GameControllerOpen failed, falling back to joystick."); + internal::warn("SDL_OpenGamepad failed, falling back to joystick."); } } } @@ -45,9 +45,9 @@ SdlController::SdlController(SDL_Joystick* const handle, const int index) SdlController::~SdlController() { if (handle) { - SDL_JoystickClose(handle); + SDL_CloseJoystick(handle); } else { - SDL_GameControllerClose(gameController); + SDL_CloseGamepad(gameController); } } @@ -64,7 +64,7 @@ float SdlController::stateWithoutDeadzone(controller::Button button) const { case controller::RightTrigger: axisIndex = xboxWired ? 4 : 5; break; default: return down(button) ? 1 : 0; } - float state = SDL_JoystickGetAxis(handle, axisIndex); + float state = SDL_GetJoystickAxis(handle, axisIndex); if (state < 0) { state /= 32768; } else { @@ -75,17 +75,17 @@ float SdlController::stateWithoutDeadzone(controller::Button button) const { } return state; } - SDL_GameControllerAxis axis; + SDL_GamepadAxis axis; switch (button) { - case controller::LeftStickX: axis = SDL_CONTROLLER_AXIS_LEFTX; break; - case controller::LeftStickY: axis = SDL_CONTROLLER_AXIS_LEFTY; break; - case controller::RightStickX: axis = SDL_CONTROLLER_AXIS_RIGHTX; break; - case controller::RightStickY: axis = SDL_CONTROLLER_AXIS_RIGHTY; break; - case controller::LeftTrigger: axis = SDL_CONTROLLER_AXIS_TRIGGERLEFT; break; - case controller::RightTrigger: axis = SDL_CONTROLLER_AXIS_TRIGGERRIGHT; break; + case controller::LeftStickX: axis = SDL_GAMEPAD_AXIS_LEFTX; break; + case controller::LeftStickY: axis = SDL_GAMEPAD_AXIS_LEFTY; break; + case controller::RightStickX: axis = SDL_GAMEPAD_AXIS_RIGHTX; break; + case controller::RightStickY: axis = SDL_GAMEPAD_AXIS_RIGHTY; break; + case controller::LeftTrigger: axis = SDL_GAMEPAD_AXIS_LEFT_TRIGGER; break; + case controller::RightTrigger: axis = SDL_GAMEPAD_AXIS_RIGHT_TRIGGER; break; default: return down(button) ? 1 : 0; } - float state = SDL_GameControllerGetAxis(gameController, axis); + float state = SDL_GetGamepadAxis(gameController, axis); if (state < 0) { state /= 32768; } else { @@ -122,7 +122,7 @@ float SdlController::stateImpl(controller::Button button) const { otherState *= std::sqrt(1 - 0.5 * state * state); state = static_cast(tmp); } - if (model != Model::XBOX_WIRED && model != Model::XBOX) { + if (handle && model != Model::XBOX_WIRED && model != Model::XBOX) { return state; // no deadzone needed } if (state * state + otherState * otherState < 0.1) { // inside deadzone circle? @@ -149,47 +149,45 @@ bool SdlController::down(const controller::Button button) const { case controller::RightTrigger: buttonIndex = 7; break; case controller::Start: buttonIndex = xbox ? 7 : 9; break; case controller::Back: buttonIndex = xbox ? 6 : 8; break; - case controller::DpadUp: return (SDL_JoystickGetHat(handle, 0) & SDL_HAT_UP) != 0; - case controller::DpadDown: return (SDL_JoystickGetHat(handle, 0) & SDL_HAT_DOWN) != 0; - case controller::DpadLeft: return (SDL_JoystickGetHat(handle, 0) & SDL_HAT_LEFT) != 0; - case controller::DpadRight: return (SDL_JoystickGetHat(handle, 0) & SDL_HAT_RIGHT) != 0; + case controller::DpadUp: return (SDL_GetJoystickHat(handle, 0) & SDL_HAT_UP) != 0; + case controller::DpadDown: return (SDL_GetJoystickHat(handle, 0) & SDL_HAT_DOWN) != 0; + case controller::DpadLeft: return (SDL_GetJoystickHat(handle, 0) & SDL_HAT_LEFT) != 0; + case controller::DpadRight: return (SDL_GetJoystickHat(handle, 0) & SDL_HAT_RIGHT) != 0; case controller::LeftStick: buttonIndex = xbox ? 9 : 11; break; case controller::RightStick: buttonIndex = xbox ? 10 : 12; break; default: return state(button) > 0.5f; // e.g. LeftStickX } - return SDL_JoystickGetButton(handle, buttonIndex) != 0; + return SDL_GetJoystickButton(handle, buttonIndex); } switch (button) { case controller::Button::A: - return SDL_GameControllerGetButton(gameController, SDL_CONTROLLER_BUTTON_A) != 0u; + return SDL_GetGamepadButton(gameController, SDL_GAMEPAD_BUTTON_SOUTH); case controller::Button::B: - return SDL_GameControllerGetButton(gameController, SDL_CONTROLLER_BUTTON_B) != 0u; + return SDL_GetGamepadButton(gameController, SDL_GAMEPAD_BUTTON_EAST); case controller::Button::X: - return SDL_GameControllerGetButton(gameController, SDL_CONTROLLER_BUTTON_X) != 0u; + return SDL_GetGamepadButton(gameController, SDL_GAMEPAD_BUTTON_WEST); case controller::Button::Y: - return SDL_GameControllerGetButton(gameController, SDL_CONTROLLER_BUTTON_Y) != 0u; + return SDL_GetGamepadButton(gameController, SDL_GAMEPAD_BUTTON_NORTH); case controller::Button::LeftButton: - return SDL_GameControllerGetButton(gameController, SDL_CONTROLLER_BUTTON_LEFTSHOULDER) != - 0u; + return SDL_GetGamepadButton(gameController, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER); case controller::Button::RightButton: - return SDL_GameControllerGetButton(gameController, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) != - 0u; + return SDL_GetGamepadButton(gameController, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER); case controller::Button::Start: - return SDL_GameControllerGetButton(gameController, SDL_CONTROLLER_BUTTON_START) != 0u; + return SDL_GetGamepadButton(gameController, SDL_GAMEPAD_BUTTON_START); case controller::Button::Back: - return SDL_GameControllerGetButton(gameController, SDL_CONTROLLER_BUTTON_BACK) != 0u; + return SDL_GetGamepadButton(gameController, SDL_GAMEPAD_BUTTON_BACK); case controller::Button::DpadUp: - return SDL_GameControllerGetButton(gameController, SDL_CONTROLLER_BUTTON_DPAD_UP) != 0u; + return SDL_GetGamepadButton(gameController, SDL_GAMEPAD_BUTTON_DPAD_UP); case controller::Button::DpadDown: - return SDL_GameControllerGetButton(gameController, SDL_CONTROLLER_BUTTON_DPAD_DOWN) != 0u; + return SDL_GetGamepadButton(gameController, SDL_GAMEPAD_BUTTON_DPAD_DOWN); case controller::Button::DpadLeft: - return SDL_GameControllerGetButton(gameController, SDL_CONTROLLER_BUTTON_DPAD_LEFT) != 0u; + return SDL_GetGamepadButton(gameController, SDL_GAMEPAD_BUTTON_DPAD_LEFT); case controller::Button::DpadRight: - return SDL_GameControllerGetButton(gameController, SDL_CONTROLLER_BUTTON_DPAD_RIGHT) != 0u; + return SDL_GetGamepadButton(gameController, SDL_GAMEPAD_BUTTON_DPAD_RIGHT); case controller::Button::LeftStick: - return SDL_GameControllerGetButton(gameController, SDL_CONTROLLER_BUTTON_LEFTSTICK) != 0u; + return SDL_GetGamepadButton(gameController, SDL_GAMEPAD_BUTTON_LEFT_STICK); case controller::Button::RightStick: - return SDL_GameControllerGetButton(gameController, SDL_CONTROLLER_BUTTON_RIGHTSTICK) != 0u; + return SDL_GetGamepadButton(gameController, SDL_GAMEPAD_BUTTON_RIGHT_STICK); default: return state(button) > 0.5f; } @@ -197,7 +195,13 @@ bool SdlController::down(const controller::Button button) const { void SdlController::rumble(const float vibration, const std::chrono::milliseconds ms) { if (haptic) { - SDL_HapticRumblePlay(haptic, vibration, ms.count()); + SDL_PlayHapticRumble(haptic, vibration, ms.count()); + } else if (handle) { + SDL_RumbleJoystick(handle, static_cast(vibration * 65535), + static_cast(vibration * 65535), ms.count()); + } else { + SDL_RumbleGamepad(gameController, static_cast(vibration * 65535), + static_cast(vibration * 65535), ms.count()); } } @@ -205,33 +209,34 @@ bool SdlController::is(SDL_Joystick* const handle) const { if (this->handle) { return this->handle == handle; } - return SDL_GameControllerGetJoystick(gameController) == handle; + return SDL_GetGamepadJoystick(gameController) == handle; } std::vector> getConnectedControllers() { static std::vector> controllers; - int numJoysticks = SDL_NumJoysticks(); + int numJoysticks = 0; + SDL_JoystickID* joystickId = SDL_GetJoysticks(&numJoysticks); std::vector> rtn; for (int i = 0; i < numJoysticks; ++i) { - SDL_Joystick* handle = SDL_JoystickOpen(i); - if (SDL_JoystickNumButtons(handle) == 0 || - (SDL_JoystickNumAxes(handle) == 0 && SDL_JoystickNumBalls(handle) == 0 && - SDL_JoystickNumHats(handle) == 0)) { + SDL_Joystick* handle = SDL_OpenJoystick(joystickId[i]); + if (SDL_GetNumJoystickButtons(handle) == 0 || + (SDL_GetNumJoystickAxes(handle) == 0 && SDL_GetNumJoystickBalls(handle) == 0 && + SDL_GetNumJoystickHats(handle) == 0)) { // This could be the Motion Sensors of the DS4. Ignore it: - SDL_JoystickClose(handle); + SDL_CloseJoystick(handle); continue; } bool found = false; for (const auto& controller : controllers) { if (controller->is(handle)) { - SDL_JoystickClose(handle); + SDL_CloseJoystick(handle); rtn.push_back(controller); found = true; break; } } if (!found) { - controllers.emplace_back(std::make_shared(handle, i)); + controllers.emplace_back(std::make_shared(handle, joystickId[i])); rtn.emplace_back(controllers.back()); } } diff --git a/src/sdl/controller/SdlController.hpp b/src/sdl/controller/SdlController.hpp index 1ae01a118..972de4b96 100644 --- a/src/sdl/controller/SdlController.hpp +++ b/src/sdl/controller/SdlController.hpp @@ -11,7 +11,7 @@ namespace jngl { class SdlController : public Controller { public: - SdlController(SDL_Joystick*, int index); + SdlController(SDL_Joystick*, SDL_JoystickID); SdlController(const SdlController&) = delete; SdlController& operator=(const SdlController&) = delete; SdlController(SdlController&&) = delete; @@ -27,7 +27,7 @@ class SdlController : public Controller { float stateWithoutDeadzone(controller::Button) const; SDL_Joystick* handle; - SDL_GameController* gameController = nullptr; + SDL_Gamepad* gameController = nullptr; SDL_Haptic* haptic = nullptr; enum class Model { diff --git a/src/sdl/other.cpp b/src/sdl/other.cpp index 069d9dd1b..5f1bf3a7d 100644 --- a/src/sdl/other.cpp +++ b/src/sdl/other.cpp @@ -4,7 +4,7 @@ #include "../jngl/Finally.hpp" #include "../jngl/other.hpp" -#include +#include #if defined(__has_include) && __has_include() #include @@ -42,7 +42,11 @@ void setVerticalSync(bool enabled) { } bool getVerticalSync() { - return SDL_GL_GetSwapInterval() == 1; + int result; + if (!SDL_GL_GetSwapInterval(&result)) { + throw std::runtime_error(SDL_GetError()); + } + return result == 1; } } // namespace jngl diff --git a/src/sdl/sdl.cpp b/src/sdl/sdl.cpp index 794dcdcb9..f6dcb2c65 100644 --- a/src/sdl/sdl.cpp +++ b/src/sdl/sdl.cpp @@ -8,10 +8,9 @@ namespace jngl { SDL::SDL() { - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0) { + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) { throw std::runtime_error(SDL_GetError()); } - setHint(SDL_HINT_WINRT_HANDLE_BACK_BUTTON, true); setHint(SDL_HINT_MOUSE_TOUCH_EVENTS, false); setHint(SDL_HINT_TOUCH_MOUSE_EVENTS, false); } @@ -20,7 +19,7 @@ SDL::~SDL() { } void SDL::setHint(const char* name, bool value) { [[maybe_unused]] const auto result = SDL_SetHint(name, value ? "1" : "0"); - assert(result == SDL_TRUE); + assert(result); } } // namespace jngl diff --git a/src/sdl/sdl.hpp b/src/sdl/sdl.hpp index bd7fa62d0..d43159db3 100644 --- a/src/sdl/sdl.hpp +++ b/src/sdl/sdl.hpp @@ -8,7 +8,7 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wimplicit-fallthrough" #endif -#include +#include #ifdef __clang__ #pragma clang diagnostic pop #endif diff --git a/src/sdl/window.cpp b/src/sdl/window.cpp index 354b69517..5d5569e49 100644 --- a/src/sdl/window.cpp +++ b/src/sdl/window.cpp @@ -47,10 +47,10 @@ Window::Window(const std::string& title, int width, int height, const bool fulls SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI; + Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_HIGH_PIXEL_DENSITY; // TODO: SDL_WINDOW_SHOWN if (fullscreen) { if (width == getDesktopWidth() && height == getDesktopHeight()) { - flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + flags |= SDL_WINDOW_FULLSCREEN; // FIXME: SDL3 } else { flags |= SDL_WINDOW_FULLSCREEN; } @@ -68,8 +68,7 @@ Window::Window(const std::string& title, int width, int height, const bool fulls SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); const auto create = [this, &title, width, height, flags]() { SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, isMultisampleSupported_ ? 4 : 0); - return SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - width, height, flags); + return SDL_CreateWindow(title.c_str(), width, height, flags); }; if ((impl->sdlWindow = create()) == nullptr) { internal::debug("Recreating window without Anti-Aliasing support."); @@ -93,7 +92,7 @@ Window::Window(const std::string& title, int width, int height, const bool fulls int openglMSAA; if (SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &openglMSAA) != 0) { internal::debug("Recreating window and OpenGL Context without Anti-Aliasing support."); - SDL_GL_DeleteContext(impl->context); + SDL_GL_DestroyContext(impl->context); SDL_DestroyWindow(impl->sdlWindow); isMultisampleSupported_ = false; impl->sdlWindow = create(); @@ -101,7 +100,7 @@ Window::Window(const std::string& title, int width, int height, const bool fulls } } -#if !defined(__linux__) || SDL_VERSION_ATLEAST(2, 30, 0) +#if (!defined(__linux__) || SDL_VERSION_ATLEAST(2, 30, 0)) && !SDL_VERSION_ATLEAST(3, 0, 0) // This code was written for UWP, Emscripten and macOS (annoying HiDPI scaling by SDL2). On // Linux (GNOME) it results in the top of the window being cut off (by the header bar height?). // The bug seems to be fixed in newer SDL2 (or GNOME) versions. @@ -238,10 +237,10 @@ void Window::UpdateInput() { SDL_Event event; while (SDL_PollEvent(&event) != 0) { switch (event.type) { - case SDL_QUIT: + case SDL_EVENT_QUIT: quit(); break; - case SDL_MOUSEMOTION: + case SDL_EVENT_MOUSE_MOTION: if (relativeMouseMode) { mousex_ += event.motion.xrel; mousey_ += event.motion.yrel; @@ -251,22 +250,22 @@ void Window::UpdateInput() { } break; #ifndef __APPLE__ // Somehow the trackpad on a Macbook is handled as a touch screen? - case SDL_FINGERUP: + case SDL_EVENT_FINGER_UP: mouseDown_.at(0) = false; mouseDown_.at(0) = false; impl->currentFingerId = nullopt; multitouch = false; break; - case SDL_FINGERDOWN: - if (impl->currentFingerId && *impl->currentFingerId != event.tfinger.fingerId) { + case SDL_EVENT_FINGER_DOWN: + if (impl->currentFingerId && *impl->currentFingerId != event.tfinger.fingerID) { multitouch = true; } - impl->currentFingerId = event.tfinger.fingerId; + impl->currentFingerId = event.tfinger.fingerID; mouseDown_.at(0) = true; mousePressed_.at(0) = true; needToBeSetFalse_.push(&mousePressed_[0]); [[fallthrough]]; - case SDL_FINGERMOTION: + case SDL_EVENT_FINGER_MOTION: if (relativeMouseMode) { mousex_ = int(std::lround(event.tfinger.dx * float(width_))); mousey_ = int(std::lround(event.tfinger.dy * float(height_))); @@ -276,7 +275,7 @@ void Window::UpdateInput() { } break; #endif - case SDL_MOUSEBUTTONDOWN: { + case SDL_EVENT_MOUSE_BUTTON_DOWN: { int button = -1; if (event.button.button == SDL_BUTTON_LEFT) { button = 0; @@ -294,11 +293,11 @@ void Window::UpdateInput() { } break; } - case SDL_MOUSEWHEEL: { + case SDL_EVENT_MOUSE_WHEEL: { mouseWheel += event.wheel.y; break; } - case SDL_MOUSEBUTTONUP: { + case SDL_EVENT_MOUSE_BUTTON_UP: { int button = -1; if (event.button.button == SDL_BUTTON_LEFT) { button = 0; @@ -315,10 +314,10 @@ void Window::UpdateInput() { } break; } - case SDL_TEXTINPUT: + case SDL_EVENT_TEXT_INPUT: textInput += event.text.text; break; - case SDL_KEYDOWN: { + case SDL_EVENT_KEY_DOWN: { static bool wasFullscreen = fullscreen_; if (event.key.repeat != 0u && fullscreen_ != wasFullscreen) { // SDL2 with Xorg has a bug, that it sends a key repeat event when toggling @@ -327,9 +326,9 @@ void Window::UpdateInput() { } wasFullscreen = fullscreen_; - keyDown_[event.key.keysym.sym] = true; - keyPressed_[event.key.keysym.sym] = true; - const char* name = SDL_GetKeyName(event.key.keysym.sym); + keyDown_[event.key.key] = true; + keyPressed_[event.key.key] = true; + const char* name = SDL_GetKeyName(event.key.key); if (strlen(name) == 1) { std::string tmp; if (getKeyDown(key::ShiftL) || getKeyDown(key::ShiftR)) { @@ -341,11 +340,11 @@ void Window::UpdateInput() { characterPressed_[tmp] = true; needToBeSetFalse_.push(&characterPressed_[tmp]); } - if (event.key.keysym.sym == SDLK_SPACE) { + if (event.key.key == SDLK_SPACE) { characterDown_[" "] = true; characterPressed_[" "] = true; needToBeSetFalse_.push(&characterPressed_[" "]); - } else if (event.key.keysym.sym == SDLK_ESCAPE) { + } else if (event.key.key == SDLK_ESCAPE) { if (const auto& work = getWork()) { work->onBackEvent(); } @@ -353,73 +352,74 @@ void Window::UpdateInput() { anyKeyPressed_ = true; break; } - case SDL_KEYUP: { - keyDown_[event.key.keysym.sym] = false; - keyPressed_[event.key.keysym.sym] = false; - const char* name = SDL_GetKeyName(event.key.keysym.sym); + case SDL_EVENT_KEY_UP: { + keyDown_[event.key.key] = false; + keyPressed_[event.key.key] = false; + const char* name = SDL_GetKeyName(event.key.key); if (strlen(name) == 1) { std::string tmp(1, name[0]); characterDown_[tmp] = false; tmp[0] = tolower(name[0]); characterDown_[tmp] = false; } - if (event.key.keysym.sym == SDLK_SPACE) { + if (event.key.key == SDLK_SPACE) { characterDown_[" "] = false; } break; } - case SDL_JOYDEVICEADDED: - case SDL_JOYDEVICEREMOVED: + case SDL_EVENT_JOYSTICK_ADDED: + case SDL_EVENT_JOYSTICK_REMOVED: if (controllerChangedCallback) { controllerChangedCallback(); } break; - case SDL_WINDOWEVENT: - if (!impl->firstFrame && event.window.event == SDL_WINDOWEVENT_RESIZED) { - const int originalWidth = width_; - const int originalHeight = height_; - SDL_GL_GetDrawableSize(impl->sdlWindow, &width_, &height_); - impl->actualWidth = width_; - impl->actualHeight = height_; - impl->actualCanvasWidth = canvasWidth; - impl->actualCanvasHeight = canvasHeight; - calculateCanvasSize({ canvasWidth, canvasHeight }, { canvasWidth, canvasHeight }); - const float tmpWidth = (float(width_) / canvasWidth) * impl->actualCanvasWidth; - const float tmpHeight = (float(height_) / canvasHeight) * impl->actualCanvasHeight; - const auto l = -1.f / 2.f; - const auto r = 1.f / 2.f; - const auto b = 1.f / 2.f; - const auto t = -1.f / 2.f; - opengl::projection = { 1.f / float(tmpWidth) * 2.f / (r - l), - 0.f, - 0.f, - -(r + l) / (r - l), - 0.f, - 1.f / float(tmpHeight) * 2.f / (t - b), - 0.f, - -(t + b) / (t - b), - 0.f, - 0.f, - -1.f, - 0.f, - 0.f, - 0.f, - 0.f, - 1.f }; - App::instance().updateProjectionMatrix(); - updateViewportAndLetterboxing(width_, height_, canvasWidth, canvasHeight); - // restore the values in canvasWidth and canvasHeight because our scaleFactor didn't - // change: - std::swap(canvasWidth, impl->actualCanvasWidth); - std::swap(canvasHeight, impl->actualCanvasHeight); - width_ = originalWidth; - height_ = originalHeight; - } - break; - case SDL_DROPFILE: - if (event.drop.file) { - std::filesystem::path path(event.drop.file); - SDL_free(event.drop.file); + // FIXME: SDL3 + // case SDL_WINDOWEVENT: + // if (!impl->firstFrame && event.window.event == SDL_WINDOWEVENT_RESIZED) { + // const int originalWidth = width_; + // const int originalHeight = height_; + // SDL_GL_GetDrawableSize(impl->sdlWindow, &width_, &height_); + // impl->actualWidth = width_; + // impl->actualHeight = height_; + // impl->actualCanvasWidth = canvasWidth; + // impl->actualCanvasHeight = canvasHeight; + // calculateCanvasSize({ canvasWidth, canvasHeight }, { canvasWidth, canvasHeight }); + // const float tmpWidth = (float(width_) / canvasWidth) * impl->actualCanvasWidth; + // const float tmpHeight = (float(height_) / canvasHeight) * impl->actualCanvasHeight; + // const auto l = -1.f / 2.f; + // const auto r = 1.f / 2.f; + // const auto b = 1.f / 2.f; + // const auto t = -1.f / 2.f; + // opengl::projection = { 1.f / float(tmpWidth) * 2.f / (r - l), + // 0.f, + // 0.f, + // -(r + l) / (r - l), + // 0.f, + // 1.f / float(tmpHeight) * 2.f / (t - b), + // 0.f, + // -(t + b) / (t - b), + // 0.f, + // 0.f, + // -1.f, + // 0.f, + // 0.f, + // 0.f, + // 0.f, + // 1.f }; + // App::instance().updateProjectionMatrix(); + // updateViewportAndLetterboxing(width_, height_, canvasWidth, canvasHeight); + // // restore the values in canvasWidth and canvasHeight because our scaleFactor didn't + // // change: + // std::swap(canvasWidth, impl->actualCanvasWidth); + // std::swap(canvasHeight, impl->actualCanvasHeight); + // width_ = originalWidth; + // height_ = originalHeight; + // } + // break; + case SDL_EVENT_DROP_FILE: + if (event.drop.data) { + std::filesystem::path path(event.drop.data); + SDL_free((void*)event.drop.data); // TODO: needed in SDL3? assert(std::filesystem::exists(path)); for (const auto& job : jobs) { job->onFileDrop(path); @@ -441,9 +441,9 @@ void Window::SwapBuffers() { void Window::SetMouseVisible(const bool visible) { isMouseVisible_ = visible; if (visible) { - SDL_ShowCursor(SDL_ENABLE); + SDL_ShowCursor(); } else { - SDL_ShowCursor(SDL_DISABLE); + SDL_HideCursor(); } } @@ -459,57 +459,63 @@ void Window::SetMouse(const int xposition, const int yposition) { } void Window::SetRelativeMouseMode(const bool relative) { - int rtn = SDL_SetRelativeMouseMode(relative ? SDL_TRUE : SDL_FALSE); - if (rtn == -1) { - throw std::runtime_error("Relative mouse mode not supported."); - } - relativeMouseMode = relative; - if (relative) { - mousex_ = 0; - mousey_ = 0; - } else { - SDL_GetMouseState(&mousex_, &mousey_); - } + // TODO: SDL3 + // int rtn = SDL_SetRelativeMouseMode(relative ? SDL_TRUE : SDL_FALSE); + // if (rtn == -1) { + // throw std::runtime_error("Relative mouse mode not supported."); + // } + // relativeMouseMode = relative; + // if (relative) { + // mousex_ = 0; + // mousey_ = 0; + // } else { + // SDL_GetMouseState(&mousex_, &mousey_); + // } } void Window::SetIcon(const std::string& filepath) { - auto imageData = ImageData::load(filepath); - const int CHANNELS = 4; - SDL_Surface* const surface = SDL_CreateRGBSurfaceFrom( - const_cast(imageData->pixels()) /* NOLINT */, imageData->getWidth(), - imageData->getHeight(), CHANNELS * 8, CHANNELS * imageData->getWidth(), 0x000000ff, - 0x0000ff00, 0x00ff0000, 0xff000000); - - if (surface == nullptr) { - internal::error(SDL_GetError()); - return; - } - - SDL_SetWindowIcon(impl->sdlWindow, surface); - SDL_FreeSurface(surface); + // TODO: SDL3 + // auto imageData = ImageData::load(filepath); + // const int CHANNELS = 4; + // SDL_Surface* const surface = SDL_CreateRGBSurfaceFrom( + // const_cast(imageData->pixels()) /* NOLINT */, imageData->getWidth(), + // imageData->getHeight(), CHANNELS * 8, CHANNELS * imageData->getWidth(), 0x000000ff, + // 0x0000ff00, 0x00ff0000, 0xff000000); + + // if (surface == nullptr) { + // internal::error(SDL_GetError()); + // return; + // } + + // SDL_SetWindowIcon(impl->sdlWindow, surface); + // SDL_FreeSurface(surface); } int getDesktopWidth() { setProcessSettings(); SDL::handle(); - SDL_DisplayMode mode; - SDL_GetDesktopDisplayMode(0, &mode); - return mode.w; + const SDL_DisplayMode* mode = SDL_GetDesktopDisplayMode(SDL_GetPrimaryDisplay()); + if (!mode) { + throw std::runtime_error(SDL_GetError()); + } + return mode->w; } int getDesktopHeight() { setProcessSettings(); SDL::handle(); - SDL_DisplayMode mode; - SDL_GetDesktopDisplayMode(0, &mode); - return mode.h; + const SDL_DisplayMode* mode = SDL_GetDesktopDisplayMode(SDL_GetPrimaryDisplay()); + if (!mode) { + throw std::runtime_error(SDL_GetError()); + } + return mode->h; } void Window::setFullscreen(bool f) { Uint32 flag = 0; if (f) { if (width_ == getDesktopWidth() && height_ == getDesktopHeight()) { - flag = SDL_WINDOW_FULLSCREEN_DESKTOP; + flag = SDL_WINDOW_FULLSCREEN; // TODO: SDL3 } else { flag = SDL_WINDOW_FULLSCREEN; } @@ -537,18 +543,18 @@ int Window::getMouseY() const { } void setCursor(Cursor type) { - SDL_SystemCursor sdlType = SDL_SYSTEM_CURSOR_ARROW; + SDL_SystemCursor sdlType = SDL_SYSTEM_CURSOR_DEFAULT; switch (type) { case Cursor::ARROW: - sdlType = SDL_SYSTEM_CURSOR_ARROW; + sdlType = SDL_SYSTEM_CURSOR_DEFAULT; break; case Cursor::I: - sdlType = SDL_SYSTEM_CURSOR_IBEAM; + sdlType = SDL_SYSTEM_CURSOR_TEXT; break; }; static SDL_Cursor* cursor = nullptr; if (cursor) { - SDL_FreeCursor(cursor); + SDL_DestroyCursor(cursor); } cursor = SDL_CreateSystemCursor(sdlType); SDL_SetCursor(cursor); diff --git a/src/sdl/windowimpl.hpp b/src/sdl/windowimpl.hpp index 5398e95a0..d2b24a934 100644 --- a/src/sdl/windowimpl.hpp +++ b/src/sdl/windowimpl.hpp @@ -21,7 +21,7 @@ class WindowImpl { public: WindowImpl() = default; ~WindowImpl() { - SDL_GL_DeleteContext(context); + SDL_GL_DestroyContext(context); SDL_DestroyWindow(sdlWindow); } WindowImpl(const WindowImpl&) = delete;