diff --git a/backends/events/sdl/sdl-events.cpp b/backends/events/sdl/sdl-events.cpp index 3cd124e6dfa78..ac6b52ec3a6b6 100644 --- a/backends/events/sdl/sdl-events.cpp +++ b/backends/events/sdl/sdl-events.cpp @@ -32,9 +32,13 @@ #include "engines/engine.h" #include "gui/gui-manager.h" -#if defined(USE_IMGUI) && SDL_VERSION_ATLEAST(2, 0, 0) +#if defined(USE_IMGUI) +#if SDL_VERSION_ATLEAST(3, 0, 0) +#include "backends/imgui/backends/imgui_impl_sdl3.h" +#elif SDL_VERSION_ATLEAST(2, 0, 0) #include "backends/imgui/backends/imgui_impl_sdl2.h" #endif +#endif #if SDL_VERSION_ATLEAST(2, 0, 0) #define GAMECONTROLLERDB_FILE "gamecontrollerdb.txt" @@ -52,7 +56,11 @@ void SdlEventSource::loadGameControllerMappingFile() { if (ConfMan.hasKey("controller_map_db")) { Common::FSNode file = Common::FSNode(ConfMan.getPath("controller_map_db")); if (file.exists()) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_AddGamepadMappingsFromFile(file.getPath().toString(Common::Path::kNativeSeparator).c_str())) +#else if (SDL_GameControllerAddMappingsFromFile(file.getPath().toString(Common::Path::kNativeSeparator).c_str()) < 0) +#endif error("File %s not valid: %s", file.getPath().toString(Common::Path::kNativeSeparator).c_str(), SDL_GetError()); else { loaded = true; @@ -65,7 +73,11 @@ void SdlEventSource::loadGameControllerMappingFile() { Common::FSNode dir = Common::FSNode(ConfMan.getPath("extrapath")); Common::FSNode file = dir.getChild(GAMECONTROLLERDB_FILE); if (file.exists()) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_AddGamepadMappingsFromFile(file.getPath().toString(Common::Path::kNativeSeparator).c_str())) +#else if (SDL_GameControllerAddMappingsFromFile(file.getPath().toString(Common::Path::kNativeSeparator).c_str()) < 0) +#endif error("File %s not valid: %s", file.getPath().toString(Common::Path::kNativeSeparator).c_str(), SDL_GetError()); else debug("Game controller DB file loaded: %s", file.getPath().toString(Common::Path::kNativeSeparator).c_str()); @@ -84,12 +96,22 @@ SdlEventSource::SdlEventSource() int joystick_num = ConfMan.getInt("joystick_num"); if (joystick_num >= 0) { // Initialize SDL joystick subsystem +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_InitSubSystem(SDL_INIT_JOYSTICK)) { +#else if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) { +#endif warning("Could not initialize SDL joystick: %s", SDL_GetError()); return; } -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD)) { + warning("Could not initialize SDL game controller: %s", SDL_GetError()); + return; + } + loadGameControllerMappingFile(); +#elif SDL_VERSION_ATLEAST(2, 0, 0) if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == -1) { warning("Could not initialize SDL game controller: %s", SDL_GetError()); return; @@ -126,7 +148,11 @@ int SdlEventSource::mapKey(SDL_Keycode sdlKey, SDL_Keymod mod, Uint16 unicode) { // Umlauts and others will set KEYCODE_INVALID on SDL2, so in such a case always keep unicode. if (key != Common::KEYCODE_INVALID) { // keycode is valid, check further also depending on modifiers +#if SDL_VERSION_ATLEAST(3,0,0) + if (mod & (SDL_KMOD_CTRL | SDL_KMOD_ALT)) { +#else if (mod & (KMOD_CTRL | KMOD_ALT)) { +#endif // Ctrl and/or Alt is active // // We need to restrict unicode to only up to 0x7E, because on macOS the option/alt key will switch to @@ -160,7 +186,11 @@ int SdlEventSource::mapKey(SDL_Keycode sdlKey, SDL_Keymod mod, Uint16 unicode) { if (key >= Common::KEYCODE_F1 && key <= Common::KEYCODE_F9) { return key - Common::KEYCODE_F1 + Common::ASCII_F1; } else if (key >= Common::KEYCODE_KP0 && key <= Common::KEYCODE_KP9) { +#if SDL_VERSION_ATLEAST(3,0,0) + if ((mod & SDL_KMOD_NUM) == 0) +#else if ((mod & KMOD_NUM) == 0) +#endif return 0; // In case Num-Lock is NOT enabled, return 0 for ascii, so that directional keys on numpad work return key - Common::KEYCODE_KP0 + '0'; } else if (key >= Common::KEYCODE_UP && key <= Common::KEYCODE_PAGEDOWN) { @@ -168,7 +198,11 @@ int SdlEventSource::mapKey(SDL_Keycode sdlKey, SDL_Keymod mod, Uint16 unicode) { } else if (unicode) { // Return unicode in case it's still set and wasn't filtered. return unicode; +#if SDL_VERSION_ATLEAST(3,0,0) + } else if (key >= 'a' && key <= 'z' && (mod & SDL_KMOD_SHIFT)) { +#else } else if (key >= 'a' && key <= 'z' && (mod & KMOD_SHIFT)) { +#endif return key & ~0x20; } else if (key >= Common::KEYCODE_NUMLOCK && key < Common::KEYCODE_LAST) { return 0; @@ -197,12 +231,22 @@ void SdlEventSource::SDLModToOSystemKeyFlags(SDL_Keymod mod, Common::Event &even event.kbd.flags = 0; - if (mod & KMOD_SHIFT) +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (mod & SDL_KMOD_GUI) + event.kbd.flags |= Common::KBD_META; + if (mod & SDL_KMOD_SHIFT) event.kbd.flags |= Common::KBD_SHIFT; - if (mod & KMOD_ALT) + if (mod & SDL_KMOD_ALT) event.kbd.flags |= Common::KBD_ALT; - if (mod & KMOD_CTRL) + if (mod & SDL_KMOD_CTRL) event.kbd.flags |= Common::KBD_CTRL; + + // Sticky flags + if (mod & SDL_KMOD_NUM) + event.kbd.flags |= Common::KBD_NUM; + if (mod & SDL_KMOD_CAPS) + event.kbd.flags |= Common::KBD_CAPS; +#else #if SDL_VERSION_ATLEAST(2, 0, 0) if (mod & KMOD_GUI) event.kbd.flags |= Common::KBD_META; @@ -211,11 +255,20 @@ void SdlEventSource::SDLModToOSystemKeyFlags(SDL_Keymod mod, Common::Event &even event.kbd.flags |= Common::KBD_META; #endif + if (mod & KMOD_SHIFT) + event.kbd.flags |= Common::KBD_SHIFT; + if (mod & KMOD_ALT) + event.kbd.flags |= Common::KBD_ALT; + if (mod & KMOD_CTRL) + event.kbd.flags |= Common::KBD_CTRL; + // Sticky flags if (mod & KMOD_NUM) event.kbd.flags |= Common::KBD_NUM; if (mod & KMOD_CAPS) event.kbd.flags |= Common::KBD_CAPS; +#endif + } Common::KeyCode SdlEventSource::SDLToOSystemKeycode(const SDL_Keycode key) { @@ -228,11 +281,19 @@ Common::KeyCode SdlEventSource::SDLToOSystemKeycode(const SDL_Keycode key) { case SDLK_ESCAPE: return Common::KEYCODE_ESCAPE; case SDLK_SPACE: return Common::KEYCODE_SPACE; case SDLK_EXCLAIM: return Common::KEYCODE_EXCLAIM; +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDLK_DBLAPOSTROPHE: return Common::KEYCODE_QUOTEDBL; +#else case SDLK_QUOTEDBL: return Common::KEYCODE_QUOTEDBL; +#endif case SDLK_HASH: return Common::KEYCODE_HASH; case SDLK_DOLLAR: return Common::KEYCODE_DOLLAR; case SDLK_AMPERSAND: return Common::KEYCODE_AMPERSAND; +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDLK_APOSTROPHE: return Common::KEYCODE_QUOTE; +#else case SDLK_QUOTE: return Common::KEYCODE_QUOTE; +#endif case SDLK_LEFTPAREN: return Common::KEYCODE_LEFTPAREN; case SDLK_RIGHTPAREN: return Common::KEYCODE_RIGHTPAREN; case SDLK_ASTERISK: return Common::KEYCODE_ASTERISK; @@ -263,6 +324,35 @@ Common::KeyCode SdlEventSource::SDLToOSystemKeycode(const SDL_Keycode key) { case SDLK_RIGHTBRACKET: return Common::KEYCODE_RIGHTBRACKET; case SDLK_CARET: return Common::KEYCODE_CARET; case SDLK_UNDERSCORE: return Common::KEYCODE_UNDERSCORE; +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDLK_GRAVE: return Common::KEYCODE_BACKQUOTE; + case SDLK_A: return Common::KEYCODE_a; + case SDLK_B: return Common::KEYCODE_b; + case SDLK_C: return Common::KEYCODE_c; + case SDLK_D: return Common::KEYCODE_d; + case SDLK_E: return Common::KEYCODE_e; + case SDLK_F: return Common::KEYCODE_f; + case SDLK_G: return Common::KEYCODE_g; + case SDLK_H: return Common::KEYCODE_h; + case SDLK_I: return Common::KEYCODE_i; + case SDLK_J: return Common::KEYCODE_j; + case SDLK_K: return Common::KEYCODE_k; + case SDLK_L: return Common::KEYCODE_l; + case SDLK_M: return Common::KEYCODE_m; + case SDLK_N: return Common::KEYCODE_n; + case SDLK_O: return Common::KEYCODE_o; + case SDLK_P: return Common::KEYCODE_p; + case SDLK_Q: return Common::KEYCODE_q; + case SDLK_R: return Common::KEYCODE_r; + case SDLK_S: return Common::KEYCODE_s; + case SDLK_T: return Common::KEYCODE_t; + case SDLK_U: return Common::KEYCODE_u; + case SDLK_V: return Common::KEYCODE_v; + case SDLK_W: return Common::KEYCODE_w; + case SDLK_X: return Common::KEYCODE_x; + case SDLK_Y: return Common::KEYCODE_y; + case SDLK_Z: return Common::KEYCODE_z; +#else case SDLK_BACKQUOTE: return Common::KEYCODE_BACKQUOTE; case SDLK_a: return Common::KEYCODE_a; case SDLK_b: return Common::KEYCODE_b; @@ -290,6 +380,7 @@ Common::KeyCode SdlEventSource::SDLToOSystemKeycode(const SDL_Keycode key) { case SDLK_x: return Common::KEYCODE_x; case SDLK_y: return Common::KEYCODE_y; case SDLK_z: return Common::KEYCODE_z; +#endif case SDLK_DELETE: return Common::KEYCODE_DELETE; case SDLK_KP_PERIOD: return Common::KEYCODE_KP_PERIOD; case SDLK_KP_DIVIDE: return Common::KEYCODE_KP_DIVIDE; @@ -360,13 +451,26 @@ Common::KeyCode SdlEventSource::SDLToOSystemKeycode(const SDL_Keycode key) { case SDLK_F17: return Common::KEYCODE_F17; case SDLK_F18: return Common::KEYCODE_F18; case SDLK_SLEEP: return Common::KEYCODE_SLEEP; - case SDLK_MUTE: return Common::KEYCODE_MUTE; case SDLK_VOLUMEUP: return Common::KEYCODE_VOLUMEUP; case SDLK_VOLUMEDOWN: return Common::KEYCODE_VOLUMEDOWN; +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDLK_MEDIA_EJECT: return Common::KEYCODE_EJECT; + case SDLK_MEDIA_NEXT_TRACK: return Common::KEYCODE_AUDIONEXT; + case SDLK_MEDIA_PREVIOUS_TRACK: return Common::KEYCODE_AUDIOPREV; + case SDLK_MEDIA_STOP: return Common::KEYCODE_AUDIOSTOP; + case SDLK_MEDIA_PLAY: return Common::KEYCODE_AUDIOPLAYPAUSE; + case SDLK_MUTE: return Common::KEYCODE_AUDIOMUTE; +#else case SDLK_EJECT: return Common::KEYCODE_EJECT; - case SDLK_WWW: return Common::KEYCODE_WWW; case SDLK_MAIL: return Common::KEYCODE_MAIL; + case SDLK_WWW: return Common::KEYCODE_WWW; case SDLK_CALCULATOR: return Common::KEYCODE_CALCULATOR; + case SDLK_AUDIONEXT: return Common::KEYCODE_AUDIONEXT; + case SDLK_AUDIOPREV: return Common::KEYCODE_AUDIOPREV; + case SDLK_AUDIOSTOP: return Common::KEYCODE_AUDIOSTOP; + case SDLK_AUDIOPLAY: return Common::KEYCODE_AUDIOPLAYPAUSE; + case SDLK_AUDIOMUTE: return Common::KEYCODE_AUDIOMUTE; +#endif case SDLK_CUT: return Common::KEYCODE_CUT; case SDLK_COPY: return Common::KEYCODE_COPY; case SDLK_PASTE: return Common::KEYCODE_PASTE; @@ -379,12 +483,10 @@ Common::KeyCode SdlEventSource::SDLToOSystemKeycode(const SDL_Keycode key) { case SDLK_AC_STOP: return Common::KEYCODE_AC_STOP; case SDLK_AC_REFRESH: return Common::KEYCODE_AC_REFRESH; case SDLK_AC_BOOKMARKS: return Common::KEYCODE_AC_BOOKMARKS; - case SDLK_AUDIONEXT: return Common::KEYCODE_AUDIONEXT; - case SDLK_AUDIOPREV: return Common::KEYCODE_AUDIOPREV; - case SDLK_AUDIOSTOP: return Common::KEYCODE_AUDIOSTOP; - case SDLK_AUDIOPLAY: return Common::KEYCODE_AUDIOPLAYPAUSE; - case SDLK_AUDIOMUTE: return Common::KEYCODE_AUDIOMUTE; -#if SDL_VERSION_ATLEAST(2, 0, 6) +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDLK_MEDIA_REWIND: return Common::KEYCODE_AUDIOREWIND; + case SDLK_MEDIA_FAST_FORWARD: return Common::KEYCODE_AUDIOFASTFORWARD; +#elif SDL_VERSION_ATLEAST(2, 0, 6) case SDLK_AUDIOREWIND: return Common::KEYCODE_AUDIOREWIND; case SDLK_AUDIOFASTFORWARD: return Common::KEYCODE_AUDIOFASTFORWARD; #endif @@ -417,10 +519,17 @@ Common::KeyCode SdlEventSource::SDLToOSystemKeycode(const SDL_Keycode key) { #if SDL_VERSION_ATLEAST(2, 0, 0) void SdlEventSource::preprocessFingerDown(SDL_Event *event) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + // front (1) or back (2) panel + SDL_TouchID port = event->tfinger.touchID; + // id (for multitouch) + SDL_FingerID id = event->tfinger.fingerID; +#else // front (1) or back (2) panel SDL_TouchID port = event->tfinger.touchId; // id (for multitouch) SDL_FingerID id = event->tfinger.fingerId; +#endif int x = _mouseX; int y = _mouseY; @@ -487,7 +596,11 @@ void SdlEventSource::finishSimulatedMouseClicks() { simulatedButton = SDL_BUTTON_RIGHT; } SDL_Event ev; +#if SDL_VERSION_ATLEAST(3, 0, 0) + ev.type = SDL_EVENT_MOUSE_BUTTON_UP; +#else ev.type = SDL_MOUSEBUTTONUP; +#endif ev.button.button = simulatedButton; ev.button.x = _mouseX; ev.button.y = _mouseY; @@ -501,10 +614,17 @@ void SdlEventSource::finishSimulatedMouseClicks() { } bool SdlEventSource::preprocessFingerUp(SDL_Event *event, Common::Event *ev) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + // front (1) or back (2) panel + SDL_TouchID port = event->tfinger.touchID; + // id (for multitouch) + SDL_FingerID id = event->tfinger.fingerID; +#else // front (1) or back (2) panel SDL_TouchID port = event->tfinger.touchId; // id (for multitouch) SDL_FingerID id = event->tfinger.fingerId; +#endif // find out how many fingers were down before this event int numFingersDown = 0; @@ -549,7 +669,11 @@ bool SdlEventSource::preprocessFingerUp(SDL_Event *event, Common::Event *ev) { } } +#if SDL_VERSION_ATLEAST(3, 0, 0) + event->type = SDL_EVENT_MOUSE_BUTTON_DOWN; +#else event->type = SDL_MOUSEBUTTONDOWN; +#endif event->button.button = simulatedButton; event->button.x = x; event->button.y = y; @@ -567,7 +691,11 @@ bool SdlEventSource::preprocessFingerUp(SDL_Event *event, Common::Event *ev) { else { simulatedButton = SDL_BUTTON_LEFT; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + event->type = SDL_EVENT_MOUSE_BUTTON_UP; +#else event->type = SDL_MOUSEBUTTONUP; +#endif event->button.button = simulatedButton; event->button.x = x; event->button.y = y; @@ -584,10 +712,17 @@ bool SdlEventSource::preprocessFingerUp(SDL_Event *event, Common::Event *ev) { } void SdlEventSource::preprocessFingerMotion(SDL_Event *event) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + // front (1) or back (2) panel + SDL_TouchID port = event->tfinger.touchID; + // id (for multitouch) + SDL_FingerID id = event->tfinger.fingerID; +#else // front (1) or back (2) panel SDL_TouchID port = event->tfinger.touchId; // id (for multitouch) SDL_FingerID id = event->tfinger.fingerId; +#endif // find out how many fingers were down before this event int numFingersDown = 0; @@ -676,7 +811,11 @@ void SdlEventSource::preprocessFingerMotion(SDL_Event *event) { _touchPanels[port]._multiFingerDragging = DRAG_THREE_FINGER; } SDL_Event ev; +#if SDL_VERSION_ATLEAST(3, 0, 0) + ev.type = SDL_EVENT_MOUSE_BUTTON_DOWN; +#else ev.type = SDL_MOUSEBUTTONDOWN; +#endif ev.button.button = simulatedButton; ev.button.x = mouseDownX; ev.button.y = mouseDownY; @@ -701,7 +840,11 @@ void SdlEventSource::preprocessFingerMotion(SDL_Event *event) { } } if (updatePointer) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + event->type = SDL_EVENT_MOUSE_MOTION; +#else event->type = SDL_MOUSEMOTION; +#endif event->motion.x = x; event->motion.y = y; } @@ -740,7 +883,33 @@ bool SdlEventSource::pollEvent(Common::Event &event) { while (SDL_PollEvent(&ev)) { preprocessEvents(&ev); -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + // Supported touch gestures: + // left mouse click: single finger short tap + // right mouse click: second finger short tap while first finger is still down + // pointer motion: single finger drag + if (ev.type == SDL_EVENT_FINGER_DOWN || ev.type == SDL_EVENT_FINGER_UP || ev.type == SDL_EVENT_FINGER_MOTION) { + // front (0) or back (1) panel + SDL_TouchID port = ev.tfinger.touchID; + // touchpad_mouse_mode off: use only front panel for direct touch control of pointer + // touchpad_mouse_mode on: also enable rear touch with indirect touch control + // where the finger can be somewhere else than the pointer and still move it + if (isTouchPortActive(port)) { + switch (ev.type) { + case SDL_EVENT_FINGER_DOWN: + preprocessFingerDown(&ev); + break; + case SDL_EVENT_FINGER_UP: + if (preprocessFingerUp(&ev, &event)) + return true; + break; + case SDL_EVENT_FINGER_MOTION: + preprocessFingerMotion(&ev); + break; + } + } + } +#elif SDL_VERSION_ATLEAST(2, 0, 0) // Supported touch gestures: // left mouse click: single finger short tap // right mouse click: second finger short tap while first finger is still down @@ -768,13 +937,20 @@ bool SdlEventSource::pollEvent(Common::Event &event) { } #endif -#if defined(USE_IMGUI) && SDL_VERSION_ATLEAST(2, 0, 0) +#if defined(USE_IMGUI) +#if SDL_VERSION_ATLEAST(3, 0, 0) + ImGui_ImplSDL3_ProcessEvent(&ev); + ImGuiIO &io = ImGui::GetIO(); + if (io.WantTextInput || io.WantCaptureMouse) + continue; +#elif SDL_VERSION_ATLEAST(2, 0, 0) if (ImGui_ImplSDL2_Ready()) { ImGui_ImplSDL2_ProcessEvent(&ev); ImGuiIO &io = ImGui::GetIO(); if (io.WantTextInput || io.WantCaptureMouse) continue; } +#endif #endif if (dispatchSDLEvent(ev, event)) return true; @@ -784,6 +960,19 @@ bool SdlEventSource::pollEvent(Common::Event &event) { } bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + switch (ev.type) { + case SDL_EVENT_KEY_DOWN: + return handleKeyDown(ev, event); + case SDL_EVENT_KEY_UP: + return handleKeyUp(ev, event); + case SDL_EVENT_MOUSE_MOTION: + return handleMouseMotion(ev, event); + case SDL_EVENT_MOUSE_BUTTON_DOWN: + return handleMouseButtonDown(ev, event); + case SDL_EVENT_MOUSE_BUTTON_UP: + return handleMouseButtonUp(ev, event); +#else switch (ev.type) { case SDL_KEYDOWN: return handleKeyDown(ev, event); @@ -797,9 +986,14 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) { return handleMouseButtonUp(ev, event); case SDL_SYSWMEVENT: return handleSysWMEvent(ev, event); +#endif #if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDL_EVENT_MOUSE_WHEEL: { +#else case SDL_MOUSEWHEEL: { +#endif Sint32 yDir = ev.wheel.y; // We want the mouse coordinates supplied with a mouse wheel event. // However, SDL2 does not supply these, thus we use whatever we got @@ -818,7 +1012,11 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) { } } +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDL_EVENT_TEXT_INPUT: { +#else case SDL_TEXTINPUT: { +#endif // When we get a TEXTINPUT event it means we got some user input for // which no KEYDOWN exists. SDL 1.2 introduces a "fake" key down+up // in such cases. We will do the same to mimic it's behavior. @@ -839,6 +1037,7 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) { return _queuedFakeKeyUp; } +#if !SDL_VERSION_ATLEAST(3, 0, 0) case SDL_WINDOWEVENT: // We're only interested in events from the current display window if (_graphicsManager) { @@ -849,7 +1048,13 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) { } switch (ev.window.event) { +#endif + +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDL_EVENT_WINDOW_EXPOSED: +#else case SDL_WINDOWEVENT_EXPOSED: +#endif if (_graphicsManager) { _graphicsManager->notifyVideoExpose(); } @@ -865,11 +1070,19 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) { // where we need to call SDL_SetWindowSize and we need the resulting event to be processed. // However if the documentation is correct we can ignore SDL_WINDOWEVENT_RESIZED since when we // get one we should always get a SDL_WINDOWEVENT_SIZE_CHANGED as well. +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: +#else case SDL_WINDOWEVENT_SIZE_CHANGED: +#endif //case SDL_WINDOWEVENT_RESIZED: return handleResizeEvent(event, ev.window.data1, ev.window.data2); +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDL_EVENT_WINDOW_FOCUS_GAINED: { +#else case SDL_WINDOWEVENT_FOCUS_GAINED: { +#endif // When we gain focus, we to update whether the display can turn off // dependingif a game isn't running or not event.type = Common::EVENT_FOCUS_GAINED; @@ -881,30 +1094,55 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) { return true; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDL_EVENT_WINDOW_FOCUS_LOST: { +#else case SDL_WINDOWEVENT_FOCUS_LOST: { +#endif // Always allow the display to turn off if ScummVM is out of focus event.type = Common::EVENT_FOCUS_LOST; SDL_EnableScreenSaver(); return true; } +#if !SDL_VERSION_ATLEAST(3, 0, 0) default: return false; } +#endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDL_EVENT_JOYSTICK_ADDED: +#else case SDL_JOYDEVICEADDED: +#endif return handleJoystickAdded(ev.jdevice, event); +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDL_EVENT_JOYSTICK_REMOVED: +#else case SDL_JOYDEVICEREMOVED: +#endif return handleJoystickRemoved(ev.jdevice, event); +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDL_EVENT_DROP_FILE: + event.type = Common::EVENT_DROP_FILE; + event.path = Common::Path(ev.drop.data, Common::Path::kNativeSeparator); + return true; +#else case SDL_DROPFILE: event.type = Common::EVENT_DROP_FILE; event.path = Common::Path(ev.drop.file, Common::Path::kNativeSeparator); SDL_free(ev.drop.file); return true; +#endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDL_EVENT_CLIPBOARD_UPDATE: +#else case SDL_CLIPBOARDUPDATE: +#endif event.type = Common::EVENT_CLIPBOARD_UPDATE; return true; #else @@ -918,7 +1156,11 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) { return handleResizeEvent(event, ev.resize.w, ev.resize.h); #endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDL_EVENT_QUIT: +#else case SDL_QUIT: +#endif event.type = Common::EVENT_QUIT; return true; @@ -926,6 +1168,22 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) { break; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (_joystick) { + switch (ev.type) { + case SDL_EVENT_JOYSTICK_BUTTON_DOWN: + return handleJoyButtonDown(ev, event); + case SDL_EVENT_JOYSTICK_BUTTON_UP: + return handleJoyButtonUp(ev, event); + case SDL_EVENT_JOYSTICK_AXIS_MOTION: + return handleJoyAxisMotion(ev, event); + case SDL_EVENT_JOYSTICK_HAT_MOTION: + return handleJoyHatMotion(ev, event); + default: + break; + } + } +#else if (_joystick) { switch (ev.type) { case SDL_JOYBUTTONDOWN: @@ -940,8 +1198,22 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) { break; } } +#endif -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (_controller) { + switch (ev.type) { + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: + return handleControllerButton(ev, event, false); + case SDL_EVENT_GAMEPAD_BUTTON_UP: + return handleControllerButton(ev, event, true); + case SDL_EVENT_GAMEPAD_AXIS_MOTION: + return handleControllerAxisMotion(ev, event); + default: + break; + } + } +#elif SDL_VERSION_ATLEAST(2, 0, 0) if (_controller) { switch (ev.type) { case SDL_CONTROLLERBUTTONDOWN: @@ -964,7 +1236,11 @@ bool SdlEventSource::handleKeyDown(SDL_Event &ev, Common::Event &event) { SDLModToOSystemKeyFlags(SDL_GetModState(), event); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Keycode sdlKeycode = ev.key.key; +#else SDL_Keycode sdlKeycode = obtainKeycode(ev.key.keysym); +#endif Common::KeyCode key = SDLToOSystemKeycode(sdlKeycode); // Handle scroll lock as a key modifier @@ -980,7 +1256,11 @@ bool SdlEventSource::handleKeyDown(SDL_Event &ev, Common::Event &event) { event.type = Common::EVENT_KEYDOWN; event.kbd.keycode = key; +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Keymod mod = ev.key.mod; +#else SDL_Keymod mod = (SDL_Keymod)ev.key.keysym.mod; +#endif #if defined(__amigaos4__) // On AmigaOS, SDL always reports numlock as off. However, we get KEYCODE_KP# only when // it is on, and get different keycodes (for example KEYCODE_PAGEDOWN) when it is off. @@ -989,7 +1269,7 @@ bool SdlEventSource::handleKeyDown(SDL_Event &ev, Common::Event &event) { mod = SDL_Keymod(mod | KMOD_NUM); } #endif - event.kbd.ascii = mapKey(sdlKeycode, mod, obtainUnicode(ev.key.keysym)); + event.kbd.ascii = mapKey(sdlKeycode, mod, obtainUnicode(ev.key)); #if SDL_VERSION_ATLEAST(2, 0, 0) event.kbdRepeat = ev.key.repeat; @@ -1004,7 +1284,11 @@ bool SdlEventSource::handleKeyUp(SDL_Event &ev, Common::Event &event) { SDLModToOSystemKeyFlags(SDL_GetModState(), event); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Keycode sdlKeycode = ev.key.key; +#else SDL_Keycode sdlKeycode = obtainKeycode(ev.key.keysym); +#endif // Set the scroll lock sticky flag if (_scrollLock) @@ -1013,7 +1297,12 @@ bool SdlEventSource::handleKeyUp(SDL_Event &ev, Common::Event &event) { event.type = Common::EVENT_KEYUP; event.kbd.keycode = SDLToOSystemKeycode(sdlKeycode); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Keymod mod = ev.key.mod; +#else SDL_Keymod mod = (SDL_Keymod)ev.key.keysym.mod; +#endif + #if defined(__amigaos4__) // On AmigaOS, SDL always reports numlock as off. However, we get KEYCODE_KP# only when // it is on, and get different keycodes (for example KEYCODE_PAGEDOWN) when it is off. @@ -1090,17 +1379,34 @@ bool SdlEventSource::handleSysWMEvent(SDL_Event &ev, Common::Event &event) { } void SdlEventSource::openJoystick(int joystickIndex) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + int numJoysticks = 0; + SDL_GetJoysticks(&numJoysticks); + if (numJoysticks > joystickIndex) { +#else if (SDL_NumJoysticks() > joystickIndex) { -#if SDL_VERSION_ATLEAST(2, 0, 0) +#endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (SDL_IsGamepad(joystickIndex)) { + _controller = SDL_OpenGamepad(joystickIndex); + debug("Using game controller: %s", SDL_GetGamepadName(_controller)); + } else +#elif SDL_VERSION_ATLEAST(2, 0, 0) if (SDL_IsGameController(joystickIndex)) { _controller = SDL_GameControllerOpen(joystickIndex); debug("Using game controller: %s", SDL_GameControllerName(_controller)); } else #endif { +#if SDL_VERSION_ATLEAST(3, 0, 0) + _joystick = SDL_OpenJoystick(joystickIndex); +#else _joystick = SDL_JoystickOpen(joystickIndex); +#endif debug("Using joystick: %s", -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_GetJoystickName(_joystick) +#elif SDL_VERSION_ATLEAST(2, 0, 0) SDL_JoystickName(_joystick) #else SDL_JoystickName(joystickIndex) @@ -1113,16 +1419,28 @@ void SdlEventSource::openJoystick(int joystickIndex) { } void SdlEventSource::closeJoystick() { -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (_controller) { + SDL_CloseGamepad(_controller); + _controller = nullptr; + } +#elif SDL_VERSION_ATLEAST(2, 0, 0) if (_controller) { SDL_GameControllerClose(_controller); _controller = nullptr; } #endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (_joystick) { + SDL_CloseJoystick(_joystick); + _joystick = nullptr; + } +#else if (_joystick) { SDL_JoystickClose(_joystick); _joystick = nullptr; } +#endif } int SdlEventSource::mapSDLJoystickButtonToOSystem(Uint8 sdlButton) { @@ -1231,7 +1549,11 @@ bool SdlEventSource::handleJoystickRemoved(const SDL_JoyDeviceEvent &device, Com SDL_Joystick *joystick; if (_controller) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + joystick = SDL_GetGamepadJoystick(_controller); +#else joystick = SDL_GameControllerGetJoystick(_controller); +#endif } else { joystick = _joystick; } @@ -1240,7 +1562,12 @@ bool SdlEventSource::handleJoystickRemoved(const SDL_JoyDeviceEvent &device, Com return false; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (SDL_GetJoystickID(joystick) != device.which) { +#else if (SDL_JoystickInstanceID(joystick) != device.which) { +#endif + return false; } @@ -1279,7 +1606,11 @@ int SdlEventSource::mapSDLControllerButtonToOSystem(Uint8 sdlButton) { } bool SdlEventSource::handleControllerButton(const SDL_Event &ev, Common::Event &event, bool buttonUp) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + int button = mapSDLControllerButtonToOSystem(ev.gbutton.button); +#else int button = mapSDLControllerButtonToOSystem(ev.cbutton.button); +#endif if (button < 0) return false; @@ -1291,9 +1622,14 @@ bool SdlEventSource::handleControllerButton(const SDL_Event &ev, Common::Event & } bool SdlEventSource::handleControllerAxisMotion(const SDL_Event &ev, Common::Event &event) { - event.type = Common::EVENT_JOYAXIS_MOTION; +#if SDL_VERSION_ATLEAST(3, 0, 0) + event.joystick.axis = ev.gaxis.axis; + event.joystick.position = ev.gaxis.value; +#else event.joystick.axis = ev.caxis.axis; event.joystick.position = ev.caxis.value; +#endif + event.type = Common::EVENT_JOYAXIS_MOTION; return true; } @@ -1337,6 +1673,7 @@ bool SdlEventSource::handleResizeEvent(Common::Event &event, int w, int h) { return false; } +#if !SDL_VERSION_ATLEAST(3, 0, 0) SDL_Keycode SdlEventSource::obtainKeycode(const SDL_Keysym keySym) { #if !SDL_VERSION_ATLEAST(2, 0, 0) && defined(WIN32) // WORKAROUND: SDL 1.2 on Windows does not use the user configured keyboard layout, @@ -1374,8 +1711,9 @@ SDL_Keycode SdlEventSource::obtainKeycode(const SDL_Keysym keySym) { return keySym.sym; } +#endif -uint32 SdlEventSource::obtainUnicode(const SDL_Keysym keySym) { +uint32 SdlEventSource::obtainUnicode(const SDL_KeyboardEvent &key) { #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_Event events[2]; @@ -1398,15 +1736,27 @@ uint32 SdlEventSource::obtainUnicode(const SDL_Keysym keySym) { // Instead a SDL_TEXTINPUT event is generated on key combinations that // generates unicode. // Here we peek into the event queue for the event to see if it exists. +#if SDL_VERSION_ATLEAST(3, 0, 0) + int n = SDL_PeepEvents(events, 2, SDL_PEEKEVENT, SDL_EVENT_KEY_DOWN, SDL_EVENT_TEXT_INPUT); + // Make sure that the TEXTINPUT event belongs to this KEYDOWN + // event and not another pending one. + if ((n > 0 && events[0].type == SDL_EVENT_TEXT_INPUT) + || (n > 1 && events[0].type != SDL_EVENT_KEY_DOWN && events[1].type == SDL_EVENT_TEXT_INPUT)) { +#else int n = SDL_PeepEvents(events, 2, SDL_PEEKEVENT, SDL_KEYDOWN, SDL_TEXTINPUT); // Make sure that the TEXTINPUT event belongs to this KEYDOWN // event and not another pending one. if ((n > 0 && events[0].type == SDL_TEXTINPUT) || (n > 1 && events[0].type != SDL_KEYDOWN && events[1].type == SDL_TEXTINPUT)) { +#endif // Remove the text input event we associate with the key press. This // makes sure we never get any SDL_TEXTINPUT events which do "belong" // to SDL_KEYDOWN events. +#if SDL_VERSION_ATLEAST(3, 0, 0) + n = SDL_PeepEvents(events, 1, SDL_GETEVENT, SDL_EVENT_TEXT_INPUT, SDL_EVENT_TEXT_INPUT); +#else n = SDL_PeepEvents(events, 1, SDL_GETEVENT, SDL_TEXTINPUT, SDL_TEXTINPUT); +#endif // This is basically a paranoia safety check because we know there // must be a text input event in the queue. if (n > 0) { @@ -1418,7 +1768,7 @@ uint32 SdlEventSource::obtainUnicode(const SDL_Keysym keySym) { return 0; } #else - return keySym.unicode; + return key.keysym.unicode; #endif } diff --git a/backends/events/sdl/sdl-events.h b/backends/events/sdl/sdl-events.h index 2fcf289e7bc70..02b548ee9de5e 100644 --- a/backends/events/sdl/sdl-events.h +++ b/backends/events/sdl/sdl-events.h @@ -73,7 +73,10 @@ class SdlEventSource : public Common::EventSource { /** Joystick */ SDL_Joystick *_joystick; -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + /** Game controller */ + SDL_Gamepad *_controller; +#elif SDL_VERSION_ATLEAST(2, 0, 0) /** Game controller */ SDL_GameController *_controller; #endif @@ -186,15 +189,17 @@ class SdlEventSource : public Common::EventSource { bool handleResizeEvent(Common::Event &event, int w, int h); /** - * Extracts unicode information for the specific key sym. + * Extracts unicode information for the specific key. * May only be used for key down events. */ - uint32 obtainUnicode(const SDL_Keysym keySym); + uint32 obtainUnicode(const SDL_KeyboardEvent &key); +#if !SDL_VERSION_ATLEAST(3, 0, 0) /** * Extracts the keycode for the specified key sym. */ SDL_Keycode obtainKeycode(const SDL_Keysym keySym); +#endif /** * Whether _fakeMouseMove contains an event we need to send. diff --git a/backends/graphics/openglsdl/openglsdl-graphics.cpp b/backends/graphics/openglsdl/openglsdl-graphics.cpp index 35dd7e3101da5..d58d6344eb668 100644 --- a/backends/graphics/openglsdl/openglsdl-graphics.cpp +++ b/backends/graphics/openglsdl/openglsdl-graphics.cpp @@ -201,7 +201,11 @@ OpenGLSdlGraphicsManager::~OpenGLSdlGraphicsManager() { #endif notifyContextDestroy(); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_GL_DestroyContext(_glContext); +#else SDL_GL_DeleteContext(_glContext); +#endif #else if (_hwScreen) { notifyContextDestroy(); @@ -255,7 +259,14 @@ void OpenGLSdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) bool OpenGLSdlGraphicsManager::getFeatureState(OSystem::Feature f) const { switch (f) { case OSystem::kFeatureFullscreenMode: -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (_window && _window->getSDLWindow()) { + // SDL_GetWindowFullscreenMode returns a pointer to the exclusive fullscreen mode to use or NULL for borderless + return ((SDL_GetWindowFlags(_window->getSDLWindow()) & SDL_WINDOW_FULLSCREEN) != 0) && (SDL_GetWindowFullscreenMode(_window->getSDLWindow()) == NULL); + } else { + return _wantsFullScreen; + } +#elif SDL_VERSION_ATLEAST(2, 0, 0) if (_window && _window->getSDLWindow()) { return (SDL_GetWindowFlags(_window->getSDLWindow()) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; } else { @@ -574,11 +585,19 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) { destroyImGui(); #endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_GL_DestroyContext(_glContext); +#else SDL_GL_DeleteContext(_glContext); +#endif _glContext = nullptr; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY; +#else uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI; +#endif if (_wantsFullScreen) { // On Linux/X11, when toggling to fullscreen, the window manager saves @@ -596,7 +615,13 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) { width = _desiredFullscreenWidth; height = _desiredFullscreenHeight; +#if SDL_VERSION_ATLEAST(3, 0, 0) + flags |= SDL_WINDOW_FULLSCREEN; + SDL_SetWindowFullscreenMode(_window->getSDLWindow(), NULL); + SDL_SyncWindow(_window->getSDLWindow()); +#else flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; +#endif } if (!_wantsFullScreen && ConfMan.getBool("window_maximized", Common::ConfigManager::kApplicationDomain)) { @@ -608,7 +633,7 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, _glContextMajor); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, _glContextMinor); -#ifdef NINTENDO_SWITCH +#if defined(NINTENDO_SWITCH) && !SDL_VERSION_ATLEAST(3, 0, 0) // Switch quirk: Switch seems to need this flag, otherwise the screen // is zoomed when switching from Normal graphics mode to OpenGL flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; @@ -627,7 +652,7 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) { initImGui(nullptr, _glContext); #endif - if (SDL_GL_SetSwapInterval(_vsync ? 1 : 0)) { + if (!SDL_GL_SetSwapInterval(_vsync ? 1 : 0)) { warning("Unable to %s VSync: %s", _vsync ? "enable" : "disable", SDL_GetError()); } diff --git a/backends/graphics/sdl/sdl-graphics.cpp b/backends/graphics/sdl/sdl-graphics.cpp index e2fd1e9342a7a..4137493e5ccce 100644 --- a/backends/graphics/sdl/sdl-graphics.cpp +++ b/backends/graphics/sdl/sdl-graphics.cpp @@ -38,7 +38,15 @@ #include "backends/platform/sdl/emscripten/emscripten.h" #endif -#if defined(USE_IMGUI) && SDL_VERSION_ATLEAST(2, 0, 0) +#if defined(USE_IMGUI) && SDL_VERSION_ATLEAST(3, 0, 0) +#include "backends/imgui/backends/imgui_impl_sdl3.h" +#ifdef USE_OPENGL +#include "backends/imgui/backends/imgui_impl_opengl3.h" +#endif +#ifdef USE_IMGUI_SDLRENDERER3 +#include "backends/imgui/backends/imgui_impl_sdlrenderer3.h" +#endif +#elif defined(USE_IMGUI) && SDL_VERSION_ATLEAST(2, 0, 0) #include "backends/imgui/backends/imgui_impl_sdl2.h" #ifdef USE_OPENGL #include "backends/imgui/backends/imgui_impl_opengl3.h" @@ -56,7 +64,14 @@ SdlGraphicsManager::SdlGraphicsManager(SdlEventSource *source, SdlWindow *window { ConfMan.registerDefault("fullscreen_res", "desktop"); +#if SDL_VERSION_ATLEAST(3, 0, 0) + float fx, fy; + SDL_GetMouseState(&fx, &fy); + _cursorX = static_cast(fx); + _cursorY = static_cast(fy); +#else SDL_GetMouseState(&_cursorX, &_cursorY); +#endif } void SdlGraphicsManager::activateManager() { @@ -219,8 +234,16 @@ bool SdlGraphicsManager::showMouse(bool visible) { // _cursorX and _cursorY are currently always clipped to the active // area, so we need to ask SDL where the system's mouse cursor is // instead +#if SDL_VERSION_ATLEAST(3, 0, 0) + float fx, fy; + int x, y; + SDL_GetMouseState(&fx, &fy); + x = static_cast(fx); + y = static_cast(fy); +#else int x, y; SDL_GetMouseState(&x, &y); +#endif if (!_activeArea.drawRect.contains(Common::Point(x, y))) { showCursor = true; } @@ -312,7 +335,15 @@ bool SdlGraphicsManager::notifyMousePosition(Common::Point &mouse) { } void SdlGraphicsManager::showSystemMouseCursor(bool visible) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if(visible) { + SDL_ShowCursor(); + } else { + SDL_HideCursor(); + } +#else SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE); +#endif } void SdlGraphicsManager::setSystemMousePosition(const int x, const int y) { @@ -354,7 +385,11 @@ bool SdlGraphicsManager::createOrUpdateWindow(int width, int height, const Uint3 // resized the game window), or when the launcher is visible (since a user // may change the scaler, which should reset the window size) if (!_window->getSDLWindow() || _lastFlags != flags || _overlayVisible || _allowWindowSizeReset) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + const bool fullscreen = (flags & (SDL_WINDOW_FULLSCREEN)) != 0; +#else const bool fullscreen = (flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)) != 0; +#endif const bool maximized = (flags & SDL_WINDOW_MAXIMIZED); if (!fullscreen && !maximized) { if (_hintedWidth > width) { @@ -368,6 +403,14 @@ bool SdlGraphicsManager::createOrUpdateWindow(int width, int height, const Uint3 if (!_window->createOrUpdateWindow(width, height, flags)) { return false; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + if(fullscreen) { + if(!SDL_SetWindowFullscreenMode(_window->getSDLWindow(), NULL)) + return false; + if(!SDL_SyncWindow(_window->getSDLWindow())) + return false; + } +#endif _lastFlags = flags; _allowWindowSizeReset = false; @@ -594,13 +637,21 @@ void SdlGraphicsManager::initImGui(SDL_Renderer *renderer, void *glContext) { if (!_imGuiReady && glContext) { io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!ImGui_ImplSDL3_InitForOpenGL(_window->getSDLWindow(), glContext)) { +#else if (!ImGui_ImplSDL2_InitForOpenGL(_window->getSDLWindow(), glContext)) { +#endif ImGui::DestroyContext(); return; } if (!ImGui_ImplOpenGL3_Init("#version 110")) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + ImGui_ImplSDL3_Shutdown(); +#else ImGui_ImplSDL2_Shutdown(); +#endif ImGui::DestroyContext(); return; } @@ -608,7 +659,23 @@ void SdlGraphicsManager::initImGui(SDL_Renderer *renderer, void *glContext) { _imGuiReady = true; } #endif -#ifdef USE_IMGUI_SDLRENDERER2 +#ifdef USE_IMGUI_SDLRENDERER3 + if (!_imGuiReady && renderer) { + if (!ImGui_ImplSDL3_InitForSDLRenderer(_window->getSDLWindow(), renderer)) { + ImGui::DestroyContext(); + return; + } + + if (!ImGui_ImplSDLRenderer3_Init(renderer)) { + ImGui_ImplSDL3_Shutdown(); + ImGui::DestroyContext(); + return; + } + + _imGuiReady = true; + _imGuiSDLRenderer = renderer; + } +#elif defined(USE_IMGUI_SDLRENDERER2) if (!_imGuiReady && renderer) { if (!ImGui_ImplSDL2_InitForSDLRenderer(_window->getSDLWindow(), renderer)) { ImGui::DestroyContext(); @@ -649,7 +716,11 @@ void SdlGraphicsManager::renderImGui() { _imGuiInited = true; } -#ifdef USE_IMGUI_SDLRENDERER2 +#ifdef USE_IMGUI_SDLRENDERER3 + if (_imGuiSDLRenderer) { + ImGui_ImplSDLRenderer3_NewFrame(); + } else { +#elif defined(USE_IMGUI_SDLRENDERER2) if (_imGuiSDLRenderer) { ImGui_ImplSDLRenderer2_NewFrame(); } else { @@ -657,15 +728,23 @@ void SdlGraphicsManager::renderImGui() { #ifdef USE_OPENGL ImGui_ImplOpenGL3_NewFrame(); #endif -#ifdef USE_IMGUI_SDLRENDERER2 +#if defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3) } #endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + ImGui_ImplSDL3_NewFrame(); +#else ImGui_ImplSDL2_NewFrame(); +#endif ImGui::NewFrame(); _imGuiCallbacks.render(); ImGui::Render(); -#ifdef USE_IMGUI_SDLRENDERER2 +#ifdef USE_IMGUI_SDLRENDERER3 + if (_imGuiSDLRenderer) { + ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), _imGuiSDLRenderer); + } else { +#elif defined(USE_IMGUI_SDLRENDERER2) if (_imGuiSDLRenderer) { ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), _imGuiSDLRenderer); } else { @@ -679,7 +758,7 @@ void SdlGraphicsManager::renderImGui() { ImGui::RenderPlatformWindowsDefault(); SDL_GL_MakeCurrent(backup_current_window, backup_current_context); #endif -#ifdef USE_IMGUI_SDLRENDERER2 +#if defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3) } #endif } @@ -696,7 +775,11 @@ void SdlGraphicsManager::destroyImGui() { _imGuiInited = false; _imGuiReady = false; -#ifdef USE_IMGUI_SDLRENDERER2 +#ifdef USE_IMGUI_SDLRENDERER3 + if (_imGuiSDLRenderer) { + ImGui_ImplSDLRenderer3_Shutdown(); + } else { +#elif defined(USE_IMGUI_SDLRENDERER2) if (_imGuiSDLRenderer) { ImGui_ImplSDLRenderer2_Shutdown(); } else { @@ -704,10 +787,14 @@ void SdlGraphicsManager::destroyImGui() { #ifdef USE_OPENGL ImGui_ImplOpenGL3_Shutdown(); #endif -#ifdef USE_IMGUI_SDLRENDERER2 +#if defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3) } #endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + ImGui_ImplSDL3_Shutdown(); +#else ImGui_ImplSDL2_Shutdown(); +#endif ImGui::DestroyContext(); } #endif diff --git a/backends/graphics/sdl/sdl-graphics.h b/backends/graphics/sdl/sdl-graphics.h index af5d76514f689..8225b2d4046ea 100644 --- a/backends/graphics/sdl/sdl-graphics.h +++ b/backends/graphics/sdl/sdl-graphics.h @@ -161,7 +161,10 @@ class SdlGraphicsManager : virtual public WindowedGraphicsManager, public Common * values stored by the graphics manager. */ void getWindowSizeFromSdl(int *width, int *height) const { -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + assert(_window); + SDL_GetWindowSizeInPixels(_window->getSDLWindow(), width, height); +#elif SDL_VERSION_ATLEAST(2, 0, 0) assert(_window); SDL_GL_GetDrawableSize(_window->getSDLWindow(), width, height); #else diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp index 9a63088c70097..5804077e866f8 100644 --- a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp +++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp @@ -189,13 +189,21 @@ SurfaceSdlGraphicsManager::~SurfaceSdlGraphicsManager() { delete _scaler; delete _mouseScaler; if (_mouseOrigSurface) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(_mouseOrigSurface); +#else SDL_FreeSurface(_mouseOrigSurface); +#endif if (_mouseOrigSurface == _mouseSurface) { _mouseSurface = nullptr; } } if (_mouseSurface) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(_mouseOrigSurface); +#else SDL_FreeSurface(_mouseSurface); +#endif } free(_currentPalette); free(_overlayPalette); @@ -484,6 +492,28 @@ OSystem::TransactionError SurfaceSdlGraphicsManager::endGFXTransaction() { return (OSystem::TransactionError)errors; } +#if SDL_VERSION_ATLEAST(3, 0, 0) +Graphics::PixelFormat SurfaceSdlGraphicsManager::convertSDLPixelFormat(SDL_PixelFormat format) const { + const SDL_PixelFormatDetails *in = SDL_GetPixelFormatDetails(format); + assert(in); + if (in->bytes_per_pixel == 1 && ( + (in->Rmask == 0xff && in->Gmask == 0xff && in->Bmask == 0xff) || + (in->Rmask == 0 && in->Gmask == 0 && in->Bmask == 0) + )) + return Graphics::PixelFormat::createFormatCLUT8(); + Graphics::PixelFormat out(in->bytes_per_pixel, + in->Rbits, in->Gbits, + in->Bbits, in->Abits, + in->Rshift, in->Gshift, + in->Bshift, in->Ashift); + + // Workaround to SDL not providing an accurate Aloss value on some platforms. + if (in->Amask == 0) + out.aLoss = 8; + + return out; +} +#else Graphics::PixelFormat SurfaceSdlGraphicsManager::convertSDLPixelFormat(SDL_PixelFormat *in) const { if (in->BytesPerPixel == 1 && ( (in->Rmask == 0xff && in->Gmask == 0xff && in->Bmask == 0xff) || @@ -502,6 +532,7 @@ Graphics::PixelFormat SurfaceSdlGraphicsManager::convertSDLPixelFormat(SDL_Pixel return out; } +#endif #ifdef USE_RGB_COLOR Common::List SurfaceSdlGraphicsManager::getSupportedFormats() const { @@ -533,6 +564,20 @@ void SurfaceSdlGraphicsManager::detectSupportedFormats() { #if SDL_VERSION_ATLEAST(2, 0, 0) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_DisplayMode* pDefaultMode = SDL_GetDesktopDisplayMode(_window->getDisplayIndex()); + if (!pDefaultMode) { + error("Could not get default system display mode"); + } + + int bpp; + Uint32 rMask, gMask, bMask, aMask; + if (!SDL_GetMasksForPixelFormat(pDefaultMode->format, &bpp, &rMask, &gMask, &bMask, &aMask)) { + error("Could not convert system pixel format %s to masks", SDL_GetPixelFormatName(pDefaultMode->format)); + } + + const uint8 bytesPerPixel = SDL_BYTESPERPIXEL(pDefaultMode->format); +#else SDL_DisplayMode defaultMode; if (SDL_GetDesktopDisplayMode(_window->getDisplayIndex(), &defaultMode) != 0) { error("Could not get default system display mode"); @@ -545,6 +590,8 @@ void SurfaceSdlGraphicsManager::detectSupportedFormats() { } const uint8 bytesPerPixel = SDL_BYTESPERPIXEL(defaultMode.format); +#endif + uint8 rBits, rShift, gBits, gShift, bBits, bShift, aBits, aShift; maskToBitCount(rMask, rBits, rShift); maskToBitCount(gMask, gBits, gShift); @@ -853,7 +900,13 @@ void SurfaceSdlGraphicsManager::fixupResolutionForAspectRatio(AspectRatio desire int bestW = 0, bestH = 0; uint bestMetric = (uint)-1; // Metric is wasted space -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + int numModes; + const int display = _window->getDisplayIndex(); + SDL_DisplayMode** modes = SDL_GetFullscreenDisplayModes(display, &numModes); + for (int i = 0; i < numModes; ++i) { + SDL_DisplayMode* mode = modes[i]; +#elif SDL_VERSION_ATLEAST(2, 0, 0) const int display = _window->getDisplayIndex(); const int numModes = SDL_GetNumDisplayModes(display); SDL_DisplayMode modeData, *mode = &modeData; @@ -884,7 +937,10 @@ void SurfaceSdlGraphicsManager::fixupResolutionForAspectRatio(AspectRatio desire // Make editors a bit more happy by having the same amount of closing as // opening curley braces. -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + } + SDL_free(modes); +#elif SDL_VERSION_ATLEAST(2, 0, 0) } #else } @@ -915,7 +971,11 @@ void SurfaceSdlGraphicsManager::setupHardwareSize() { } void SurfaceSdlGraphicsManager::initGraphicsSurface() { +#if SDL_VERSION_ATLEAST(3, 0, 0) + Uint32 flags = 0; +#else Uint32 flags = SDL_SWSURFACE; +#endif if (_videoMode.fullscreen) flags |= SDL_FULLSCREEN; @@ -943,8 +1003,13 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() { const Uint32 gMask = ((0xFF >> format.gLoss) << format.gShift); const Uint32 bMask = ((0xFF >> format.bLoss) << format.bShift); const Uint32 aMask = ((0xFF >> format.aLoss) << format.aShift); +#if SDL_VERSION_ATLEAST(3, 0, 0) + _screen = SDL_CreateSurface(_videoMode.screenWidth, _videoMode.screenHeight, + SDL_GetPixelFormatForMasks(_screenFormat.bytesPerPixel * 8, rMask, gMask, bMask, aMask)); +#else _screen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.screenWidth, _videoMode.screenHeight, _screenFormat.bytesPerPixel * 8, rMask, gMask, bMask, aMask); +#endif if (_screen == nullptr) error("allocating _screen failed"); @@ -1013,6 +1078,19 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() { // // Need some extra bytes around when using 2xSaI +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_PixelFormatDetails *pixelFormatDetails = SDL_GetPixelFormatDetails(_hwScreen->format); + if (pixelFormatDetails == nullptr) + error("getting pixel format details failed"); + _tmpscreen = SDL_CreateSurface(_videoMode.screenWidth + _maxExtraPixels * 2, + _videoMode.screenHeight + _maxExtraPixels * 2, + SDL_GetPixelFormatForMasks( + pixelFormatDetails->bits_per_pixel, + pixelFormatDetails->Rmask, + pixelFormatDetails->Gmask, + pixelFormatDetails->Bmask, + pixelFormatDetails->Amask)); +#else _tmpscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.screenWidth + _maxExtraPixels * 2, _videoMode.screenHeight + _maxExtraPixels * 2, _hwScreen->format->BitsPerPixel, @@ -1020,6 +1098,7 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() { _hwScreen->format->Gmask, _hwScreen->format->Bmask, _hwScreen->format->Amask); +#endif if (_tmpscreen == nullptr) error("allocating _tmpscreen failed"); @@ -1030,12 +1109,22 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() { _videoMode.screenWidth, _videoMode.screenHeight, _maxExtraPixels); } +#if SDL_VERSION_ATLEAST(3, 0, 0) + _overlayscreen = SDL_CreateSurface(_videoMode.overlayWidth, _videoMode.overlayHeight, + SDL_GetPixelFormatForMasks( + pixelFormatDetails->bits_per_pixel, + pixelFormatDetails->Rmask, + pixelFormatDetails->Gmask, + pixelFormatDetails->Bmask, + pixelFormatDetails->Amask)); +#else _overlayscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.overlayWidth, _videoMode.overlayHeight, _hwScreen->format->BitsPerPixel, _hwScreen->format->Rmask, _hwScreen->format->Gmask, _hwScreen->format->Bmask, _hwScreen->format->Amask); +#endif if (_overlayscreen == nullptr) error("allocating _overlayscreen failed"); @@ -1045,6 +1134,16 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() { if (_overlayFormat.bytesPerPixel == 1 && _overlayFormat.rBits() == 0) _overlayFormat = Graphics::PixelFormat(1, 3, 3, 2, 0, 5, 2, 0, 0); +#if SDL_VERSION_ATLEAST(3, 0, 0) + _tmpscreen2 = SDL_CreateSurface(_videoMode.overlayWidth + _maxExtraPixels * 2, + _videoMode.overlayHeight + _maxExtraPixels * 2, + SDL_GetPixelFormatForMasks( + pixelFormatDetails->bits_per_pixel, + pixelFormatDetails->Rmask, + pixelFormatDetails->Gmask, + pixelFormatDetails->Bmask, + pixelFormatDetails->Amask)); +#else _tmpscreen2 = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.overlayWidth + _maxExtraPixels * 2, _videoMode.overlayHeight + _maxExtraPixels * 2, _hwScreen->format->BitsPerPixel, @@ -1052,6 +1151,7 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() { _hwScreen->format->Gmask, _hwScreen->format->Bmask, _hwScreen->format->Amask); +#endif if (_tmpscreen2 == nullptr) error("allocating _tmpscreen2 failed"); @@ -1066,7 +1166,11 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() { void SurfaceSdlGraphicsManager::unloadGFXMode() { if (_screen) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(_screen); +#else SDL_FreeSurface(_screen); +#endif _screen = nullptr; } @@ -1075,33 +1179,57 @@ void SurfaceSdlGraphicsManager::unloadGFXMode() { #endif if (_hwScreen) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(_hwScreen); +#else SDL_FreeSurface(_hwScreen); +#endif _hwScreen = nullptr; } if (_tmpscreen) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(_tmpscreen); +#else SDL_FreeSurface(_tmpscreen); +#endif _tmpscreen = nullptr; } if (_tmpscreen2) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(_tmpscreen2); +#else SDL_FreeSurface(_tmpscreen2); +#endif _tmpscreen2 = nullptr; } if (_overlayscreen) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(_overlayscreen); +#else SDL_FreeSurface(_overlayscreen); +#endif _overlayscreen = nullptr; } #ifdef USE_OSD if (_osdMessageSurface) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(_osdMessageSurface); +#else SDL_FreeSurface(_osdMessageSurface); +#endif _osdMessageSurface = nullptr; } if (_osdIconSurface) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(_osdIconSurface); +#else SDL_FreeSurface(_osdIconSurface); +#endif _osdIconSurface = nullptr; } #endif @@ -1128,15 +1256,27 @@ bool SurfaceSdlGraphicsManager::hotswapGFXMode() { // Release the HW screen surface if (_hwScreen) { - SDL_FreeSurface(_hwScreen); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(_osdIconSurface); +#else + SDL_FreeSurface(_osdIconSurface); +#endif _hwScreen = nullptr; } if (_tmpscreen) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(_tmpscreen); +#else SDL_FreeSurface(_tmpscreen); +#endif _tmpscreen = nullptr; } if (_tmpscreen2) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(_tmpscreen2); +#else SDL_FreeSurface(_tmpscreen2); +#endif _tmpscreen2 = nullptr; } @@ -1158,8 +1298,13 @@ bool SurfaceSdlGraphicsManager::hotswapGFXMode() { SDL_BlitSurface(old_overlayscreen, nullptr, _overlayscreen, nullptr); // Free the old surfaces +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(old_screen); + SDL_DestroySurface(old_overlayscreen); +#else SDL_FreeSurface(old_screen); SDL_FreeSurface(old_overlayscreen); +#endif // Update cursor to new scale blitCursor(); @@ -1318,7 +1463,7 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() { if (_isDoubleBuf && _numDirtyRects) _forceRedraw = true; -#if defined(USE_IMGUI) && defined(USE_IMGUI_SDLRENDERER2) +#if defined(USE_IMGUI) && (defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3)) if (_imGuiCallbacks.render) { _forceRedraw = true; } @@ -1367,7 +1512,14 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() { SDL_LockSurface(srcSurf); SDL_LockSurface(_hwScreen); +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_PixelFormatDetails *pixelFormatDetails = SDL_GetPixelFormatDetails(_hwScreen->format); + if (!pixelFormatDetails) + error("SDL_GetPixelFormatDetails failed: %s", SDL_GetError()); + bpp = pixelFormatDetails->bytes_per_pixel; +#else bpp = _hwScreen->format->BytesPerPixel; +#endif srcPitch = srcSurf->pitch; dstPitch = _hwScreen->pitch; @@ -1476,11 +1628,19 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() { SDL_LockSurface(_hwScreen); // Use white as color for now. +#if SDL_VERSION_ATLEAST(3, 0, 0) + Uint32 rectColor = SDL_MapSurfaceRGB(_hwScreen, 0xFF, 0xFF, 0xFF); +#else Uint32 rectColor = SDL_MapRGB(_hwScreen->format, 0xFF, 0xFF, 0xFF); +#endif // First draw the top and bottom lines // then draw the left and right lines +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (pixelFormatDetails->bytes_per_pixel == 2) { +#else if (_hwScreen->format->BytesPerPixel == 2) { +#endif uint16 *top = (uint16 *)((byte *)_hwScreen->pixels + y * _hwScreen->pitch + x * 2); uint16 *bottom = (uint16 *)((byte *)_hwScreen->pixels + (y + h) * _hwScreen->pitch + x * 2); byte *left = ((byte *)_hwScreen->pixels + y * _hwScreen->pitch + x * 2); @@ -1498,7 +1658,11 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() { left += _hwScreen->pitch; right += _hwScreen->pitch; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + } else if (pixelFormatDetails->bytes_per_pixel == 4) { +#else } else if (_hwScreen->format->BytesPerPixel == 4) { +#endif uint32 *top = (uint32 *)((byte *)_hwScreen->pixels + y * _hwScreen->pitch + x * 4); uint32 *bottom = (uint32 *)((byte *)_hwScreen->pixels + (y + h) * _hwScreen->pitch + x * 4); byte *left = ((byte *)_hwScreen->pixels + y * _hwScreen->pitch + x * 4); @@ -1541,7 +1705,7 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() { #if SDL_VERSION_ATLEAST(2, 0, 0) -#if defined(USE_IMGUI) && defined(USE_IMGUI_SDLRENDERER2) +#if defined(USE_IMGUI) && (defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3)) renderImGui(); #endif @@ -1576,7 +1740,11 @@ bool SurfaceSdlGraphicsManager::saveScreenshot(const Common::Path &filename) con bool success; +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Palette *sdlPalette = SDL_CreateSurfacePalette(_hwScreen); +#else SDL_Palette *sdlPalette = _hwScreen->format->palette; +#endif if (sdlPalette) { byte palette[256 * 3]; for (int i = 0; i < sdlPalette->ncolors; i++) { @@ -1678,7 +1846,11 @@ void SurfaceSdlGraphicsManager::copyRectToScreen(const void *buf, int pitch, int addDirtyRect(x, y, w, h, false); // Try to lock the screen surface +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_LockSurface(_screen)) +#else if (SDL_LockSurface(_screen) == -1) +#endif error("SDL_LockSurface failed: %s", SDL_GetError()); byte *dst = (byte *)_screen->pixels + y * _screen->pitch + x * _screenFormat.bytesPerPixel; @@ -1708,7 +1880,11 @@ Graphics::Surface *SurfaceSdlGraphicsManager::lockScreen() { _screenIsLocked = true; // Try to lock the screen surface +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_LockSurface(_screen)) +#else if (SDL_LockSurface(_screen) == -1) +#endif error("SDL_LockSurface failed: %s", SDL_GetError()); _framebuffer.init(_screen->w, _screen->h, _screen->pitch, _screen->pixels, _screenFormat); @@ -1944,12 +2120,22 @@ void SurfaceSdlGraphicsManager::clearOverlay() { if (SDL_BlitSurface(_screen, &src, _tmpscreen, &dst) != 0) error("SDL_BlitSurface failed: %s", SDL_GetError()); +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_PixelFormatDetails *pixelFormatDetails = SDL_GetPixelFormatDetails(_tmpscreen->format); + if (!pixelFormatDetails) + error("SDL_GetPixelFormatDetails failed: %s", SDL_GetError()); +#endif + SDL_LockSurface(_tmpscreen); SDL_LockSurface(_overlayscreen); // Transpose from game palette to RGB332 (overlay palette) if (_isHwPalette) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + byte *p = (byte *)(_tmpscreen->pixels) + _maxExtraPixels * _tmpscreen->pitch + _maxExtraPixels * pixelFormatDetails->bytes_per_pixel; +#else byte *p = (byte *)(_tmpscreen->pixels) + _maxExtraPixels * _tmpscreen->pitch + _maxExtraPixels * _tmpscreen->format->BytesPerPixel; +#endif int pitchSkip = _tmpscreen->pitch - _videoMode.screenWidth; for (int y = 0; y < _videoMode.screenHeight; y++) { for (int x = 0; x < _videoMode.screenWidth; x++, p++) { @@ -1960,7 +2146,11 @@ void SurfaceSdlGraphicsManager::clearOverlay() { } } +#if SDL_VERSION_ATLEAST(3, 0, 0) + _scaler->scale((byte *)(_tmpscreen->pixels) + _maxExtraPixels * _tmpscreen->pitch + _maxExtraPixels * pixelFormatDetails->bytes_per_pixel, _tmpscreen->pitch, +#else _scaler->scale((byte *)(_tmpscreen->pixels) + _maxExtraPixels * _tmpscreen->pitch + _maxExtraPixels * _tmpscreen->format->BytesPerPixel, _tmpscreen->pitch, +#endif (byte *)_overlayscreen->pixels, _overlayscreen->pitch, _videoMode.screenWidth, _videoMode.screenHeight, 0, 0); #ifdef USE_ASPECT @@ -1981,7 +2171,11 @@ void SurfaceSdlGraphicsManager::grabOverlay(Graphics::Surface &surface) const { if (_overlayscreen == nullptr) return; +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_LockSurface(_overlayscreen)) +#else if (SDL_LockSurface(_overlayscreen) == -1) +#endif error("SDL_LockSurface failed: %s", SDL_GetError()); assert(surface.w >= _videoMode.overlayWidth); @@ -2003,7 +2197,14 @@ void SurfaceSdlGraphicsManager::copyRectToOverlay(const void *buf, int pitch, in return; const byte *src = (const byte *)buf; +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_PixelFormatDetails *pixelFormatDetails = SDL_GetPixelFormatDetails(_overlayscreen->format); + if (!pixelFormatDetails) + error("SDL_GetPixelFormatDetails failed: %s", SDL_GetError()); + uint bpp = pixelFormatDetails->bytes_per_pixel; +#else uint bpp = _overlayscreen->format->BytesPerPixel; +#endif // Clip the coordinates if (x < 0) { @@ -2032,7 +2233,11 @@ void SurfaceSdlGraphicsManager::copyRectToOverlay(const void *buf, int pitch, in // Mark the modified region as dirty addDirtyRect(x, y, w, h, true); +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_LockSurface(_overlayscreen)) +#else if (SDL_LockSurface(_overlayscreen) == -1) +#endif error("SDL_LockSurface failed: %s", SDL_GetError()); byte *dst = (byte *)_overlayscreen->pixels + y * _overlayscreen->pitch + x * bpp; @@ -2185,7 +2390,11 @@ void SurfaceSdlGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, _mouseCurState.h = h; if (_mouseOrigSurface) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(_mouseOrigSurface); +#else SDL_FreeSurface(_mouseOrigSurface); +#endif if (_mouseSurface == _mouseOrigSurface) { _mouseSurface = nullptr; @@ -2195,7 +2404,11 @@ void SurfaceSdlGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, } if (formatChanged && _mouseSurface) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(_mouseSurface); +#else SDL_FreeSurface(_mouseSurface); +#endif _mouseSurface = nullptr; } @@ -2206,6 +2419,17 @@ void SurfaceSdlGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, assert(!_mouseOrigSurface); // Allocate bigger surface because scalers will read past the boudaries. +#if SDL_VERSION_ATLEAST(3, 0, 0) + _mouseOrigSurface = SDL_CreateSurface( + _mouseCurState.w + _maxExtraPixels * 2, + _mouseCurState.h + _maxExtraPixels * 2, + SDL_GetPixelFormatForMasks( + _cursorFormat.bytesPerPixel * 8, + ((0xFF >> _cursorFormat.rLoss) << _cursorFormat.rShift), + ((0xFF >> _cursorFormat.gLoss) << _cursorFormat.gShift), + ((0xFF >> _cursorFormat.bLoss) << _cursorFormat.bShift), + ((0xFF >> _cursorFormat.aLoss) << _cursorFormat.aShift))); +#else _mouseOrigSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, _mouseCurState.w + _maxExtraPixels * 2, _mouseCurState.h + _maxExtraPixels * 2, @@ -2214,6 +2438,7 @@ void SurfaceSdlGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, ((0xFF >> _cursorFormat.gLoss) << _cursorFormat.gShift), ((0xFF >> _cursorFormat.bLoss) << _cursorFormat.bShift), ((0xFF >> _cursorFormat.aLoss) << _cursorFormat.aShift)); +#endif if (_mouseOrigSurface == nullptr) { error("Allocating _mouseOrigSurface failed"); @@ -2234,8 +2459,14 @@ void SurfaceSdlGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, } if (keycolorChanged) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + uint32 flags = _disableMouseKeyColor ? 0 : SDL_SRCCOLORKEY | SDL_SRCALPHA; + SDL_SetSurfaceColorKey(_mouseOrigSurface, flags, _mouseKeyColor); + SDL_SetSurfaceRLE(_mouseOrigSurface, !_disableMouseKeyColor); +#else uint32 flags = _disableMouseKeyColor ? 0 : SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA; SDL_SetColorKey(_mouseOrigSurface, flags, _mouseKeyColor); +#endif } SDL_LockSurface(_mouseOrigSurface); @@ -2253,9 +2484,13 @@ void SurfaceSdlGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, } // Draw from [_maxExtraPixels,_maxExtraPixels] since scalers will read past boudaries +#if SDL_VERSION_ATLEAST(3, 0, 0) + Graphics::copyBlit((byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch * _maxExtraPixels + _maxExtraPixels * _cursorFormat.bytesPerPixel, + (const byte *)buf, _mouseOrigSurface->pitch, w * _cursorFormat.bytesPerPixel, w, h, _cursorFormat.bytesPerPixel); +#else Graphics::copyBlit((byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch * _maxExtraPixels + _maxExtraPixels * _mouseOrigSurface->format->BytesPerPixel, (const byte *)buf, _mouseOrigSurface->pitch, w * _cursorFormat.bytesPerPixel, w, h, _cursorFormat.bytesPerPixel); - +#endif SDL_UnlockSurface(_mouseOrigSurface); blitCursor(); @@ -2316,8 +2551,26 @@ void SurfaceSdlGraphicsManager::blitCursor() { if (sizeChanged || !_mouseSurface) { if (_mouseSurface) +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(_mouseSurface); +#else SDL_FreeSurface(_mouseSurface); +#endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_PixelFormatDetails *pixelFormatDetails = SDL_GetPixelFormatDetails(_mouseOrigSurface->format); + if (pixelFormatDetails == nullptr) + error("getting pixel format details failed"); + _mouseSurface = SDL_CreateSurface( + _mouseCurState.rW, + _mouseCurState.rH, + SDL_GetPixelFormatForMasks( + pixelFormatDetails->bits_per_pixel, + pixelFormatDetails->Rmask, + pixelFormatDetails->Gmask, + pixelFormatDetails->Bmask, + pixelFormatDetails->Amask)); +#else _mouseSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, _mouseCurState.rW, _mouseCurState.rH, @@ -2326,14 +2579,21 @@ void SurfaceSdlGraphicsManager::blitCursor() { _mouseOrigSurface->format->Gmask, _mouseOrigSurface->format->Bmask, _mouseOrigSurface->format->Amask); +#endif if (_mouseSurface == nullptr) error("Allocating _mouseSurface failed"); } SDL_SetColors(_mouseSurface, _cursorPaletteDisabled ? _currentPalette : _cursorPalette, 0, 256); +#if SDL_VERSION_ATLEAST(3, 0, 0) + uint32 flags = _disableMouseKeyColor ? 0 : SDL_SRCCOLORKEY | SDL_SRCALPHA; + SDL_SetSurfaceColorKey(_mouseSurface, flags, _mouseKeyColor); + SDL_SetSurfaceRLE(_mouseSurface, !_disableMouseKeyColor); +#else uint32 flags = _disableMouseKeyColor ? 0 : SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA; SDL_SetColorKey(_mouseSurface, flags, _mouseKeyColor); +#endif SDL_LockSurface(_mouseOrigSurface); SDL_LockSurface(_mouseSurface); @@ -2347,23 +2607,55 @@ void SurfaceSdlGraphicsManager::blitCursor() { // fall back on the Normal scaler when a smaller cursor is supplied. if (_mouseScaler && _scalerPlugin->canDrawCursor() && (uint)_mouseCurState.h >= _extraPixels) { _mouseScaler->setFactor(_videoMode.scaleFactor); +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_PixelFormatDetails *pixelFormatDetails = SDL_GetPixelFormatDetails(_mouseOrigSurface->format); + if (pixelFormatDetails == nullptr) + error("getting pixel format details failed"); + _mouseScaler->scale( + (byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch * _maxExtraPixels + _maxExtraPixels * pixelFormatDetails->bytes_per_pixel, + _mouseOrigSurface->pitch, (byte *)_mouseSurface->pixels, _mouseSurface->pitch, + _mouseCurState.w, _mouseCurState.h, 0, 0); +#else _mouseScaler->scale( (byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch * _maxExtraPixels + _maxExtraPixels * _mouseOrigSurface->format->BytesPerPixel, _mouseOrigSurface->pitch, (byte *)_mouseSurface->pixels, _mouseSurface->pitch, _mouseCurState.w, _mouseCurState.h, 0, 0); +#endif } else #endif { +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_PixelFormatDetails *pixelFormatDetails = SDL_GetPixelFormatDetails(_mouseOrigSurface->format); + if (pixelFormatDetails == nullptr) + error("getting pixel format details failed"); + Graphics::scaleBlit((byte *)_mouseSurface->pixels, (const byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch * _maxExtraPixels + _maxExtraPixels * pixelFormatDetails->bytes_per_pixel, + _mouseSurface->pitch, _mouseOrigSurface->pitch, + _mouseCurState.w * _videoMode.scaleFactor, _mouseCurState.h * _videoMode.scaleFactor, + _mouseCurState.w, _mouseCurState.h, convertSDLPixelFormat(_mouseSurface->format)); +#else Graphics::scaleBlit((byte *)_mouseSurface->pixels, (const byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch * _maxExtraPixels + _maxExtraPixels * _mouseOrigSurface->format->BytesPerPixel, _mouseSurface->pitch, _mouseOrigSurface->pitch, _mouseCurState.w * _videoMode.scaleFactor, _mouseCurState.h * _videoMode.scaleFactor, _mouseCurState.w, _mouseCurState.h, convertSDLPixelFormat(_mouseSurface->format)); +#endif } } else { +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_PixelFormatDetails *srcPixelFormatDetails = SDL_GetPixelFormatDetails(_mouseOrigSurface->format); + if (srcPixelFormatDetails == nullptr) + error("getting pixel format details failed"); + const SDL_PixelFormatDetails *dstPixelFormatDetails = SDL_GetPixelFormatDetails(_mouseSurface->format); + if (dstPixelFormatDetails == nullptr) + error("getting pixel format details failed"); + Graphics::copyBlit((byte *)_mouseSurface->pixels, (const byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch * _maxExtraPixels + _maxExtraPixels * srcPixelFormatDetails->bytes_per_pixel, + _mouseSurface->pitch, _mouseOrigSurface->pitch, + _mouseCurState.w, _mouseCurState.h, dstPixelFormatDetails->bytes_per_pixel); +#else Graphics::copyBlit((byte *)_mouseSurface->pixels, (const byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch * _maxExtraPixels + _maxExtraPixels * _mouseOrigSurface->format->BytesPerPixel, _mouseSurface->pitch, _mouseOrigSurface->pitch, _mouseCurState.w, _mouseCurState.h, _mouseSurface->format->BytesPerPixel); +#endif } #ifdef USE_ASPECT @@ -2497,10 +2789,19 @@ void SurfaceSdlGraphicsManager::displayMessageOnOSD(const Common::U32String &msg if (height > _hwScreen->h) height = _hwScreen->h; +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_PixelFormatDetails *pixelFormatDetails = SDL_GetPixelFormatDetails(_hwScreen->format); + if (pixelFormatDetails == nullptr) + error("getting pixel format details failed"); + _osdMessageSurface = SDL_CreateSurface( + width, height, + SDL_GetPixelFormatForMasks(pixelFormatDetails->bits_per_pixel, pixelFormatDetails->Rmask, pixelFormatDetails->Gmask, pixelFormatDetails->Bmask, pixelFormatDetails->Amask)); +#else _osdMessageSurface = SDL_CreateRGBSurface( SDL_SWSURFACE, width, height, _hwScreen->format->BitsPerPixel, _hwScreen->format->Rmask, _hwScreen->format->Gmask, _hwScreen->format->Bmask, _hwScreen->format->Amask ); +#endif // Lock the surface if (SDL_LockSurface(_osdMessageSurface)) @@ -2508,7 +2809,11 @@ void SurfaceSdlGraphicsManager::displayMessageOnOSD(const Common::U32String &msg // Draw a dark gray rect // TODO: Rounded corners ? Border? +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_FillSurfaceRect(_osdMessageSurface, nullptr, SDL_MapSurfaceRGB(_osdMessageSurface, 64, 64, 64)); +#else SDL_FillRect(_osdMessageSurface, nullptr, SDL_MapRGB(_osdMessageSurface->format, 64, 64, 64)); +#endif Graphics::Surface dst; dst.init(_osdMessageSurface->w, _osdMessageSurface->h, _osdMessageSurface->pitch, _osdMessageSurface->pixels, @@ -2518,7 +2823,11 @@ void SurfaceSdlGraphicsManager::displayMessageOnOSD(const Common::U32String &msg for (i = 0; i < lines.size(); i++) { font->drawString(&dst, lines[i], 0, 0 + i * lineHeight + vOffset + lineSpacing, width, +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_MapSurfaceRGB(_osdMessageSurface, 255, 255, 255), +#else SDL_MapRGB(_osdMessageSurface->format, 255, 255, 255), +#endif Graphics::kTextAlignCenter, 0, true); } @@ -2529,7 +2838,12 @@ void SurfaceSdlGraphicsManager::displayMessageOnOSD(const Common::U32String &msg _osdMessageAlpha = SDL_ALPHA_TRANSPARENT + kOSDInitialAlpha * (SDL_ALPHA_OPAQUE - SDL_ALPHA_TRANSPARENT) / 100; _osdMessageFadeStartTime = SDL_GetTicks() + kOSDFadeOutDelay; // Enable alpha blending +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_SetAlpha(_osdMessageSurface, SDL_SRCALPHA, _osdMessageAlpha); + SDL_SetSurfaceRLE(_osdMessageSurface, true); +#else SDL_SetAlpha(_osdMessageSurface, SDL_RLEACCEL | SDL_SRCALPHA, _osdMessageAlpha); +#endif #if defined(MACOSX) macOSTouchbarUpdate(msg.encode().c_str()); @@ -2564,13 +2878,28 @@ void SurfaceSdlGraphicsManager::displayActivityIconOnOSD(const Graphics::Surface } if (_osdIconSurface) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(_osdIconSurface); +#else SDL_FreeSurface(_osdIconSurface); +#endif _osdIconSurface = nullptr; } if (icon) { const Graphics::PixelFormat &iconFormat = icon->format; +#if SDL_VERSION_ATLEAST(3, 0, 0) + _osdIconSurface = SDL_CreateSurface( + icon->w, icon->h, + SDL_GetPixelFormatForMasks( + iconFormat.bytesPerPixel * 8, + ((0xFF >> iconFormat.rLoss) << iconFormat.rShift), + ((0xFF >> iconFormat.gLoss) << iconFormat.gShift), + ((0xFF >> iconFormat.bLoss) << iconFormat.bShift), + ((0xFF >> iconFormat.aLoss) << iconFormat.aShift)) + ); +#else _osdIconSurface = SDL_CreateRGBSurface( SDL_SWSURFACE, icon->w, icon->h, iconFormat.bytesPerPixel * 8, @@ -2579,6 +2908,7 @@ void SurfaceSdlGraphicsManager::displayActivityIconOnOSD(const Graphics::Surface ((0xFF >> iconFormat.bLoss) << iconFormat.bShift), ((0xFF >> iconFormat.aLoss) << iconFormat.aShift) ); +#endif // Lock the surface if (SDL_LockSurface(_osdIconSurface)) @@ -2609,7 +2939,11 @@ SDL_Rect SurfaceSdlGraphicsManager::getOSDIconRect() const { void SurfaceSdlGraphicsManager::removeOSDMessage() { // Remove the previous message if (_osdMessageSurface) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(_osdMessageSurface); +#else SDL_FreeSurface(_osdMessageSurface); +#endif _forceRedraw = true; } @@ -2635,7 +2969,12 @@ void SurfaceSdlGraphicsManager::updateOSD() { const int startAlpha = SDL_ALPHA_TRANSPARENT + kOSDInitialAlpha * (SDL_ALPHA_OPAQUE - SDL_ALPHA_TRANSPARENT) / 100; _osdMessageAlpha = startAlpha + diff * (SDL_ALPHA_TRANSPARENT - startAlpha) / kOSDFadeOutDuration; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_SetAlpha(_osdMessageSurface, SDL_SRCALPHA, _osdMessageAlpha); + SDL_SetSurfaceRLE(_osdMessageSurface, true); +#else SDL_SetAlpha(_osdMessageSurface, SDL_RLEACCEL | SDL_SRCALPHA, _osdMessageAlpha); +#endif } if (_osdMessageAlpha == SDL_ALPHA_TRANSPARENT) { @@ -2823,7 +3162,7 @@ void SurfaceSdlGraphicsManager::notifyResize(const int width, const int height) #if SDL_VERSION_ATLEAST(2, 0, 0) void SurfaceSdlGraphicsManager::deinitializeRenderer() { -#if defined(USE_IMGUI) && defined(USE_IMGUI_SDLRENDERER2) +#if defined(USE_IMGUI) && (defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3)) destroyImGui(); #endif @@ -2840,12 +3179,18 @@ void SurfaceSdlGraphicsManager::recreateScreenTexture() { if (!_renderer) return; +#if !SDL_VERSION_ATLEAST(3, 0, 0) SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, _videoMode.filtering ? "linear" : "nearest"); +#endif SDL_Texture *oldTexture = _screenTexture; _screenTexture = SDL_CreateTexture(_renderer, SDL_PIXELFORMAT_RGB565, SDL_TEXTUREACCESS_STREAMING, _videoMode.hardwareWidth, _videoMode.hardwareHeight); - if (_screenTexture) + if (_screenTexture) { SDL_DestroyTexture(oldTexture); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_SetTextureScaleMode(_screenTexture, _videoMode.filtering ? SDL_SCALEMODE_LINEAR : SDL_SCALEMODE_NEAREST); +#endif + } else _screenTexture = oldTexture; } @@ -2854,15 +3199,27 @@ SDL_Surface *SurfaceSdlGraphicsManager::SDL_SetVideoMode(int width, int height, deinitializeRenderer(); uint32 createWindowFlags = SDL_WINDOW_RESIZABLE; - uint32 rendererFlags = 0; if ((flags & SDL_FULLSCREEN) != 0) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + createWindowFlags |= SDL_WINDOW_FULLSCREEN; +#else createWindowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP; +#endif } if (!createOrUpdateWindow(width, height, createWindowFlags)) { return nullptr; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + if ((flags & SDL_FULLSCREEN) != 0) { + if (!SDL_SetWindowFullscreenMode(_window->getSDLWindow(), NULL)) + warning("SDL_SetWindowFullscreenMode failed (%s)", SDL_GetError()); + if(!SDL_SyncWindow(_window->getSDLWindow())) + warning("SDL_SyncWindow failed (%s)", SDL_GetError()); + } +#endif + #if defined(MACOSX) && SDL_VERSION_ATLEAST(2, 0, 10) // WORKAROUND: Bug #11430: "macOS: blurry content on Retina displays" // Since SDL 2.0.10, Metal takes priority over OpenGL rendering on macOS, @@ -2871,18 +3228,34 @@ SDL_Surface *SurfaceSdlGraphicsManager::SDL_SetVideoMode(int width, int height, SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); #endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, _window->getSDLWindow()); + SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, _videoMode.vsync ? 1 : 0); + _renderer = SDL_CreateRendererWithProperties(props); + SDL_DestroyProperties(props); +#else + uint32 rendererFlags = 0; if (_videoMode.vsync) { rendererFlags |= SDL_RENDERER_PRESENTVSYNC; } - _renderer = SDL_CreateRenderer(_window->getSDLWindow(), -1, rendererFlags); +#endif if (!_renderer) { if (_videoMode.vsync) { // VSYNC might not be available, so retry without VSYNC warning("SDL_SetVideoMode: SDL_CreateRenderer() failed with VSYNC option, retrying without it..."); _videoMode.vsync = false; +#if SDL_VERSION_ATLEAST(3, 0, 0) + props = SDL_CreateProperties(); + SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, _window->getSDLWindow()); + SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, 0); + _renderer = SDL_CreateRendererWithProperties(props); + SDL_DestroyProperties(props); +#else rendererFlags &= ~SDL_RENDERER_PRESENTVSYNC; _renderer = SDL_CreateRenderer(_window->getSDLWindow(), -1, rendererFlags); +#endif } if (!_renderer) { deinitializeRenderer(); @@ -2893,23 +3266,36 @@ SDL_Surface *SurfaceSdlGraphicsManager::SDL_SetVideoMode(int width, int height, getWindowSizeFromSdl(&_windowWidth, &_windowHeight); handleResize(_windowWidth, _windowHeight); +#if !SDL_VERSION_ATLEAST(3, 0, 0) SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, _videoMode.filtering ? "linear" : "nearest"); +#endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_PixelFormat format = SDL_PIXELFORMAT_RGB565; +#else Uint32 format = SDL_PIXELFORMAT_RGB565; +#endif _screenTexture = SDL_CreateTexture(_renderer, format, SDL_TEXTUREACCESS_STREAMING, width, height); if (!_screenTexture) { deinitializeRenderer(); return nullptr; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_SetTextureScaleMode(_screenTexture, _videoMode.filtering ? SDL_SCALEMODE_LINEAR : SDL_SCALEMODE_NEAREST); +#endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Surface *screen = SDL_CreateSurface(width, height, format); +#else SDL_Surface *screen = SDL_CreateRGBSurfaceWithFormat(0, width, height, SDL_BITSPERPIXEL(format), format); +#endif if (!screen) { deinitializeRenderer(); return nullptr; } -#if defined(USE_IMGUI) && defined(USE_IMGUI_SDLRENDERER2) +#if defined(USE_IMGUI) && (defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3)) // Setup Dear ImGui initImGui(_renderer, nullptr); #endif @@ -2940,18 +3326,38 @@ void SurfaceSdlGraphicsManager::SDL_UpdateRects(SDL_Surface *screen, int numrect SDL_RenderClear(_renderer); - if (rotangle != 0) + if (rotangle != 0) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_FRect fViewport; + SDL_RectToFRect(&viewport, &fViewport); + SDL_RenderTextureRotated(_renderer, _screenTexture, nullptr, &fViewport, rotangle, nullptr, SDL_FLIP_NONE); +#else SDL_RenderCopyEx(_renderer, _screenTexture, nullptr, &viewport, rotangle, nullptr, SDL_FLIP_NONE); - else +#endif + } + else { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_FRect fViewport; + SDL_RectToFRect(&viewport, &fViewport); + SDL_RenderTexture(_renderer, _screenTexture, nullptr, &fViewport); +#else SDL_RenderCopy(_renderer, _screenTexture, nullptr, &viewport); +#endif + } } int SurfaceSdlGraphicsManager::SDL_SetColors(SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Palette *palette = SDL_CreateSurfacePalette(surface); + if (palette) { + return !SDL_SetPaletteColors(palette, colors, firstcolor, ncolors) ? 1 : 0; + } +#else if (surface->format->palette) { return !SDL_SetPaletteColors(surface->format->palette, colors, firstcolor, ncolors) ? 1 : 0; - } else { - return 0; } +#endif + return 0; } int SurfaceSdlGraphicsManager::SDL_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha) { @@ -2973,10 +3379,14 @@ int SurfaceSdlGraphicsManager::SDL_SetAlpha(SDL_Surface *surface, Uint32 flag, U } int SurfaceSdlGraphicsManager::SDL_SetColorKey(SDL_Surface *surface, Uint32 flag, Uint32 key) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + return SDL_SetSurfaceColorKey(surface, flag, key) ? -1 : 0; +#else return ::SDL_SetColorKey(surface, flag ? SDL_TRUE : SDL_FALSE, key) ? -1 : 0; +#endif } -#if defined(USE_IMGUI) && defined(USE_IMGUI_SDLRENDERER2) +#if defined(USE_IMGUI) && (defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3)) void *SurfaceSdlGraphicsManager::getImGuiTexture(const Graphics::Surface &image, const byte *palette, int palCount) { // Upload pixels into texture @@ -2989,7 +3399,11 @@ void *SurfaceSdlGraphicsManager::getImGuiTexture(const Graphics::Surface &image, Graphics::Surface *s = image.convertTo(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24), palette, palCount); SDL_UpdateTexture(texture, nullptr, s->getPixels(), s->pitch); SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); +#ifdef USE_IMGUI_SDLRENDERER3 + SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_LINEAR); +#elif defined(USE_IMGUI_SDLRENDERER2) SDL_SetTextureScaleMode(texture, SDL_ScaleModeLinear); +#endif s->free(); delete s; @@ -3000,7 +3414,7 @@ void *SurfaceSdlGraphicsManager::getImGuiTexture(const Graphics::Surface &image, void SurfaceSdlGraphicsManager::freeImGuiTexture(void *texture) { SDL_DestroyTexture((SDL_Texture *) texture); } -#endif // defined(USE_IMGUI) && defined(USE_IMGUI_SDLRENDERER2) +#endif // defined(USE_IMGUI) && (defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3)) #endif // SDL_VERSION_ATLEAST(2, 0, 0) diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.h b/backends/graphics/surfacesdl/surfacesdl-graphics.h index 87abc4f760309..ce716bd5e1920 100644 --- a/backends/graphics/surfacesdl/surfacesdl-graphics.h +++ b/backends/graphics/surfacesdl/surfacesdl-graphics.h @@ -95,7 +95,11 @@ class SurfaceSdlGraphicsManager : public SdlGraphicsManager { * @param in The SDL pixel format to convert * @param out A pixel format to be written to */ +#if SDL_VERSION_ATLEAST(3, 0, 0) + Graphics::PixelFormat convertSDLPixelFormat(SDL_PixelFormat in) const; +#else Graphics::PixelFormat convertSDLPixelFormat(SDL_PixelFormat *in) const; +#endif public: void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) override; Graphics::Surface *lockScreen() override; @@ -129,7 +133,7 @@ class SurfaceSdlGraphicsManager : public SdlGraphicsManager { void notifyVideoExpose() override; void notifyResize(const int width, const int height) override; -#if defined(USE_IMGUI) && defined(USE_IMGUI_SDLRENDERER2) +#if defined(USE_IMGUI) && (defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3)) void *getImGuiTexture(const Graphics::Surface &image, const byte *palette, int palCount) override; void freeImGuiTexture(void *texture) override; #endif diff --git a/backends/graphics3d/openglsdl/openglsdl-graphics3d.cpp b/backends/graphics3d/openglsdl/openglsdl-graphics3d.cpp index f530d599be1f4..9f811452ba76b 100644 --- a/backends/graphics3d/openglsdl/openglsdl-graphics3d.cpp +++ b/backends/graphics3d/openglsdl/openglsdl-graphics3d.cpp @@ -385,7 +385,10 @@ void OpenGLSdlGraphics3dManager::createOrUpdateScreen() { g_system->quit(); } -#if SDL_VERSION_ATLEAST(2, 0, 1) +#if SDL_VERSION_ATLEAST(3, 0, 0) + int obtainedWidth = 0, obtainedHeight = 0; + SDL_GetWindowSizeInPixels(_window->getSDLWindow(), &obtainedWidth, &obtainedHeight); +#elif SDL_VERSION_ATLEAST(2, 0, 1) int obtainedWidth = 0, obtainedHeight = 0; SDL_GL_GetDrawableSize(_window->getSDLWindow(), &obtainedWidth, &obtainedHeight); #else @@ -410,8 +413,13 @@ void OpenGLSdlGraphics3dManager::notifyResize(const int width, const int height) #if SDL_VERSION_ATLEAST(2, 0, 0) // Get the updated size directly from SDL, in case there are multiple // resize events in the message queue. +#if SDL_VERSION_ATLEAST(3, 0, 0) + int newWidth = 0, newHeight = 0; + SDL_GetWindowSizeInPixels(_window->getSDLWindow(), &newWidth, &newHeight); +#else int newWidth = 0, newHeight = 0; SDL_GL_GetDrawableSize(_window->getSDLWindow(), &newWidth, &newHeight); +#endif if (newWidth == _overlayScreen->getWidth() && newHeight == _overlayScreen->getHeight()) { return; // nothing to do @@ -457,7 +465,7 @@ void OpenGLSdlGraphics3dManager::initializeOpenGLContext() const { OpenGLContext.initialize(_glContextType); #if SDL_VERSION_ATLEAST(2, 0, 0) - if (SDL_GL_SetSwapInterval(_vsync ? 1 : 0)) { + if (!SDL_GL_SetSwapInterval(_vsync ? 1 : 0)) { warning("Unable to %s VSync: %s", _vsync ? "enable" : "disable", SDL_GetError()); } #endif @@ -787,7 +795,15 @@ int16 OpenGLSdlGraphics3dManager::getOverlayWidth() const { } bool OpenGLSdlGraphics3dManager::showMouse(bool visible) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if(visible) { + SDL_ShowCursor(); + } else { + SDL_HideCursor(); + } +#elif SDL_VERSION_ATLEAST(2, 0, 0) SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE); +#endif return true; } @@ -803,7 +819,11 @@ void OpenGLSdlGraphics3dManager::deinitializeRenderer() { destroyImGui(); #endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_GL_DestroyContext(_glContext); +#else SDL_GL_DeleteContext(_glContext); +#endif _glContext = nullptr; } #endif // SDL_VERSION_ATLEAST(2, 0, 0) diff --git a/backends/imgui/backends/imgui_impl_sdl3.cpp b/backends/imgui/backends/imgui_impl_sdl3.cpp new file mode 100644 index 0000000000000..44536554eae29 --- /dev/null +++ b/backends/imgui/backends/imgui_impl_sdl3.cpp @@ -0,0 +1,772 @@ +// dear imgui: Platform Backend for SDL3 (*EXPERIMENTAL*) +// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) +// (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) + +// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) + +// Implemented features: +// [X] Platform: Clipboard support. +// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen. +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5] +// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: IME support. + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2024-10-24: Emscripten: SDL_EVENT_MOUSE_WHEEL event doesn't require dividing by 100.0f on Emscripten. +// 2024-09-03: Update for SDL3 api changes: SDL_GetGamepads() memory ownership revert. (#7918, #7898, #7807) +// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: +// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn +// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn +// - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn +// 2024-08-19: Storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. +// 2024-08-19: ImGui_ImplSDL3_ProcessEvent() now ignores events intended for other SDL windows. (#7853) +// 2024-07-22: Update for SDL3 api changes: SDL_GetGamepads() memory ownership change. (#7807) +// 2024-07-18: Update for SDL3 api changes: SDL_GetClipboardText() memory ownership change. (#7801) +// 2024-07-15: Update for SDL3 api changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794) +// 2024-07-02: Update for SDL3 api changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762). +// 2024-07-01: Update for SDL3 api changes: SDL_SetTextInputRect() changed to SDL_SetTextInputArea(). +// 2024-06-26: Update for SDL3 api changes: SDL_StartTextInput()/SDL_StopTextInput()/SDL_SetTextInputRect() functions signatures. +// 2024-06-24: Update for SDL3 api changes: SDL_EVENT_KEY_DOWN/SDL_EVENT_KEY_UP contents. +// 2024-06-03; Update for SDL3 api changes: SDL_SYSTEM_CURSOR_ renames. +// 2024-05-15: Update for SDL3 api changes: SDLK_ renames. +// 2024-04-15: Inputs: Re-enable calling SDL_StartTextInput()/SDL_StopTextInput() as SDL3 no longer enables it by default and should play nicer with IME. +// 2024-02-13: Inputs: Fixed gamepad support. Handle gamepad disconnection. Added ImGui_ImplSDL3_SetGamepadMode(). +// 2023-11-13: Updated for recent SDL3 API changes. +// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys. +// 2023-05-04: Fixed build on Emscripten/iOS/Android. (#6391) +// 2023-04-06: Inputs: Avoid calling SDL_StartTextInput()/SDL_StopTextInput() as they don't only pertain to IME. It's unclear exactly what their relation is to IME. (#6306) +// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen. (#2702) +// 2023-02-23: Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. (#6189, #6114, #3644) +// 2023-02-07: Forked "imgui_impl_sdl2" into "imgui_impl_sdl3". Removed version checks for old feature. Refer to imgui_impl_sdl2.cpp for older changelog. + +#include "backends/imgui/imgui.h" +#ifndef IMGUI_DISABLE +#include "backends/platform/sdl/sdl.h" +#include "imgui_impl_sdl3.h" + +// Clang warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision +#endif + +// SDL +#include +#if defined(__APPLE__) +#include +#endif +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#endif + +#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) && !defined(__amigaos4__) +#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 1 +#else +#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 0 +#endif + +// FIXME-LEGACY: remove when SDL 3.1.3 preview is released. +#ifndef SDLK_APOSTROPHE +#define SDLK_APOSTROPHE SDLK_QUOTE +#endif +#ifndef SDLK_GRAVE +#define SDLK_GRAVE SDLK_BACKQUOTE +#endif + +// SDL Data +struct ImGui_ImplSDL3_Data +{ + SDL_Window* Window; + SDL_WindowID WindowID; + SDL_Renderer* Renderer; + Uint64 Time; + char* ClipboardTextData; + + // IME handling + SDL_Window* ImeWindow; + + // Mouse handling + Uint32 MouseWindowID; + int MouseButtonsDown; + SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT]; + SDL_Cursor* MouseLastCursor; + int MousePendingLeaveFrame; + bool MouseCanUseGlobalState; + + // Gamepad handling + ImVector Gamepads; + ImGui_ImplSDL3_GamepadMode GamepadMode; + bool WantUpdateGamepadsList; + + ImGui_ImplSDL3_Data() { memset((void*)this, 0, sizeof(*this)); } +}; + +// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts +// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. +// FIXME: multi-context support is not well tested and probably dysfunctional in this backend. +// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context. +static ImGui_ImplSDL3_Data* ImGui_ImplSDL3_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplSDL3_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr; +} + +// Functions +static const char* ImGui_ImplSDL3_GetClipboardText(ImGuiContext*) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + if (bd->ClipboardTextData) + SDL_free(bd->ClipboardTextData); + const char* sdl_clipboard_text = SDL_GetClipboardText(); + bd->ClipboardTextData = sdl_clipboard_text ? SDL_strdup(sdl_clipboard_text) : nullptr; + return bd->ClipboardTextData; +} + +static void ImGui_ImplSDL3_SetClipboardText(ImGuiContext*, const char* text) +{ + SDL_SetClipboardText(text); +} + +static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + SDL_WindowID window_id = (SDL_WindowID)(intptr_t)viewport->PlatformHandle; + SDL_Window* window = SDL_GetWindowFromID(window_id); + if ((data->WantVisible == false || bd->ImeWindow != window) && bd->ImeWindow != nullptr) + { + SDL_StopTextInput(bd->ImeWindow); + bd->ImeWindow = nullptr; + } + if (data->WantVisible) + { + SDL_Rect r; + r.x = (int)data->InputPos.x; + r.y = (int)data->InputPos.y; + r.w = 1; + r.h = (int)data->InputLineHeight; + SDL_SetTextInputArea(window, &r, 0); + SDL_StartTextInput(window); + bd->ImeWindow = window; + } +} + +// Not static to allow third-party code to use that if they want to (but undocumented) +ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode); +ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode) +{ + // Keypad doesn't have individual key values in SDL3 + switch (scancode) + { + case SDL_SCANCODE_KP_0: return ImGuiKey_Keypad0; + case SDL_SCANCODE_KP_1: return ImGuiKey_Keypad1; + case SDL_SCANCODE_KP_2: return ImGuiKey_Keypad2; + case SDL_SCANCODE_KP_3: return ImGuiKey_Keypad3; + case SDL_SCANCODE_KP_4: return ImGuiKey_Keypad4; + case SDL_SCANCODE_KP_5: return ImGuiKey_Keypad5; + case SDL_SCANCODE_KP_6: return ImGuiKey_Keypad6; + case SDL_SCANCODE_KP_7: return ImGuiKey_Keypad7; + case SDL_SCANCODE_KP_8: return ImGuiKey_Keypad8; + case SDL_SCANCODE_KP_9: return ImGuiKey_Keypad9; + case SDL_SCANCODE_KP_PERIOD: return ImGuiKey_KeypadDecimal; + case SDL_SCANCODE_KP_DIVIDE: return ImGuiKey_KeypadDivide; + case SDL_SCANCODE_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; + case SDL_SCANCODE_KP_MINUS: return ImGuiKey_KeypadSubtract; + case SDL_SCANCODE_KP_PLUS: return ImGuiKey_KeypadAdd; + case SDL_SCANCODE_KP_ENTER: return ImGuiKey_KeypadEnter; + case SDL_SCANCODE_KP_EQUALS: return ImGuiKey_KeypadEqual; + default: break; + } + switch (keycode) + { + case SDLK_TAB: return ImGuiKey_Tab; + case SDLK_LEFT: return ImGuiKey_LeftArrow; + case SDLK_RIGHT: return ImGuiKey_RightArrow; + case SDLK_UP: return ImGuiKey_UpArrow; + case SDLK_DOWN: return ImGuiKey_DownArrow; + case SDLK_PAGEUP: return ImGuiKey_PageUp; + case SDLK_PAGEDOWN: return ImGuiKey_PageDown; + case SDLK_HOME: return ImGuiKey_Home; + case SDLK_END: return ImGuiKey_End; + case SDLK_INSERT: return ImGuiKey_Insert; + case SDLK_DELETE: return ImGuiKey_Delete; + case SDLK_BACKSPACE: return ImGuiKey_Backspace; + case SDLK_SPACE: return ImGuiKey_Space; + case SDLK_RETURN: return ImGuiKey_Enter; + case SDLK_ESCAPE: return ImGuiKey_Escape; + case SDLK_APOSTROPHE: return ImGuiKey_Apostrophe; + case SDLK_COMMA: return ImGuiKey_Comma; + case SDLK_MINUS: return ImGuiKey_Minus; + case SDLK_PERIOD: return ImGuiKey_Period; + case SDLK_SLASH: return ImGuiKey_Slash; + case SDLK_SEMICOLON: return ImGuiKey_Semicolon; + case SDLK_EQUALS: return ImGuiKey_Equal; + case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket; + case SDLK_BACKSLASH: return ImGuiKey_Backslash; + case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket; + case SDLK_GRAVE: return ImGuiKey_GraveAccent; + case SDLK_CAPSLOCK: return ImGuiKey_CapsLock; + case SDLK_SCROLLLOCK: return ImGuiKey_ScrollLock; + case SDLK_NUMLOCKCLEAR: return ImGuiKey_NumLock; + case SDLK_PRINTSCREEN: return ImGuiKey_PrintScreen; + case SDLK_PAUSE: return ImGuiKey_Pause; + case SDLK_LCTRL: return ImGuiKey_LeftCtrl; + case SDLK_LSHIFT: return ImGuiKey_LeftShift; + case SDLK_LALT: return ImGuiKey_LeftAlt; + case SDLK_LGUI: return ImGuiKey_LeftSuper; + case SDLK_RCTRL: return ImGuiKey_RightCtrl; + case SDLK_RSHIFT: return ImGuiKey_RightShift; + case SDLK_RALT: return ImGuiKey_RightAlt; + case SDLK_RGUI: return ImGuiKey_RightSuper; + case SDLK_APPLICATION: return ImGuiKey_Menu; + case SDLK_0: return ImGuiKey_0; + case SDLK_1: return ImGuiKey_1; + case SDLK_2: return ImGuiKey_2; + case SDLK_3: return ImGuiKey_3; + case SDLK_4: return ImGuiKey_4; + case SDLK_5: return ImGuiKey_5; + case SDLK_6: return ImGuiKey_6; + case SDLK_7: return ImGuiKey_7; + case SDLK_8: return ImGuiKey_8; + case SDLK_9: return ImGuiKey_9; + case SDLK_A: return ImGuiKey_A; + case SDLK_B: return ImGuiKey_B; + case SDLK_C: return ImGuiKey_C; + case SDLK_D: return ImGuiKey_D; + case SDLK_E: return ImGuiKey_E; + case SDLK_F: return ImGuiKey_F; + case SDLK_G: return ImGuiKey_G; + case SDLK_H: return ImGuiKey_H; + case SDLK_I: return ImGuiKey_I; + case SDLK_J: return ImGuiKey_J; + case SDLK_K: return ImGuiKey_K; + case SDLK_L: return ImGuiKey_L; + case SDLK_M: return ImGuiKey_M; + case SDLK_N: return ImGuiKey_N; + case SDLK_O: return ImGuiKey_O; + case SDLK_P: return ImGuiKey_P; + case SDLK_Q: return ImGuiKey_Q; + case SDLK_R: return ImGuiKey_R; + case SDLK_S: return ImGuiKey_S; + case SDLK_T: return ImGuiKey_T; + case SDLK_U: return ImGuiKey_U; + case SDLK_V: return ImGuiKey_V; + case SDLK_W: return ImGuiKey_W; + case SDLK_X: return ImGuiKey_X; + case SDLK_Y: return ImGuiKey_Y; + case SDLK_Z: return ImGuiKey_Z; + case SDLK_F1: return ImGuiKey_F1; + case SDLK_F2: return ImGuiKey_F2; + case SDLK_F3: return ImGuiKey_F3; + case SDLK_F4: return ImGuiKey_F4; + case SDLK_F5: return ImGuiKey_F5; + case SDLK_F6: return ImGuiKey_F6; + case SDLK_F7: return ImGuiKey_F7; + case SDLK_F8: return ImGuiKey_F8; + case SDLK_F9: return ImGuiKey_F9; + case SDLK_F10: return ImGuiKey_F10; + case SDLK_F11: return ImGuiKey_F11; + case SDLK_F12: return ImGuiKey_F12; + case SDLK_F13: return ImGuiKey_F13; + case SDLK_F14: return ImGuiKey_F14; + case SDLK_F15: return ImGuiKey_F15; + case SDLK_F16: return ImGuiKey_F16; + case SDLK_F17: return ImGuiKey_F17; + case SDLK_F18: return ImGuiKey_F18; + case SDLK_F19: return ImGuiKey_F19; + case SDLK_F20: return ImGuiKey_F20; + case SDLK_F21: return ImGuiKey_F21; + case SDLK_F22: return ImGuiKey_F22; + case SDLK_F23: return ImGuiKey_F23; + case SDLK_F24: return ImGuiKey_F24; + case SDLK_AC_BACK: return ImGuiKey_AppBack; + case SDLK_AC_FORWARD: return ImGuiKey_AppForward; + default: break; + } + return ImGuiKey_None; +} + +static void ImGui_ImplSDL3_UpdateKeyModifiers(SDL_Keymod sdl_key_mods) +{ + ImGuiIO& io = ImGui::GetIO(); + io.AddKeyEvent(ImGuiMod_Ctrl, (sdl_key_mods & SDL_KMOD_CTRL) != 0); + io.AddKeyEvent(ImGuiMod_Shift, (sdl_key_mods & SDL_KMOD_SHIFT) != 0); + io.AddKeyEvent(ImGuiMod_Alt, (sdl_key_mods & SDL_KMOD_ALT) != 0); + io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & SDL_KMOD_GUI) != 0); +} + + +static ImGuiViewport* ImGui_ImplSDL3_GetViewportForWindowID(SDL_WindowID window_id) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : nullptr; +} + +// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. +// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. +// - 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. +// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field. +bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL3_Init()?"); + ImGuiIO& io = ImGui::GetIO(); + + switch (event->type) + { + case SDL_EVENT_MOUSE_MOTION: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->motion.windowID) == nullptr) + return false; + ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y); + io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); + io.AddMousePosEvent(mouse_pos.x, mouse_pos.y); + return true; + } + case SDL_EVENT_MOUSE_WHEEL: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->wheel.windowID) == nullptr) + return false; + //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY); + float wheel_x = -event->wheel.x; + float wheel_y = event->wheel.y; + io.AddMouseSourceEvent(event->wheel.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); + io.AddMouseWheelEvent(wheel_x, wheel_y); + return true; + } + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->button.windowID) == nullptr) + return false; + int mouse_button = -1; + if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; } + if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; } + if (event->button.button == SDL_BUTTON_MIDDLE) { mouse_button = 2; } + if (event->button.button == SDL_BUTTON_X1) { mouse_button = 3; } + if (event->button.button == SDL_BUTTON_X2) { mouse_button = 4; } + if (mouse_button == -1) + break; + io.AddMouseSourceEvent(event->button.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); + io.AddMouseButtonEvent(mouse_button, (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN)); + bd->MouseButtonsDown = (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) ? (bd->MouseButtonsDown | (1 << mouse_button)) : (bd->MouseButtonsDown & ~(1 << mouse_button)); + return true; + } + case SDL_EVENT_TEXT_INPUT: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->text.windowID) == nullptr) + return false; + io.AddInputCharactersUTF8(event->text.text); + return true; + } + case SDL_EVENT_KEY_DOWN: + case SDL_EVENT_KEY_UP: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->key.windowID) == nullptr) + return false; + //IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%d: key=%d, scancode=%d, mod=%X\n", (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP", event->key.key, event->key.scancode, event->key.mod); + ImGui_ImplSDL3_UpdateKeyModifiers((SDL_Keymod)event->key.mod); + ImGuiKey key = ImGui_ImplSDL3_KeyEventToImGuiKey(event->key.key, event->key.scancode); + io.AddKeyEvent(key, (event->type == SDL_EVENT_KEY_DOWN)); + io.SetKeyEventNativeData(key, event->key.key, event->key.scancode, event->key.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions. + return true; + } + case SDL_EVENT_WINDOW_MOUSE_ENTER: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr) + return false; + bd->MouseWindowID = event->window.windowID; + bd->MousePendingLeaveFrame = 0; + return true; + } + // - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late, + // causing SDL_WINDOWEVENT_LEAVE on previous frame to interrupt drag operation by clear mouse position. This is why + // we delay process the SDL_WINDOWEVENT_LEAVE events by one frame. See issue #5012 for details. + // FIXME: Unconfirmed whether this is still needed with SDL3. + case SDL_EVENT_WINDOW_MOUSE_LEAVE: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr) + return false; + bd->MousePendingLeaveFrame = ImGui::GetFrameCount() + 1; + return true; + } + case SDL_EVENT_WINDOW_FOCUS_GAINED: + case SDL_EVENT_WINDOW_FOCUS_LOST: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr) + return false; + io.AddFocusEvent(event->type == SDL_EVENT_WINDOW_FOCUS_GAINED); + return true; + } + case SDL_EVENT_GAMEPAD_ADDED: + case SDL_EVENT_GAMEPAD_REMOVED: + { + bd->WantUpdateGamepadsList = true; + return true; + } + } + return false; +} + +static void ImGui_ImplSDL3_SetupPlatformHandles(ImGuiViewport* viewport, SDL_Window* window) +{ + viewport->PlatformHandle = (void*)(intptr_t)SDL_GetWindowID(window); + viewport->PlatformHandleRaw = nullptr; +#if defined(_WIN32) && !defined(__WINRT__) + viewport->PlatformHandleRaw = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr); +#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA) + viewport->PlatformHandleRaw = SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr); +#endif +} + +static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void* sdl_gl_context) +{ + ImGuiIO& io = ImGui::GetIO(); + IMGUI_CHECKVERSION(); + IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); + IM_UNUSED(sdl_gl_context); // Unused in this branch + + // Check and store if we are on a SDL backend that supports global mouse position + // ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list) + bool mouse_can_use_global_state = false; +#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE + const char* sdl_backend = SDL_GetCurrentVideoDriver(); + const char* global_mouse_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" }; + for (int n = 0; n < IM_ARRAYSIZE(global_mouse_whitelist); n++) + if (strncmp(sdl_backend, global_mouse_whitelist[n], strlen(global_mouse_whitelist[n])) == 0) + mouse_can_use_global_state = true; +#endif + + // Setup backend capabilities flags + ImGui_ImplSDL3_Data* bd = IM_NEW(ImGui_ImplSDL3_Data)(); + io.BackendPlatformUserData = (void*)bd; + io.BackendPlatformName = "imgui_impl_sdl3"; + io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) + io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + + bd->Window = window; + bd->WindowID = SDL_GetWindowID(window); + bd->Renderer = renderer; + bd->MouseCanUseGlobalState = mouse_can_use_global_state; + + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL3_SetClipboardText; + platform_io.Platform_GetClipboardTextFn = ImGui_ImplSDL3_GetClipboardText; + platform_io.Platform_SetImeDataFn = ImGui_ImplSDL3_PlatformSetImeData; + + // Gamepad handling + bd->GamepadMode = ImGui_ImplSDL3_GamepadMode_AutoFirst; + bd->WantUpdateGamepadsList = true; + + // Load mouse cursors + bd->MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT); + bd->MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_TEXT); + bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_MOVE); + bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NS_RESIZE); + bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_EW_RESIZE); + bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NESW_RESIZE); + bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NWSE_RESIZE); + bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER); + bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NOT_ALLOWED); + + // Set platform dependent data in viewport + // Our mouse update function expect PlatformHandle to be filled for the main viewport + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGui_ImplSDL3_SetupPlatformHandles(main_viewport, window); + + // From 2.0.5: Set SDL hint to receive mouse click events on window focus, otherwise SDL doesn't emit the event. + // Without this, when clicking to gain focus, our widgets wouldn't activate even though they showed as hovered. + // (This is unfortunately a global SDL setting, so enabling it might have a side-effect on your application. + // It is unlikely to make a difference, but if your app absolutely needs to ignore the initial on-focus click: + // you can ignore SDL_EVENT_MOUSE_BUTTON_DOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED) +#ifdef SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH + SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); +#endif + + // From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710) +#ifdef SDL_HINT_MOUSE_AUTO_CAPTURE + SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0"); +#endif + + return true; +} + +bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context) +{ + IM_UNUSED(sdl_gl_context); // Viewport branch will need this. + return ImGui_ImplSDL3_Init(window, nullptr, sdl_gl_context); +} + +bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window) +{ + return ImGui_ImplSDL3_Init(window, nullptr, nullptr); +} + +bool ImGui_ImplSDL3_InitForD3D(SDL_Window* window) +{ +#if !defined(_WIN32) + IM_ASSERT(0 && "Unsupported"); +#endif + return ImGui_ImplSDL3_Init(window, nullptr, nullptr); +} + +bool ImGui_ImplSDL3_InitForMetal(SDL_Window* window) +{ + return ImGui_ImplSDL3_Init(window, nullptr, nullptr); +} + +bool ImGui_ImplSDL3_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer) +{ + return ImGui_ImplSDL3_Init(window, renderer, nullptr); +} + +bool ImGui_ImplSDL3_InitForOther(SDL_Window* window) +{ + return ImGui_ImplSDL3_Init(window, nullptr, nullptr); +} + +static void ImGui_ImplSDL3_CloseGamepads(); + +void ImGui_ImplSDL3_Shutdown() +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + + if (bd->ClipboardTextData) + SDL_free(bd->ClipboardTextData); + for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) + SDL_DestroyCursor(bd->MouseCursors[cursor_n]); + ImGui_ImplSDL3_CloseGamepads(); + + io.BackendPlatformName = nullptr; + io.BackendPlatformUserData = nullptr; + io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); + IM_DELETE(bd); +} + +static void ImGui_ImplSDL3_UpdateMouseData() +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + ImGuiIO& io = ImGui::GetIO(); + + // We forward mouse input when hovered or captured (via SDL_EVENT_MOUSE_MOTION) or when focused (below) +#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE + // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside + SDL_CaptureMouse(bd->MouseButtonsDown != 0); + SDL_Window* focused_window = SDL_GetKeyboardFocus(); + const bool is_app_focused = (bd->Window == focused_window); +#else + SDL_Window* focused_window = bd->Window; + const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only +#endif + if (is_app_focused) + { + // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user) + if (io.WantSetMousePos) + SDL_WarpMouseInWindow(bd->Window, io.MousePos.x, io.MousePos.y); + + // (Optional) Fallback to provide mouse position when focused (SDL_EVENT_MOUSE_MOTION already provides this when hovered or captured) + if (bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0) + { + // Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) + float mouse_x_global, mouse_y_global; + int window_x, window_y; + SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global); + SDL_GetWindowPosition(focused_window, &window_x, &window_y); + io.AddMousePosEvent(mouse_x_global - window_x, mouse_y_global - window_y); + } + } +} + +static void ImGui_ImplSDL3_UpdateMouseCursor() +{ + ImGuiIO& io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) + return; + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + + ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); + if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) + { + // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor + SDL_HideCursor(); + } + else + { + // Show OS mouse cursor + SDL_Cursor* expected_cursor = bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]; + if (bd->MouseLastCursor != expected_cursor) + { + SDL_SetCursor(expected_cursor); // SDL function doesn't have an early out (see #6113) + bd->MouseLastCursor = expected_cursor; + } + SDL_ShowCursor(); + } +} + +static void ImGui_ImplSDL3_CloseGamepads() +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + if (bd->GamepadMode != ImGui_ImplSDL3_GamepadMode_Manual) + for (SDL_Gamepad* gamepad : bd->Gamepads) + SDL_CloseGamepad(gamepad); + bd->Gamepads.resize(0); +} + +void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array, int manual_gamepads_count) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + ImGui_ImplSDL3_CloseGamepads(); + if (mode == ImGui_ImplSDL3_GamepadMode_Manual) + { + IM_ASSERT(manual_gamepads_array != nullptr && manual_gamepads_count > 0); + for (int n = 0; n < manual_gamepads_count; n++) + bd->Gamepads.push_back(manual_gamepads_array[n]); + } + else + { + IM_ASSERT(manual_gamepads_array == nullptr && manual_gamepads_count <= 0); + bd->WantUpdateGamepadsList = true; + } + bd->GamepadMode = mode; +} + +static void ImGui_ImplSDL3_UpdateGamepadButton(ImGui_ImplSDL3_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GamepadButton button_no) +{ + bool merged_value = false; + for (SDL_Gamepad* gamepad : bd->Gamepads) + merged_value |= SDL_GetGamepadButton(gamepad, button_no) != 0; + io.AddKeyEvent(key, merged_value); +} + +static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; } +static void ImGui_ImplSDL3_UpdateGamepadAnalog(ImGui_ImplSDL3_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GamepadAxis axis_no, float v0, float v1) +{ + float merged_value = 0.0f; + for (SDL_Gamepad* gamepad : bd->Gamepads) + { + float vn = Saturate((float)(SDL_GetGamepadAxis(gamepad, axis_no) - v0) / (float)(v1 - v0)); + if (merged_value < vn) + merged_value = vn; + } + io.AddKeyAnalogEvent(key, merged_value > 0.1f, merged_value); +} + +static void ImGui_ImplSDL3_UpdateGamepads() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + + // Update list of gamepads to use + if (bd->WantUpdateGamepadsList && bd->GamepadMode != ImGui_ImplSDL3_GamepadMode_Manual) + { + ImGui_ImplSDL3_CloseGamepads(); + int sdl_gamepads_count = 0; + SDL_JoystickID* sdl_gamepads = SDL_GetGamepads(&sdl_gamepads_count); + for (int n = 0; n < sdl_gamepads_count; n++) + if (SDL_Gamepad* gamepad = SDL_OpenGamepad(sdl_gamepads[n])) + { + bd->Gamepads.push_back(gamepad); + if (bd->GamepadMode == ImGui_ImplSDL3_GamepadMode_AutoFirst) + break; + } + bd->WantUpdateGamepadsList = false; + SDL_free(sdl_gamepads); + } + + // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. + if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) + return; + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; + if (bd->Gamepads.Size == 0) + return; + io.BackendFlags |= ImGuiBackendFlags_HasGamepad; + + // Update gamepad inputs + const int thumb_dead_zone = 8000; // SDL_gamepad.h suggests using this value. + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadStart, SDL_GAMEPAD_BUTTON_START); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadBack, SDL_GAMEPAD_BUTTON_BACK); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceLeft, SDL_GAMEPAD_BUTTON_WEST); // Xbox X, PS Square + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceRight, SDL_GAMEPAD_BUTTON_EAST); // Xbox B, PS Circle + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceUp, SDL_GAMEPAD_BUTTON_NORTH); // Xbox Y, PS Triangle + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceDown, SDL_GAMEPAD_BUTTON_SOUTH); // Xbox A, PS Cross + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadLeft, SDL_GAMEPAD_BUTTON_DPAD_LEFT); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadRight, SDL_GAMEPAD_BUTTON_DPAD_RIGHT); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadUp, SDL_GAMEPAD_BUTTON_DPAD_UP); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadDown, SDL_GAMEPAD_BUTTON_DPAD_DOWN); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL1, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR1, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadL2, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, 0.0f, 32767); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadR2, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, 0.0f, 32767); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL3, SDL_GAMEPAD_BUTTON_LEFT_STICK); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR3, SDL_GAMEPAD_BUTTON_RIGHT_STICK); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickLeft, SDL_GAMEPAD_AXIS_LEFTX, -thumb_dead_zone, -32768); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickRight, SDL_GAMEPAD_AXIS_LEFTX, +thumb_dead_zone, +32767); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickUp, SDL_GAMEPAD_AXIS_LEFTY, -thumb_dead_zone, -32768); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickDown, SDL_GAMEPAD_AXIS_LEFTY, +thumb_dead_zone, +32767); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickLeft, SDL_GAMEPAD_AXIS_RIGHTX, -thumb_dead_zone, -32768); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickRight, SDL_GAMEPAD_AXIS_RIGHTX, +thumb_dead_zone, +32767); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickUp, SDL_GAMEPAD_AXIS_RIGHTY, -thumb_dead_zone, -32768); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickDown, SDL_GAMEPAD_AXIS_RIGHTY, +thumb_dead_zone, +32767); +} + +void ImGui_ImplSDL3_NewFrame() +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL3_Init()?"); + ImGuiIO& io = ImGui::GetIO(); + + // Setup display size (every frame to accommodate for window resizing) + int w, h; + int display_w, display_h; + SDL_GetWindowSize(bd->Window, &w, &h); + if (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_MINIMIZED) + w = h = 0; + SDL_GetWindowSizeInPixels(bd->Window, &display_w, &display_h); + io.DisplaySize = ImVec2((float)w, (float)h); + if (w > 0 && h > 0) + io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h); + + // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution) + // (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. Happens in VMs and Emscripten, see #6189, #6114, #3644) + static Uint64 frequency = SDL_GetPerformanceFrequency(); + Uint64 current_time = SDL_GetPerformanceCounter(); + if (current_time <= bd->Time) + current_time = bd->Time + 1; + io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f); + bd->Time = current_time; + + if (bd->MousePendingLeaveFrame && bd->MousePendingLeaveFrame >= ImGui::GetFrameCount() && bd->MouseButtonsDown == 0) + { + bd->MouseWindowID = 0; + bd->MousePendingLeaveFrame = 0; + io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); + } + + ImGui_ImplSDL3_UpdateMouseData(); + ImGui_ImplSDL3_UpdateMouseCursor(); + + // Update game controllers (if enabled and available) + ImGui_ImplSDL3_UpdateGamepads(); +} + +//----------------------------------------------------------------------------- + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui/backends/imgui_impl_sdl3.h b/backends/imgui/backends/imgui_impl_sdl3.h new file mode 100644 index 0000000000000..6483fb5d06bd1 --- /dev/null +++ b/backends/imgui/backends/imgui_impl_sdl3.h @@ -0,0 +1,48 @@ +// dear imgui: Platform Backend for SDL3 (*EXPERIMENTAL*) +// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) +// (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) + +// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) + +// Implemented features: +// [X] Platform: Clipboard support. +// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen. +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5] +// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: IME support. + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +#pragma once +#include "backends/imgui/imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE + +struct SDL_Window; +struct SDL_Renderer; +struct SDL_Gamepad; +typedef union SDL_Event SDL_Event; + +// Follow "Getting Started" link and check examples/ folder to learn about using backends! +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context); +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window); +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForD3D(SDL_Window* window); +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForMetal(SDL_Window* window); +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer); +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForOther(SDL_Window* window); +IMGUI_IMPL_API void ImGui_ImplSDL3_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplSDL3_NewFrame(); +IMGUI_IMPL_API bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event); + +// Gamepad selection automatically starts in AutoFirst mode, picking first available SDL_Gamepad. You may override this. +// When using manual mode, caller is responsible for opening/closing gamepad. +enum ImGui_ImplSDL3_GamepadMode { ImGui_ImplSDL3_GamepadMode_AutoFirst, ImGui_ImplSDL3_GamepadMode_AutoAll, ImGui_ImplSDL3_GamepadMode_Manual }; +IMGUI_IMPL_API void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array = nullptr, int manual_gamepads_count = -1); + +#endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui/backends/imgui_impl_sdlrenderer3.cpp new file mode 100644 index 0000000000000..153d4b767c5e2 --- /dev/null +++ b/backends/imgui/backends/imgui_impl_sdlrenderer3.cpp @@ -0,0 +1,283 @@ +// dear imgui: Renderer Backend for SDL_Renderer for SDL3 +// (Requires: SDL 3.0.0+) + +// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) + +// Note how SDL_Renderer is an _optional_ component of SDL3. +// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. +// If your application will want to render any non trivial amount of graphics other than UI, +// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and +// it might be difficult to step out of those boundaries. + +// Implemented features: +// [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. + +// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +// CHANGELOG +// 2024-07-01: Update for SDL3 api changes: SDL_RenderGeometryRaw() uint32 version was removed (SDL#9009). +// 2024-05-14: *BREAKING CHANGE* ImGui_ImplSDLRenderer3_RenderDrawData() requires SDL_Renderer* passed as parameter. +// 2024-02-12: Amend to query SDL_RenderViewportSet() and restore viewport accordingly. +// 2023-05-30: Initial version. + +#include "backends/imgui/imgui.h" +#ifndef IMGUI_DISABLE +#include "imgui_impl_sdlrenderer3.h" +#include // intptr_t + +// Clang warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#endif + +// SDL +#include +#if !SDL_VERSION_ATLEAST(3,0,0) +#error This backend requires SDL 3.0.0+ +#endif + +// SDL_Renderer data +struct ImGui_ImplSDLRenderer3_Data +{ + SDL_Renderer* Renderer; // Main viewport's renderer + SDL_Texture* FontTexture; + ImVector ColorBuffer; + + ImGui_ImplSDLRenderer3_Data() { memset((void*)this, 0, sizeof(*this)); } +}; + +// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts +// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. +static ImGui_ImplSDLRenderer3_Data* ImGui_ImplSDLRenderer3_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplSDLRenderer3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; +} + +// Functions +bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer) +{ + ImGuiIO& io = ImGui::GetIO(); + IMGUI_CHECKVERSION(); + IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); + IM_ASSERT(renderer != nullptr && "SDL_Renderer not initialized!"); + + // Setup backend capabilities flags + ImGui_ImplSDLRenderer3_Data* bd = IM_NEW(ImGui_ImplSDLRenderer3_Data)(); + io.BackendRendererUserData = (void*)bd; + io.BackendRendererName = "imgui_impl_sdlrenderer3"; + io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + + bd->Renderer = renderer; + + return true; +} + +void ImGui_ImplSDLRenderer3_Shutdown() +{ + ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); + IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + + ImGui_ImplSDLRenderer3_DestroyDeviceObjects(); + + io.BackendRendererName = nullptr; + io.BackendRendererUserData = nullptr; + io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + IM_DELETE(bd); +} + +static void ImGui_ImplSDLRenderer3_SetupRenderState(SDL_Renderer* renderer) +{ + // Clear out any viewports and cliprect set by the user + // FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process. + SDL_SetRenderViewport(renderer, nullptr); + SDL_SetRenderClipRect(renderer, nullptr); +} + +void ImGui_ImplSDLRenderer3_NewFrame() +{ + ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDLRenderer3_Init()?"); + + if (!bd->FontTexture) + ImGui_ImplSDLRenderer3_CreateDeviceObjects(); +} + +// https://github.com/libsdl-org/SDL/issues/9009 +static int SDL_RenderGeometryRaw8BitColor(SDL_Renderer* renderer, ImVector& colors_out, SDL_Texture* texture, const float* xy, int xy_stride, const SDL_Color* color, int color_stride, const float* uv, int uv_stride, int num_vertices, const void* indices, int num_indices, int size_indices) +{ + const Uint8* color2 = (const Uint8*)color; + colors_out.resize(num_vertices); + SDL_FColor* color3 = colors_out.Data; + for (int i = 0; i < num_vertices; i++) + { + color3[i].r = color->r / 255.0f; + color3[i].g = color->g / 255.0f; + color3[i].b = color->b / 255.0f; + color3[i].a = color->a / 255.0f; + color2 += color_stride; + color = (const SDL_Color*)color2; + } + return SDL_RenderGeometryRaw(renderer, texture, xy, xy_stride, color3, sizeof(*color3), uv, uv_stride, num_vertices, indices, num_indices, size_indices); +} + +void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* renderer) +{ + ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); + + // If there's a scale factor set by the user, use that instead + // If the user has specified a scale factor to SDL_Renderer already via SDL_RenderSetScale(), SDL will scale whatever we pass + // to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here. + float rsx = 1.0f; + float rsy = 1.0f; + SDL_GetRenderScale(renderer, &rsx, &rsy); + ImVec2 render_scale; + render_scale.x = (rsx == 1.0f) ? draw_data->FramebufferScale.x : 1.0f; + render_scale.y = (rsy == 1.0f) ? draw_data->FramebufferScale.y : 1.0f; + + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + int fb_width = (int)(draw_data->DisplaySize.x * render_scale.x); + int fb_height = (int)(draw_data->DisplaySize.y * render_scale.y); + if (fb_width == 0 || fb_height == 0) + return; + + // Backup SDL_Renderer state that will be modified to restore it afterwards + struct BackupSDLRendererState + { + SDL_Rect Viewport; + bool ViewportEnabled; + bool ClipEnabled; + SDL_Rect ClipRect; + }; + BackupSDLRendererState old = {}; + old.ViewportEnabled = SDL_RenderViewportSet(renderer); + old.ClipEnabled = SDL_RenderClipEnabled(renderer); + SDL_GetRenderViewport(renderer, &old.Viewport); + SDL_GetRenderClipRect(renderer, &old.ClipRect); + + // Will project scissor/clipping rectangles into framebuffer space + ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports + ImVec2 clip_scale = render_scale; + + // Render command lists + ImGui_ImplSDLRenderer3_SetupRenderState(renderer); + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; + const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; + + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback) + { + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + ImGui_ImplSDLRenderer3_SetupRenderState(renderer); + else + pcmd->UserCallback(cmd_list, pcmd); + } + else + { + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); + ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); + if (clip_min.x < 0.0f) { clip_min.x = 0.0f; } + if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } + if (clip_max.x > (float)fb_width) { clip_max.x = (float)fb_width; } + if (clip_max.y > (float)fb_height) { clip_max.y = (float)fb_height; } + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) + continue; + + SDL_Rect r = { (int)(clip_min.x), (int)(clip_min.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y) }; + SDL_SetRenderClipRect(renderer, &r); + + const float* xy = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, pos)); + const float* uv = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, uv)); + const SDL_Color* color = (const SDL_Color*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, col)); // SDL 2.0.19+ + + // Bind texture, Draw + SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID(); + SDL_RenderGeometryRaw8BitColor(renderer, bd->ColorBuffer, tex, + xy, (int)sizeof(ImDrawVert), + color, (int)sizeof(ImDrawVert), + uv, (int)sizeof(ImDrawVert), + cmd_list->VtxBuffer.Size - pcmd->VtxOffset, + idx_buffer + pcmd->IdxOffset, pcmd->ElemCount, sizeof(ImDrawIdx)); + } + } + } + + // Restore modified SDL_Renderer state + SDL_SetRenderViewport(renderer, old.ViewportEnabled ? &old.Viewport : nullptr); + SDL_SetRenderClipRect(renderer, old.ClipEnabled ? &old.ClipRect : nullptr); +} + +// Called by Init/NewFrame/Shutdown +bool ImGui_ImplSDLRenderer3_CreateFontsTexture() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); + + // Build texture atlas + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. + + // Upload texture to graphics system + // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + bd->FontTexture = SDL_CreateTexture(bd->Renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, height); + if (bd->FontTexture == nullptr) + { + SDL_Log("error creating texture"); + return false; + } + SDL_UpdateTexture(bd->FontTexture, nullptr, pixels, 4 * width); + SDL_SetTextureBlendMode(bd->FontTexture, SDL_BLENDMODE_BLEND); + SDL_SetTextureScaleMode(bd->FontTexture, SDL_SCALEMODE_LINEAR); + + // Store our identifier + io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture); + + return true; +} + +void ImGui_ImplSDLRenderer3_DestroyFontsTexture() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); + if (bd->FontTexture) + { + io.Fonts->SetTexID(0); + SDL_DestroyTexture(bd->FontTexture); + bd->FontTexture = nullptr; + } +} + +bool ImGui_ImplSDLRenderer3_CreateDeviceObjects() +{ + return ImGui_ImplSDLRenderer3_CreateFontsTexture(); +} + +void ImGui_ImplSDLRenderer3_DestroyDeviceObjects() +{ + ImGui_ImplSDLRenderer3_DestroyFontsTexture(); +} + +//----------------------------------------------------------------------------- + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui/backends/imgui_impl_sdlrenderer3.h b/backends/imgui/backends/imgui_impl_sdlrenderer3.h new file mode 100644 index 0000000000000..6529469c02574 --- /dev/null +++ b/backends/imgui/backends/imgui_impl_sdlrenderer3.h @@ -0,0 +1,42 @@ +// dear imgui: Renderer Backend for SDL_Renderer for SDL3 +// (Requires: SDL 3.0.0+) + +// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) + +// Note how SDL_Renderer is an _optional_ component of SDL3. +// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. +// If your application will want to render any non trivial amount of graphics other than UI, +// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and +// it might be difficult to step out of those boundaries. + +// Implemented features: +// [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. + +// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +#pragma once +#include "backends/imgui/imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE + +struct SDL_Renderer; + +// Follow "Getting Started" link and check examples/ folder to learn about using backends! +IMGUI_IMPL_API bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer); +IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* renderer); + +// Called by Init/NewFrame/Shutdown +IMGUI_IMPL_API bool ImGui_ImplSDLRenderer3_CreateFontsTexture(); +IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_DestroyFontsTexture(); +IMGUI_IMPL_API bool ImGui_ImplSDLRenderer3_CreateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_DestroyDeviceObjects(); + +#endif // #ifndef IMGUI_DISABLE diff --git a/backends/mixer/sdl/sdl-mixer.cpp b/backends/mixer/sdl/sdl-mixer.cpp index 022d50e05988e..cff19f84edeb3 100644 --- a/backends/mixer/sdl/sdl-mixer.cpp +++ b/backends/mixer/sdl/sdl-mixer.cpp @@ -44,8 +44,13 @@ SdlMixerManager::~SdlMixerManager() { if (_mixer) _mixer->setReady(false); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_CloseAudioDevice(SDL_GetAudioStreamDevice(_stream)); + SDL_DestroyAudioStream(_stream); +#else if (_isAudioOpen) SDL_CloseAudio(); +#endif if (_isSubsystemInitialized) SDL_QuitSubSystem(SDL_INIT_AUDIO); @@ -63,7 +68,11 @@ void SdlMixerManager::init() { SDL_AudioSpec desired = getAudioSpec(SAMPLES_PER_SEC); // Start SDL Audio subsystem +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) { +#else if (SDL_InitSubSystem(SDL_INIT_AUDIO) == -1) { +#endif warning("Could not initialize SDL audio subsystem: %s", SDL_GetError()); return; } @@ -80,6 +89,10 @@ void SdlMixerManager::init() { #endif debug(1, "Using SDL Audio Driver \"%s\"", sdlDriverName); +#if SDL_VERSION_ATLEAST(3, 0, 0) + _isAudioOpen = true; + _obtained = desired; +#else // Needed as SDL_OpenAudio as of SDL-1.2.14 mutates fields in // "desired" if used directly. SDL_AudioSpec fmt = desired; @@ -106,11 +119,13 @@ void SdlMixerManager::init() { _obtained = desired; } +#endif debug(1, "Output sample rate: %d Hz", _obtained.freq); if (_obtained.freq != desired.freq) warning("SDL mixer output sample rate: %d differs from desired: %d", _obtained.freq, desired.freq); +#if !SDL_VERSION_ATLEAST(3, 0, 0) debug(1, "Output buffer size: %d samples", _obtained.samples); if (_obtained.samples != desired.samples) warning("SDL mixer output buffer size: %d differs from desired: %d", _obtained.samples, desired.samples); @@ -118,14 +133,23 @@ void SdlMixerManager::init() { debug(1, "Output channels: %d", _obtained.channels); if (_obtained.channels != 1 && _obtained.channels != 2) error("SDL mixer output requires mono or stereo output device"); +#endif - _mixer = new Audio::MixerImpl(_obtained.freq, _obtained.channels >= 2, desired.samples); + int desiredSamples = 0; +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_GetAudioDeviceFormat(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &_obtained, &desiredSamples); +#else + desiredSamples = desired.samples; +#endif + + _mixer = new Audio::MixerImpl(_obtained.freq, _obtained.channels >= 2, desiredSamples); assert(_mixer); _mixer->setReady(true); startAudio(); } +#if !SDL_VERSION_ATLEAST(3, 0, 0) static uint32 roundDownPowerOfTwo(uint32 samples) { // Public domain code from Sean Eron Anderson // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 @@ -143,6 +167,7 @@ static uint32 roundDownPowerOfTwo(uint32 samples) { return rounded; } +#endif SDL_AudioSpec SdlMixerManager::getAudioSpec(uint32 outputRate) { SDL_AudioSpec desired; @@ -184,18 +209,28 @@ SDL_AudioSpec SdlMixerManager::getAudioSpec(uint32 outputRate) { memset(&desired, 0, sizeof(desired)); desired.freq = freq; - desired.format = AUDIO_S16SYS; desired.channels = channels; +#if SDL_VERSION_ATLEAST(3, 0, 0) + desired.format = SDL_AUDIO_S16; +#else + desired.format = AUDIO_S16SYS; desired.samples = roundDownPowerOfTwo(samples); desired.callback = sdlCallback; desired.userdata = this; +#endif return desired; } void SdlMixerManager::startAudio() { // Start the sound system +#if SDL_VERSION_ATLEAST(3, 0, 0) + _stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &_obtained, sdl3Callback, this); + if (_stream) + SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(_stream)); +#else SDL_PauseAudio(0); +#endif } void SdlMixerManager::callbackHandler(byte *samples, int len) { @@ -210,18 +245,46 @@ void SdlMixerManager::sdlCallback(void *this_, byte *samples, int len) { manager->callbackHandler(samples, len); } +#if SDL_VERSION_ATLEAST(3, 0, 0) +void SdlMixerManager::sdl3Callback(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount) +{ + if (additional_amount > 0) { + Uint8 *data = SDL_stack_alloc(Uint8, additional_amount); + if (data) { + SdlMixerManager *manager = (SdlMixerManager *)userdata; + manager->sdlCallback(userdata, data, additional_amount); + SDL_PutAudioStreamData(stream, data, additional_amount); + SDL_stack_free(data); + } + } +} +#endif + void SdlMixerManager::suspendAudio() { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_CloseAudioDevice(SDL_GetAudioStreamDevice(_stream)); + SDL_DestroyAudioStream(_stream); +#else SDL_CloseAudio(); +#endif _audioSuspended = true; } int SdlMixerManager::resumeAudio() { if (!_audioSuspended) return -2; +#if SDL_VERSION_ATLEAST(3, 0, 0) + _stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &_obtained, sdl3Callback, this); + if(!_stream) + return -1; + if (SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(_stream))) + return -1; +#else if (SDL_OpenAudio(&_obtained, nullptr) < 0) { return -1; } SDL_PauseAudio(0); +#endif _audioSuspended = false; return 0; } diff --git a/backends/mixer/sdl/sdl-mixer.h b/backends/mixer/sdl/sdl-mixer.h index 2c8ea2232b8ef..3dd0670396fa9 100644 --- a/backends/mixer/sdl/sdl-mixer.h +++ b/backends/mixer/sdl/sdl-mixer.h @@ -80,9 +80,16 @@ class SdlMixerManager : public MixerManager { * by subclasses, so it invokes the non-static function callbackHandler() */ static void sdlCallback(void *this_, byte *samples, int len); +#if SDL_VERSION_ATLEAST(3, 0, 0) + static void sdl3Callback(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount); +#endif bool _isSubsystemInitialized; bool _isAudioOpen; + +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_AudioStream *_stream = nullptr; +#endif }; #endif diff --git a/backends/module.mk b/backends/module.mk index d1acc3d8d547c..5d4d8f130bd4d 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -510,5 +510,20 @@ MODULE_OBJS += \ endif endif +ifdef USE_SDL3 +ifdef USE_IMGUI +ifdef USE_OPENGL +MODULE_OBJS += \ + imgui/backends/imgui_impl_opengl3.o +endif +ifdef USE_IMGUI_SDLRENDERER3 +MODULE_OBJS += \ + imgui/backends/imgui_impl_sdlrenderer3.o +endif +MODULE_OBJS += \ + imgui/backends/imgui_impl_sdl3.o +endif +endif + # Include common rules include $(srcdir)/rules.mk diff --git a/backends/mutex/sdl/sdl-mutex.cpp b/backends/mutex/sdl/sdl-mutex.cpp index 3ada8e5ecd07e..11e951b4845ad 100644 --- a/backends/mutex/sdl/sdl-mutex.cpp +++ b/backends/mutex/sdl/sdl-mutex.cpp @@ -34,11 +34,29 @@ class SdlMutexInternal final : public Common::MutexInternal { SdlMutexInternal() { _mutex = SDL_CreateMutex(); } ~SdlMutexInternal() override { SDL_DestroyMutex(_mutex); } - bool lock() override { return (SDL_mutexP(_mutex) == 0); } - bool unlock() override { return (SDL_mutexV(_mutex) == 0); } + bool lock() override { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_LockMutex(_mutex); + return true; +#else + return (SDL_mutexP(_mutex) == 0); +#endif + } + bool unlock() override { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_UnlockMutex(_mutex); + return true; +#else + return (SDL_mutexV(_mutex) == 0); +#endif + } private: +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Mutex *_mutex; +#else SDL_mutex *_mutex; +#endif }; Common::MutexInternal *createSdlMutexInternal() { diff --git a/backends/platform/sdl/macosx/macosx-window.mm b/backends/platform/sdl/macosx/macosx-window.mm index d8481eaee465a..59e194d529f0f 100644 --- a/backends/platform/sdl/macosx/macosx-window.mm +++ b/backends/platform/sdl/macosx/macosx-window.mm @@ -27,7 +27,7 @@ #include float SdlWindow_MacOSX::getDpiScalingFactor() const { -#if SDL_VERSION_ATLEAST(2, 0, 0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 +#if !SDL_VERSION_ATLEAST(3, 0, 0) && SDL_VERSION_ATLEAST(2, 0, 0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 SDL_SysWMinfo wmInfo; if (getSDLWMInformation(&wmInfo)) { NSWindow *nswindow = wmInfo.info.cocoa.window; diff --git a/backends/platform/sdl/sdl-sys.h b/backends/platform/sdl/sdl-sys.h index 87bd6cabfab38..1eb5129887f03 100644 --- a/backends/platform/sdl/sdl-sys.h +++ b/backends/platform/sdl/sdl-sys.h @@ -187,12 +187,18 @@ #endif +#ifdef USE_SDL3 +#include +#else #include +#endif // Ignore warnings from system headers pulled by SDL #pragma warning(push) #pragma warning(disable:4121) // alignment of a member was sensitive to packing +#if !SDL_VERSION_ATLEAST(3, 0, 0) #include +#endif #pragma warning(pop) // Restore the forbidden exceptions from the hack above diff --git a/backends/platform/sdl/sdl-window.cpp b/backends/platform/sdl/sdl-window.cpp index 508ffe8a6c823..0afe7a407b868 100644 --- a/backends/platform/sdl/sdl-window.cpp +++ b/backends/platform/sdl/sdl-window.cpp @@ -29,7 +29,9 @@ #include "icons/scummvm.xpm" -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) +static const uint32 fullscreenMask = SDL_WINDOW_FULLSCREEN; +#elif SDL_VERSION_ATLEAST(2, 0, 0) static const uint32 fullscreenMask = SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN; #endif @@ -122,7 +124,11 @@ void SdlWindow::setupIcon() { } } +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Surface *sdl_surf = SDL_CreateSurfaceFrom(w, h, SDL_GetPixelFormatForMasks(32, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000), icon, w * 4); +#else SDL_Surface *sdl_surf = SDL_CreateRGBSurfaceFrom(icon, w, h, 32, w * 4, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000); +#endif if (!sdl_surf) { warning("SDL_CreateRGBSurfaceFrom(icon) failed"); } @@ -135,7 +141,11 @@ void SdlWindow::setupIcon() { SDL_WM_SetIcon(sdl_surf, NULL); #endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(sdl_surf); +#else SDL_FreeSurface(sdl_surf); +#endif free(icon); #endif } @@ -154,7 +164,11 @@ void SdlWindow::setWindowCaption(const Common::String &caption) { void SdlWindow::grabMouse(bool grab) { #if SDL_VERSION_ATLEAST(2, 0, 0) if (_window) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_SetWindowMouseGrab(_window, grab); +#else SDL_SetWindowGrab(_window, grab ? SDL_TRUE : SDL_FALSE); +#endif #if SDL_VERSION_ATLEAST(2, 0, 18) SDL_SetWindowMouseRect(_window, grab ? &grabRect : NULL); #endif @@ -187,7 +201,10 @@ void SdlWindow::setMouseRect(const Common::Rect &rect) { } bool SdlWindow::lockMouse(bool lock) { -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_SetWindowRelativeMouseMode(_window, lock); + _inputLockState = lock; +#elif SDL_VERSION_ATLEAST(2, 0, 0) SDL_SetRelativeMouseMode(lock ? SDL_TRUE : SDL_FALSE); _inputLockState = lock; #else @@ -244,6 +261,7 @@ void SdlWindow::iconifyWindow() { #endif } +#if !SDL_VERSION_ATLEAST(3, 0, 0) bool SdlWindow::getSDLWMInformation(SDL_SysWMinfo *info) const { SDL_VERSION(&info->version); #if SDL_VERSION_ATLEAST(2, 0, 0) @@ -254,9 +272,17 @@ bool SdlWindow::getSDLWMInformation(SDL_SysWMinfo *info) const { return false; #endif } +#endif Common::Rect SdlWindow::getDesktopResolution() { -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_DisplayMode* pDisplayMode = SDL_GetDesktopDisplayMode(getDisplayIndex()); + if (pDisplayMode) { + _desktopRes = Common::Rect(pDisplayMode->w, pDisplayMode->h); + } else { + warning("SDL_GetDesktopDisplayMode failed: %s", SDL_GetError()); + } +#elif SDL_VERSION_ATLEAST(2, 0, 0) SDL_DisplayMode displayMode; if (!SDL_GetDesktopDisplayMode(getDisplayIndex(), &displayMode)) { _desktopRes = Common::Rect(displayMode.w, displayMode.h); @@ -279,7 +305,9 @@ void SdlWindow::getDisplayDpi(float *dpi, float *defaultDpi) const { *defaultDpi = systemDpi; if (dpi) { -#if SDL_VERSION_ATLEAST(2, 0, 4) +#if SDL_VERSION_ATLEAST(3, 0, 0) + *dpi = SDL_GetWindowDisplayScale(_window) * systemDpi; +#elif SDL_VERSION_ATLEAST(2, 0, 4) if (SDL_GetDisplayDPI(getDisplayIndex(), nullptr, dpi, nullptr) != 0) { *dpi = systemDpi; } @@ -303,7 +331,9 @@ float SdlWindow::getDpiScalingFactor() const { } float SdlWindow::getSdlDpiScalingFactor() const { -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + return SDL_GetWindowDisplayScale(getSDLWindow()); +#elif SDL_VERSION_ATLEAST(2, 0, 0) int windowWidth, windowHeight; SDL_GetWindowSize(getSDLWindow(), &windowWidth, &windowHeight); int realWidth, realHeight; @@ -316,7 +346,11 @@ float SdlWindow::getSdlDpiScalingFactor() const { #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_Surface *copySDLSurface(SDL_Surface *src) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + const bool locked = SDL_MUSTLOCK(src); +#else const bool locked = SDL_MUSTLOCK(src) == SDL_TRUE; +#endif if (locked) { if (SDL_LockSurface(src) != 0) { @@ -324,10 +358,15 @@ SDL_Surface *copySDLSurface(SDL_Surface *src) { } } +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Surface *res = SDL_CreateSurfaceFrom(src->w, src->h, src->format, + src->pixels, src->pitch); +#else SDL_Surface *res = SDL_CreateRGBSurfaceFrom(src->pixels, src->w, src->h, src->format->BitsPerPixel, src->pitch, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask); +#endif if (locked) { SDL_UnlockSurface(src); @@ -337,6 +376,16 @@ SDL_Surface *copySDLSurface(SDL_Surface *src) { } int SdlWindow::getDisplayIndex() const { +#if SDL_VERSION_ATLEAST(3, 0, 0) + int display = 0; + int num_displays; + SDL_DisplayID *displays = SDL_GetDisplays(&num_displays); + if (num_displays > 0) { + display = static_cast(displays[0]); + } + SDL_free(displays); + return display; +#else if (_window) { int displayIndex = SDL_GetWindowDisplayIndex(_window); if (displayIndex >= 0) @@ -344,12 +393,19 @@ int SdlWindow::getDisplayIndex() const { } // Default to primary display return 0; +#endif } bool SdlWindow::createOrUpdateWindow(int width, int height, uint32 flags) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (_inputGrabState) { + flags |= SDL_WINDOW_MOUSE_GRABBED; + } +#else if (_inputGrabState) { flags |= SDL_WINDOW_INPUT_GRABBED; } +#endif // SDL_WINDOW_RESIZABLE can also be updated without recreating the window // starting with SDL 2.0.5, but it is not treated as updateable here @@ -360,7 +416,11 @@ bool SdlWindow::createOrUpdateWindow(int width, int height, uint32 flags) { // 2. Users (particularly on Windows) will sometimes swap older SDL DLLs // to avoid bugs, which would be impossible if the feature was enabled // at compile time using SDL_VERSION_ATLEAST. +#if SDL_VERSION_ATLEAST(3, 0, 0) + const uint32 updateableFlagsMask = fullscreenMask | SDL_WINDOW_MOUSE_GRABBED; +#else const uint32 updateableFlagsMask = fullscreenMask | SDL_WINDOW_INPUT_GRABBED; +#endif const uint32 oldNonUpdateableFlags = _lastFlags & ~updateableFlagsMask; const uint32 newNonUpdateableFlags = flags & ~updateableFlagsMask; @@ -387,7 +447,9 @@ bool SdlWindow::createOrUpdateWindow(int width, int height, uint32 flags) { ) { int top, left, bottom, right; -#if SDL_VERSION_ATLEAST(2, 0, 5) +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!_window || !SDL_GetWindowBordersSize(_window, &top, &left, &bottom, &right)) +#elif SDL_VERSION_ATLEAST(2, 0, 5) if (!_window || SDL_GetWindowBordersSize(_window, &top, &left, &bottom, &right) < 0) #endif { @@ -408,13 +470,31 @@ bool SdlWindow::createOrUpdateWindow(int width, int height, uint32 flags) { if (!_window || oldNonUpdateableFlags != newNonUpdateableFlags) { destroyWindow(); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, _windowCaption.c_str()); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, _lastX); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, _lastY); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, width); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, height); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, flags); + _window = SDL_CreateWindowWithProperties(props); + SDL_DestroyProperties(props); +#else _window = SDL_CreateWindow(_windowCaption.c_str(), _lastX, _lastY, width, height, flags); +#endif if (_window) { setupIcon(); } } else { if (fullscreenFlags) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if(!SDL_SetWindowFullscreenMode(_window, NULL)) + warning("SDL_SetWindowFullscreenMode failed: %s", SDL_GetError()); + if(!SDL_SyncWindow(_window)) + warning("SDL_SyncWindow failed: %s", SDL_GetError()); +#else SDL_DisplayMode fullscreenMode; fullscreenMode.w = width; fullscreenMode.h = height; @@ -422,6 +502,7 @@ bool SdlWindow::createOrUpdateWindow(int width, int height, uint32 flags) { fullscreenMode.format = 0; fullscreenMode.refresh_rate = 0; SDL_SetWindowDisplayMode(_window, &fullscreenMode); +#endif } else { SDL_SetWindowSize(_window, width, height); } @@ -429,8 +510,13 @@ bool SdlWindow::createOrUpdateWindow(int width, int height, uint32 flags) { SDL_SetWindowFullscreen(_window, fullscreenFlags); } +#if SDL_VERSION_ATLEAST(3, 0, 0) + const bool shouldGrab = (flags & SDL_WINDOW_MOUSE_GRABBED) || fullscreenFlags; + SDL_SetWindowMouseGrab(_window, shouldGrab); +#else const bool shouldGrab = (flags & SDL_WINDOW_INPUT_GRABBED) || fullscreenFlags; SDL_SetWindowGrab(_window, shouldGrab ? SDL_TRUE : SDL_FALSE); +#endif #if SDL_VERSION_ATLEAST(2, 0, 18) SDL_SetWindowMouseRect(_window, shouldGrab ? &grabRect : NULL); #endif diff --git a/backends/platform/sdl/sdl-window.h b/backends/platform/sdl/sdl-window.h index b3e4933874c0d..1aabe99df9029 100644 --- a/backends/platform/sdl/sdl-window.h +++ b/backends/platform/sdl/sdl-window.h @@ -78,6 +78,7 @@ class SdlWindow { */ void iconifyWindow(); +#if !SDL_VERSION_ATLEAST(3, 0, 0) /** * Query platform specific SDL window manager information. * @@ -85,6 +86,7 @@ class SdlWindow { * for accessing it in a version safe manner. */ bool getSDLWMInformation(SDL_SysWMinfo *info) const; +#endif /* * Retrieve the current desktop resolution. @@ -109,7 +111,11 @@ class SdlWindow { virtual float getDpiScalingFactor() const; bool mouseIsGrabbed() const { -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (_window) { + return SDL_GetWindowMouseGrab(_window); + } +#elif SDL_VERSION_ATLEAST(2, 0, 0) if (_window) { return SDL_GetWindowGrab(_window) == SDL_TRUE; } @@ -118,7 +124,9 @@ class SdlWindow { } bool mouseIsLocked() const { -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + return SDL_GetWindowRelativeMouseMode(_window); +#elif SDL_VERSION_ATLEAST(2, 0, 0) return SDL_GetRelativeMouseMode() == SDL_TRUE; #else return _inputLockState; diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp index 3fc1f20b3053d..3d93c49add959 100644 --- a/backends/platform/sdl/sdl.cpp +++ b/backends/platform/sdl/sdl.cpp @@ -20,6 +20,7 @@ */ #define FORBIDDEN_SYMBOL_ALLOW_ALL +#define SDL_FUNCTION_POINTER_IS_VOID_POINTER #include "backends/platform/sdl/sdl.h" #include "common/config-manager.h" @@ -72,8 +73,10 @@ #include #endif -#if SDL_VERSION_ATLEAST(2, 0, 0) -#include +#if SDL_VERSION_ATLEAST(3, 0, 0) + #include +#elif SDL_VERSION_ATLEAST(2, 0, 0) + #include #endif OSystem_SDL::OSystem_SDL() @@ -97,7 +100,11 @@ OSystem_SDL::OSystem_SDL() } OSystem_SDL::~OSystem_SDL() { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_ShowCursor(); +#else SDL_ShowCursor(SDL_ENABLE); +#endif #ifdef USE_MULTIPLE_RENDERERS clearGraphicsModes(); @@ -162,8 +169,12 @@ void OSystem_SDL::init() { #endif #if !defined(OPENPANDORA) +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_HideCursor(); +#else // Disable OS cursor SDL_ShowCursor(SDL_DISABLE); +#endif #endif if (_window == nullptr) @@ -206,7 +217,13 @@ bool OSystem_SDL::hasFeature(Feature f) { #if defined(USE_SCUMMVMDLC) && defined(USE_LIBCURL) if (f == kFeatureDLC) return true; #endif -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (f == kFeatureTouchpadMode) { + int count = 0; + SDL_free(SDL_GetTouchDevices(&count)); + return count > 0; + } +#elif SDL_VERSION_ATLEAST(2, 0, 0) if (f == kFeatureTouchpadMode) { return SDL_GetNumTouchDevices() > 0; } @@ -383,7 +400,17 @@ void OSystem_SDL::detectOpenGLFeaturesSupport() { #else // Spawn a 32x32 window off-screen with a GL context to test if framebuffers are supported #if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, ""); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, 32); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, 32); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); + SDL_Window *window = SDL_CreateWindowWithProperties(props); + SDL_DestroyProperties(props); +#else SDL_Window *window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 32, 32, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); +#endif if (!window) { return; } @@ -417,7 +444,11 @@ void OSystem_SDL::detectOpenGLFeaturesSupport() { _supportsFrameBuffer = OpenGLContext.framebufferObjectSupported; _supportsShaders = OpenGLContext.enginesShadersSupported; OpenGLContext.reset(); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_GL_DestroyContext(glContext); +#else SDL_GL_DeleteContext(glContext); +#endif SDL_DestroyWindow(window); #else SDL_putenv(const_cast("SDL_VIDEO_WINDOW_POS=9000,9000")); @@ -443,7 +474,17 @@ void OSystem_SDL::detectAntiAliasingSupport() { SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, requestedSamples); #if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, ""); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, 32); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, 32); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); + SDL_Window *window = SDL_CreateWindowWithProperties(props); + SDL_DestroyProperties(props); +#else SDL_Window *window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 32, 32, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); +#endif if (window) { SDL_GLContext glContext = SDL_GL_CreateContext(window); if (glContext) { @@ -454,7 +495,11 @@ void OSystem_SDL::detectAntiAliasingSupport() { _antiAliasLevels.push_back(requestedSamples); } +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_GL_DestroyContext(glContext); +#else SDL_GL_DeleteContext(glContext); +#endif } SDL_DestroyWindow(window); @@ -533,11 +578,17 @@ void OSystem_SDL::initSDL() { // or otherwise the application won't start. uint32 sdlFlags = SDL_INIT_VIDEO; +#if !SDL_VERSION_ATLEAST(3, 0, 0) if (ConfMan.hasKey("disable_sdl_parachute")) sdlFlags |= SDL_INIT_NOPARACHUTE; +#endif // Initialize SDL (SDL Subsystems are initialized in the corresponding sdl managers) +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_Init(sdlFlags)) +#else if (SDL_Init(sdlFlags) == -1) +#endif error("Could not initialize SDL: %s", SDL_GetError()); _initedSDL = true; @@ -657,7 +708,19 @@ Common::WriteStream *OSystem_SDL::createLogFile() { Common::String OSystem_SDL::getSystemLanguage() const { -#if SDL_VERSION_ATLEAST(2, 0, 14) +#if SDL_VERSION_ATLEAST(3, 0, 0) + int count = 0; + SDL_Locale **pLocales = SDL_GetPreferredLocales(&count); + if (pLocales) { + SDL_Locale *locales = *pLocales; + if (locales[0].language != NULL) { + Common::String str = Common::String::format("%s_%s", locales[0].country, locales[0].language); + SDL_free(locales); + return str; + } + SDL_free(pLocales); + } +#elif SDL_VERSION_ATLEAST(2, 0, 14) SDL_Locale *locales = SDL_GetPreferredLocales(); if (locales) { if (locales[0].language != NULL) { @@ -667,7 +730,7 @@ Common::String OSystem_SDL::getSystemLanguage() const { } SDL_free(locales); } -#endif // SDL_VERSION_ATLEAST(2, 0, 14) +#endif #if defined(USE_DETECTLANG) && !defined(WIN32) // Activating current locale settings const Common::String locale = setlocale(LC_ALL, ""); @@ -704,7 +767,11 @@ Common::String OSystem_SDL::getSystemLanguage() const { #if SDL_VERSION_ATLEAST(2, 0, 0) bool OSystem_SDL::hasTextInClipboard() { +#if SDL_VERSION_ATLEAST(3, 0, 0) + return SDL_HasClipboardText(); +#else return SDL_HasClipboardText() == SDL_TRUE; +#endif } Common::U32String OSystem_SDL::getTextFromClipboard() { diff --git a/backends/timer/sdl/sdl-timer.cpp b/backends/timer/sdl/sdl-timer.cpp index 81812888f8ce4..cda5923c3a7df 100644 --- a/backends/timer/sdl/sdl-timer.cpp +++ b/backends/timer/sdl/sdl-timer.cpp @@ -28,16 +28,25 @@ #include "common/textconsole.h" +#if SDL_VERSION_ATLEAST(3, 0, 0) +static Uint32 timer_handler(void *userdata, SDL_TimerID timerID, Uint32 interval) { + ((DefaultTimerManager *)userdata)->handler(); + return interval; +} +#else static Uint32 timer_handler(Uint32 interval, void *param) { ((DefaultTimerManager *)param)->handler(); return interval; } +#endif SdlTimerManager::SdlTimerManager() { +#if !SDL_VERSION_ATLEAST(3, 0, 0) // Initializes the SDL timer subsystem if (SDL_InitSubSystem(SDL_INIT_TIMER) == -1) { error("Could not initialize SDL: %s", SDL_GetError()); } +#endif // Creates the timer callback _timerID = SDL_AddTimer(10, &timer_handler, this); @@ -47,7 +56,9 @@ SdlTimerManager::~SdlTimerManager() { // Removes the timer callback SDL_RemoveTimer(_timerID); +#if !SDL_VERSION_ATLEAST(3, 0, 0) SDL_QuitSubSystem(SDL_INIT_TIMER); +#endif } #endif diff --git a/base/commandLine.cpp b/base/commandLine.cpp index 234ab0b51fb2e..bdb3174be3364 100644 --- a/base/commandLine.cpp +++ b/base/commandLine.cpp @@ -1933,7 +1933,10 @@ bool processSettings(Common::String &command, Common::StringMap &settings, Commo } else if (command == "version") { printf("%s\n", gScummVMFullVersion); #ifdef SDL_BACKEND -#ifdef USE_SDL2 +#ifdef USE_SDL3 + int sdlLinkedVersion = SDL_GetVersion(); + printf("Using SDL backend with SDL %d.%d.%d\n", SDL_VERSIONNUM_MAJOR(sdlLinkedVersion), SDL_VERSIONNUM_MINOR(sdlLinkedVersion), SDL_VERSIONNUM_MICRO(sdlLinkedVersion)); +#elif defined(USE_SDL2) SDL_version sdlLinkedVersion; SDL_GetVersion(&sdlLinkedVersion); printf("Using SDL backend with SDL %d.%d.%d\n", sdlLinkedVersion.major, sdlLinkedVersion.minor, sdlLinkedVersion.patch); diff --git a/configure b/configure index b490265b5b965..7f7c4ee274bc1 100755 --- a/configure +++ b/configure @@ -248,7 +248,7 @@ _staticlibpath= _xcodetoolspath= _sparklepath= _pkgconfig=pkg-config -_sdlconfig=sdl2-config +_sdlconfig="pkg-config sdl3" _libcurlconfig=curl-config _libmikmodconfig=libmikmod-config _freetypeconfig=freetype-config @@ -4144,21 +4144,31 @@ fi # Setup SDL specifics for SDL based backends # if test "$_sdl" = auto ; then - find_sdlconfig + if test "$_pkg_config" = "yes" && $_pkgconfig --exists sdl3; then + _sdlconfig="pkg-config sdl3" + _sdlversion=`$_sdlconfig --modversion` + cat > $TMPC << EOF +#include "SDL3/SDL.h" +int main(int argc, char *argv[]) { SDL_Init(0); return 0; } +EOF + else + find_sdlconfig + _sdlversion=`$_sdlconfig --version` + cat > $TMPC << EOF +#include "SDL.h" +int main(int argc, char *argv[]) { SDL_Init(0); return 0; } +EOF + fi + append_var SDL_CFLAGS "`$_sdlconfig --cflags | sed 's/[[:space:]]*-Dmain=SDL_main//g'`" if test "$_static_build" = yes ; then append_var SDL_LIBS "`$_sdlconfig --static-libs`" else append_var SDL_LIBS "`$_sdlconfig --libs`" fi - _sdlversion=`$_sdlconfig --version` echocheck "SDL" _sdl=no - cat > $TMPC << EOF -#include "SDL.h" -int main(int argc, char *argv[]) { SDL_Init(0); return 0; } -EOF cc_check $LIBS $SDL_LIBS $INCLUDES $SDL_CFLAGS && _sdl=yes echo "$_sdl" if test "$_sdl" = no ; then @@ -4173,6 +4183,11 @@ if test "$_sdl" = yes ; then append_var INCLUDES "$SDL_CFLAGS" append_var LIBS "$SDL_LIBS" case $_sdlversion in + 3.*.*) + append_var DEFINES "-DUSE_SDL3" + add_line_to_config_mk "USE_SDL3 = 1" + _sdlMajorVersionNumber=3 + ;; 2.*.*) append_var DEFINES "-DUSE_SDL2" add_line_to_config_mk "USE_SDL2 = 1" @@ -4193,14 +4208,26 @@ fi # of SDL-net or SDL2-net that does not require SDL or SDL2 respectively # if test "$_sdlnet" = auto ; then + # If SDL3 was detected, then test for SDL3_net exclusively # If SDL2 was detected, then test for SDL2_net exclusively # If SDL was detected, then test for SDL_net exclusively - # If neither SDL nor SDL2 detected, then test for both (SDL2_net success takes priority) + # If neither SDL nor SDL2 detected nor SDL3 detected, then test for both (SDL3_net success takes priority) + set_var SDL3_NET_LIBS "$SDL_NET_LIBS" + set_var SDL3_NET_CFLAGS "$SDL_NET_CFLAGS" set_var SDL2_NET_LIBS "$SDL_NET_LIBS" set_var SDL2_NET_CFLAGS "$SDL_NET_CFLAGS" set_var SDL1_NET_LIBS "$SDL_NET_LIBS" set_var SDL1_NET_CFLAGS "$SDL_NET_CFLAGS" + if test "$_sdl" = no || test "$_sdlMajorVersionNumber" = 3; then + if test "$_pkg_config" = "yes" && $_pkgconfig --exists SDL3_net; then + append_var SDL3_NET_LIBS "`$_pkgconfig --libs SDL3_net`" + append_var SDL3_NET_CFLAGS "`$_pkgconfig --cflags SDL3_net | sed 's/[[:space:]]*-Dmain=SDL_main//g'`" + else + append_var SDL3_NET_LIBS "-lSDL3_net" + fi + fi + if test "$_sdl" = no || test "$_sdlMajorVersionNumber" = 2; then if test "$_pkg_config" = "yes" && $_pkgconfig --exists SDL2_net; then append_var SDL2_NET_LIBS "`$_pkgconfig --libs SDL2_net`" @@ -4222,26 +4249,39 @@ if test "$_sdlnet" = auto ; then # Check for SDL_Net echocheck "SDL_Net" _sdlnet=no - cat > $TMPC << EOF -#include "SDL_net.h" + +cat > $TMPC << EOF +#include "SDL3/SDL_net.h" int main(int argc, char *argv[]) { SDLNet_Init(); return 0; } EOF - cc_check $SDL2_NET_LIBS $LIBS $INCLUDES $SDL2_NET_CFLAGS && _sdlnet=yes + cc_check $SDL3_NET_LIBS $LIBS $INCLUDES $SDL3_NET_CFLAGS && _sdlnet=yes if test "$_sdlnet" = yes ; then set_var SDL_NET_LIBS "$SDL2_NET_LIBS" set_var SDL_NET_CFLAGS "$SDL2_NET_CFLAGS" add_line_to_config_mk "SDL_NET_MAJOR = 2" else cat > $TMPC << EOF -#include "SDL_net.h" +#include "SDL3/SDL_net.h" int main(int argc, char *argv[]) { SDLNet_Init(); return 0; } EOF - cc_check $SDL1_NET_LIBS $LIBS $INCLUDES $SDL1_NET_CFLAGS && _sdlnet=yes + + cc_check $SDL2_NET_LIBS $LIBS $INCLUDES $SDL2_NET_CFLAGS && _sdlnet=yes if test "$_sdlnet" = yes ; then - set_var SDL_NET_LIBS "$SDL1_NET_LIBS" - set_var SDL_NET_CFLAGS "$SDL1_NET_CFLAGS" - add_line_to_config_mk "SDL_NET_MAJOR = 1" + set_var SDL_NET_LIBS "$SDL2_NET_LIBS" + set_var SDL_NET_CFLAGS "$SDL2_NET_CFLAGS" + add_line_to_config_mk "SDL_NET_MAJOR = 2" + else + cat > $TMPC << EOF +#include "SDL_net.h" +int main(int argc, char *argv[]) { SDLNet_Init(); return 0; } +EOF + cc_check $SDL1_NET_LIBS $LIBS $INCLUDES $SDL1_NET_CFLAGS && _sdlnet=yes + if test "$_sdlnet" = yes ; then + set_var SDL_NET_LIBS "$SDL1_NET_LIBS" + set_var SDL_NET_CFLAGS "$SDL1_NET_CFLAGS" + add_line_to_config_mk "SDL_NET_MAJOR = 1" + fi fi fi @@ -6745,7 +6785,26 @@ if test "$_imgui" != no ; then if test "$_freetype2" = yes ; then case $_backend in sdl | sailfish) - if test "$_sdlMajorVersionNumber" -ge 2 ; then + if test "$_sdlMajorVersionNumber" -ge 3 ; then + cat > $TMPC << EOF +#include +#if !SDL_VERSION_ATLEAST(3,0,0) +#error Missing SDL_RenderGeometryRaw() function +#endif +int main(int argc, char *argv[]) { return 0; } +EOF + if cc_check $LIBS $SDL_LIBS $INCLUDES $SDL_CFLAGS; then + define_in_config_if_yes yes 'USE_IMGUI_SDLRENDERER3' + _imgui=yes + echo "yes" + elif test "$_opengl" = yes; then + _imgui=yes + echo "yes" + else + _imgui=no + echo "no (requires OpenGL or recent SDL)" + fi + elif test "$_sdlMajorVersionNumber" -ge 2 ; then cat > $TMPC << EOF #include "SDL.h" #if !SDL_VERSION_ATLEAST(2,0,18)