diff --git a/neo/CMakeLists.txt b/neo/CMakeLists.txt index 5d31f19a..53f97890 100644 --- a/neo/CMakeLists.txt +++ b/neo/CMakeLists.txt @@ -1224,6 +1224,9 @@ include_directories(${CMAKE_BINARY_DIR}) include_directories(${CMAKE_SOURCE_DIR}) add_library(idlib STATIC ${src_idlib}) +if(SDL3) + target_link_libraries(idlib SDL3::Headers) # so it can use SDL_Endian.h +endif() if (AROS) add_library(dll STATIC ${src_arosdll}) if(CMAKE_SYSTEM_PROCESSOR STREQUAL "i386") diff --git a/neo/framework/Common.cpp b/neo/framework/Common.cpp index 95d11fb2..76101066 100644 --- a/neo/framework/Common.cpp +++ b/neo/framework/Common.cpp @@ -26,7 +26,13 @@ If you have questions concerning this license or the applicable additional terms =========================================================================== */ -#include +#ifdef D3_SDL3 + #include + // DG: compat with SDL2 + #define SDL_setenv SDL_setenv_unsafe +#else // SDL1.2 or SDL2 + #include +#endif #include "sys/platform.h" #include "idlib/containers/HashTable.h" @@ -2799,7 +2805,12 @@ void idCommonLocal::SetMachineSpec( void ) { } } -static unsigned int AsyncTimer(unsigned int interval, void *) { +#if SDL_VERSION_ATLEAST(3, 0, 0) +static Uint32 AsyncTimer(void * /*userdata*/, SDL_TimerID /* timerID */, Uint32 interval) +#else // SDL2 or SDL1.2 +static unsigned int AsyncTimer(unsigned int interval, void *) +#endif +{ common->Async(); Sys_TriggerEvent(TRIGGER_EVENT_ONE); @@ -2947,13 +2958,19 @@ void idCommonLocal::Init( int argc, char **argv ) { #endif #endif -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + if ( ! SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD) ) + { + if ( SDL_Init(SDL_INIT_VIDEO) ) { // retry without joystick/gamepad if it failed + Sys_Printf( "WARNING: Couldn't get SDL gamepad support! Gamepads won't work!\n" ); + } else +#elif SDL_VERSION_ATLEAST(2, 0, 0) if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0) { if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO) == 0) { // retry without joystick/gamecontroller if it failed Sys_Printf( "WARNING: Couldn't get SDL gamecontroller support! Gamepads won't work!\n" ); } else -#else +#else // SDL1.2 if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO) != 0) // no gamecontroller support in SDL1 { #endif @@ -3005,14 +3022,22 @@ void idCommonLocal::Init( int argc, char **argv ) { idCVar::RegisterStaticVars(); // print engine version -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + int sdlv = SDL_GetVersion(); + int sdlvmaj = SDL_VERSIONNUM_MAJOR(sdlv); + int sdlvmin = SDL_VERSIONNUM_MINOR(sdlv); + int sdlvmicro = SDL_VERSIONNUM_MICRO(sdlv); + Printf( "%s using SDL v%d.%d.%d\n", version.string, sdlvmaj, sdlvmin, sdlvmicro ); +#else + #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_version sdlv; SDL_GetVersion(&sdlv); -#else + #else SDL_version sdlv = *SDL_Linked_Version(); -#endif + #endif Printf( "%s using SDL v%u.%u.%u\n", version.string, sdlv.major, sdlv.minor, sdlv.patch ); +#endif #if SDL_VERSION_ATLEAST(2, 0, 0) Printf( "SDL video driver: %s\n", SDL_GetCurrentVideoDriver() ); diff --git a/neo/framework/Dhewm3SettingsMenu.cpp b/neo/framework/Dhewm3SettingsMenu.cpp index 66d654a4..27aa963b 100644 --- a/neo/framework/Dhewm3SettingsMenu.cpp +++ b/neo/framework/Dhewm3SettingsMenu.cpp @@ -2,7 +2,11 @@ #include // std::sort - TODO: replace with something custom.. -#include // to show display size +#ifdef D3_SDL3 + #include +#else // SDL1.2 or SDL2 + #include +#endif #define IMGUI_DEFINE_MATH_OPERATORS @@ -1903,9 +1907,15 @@ static void DrawVideoOptionsMenu() } // resolution info text - int sdlDisplayIdx = SDL_GetWindowDisplayIndex( SDL_GL_GetCurrentWindow() ); +#if SDL_VERSION_ATLEAST(3, 0, 0) + // in SDL3 it's a Display ID, in SDL2 a Display Index, in both cases the value + // can be fed into SDL_GetDisplayBounds() + SDL_DisplayID sdlDisplayId_x = SDL_GetDisplayForWindow( SDL_GL_GetCurrentWindow() ); +#else // SDL2 + int sdlDisplayId_x = SDL_GetWindowDisplayIndex( SDL_GL_GetCurrentWindow() ); +#endif SDL_Rect displayRect = {}; - SDL_GetDisplayBounds( sdlDisplayIdx, &displayRect ); + SDL_GetDisplayBounds( sdlDisplayId_x, &displayRect ); if ( (int)glConfig.winWidth != glConfig.vidWidth ) { ImGui::TextDisabled( "Current Resolution: %g x %g (Physical: %d x %d)", glConfig.winWidth, glConfig.winHeight, glConfig.vidWidth, glConfig.vidHeight ); diff --git a/neo/renderer/qgl.h b/neo/renderer/qgl.h index 64177009..abce7a8e 100644 --- a/neo/renderer/qgl.h +++ b/neo/renderer/qgl.h @@ -42,7 +42,11 @@ If you have questions concerning this license or the applicable additional terms #endif #endif -#include +#ifdef D3_SDL3 + #include +#else // SDL1.2 or SDL2 + #include +#endif #if defined( ID_DEDICATED ) && defined( _WIN32 ) // restore WINGDIAPI diff --git a/neo/sys/cpu.cpp b/neo/sys/cpu.cpp index 56a06dcf..c7f9e235 100644 --- a/neo/sys/cpu.cpp +++ b/neo/sys/cpu.cpp @@ -28,7 +28,14 @@ If you have questions concerning this license or the applicable additional terms #include -#include +#ifdef D3_SDL3 + #include + // in SDL3, SDL_Has3DNow() has been removed, and nowadays it's not really relevant anyway, + // so just replace it with 0/false + #define SDL_Has3DNow() 0 +#else // SDL1.2 or SDL2 + #include +#endif // MSVC header intrin.h uses strcmp and errors out when not set #define IDSTR_NO_REDIRECT diff --git a/neo/sys/events.cpp b/neo/sys/events.cpp index b0c77ef8..184e9535 100644 --- a/neo/sys/events.cpp +++ b/neo/sys/events.cpp @@ -26,7 +26,13 @@ If you have questions concerning this license or the applicable additional terms =========================================================================== */ -#include +#ifdef D3_SDL3 + // HACK: I don't want SDL.h to drag in SDL_oldnames.h to avoid all the warnings about redefined definitions + #define SDL_oldnames_h_ + #include +#else // SDL1.2 or SDL2 + #include +#endif #include "sys/platform.h" #include "idlib/containers/List.h" @@ -61,6 +67,109 @@ If you have questions concerning this license or the applicable additional terms #define SDLK_PRINTSCREEN SDLK_PRINT #endif +// unholy hacks to make it work with SDL3 (and also SDL2 and partly SDL1.2) +#if SDL_VERSION_ATLEAST(3, 0, 0) + // in SDL3 SDL_GetKeyFromScancode() got additional arguments that I don't care about + #define SDL_GetKeyFromScancode(SC) SDL_GetKeyFromScancode(SC, 0, true) + + #define SDLK_z SDLK_Z + + #define SDL_TEXTINPUT SDL_EVENT_TEXT_INPUT + #define SDL_KEYDOWN SDL_EVENT_KEY_DOWN + #define SDL_KEYUP SDL_EVENT_KEY_UP + + #define SDL_USEREVENT SDL_EVENT_USER + #define SDL_QUIT SDL_EVENT_QUIT + + #define SDL_MOUSEBUTTONDOWN SDL_EVENT_MOUSE_BUTTON_DOWN + #define SDL_MOUSEBUTTONUP SDL_EVENT_MOUSE_BUTTON_UP + #define SDL_MOUSEMOTION SDL_EVENT_MOUSE_MOTION + #define SDL_MOUSEWHEEL SDL_EVENT_MOUSE_WHEEL + + #define SDL_JoystickGUID SDL_GUID + #define SDL_JoystickGetGUID SDL_GetJoystickGUID + #define SDL_JoystickName SDL_GetJoystickName + #define SDL_JoystickGetGUIDString SDL_GUIDToString + + #define SDL_JOYDEVICEADDED SDL_EVENT_JOYSTICK_ADDED + #define SDL_JOYDEVICEREMOVED SDL_EVENT_JOYSTICK_REMOVED + + // game controllers have been renamed to gamepads and the + // corresponding SDL_Event members are now prefixed with g instead of c + #define cbutton gbutton + #define caxis gaxis + + #define SDL_GameController SDL_Gamepad + #define SDL_GameControllerAxis SDL_GamepadAxis + #define SDL_GameControllerButton SDL_GamepadButton + + #define SDL_GameControllerGetType SDL_GetGamepadType + #define SDL_GameControllerName SDL_GetGamepadName + #define SDL_GameControllerGetJoystick SDL_GetGamepadJoystick + #define SDL_GameControllerGetVendor SDL_GetGamepadVendor + #define SDL_GameControllerGetProduct SDL_GetGamepadProduct + #define SDL_GameControllerOpen SDL_OpenGamepad + + #define SDL_CONTROLLERAXISMOTION SDL_EVENT_GAMEPAD_AXIS_MOTION + #define SDL_CONTROLLERBUTTONDOWN SDL_EVENT_GAMEPAD_BUTTON_DOWN + #define SDL_CONTROLLERBUTTONUP SDL_EVENT_GAMEPAD_BUTTON_UP + + #define SDL_CONTROLLER_AXIS_LEFTX SDL_GAMEPAD_AXIS_LEFTX + #define SDL_CONTROLLER_AXIS_LEFTY SDL_GAMEPAD_AXIS_LEFTY + #define SDL_CONTROLLER_AXIS_MAX SDL_GAMEPAD_AXIS_MAX + #define SDL_CONTROLLER_AXIS_RIGHTX SDL_GAMEPAD_AXIS_RIGHTX + #define SDL_CONTROLLER_AXIS_RIGHTY SDL_GAMEPAD_AXIS_RIGHTY + #define SDL_CONTROLLER_AXIS_TRIGGERLEFT SDL_GAMEPAD_AXIS_LEFT_TRIGGER + #define SDL_CONTROLLER_AXIS_TRIGGERRIGHT SDL_GAMEPAD_AXIS_RIGHT_TRIGGER + //#define SDL_CONTROLLER_BINDTYPE_AXIS SDL_GAMEPAD_BINDTYPE_AXIS + //#define SDL_CONTROLLER_BINDTYPE_BUTTON SDL_GAMEPAD_BINDTYPE_BUTTON + //#define SDL_CONTROLLER_BINDTYPE_HAT SDL_GAMEPAD_BINDTYPE_HAT + //#define SDL_CONTROLLER_BINDTYPE_NONE SDL_GAMEPAD_BINDTYPE_NONE + #define SDL_CONTROLLER_BUTTON_A SDL_GAMEPAD_BUTTON_SOUTH + #define SDL_CONTROLLER_BUTTON_B SDL_GAMEPAD_BUTTON_EAST + #define SDL_CONTROLLER_BUTTON_X SDL_GAMEPAD_BUTTON_WEST + #define SDL_CONTROLLER_BUTTON_Y SDL_GAMEPAD_BUTTON_NORTH + #define SDL_CONTROLLER_BUTTON_BACK SDL_GAMEPAD_BUTTON_BACK + #define SDL_CONTROLLER_BUTTON_DPAD_DOWN SDL_GAMEPAD_BUTTON_DPAD_DOWN + #define SDL_CONTROLLER_BUTTON_DPAD_LEFT SDL_GAMEPAD_BUTTON_DPAD_LEFT + #define SDL_CONTROLLER_BUTTON_DPAD_RIGHT SDL_GAMEPAD_BUTTON_DPAD_RIGHT + #define SDL_CONTROLLER_BUTTON_DPAD_UP SDL_GAMEPAD_BUTTON_DPAD_UP + #define SDL_CONTROLLER_BUTTON_GUIDE SDL_GAMEPAD_BUTTON_GUIDE + #define SDL_CONTROLLER_BUTTON_INVALID SDL_GAMEPAD_BUTTON_INVALID + #define SDL_CONTROLLER_BUTTON_LEFTSHOULDER SDL_GAMEPAD_BUTTON_LEFT_SHOULDER + #define SDL_CONTROLLER_BUTTON_LEFTSTICK SDL_GAMEPAD_BUTTON_LEFT_STICK + #define SDL_CONTROLLER_BUTTON_MISC1 SDL_GAMEPAD_BUTTON_MISC1 + #define SDL_CONTROLLER_BUTTON_PADDLE1 SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1 + #define SDL_CONTROLLER_BUTTON_PADDLE2 SDL_GAMEPAD_BUTTON_LEFT_PADDLE1 + #define SDL_CONTROLLER_BUTTON_PADDLE3 SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2 + #define SDL_CONTROLLER_BUTTON_PADDLE4 SDL_GAMEPAD_BUTTON_LEFT_PADDLE2 + #define SDL_CONTROLLER_BUTTON_RIGHTSHOULDER SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER + #define SDL_CONTROLLER_BUTTON_RIGHTSTICK SDL_GAMEPAD_BUTTON_RIGHT_STICK + #define SDL_CONTROLLER_BUTTON_START SDL_GAMEPAD_BUTTON_START + #define SDL_CONTROLLER_BUTTON_TOUCHPAD SDL_GAMEPAD_BUTTON_TOUCHPAD + + #define SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT + #define SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR + #define SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT + #define SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO + #define SDL_CONTROLLER_TYPE_PS3 SDL_GAMEPAD_TYPE_PS3 + #define SDL_CONTROLLER_TYPE_PS4 SDL_GAMEPAD_TYPE_PS4 + #define SDL_CONTROLLER_TYPE_PS5 SDL_GAMEPAD_TYPE_PS5 + #define SDL_CONTROLLER_TYPE_UNKNOWN SDL_GAMEPAD_TYPE_STANDARD + #define SDL_CONTROLLER_TYPE_VIRTUAL SDL_GAMEPAD_TYPE_VIRTUAL + #define SDL_CONTROLLER_TYPE_XBOX360 SDL_GAMEPAD_TYPE_XBOX360 + #define SDL_CONTROLLER_TYPE_XBOXONE SDL_GAMEPAD_TYPE_XBOXONE + + #define IS_SDL_BTN_DOWN(EV) EV.down + + // FIXME: at some point I need to enable (and possibly later disable) SDL_TextInput! + +#else // SDL2 and SDL1.2 + + #define IS_SDL_BTN_DOWN(EV) (EV.state == SDL_PRESSED) + +#endif // SDL_VERSION_ATLEAST(3, 0, 0) - unholy hacks + extern idCVar in_useGamepad; // from UsercmdGen.cpp extern idCVar joy_deadZone; // ditto @@ -78,7 +187,7 @@ static idCVar in_ignoreConsoleKey("in_ignoreConsoleKey", "0", CVAR_SYSTEM | CVAR "Console only opens with Shift+Esc, not ` or ^ etc"); static idCVar in_nograb("in_nograb", "0", CVAR_SYSTEM | CVAR_NOCHEAT, "prevents input grabbing"); -static idCVar in_grabKeyboard("in_grabKeyboard", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT | CVAR_BOOL, +idCVar in_grabKeyboard("in_grabKeyboard", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT | CVAR_BOOL, "if enabled, grabs all keyboard input if mouse is grabbed (so keyboard shortcuts from the OS like Alt-Tab or Windows Key won't work)"); idCVar joy_gamepadLayout("joy_gamepadLayout", "-1", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT | CVAR_INTEGER, @@ -100,7 +209,7 @@ struct kbd_poll_t { int key; bool state; - kbd_poll_t() { + kbd_poll_t() : key(0), state(false) { } kbd_poll_t(int k, bool s) { @@ -113,7 +222,7 @@ struct mouse_poll_t { int action; int value; - mouse_poll_t() { + mouse_poll_t() : action(0), value(0) { } mouse_poll_t(int a, int v) { @@ -269,10 +378,11 @@ const char* Sys_GetLocalizedJoyKeyName( int key ) { if (key >= K_FIRST_JOY && key <= K_LAST_JOY) { if (key <= K_JOY_BTN_BACK) { -#if SDL_VERSION_ATLEAST(3, 0, 0) +#if 0 //SDL_VERSION_ATLEAST(3, 0, 0) // TODO: or use the SDL2 code and just set joy_gamepadLayout automatically based on SDL_GetGamepadType() ? - SDL_GamepadButton gpbtn = SDL_GAMEPAD_BUTTON_SOUTH + (key - K_JOY_BTN_SOUTH); - SDL_GamepadButtonLabel label = SDL_GetGamepadButtonLabelForType(TODO, gpbtn); + SDL_GamepadButton gpbtn = (SDL_GamepadButton)(SDL_GAMEPAD_BUTTON_SOUTH + (key - K_JOY_BTN_SOUTH)); + SDL_GamepadType sdlGamepadType = TODO; + SDL_GamepadButtonLabel label = SDL_GetGamepadButtonLabelForType(sdlGamepadType, gpbtn); switch(label) { case SDL_GAMEPAD_BUTTON_LABEL_A: return "Pad A"; @@ -844,7 +954,8 @@ void Sys_InitInput() { in_kbd.SetModified(); Sys_GetConsoleKey(false); // initialize consoleKeymappingIdx from in_kbd #if SDL_VERSION_ATLEAST(2, 0, 0) - const char* grabKeyboardEnv = SDL_getenv(SDL_HINT_GRAB_KEYBOARD); + // NOTE: SDL3 doesn't support that hint/env var, but whatever, support the env var anyway + const char* grabKeyboardEnv = SDL_getenv("SDL_GRAB_KEYBOARD"); if ( grabKeyboardEnv ) { common->Printf( "The SDL_GRAB_KEYBOARD environment variable is set, setting the in_grabKeyboard CVar to the same value (%s)\n", grabKeyboardEnv ); in_grabKeyboard.SetString( grabKeyboardEnv ); @@ -861,7 +972,19 @@ void Sys_InitInput() { memset( buttonStates, 0, sizeof( buttonStates ) ); memset( joyAxis, 0, sizeof( joyAxis ) ); -#if SDL_VERSION_ATLEAST(2, 0, 0) // gamecontroller/gamepad not supported in SDL1 +#if SDL_VERSION_ATLEAST(3, 0, 0) + int numJoysticks = 0; + SDL_JoystickID* joysticks = SDL_GetJoysticks(&numJoysticks); + for( int i = 0; i < numJoysticks; ++i ) + { + SDL_GameController* gc = SDL_GameControllerOpen( joysticks[i] ); + if ( gc != NULL ) { + setGamepadType( gc ); + } + } + SDL_free(joysticks); + +#elif SDL_VERSION_ATLEAST(2, 0, 0) // use button positions instead of button labels, // Sys_GetLocalizedJoyKeyName() will do the translation // (I think this also was the default before 2.0.12?) @@ -875,7 +998,7 @@ void Sys_InitInput() { setGamepadType( gc ); } } -#endif +#endif // gamecontroller/gamepad not supported in SDL1 } /* @@ -1082,6 +1205,7 @@ sysEvent_t Sys_GetEvent() { SDL_Event ev; sysEvent_t res = { }; int key; + bool isDown = false; static const sysEvent_t res_none = { SE_NONE, 0, 0, 0, NULL }; @@ -1094,7 +1218,9 @@ sysEvent_t Sys_GetEvent() { } #if SDL_VERSION_ATLEAST(2, 0, 0) - static char s[SDL_TEXTINPUTEVENT_TEXT_SIZE] = {0}; + // s used to have SDL_TEXTINPUTEVENT_TEXT_SIZE (32) bytes, but in SDL3 the string can have + // arbitrary size, however I assume that 128 should still be more than enough + static char s[128] = {0}; static size_t s_pos = 0; if (s[0] != '\0') { @@ -1103,7 +1229,7 @@ sysEvent_t Sys_GetEvent() { ++s_pos; - if (!s[s_pos] || s_pos == SDL_TEXTINPUTEVENT_TEXT_SIZE) { + if (!s[s_pos] || s_pos == sizeof(s)) { memset(s, 0, sizeof(s)); s_pos = 0; } @@ -1131,7 +1257,36 @@ sysEvent_t Sys_GetEvent() { } switch (ev.type) { -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDL_EVENT_WINDOW_FOCUS_GAINED: { + // unset modifier, in case alt-tab was used to leave window and ALT is still set + // as that can cause fullscreen-toggling when pressing enter... + SDL_Keymod currentmod = SDL_GetModState(); + + int newmod = SDL_KMOD_NONE; + if (currentmod & SDL_KMOD_CAPS) // preserve capslock + newmod |= SDL_KMOD_CAPS; + + SDL_SetModState((SDL_Keymod)newmod); + } // new context because visual studio complains about newmod and currentmod not initialized because of the case SDL_WINDOWEVENT_FOCUS_LOST + + + in_hasFocus = true; + + // start playing the game sound world again (when coming from editor) + session->SetPlayingSoundWorld(); + continue; // handle next event + + case SDL_EVENT_WINDOW_FOCUS_LOST: + in_hasFocus = false; + continue; // handle next event + + case SDL_EVENT_WINDOW_RESIZED: + case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: + GLimp_UpdateWindowSize(); + continue; // handle next event + +#elif SDL_VERSION_ATLEAST(2, 0, 0) case SDL_WINDOWEVENT: switch (ev.window.event) { case SDL_WINDOWEVENT_FOCUS_GAINED: { @@ -1188,7 +1343,11 @@ sysEvent_t Sys_GetEvent() { #endif case SDL_KEYDOWN: +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (ev.key.key == SDLK_RETURN && (ev.key.mod & SDL_KMOD_ALT) > 0) { +#else if (ev.key.keysym.sym == SDLK_RETURN && (ev.key.keysym.mod & KMOD_ALT) > 0) { +#endif cvarSystem->SetCVarBool("r_fullscreen", !renderSystem->IsFullScreen()); PushConsoleEvent("vid_restart partial"); return res_none; @@ -1213,12 +1372,18 @@ sysEvent_t Sys_GetEvent() { continue; // handle next event } } -#else +#else // SDL2+ { + #if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Scancode sc = ev.key.scancode; + SDL_Keycode keycode = ev.key.key; + #else // SDL2 + SDL_Scancode sc = ev.key.keysym.scancode; + SDL_Keycode keycode = ev.key.keysym.sym; + #endif // workaround for AZERTY-keyboards, which don't have 1, 2, ..., 9, 0 in first row: // always map those physical keys (scancodes) to those keycodes anyway // see also https://bugzilla.libsdl.org/show_bug.cgi?id=3188 - SDL_Scancode sc = ev.key.keysym.scancode; if(sc == SDL_SCANCODE_0) { key = '0'; @@ -1232,10 +1397,10 @@ sysEvent_t Sys_GetEvent() { } else { - key = mapkey(ev.key.keysym.sym); + key = mapkey(keycode); } - if ( !in_ignoreConsoleKey.GetBool() && ev.key.keysym.scancode == SDL_SCANCODE_GRAVE ) { + if ( !in_ignoreConsoleKey.GetBool() && sc == SDL_SCANCODE_GRAVE ) { // that key between Esc, Tab and 1 is the console key key = K_CONSOLE; } @@ -1245,22 +1410,23 @@ sysEvent_t Sys_GetEvent() { key = getKeynumForSDLscancode(sc); if(!key) { if (ev.type == SDL_KEYDOWN) { - common->Warning("unmapped SDL key %d (scancode %d)", ev.key.keysym.sym, (int)sc); + common->Warning("unmapped SDL key %d (scancode %d)", keycode, (int)sc); } continue; // handle next event } } } #endif + isDown = IS_SDL_BTN_DOWN( ev.key ); res.evType = SE_KEY; res.evValue = key; - res.evValue2 = ev.key.state == SDL_PRESSED ? 1 : 0; + res.evValue2 = isDown; - kbd_polls.Append(kbd_poll_t(key, ev.key.state == SDL_PRESSED)); + kbd_polls.Append( kbd_poll_t( key, isDown ) ); #if SDL_VERSION_ATLEAST(2, 0, 0) - if (key == K_BACKSPACE && ev.key.state == SDL_PRESSED) + if (key == K_BACKSPACE && isDown) c = key; #else if (ev.key.state == SDL_PRESSED && (ev.key.keysym.unicode & 0xff00) == 0) @@ -1277,7 +1443,14 @@ sysEvent_t Sys_GetEvent() { if ( isAscii(ev.text.text) ) { res.evValue = ev.text.text[0]; if ( ev.text.text[1] != '\0' ) { + #if SDL_VERSION_ATLEAST(3, 0, 0) + // yes, it's ok if this is not 0-terminated, the code generating + // events from s handles that + strncpy(s, ev.text.text, sizeof(s)); + #else // SDL2 memcpy( s, ev.text.text, SDL_TEXTINPUTEVENT_TEXT_SIZE ); + s[SDL_TEXTINPUTEVENT_TEXT_SIZE] = '\0'; + #endif s_pos = 1; // pos 0 is returned } return res; @@ -1294,10 +1467,6 @@ sysEvent_t Sys_GetEvent() { } continue; // handle next event - - case SDL_TEXTEDITING: - // on windows we get this event whenever the window gains focus.. just ignore it. - continue; #endif case SDL_MOUSEMOTION: @@ -1335,20 +1504,21 @@ sysEvent_t Sys_GetEvent() { case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: + isDown = IS_SDL_BTN_DOWN(ev.button); res.evType = SE_KEY; switch (ev.button.button) { case SDL_BUTTON_LEFT: res.evValue = K_MOUSE1; - mouse_polls.Append(mouse_poll_t(M_ACTION1, ev.button.state == SDL_PRESSED ? 1 : 0)); + mouse_polls.Append(mouse_poll_t(M_ACTION1, isDown)); break; case SDL_BUTTON_MIDDLE: res.evValue = K_MOUSE3; - mouse_polls.Append(mouse_poll_t(M_ACTION3, ev.button.state == SDL_PRESSED ? 1 : 0)); + mouse_polls.Append(mouse_poll_t(M_ACTION3, isDown)); break; case SDL_BUTTON_RIGHT: res.evValue = K_MOUSE2; - mouse_polls.Append(mouse_poll_t(M_ACTION2, ev.button.state == SDL_PRESSED ? 1 : 0)); + mouse_polls.Append(mouse_poll_t(M_ACTION2, isDown)); break; #if !SDL_VERSION_ATLEAST(2, 0, 0) @@ -1370,14 +1540,14 @@ sysEvent_t Sys_GetEvent() { { int buttonIndex = ev.button.button - SDL_BUTTON_LEFT; res.evValue = K_MOUSE1 + buttonIndex; - mouse_polls.Append( mouse_poll_t( M_ACTION1 + buttonIndex, ev.button.state == SDL_PRESSED ? 1 : 0 ) ); + mouse_polls.Append( mouse_poll_t( M_ACTION1 + buttonIndex, isDown ) ); } else #endif continue; // handle next event } - res.evValue2 = ev.button.state == SDL_PRESSED ? 1 : 0; + res.evValue2 = isDown; return res; @@ -1390,8 +1560,10 @@ sysEvent_t Sys_GetEvent() { continue; } + isDown = IS_SDL_BTN_DOWN(ev.cbutton); + res.evType = SE_KEY; - res.evValue2 = ev.cbutton.state == SDL_PRESSED ? 1 : 0; + res.evValue2 = isDown; // special case: always treat the start button as escape so it opens/closes the menu // (also makes that button non-bindable) @@ -1410,7 +1582,7 @@ sysEvent_t Sys_GetEvent() { } sys_jEvents jEvent = mapjoybutton( (SDL_GameControllerButton)ev.cbutton.button ); - joystick_polls.Append( joystick_poll_t(jEvent, ev.cbutton.state == SDL_PRESSED ? 1 : 0) ); + joystick_polls.Append( joystick_poll_t(jEvent, isDown) ); if ( ( jEvent >= J_ACTION_FIRST ) && ( jEvent <= J_ACTION_MAX ) ) { res.evValue = K_FIRST_JOY + ( jEvent - J_ACTION_FIRST ); @@ -1630,7 +1802,8 @@ void Sys_GenerateEvents() { if (s) PushConsoleEvent(s); -#ifndef ID_DEDICATED // doesn't make sense on dedicated server + // doesn't make sense on dedicated server and SDL3 handles this in GLimp_GrabInput() +#if !defined(ID_DEDICATED) && ! SDL_VERSION_ATLEAST(3, 0, 0) if ( in_grabKeyboard.IsModified() ) { #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_SetHint( SDL_HINT_GRAB_KEYBOARD, in_grabKeyboard.GetString() ); diff --git a/neo/sys/glimp.cpp b/neo/sys/glimp.cpp index 1f0bad2e..4bd3f52b 100644 --- a/neo/sys/glimp.cpp +++ b/neo/sys/glimp.cpp @@ -26,7 +26,17 @@ If you have questions concerning this license or the applicable additional terms =========================================================================== */ -#include +#ifdef D3_SDL3 + #include + // backwards-compat with SDL2 + #define SDL_WINDOW_ALLOW_HIGHDPI SDL_WINDOW_HIGH_PIXEL_DENSITY + #define SDL_GL_DeleteContext SDL_GL_DestroyContext + typedef SDL_WindowFlags My_SDL_WindowFlags; +#else // SDL1.2 or SDL2 + #include + // for compat with SDL3 - unfortunately SDL2 also has a SDL_WindowFlags type, but it's an enum + typedef Uint32 My_SDL_WindowFlags; +#endif #include "sys/platform.h" #include "framework/Licensee.h" @@ -99,7 +109,6 @@ If you have questions concerning this license or the applicable additional terms #endif // _WIN32 and ID_ALLOW_TOOLS - #if SDL_VERSION_ATLEAST(2, 0, 0) static SDL_Window *window = NULL; static SDL_GLContext context = NULL; @@ -109,6 +118,24 @@ static SDL_Surface *window = NULL; #define SDL_WINDOW_FULLSCREEN SDL_FULLSCREEN #endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + +extern idCVar in_grabKeyboard; + +static void SetSDLIcon() +{ + #include "doom_icon.h" // contains the struct d3_icon + + SDL_Surface* icon = SDL_CreateSurfaceFrom(d3_icon.width, d3_icon.height, + SDL_PIXELFORMAT_RGBA32, (void*)d3_icon.pixel_data, + d3_icon.bytes_per_pixel*d3_icon.width); + + SDL_SetWindowIcon(window, icon); + SDL_DestroySurface(icon); +} + +#else // SDL2 and SDL1.2 + static void SetSDLIcon() { Uint32 rmask, gmask, bmask, amask; @@ -141,6 +168,7 @@ static void SetSDLIcon() SDL_FreeSurface(icon); } +#endif // SDL2 and SDL1.2 /* =================== @@ -152,16 +180,23 @@ bool GLimp_Init(glimpParms_t parms) { assert(SDL_WasInit(SDL_INIT_VIDEO)); - Uint32 flags = SDL_WINDOW_OPENGL; + My_SDL_WindowFlags flags = SDL_WINDOW_OPENGL; if (parms.fullScreen == 1) { -#if SDL_VERSION_ATLEAST(2, 0, 0) - if(parms.fullScreenDesktop) - flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; - else -#endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + // in SDL3 windows with SDL_WINDOW_FULLSCREEN set are fullscreen-desktop by default + // and for exclusive fullscreen SDL_SetWindowFullscreenMode() must be called + // after creating the window, so only set the flag if we want fullscreen-desktop mode + if(parms.fullScreen && parms.fullScreenDesktop) { flags |= SDL_WINDOW_FULLSCREEN; + } +#elif SDL_VERSION_ATLEAST(2, 0, 0) + flags |= parms.fullScreenDesktop ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN; +#else // SDL1.2 + flags |= SDL_WINDOW_FULLSCREEN; +#endif + } r_windowResizable.ClearModified(); @@ -190,9 +225,10 @@ bool GLimp_Init(glimpParms_t parms) { * (unless the user disables that with r_fillWindowAlphaChan 0) */ #ifdef SDL_HINT_VIDEO_EGL_ALLOW_TRANSPARENCY SDL_SetHint(SDL_HINT_VIDEO_EGL_ALLOW_TRANSPARENCY, "1"); - #else // little hack so this works if the SDL2 version used for building is older than runtime version + #elif SDL_MAJOR_VERSION == 2 // little hack so this works if the SDL2 version used for building is older than runtime version SDL_SetHint("SDL_VIDEO_EGL_ALLOW_TRANSPARENCY", "1"); #endif + // Note: for SDL3 we use SDL_PROP_WINDOW_CREATE_TRANSPARENT_BOOLEAN below #endif int colorbits = 24; @@ -277,7 +313,7 @@ try_again: SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, (multisamples > 1) ? 1 : 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, multisamples); -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(2, 0, 0) // SDL2 and SDL3 window creation if ( r_glDebugContext.GetBool() ) { common->Printf( "Requesting an OpenGL Debug Context (r_glDebugContext is enabled)\n" ); @@ -292,37 +328,137 @@ try_again: windowMode, parms.width, parms.height, r_mode.GetInteger()); } - int displayIndex = 0; -#if SDL_VERSION_ATLEAST(2, 0, 4) + Uint32 selectedDisplay = 0; + #if SDL_VERSION_ATLEAST(2, 0, 4) // try to put the window on the display the mousecursor currently is on { + #if SDL_VERSION_ATLEAST(3, 0, 0) + float x, y; + int numDisplays = 0; + SDL_DisplayID* displayIDs = SDL_GetDisplays(&numDisplays); + #else // SDL2 + int numDisplays = SDL_GetNumVideoDisplays(); int x, y; + #endif + SDL_GetGlobalMouseState(&x, &y); - int numDisplays = SDL_GetNumVideoDisplays(); common->Printf("SDL detected %d displays: \n", numDisplays); bool found = false; for ( int j=0; jPrintf(" %d: %dx%d at (%d, %d) to (%d, %d)\n", j, rect.w, rect.h, rect.x, rect.y, rect.x+rect.w, rect.y+rect.h); if ( !found && x >= rect.x && x < rect.x + rect.w && y >= rect.y && y < rect.y + rect.h ) { - displayIndex = j; + selectedDisplay = j; found = true; } } } - common->Printf("Will use display %d because mouse cursor is at (%d, %d).\n", - displayIndex, x, y); + #if SDL_VERSION_ATLEAST(3, 0, 0) + if(displayIDs != NULL) { + SDL_DisplayID displayID = displayIDs[selectedDisplay]; + common->Printf("Will use display %u (%u) because mouse cursor is at (%g, %g).\n", + selectedDisplay, displayID, x, y); + selectedDisplay = displayID; + SDL_free(displayIDs); + } + #else // SDL2 + common->Printf("Will use display %u because mouse cursor is at (%d, %d).\n", + selectedDisplay, x, y); + #endif } -#endif + #endif // SDL_VERSION_ATLEAST(2, 0, 4) + #if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, ENGINE_VERSION); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_UNDEFINED_DISPLAY(selectedDisplay)); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_UNDEFINED_DISPLAY(selectedDisplay)); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, parms.width); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, parms.height); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, flags); + // TODO: the following is probably redundant with the flag already set + //if ( parms.fullScreen && parms.fullScreenDesktop ) { + // SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, true); + //} // for exclusive fullscreen, create in windowed mode first, need to call + + // TODO: SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TRANSPARENT_BOOLEAN, true); + // as replacement for SDL_VIDEO_EGL_ALLOW_TRANSPARENCY - but when? + // I don't want this on all platforms.. but can I already detect wayland at this point? + // Also: recent mesa versions should have this fixed, so maybe ignore the issue for SDL3? what about nvidia? + + window = SDL_CreateWindowWithProperties(props); + SDL_DestroyProperties(props); + if (window == NULL) { + common->Warning("Couldn't set GL mode %d/%d/%d with %dx MSAA: %s", + channelcolorbits, tdepthbits, tstencilbits, parms.multiSamples, SDL_GetError()); + + // before trying to reduce color channel size or whatever, first try reducing MSAA, if possible + if (multisamples > 1) { + multisamples = (multisamples <= 2) ? 0 : (multisamples/2); + + // using goto because enhancing that logic which reduces attributes + // based on i (so it'd first try reducing MSAA) would be too painful + goto try_again; + } + + continue; + } else { + // creating the window succeeded, so adjust r_multiSamples to the value that was actually used + parms.multiSamples = multisamples; + r_multiSamples.SetInteger(multisamples); + } + + // handle exclusive fullscreen mode + // TODO: just call GLimp_SetScreenParms() ? + if (parms.fullScreen && !parms.fullScreenDesktop) { + SDL_DisplayID displayID = SDL_GetDisplayForWindow( window ); + SDL_DisplayMode mode = {}; + if ( SDL_GetClosestFullscreenDisplayMode(displayID, parms.width, parms.height, + parms.displayHz, true, &mode) ) + { + if ( ! SDL_SetWindowFullscreenMode(window, &mode) ) { + common->Warning("Can't set window fullscreen mode: %s\n", SDL_GetError()); + SDL_DestroyWindow(window); + window = NULL; + return false; // trying other color depth etc is unlikely to help with this issue + } + + if ( ! SDL_SetWindowFullscreen(window, true) ) { + common->Warning("Can't switch window to fullscreen mode: %s\n", SDL_GetError()); + SDL_DestroyWindow(window); + window = NULL; + return false; // trying other color depth etc is unlikely to help with this issue + } + + if ( ! SDL_SyncWindow(window) ) { + common->Warning("SDL_SyncWindow() failed: %s\n", SDL_GetError()); + SDL_DestroyWindow(window); + window = NULL; + return false; // trying other color depth etc is unlikely to help with this issue + } + + } else { + common->Warning("Can't get display mode: %s\n", SDL_GetError()); + SDL_DestroyWindow(window); + window = NULL; + return false; // trying other color depth etc is unlikely to help with this issue + } + } + + #else // SDL2 window = SDL_CreateWindow(ENGINE_VERSION, - SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), - SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), + SDL_WINDOWPOS_UNDEFINED_DISPLAY(selectedDisplay), + SDL_WINDOWPOS_UNDEFINED_DISPLAY(selectedDisplay), parms.width, parms.height, flags); if (!window) { @@ -364,8 +500,8 @@ try_again: common->Warning("Current display mode isn't requested display mode\n"); common->Warning("Likely SDL bug #4700, trying to work around it..\n"); int dIdx = SDL_GetWindowDisplayIndex(window); - if(dIdx != displayIndex) { - common->Warning("Window's display index is %d, but we wanted %d!\n", dIdx, displayIndex); + if(dIdx != selectedDisplay) { + common->Warning("Window's display index is %d, but we wanted %d!\n", dIdx, selectedDisplay); } /* Mkay, try to hack around that. */ @@ -413,6 +549,7 @@ try_again: common->Warning("Now we have the requested resolution (%d x %d)\n", parms.width, parms.height); } } + #endif // SDL2 context = SDL_GL_CreateContext(window); @@ -424,7 +561,8 @@ try_again: SetSDLIcon(); // for SDL2 this must be done after creating the window - glConfig.isFullscreen = (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN; + // TODO: also check for fullscreen-desktop? + glConfig.isFullscreen = (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) != 0; const char* fsStr = glConfig.isFullscreen ? "fullscreen " : ""; if ( (int)glConfig.winWidth != glConfig.vidWidth ) { common->Printf( "Got a HighDPI %swindow with physical resolution %d x %d and virtual resolution %g x %g\n", @@ -432,7 +570,7 @@ try_again: } else { common->Printf( "Got a %swindow with resolution %g x %g\n", fsStr, glConfig.winWidth, glConfig.winHeight ); } -#else +#else // SDL1.2 window creation SDL_WM_SetCaption(ENGINE_VERSION, ENGINE_VERSION); SetSDLIcon(); // for SDL1.2 this must be done before creating the window @@ -654,6 +792,9 @@ bool GLimp_SetScreenParms(glimpParms_t parms) { } SDL_RestoreWindow( window ); // make sure we're not maximized, then setting the size wouldn't work SDL_SetWindowSize( window, parms.width, parms.height ); + #if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_SyncWindow( window ); + #endif } else { // we want some kind of fullscreen mode // it's probably safest to first switch to windowed mode @@ -661,6 +802,60 @@ bool GLimp_SetScreenParms(glimpParms_t parms) { SDL_SetWindowFullscreen( window, 0 ); } + #if SDL_VERSION_ATLEAST(3, 0, 0) + if ( wantFullscreenDesktop ) { + SDL_SetWindowFullscreenMode( window, NULL ); // setting it to NULL enables fullscreen desktop mode + SDL_SetWindowFullscreen( window, true ); + // TODO: check return values + + if ( ! SDL_SyncWindow( window ) ) { + common->Warning( "SDL_SyncWindow() failed: %s\n", SDL_GetError() ); + //TODO: probably not SDL_DestroyWindow( window ); + //window = NULL; + return false; + } + } else { // want real fullscreen + SDL_DisplayID displayID = SDL_GetDisplayForWindow( window ); + SDL_DisplayMode mode = {}; + if ( SDL_GetClosestFullscreenDisplayMode( displayID, parms.width, parms.height, + parms.displayHz, true, &mode ) ) + { + if ( ! SDL_SetWindowFullscreenMode( window, &mode ) ) { + common->Warning( "Can't set window fullscreen mode: %s\n", SDL_GetError() ); + //TODO: probably not SDL_DestroyWindow( window ); + //window = NULL; + return false; + } + + if ( ! SDL_SetWindowFullscreen( window, true ) ) { + common->Warning( "Can't switch window to fullscreen mode: %s\n", SDL_GetError() ); + //TODO: probably not SDL_DestroyWindow( window ); + //window = NULL; + return false; + } + + if ( ! SDL_SyncWindow( window ) ) { + common->Warning( "SDL_SyncWindow() failed: %s\n", SDL_GetError() ); + //TODO: probably not SDL_DestroyWindow( window ); + //window = NULL; + return false; + } + + } else { + if ( parms.displayHz != 0 ) { + common->Warning( "Can't get display mode for %d x %d @ %dHz: %s\n", parms.width, + parms.height, parms.displayHz, SDL_GetError() ); + } else { + common->Warning( "Can't get display mode for %d x %d: %s\n", parms.width, + parms.height, SDL_GetError() ); + } + //TODO: probably not SDL_DestroyWindow( window ); + //window = NULL; + return false; + } + } + + #else // SDL2 if ( wantFullscreenDesktop ) { if ( SDL_SetWindowFullscreen( window, SDL_WINDOW_FULLSCREEN_DESKTOP ) != 0 ) { common->Warning( "GLimp_SetScreenParms(): Couldn't switch to fullscreen desktop mode, SDL error: %s\n", SDL_GetError() ); @@ -703,6 +898,7 @@ bool GLimp_SetScreenParms(glimpParms_t parms) { return false; } } + #endif // SDL2 } glConfig.isFullscreen = (SDL_GetWindowFlags( window ) & SDL_WINDOW_FULLSCREEN) != 0; @@ -732,10 +928,23 @@ glimpParms_t GLimp_GetCurState() } ret.multiSamples = curMultiSamples; - Uint32 winFlags = SDL_GetWindowFlags( window ); + My_SDL_WindowFlags winFlags = SDL_GetWindowFlags( window ); ret.fullScreen = (winFlags & SDL_WINDOW_FULLSCREEN) != 0; +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (ret.fullScreen) { + const SDL_DisplayMode* fullscreenMode = SDL_GetWindowFullscreenMode( window ); + if (fullscreenMode != NULL) { + ret.width = fullscreenMode->w; + ret.height = fullscreenMode->h; + ret.displayHz = fullscreenMode->refresh_rate; + } else { + // SDL_WINDOW_FULLSCREEN is set, but SDL_GetWindowFullscreenMode() returns NULL + // => fullscreen desktop mode + ret.fullScreenDesktop = true; + } + } +#else // SDL2 ret.fullScreenDesktop = (winFlags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP; - if ( ret.fullScreen && !ret.fullScreenDesktop ) { // I think SDL_GetWindowDisplayMode() is only for "real" fullscreen? SDL_DisplayMode real_mode = {}; if ( SDL_GetWindowDisplayMode( window, &real_mode ) == 0 ) { @@ -746,7 +955,9 @@ glimpParms_t GLimp_GetCurState() common->Warning( "GLimp_GetCurState(): Can't get display mode: %s\n", SDL_GetError() ); } } - if ( ret.width == 0 && ret.height == 0 ) { // windowed mode or SDL_GetWindowDisplayMode() failed +#endif + + if ( ret.width == 0 && ret.height == 0 ) { // windowed mode, fullscreen-desktop mode or SDL_GetWindowDisplayMode() failed SDL_GetWindowSize( window, &ret.width, &ret.height ); } @@ -797,11 +1008,14 @@ void GLimp_SwapBuffers() { #endif } +// SDL3 doesn't support hardware gamma +#if ! SDL_VERSION_ATLEAST(3, 0, 0) static bool gammaOrigError = false; static bool gammaOrigSet = false; static unsigned short gammaOrigRed[256]; static unsigned short gammaOrigGreen[256]; static unsigned short gammaOrigBlue[256]; +#endif /* ================= @@ -809,6 +1023,12 @@ GLimp_SetGamma ================= */ void GLimp_SetGamma(unsigned short red[256], unsigned short green[256], unsigned short blue[256]) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if ( ! r_gammaInShader.GetBool() ) { + common->Warning( "This build of dhewm3 uses SDL3, which does not support hardware gamma." ); + common->Warning( "If you want to adjust gamma or brightness, enable r_gammaInShader" ); + } +#else // SDL2 and SDL1.2 if (!window) { common->Warning("GLimp_SetGamma called without window"); return; @@ -833,6 +1053,7 @@ void GLimp_SetGamma(unsigned short red[256], unsigned short green[256], unsigned if (SDL_SetGammaRamp(red, green, blue)) #endif common->Warning("Couldn't set gamma ramp: %s", SDL_GetError()); +#endif // SDL2 and SDL1.2 } /* @@ -843,6 +1064,7 @@ Restore original system gamma setting ================= */ void GLimp_ResetGamma() { +#if ! SDL_VERSION_ATLEAST(3, 0, 0) // only for SDL2 and SDL1.2 if( gammaOrigError ) { common->Warning( "Can't reset hardware gamma because getting the Gamma Ramp at startup failed!\n" ); common->Warning( "You might have to restart the game for gamma/brightness in shaders to work properly.\n" ); @@ -850,12 +1072,13 @@ void GLimp_ResetGamma() { } if( gammaOrigSet ) { -#if SDL_VERSION_ATLEAST(2, 0, 0) + #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_SetWindowGammaRamp( window, gammaOrigRed, gammaOrigGreen, gammaOrigBlue ); -#else + #else SDL_SetGammaRamp( gammaOrigRed, gammaOrigGreen, gammaOrigBlue ); -#endif + #endif } +#endif // ! SDL_VERSION_ATLEAST(3, 0, 0) } @@ -893,8 +1116,21 @@ void GLimp_GrabInput(int flags) { common->Warning("GLimp_GrabInput called without window"); return; } - -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (flags & GRAB_HIDECURSOR) { + SDL_HideCursor(); + } else { + SDL_ShowCursor(); + } + SDL_SetWindowRelativeMouseMode( window, (flags & GRAB_RELATIVEMOUSE) != 0 ); + if (flags & GRAB_GRABMOUSE) { + SDL_SetWindowMouseGrab( window, true ); + SDL_SetWindowKeyboardGrab( window, in_grabKeyboard.GetBool() ); + } else { + SDL_SetWindowMouseGrab( window, false ); + SDL_SetWindowKeyboardGrab( window, false ); + } +#elif SDL_VERSION_ATLEAST(2, 0, 0) SDL_ShowCursor( (flags & GRAB_HIDECURSOR) ? SDL_DISABLE : SDL_ENABLE ); SDL_SetRelativeMouseMode( (flags & GRAB_RELATIVEMOUSE) ? SDL_TRUE : SDL_FALSE ); SDL_SetWindowGrab( window, (flags & GRAB_GRABMOUSE) ? SDL_TRUE : SDL_FALSE ); @@ -909,7 +1145,11 @@ void GLimp_GrabInput(int flags) { bool GLimp_SetSwapInterval( int swapInterval ) { #if SDL_VERSION_ATLEAST(2, 0, 0) + #if SDL_VERSION_ATLEAST(3, 0, 0) + if ( ! SDL_GL_SetSwapInterval( swapInterval ) ) { + #elif SDL_VERSION_ATLEAST(2, 0, 0) if ( SDL_GL_SetSwapInterval( swapInterval ) < 0 ) { + #endif common->Warning( "SDL_GL_SetSwapInterval( %d ) not supported", swapInterval ); return false; } @@ -922,7 +1162,10 @@ bool GLimp_SetSwapInterval( int swapInterval ) bool GLimp_SetWindowResizable( bool enableResizable ) { -#if SDL_VERSION_ATLEAST(2, 0, 5) +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_SetWindowResizable( window, enableResizable ); + return true; +#elif SDL_VERSION_ATLEAST(2, 0, 5) SDL_SetWindowResizable( window, (SDL_bool)enableResizable ); return true; #else @@ -934,7 +1177,18 @@ bool GLimp_SetWindowResizable( bool enableResizable ) void GLimp_UpdateWindowSize() { #if SDL_VERSION_ATLEAST(2, 0, 0) - Uint32 winFlags = SDL_GetWindowFlags( window ); + My_SDL_WindowFlags winFlags = SDL_GetWindowFlags( window ); + + #if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_GetWindowSizeInPixels( window, &glConfig.vidWidth, &glConfig.vidHeight ); + + const SDL_DisplayMode* fullscreenMode = SDL_GetWindowFullscreenMode( window ); + if ( (winFlags & SDL_WINDOW_FULLSCREEN) != 0 && fullscreenMode != NULL) { + glConfig.winWidth = fullscreenMode->w; + glConfig.winHeight = fullscreenMode->h; + } + #else // SDL2 + SDL_GL_GetDrawableSize( window, &glConfig.vidWidth, &glConfig.vidHeight ); if ( (winFlags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN ) { // real fullscreen mode => must use SDL_GetWindowDisplayMode() // TODO: well, theoretically SDL_GetWindowSize() should work for fullscreen mode as well, @@ -951,13 +1205,13 @@ void GLimp_UpdateWindowSize() } else { common->Warning( "GLimp_UpdateWindowSize(): SDL_GetWindowDisplayMode() failed: %s\n", SDL_GetError() ); } - - } else { + } + #endif // SDL2 + else { int ww=0, wh=0; SDL_GetWindowSize( window, &ww, &wh ); glConfig.winWidth = ww; glConfig.winHeight = wh; } - SDL_GL_GetDrawableSize( window, &glConfig.vidWidth, &glConfig.vidHeight ); #endif } diff --git a/neo/sys/linux/main.cpp b/neo/sys/linux/main.cpp index 28a8a9b5..3468d393 100644 --- a/neo/sys/linux/main.cpp +++ b/neo/sys/linux/main.cpp @@ -32,7 +32,7 @@ If you have questions concerning this license or the applicable additional terms #include #include -#include +//#include - not needed, dhewm3 doesn't currently use SDL's SDL_main #include "sys/platform.h" #include "framework/Licensee.h" diff --git a/neo/sys/posix/posix_main.cpp b/neo/sys/posix/posix_main.cpp index ee975d32..276a0bb9 100644 --- a/neo/sys/posix/posix_main.cpp +++ b/neo/sys/posix/posix_main.cpp @@ -49,7 +49,13 @@ If you have questions concerning this license or the applicable additional terms #include "sys/posix/posix_public.h" -#include // clipboard +// SDL.h for clipboard: +#ifdef D3_SDL3 + #include +#else // SDL1.2 or SDL2 + #include +#endif + #define COMMAND_HISTORY 64 diff --git a/neo/sys/sys_imgui.cpp b/neo/sys/sys_imgui.cpp index 0a3003de..435d181d 100644 --- a/neo/sys/sys_imgui.cpp +++ b/neo/sys/sys_imgui.cpp @@ -1,7 +1,20 @@ #define IMGUI_DEFINE_MATH_OPERATORS -#include +#ifdef D3_SDL3 + #include + // compat with SDL2 + #define SDL_TEXTINPUT SDL_EVENT_TEXT_INPUT + #define SDL_CONTROLLERAXISMOTION SDL_EVENT_GAMEPAD_AXIS_MOTION + #define SDL_CONTROLLERBUTTONDOWN SDL_EVENT_GAMEPAD_BUTTON_DOWN + #define SDL_MOUSEBUTTONDOWN SDL_EVENT_MOUSE_BUTTON_DOWN + #define SDL_MOUSEMOTION SDL_EVENT_MOUSE_MOTION + #define SDL_MOUSEWHEEL SDL_EVENT_MOUSE_WHEEL + #define SDL_KEYDOWN SDL_EVENT_KEY_DOWN +#else // SDL2 + #include +#endif + #include "sys_imgui.h" @@ -13,7 +26,20 @@ typedef char* (*MY_XGETDEFAULTFUN)(Display*, const char*, const char*); #endif #include "../libs/imgui/backends/imgui_impl_opengl2.h" -#include "../libs/imgui/backends/imgui_impl_sdl2.h" + +#if SDL_VERSION_ATLEAST(3, 0, 0) + #include "../libs/imgui/backends/imgui_impl_sdl3.h" + #define ImGui_ImplSDLx_InitForOpenGL ImGui_ImplSDL3_InitForOpenGL + #define ImGui_ImplSDLx_Shutdown ImGui_ImplSDL3_Shutdown + #define ImGui_ImplSDLx_NewFrame ImGui_ImplSDL3_NewFrame + #define ImGui_ImplSDLx_ProcessEvent ImGui_ImplSDL3_ProcessEvent +#else + #include "../libs/imgui/backends/imgui_impl_sdl2.h" + #define ImGui_ImplSDLx_InitForOpenGL ImGui_ImplSDL2_InitForOpenGL + #define ImGui_ImplSDLx_Shutdown ImGui_ImplSDL2_Shutdown + #define ImGui_ImplSDLx_NewFrame ImGui_ImplSDL2_NewFrame + #define ImGui_ImplSDLx_ProcessEvent ImGui_ImplSDL2_ProcessEvent +#endif #include "framework/Common.h" #include "framework/KeyInput.h" @@ -145,7 +171,7 @@ void ShowWarningOverlay( const char* text ) warningOverlayStartPos = ImGui::GetMousePos(); } - +#if SDL_MAJOR_VERSION == 2 // not used with SDL3 static float GetDefaultDPI() { SDL_Window* win = sdlWindow; @@ -185,6 +211,7 @@ static float GetDefaultDPI() } return dpi; } +#endif // SDL2-only static float GetDefaultScale() { @@ -192,8 +219,13 @@ static float GetDefaultScale() // in HighDPI mode, the font sizes are already scaled (to window coordinates), apparently return 1.0f; } + +#if SDL_VERSION_ATLEAST(3, 0, 0) + float ret = SDL_GetWindowDisplayScale( sdlWindow ); +#else // TODO: different reference DPI on mac? also, doesn't work that well on my laptop.. float ret = GetDefaultDPI() / 96.0f; +#endif ret = round(ret*2.0)*0.5; // round to .0 or .5 return ret; } @@ -250,7 +282,7 @@ bool Init(void* _sdlWindow, void* sdlGlContext) imgui_scale.SetModified(); // so NewFrame() will load the scaled font // Setup Platform/Renderer backends - if ( ! ImGui_ImplSDL2_InitForOpenGL( sdlWindow, sdlGlContext ) ) { + if ( ! ImGui_ImplSDLx_InitForOpenGL( sdlWindow, sdlGlContext ) ) { ImGui::DestroyContext( imguiCtx ); imguiCtx = NULL; common->Warning( "Failed to initialize ImGui SDL platform backend!\n" ); @@ -258,7 +290,7 @@ bool Init(void* _sdlWindow, void* sdlGlContext) } if ( ! ImGui_ImplOpenGL2_Init() ) { - ImGui_ImplSDL2_Shutdown(); + ImGui_ImplSDLx_Shutdown(); ImGui::DestroyContext( imguiCtx ); imguiCtx = NULL; common->Warning( "Failed to initialize ImGui OpenGL renderer backend!\n" ); @@ -302,7 +334,7 @@ void Shutdown() // TODO: only if init was successful! ImGui_ImplOpenGL2_Shutdown(); - ImGui_ImplSDL2_Shutdown(); + ImGui_ImplSDLx_Shutdown(); ImGui::DestroyContext( imguiCtx ); imgui_initialized = false; } @@ -345,7 +377,7 @@ void NewFrame() else ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange; - ImGui_ImplSDL2_NewFrame(); + ImGui_ImplSDLx_NewFrame(); ImGui::NewFrame(); haveNewFrame = true; @@ -379,7 +411,7 @@ bool ProcessEvent(const void* sdlEvent) // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. - bool imguiUsedEvent = ImGui_ImplSDL2_ProcessEvent( ev ); + bool imguiUsedEvent = ImGui_ImplSDLx_ProcessEvent( ev ); if ( keybindModeEnabled ) { // in keybind mode, all input events are passed to Doom3 so it can translate them // to internal events and we can access and use them to create a new binding @@ -424,7 +456,11 @@ bool ProcessEvent(const void* sdlEvent) return true; case SDL_KEYDOWN: //case SDL_KEYUP: NOTE: see above why key up events are passed to the engine +#if SDL_VERSION_ATLEAST(3, 0, 0) + if ( ev->key.key < SDLK_F1 || ev->key.key > SDLK_F12) { +#else if ( ev->key.keysym.sym < SDLK_F1 || ev->key.keysym.sym > SDLK_F12) { +#endif // F1 - F12 are passed to the engine so its shortcuts // (like quickload or screenshot) still work // Doom3's menu does the same diff --git a/neo/sys/threads.cpp b/neo/sys/threads.cpp index 0b1132ae..99eb9e8c 100644 --- a/neo/sys/threads.cpp +++ b/neo/sys/threads.cpp @@ -26,10 +26,15 @@ If you have questions concerning this license or the applicable additional terms =========================================================================== */ -#include -#include -#include -#include +#ifdef D3_SDL3 + #define SDL_oldnames_h_ // HACK: I don't want SDL.h to drag in SDL_oldnames.h to avoid all the warnings about redefined definitions + #include +#else // SDL1.2 or SDL2 + #include + #include + #include + #include +#endif #include "sys/platform.h" #include "framework/Common.h" @@ -40,6 +45,15 @@ If you have questions concerning this license or the applicable additional terms // SDL1.2 doesn't have SDL_threadID but uses Uint32. // this typedef helps using the same code for SDL1.2 and SDL2 typedef Uint32 SDL_threadID; +#elif SDL_MAJOR_VERSION >= 3 + // backwards-compat with SDL2 + #define SDL_mutex SDL_Mutex + #define SDL_cond SDL_Condition + #define SDL_threadID SDL_ThreadID + #define SDL_CreateCond SDL_CreateCondition + #define SDL_DestroyCond SDL_DestroyCondition + #define SDL_CondWait SDL_WaitCondition + #define SDL_CondSignal SDL_SignalCondition #endif #if __cplusplus >= 201103 @@ -159,8 +173,12 @@ Sys_EnterCriticalSection void Sys_EnterCriticalSection(int index) { assert(index >= 0 && index < MAX_CRITICAL_SECTIONS); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_LockMutex(mutex[index]); // in SDL3, this returns void and can't fail +#else // SDL2 and SDL1.2 if (SDL_LockMutex(mutex[index]) != 0) common->Error("ERROR: SDL_LockMutex failed\n"); +#endif } /* @@ -171,8 +189,12 @@ Sys_LeaveCriticalSection void Sys_LeaveCriticalSection(int index) { assert(index >= 0 && index < MAX_CRITICAL_SECTIONS); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_UnlockMutex(mutex[index]); // in SDL3, this returns void and can't fail +#else // SDL2 and SDL1.2 if (SDL_UnlockMutex(mutex[index]) != 0) common->Error("ERROR: SDL_UnlockMutex failed\n"); +#endif } /* @@ -204,8 +226,12 @@ void Sys_WaitForEvent(int index) { signaled[index] = false; } else { waiting[index] = true; +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_CondWait(cond[index], mutex[CRITICAL_SECTION_SYS]); // in SDL3, this returns void and can't fail +#else // SDL2 and SDL1.2 if (SDL_CondWait(cond[index], mutex[CRITICAL_SECTION_SYS]) != 0) common->Error("ERROR: SDL_CondWait failed\n"); +#endif waiting[index] = false; } @@ -223,8 +249,12 @@ void Sys_TriggerEvent(int index) { Sys_EnterCriticalSection(CRITICAL_SECTION_SYS); if (waiting[index]) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_CondSignal(cond[index]); // in SDL3, this returns void and can't fail +#else // SDL2 and SDL1.2 if (SDL_CondSignal(cond[index]) != 0) common->Error("ERROR: SDL_CondSignal failed\n"); +#endif } else { // emulate windows behaviour: if no thread is waiting, leave the signal on so next wait keeps going signaled[index] = true; diff --git a/neo/tools/debugger/DebuggerServer.cpp b/neo/tools/debugger/DebuggerServer.cpp index 3b3adc7d..5d4446d6 100644 --- a/neo/tools/debugger/DebuggerServer.cpp +++ b/neo/tools/debugger/DebuggerServer.cpp @@ -41,6 +41,14 @@ If you have questions concerning this license or the applicable additional terms const int MAX_MSGLEN = 8600; #endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + // compat with SDL2 + #define SDL_CreateCond SDL_CreateCondition + #define SDL_DestroyCond SDL_DestroyCondition + #define SDL_CondWait SDL_WaitCondition + #define SDL_CondSignal SDL_SignalCondition +#endif + /* ================ rvDebuggerServer::rvDebuggerServer diff --git a/neo/tools/debugger/DebuggerServer.h b/neo/tools/debugger/DebuggerServer.h index 8f741ea1..8d62a5cd 100644 --- a/neo/tools/debugger/DebuggerServer.h +++ b/neo/tools/debugger/DebuggerServer.h @@ -28,7 +28,16 @@ If you have questions concerning this license or the applicable additional terms #ifndef DEBUGGERSERVER_H_ #define DEBUGGERSERVER_H_ -#include +#ifdef D3_SDL3 + #define SDL_oldnames_h_ // HACK: I don't want SDL.h to drag in SDL_oldnames.h to avoid all the warnings about redefined definitions + #include + // backwards-compat with SDL <= 2 + #define SDL_mutex SDL_Mutex + #define SDL_cond SDL_Condition +#else // SDL1.2 or SDL2 + #include +#endif + #include "sys/platform.h" #include "idlib/Str.h" #include "DebuggerMessages.h"