diff --git a/doc/compiling/README.md b/doc/compiling/README.md index 152662491..191027b01 100644 --- a/doc/compiling/README.md +++ b/doc/compiling/README.md @@ -23,6 +23,8 @@ General options and their default values: PRECOMPILE_HEADERS=FALSE - Precompile some headers (experimental; requires CMake 3.16 or later) PRECOMPILED_HEADERS_PATH= - Path to a file listing all headers to precompile (default points to src/precompiled_headers.txt) USE_SDL2_STATIC=TRUE - Links with SDL2::SDL2-static instead of SDL2::SDL2 + USE_SDL3=FALSE - Build with SDL3; Enables IrrlichtMt device SDL3 (experimental) + USE_SDL3_STATIC=TRUE - Links with SDL3::SDL3-static instead of SDL3::SDL3-shared ENABLE_CURL=ON - Build with cURL; Enables use of online mod repo, public serverlist and remote media fetching via http ENABLE_CURSES=ON - Build with (n)curses; Enables a server side terminal (command line option: --terminal) ENABLE_GETTEXT=ON - Build with Gettext; Allows using translations diff --git a/irr/README.md b/irr/README.md index f2c6c2863..507b8c539 100644 --- a/irr/README.md +++ b/irr/README.md @@ -11,15 +11,16 @@ Build The build system is CMake. The following libraries are required to be installed: -* zlib, libPNG, libJPEG -* OpenGL - * or on mobile: OpenGL ES (can be optionally enabled on desktop too) -* SDL2 + * zlib, libPNG, libJPEG + * OpenGL + * or on mobile: OpenGL ES (can be optionally enabled on desktop too) + * SDL2 or SDL3 (see below) Aside from standard search options (`ZLIB_INCLUDE_DIR`, `ZLIB_LIBRARY`, ...) the following options are available: -* `ENABLE_OPENGL` - Enable OpenGL driver -* `ENABLE_OPENGL3` - Enable OpenGL 3+ driver -* `ENABLE_GLES2` - Enable OpenGL ES 2+ driver + * `ENABLE_OPENGL` - Enable OpenGL driver + * `ENABLE_OPENGL3` - Enable OpenGL 3+ driver + * `ENABLE_GLES2` - Enable OpenGL ES 2+ driver + * `USE_SDL3` (default: `OFF`) - Use the SDL3 device instead of SDL2 (**experimental**) **However, IrrlichtMt cannot be built or installed separately.** @@ -34,6 +35,25 @@ We aim to support these platforms: This doesn't mean other platforms don't work or won't be supported, if you find something that doesn't work contributions are welcome. +Compatibility matrix +-------------------- + +Driver (rows) vs Device (columns) + +| | SDL2 [1] | SDL3 [2] | +|---------------------------|----------|----------| +| OpenGL 1.2 (to 2.1) | Works | Testing | +| OpenGL 3.2+ | Works | Works | +| OpenGL ES 2.x | Works | Testing | +| WebGL 1 | ? | ? | +| Null (no graphics output) | Works | Works | + +Notes: + + * [1] `CIrrDeviceSDL` with `USE_SDL3=0`: supports [many platforms](https://wiki.libsdl.org/SDL3/README-platforms) + * [2] `CIrrDeviceSDL` with `USE_SDL3=1` + + License ------- diff --git a/irr/include/EDeviceTypes.h b/irr/include/EDeviceTypes.h index 6473728c2..71d1c39d1 100644 --- a/irr/include/EDeviceTypes.h +++ b/irr/include/EDeviceTypes.h @@ -14,7 +14,8 @@ enum E_DEVICE_TYPE EIDT_OSX, //! A device which uses Simple DirectMedia Layer - /** The SDL device works under all platforms supported by SDL. */ + /** The SDL device works under all platforms supported by SDL2 or SDL3. + See also: CMake option 'USE_SDL3' */ EIDT_SDL, //! This selection allows Irrlicht to choose the best device from the ones available. diff --git a/irr/include/IrrlichtDevice.h b/irr/include/IrrlichtDevice.h index 2d5f85bcb..e15613241 100644 --- a/irr/include/IrrlichtDevice.h +++ b/irr/include/IrrlichtDevice.h @@ -361,6 +361,7 @@ public: \return The corresponding keycode. */ virtual Keycode getKeyFromScancode(const u32 scancode) const { - return Keycode(KEY_UNKNOWN, (wchar_t)scancode); + (void)scancode; + return Keycode(KEY_UNKNOWN, (wchar_t)0xFFFF); } }; diff --git a/irr/src/CIrrDeviceSDL.cpp b/irr/src/CIrrDeviceSDL.cpp index ed7c6120f..06b665633 100644 --- a/irr/src/CIrrDeviceSDL.cpp +++ b/irr/src/CIrrDeviceSDL.cpp @@ -17,7 +17,12 @@ #include "Keycodes.h" #include "COSOperator.h" #include "SIrrCreationParameters.h" + +#ifdef _IRR_USE_SDL3_ +#include +#else #include +#endif #include #include @@ -29,6 +34,72 @@ #include "CSDLManager.h" +#ifndef _IRR_USE_SDL3_ + // SDL2 backwards compatibility for things that were renamed in SDL3. + #define SDL_KMOD_SHIFT KMOD_SHIFT + #define SDL_KMOD_CTRL KMOD_CTRL + #define SDL_KMOD_NUM KMOD_NUM + + #define SDL_TextInputActive(unused) SDL_IsTextInputActive() + #define SDL_CloseJoystick SDL_JoystickClose + #define SDL_GL_DestroyContext SDL_GL_DeleteContext + + #define SDL_WINDOW_HIGH_PIXEL_DENSITY SDL_WINDOW_ALLOW_HIGHDPI + #define SDL_StartTextInput(unused) SDL_StartTextInput() + #define SDL_StopTextInput(unused) SDL_StopTextInput() + + #define SDL_EVENT_MOUSE_MOTION SDL_MOUSEMOTION + #define SDL_EVENT_MOUSE_WHEEL SDL_MOUSEWHEEL + #define SDL_EVENT_MOUSE_BUTTON_DOWN SDL_MOUSEBUTTONDOWN + #define SDL_EVENT_MOUSE_BUTTON_UP SDL_MOUSEBUTTONUP + + #define SDL_EVENT_TEXT_INPUT SDL_TEXTINPUT + #define SDL_EVENT_KEY_DOWN SDL_KEYDOWN + #define SDL_EVENT_KEY_UP SDL_KEYUP + #define SDL_EVENT_QUIT SDL_QUIT + + #define SDL_EVENT_USER SDL_USEREVENT + #define SDL_EVENT_FINGER_DOWN SDL_FINGERDOWN + #define SDL_EVENT_FINGER_MOTION SDL_FINGERMOTION + #define SDL_EVENT_FINGER_UP SDL_FINGERUP + + #define SDL_EVENT_WILL_ENTER_BACKGROUND SDL_APP_WILLENTERBACKGROUND + #define SDL_EVENT_WILL_ENTER_FOREGROUND SDL_APP_WILLENTERFOREGROUND + #define SDL_EVENT_RENDER_TARGETS_RESET SDL_RENDER_TARGETS_RESET + #define SDL_EVENT_RENDER_DEVICE_LOST SDL_RENDER_DEVICE_RESET + + #define SDL_UpdateJoysticks SDL_JoystickUpdate + #define SDL_GetNumJoystickButtons SDL_JoystickNumButtons + + #define SDL_GetNumJoystickAxes SDL_JoystickNumAxes + #define SDL_GetJoystickButton SDL_JoystickGetButton + #define SDL_GetJoystickAxis SDL_JoystickGetAxis + #define SDL_GetNumJoystickHats SDL_JoystickNumHats + #define SDL_GetJoystickHat SDL_JoystickGetHat + + #define SDL_OpenJoystick SDL_JoystickOpen + #define SDL_GetJoystickName SDL_JoystickName + + #define SDL_GetWindowSizeInPixels SDL_GL_GetDrawableSize + #define SDL_DestroySurface SDL_FreeSurface + + #define SDL_SYSTEM_CURSOR_DEFAULT SDL_SYSTEM_CURSOR_ARROW + #define SDL_SYSTEM_CURSOR_POINTER SDL_SYSTEM_CURSOR_HAND + #define SDL_SYSTEM_CURSOR_TEXT SDL_SYSTEM_CURSOR_IBEAM + #define SDL_SYSTEM_CURSOR_NOT_ALLOWED SDL_SYSTEM_CURSOR_NO + #define SDL_SYSTEM_CURSOR_MOVE SDL_SYSTEM_CURSOR_SIZEALL + #define SDL_SYSTEM_CURSOR_NESW_RESIZE SDL_SYSTEM_CURSOR_SIZENESW + #define SDL_SYSTEM_CURSOR_NWSE_RESIZE SDL_SYSTEM_CURSOR_SIZENWSE + #define SDL_SYSTEM_CURSOR_NS_RESIZE SDL_SYSTEM_CURSOR_SIZENS + #define SDL_SYSTEM_CURSOR_EW_RESIZE SDL_SYSTEM_CURSOR_SIZEWE +#endif + +#ifdef _IRR_USE_SDL3_ + #define SDL_FINGER_ID(ev) ((ev).tfinger.fingerID) +#else + #define SDL_FINGER_ID(ev) ((ev).tfinger.fingerId) +#endif + // Since SDL doesn't have mouse keys as keycodes we need to fall back to EKEY_CODE in some cases. static inline bool is_fake_key(EKEY_CODE key) { switch (key) { @@ -158,7 +229,7 @@ bool CIrrDeviceSDL::keyIsKnownSpecial(EKEY_CODE irrlichtKey) } } -int CIrrDeviceSDL::findCharToPassToIrrlicht(uint32_t sdlKey, EKEY_CODE irrlichtKey, bool numlock) +wchar_t CIrrDeviceSDL::findCharToPassToIrrlicht(uint32_t sdlKey, EKEY_CODE irrlichtKey, u16 keymod) { switch (irrlichtKey) { // special cases that always return a char regardless of how the SDL keycode @@ -184,7 +255,7 @@ int CIrrDeviceSDL::findCharToPassToIrrlicht(uint32_t sdlKey, EKEY_CODE irrlichtK break; } - if (numlock) { + if (keymod & SDL_KMOD_NUM) { // Number keys on the numpad are also affected, but we only want them // to produce number chars when numlock is enabled. switch (irrlichtKey) { @@ -252,15 +323,32 @@ std::variant CIrrDeviceSDL::getScancodeFromKey(const Keycode &ke } else { keynum = std::get(key); } + + // SDL3 returns a valid scancode for keycode 0. This is undesired. + if (keynum == 0) + return (u32) 0; + +#ifdef _IRR_USE_SDL3_ + SDL_Keymod kmod = SDL_KMOD_NONE; // TODO: respect modifiers + return (u32)SDL_GetScancodeFromKey(keynum, &kmod); +#else + // Modifiers not supported return (u32)SDL_GetScancodeFromKey(keynum); +#endif } Keycode CIrrDeviceSDL::getKeyFromScancode(const u32 scancode) const { +#ifdef _IRR_USE_SDL3_ + // TODO: SDL_HINT_KEYCODE_OPTIONS ? + auto keycode = SDL_GetKeyFromScancode((SDL_Scancode)scancode, SDL_KMOD_NONE, true); +#else + // Modifiers not supported auto keycode = SDL_GetKeyFromScancode((SDL_Scancode)scancode); +#endif const auto &keyentry = KeyMap.find(keycode); auto irrcode = keyentry != KeyMap.end() ? keyentry->second : KEY_UNKNOWN; - auto keychar = findCharToPassToIrrlicht(keycode, irrcode, false); + wchar_t keychar = findCharToPassToIrrlicht(keycode, irrcode, false); return Keycode(irrcode, keychar); } @@ -273,18 +361,22 @@ void CIrrDeviceSDL::resetReceiveTextInputEvents() // sent as text input events instead of the result) when // SDL_StartTextInput() is called on the same input box. core::rect pos = elem->getAbsolutePosition(); - if (!SDL_IsTextInputActive() || lastElemPos != pos) { + if (!SDL_TextInputActive(Window) || lastElemPos != pos) { lastElemPos = pos; SDL_Rect rect; rect.x = pos.UpperLeftCorner.X; rect.y = pos.UpperLeftCorner.Y; rect.w = pos.getWidth(); rect.h = pos.getHeight(); +#ifdef _IRR_USE_SDL3_ + SDL_SetTextInputArea(Window, &rect, 10); +#else SDL_SetTextInputRect(&rect); - SDL_StartTextInput(); +#endif + SDL_StartTextInput(Window); } } else { - SDL_StopTextInput(); + SDL_StopTextInput(Window); } } @@ -302,7 +394,9 @@ CIrrDeviceSDL::CIrrDeviceSDL(const SIrrlichtCreationParameters ¶m) : // Blocking on pause causes problems with multiplayer. // see SDL_SetHint(SDL_HINT_ANDROID_BLOCK_ON_PAUSE, "0"); +#ifndef _IRR_USE_SDL3_ SDL_SetHint(SDL_HINT_ANDROID_BLOCK_ON_PAUSE_PAUSEAUDIO, "0"); +#endif // SDL3: Handled automatically SDL_SetHint(SDL_HINT_ANDROID_TRAP_BACK_BUTTON, "1"); @@ -319,14 +413,16 @@ CIrrDeviceSDL::CIrrDeviceSDL(const SIrrlichtCreationParameters ¶m) : #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) // These are not interesting for our use + #ifndef _IRR_USE_SDL3_ SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); + #endif // SDL3: Removed hint SDL_SetHint(SDL_HINT_TV_REMOTE_AS_JOYSTICK, "0"); #endif -#if SDL_VERSION_ATLEAST(2, 24, 0) +#if SDL_VERSION_ATLEAST(2, 24, 0) && !SDL_VERSION_ATLEAST(3, 0, 0) // highdpi support on Windows SDL_SetHint(SDL_HINT_WINDOWS_DPI_SCALING, "1"); -#endif +#endif // SDL3: Removed hint // Minetest has its own code to synthesize mouse events from touch events, // so we prevent SDL from doing it. @@ -338,18 +434,33 @@ CIrrDeviceSDL::CIrrDeviceSDL(const SIrrlichtCreationParameters ¶m) : #endif // Set IME hints +#ifdef _IRR_USE_SDL3_ + SDL_SetHint(SDL_HINT_IME_IMPLEMENTED_UI, "0"); +#else SDL_SetHint(SDL_HINT_IME_INTERNAL_EDITING, "1"); +#endif #if defined(SDL_HINT_IME_SHOW_UI) SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); #endif - u32 flags = SDL_INIT_TIMER | SDL_INIT_EVENTS; + // Initialize SDL + + u32 flags = SDL_INIT_EVENTS; +#ifndef _IRR_USE_SDL3_ + flags |= SDL_INIT_TIMER; +#endif if (CreationParams.DriverType != video::EDT_NULL) flags |= SDL_INIT_VIDEO; #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) flags |= SDL_INIT_JOYSTICK; #endif - if (SDL_Init(flags) < 0) { + +#ifdef _IRR_USE_SDL3_ + if (!SDL_Init(flags)) +#else + if (SDL_Init(flags) < 0) +#endif + { os::Printer::log("Unable to initialize SDL", SDL_GetError(), ELL_ERROR); Close = true; } @@ -368,18 +479,7 @@ CIrrDeviceSDL::CIrrDeviceSDL(const SIrrlichtCreationParameters ¶m) : core::stringc sdlver = "SDL "; { - SDL_version v{}; - SDL_GetVersion(&v); - sdlver += v.major; - sdlver += "."; - sdlver += v.minor; - sdlver += "."; - sdlver += v.patch; - // the SDL team seems to intentionally number sdl2-compat this way: - // - if (v.patch >= 50) - sdlver += " (compat)"; - + sdlver += getVersionString(); sdlver += " on "; sdlver += SDL_GetPlatform(); } @@ -407,11 +507,11 @@ CIrrDeviceSDL::~CIrrDeviceSDL() #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) const u32 numJoysticks = Joysticks.size(); for (u32 i = 0; i < numJoysticks; ++i) - SDL_JoystickClose(Joysticks[i]); + SDL_CloseJoystick(Joysticks[i]); #endif if (Window && Context) { SDL_GL_MakeCurrent(Window, NULL); - SDL_GL_DeleteContext(Context); + SDL_GL_DestroyContext(Context); } if (Window) { SDL_DestroyWindow(Window); @@ -532,9 +632,11 @@ bool CIrrDeviceSDL::createWindow() bool CIrrDeviceSDL::createWindowWithContext() { u32 SDL_Flags = 0; - SDL_Flags |= SDL_WINDOW_ALLOW_HIGHDPI; + SDL_Flags |= SDL_WINDOW_HIGH_PIXEL_DENSITY; +#ifndef _IRR_USE_SDL3_ SDL_Flags |= getFullscreenFlag(CreationParams.Fullscreen); +#endif if (Resizable) SDL_Flags |= SDL_WINDOW_RESIZABLE; if (CreationParams.WindowMaximized) @@ -629,7 +731,11 @@ bool CIrrDeviceSDL::createWindowWithContext() SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); } +#ifdef _IRR_USE_SDL3_ + Window = SDL_CreateWindow("", Width, Height, SDL_Flags); +#else Window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, Width, Height, SDL_Flags); +#endif if (!Window) { os::Printer::log("Could not create window", SDL_GetError(), ELL_WARNING); return false; @@ -643,6 +749,11 @@ bool CIrrDeviceSDL::createWindowWithContext() return false; } +#ifdef _IRR_USE_SDL3_ + if (CreationParams.Fullscreen) + SDL_SetWindowFullscreen(Window, true); +#endif + updateSizeAndScale(); if (ScaleX != 1.0f || ScaleY != 1.0f) { // The given window size is in pixels, not in screen coordinates. @@ -708,12 +819,33 @@ bool CIrrDeviceSDL::run() SEvent irrevent; SDL_Event SDL_event; + auto get_touch_id_x_y = [this, &irrevent, &SDL_event]() { + irrevent.TouchInput.ID = SDL_FINGER_ID(SDL_event); + irrevent.TouchInput.X = static_cast(SDL_event.tfinger.x * Width); + irrevent.TouchInput.Y = static_cast(SDL_event.tfinger.y * Height); + }; + + auto handle_window_resize_event = [this, &irrevent]() { + u32 old_w = Width, old_h = Height; + f32 old_scale_x = ScaleX, old_scale_y = ScaleY; + updateSizeAndScale(); + if (old_w != Width || old_h != Height) { + if (VideoDriver) + VideoDriver->OnResize(core::dimension2d(Width, Height)); + } + if (old_scale_x != ScaleX || old_scale_y != ScaleY) { + irrevent.EventType = EET_APPLICATION_EVENT; + irrevent.ApplicationEvent.EventType = EAET_DPI_CHANGED; + postEventFromUser(irrevent); + } + }; + while (!Close && wrap_PollEvent(&SDL_event)) { // os::Printer::log("event: ", core::stringc((int)SDL_event.type).c_str(), ELL_INFORMATION); // just for debugging irrevent = {}; switch (SDL_event.type) { - case SDL_MOUSEMOTION: { + case SDL_EVENT_MOUSE_MOTION: { SDL_Keymod keymod = SDL_GetModState(); irrevent.EventType = EET_MOUSE_INPUT_EVENT; @@ -721,7 +853,12 @@ bool CIrrDeviceSDL::run() MouseXRel = static_cast(SDL_event.motion.xrel * ScaleX); MouseYRel = static_cast(SDL_event.motion.yrel * ScaleY); - if (!SDL_GetRelativeMouseMode()) { +#ifdef _IRR_USE_SDL3_ + if (!SDL_GetWindowRelativeMouseMode(Window)) +#else + if (!SDL_GetRelativeMouseMode()) +#endif + { MouseX = static_cast(SDL_event.motion.x * ScaleX); MouseY = static_cast(SDL_event.motion.y * ScaleY); } else { @@ -732,25 +869,27 @@ bool CIrrDeviceSDL::run() irrevent.MouseInput.Y = MouseY; irrevent.MouseInput.ButtonStates = MouseButtonStates; - irrevent.MouseInput.Shift = (keymod & KMOD_SHIFT) != 0; - irrevent.MouseInput.Control = (keymod & KMOD_CTRL) != 0; + irrevent.MouseInput.Shift = (keymod & SDL_KMOD_SHIFT) != 0; + irrevent.MouseInput.Control = (keymod & SDL_KMOD_CTRL) != 0; postEventFromUser(irrevent); break; } - case SDL_MOUSEWHEEL: { + case SDL_EVENT_MOUSE_WHEEL: { SDL_Keymod keymod = SDL_GetModState(); irrevent.EventType = EET_MOUSE_INPUT_EVENT; irrevent.MouseInput.Event = EMIE_MOUSE_WHEEL; -#if SDL_VERSION_ATLEAST(2, 0, 18) +#ifdef _IRR_USE_SDL3_ + irrevent.MouseInput.Wheel = SDL_event.wheel.y; +#elif SDL_VERSION_ATLEAST(2, 0, 18) irrevent.MouseInput.Wheel = SDL_event.wheel.preciseY; #else irrevent.MouseInput.Wheel = SDL_event.wheel.y; #endif irrevent.MouseInput.ButtonStates = MouseButtonStates; - irrevent.MouseInput.Shift = (keymod & KMOD_SHIFT) != 0; - irrevent.MouseInput.Control = (keymod & KMOD_CTRL) != 0; + irrevent.MouseInput.Shift = (keymod & SDL_KMOD_SHIFT) != 0; + irrevent.MouseInput.Control = (keymod & SDL_KMOD_CTRL) != 0; irrevent.MouseInput.X = MouseX; irrevent.MouseInput.Y = MouseY; @@ -761,8 +900,8 @@ bool CIrrDeviceSDL::run() postEventFromUser(irrevent); break; } - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: { + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: { SDL_Keymod keymod = SDL_GetModState(); irrevent.EventType = EET_MOUSE_INPUT_EVENT; @@ -801,7 +940,7 @@ bool CIrrDeviceSDL::run() #endif switch (button) { case SDL_BUTTON_LEFT: - if (SDL_event.type == SDL_MOUSEBUTTONDOWN) { + if (SDL_event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) { irrevent.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN; MouseButtonStates |= EMBSM_LEFT; } else { @@ -811,7 +950,7 @@ bool CIrrDeviceSDL::run() break; case SDL_BUTTON_RIGHT: - if (SDL_event.type == SDL_MOUSEBUTTONDOWN) { + if (SDL_event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) { irrevent.MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN; MouseButtonStates |= EMBSM_RIGHT; } else { @@ -821,7 +960,7 @@ bool CIrrDeviceSDL::run() break; case SDL_BUTTON_MIDDLE: - if (SDL_event.type == SDL_MOUSEBUTTONDOWN) { + if (SDL_event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) { irrevent.MouseInput.Event = EMIE_MMOUSE_PRESSED_DOWN; MouseButtonStates |= EMBSM_MIDDLE; } else { @@ -844,8 +983,8 @@ bool CIrrDeviceSDL::run() break; } - bool shift = (keymod & KMOD_SHIFT) != 0; - bool control = (keymod & KMOD_CTRL) != 0; + bool shift = (keymod & SDL_KMOD_SHIFT) != 0; + bool control = (keymod & SDL_KMOD_CTRL) != 0; if (irrevent.EventType == EET_MOUSE_INPUT_EVENT && irrevent.MouseInput.Event != EMIE_MOUSE_MOVED) { irrevent.MouseInput.ButtonStates = MouseButtonStates; irrevent.MouseInput.X = static_cast(SDL_event.button.x * ScaleX); @@ -866,7 +1005,7 @@ bool CIrrDeviceSDL::run() } } else if (irrevent.EventType == EET_KEY_INPUT_EVENT) { irrevent.KeyInput.Char = 0; - irrevent.KeyInput.PressedDown = SDL_event.type == SDL_MOUSEBUTTONDOWN; + irrevent.KeyInput.PressedDown = SDL_event.type == SDL_EVENT_MOUSE_BUTTON_DOWN; irrevent.KeyInput.Shift = shift; irrevent.KeyInput.Control = control; postEventFromUser(irrevent); @@ -874,7 +1013,7 @@ bool CIrrDeviceSDL::run() break; } - case SDL_TEXTINPUT: { + case SDL_EVENT_TEXT_INPUT: { irrevent.EventType = EET_STRING_INPUT_EVENT; irrevent.StringInput.Str = new core::stringw(); core::utf8ToWString(*irrevent.StringInput.Str, SDL_event.text.text); @@ -883,10 +1022,17 @@ bool CIrrDeviceSDL::run() irrevent.StringInput.Str = NULL; } break; - case SDL_KEYDOWN: - case SDL_KEYUP: { + case SDL_EVENT_KEY_DOWN: + case SDL_EVENT_KEY_UP: { +#ifdef _IRR_USE_SDL3_ + SDL_Keycode keysym = SDL_event.key.key; + SDL_Scancode scancode = SDL_event.key.scancode; + SDL_Keymod keymod = SDL_event.key.mod; +#else auto keysym = SDL_event.key.keysym.sym; auto scancode = SDL_event.key.keysym.scancode; + auto keymod = SDL_event.key.keysym.mod; +#endif // Treat AC_BACK as the Escape key if (scancode == SDL_SCANCODE_AC_BACK) { @@ -900,26 +1046,41 @@ bool CIrrDeviceSDL::run() if (!Keycode::isValid(key)) os::Printer::log("keycode not mapped", core::stringc(keysym), ELL_DEBUG); - // Make sure to only input special characters if something is in focus, as SDL_TEXTINPUT handles normal unicode already - if (SDL_IsTextInputActive() && !keyIsKnownSpecial(key) && (SDL_event.key.keysym.mod & KMOD_CTRL) == 0) + // Make sure to only input special characters if something is in focus, + // as SDL_EVENT_TEXT_INPUT handles normal unicode already + if (SDL_TextInputActive(Window) && !keyIsKnownSpecial(key) && (keymod & SDL_KMOD_CTRL) == 0) break; irrevent.EventType = EET_KEY_INPUT_EVENT; irrevent.KeyInput.Key = key; - irrevent.KeyInput.PressedDown = (SDL_event.type == SDL_KEYDOWN); - irrevent.KeyInput.Shift = (SDL_event.key.keysym.mod & KMOD_SHIFT) != 0; - irrevent.KeyInput.Control = (SDL_event.key.keysym.mod & KMOD_CTRL) != 0; - irrevent.KeyInput.Char = findCharToPassToIrrlicht(keysym, key, - (SDL_event.key.keysym.mod & KMOD_NUM) != 0); + irrevent.KeyInput.PressedDown = (SDL_event.type == SDL_EVENT_KEY_DOWN); + irrevent.KeyInput.Shift = (keymod & SDL_KMOD_SHIFT) != 0; + irrevent.KeyInput.Control = (keymod & SDL_KMOD_CTRL) != 0; +#ifdef _IRR_USE_SDL3_ + { + // Look up the printable character (like in an input box) + SDL_Keycode keycode_Aa = SDL_GetKeyFromScancode(scancode, keymod, false); + // Discard non-printable, such as CTRL, HOME, F3, ... + irrevent.KeyInput.Char = keycode_Aa < SDLK_EXTENDED_MASK ? keycode_Aa : 0; + } +#else + irrevent.KeyInput.Char = findCharToPassToIrrlicht(keysym, key, keymod); +#endif irrevent.KeyInput.SystemKeyCode = scancode; postEventFromUser(irrevent); } break; - case SDL_QUIT: + case SDL_EVENT_QUIT: Close = true; break; +#ifdef _IRR_USE_SDL3_ + case SDL_EVENT_WINDOW_RESIZED: + case SDL_EVENT_WINDOW_DISPLAY_CHANGED: + handle_window_resize_event(); + break; +#else case SDL_WINDOWEVENT: switch (SDL_event.window.event) { case SDL_WINDOWEVENT_RESIZED: @@ -927,23 +1088,13 @@ bool CIrrDeviceSDL::run() #if SDL_VERSION_ATLEAST(2, 0, 18) case SDL_WINDOWEVENT_DISPLAY_CHANGED: #endif - u32 old_w = Width, old_h = Height; - f32 old_scale_x = ScaleX, old_scale_y = ScaleY; - updateSizeAndScale(); - if (old_w != Width || old_h != Height) { - if (VideoDriver) - VideoDriver->OnResize(core::dimension2d(Width, Height)); - } - if (old_scale_x != ScaleX || old_scale_y != ScaleY) { - irrevent.EventType = EET_APPLICATION_EVENT; - irrevent.ApplicationEvent.EventType = EAET_DPI_CHANGED; - postEventFromUser(irrevent); - } + handle_window_resize_event(); break; } break; +#endif - case SDL_USEREVENT: + case SDL_EVENT_USER: irrevent.EventType = EET_USER_EVENT; irrevent.UserEvent.UserData1 = reinterpret_cast(SDL_event.user.data1); irrevent.UserEvent.UserData2 = reinterpret_cast(SDL_event.user.data2); @@ -951,35 +1102,29 @@ bool CIrrDeviceSDL::run() postEventFromUser(irrevent); break; - case SDL_FINGERDOWN: + case SDL_EVENT_FINGER_DOWN: irrevent.EventType = EET_TOUCH_INPUT_EVENT; irrevent.TouchInput.Event = ETIE_PRESSED_DOWN; - irrevent.TouchInput.ID = SDL_event.tfinger.fingerId; - irrevent.TouchInput.X = static_cast(SDL_event.tfinger.x * Width); - irrevent.TouchInput.Y = static_cast(SDL_event.tfinger.y * Height); + get_touch_id_x_y(); CurrentTouchCount++; irrevent.TouchInput.touchedCount = CurrentTouchCount; postEventFromUser(irrevent); break; - case SDL_FINGERMOTION: + case SDL_EVENT_FINGER_MOTION: irrevent.EventType = EET_TOUCH_INPUT_EVENT; irrevent.TouchInput.Event = ETIE_MOVED; - irrevent.TouchInput.ID = SDL_event.tfinger.fingerId; - irrevent.TouchInput.X = static_cast(SDL_event.tfinger.x * Width); - irrevent.TouchInput.Y = static_cast(SDL_event.tfinger.y * Height); + get_touch_id_x_y(); irrevent.TouchInput.touchedCount = CurrentTouchCount; postEventFromUser(irrevent); break; - case SDL_FINGERUP: + case SDL_EVENT_FINGER_UP: irrevent.EventType = EET_TOUCH_INPUT_EVENT; irrevent.TouchInput.Event = ETIE_LEFT_UP; - irrevent.TouchInput.ID = SDL_event.tfinger.fingerId; - irrevent.TouchInput.X = static_cast(SDL_event.tfinger.x * Width); - irrevent.TouchInput.Y = static_cast(SDL_event.tfinger.y * Height); + get_touch_id_x_y(); // To match Android behavior, still count the pointer that was // just released. irrevent.TouchInput.touchedCount = CurrentTouchCount; @@ -990,25 +1135,25 @@ bool CIrrDeviceSDL::run() postEventFromUser(irrevent); break; - // Contrary to what the SDL documentation says, SDL_APP_WILLENTERBACKGROUND + // SDL2: Contrary to what the SDL documentation says, SDL_APP_WILLENTERBACKGROUND // and SDL_APP_WILLENTERFOREGROUND are actually sent in onStop/onStart, // not onPause/onResume, on recent Android versions. This can be verified // by testing or by looking at the org.libsdl.app.SDLActivity Java code. // -> This means we can use them to implement isWindowVisible(). - case SDL_APP_WILLENTERBACKGROUND: + case SDL_EVENT_WILL_ENTER_BACKGROUND: IsInBackground = true; break; - case SDL_APP_WILLENTERFOREGROUND: + case SDL_EVENT_WILL_ENTER_FOREGROUND: IsInBackground = false; break; - case SDL_RENDER_TARGETS_RESET: + case SDL_EVENT_RENDER_TARGETS_RESET: os::Printer::log("Received SDL_RENDER_TARGETS_RESET. Rendering is probably broken.", ELL_ERROR); break; - case SDL_RENDER_DEVICE_RESET: + case SDL_EVENT_RENDER_DEVICE_LOST: os::Printer::log("Received SDL_RENDER_DEVICE_RESET. Rendering is probably broken.", ELL_ERROR); break; @@ -1023,7 +1168,7 @@ bool CIrrDeviceSDL::run() // open/close in the constructor/destructor instead // update joystick states manually - SDL_JoystickUpdate(); + SDL_UpdateJoysticks(); // we'll always send joystick input events... SEvent joyevent; joyevent.EventType = EET_JOYSTICK_INPUT_EVENT; @@ -1032,13 +1177,14 @@ bool CIrrDeviceSDL::run() if (joystick) { int j; // query all buttons - const int numButtons = core::min_(SDL_JoystickNumButtons(joystick), 32); + const int numButtons = core::min_(SDL_GetNumJoystickButtons(joystick), 32); joyevent.JoystickEvent.ButtonStates = 0; for (j = 0; j < numButtons; ++j) - joyevent.JoystickEvent.ButtonStates |= (SDL_JoystickGetButton(joystick, j) << j); + joyevent.JoystickEvent.ButtonStates |= (SDL_GetJoystickButton(joystick, j) << j); // query all axes, already in correct range - const int numAxes = core::min_(SDL_JoystickNumAxes(joystick), (int)SEvent::SJoystickEvent::NUMBER_OF_AXES); + const int numAxes = core::min_(SDL_GetNumJoystickAxes(joystick), + (int)SEvent::SJoystickEvent::NUMBER_OF_AXES); joyevent.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_X] = 0; joyevent.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Y] = 0; joyevent.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Z] = 0; @@ -1046,11 +1192,11 @@ bool CIrrDeviceSDL::run() joyevent.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_U] = 0; joyevent.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_V] = 0; for (j = 0; j < numAxes; ++j) - joyevent.JoystickEvent.Axis[j] = SDL_JoystickGetAxis(joystick, j); + joyevent.JoystickEvent.Axis[j] = SDL_GetJoystickAxis(joystick, j); // we can only query one hat, SDL only supports 8 directions - if (SDL_JoystickNumHats(joystick) > 0) { - switch (SDL_JoystickGetHat(joystick, 0)) { + if (SDL_GetNumJoystickHats(joystick) > 0) { + switch (SDL_GetJoystickHat(joystick, 0)) { case SDL_HAT_UP: joyevent.JoystickEvent.POV = 0; break; @@ -1101,21 +1247,28 @@ bool CIrrDeviceSDL::activateJoysticks(core::array &joystickInfo) #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) joystickInfo.clear(); + int numJoysticks = 0; +#ifdef _IRR_USE_SDL3_ + (void)SDL_GetJoysticks(&numJoysticks); +#else + numJoysticks = SDL_NumJoysticks(); +#endif // we can name up to 256 different joysticks - const int numJoysticks = core::min_(SDL_NumJoysticks(), 256); + numJoysticks = core::min_(numJoysticks, 256); + Joysticks.reallocate(numJoysticks); joystickInfo.reallocate(numJoysticks); int joystick = 0; for (; joystick < numJoysticks; ++joystick) { - Joysticks.push_back(SDL_JoystickOpen(joystick)); + Joysticks.push_back(SDL_OpenJoystick(joystick)); SJoystickInfo info; info.Joystick = joystick; - info.Axes = SDL_JoystickNumAxes(Joysticks[joystick]); - info.Buttons = SDL_JoystickNumButtons(Joysticks[joystick]); - info.Name = SDL_JoystickName(Joysticks[joystick]); - info.PovHat = (SDL_JoystickNumHats(Joysticks[joystick]) > 0) + info.Axes = SDL_GetNumJoystickAxes(Joysticks[joystick]); + info.Buttons = SDL_GetNumJoystickButtons(Joysticks[joystick]); + info.Name = SDL_GetJoystickName(Joysticks[joystick]); + info.PovHat = (SDL_GetNumJoystickHats(Joysticks[joystick]) > 0) ? SJoystickInfo::POV_HAT_PRESENT : SJoystickInfo::POV_HAT_ABSENT; @@ -1143,7 +1296,7 @@ void CIrrDeviceSDL::updateSizeAndScale() SDL_GetWindowSize(Window, &window_w, &window_h); int drawable_w, drawable_h; - SDL_GL_GetDrawableSize(Window, &drawable_w, &drawable_h); + SDL_GetWindowSizeInPixels(Window, &drawable_w, &drawable_h); ScaleX = (float)drawable_w / (float)window_w; ScaleY = (float)drawable_h / (float)window_h; @@ -1152,6 +1305,35 @@ void CIrrDeviceSDL::updateSizeAndScale() Height = drawable_h; } +std::string CIrrDeviceSDL::getVersionString() const +{ + char buf[32]; +#ifdef _IRR_USE_SDL3_ + int ver = SDL_GetVersion(); + snprintf_irr(buf, sizeof(buf), "%d.%d.%d (%d.%d.%d)", + // Version of the dynamic library + SDL_VERSIONNUM_MAJOR(ver), + SDL_VERSIONNUM_MINOR(ver), + SDL_VERSIONNUM_MICRO(ver), + // Version of the headers + SDL_MAJOR_VERSION, + SDL_MINOR_VERSION, + SDL_MICRO_VERSION + ); + return std::string(buf); +#else + SDL_version ver{}; + SDL_GetVersion(&ver); + snprintf_irr(buf, sizeof(buf), "%d.%d.%d%s", + ver.major, ver.minor, ver.patch, + // the SDL team seems to intentionally number sdl2-compat this way: + // + ver.patch >= 50 ? " (compat)" : "" + ); + return std::string(buf); +#endif +} + //! Get the display density in dots per inch. float CIrrDeviceSDL::getDisplayDensity() const { @@ -1200,8 +1382,14 @@ bool CIrrDeviceSDL::setWindowIcon(const video::IImage *img) u32 height = img->getDimension().Height; u32 width = img->getDimension().Width; - SDL_Surface *surface = SDL_CreateRGBSurface(0, width, height, 32, - 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000); + SDL_Surface *surface = +#ifdef _IRR_USE_SDL3_ + SDL_CreateSurface(width, height, SDL_GetPixelFormatForMasks( + 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000)); +#else + SDL_CreateRGBSurface(0, width, height, + 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000); +#endif if (!surface) { os::Printer::log("Failed to create SDL suface", ELL_ERROR); @@ -1214,13 +1402,13 @@ bool CIrrDeviceSDL::setWindowIcon(const video::IImage *img) if (!succ) { os::Printer::log("Could not copy icon image. Is the format not ECF_A8R8G8B8?", ELL_ERROR); - SDL_FreeSurface(surface); + SDL_DestroySurface(surface); return false; } SDL_SetWindowIcon(Window, surface); - SDL_FreeSurface(surface); + SDL_DestroySurface(surface); return true; } @@ -1240,7 +1428,11 @@ void CIrrDeviceSDL::setResizable(bool resize) #else // !_IRR_EMSCRIPTEN_PLATFORM_ if (resize != Resizable) { if (Window) { +#ifdef _IRR_USE_SDL3_ + SDL_SetWindowResizable(Window, resize); +#else SDL_SetWindowResizable(Window, (SDL_bool)resize); +#endif } Resizable = resize; } @@ -1283,11 +1475,17 @@ bool CIrrDeviceSDL::isFullscreen() const { if (!Window) return false; + u32 flags = SDL_GetWindowFlags(Window); return (flags & SDL_WINDOW_FULLSCREEN) != 0 || +#ifdef _IRR_USE_SDL3_ + !SDL_GetWindowFullscreenMode(Window); // borderless fullscreen +#else (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; +#endif } +#ifndef _IRR_USE_SDL3_ u32 CIrrDeviceSDL::getFullscreenFlag(bool fullscreen) { if (!fullscreen) @@ -1298,15 +1496,20 @@ u32 CIrrDeviceSDL::getFullscreenFlag(bool fullscreen) return SDL_WINDOW_FULLSCREEN_DESKTOP; #endif } +#endif bool CIrrDeviceSDL::setFullscreen(bool fullscreen) { if (!Window) return false; +#ifdef _IRR_USE_SDL3_ + bool success = SDL_SetWindowFullscreen(Window, fullscreen); +#else // The SDL wiki says that this may trigger SDL_RENDER_TARGETS_RESET, but // looking at the SDL source, this only happens with D3D, so it's not // relevant to us. bool success = SDL_SetWindowFullscreen(Window, getFullscreenFlag(fullscreen)) == 0; +#endif if (!success) os::Printer::log("SDL_SetWindowFullscreen failed", SDL_GetError(), ELL_ERROR); return success; @@ -1335,8 +1538,12 @@ bool CIrrDeviceSDL::isWindowActive() const return false; } #endif - const u32 windowFlags = SDL_GetWindowFlags(Window); - return windowFlags & SDL_WINDOW_SHOWN && windowFlags & SDL_WINDOW_INPUT_FOCUS; + const u64 windowFlags = SDL_GetWindowFlags(Window); +#ifdef _IRR_USE_SDL3_ + return !(windowFlags & SDL_WINDOW_HIDDEN) && (windowFlags & SDL_WINDOW_INPUT_FOCUS); +#else + return (windowFlags & SDL_WINDOW_SHOWN) && (windowFlags & SDL_WINDOW_INPUT_FOCUS); +#endif } //! returns if window has focus. @@ -1351,6 +1558,7 @@ bool CIrrDeviceSDL::isWindowMinimized() const return Window && (SDL_GetWindowFlags(Window) & SDL_WINDOW_MINIMIZED) != 0; } + void CIrrDeviceSDL::createKeyMap() { // I don't know if this is the best method to create @@ -1405,32 +1613,13 @@ void CIrrDeviceSDL::createKeyMap() KeyMap.emplace(SDLK_8, KEY_KEY_8); KeyMap.emplace(SDLK_9, KEY_KEY_9); - KeyMap.emplace(SDLK_a, KEY_KEY_A); - KeyMap.emplace(SDLK_b, KEY_KEY_B); - KeyMap.emplace(SDLK_c, KEY_KEY_C); - KeyMap.emplace(SDLK_d, KEY_KEY_D); - KeyMap.emplace(SDLK_e, KEY_KEY_E); - KeyMap.emplace(SDLK_f, KEY_KEY_F); - KeyMap.emplace(SDLK_g, KEY_KEY_G); - KeyMap.emplace(SDLK_h, KEY_KEY_H); - KeyMap.emplace(SDLK_i, KEY_KEY_I); - KeyMap.emplace(SDLK_j, KEY_KEY_J); - KeyMap.emplace(SDLK_k, KEY_KEY_K); - KeyMap.emplace(SDLK_l, KEY_KEY_L); - KeyMap.emplace(SDLK_m, KEY_KEY_M); - KeyMap.emplace(SDLK_n, KEY_KEY_N); - KeyMap.emplace(SDLK_o, KEY_KEY_O); - KeyMap.emplace(SDLK_p, KEY_KEY_P); - KeyMap.emplace(SDLK_q, KEY_KEY_Q); - KeyMap.emplace(SDLK_r, KEY_KEY_R); - KeyMap.emplace(SDLK_s, KEY_KEY_S); - KeyMap.emplace(SDLK_t, KEY_KEY_T); - KeyMap.emplace(SDLK_u, KEY_KEY_U); - KeyMap.emplace(SDLK_v, KEY_KEY_V); - KeyMap.emplace(SDLK_w, KEY_KEY_W); - KeyMap.emplace(SDLK_x, KEY_KEY_X); - KeyMap.emplace(SDLK_y, KEY_KEY_Y); - KeyMap.emplace(SDLK_z, KEY_KEY_Z); +#ifdef _IRR_USE_SDL3_ + const u32 sdl_key_A = SDLK_A; +#else + const u32 sdl_key_A = SDLK_a; +#endif + for (u32 i = 0; i <= ('Z' - 'A'); ++i) + KeyMap.emplace(sdl_key_A + i, (EKEY_CODE)(KEY_KEY_A + i)); KeyMap.emplace(SDLK_LGUI, KEY_LWIN); KeyMap.emplace(SDLK_RGUI, KEY_RWIN); @@ -1492,19 +1681,19 @@ void CIrrDeviceSDL::CCursorControl::initCursors() { Cursors.reserve(gui::ECI_COUNT); - Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW)); // ECI_NORMAL - Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR)); // ECI_CROSS - Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND)); // ECI_HAND - Cursors.emplace_back(nullptr); // ECI_HELP - Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM)); // ECI_IBEAM - Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO)); // ECI_NO - Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT)); // ECI_WAIT - Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL)); // ECI_SIZEALL - Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW)); // ECI_SIZENESW - Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE)); // ECI_SIZENWSE - Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS)); // ECI_SIZENS - Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE)); // ECI_SIZEWE - Cursors.emplace_back(nullptr); // ECI_UP + Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT)); // ECI_NORMAL + Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR)); // ECI_CROSS + Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER)); // ECI_HAND + Cursors.emplace_back(nullptr); // ECI_HELP + Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_TEXT)); // ECI_IBEAM + Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NOT_ALLOWED)); // ECI_NO + Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT)); // ECI_WAIT + Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_MOVE)); // ECI_SIZEALL + Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NESW_RESIZE)); // ECI_SIZENESW + Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NWSE_RESIZE)); // ECI_SIZENWSE + Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NS_RESIZE)); // ECI_SIZENS + Cursors.emplace_back(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_EW_RESIZE)); // ECI_SIZEWE + Cursors.emplace_back(nullptr); // ECI_UP } #endif // _IRR_COMPILE_WITH_SDL_DEVICE_ diff --git a/irr/src/CIrrDeviceSDL.h b/irr/src/CIrrDeviceSDL.h index ddda12968..56a336f34 100644 --- a/irr/src/CIrrDeviceSDL.h +++ b/irr/src/CIrrDeviceSDL.h @@ -16,7 +16,13 @@ #include #endif +#ifdef _IRR_USE_SDL3_ +#define SDL_DISABLE_OLD_NAMES +#include +#include +#else #include +#endif #include #include @@ -99,12 +105,7 @@ public: } //! Get the SDL version - std::string getVersionString() const override - { - SDL_version ver; - SDL_GetVersion(&ver); - return std::to_string(ver.major) + "." + std::to_string(ver.minor) + "." + std::to_string(ver.patch); - } + std::string getVersionString() const override; //! Get the display density in dots per inch. float getDisplayDensity() const override; @@ -125,11 +126,14 @@ public: void setVisible(bool visible) override { IsVisible = visible; +#ifdef _IRR_USE_SDL3_ if (visible) - SDL_ShowCursor(SDL_ENABLE); - else { - SDL_ShowCursor(SDL_DISABLE); - } + SDL_ShowCursor(); + else + SDL_HideCursor(); +#else + SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE); +#endif } //! Returns if the cursor is currently visible. @@ -166,8 +170,11 @@ public: static_cast(x / Device->ScaleX), static_cast(y / Device->ScaleY)); #endif - +#ifdef _IRR_USE_SDL3_ + if (SDL_GetWindowRelativeMouseMode(Device->Window)) { +#else if (SDL_GetRelativeMouseMode()) { +#endif // There won't be an event for this warp (details on libsdl-org/SDL/issues/6034) Device->MouseX = x; Device->MouseY = y; @@ -197,13 +204,16 @@ public: virtual void setRelativeMode(bool relative) override { +#ifdef _IRR_USE_SDL3_ + if (relative != (bool)SDL_GetWindowRelativeMouseMode(Device->Window)) { + SDL_SetWindowRelativeMouseMode(Device->Window, relative); + } +#else // Only change it when necessary, as it flushes mouse motion when enabled if (relative != static_cast(SDL_GetRelativeMouseMode())) { - if (relative) - SDL_SetRelativeMouseMode(SDL_TRUE); - else - SDL_SetRelativeMouseMode(SDL_FALSE); + SDL_SetRelativeMouseMode(relative ? SDL_TRUE : SDL_FALSE); } +#endif } void setActiveIcon(gui::ECURSOR_ICON iconId) override @@ -263,8 +273,13 @@ public: { void operator()(SDL_Cursor *ptr) { +#ifdef _IRR_USE_SDL3_ + if (ptr) + SDL_DestroyCursor(ptr); +#else if (ptr) SDL_FreeCursor(ptr); +#endif } }; std::vector> Cursors; @@ -282,7 +297,7 @@ private: static bool keyIsKnownSpecial(EKEY_CODE irrlichtKey); // Return the Char that should be sent to Irrlicht for the given key (either the one passed in or 0). - static int findCharToPassToIrrlicht(uint32_t sdlKey, EKEY_CODE irrlichtKey, bool numlock); + static wchar_t findCharToPassToIrrlicht(uint32_t sdlKey, EKEY_CODE irrlichtKey, u16 keymod); std::variant getScancodeFromKey(const Keycode &key) const override; Keycode getKeyFromScancode(const u32 scancode) const override; @@ -316,7 +331,9 @@ private: bool Resizable; +#ifndef _IRR_USE_SDL3_ static u32 getFullscreenFlag(bool fullscreen); +#endif // SDL3: Replaced by boolean core::rect lastElemPos; diff --git a/irr/src/CMakeLists.txt b/irr/src/CMakeLists.txt index 03212d048..ee9964c0b 100644 --- a/irr/src/CMakeLists.txt +++ b/irr/src/CMakeLists.txt @@ -1,5 +1,11 @@ +set(DEFAULT_SDL3 OFF) + option(USE_SDL2_STATIC "Link with SDL2 static libraries" FALSE) +option(USE_SDL3 "Use the SDL3 backend instead of SDL3" ${DEFAULT_SDL3}) +option(USE_SDL3_STATIC "Link with SDL3 static libraries" FALSE) + + # Compiler flags if(CMAKE_BUILD_TYPE STREQUAL "Debug") @@ -165,8 +171,25 @@ endif() if(ENABLE_OPENGL) find_package(OpenGL REQUIRED) endif() + set(USE_SDL2_SHARED FALSE) -if(TRUE) +if(USE_SDL3) + set(USE_SDL2_STATIC FALSE) # do not attempt to link against SDL2 + + # https://wiki.libsdl.org/SDL3/README-cmake#a-system-sdl-library + if(USE_SDL3_STATIC) + set(SDL3_SUFFIX "static") + else() + set(SDL3_SUFFIX "shared") + endif() + find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3-${SDL3_SUFFIX}) + message(STATUS "Found SDL3: ${SDL3_VERSION} (${SDL3_SUFFIX} library)") + + # https://github.com/libsdl-org/SDL/blob/release-3.2.x/include/SDL3/SDL_opengl_glext.h#L2859 + set(USE_SDL_OPENGL_4_5_HEADER TRUE) +else() + # Fall back to SDL2 + if(NOT USE_SDL2_STATIC) set(USE_SDL2_SHARED TRUE) endif() @@ -199,20 +222,22 @@ endif() # More special config -if(ENABLE_OPENGL) - # The legacy GL driver requires some symbols from GL 4.5 to compile, - # which SDL only provides since 2.26.0 (Nov 2022). - # We have a fallback in case this isn't satisfied so test for it. +if(USE_SDL2 AND ENABLE_OPENGL) set(CMAKE_REQUIRED_INCLUDES ${SDL2_INCLUDE_DIRS}) - unset(CHECK_GL_VERSION_4_5 CACHE) + unset(USE_SDL_OPENGL_4_5_HEADER CACHE) # Why? check_cxx_source_compiles("#include \n\ #ifndef GL_VERSION_4_5\n\ #error\n\ #endif\n\ -int main() {}" CHECK_GL_VERSION_4_5) - if(CHECK_GL_VERSION_4_5) - add_compile_definitions(IRR_PREFER_SDL_GL_HEADER) - endif() +int main() {}" USE_SDL_OPENGL_4_5_HEADER) +endif() + +if(USE_SDL_OPENGL_4_5_HEADER) + # The legacy GL driver requires some symbols from GL 4.5 to compile, + # which SDL only provides since 2.26.0 (Nov 2022). + # We have a fallback in case this isn't satisfied so test for it. + add_compile_definitions(IRR_PREFER_SDL_GL_HEADER) + message(STATUS "Using SDL OpenGL header") endif() # Platform-specific libs @@ -228,7 +253,7 @@ set(link_includes "${ZLIB_INCLUDE_DIR}" "${JPEG_INCLUDE_DIR}" "${PNG_INCLUDE_DIR}" - "${SDL2_INCLUDE_DIRS}" + "$<$>:${SDL2_INCLUDE_DIRS}>" ${OPENGL_INCLUDE_DIR} ${OPENGLES2_INCLUDE_DIR} @@ -387,6 +412,12 @@ if(APPLE) set_target_properties(IRROTHEROBJ PROPERTIES COMPILE_OPTIONS "-xobjective-c++") endif() +if(USE_SDL3) + include_directories(IRROTHEROBJ $) + target_compile_definitions(IRROTHEROBJ PRIVATE _IRR_USE_SDL3_) + target_compile_definitions(IRRVIDEOOBJ PRIVATE _IRR_USE_SDL3_) # OpenGL/Common.h +endif() + add_library(IRRGUIOBJ OBJECT CGUIButton.h CGUICheckBox.h @@ -485,6 +516,8 @@ target_link_libraries(IrrlichtMt PRIVATE "$<$:SDL2::SDL2>" "$<$:SDL2::SDL2-static>" + "$<$:SDL3::SDL3-${SDL3_SUFFIX}>" + "$<$:${OPENGL_LIBRARIES}>" ${EGL_LIBRARY} diff --git a/irr/src/COSOperator.cpp b/irr/src/COSOperator.cpp index e08d7176a..470b2930d 100644 --- a/irr/src/COSOperator.cpp +++ b/irr/src/COSOperator.cpp @@ -17,9 +17,13 @@ #endif #endif -#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) -#include -#include +// "SDL_version.h" for SDL_VERSION_ATLEAST +#ifdef _IRR_USE_SDL3_ + #include + #include +#else + #include + #include #endif #include "fast_atof.h" @@ -89,4 +93,3 @@ const c8 *COSOperator::getTextFromPrimarySelection() const return 0; #endif } - diff --git a/irr/src/COpenGLCommon.h b/irr/src/COpenGLCommon.h index 6f976b640..ce864aef9 100644 --- a/irr/src/COpenGLCommon.h +++ b/irr/src/COpenGLCommon.h @@ -7,8 +7,12 @@ #ifdef _IRR_COMPILE_WITH_OPENGL_ #if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) && defined(IRR_PREFER_SDL_GL_HEADER) -#include -#include + #ifdef _IRR_USE_SDL3_ + #include + #else + #include + #include + #endif #else #include "vendor/gl.h" #endif diff --git a/irr/src/CSDLManager.cpp b/irr/src/CSDLManager.cpp index 96f193c83..839295721 100644 --- a/irr/src/CSDLManager.cpp +++ b/irr/src/CSDLManager.cpp @@ -33,7 +33,7 @@ bool CSDLManager::activateContext(const SExposedVideoData &videoData, bool resto void *CSDLManager::getProcAddress(const std::string &procName) { - return SDL_GL_GetProcAddress(procName.c_str()); + return (void *)SDL_GL_GetProcAddress(procName.c_str()); } bool CSDLManager::swapBuffers() diff --git a/irr/src/OpenGL/Common.h b/irr/src/OpenGL/Common.h index fbde39d3b..a61da8c7d 100644 --- a/irr/src/OpenGL/Common.h +++ b/irr/src/OpenGL/Common.h @@ -8,8 +8,12 @@ #include "irrTypes.h" // even though we have mt_opengl.h our driver code still uses GL_* constants #if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) -#include -#include +#ifdef _IRR_USE_SDL3_ + #include +#else + #include + #include +#endif #else #include "vendor/gl.h" #endif diff --git a/irr/src/os.cpp b/irr/src/os.cpp index f8bf3a948..9e871c63b 100644 --- a/irr/src/os.cpp +++ b/irr/src/os.cpp @@ -7,7 +7,11 @@ #include "irrMath.h" #if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) -#include +#ifdef _IRR_USE_SDL3_ + #include +#else + #include +#endif #define bswap_16(X) SDL_Swap16(X) #define bswap_32(X) SDL_Swap32(X) #define bswap_64(X) SDL_Swap64(X)