[Scummvm-git-logs] scummvm master -> 8afb2c1f62f518fa5872eefbd5a951b29e6ceb32
sev-
noreply at scummvm.org
Tue Feb 18 21:14:00 UTC 2025
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
8afb2c1f62 BACKENDS: Add SDL3 backend + update imgui
Commit: 8afb2c1f62f518fa5872eefbd5a951b29e6ceb32
https://github.com/scummvm/scummvm/commit/8afb2c1f62f518fa5872eefbd5a951b29e6ceb32
Author: scemino (scemino74 at gmail.com)
Date: 2025-02-18T22:13:56+01:00
Commit Message:
BACKENDS: Add SDL3 backend + update imgui
Changed paths:
A backends/events/sdl/sdl-common-events.cpp
A backends/events/sdl/sdl1-events.cpp
A backends/events/sdl/sdl2-events.cpp
A backends/events/sdl/sdl3-events.cpp
A backends/imgui/backends/imgui_impl_sdl3.cpp
A backends/imgui/backends/imgui_impl_sdl3.h
A backends/imgui/backends/imgui_impl_sdlrenderer3.cpp
A backends/imgui/backends/imgui_impl_sdlrenderer3.h
R backends/events/sdl/sdl-events.cpp
backends/events/sdl/sdl-events.h
backends/graphics/openglsdl/openglsdl-graphics.cpp
backends/graphics/sdl/sdl-graphics.cpp
backends/graphics/sdl/sdl-graphics.h
backends/graphics/surfacesdl/surfacesdl-graphics.cpp
backends/graphics/surfacesdl/surfacesdl-graphics.h
backends/graphics3d/openglsdl/openglsdl-graphics3d.cpp
backends/imgui/scummvm.patch
backends/mixer/sdl/sdl-mixer.cpp
backends/mixer/sdl/sdl-mixer.h
backends/module.mk
backends/mutex/sdl/sdl-mutex.cpp
backends/platform/sdl/amigaos/amigaos.cpp
backends/platform/sdl/macosx/macosx-window.mm
backends/platform/sdl/sdl-sys.h
backends/platform/sdl/sdl-window.cpp
backends/platform/sdl/sdl-window.h
backends/platform/sdl/sdl.cpp
backends/timer/sdl/sdl-timer.cpp
base/commandLine.cpp
configure
diff --git a/backends/events/sdl/sdl-common-events.cpp b/backends/events/sdl/sdl-common-events.cpp
new file mode 100644
index 00000000000..2821bf1ec8d
--- /dev/null
+++ b/backends/events/sdl/sdl-common-events.cpp
@@ -0,0 +1,254 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/scummsys.h"
+
+#if defined(SDL_BACKEND)
+
+#include "backends/events/sdl/sdl-events.h"
+#include "backends/platform/sdl/sdl.h"
+#include "backends/graphics/graphics.h"
+#include "common/config-manager.h"
+#include "common/textconsole.h"
+#include "common/fs.h"
+#include "engines/engine.h"
+#include "gui/gui-manager.h"
+
+SdlEventSource::~SdlEventSource() {
+ closeJoystick();
+}
+
+bool SdlEventSource::processMouseEvent(Common::Event &event, int x, int y, int relx, int rely) {
+ _mouseX = x;
+ _mouseY = y;
+
+ event.mouse.x = x;
+ event.mouse.y = y;
+ event.relMouse.x = relx;
+ event.relMouse.y = rely;
+
+ if (_graphicsManager) {
+ return _graphicsManager->notifyMousePosition(event.mouse);
+ }
+
+ return true;
+}
+
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+Common::Point SdlEventSource::getTouchscreenSize() {
+ int windowWidth, windowHeight;
+ SDL_GetWindowSize((dynamic_cast<SdlGraphicsManager*>(_graphicsManager))->getWindow()->getSDLWindow(), &windowWidth, &windowHeight);
+ return Common::Point(windowWidth, windowHeight);
+}
+
+bool SdlEventSource::isTouchPortTouchpadMode(SDL_TouchID port) {
+ return g_system->getFeatureState(OSystem::kFeatureTouchpadMode);
+}
+
+bool SdlEventSource::isTouchPortActive(SDL_TouchID port) {
+ return true;
+}
+
+void SdlEventSource::convertTouchXYToGameXY(float touchX, float touchY, int *gameX, int *gameY) {
+ int windowWidth, windowHeight;
+ SDL_GetWindowSize((dynamic_cast<SdlGraphicsManager*>(_graphicsManager))->getWindow()->getSDLWindow(), &windowWidth, &windowHeight);
+
+ *gameX = windowWidth * touchX;
+ *gameY = windowHeight * touchY;
+}
+#endif
+
+bool SdlEventSource::handleMouseMotion(SDL_Event &ev, Common::Event &event) {
+ event.type = Common::EVENT_MOUSEMOVE;
+
+ return processMouseEvent(event, ev.motion.x, ev.motion.y, ev.motion.xrel, ev.motion.yrel);
+}
+
+bool SdlEventSource::handleMouseButtonDown(SDL_Event &ev, Common::Event &event) {
+ if (ev.button.button == SDL_BUTTON_LEFT)
+ event.type = Common::EVENT_LBUTTONDOWN;
+ else if (ev.button.button == SDL_BUTTON_RIGHT)
+ event.type = Common::EVENT_RBUTTONDOWN;
+#if defined(SDL_BUTTON_WHEELUP) && defined(SDL_BUTTON_WHEELDOWN)
+ else if (ev.button.button == SDL_BUTTON_WHEELUP)
+ event.type = Common::EVENT_WHEELUP;
+ else if (ev.button.button == SDL_BUTTON_WHEELDOWN)
+ event.type = Common::EVENT_WHEELDOWN;
+#endif
+#if defined(SDL_BUTTON_MIDDLE)
+ else if (ev.button.button == SDL_BUTTON_MIDDLE)
+ event.type = Common::EVENT_MBUTTONDOWN;
+#endif
+#if defined(SDL_BUTTON_X1)
+ else if (ev.button.button == SDL_BUTTON_X1)
+ event.type = Common::EVENT_X1BUTTONDOWN;
+#endif
+#if defined(SDL_BUTTON_X2)
+ else if (ev.button.button == SDL_BUTTON_X2)
+ event.type = Common::EVENT_X2BUTTONDOWN;
+#endif
+ else
+ return false;
+
+ return processMouseEvent(event, ev.button.x, ev.button.y);
+}
+
+bool SdlEventSource::handleMouseButtonUp(SDL_Event &ev, Common::Event &event) {
+ if (ev.button.button == SDL_BUTTON_LEFT)
+ event.type = Common::EVENT_LBUTTONUP;
+ else if (ev.button.button == SDL_BUTTON_RIGHT)
+ event.type = Common::EVENT_RBUTTONUP;
+#if defined(SDL_BUTTON_MIDDLE)
+ else if (ev.button.button == SDL_BUTTON_MIDDLE)
+ event.type = Common::EVENT_MBUTTONUP;
+#endif
+#if defined(SDL_BUTTON_X1)
+ else if (ev.button.button == SDL_BUTTON_X1)
+ event.type = Common::EVENT_X1BUTTONUP;
+#endif
+#if defined(SDL_BUTTON_X2)
+ else if (ev.button.button == SDL_BUTTON_X2)
+ event.type = Common::EVENT_X2BUTTONUP;
+#endif
+ else
+ return false;
+
+ return processMouseEvent(event, ev.button.x, ev.button.y);
+}
+
+bool SdlEventSource::handleSysWMEvent(SDL_Event &ev, Common::Event &event) {
+ return false;
+}
+
+int SdlEventSource::mapSDLJoystickButtonToOSystem(Uint8 sdlButton) {
+ const Common::JoystickButton osystemButtons[] = {
+ Common::JOYSTICK_BUTTON_A,
+ Common::JOYSTICK_BUTTON_B,
+ Common::JOYSTICK_BUTTON_X,
+ Common::JOYSTICK_BUTTON_Y,
+ Common::JOYSTICK_BUTTON_LEFT_SHOULDER,
+ Common::JOYSTICK_BUTTON_RIGHT_SHOULDER,
+ Common::JOYSTICK_BUTTON_BACK,
+ Common::JOYSTICK_BUTTON_START,
+ Common::JOYSTICK_BUTTON_LEFT_STICK,
+ Common::JOYSTICK_BUTTON_RIGHT_STICK
+ };
+
+ if (sdlButton >= ARRAYSIZE(osystemButtons)) {
+ return -1;
+ }
+
+ return osystemButtons[sdlButton];
+}
+
+bool SdlEventSource::handleJoyButtonDown(SDL_Event &ev, Common::Event &event) {
+ int button = mapSDLJoystickButtonToOSystem(ev.jbutton.button);
+ if (button < 0) {
+ return false;
+ }
+
+ event.type = Common::EVENT_JOYBUTTON_DOWN;
+ event.joystick.button = button;
+
+ return true;
+}
+
+bool SdlEventSource::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) {
+ int button = mapSDLJoystickButtonToOSystem(ev.jbutton.button);
+ if (button < 0) {
+ return false;
+ }
+
+ event.type = Common::EVENT_JOYBUTTON_UP;
+ event.joystick.button = button;
+
+ return true;
+}
+
+bool SdlEventSource::handleJoyAxisMotion(SDL_Event &ev, Common::Event &event) {
+ event.type = Common::EVENT_JOYAXIS_MOTION;
+ event.joystick.axis = ev.jaxis.axis;
+ event.joystick.position = ev.jaxis.value;
+
+ return true;
+}
+
+#define HANDLE_HAT_UP(new, old, mask, joybutton) \
+ if ((old & mask) && !(new & mask)) { \
+ event.joystick.button = joybutton; \
+ g_system->getEventManager()->pushEvent(event); \
+ }
+
+#define HANDLE_HAT_DOWN(new, old, mask, joybutton) \
+ if ((new & mask) && !(old & mask)) { \
+ event.joystick.button = joybutton; \
+ g_system->getEventManager()->pushEvent(event); \
+ }
+
+bool SdlEventSource::handleJoyHatMotion(SDL_Event &ev, Common::Event &event) {
+ event.type = Common::EVENT_JOYBUTTON_UP;
+ HANDLE_HAT_UP(ev.jhat.value, _lastHatPosition, SDL_HAT_UP, Common::JOYSTICK_BUTTON_DPAD_UP)
+ HANDLE_HAT_UP(ev.jhat.value, _lastHatPosition, SDL_HAT_DOWN, Common::JOYSTICK_BUTTON_DPAD_DOWN)
+ HANDLE_HAT_UP(ev.jhat.value, _lastHatPosition, SDL_HAT_LEFT, Common::JOYSTICK_BUTTON_DPAD_LEFT)
+ HANDLE_HAT_UP(ev.jhat.value, _lastHatPosition, SDL_HAT_RIGHT, Common::JOYSTICK_BUTTON_DPAD_RIGHT)
+
+ event.type = Common::EVENT_JOYBUTTON_DOWN;
+ HANDLE_HAT_DOWN(ev.jhat.value, _lastHatPosition, SDL_HAT_UP, Common::JOYSTICK_BUTTON_DPAD_UP)
+ HANDLE_HAT_DOWN(ev.jhat.value, _lastHatPosition, SDL_HAT_DOWN, Common::JOYSTICK_BUTTON_DPAD_DOWN)
+ HANDLE_HAT_DOWN(ev.jhat.value, _lastHatPosition, SDL_HAT_LEFT, Common::JOYSTICK_BUTTON_DPAD_LEFT)
+ HANDLE_HAT_DOWN(ev.jhat.value, _lastHatPosition, SDL_HAT_RIGHT, Common::JOYSTICK_BUTTON_DPAD_RIGHT)
+
+ _lastHatPosition = ev.jhat.value;
+
+ return false;
+}
+
+bool SdlEventSource::remapKey(SDL_Event &ev, Common::Event &event) {
+ return false;
+}
+
+void SdlEventSource::fakeWarpMouse(const int x, const int y) {
+ _queuedFakeMouseMove = true;
+ _fakeMouseMove.type = Common::EVENT_MOUSEMOVE;
+ _fakeMouseMove.mouse = Common::Point(x, y);
+}
+
+void SdlEventSource::setEngineRunning(const bool value) {
+ _engineRunning = value;
+}
+
+bool SdlEventSource::handleResizeEvent(Common::Event &event, int w, int h) {
+ if (_graphicsManager) {
+ _graphicsManager->notifyResize(w, h);
+
+ // If the screen changed, send an Common::EVENT_SCREEN_CHANGED
+ int screenID = g_system->getScreenChangeID();
+ if (screenID != _lastScreenID) {
+ _lastScreenID = screenID;
+ event.type = Common::EVENT_SCREEN_CHANGED;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#endif
diff --git a/backends/events/sdl/sdl-events.h b/backends/events/sdl/sdl-events.h
index 2fcf289e7bc..02b548ee9de 100644
--- a/backends/events/sdl/sdl-events.h
+++ b/backends/events/sdl/sdl-events.h
@@ -73,7 +73,10 @@ protected:
/** 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 @@ protected:
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/events/sdl/sdl1-events.cpp b/backends/events/sdl/sdl1-events.cpp
new file mode 100644
index 00000000000..24931213611
--- /dev/null
+++ b/backends/events/sdl/sdl1-events.cpp
@@ -0,0 +1,478 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/scummsys.h"
+
+#if defined(SDL_BACKEND)
+
+#include "backends/events/sdl/sdl-events.h"
+#include "backends/platform/sdl/sdl.h"
+#include "backends/graphics/graphics.h"
+#include "common/config-manager.h"
+#include "common/textconsole.h"
+#include "common/fs.h"
+#include "engines/engine.h"
+#include "gui/gui-manager.h"
+
+SdlEventSource::SdlEventSource()
+ : EventSource(), _scrollLock(false), _joystick(nullptr), _lastScreenID(0), _graphicsManager(nullptr), _queuedFakeMouseMove(false),
+ _lastHatPosition(SDL_HAT_CENTERED), _mouseX(0), _mouseY(0), _engineRunning(false)
+ {
+ int joystick_num = ConfMan.getInt("joystick_num");
+ if (joystick_num >= 0) {
+ // Initialize SDL joystick subsystem
+ if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) {
+ warning("Could not initialize SDL joystick: %s", SDL_GetError());
+ return;
+ }
+
+ openJoystick(joystick_num);
+ }
+}
+
+int SdlEventSource::mapKey(SDL_Keycode sdlKey, SDL_Keymod mod, Uint16 unicode) {
+ Common::KeyCode key = SDLToOSystemKeycode(sdlKey);
+
+ // Keep unicode in case it's regular ASCII text, Hebrew or in case we didn't get a valid keycode
+ //
+ // We need to use unicode in those cases, simply because SDL1.x passes us non-layout-adjusted keycodes.
+ // So unicode is the only way to get layout-adjusted keys.
+ if (unicode < 0x20) {
+ // don't use unicode, in case it's control characters
+ unicode = 0;
+ } else {
+ // Use unicode, in case keycode is invalid.
+ // 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 (mod & (KMOD_CTRL | KMOD_ALT)) {
+ // 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
+ // an alternate keyboard, which will cause us to receive Unicode characters for some keys, which are outside
+ // of the ASCII range (e.g. alt-x will get us U+2248). We need to return 'x' for alt-x, so using unicode
+ // in that case would break alt-shortcuts.
+ if (unicode > 0x7E)
+ unicode = 0; // do not allow any characters above 0x7E
+ } else {
+ // We allow Hebrew characters
+ if (unicode >= 0x05D0 && unicode <= 0x05EA)
+ return unicode;
+
+ // Cyrillic
+ if (unicode >= 0x0400 && unicode <= 0x045F)
+ return unicode;
+
+ // We must not restrict as much as when Ctrl/Alt-modifiers are active, otherwise
+ // we wouldn't let umlauts through for SDL1. For SDL1 umlauts may set for example KEYCODE_QUOTE, KEYCODE_MINUS, etc.
+ if (unicode > 0xFF)
+ unicode = 0; // do not allow any characters above 0xFF
+ }
+ }
+ }
+
+ // Attention:
+ // When using SDL1.x, we will get scancodes via sdlKey, that are raw scancodes, so NOT adjusted to keyboard layout/
+ // mapping. So for example for certain locales, we will get KEYCODE_y, when 'z' is pressed and so on.
+ // When using SDL2.x however, we will get scancodes based on the keyboard layout.
+
+ 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 ((mod & KMOD_NUM) == 0)
+ 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) {
+ return key;
+ } else if (unicode) {
+ // Return unicode in case it's still set and wasn't filtered.
+ return unicode;
+ } else if (key >= 'a' && key <= 'z' && (mod & KMOD_SHIFT)) {
+ return key & ~0x20;
+ } else if (key >= Common::KEYCODE_NUMLOCK && key < Common::KEYCODE_LAST) {
+ return 0;
+ } else {
+ return key;
+ }
+}
+
+void SdlEventSource::SDLModToOSystemKeyFlags(SDL_Keymod mod, Common::Event &event) {
+
+ event.kbd.flags = 0;
+
+ 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;
+ if (mod & KMOD_META)
+ event.kbd.flags |= Common::KBD_META;
+
+ // Sticky flags
+ if (mod & KMOD_NUM)
+ event.kbd.flags |= Common::KBD_NUM;
+ if (mod & KMOD_CAPS)
+ event.kbd.flags |= Common::KBD_CAPS;
+}
+
+Common::KeyCode SdlEventSource::SDLToOSystemKeycode(const SDL_Keycode key) {
+ switch (key) {
+ case SDLK_BACKSPACE: return Common::KEYCODE_BACKSPACE;
+ case SDLK_TAB: return Common::KEYCODE_TAB;
+ case SDLK_CLEAR: return Common::KEYCODE_CLEAR;
+ case SDLK_RETURN: return Common::KEYCODE_RETURN;
+ case SDLK_PAUSE: return Common::KEYCODE_PAUSE;
+ case SDLK_ESCAPE: return Common::KEYCODE_ESCAPE;
+ case SDLK_SPACE: return Common::KEYCODE_SPACE;
+ case SDLK_EXCLAIM: return Common::KEYCODE_EXCLAIM;
+ case SDLK_QUOTEDBL: return Common::KEYCODE_QUOTEDBL;
+ case SDLK_HASH: return Common::KEYCODE_HASH;
+ case SDLK_DOLLAR: return Common::KEYCODE_DOLLAR;
+ case SDLK_AMPERSAND: return Common::KEYCODE_AMPERSAND;
+ case SDLK_QUOTE: return Common::KEYCODE_QUOTE;
+ case SDLK_LEFTPAREN: return Common::KEYCODE_LEFTPAREN;
+ case SDLK_RIGHTPAREN: return Common::KEYCODE_RIGHTPAREN;
+ case SDLK_ASTERISK: return Common::KEYCODE_ASTERISK;
+ case SDLK_PLUS: return Common::KEYCODE_PLUS;
+ case SDLK_COMMA: return Common::KEYCODE_COMMA;
+ case SDLK_MINUS: return Common::KEYCODE_MINUS;
+ case SDLK_PERIOD: return Common::KEYCODE_PERIOD;
+ case SDLK_SLASH: return Common::KEYCODE_SLASH;
+ case SDLK_0: return Common::KEYCODE_0;
+ case SDLK_1: return Common::KEYCODE_1;
+ case SDLK_2: return Common::KEYCODE_2;
+ case SDLK_3: return Common::KEYCODE_3;
+ case SDLK_4: return Common::KEYCODE_4;
+ case SDLK_5: return Common::KEYCODE_5;
+ case SDLK_6: return Common::KEYCODE_6;
+ case SDLK_7: return Common::KEYCODE_7;
+ case SDLK_8: return Common::KEYCODE_8;
+ case SDLK_9: return Common::KEYCODE_9;
+ case SDLK_COLON: return Common::KEYCODE_COLON;
+ case SDLK_SEMICOLON: return Common::KEYCODE_SEMICOLON;
+ case SDLK_LESS: return Common::KEYCODE_LESS;
+ case SDLK_EQUALS: return Common::KEYCODE_EQUALS;
+ case SDLK_GREATER: return Common::KEYCODE_GREATER;
+ case SDLK_QUESTION: return Common::KEYCODE_QUESTION;
+ case SDLK_AT: return Common::KEYCODE_AT;
+ case SDLK_LEFTBRACKET: return Common::KEYCODE_LEFTBRACKET;
+ case SDLK_BACKSLASH: return Common::KEYCODE_BACKSLASH;
+ case SDLK_RIGHTBRACKET: return Common::KEYCODE_RIGHTBRACKET;
+ case SDLK_CARET: return Common::KEYCODE_CARET;
+ case SDLK_UNDERSCORE: return Common::KEYCODE_UNDERSCORE;
+ case SDLK_BACKQUOTE: 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;
+ 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;
+ case SDLK_KP_MULTIPLY: return Common::KEYCODE_KP_MULTIPLY;
+ case SDLK_KP_MINUS: return Common::KEYCODE_KP_MINUS;
+ case SDLK_KP_PLUS: return Common::KEYCODE_KP_PLUS;
+ case SDLK_KP_ENTER: return Common::KEYCODE_KP_ENTER;
+ case SDLK_KP_EQUALS: return Common::KEYCODE_KP_EQUALS;
+ case SDLK_UP: return Common::KEYCODE_UP;
+ case SDLK_DOWN: return Common::KEYCODE_DOWN;
+ case SDLK_RIGHT: return Common::KEYCODE_RIGHT;
+ case SDLK_LEFT: return Common::KEYCODE_LEFT;
+ case SDLK_INSERT: return Common::KEYCODE_INSERT;
+ case SDLK_HOME: return Common::KEYCODE_HOME;
+ case SDLK_END: return Common::KEYCODE_END;
+ case SDLK_PAGEUP: return Common::KEYCODE_PAGEUP;
+ case SDLK_PAGEDOWN: return Common::KEYCODE_PAGEDOWN;
+ case SDLK_F1: return Common::KEYCODE_F1;
+ case SDLK_F2: return Common::KEYCODE_F2;
+ case SDLK_F3: return Common::KEYCODE_F3;
+ case SDLK_F4: return Common::KEYCODE_F4;
+ case SDLK_F5: return Common::KEYCODE_F5;
+ case SDLK_F6: return Common::KEYCODE_F6;
+ case SDLK_F7: return Common::KEYCODE_F7;
+ case SDLK_F8: return Common::KEYCODE_F8;
+ case SDLK_F9: return Common::KEYCODE_F9;
+ case SDLK_F10: return Common::KEYCODE_F10;
+ case SDLK_F11: return Common::KEYCODE_F11;
+ case SDLK_F12: return Common::KEYCODE_F12;
+ case SDLK_F13: return Common::KEYCODE_F13;
+ case SDLK_F14: return Common::KEYCODE_F14;
+ case SDLK_F15: return Common::KEYCODE_F15;
+ case SDLK_CAPSLOCK: return Common::KEYCODE_CAPSLOCK;
+ case SDLK_RSHIFT: return Common::KEYCODE_RSHIFT;
+ case SDLK_LSHIFT: return Common::KEYCODE_LSHIFT;
+ case SDLK_RCTRL: return Common::KEYCODE_RCTRL;
+ case SDLK_LCTRL: return Common::KEYCODE_LCTRL;
+ case SDLK_RALT: return Common::KEYCODE_RALT;
+ case SDLK_LALT: return Common::KEYCODE_LALT;
+ case SDLK_MODE: return Common::KEYCODE_MODE;
+ case SDLK_HELP: return Common::KEYCODE_HELP;
+ case SDLK_SYSREQ: return Common::KEYCODE_SYSREQ;
+ case SDLK_MENU: return Common::KEYCODE_MENU;
+ case SDLK_POWER: return Common::KEYCODE_POWER;
+#if SDL_VERSION_ATLEAST(1, 2, 3)
+ case SDLK_UNDO: return Common::KEYCODE_UNDO;
+#endif
+ case SDLK_SCROLLOCK: return Common::KEYCODE_SCROLLOCK;
+ case SDLK_NUMLOCK: return Common::KEYCODE_NUMLOCK;
+ case SDLK_LSUPER: return Common::KEYCODE_LSUPER;
+ case SDLK_RSUPER: return Common::KEYCODE_RSUPER;
+ case SDLK_PRINT: return Common::KEYCODE_PRINT;
+ case SDLK_COMPOSE: return Common::KEYCODE_COMPOSE;
+ case SDLK_KP0: return Common::KEYCODE_KP0;
+ case SDLK_KP1: return Common::KEYCODE_KP1;
+ case SDLK_KP2: return Common::KEYCODE_KP2;
+ case SDLK_KP3: return Common::KEYCODE_KP3;
+ case SDLK_KP4: return Common::KEYCODE_KP4;
+ case SDLK_KP5: return Common::KEYCODE_KP5;
+ case SDLK_KP6: return Common::KEYCODE_KP6;
+ case SDLK_KP7: return Common::KEYCODE_KP7;
+ case SDLK_KP8: return Common::KEYCODE_KP8;
+ case SDLK_KP9: return Common::KEYCODE_KP9;
+ case SDLK_WORLD_16: return Common::KEYCODE_TILDE;
+ case SDLK_BREAK: return Common::KEYCODE_BREAK;
+ case SDLK_LMETA: return Common::KEYCODE_LMETA;
+ case SDLK_RMETA: return Common::KEYCODE_RMETA;
+ case SDLK_EURO: return Common::KEYCODE_EURO;
+ default: return Common::KEYCODE_INVALID;
+ }
+}
+
+bool SdlEventSource::pollEvent(Common::Event &event) {
+ // If the screen changed, send an Common::EVENT_SCREEN_CHANGED
+ int screenID = g_system->getScreenChangeID();
+ if (screenID != _lastScreenID) {
+ _lastScreenID = screenID;
+ event.type = Common::EVENT_SCREEN_CHANGED;
+ return true;
+ }
+
+ if (_queuedFakeMouseMove) {
+ event = _fakeMouseMove;
+ _queuedFakeMouseMove = false;
+ return true;
+ }
+
+ SDL_Event ev;
+ while (SDL_PollEvent(&ev)) {
+ preprocessEvents(&ev);
+ if (dispatchSDLEvent(ev, event))
+ return true;
+ }
+
+ return false;
+}
+
+bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
+ switch (ev.type) {
+ case SDL_KEYDOWN:
+ return handleKeyDown(ev, event);
+ case SDL_KEYUP:
+ return handleKeyUp(ev, event);
+ case SDL_MOUSEMOTION:
+ return handleMouseMotion(ev, event);
+ case SDL_MOUSEBUTTONDOWN:
+ return handleMouseButtonDown(ev, event);
+ case SDL_MOUSEBUTTONUP:
+ return handleMouseButtonUp(ev, event);
+ case SDL_SYSWMEVENT:
+ return handleSysWMEvent(ev, event);
+ case SDL_VIDEOEXPOSE:
+ if (_graphicsManager) {
+ _graphicsManager->notifyVideoExpose();
+ }
+ return false;
+
+ case SDL_VIDEORESIZE:
+ return handleResizeEvent(event, ev.resize.w, ev.resize.h);
+ case SDL_QUIT:
+ event.type = Common::EVENT_QUIT;
+ return true;
+
+ default:
+ break;
+ }
+
+ if (_joystick) {
+ switch (ev.type) {
+ case SDL_JOYBUTTONDOWN:
+ return handleJoyButtonDown(ev, event);
+ case SDL_JOYBUTTONUP:
+ return handleJoyButtonUp(ev, event);
+ case SDL_JOYAXISMOTION:
+ return handleJoyAxisMotion(ev, event);
+ case SDL_JOYHATMOTION:
+ return handleJoyHatMotion(ev, event);
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
+
+bool SdlEventSource::handleKeyDown(SDL_Event &ev, Common::Event &event) {
+
+ SDLModToOSystemKeyFlags(SDL_GetModState(), event);
+
+ SDL_Keycode sdlKeycode = obtainKeycode(ev.key.keysym);
+ Common::KeyCode key = SDLToOSystemKeycode(sdlKeycode);
+
+ // Handle scroll lock as a key modifier
+ if (key == Common::KEYCODE_SCROLLOCK)
+ _scrollLock = !_scrollLock;
+
+ if (_scrollLock)
+ event.kbd.flags |= Common::KBD_SCRL;
+
+ if (remapKey(ev, event))
+ return true;
+
+ event.type = Common::EVENT_KEYDOWN;
+ event.kbd.keycode = key;
+
+ SDL_Keymod mod = (SDL_Keymod)ev.key.keysym.mod;
+#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.
+ if (event.kbd.keycode >= Common::KEYCODE_KP0 && event.kbd.keycode <= Common::KEYCODE_KP9) {
+ event.kbd.flags |= Common::KBD_NUM;
+ mod = SDL_Keymod(mod | KMOD_NUM);
+ }
+#endif
+ event.kbd.ascii = mapKey(sdlKeycode, mod, ev.key.keysym.unicode);
+
+ return true;
+}
+
+bool SdlEventSource::handleKeyUp(SDL_Event &ev, Common::Event &event) {
+ if (remapKey(ev, event))
+ return true;
+
+ SDLModToOSystemKeyFlags(SDL_GetModState(), event);
+
+ SDL_Keycode sdlKeycode = obtainKeycode(ev.key.keysym);
+
+ // Set the scroll lock sticky flag
+ if (_scrollLock)
+ event.kbd.flags |= Common::KBD_SCRL;
+
+ event.type = Common::EVENT_KEYUP;
+ event.kbd.keycode = SDLToOSystemKeycode(sdlKeycode);
+
+ SDL_Keymod mod = (SDL_Keymod)ev.key.keysym.mod;
+#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.
+ if (event.kbd.keycode >= Common::KEYCODE_KP0 && event.kbd.keycode <= Common::KEYCODE_KP9) {
+ event.kbd.flags |= Common::KBD_NUM;
+ mod = SDL_Keymod(mod | KMOD_NUM);
+ }
+#endif
+ event.kbd.ascii = mapKey(sdlKeycode, mod, 0);
+
+ return true;
+}
+
+void SdlEventSource::openJoystick(int joystickIndex) {
+ if (SDL_NumJoysticks() > joystickIndex) {
+ _joystick = SDL_JoystickOpen(joystickIndex);
+ debug("Using joystick: %s",
+ SDL_JoystickName(joystickIndex)
+ );
+ } else {
+ debug(5, "Invalid joystick: %d", joystickIndex);
+ }
+}
+
+void SdlEventSource::closeJoystick() {
+ if (_joystick) {
+ SDL_JoystickClose(_joystick);
+ _joystick = nullptr;
+ }
+}
+
+bool SdlEventSource::isJoystickConnected() const {
+ return _joystick;
+}
+
+SDL_Keycode SdlEventSource::obtainKeycode(const SDL_Keysym keySym) {
+#ifdef WIN32
+ // WORKAROUND: SDL 1.2 on Windows does not use the user configured keyboard layout,
+ // resulting in "keySym.sym" values to always be those expected for an US keyboard.
+ // For example, SDL returns SDLK_Q when pressing the 'A' key on an AZERTY keyboard.
+ // This defeats the purpose of keycodes which is to be able to refer to a key without
+ // knowing where it is physically located.
+ // We work around this issue by querying the currently active Windows keyboard layout
+ // using the scancode provided by SDL.
+
+ if (keySym.sym >= SDLK_0 && keySym.sym <= SDLK_9) {
+ // The keycode returned by SDL is kept for the number keys.
+ // Querying the keyboard layout for those would return the base key values
+ // for AZERTY keyboards, which are not numbers. For example, SDLK_1 would
+ // map to SDLK_AMPERSAND. This is theoretically correct but practically unhelpful,
+ // because it makes it impossible to handle key combinations such as "ctrl-1".
+ return keySym.sym;
+ }
+
+ int vk = MapVirtualKey(keySym.scancode, MAPVK_VSC_TO_VK);
+ if (vk) {
+ int ch = (MapVirtualKey(vk, MAPVK_VK_TO_CHAR) & 0x7FFF);
+ // The top bit of the result of MapVirtualKey with MAPVK_VSC_TO_VK signals
+ // a dead key was pressed. In that case we keep the value of the accent alone.
+ if (ch) {
+ if (ch >= 'A' && ch <= 'Z') {
+ // Windows returns uppercase ASCII whereas SDL expects lowercase
+ return (SDL_Keycode)(SDLK_a + (ch - 'A'));
+ } else {
+ return (SDL_Keycode)ch;
+ }
+ }
+ }
+#endif
+
+ return keySym.sym;
+}
+
+#endif
diff --git a/backends/events/sdl/sdl-events.cpp b/backends/events/sdl/sdl2-events.cpp
similarity index 78%
rename from backends/events/sdl/sdl-events.cpp
rename to backends/events/sdl/sdl2-events.cpp
index 3cd124e6dfa..af2a0acb60d 100644
--- a/backends/events/sdl/sdl-events.cpp
+++ b/backends/events/sdl/sdl2-events.cpp
@@ -32,11 +32,10 @@
#include "engines/engine.h"
#include "gui/gui-manager.h"
-#if defined(USE_IMGUI) && SDL_VERSION_ATLEAST(2, 0, 0)
+#if defined(USE_IMGUI)
#include "backends/imgui/backends/imgui_impl_sdl2.h"
#endif
-#if SDL_VERSION_ATLEAST(2, 0, 0)
#define GAMECONTROLLERDB_FILE "gamecontrollerdb.txt"
static uint32 convUTF8ToUTF32(const char *src) {
@@ -72,30 +71,25 @@ void SdlEventSource::loadGameControllerMappingFile() {
}
}
}
-#endif
SdlEventSource::SdlEventSource()
: EventSource(), _scrollLock(false), _joystick(nullptr), _lastScreenID(0), _graphicsManager(nullptr), _queuedFakeMouseMove(false),
_lastHatPosition(SDL_HAT_CENTERED), _mouseX(0), _mouseY(0), _engineRunning(false)
-#if SDL_VERSION_ATLEAST(2, 0, 0)
, _queuedFakeKeyUp(false), _fakeKeyUp(), _controller(nullptr)
-#endif
{
int joystick_num = ConfMan.getInt("joystick_num");
if (joystick_num >= 0) {
// Initialize SDL joystick subsystem
- if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) {
+ if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) {
warning("Could not initialize SDL joystick: %s", SDL_GetError());
return;
}
-#if SDL_VERSION_ATLEAST(2, 0, 0)
- if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == -1) {
+ if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0) {
warning("Could not initialize SDL game controller: %s", SDL_GetError());
return;
}
loadGameControllerMappingFile();
-#endif
openJoystick(joystick_num);
}
@@ -107,10 +101,6 @@ SdlEventSource::SdlEventSource()
}
-SdlEventSource::~SdlEventSource() {
- closeJoystick();
-}
-
int SdlEventSource::mapKey(SDL_Keycode sdlKey, SDL_Keymod mod, Uint16 unicode) {
Common::KeyCode key = SDLToOSystemKeycode(sdlKey);
@@ -177,39 +167,18 @@ int SdlEventSource::mapKey(SDL_Keycode sdlKey, SDL_Keymod mod, Uint16 unicode) {
}
}
-bool SdlEventSource::processMouseEvent(Common::Event &event, int x, int y, int relx, int rely) {
- _mouseX = x;
- _mouseY = y;
-
- event.mouse.x = x;
- event.mouse.y = y;
- event.relMouse.x = relx;
- event.relMouse.y = rely;
-
- if (_graphicsManager) {
- return _graphicsManager->notifyMousePosition(event.mouse);
- }
-
- return true;
-}
-
void SdlEventSource::SDLModToOSystemKeyFlags(SDL_Keymod mod, Common::Event &event) {
event.kbd.flags = 0;
+ if (mod & KMOD_GUI)
+ event.kbd.flags |= Common::KBD_META;
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;
-#if SDL_VERSION_ATLEAST(2, 0, 0)
- if (mod & KMOD_GUI)
- event.kbd.flags |= Common::KBD_META;
-#else
- if (mod & KMOD_META)
- event.kbd.flags |= Common::KBD_META;
-#endif
// Sticky flags
if (mod & KMOD_NUM)
@@ -337,7 +306,6 @@ Common::KeyCode SdlEventSource::SDLToOSystemKeycode(const SDL_Keycode key) {
#if SDL_VERSION_ATLEAST(1, 2, 3)
case SDLK_UNDO: return Common::KEYCODE_UNDO;
#endif
-#if SDL_VERSION_ATLEAST(2, 0, 0)
case SDLK_SCROLLLOCK: return Common::KEYCODE_SCROLLOCK;
case SDLK_NUMLOCKCLEAR: return Common::KEYCODE_NUMLOCK;
case SDLK_LGUI: return Common::KEYCODE_LSUPER;
@@ -360,13 +328,17 @@ 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;
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;
case SDLK_CUT: return Common::KEYCODE_CUT;
case SDLK_COPY: return Common::KEYCODE_COPY;
case SDLK_PASTE: return Common::KEYCODE_PASTE;
@@ -379,43 +351,14 @@ 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)
case SDLK_AUDIOREWIND: return Common::KEYCODE_AUDIOREWIND;
case SDLK_AUDIOFASTFORWARD: return Common::KEYCODE_AUDIOFASTFORWARD;
-#endif
-#else
- case SDLK_SCROLLOCK: return Common::KEYCODE_SCROLLOCK;
- case SDLK_NUMLOCK: return Common::KEYCODE_NUMLOCK;
- case SDLK_LSUPER: return Common::KEYCODE_LSUPER;
- case SDLK_RSUPER: return Common::KEYCODE_RSUPER;
- case SDLK_PRINT: return Common::KEYCODE_PRINT;
- case SDLK_COMPOSE: return Common::KEYCODE_COMPOSE;
- case SDLK_KP0: return Common::KEYCODE_KP0;
- case SDLK_KP1: return Common::KEYCODE_KP1;
- case SDLK_KP2: return Common::KEYCODE_KP2;
- case SDLK_KP3: return Common::KEYCODE_KP3;
- case SDLK_KP4: return Common::KEYCODE_KP4;
- case SDLK_KP5: return Common::KEYCODE_KP5;
- case SDLK_KP6: return Common::KEYCODE_KP6;
- case SDLK_KP7: return Common::KEYCODE_KP7;
- case SDLK_KP8: return Common::KEYCODE_KP8;
- case SDLK_KP9: return Common::KEYCODE_KP9;
- case SDLK_WORLD_16: return Common::KEYCODE_TILDE;
- case SDLK_BREAK: return Common::KEYCODE_BREAK;
- case SDLK_LMETA: return Common::KEYCODE_LMETA;
- case SDLK_RMETA: return Common::KEYCODE_RMETA;
- case SDLK_EURO: return Common::KEYCODE_EURO;
#endif
default: return Common::KEYCODE_INVALID;
}
}
-#if SDL_VERSION_ATLEAST(2, 0, 0)
void SdlEventSource::preprocessFingerDown(SDL_Event *event) {
// front (1) or back (2) panel
SDL_TouchID port = event->tfinger.touchId;
@@ -452,28 +395,6 @@ void SdlEventSource::preprocessFingerDown(SDL_Event *event) {
}
}
-Common::Point SdlEventSource::getTouchscreenSize() {
- int windowWidth, windowHeight;
- SDL_GetWindowSize((dynamic_cast<SdlGraphicsManager*>(_graphicsManager))->getWindow()->getSDLWindow(), &windowWidth, &windowHeight);
- return Common::Point(windowWidth, windowHeight);
-}
-
-bool SdlEventSource::isTouchPortTouchpadMode(SDL_TouchID port) {
- return g_system->getFeatureState(OSystem::kFeatureTouchpadMode);
-}
-
-bool SdlEventSource::isTouchPortActive(SDL_TouchID port) {
- return true;
-}
-
-void SdlEventSource::convertTouchXYToGameXY(float touchX, float touchY, int *gameX, int *gameY) {
- int windowWidth, windowHeight;
- SDL_GetWindowSize((dynamic_cast<SdlGraphicsManager*>(_graphicsManager))->getWindow()->getSDLWindow(), &windowWidth, &windowHeight);
-
- *gameX = windowWidth * touchX;
- *gameY = windowHeight * touchY;
-}
-
void SdlEventSource::finishSimulatedMouseClicks() {
for (auto &panel : _touchPanels) {
for (int i = 0; i < 2; i++) {
@@ -707,10 +628,8 @@ void SdlEventSource::preprocessFingerMotion(SDL_Event *event) {
}
}
}
-#endif
bool SdlEventSource::pollEvent(Common::Event &event) {
-#if SDL_VERSION_ATLEAST(2, 0, 0)
finishSimulatedMouseClicks();
// In case we still need to send a key up event for a key down from a
@@ -720,7 +639,6 @@ bool SdlEventSource::pollEvent(Common::Event &event) {
_queuedFakeKeyUp = false;
return true;
}
-#endif
// If the screen changed, send an Common::EVENT_SCREEN_CHANGED
int screenID = g_system->getScreenChangeID();
@@ -740,7 +658,6 @@ bool SdlEventSource::pollEvent(Common::Event &event) {
while (SDL_PollEvent(&ev)) {
preprocessEvents(&ev);
-#if 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
@@ -766,9 +683,8 @@ bool SdlEventSource::pollEvent(Common::Event &event) {
}
}
}
-#endif
-#if defined(USE_IMGUI) && SDL_VERSION_ATLEAST(2, 0, 0)
+#if defined(USE_IMGUI)
if (ImGui_ImplSDL2_Ready()) {
ImGui_ImplSDL2_ProcessEvent(&ev);
ImGuiIO &io = ImGui::GetIO();
@@ -798,7 +714,6 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
case SDL_SYSWMEVENT:
return handleSysWMEvent(ev, event);
-#if SDL_VERSION_ATLEAST(2, 0, 0)
case SDL_MOUSEWHEEL: {
Sint32 yDir = ev.wheel.y;
// We want the mouse coordinates supplied with a mouse wheel event.
@@ -849,6 +764,7 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
}
switch (ev.window.event) {
+
case SDL_WINDOWEVENT_EXPOSED:
if (_graphicsManager) {
_graphicsManager->notifyVideoExpose();
@@ -907,16 +823,6 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
case SDL_CLIPBOARDUPDATE:
event.type = Common::EVENT_CLIPBOARD_UPDATE;
return true;
-#else
- case SDL_VIDEOEXPOSE:
- if (_graphicsManager) {
- _graphicsManager->notifyVideoExpose();
- }
- return false;
-
- case SDL_VIDEORESIZE:
- return handleResizeEvent(event, ev.resize.w, ev.resize.h);
-#endif
case SDL_QUIT:
event.type = Common::EVENT_QUIT;
@@ -941,7 +847,6 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
}
}
-#if SDL_VERSION_ATLEAST(2, 0, 0)
if (_controller) {
switch (ev.type) {
case SDL_CONTROLLERBUTTONDOWN:
@@ -954,8 +859,6 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
break;
}
}
-#endif
-
return false;
}
@@ -989,11 +892,9 @@ 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;
-#endif
return true;
}
@@ -1014,6 +915,7 @@ bool SdlEventSource::handleKeyUp(SDL_Event &ev, Common::Event &event) {
event.kbd.keycode = SDLToOSystemKeycode(sdlKeycode);
SDL_Keymod mod = (SDL_Keymod)ev.key.keysym.mod;
+
#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.
@@ -1027,85 +929,14 @@ bool SdlEventSource::handleKeyUp(SDL_Event &ev, Common::Event &event) {
return true;
}
-bool SdlEventSource::handleMouseMotion(SDL_Event &ev, Common::Event &event) {
- event.type = Common::EVENT_MOUSEMOVE;
-
- return processMouseEvent(event, ev.motion.x, ev.motion.y, ev.motion.xrel, ev.motion.yrel);
-}
-
-bool SdlEventSource::handleMouseButtonDown(SDL_Event &ev, Common::Event &event) {
- if (ev.button.button == SDL_BUTTON_LEFT)
- event.type = Common::EVENT_LBUTTONDOWN;
- else if (ev.button.button == SDL_BUTTON_RIGHT)
- event.type = Common::EVENT_RBUTTONDOWN;
-#if defined(SDL_BUTTON_WHEELUP) && defined(SDL_BUTTON_WHEELDOWN)
- else if (ev.button.button == SDL_BUTTON_WHEELUP)
- event.type = Common::EVENT_WHEELUP;
- else if (ev.button.button == SDL_BUTTON_WHEELDOWN)
- event.type = Common::EVENT_WHEELDOWN;
-#endif
-#if defined(SDL_BUTTON_MIDDLE)
- else if (ev.button.button == SDL_BUTTON_MIDDLE)
- event.type = Common::EVENT_MBUTTONDOWN;
-#endif
-#if defined(SDL_BUTTON_X1)
- else if (ev.button.button == SDL_BUTTON_X1)
- event.type = Common::EVENT_X1BUTTONDOWN;
-#endif
-#if defined(SDL_BUTTON_X2)
- else if (ev.button.button == SDL_BUTTON_X2)
- event.type = Common::EVENT_X2BUTTONDOWN;
-#endif
- else
- return false;
-
- return processMouseEvent(event, ev.button.x, ev.button.y);
-}
-
-bool SdlEventSource::handleMouseButtonUp(SDL_Event &ev, Common::Event &event) {
- if (ev.button.button == SDL_BUTTON_LEFT)
- event.type = Common::EVENT_LBUTTONUP;
- else if (ev.button.button == SDL_BUTTON_RIGHT)
- event.type = Common::EVENT_RBUTTONUP;
-#if defined(SDL_BUTTON_MIDDLE)
- else if (ev.button.button == SDL_BUTTON_MIDDLE)
- event.type = Common::EVENT_MBUTTONUP;
-#endif
-#if defined(SDL_BUTTON_X1)
- else if (ev.button.button == SDL_BUTTON_X1)
- event.type = Common::EVENT_X1BUTTONUP;
-#endif
-#if defined(SDL_BUTTON_X2)
- else if (ev.button.button == SDL_BUTTON_X2)
- event.type = Common::EVENT_X2BUTTONUP;
-#endif
- else
- return false;
-
- return processMouseEvent(event, ev.button.x, ev.button.y);
-}
-
-bool SdlEventSource::handleSysWMEvent(SDL_Event &ev, Common::Event &event) {
- return false;
-}
-
void SdlEventSource::openJoystick(int joystickIndex) {
if (SDL_NumJoysticks() > joystickIndex) {
-#if SDL_VERSION_ATLEAST(2, 0, 0)
if (SDL_IsGameController(joystickIndex)) {
_controller = SDL_GameControllerOpen(joystickIndex);
debug("Using game controller: %s", SDL_GameControllerName(_controller));
- } else
-#endif
- {
+ } else {
_joystick = SDL_JoystickOpen(joystickIndex);
- debug("Using joystick: %s",
-#if SDL_VERSION_ATLEAST(2, 0, 0)
- SDL_JoystickName(_joystick)
-#else
- SDL_JoystickName(joystickIndex)
-#endif
- );
+ debug("Using joystick: %s", SDL_JoystickName(_joystick));
}
} else {
debug(5, "Invalid joystick: %d", joystickIndex);
@@ -1113,102 +944,16 @@ void SdlEventSource::openJoystick(int joystickIndex) {
}
void SdlEventSource::closeJoystick() {
-#if SDL_VERSION_ATLEAST(2, 0, 0)
if (_controller) {
SDL_GameControllerClose(_controller);
_controller = nullptr;
}
-#endif
if (_joystick) {
SDL_JoystickClose(_joystick);
_joystick = nullptr;
}
}
-int SdlEventSource::mapSDLJoystickButtonToOSystem(Uint8 sdlButton) {
- Common::JoystickButton osystemButtons[] = {
- Common::JOYSTICK_BUTTON_A,
- Common::JOYSTICK_BUTTON_B,
- Common::JOYSTICK_BUTTON_X,
- Common::JOYSTICK_BUTTON_Y,
- Common::JOYSTICK_BUTTON_LEFT_SHOULDER,
- Common::JOYSTICK_BUTTON_RIGHT_SHOULDER,
- Common::JOYSTICK_BUTTON_BACK,
- Common::JOYSTICK_BUTTON_START,
- Common::JOYSTICK_BUTTON_LEFT_STICK,
- Common::JOYSTICK_BUTTON_RIGHT_STICK
- };
-
- if (sdlButton >= ARRAYSIZE(osystemButtons)) {
- return -1;
- }
-
- return osystemButtons[sdlButton];
-}
-
-bool SdlEventSource::handleJoyButtonDown(SDL_Event &ev, Common::Event &event) {
- int button = mapSDLJoystickButtonToOSystem(ev.jbutton.button);
- if (button < 0) {
- return false;
- }
-
- event.type = Common::EVENT_JOYBUTTON_DOWN;
- event.joystick.button = button;
-
- return true;
-}
-
-bool SdlEventSource::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) {
- int button = mapSDLJoystickButtonToOSystem(ev.jbutton.button);
- if (button < 0) {
- return false;
- }
-
- event.type = Common::EVENT_JOYBUTTON_UP;
- event.joystick.button = button;
-
- return true;
-}
-
-bool SdlEventSource::handleJoyAxisMotion(SDL_Event &ev, Common::Event &event) {
- event.type = Common::EVENT_JOYAXIS_MOTION;
- event.joystick.axis = ev.jaxis.axis;
- event.joystick.position = ev.jaxis.value;
-
- return true;
-}
-
-#define HANDLE_HAT_UP(new, old, mask, joybutton) \
- if ((old & mask) && !(new & mask)) { \
- event.joystick.button = joybutton; \
- g_system->getEventManager()->pushEvent(event); \
- }
-
-#define HANDLE_HAT_DOWN(new, old, mask, joybutton) \
- if ((new & mask) && !(old & mask)) { \
- event.joystick.button = joybutton; \
- g_system->getEventManager()->pushEvent(event); \
- }
-
-bool SdlEventSource::handleJoyHatMotion(SDL_Event &ev, Common::Event &event) {
- event.type = Common::EVENT_JOYBUTTON_UP;
- HANDLE_HAT_UP(ev.jhat.value, _lastHatPosition, SDL_HAT_UP, Common::JOYSTICK_BUTTON_DPAD_UP)
- HANDLE_HAT_UP(ev.jhat.value, _lastHatPosition, SDL_HAT_DOWN, Common::JOYSTICK_BUTTON_DPAD_DOWN)
- HANDLE_HAT_UP(ev.jhat.value, _lastHatPosition, SDL_HAT_LEFT, Common::JOYSTICK_BUTTON_DPAD_LEFT)
- HANDLE_HAT_UP(ev.jhat.value, _lastHatPosition, SDL_HAT_RIGHT, Common::JOYSTICK_BUTTON_DPAD_RIGHT)
-
- event.type = Common::EVENT_JOYBUTTON_DOWN;
- HANDLE_HAT_DOWN(ev.jhat.value, _lastHatPosition, SDL_HAT_UP, Common::JOYSTICK_BUTTON_DPAD_UP)
- HANDLE_HAT_DOWN(ev.jhat.value, _lastHatPosition, SDL_HAT_DOWN, Common::JOYSTICK_BUTTON_DPAD_DOWN)
- HANDLE_HAT_DOWN(ev.jhat.value, _lastHatPosition, SDL_HAT_LEFT, Common::JOYSTICK_BUTTON_DPAD_LEFT)
- HANDLE_HAT_DOWN(ev.jhat.value, _lastHatPosition, SDL_HAT_RIGHT, Common::JOYSTICK_BUTTON_DPAD_RIGHT)
-
- _lastHatPosition = ev.jhat.value;
-
- return false;
-}
-
-#if SDL_VERSION_ATLEAST(2, 0, 0)
bool SdlEventSource::handleJoystickAdded(const SDL_JoyDeviceEvent &device, Common::Event &event) {
debug(5, "SdlEventSource: Received joystick added event for index '%d'", device.which);
@@ -1280,7 +1025,6 @@ int SdlEventSource::mapSDLControllerButtonToOSystem(Uint8 sdlButton) {
bool SdlEventSource::handleControllerButton(const SDL_Event &ev, Common::Event &event, bool buttonUp) {
int button = mapSDLControllerButtonToOSystem(ev.cbutton.button);
-
if (button < 0)
return false;
@@ -1291,92 +1035,22 @@ 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;
event.joystick.axis = ev.caxis.axis;
event.joystick.position = ev.caxis.value;
+ event.type = Common::EVENT_JOYAXIS_MOTION;
return true;
}
-#endif
-
-bool SdlEventSource::remapKey(SDL_Event &ev, Common::Event &event) {
- return false;
-}
-
-void SdlEventSource::fakeWarpMouse(const int x, const int y) {
- _queuedFakeMouseMove = true;
- _fakeMouseMove.type = Common::EVENT_MOUSEMOVE;
- _fakeMouseMove.mouse = Common::Point(x, y);
-}
bool SdlEventSource::isJoystickConnected() const {
- return _joystick
-#if SDL_VERSION_ATLEAST(2, 0, 0)
- || _controller
-#endif
- ;
-}
-
-void SdlEventSource::setEngineRunning(const bool value) {
- _engineRunning = value;
-}
-
-bool SdlEventSource::handleResizeEvent(Common::Event &event, int w, int h) {
- if (_graphicsManager) {
- _graphicsManager->notifyResize(w, h);
-
- // If the screen changed, send an Common::EVENT_SCREEN_CHANGED
- int screenID = g_system->getScreenChangeID();
- if (screenID != _lastScreenID) {
- _lastScreenID = screenID;
- event.type = Common::EVENT_SCREEN_CHANGED;
- return true;
- }
- }
-
- return false;
+ return _joystick || _controller;
}
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,
- // resulting in "keySym.sym" values to always be those expected for an US keyboard.
- // For example, SDL returns SDLK_Q when pressing the 'A' key on an AZERTY keyboard.
- // This defeats the purpose of keycodes which is to be able to refer to a key without
- // knowing where it is physically located.
- // We work around this issue by querying the currently active Windows keyboard layout
- // using the scancode provided by SDL.
-
- if (keySym.sym >= SDLK_0 && keySym.sym <= SDLK_9) {
- // The keycode returned by SDL is kept for the number keys.
- // Querying the keyboard layout for those would return the base key values
- // for AZERTY keyboards, which are not numbers. For example, SDLK_1 would
- // map to SDLK_AMPERSAND. This is theoretically correct but practically unhelpful,
- // because it makes it impossible to handle key combinations such as "ctrl-1".
- return keySym.sym;
- }
-
- int vk = MapVirtualKey(keySym.scancode, MAPVK_VSC_TO_VK);
- if (vk) {
- int ch = (MapVirtualKey(vk, MAPVK_VK_TO_CHAR) & 0x7FFF);
- // The top bit of the result of MapVirtualKey with MAPVK_VSC_TO_VK signals
- // a dead key was pressed. In that case we keep the value of the accent alone.
- if (ch) {
- if (ch >= 'A' && ch <= 'Z') {
- // Windows returns uppercase ASCII whereas SDL expects lowercase
- return (SDL_Keycode)(SDLK_a + (ch - 'A'));
- } else {
- return (SDL_Keycode)ch;
- }
- }
- }
-#endif
-
return keySym.sym;
}
-uint32 SdlEventSource::obtainUnicode(const SDL_Keysym keySym) {
-#if SDL_VERSION_ATLEAST(2, 0, 0)
+uint32 SdlEventSource::obtainUnicode(const SDL_KeyboardEvent &key) {
SDL_Event events[2];
// Update the event queue here to give SDL a chance to insert TEXTINPUT
@@ -1417,9 +1091,6 @@ uint32 SdlEventSource::obtainUnicode(const SDL_Keysym keySym) {
} else {
return 0;
}
-#else
- return keySym.unicode;
-#endif
}
#endif
diff --git a/backends/events/sdl/sdl3-events.cpp b/backends/events/sdl/sdl3-events.cpp
new file mode 100644
index 00000000000..c2f7080a49e
--- /dev/null
+++ b/backends/events/sdl/sdl3-events.cpp
@@ -0,0 +1,1052 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/scummsys.h"
+
+#if defined(SDL_BACKEND)
+
+#include "backends/events/sdl/sdl-events.h"
+#include "backends/platform/sdl/sdl.h"
+#include "backends/graphics/graphics.h"
+#include "common/config-manager.h"
+#include "common/textconsole.h"
+#include "common/fs.h"
+#include "engines/engine.h"
+#include "gui/gui-manager.h"
+
+#if defined(USE_IMGUI)
+#include "backends/imgui/backends/imgui_impl_sdl3.h"
+#endif
+
+#define GAMECONTROLLERDB_FILE "gamecontrollerdb.txt"
+
+static uint32 convUTF8ToUTF32(const char *src) {
+ if (!src || src[0] == 0)
+ return 0;
+
+ Common::U32String u32(src);
+ return u32[0];
+}
+
+void SdlEventSource::loadGameControllerMappingFile() {
+ bool loaded = false;
+ if (ConfMan.hasKey("controller_map_db")) {
+ Common::FSNode file = Common::FSNode(ConfMan.getPath("controller_map_db"));
+ if (file.exists()) {
+ if (!SDL_AddGamepadMappingsFromFile(file.getPath().toString(Common::Path::kNativeSeparator).c_str()))
+ error("File %s not valid: %s", file.getPath().toString(Common::Path::kNativeSeparator).c_str(), SDL_GetError());
+ else {
+ loaded = true;
+ debug("Game controller DB file loaded: %s", file.getPath().toString(Common::Path::kNativeSeparator).c_str());
+ }
+ } else
+ warning("Game controller DB file not found: %s", file.getPath().toString(Common::Path::kNativeSeparator).c_str());
+ }
+ if (!loaded && ConfMan.hasKey("extrapath")) {
+ Common::FSNode dir = Common::FSNode(ConfMan.getPath("extrapath"));
+ Common::FSNode file = dir.getChild(GAMECONTROLLERDB_FILE);
+ if (file.exists()) {
+ if (!SDL_AddGamepadMappingsFromFile(file.getPath().toString(Common::Path::kNativeSeparator).c_str()))
+ 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());
+ }
+ }
+}
+
+SdlEventSource::SdlEventSource()
+ : EventSource(), _scrollLock(false), _joystick(nullptr), _lastScreenID(0), _graphicsManager(nullptr), _queuedFakeMouseMove(false),
+ _lastHatPosition(SDL_HAT_CENTERED), _mouseX(0), _mouseY(0), _engineRunning(false)
+ , _queuedFakeKeyUp(false), _fakeKeyUp(), _controller(nullptr) {
+ int joystick_num = ConfMan.getInt("joystick_num");
+ if (joystick_num >= 0) {
+ // Initialize SDL joystick subsystem
+ if (!SDL_InitSubSystem(SDL_INIT_JOYSTICK)) {
+ warning("Could not initialize SDL joystick: %s", SDL_GetError());
+ return;
+ }
+
+ if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD)) {
+ warning("Could not initialize SDL game controller: %s", SDL_GetError());
+ return;
+ }
+ loadGameControllerMappingFile();
+
+ openJoystick(joystick_num);
+ }
+
+ // ensure that touch doesn't create double-events
+ SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
+}
+
+int SdlEventSource::mapKey(SDL_Keycode sdlKey, SDL_Keymod mod, Uint16 unicode) {
+ Common::KeyCode key = SDLToOSystemKeycode(sdlKey);
+
+ // Keep unicode in case it's regular ASCII text, Hebrew or in case we didn't get a valid keycode
+ //
+ // We need to use unicode in those cases, simply because SDL1.x passes us non-layout-adjusted keycodes.
+ // So unicode is the only way to get layout-adjusted keys.
+ if (unicode < 0x20) {
+ // don't use unicode, in case it's control characters
+ unicode = 0;
+ } else {
+ // Use unicode, in case keycode is invalid.
+ // 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 (mod & (SDL_KMOD_CTRL | SDL_KMOD_ALT)) {
+ // 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
+ // an alternate keyboard, which will cause us to receive Unicode characters for some keys, which are outside
+ // of the ASCII range (e.g. alt-x will get us U+2248). We need to return 'x' for alt-x, so using unicode
+ // in that case would break alt-shortcuts.
+ if (unicode > 0x7E)
+ unicode = 0; // do not allow any characters above 0x7E
+ } else {
+ // We allow Hebrew characters
+ if (unicode >= 0x05D0 && unicode <= 0x05EA)
+ return unicode;
+
+ // Cyrillic
+ if (unicode >= 0x0400 && unicode <= 0x045F)
+ return unicode;
+
+ // We must not restrict as much as when Ctrl/Alt-modifiers are active, otherwise
+ // we wouldn't let umlauts through for SDL1. For SDL1 umlauts may set for example KEYCODE_QUOTE, KEYCODE_MINUS, etc.
+ if (unicode > 0xFF)
+ unicode = 0; // do not allow any characters above 0xFF
+ }
+ }
+ }
+
+ 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 ((mod & SDL_KMOD_NUM) == 0)
+ 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) {
+ return key;
+ } else if (unicode) {
+ // Return unicode in case it's still set and wasn't filtered.
+ return unicode;
+ } else if (key >= 'a' && key <= 'z' && (mod & SDL_KMOD_SHIFT)) {
+ return key & ~0x20;
+ } else if (key >= Common::KEYCODE_NUMLOCK && key < Common::KEYCODE_LAST) {
+ return 0;
+ } else {
+ return key;
+ }
+}
+
+void SdlEventSource::SDLModToOSystemKeyFlags(SDL_Keymod mod, Common::Event &event) {
+
+ event.kbd.flags = 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 & SDL_KMOD_ALT)
+ event.kbd.flags |= Common::KBD_ALT;
+ 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;
+}
+
+Common::KeyCode SdlEventSource::SDLToOSystemKeycode(const SDL_Keycode key) {
+ switch (key) {
+ case SDLK_BACKSPACE: return Common::KEYCODE_BACKSPACE;
+ case SDLK_TAB: return Common::KEYCODE_TAB;
+ case SDLK_CLEAR: return Common::KEYCODE_CLEAR;
+ case SDLK_RETURN: return Common::KEYCODE_RETURN;
+ case SDLK_PAUSE: return Common::KEYCODE_PAUSE;
+ case SDLK_ESCAPE: return Common::KEYCODE_ESCAPE;
+ case SDLK_SPACE: return Common::KEYCODE_SPACE;
+ case SDLK_EXCLAIM: return Common::KEYCODE_EXCLAIM;
+ case SDLK_DBLAPOSTROPHE: return Common::KEYCODE_QUOTEDBL;
+ case SDLK_HASH: return Common::KEYCODE_HASH;
+ case SDLK_DOLLAR: return Common::KEYCODE_DOLLAR;
+ case SDLK_AMPERSAND: return Common::KEYCODE_AMPERSAND;
+ case SDLK_APOSTROPHE: return Common::KEYCODE_QUOTE;
+ case SDLK_LEFTPAREN: return Common::KEYCODE_LEFTPAREN;
+ case SDLK_RIGHTPAREN: return Common::KEYCODE_RIGHTPAREN;
+ case SDLK_ASTERISK: return Common::KEYCODE_ASTERISK;
+ case SDLK_PLUS: return Common::KEYCODE_PLUS;
+ case SDLK_COMMA: return Common::KEYCODE_COMMA;
+ case SDLK_MINUS: return Common::KEYCODE_MINUS;
+ case SDLK_PERIOD: return Common::KEYCODE_PERIOD;
+ case SDLK_SLASH: return Common::KEYCODE_SLASH;
+ case SDLK_0: return Common::KEYCODE_0;
+ case SDLK_1: return Common::KEYCODE_1;
+ case SDLK_2: return Common::KEYCODE_2;
+ case SDLK_3: return Common::KEYCODE_3;
+ case SDLK_4: return Common::KEYCODE_4;
+ case SDLK_5: return Common::KEYCODE_5;
+ case SDLK_6: return Common::KEYCODE_6;
+ case SDLK_7: return Common::KEYCODE_7;
+ case SDLK_8: return Common::KEYCODE_8;
+ case SDLK_9: return Common::KEYCODE_9;
+ case SDLK_COLON: return Common::KEYCODE_COLON;
+ case SDLK_SEMICOLON: return Common::KEYCODE_SEMICOLON;
+ case SDLK_LESS: return Common::KEYCODE_LESS;
+ case SDLK_EQUALS: return Common::KEYCODE_EQUALS;
+ case SDLK_GREATER: return Common::KEYCODE_GREATER;
+ case SDLK_QUESTION: return Common::KEYCODE_QUESTION;
+ case SDLK_AT: return Common::KEYCODE_AT;
+ case SDLK_LEFTBRACKET: return Common::KEYCODE_LEFTBRACKET;
+ case SDLK_BACKSLASH: return Common::KEYCODE_BACKSLASH;
+ case SDLK_RIGHTBRACKET: return Common::KEYCODE_RIGHTBRACKET;
+ case SDLK_CARET: return Common::KEYCODE_CARET;
+ case SDLK_UNDERSCORE: return Common::KEYCODE_UNDERSCORE;
+ 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;
+ 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;
+ case SDLK_KP_MULTIPLY: return Common::KEYCODE_KP_MULTIPLY;
+ case SDLK_KP_MINUS: return Common::KEYCODE_KP_MINUS;
+ case SDLK_KP_PLUS: return Common::KEYCODE_KP_PLUS;
+ case SDLK_KP_ENTER: return Common::KEYCODE_KP_ENTER;
+ case SDLK_KP_EQUALS: return Common::KEYCODE_KP_EQUALS;
+ case SDLK_UP: return Common::KEYCODE_UP;
+ case SDLK_DOWN: return Common::KEYCODE_DOWN;
+ case SDLK_RIGHT: return Common::KEYCODE_RIGHT;
+ case SDLK_LEFT: return Common::KEYCODE_LEFT;
+ case SDLK_INSERT: return Common::KEYCODE_INSERT;
+ case SDLK_HOME: return Common::KEYCODE_HOME;
+ case SDLK_END: return Common::KEYCODE_END;
+ case SDLK_PAGEUP: return Common::KEYCODE_PAGEUP;
+ case SDLK_PAGEDOWN: return Common::KEYCODE_PAGEDOWN;
+ case SDLK_F1: return Common::KEYCODE_F1;
+ case SDLK_F2: return Common::KEYCODE_F2;
+ case SDLK_F3: return Common::KEYCODE_F3;
+ case SDLK_F4: return Common::KEYCODE_F4;
+ case SDLK_F5: return Common::KEYCODE_F5;
+ case SDLK_F6: return Common::KEYCODE_F6;
+ case SDLK_F7: return Common::KEYCODE_F7;
+ case SDLK_F8: return Common::KEYCODE_F8;
+ case SDLK_F9: return Common::KEYCODE_F9;
+ case SDLK_F10: return Common::KEYCODE_F10;
+ case SDLK_F11: return Common::KEYCODE_F11;
+ case SDLK_F12: return Common::KEYCODE_F12;
+ case SDLK_F13: return Common::KEYCODE_F13;
+ case SDLK_F14: return Common::KEYCODE_F14;
+ case SDLK_F15: return Common::KEYCODE_F15;
+ case SDLK_CAPSLOCK: return Common::KEYCODE_CAPSLOCK;
+ case SDLK_RSHIFT: return Common::KEYCODE_RSHIFT;
+ case SDLK_LSHIFT: return Common::KEYCODE_LSHIFT;
+ case SDLK_RCTRL: return Common::KEYCODE_RCTRL;
+ case SDLK_LCTRL: return Common::KEYCODE_LCTRL;
+ case SDLK_RALT: return Common::KEYCODE_RALT;
+ case SDLK_LALT: return Common::KEYCODE_LALT;
+ case SDLK_MODE: return Common::KEYCODE_MODE;
+ case SDLK_HELP: return Common::KEYCODE_HELP;
+ case SDLK_SYSREQ: return Common::KEYCODE_SYSREQ;
+ case SDLK_MENU: return Common::KEYCODE_MENU;
+ case SDLK_POWER: return Common::KEYCODE_POWER;
+ case SDLK_UNDO: return Common::KEYCODE_UNDO;
+ case SDLK_SCROLLLOCK: return Common::KEYCODE_SCROLLOCK;
+ case SDLK_NUMLOCKCLEAR: return Common::KEYCODE_NUMLOCK;
+ case SDLK_LGUI: return Common::KEYCODE_LSUPER;
+ case SDLK_RGUI: return Common::KEYCODE_RSUPER;
+ case SDLK_PRINTSCREEN: return Common::KEYCODE_PRINT;
+ case SDLK_APPLICATION: return Common::KEYCODE_COMPOSE;
+ case SDLK_KP_0: return Common::KEYCODE_KP0;
+ case SDLK_KP_1: return Common::KEYCODE_KP1;
+ case SDLK_KP_2: return Common::KEYCODE_KP2;
+ case SDLK_KP_3: return Common::KEYCODE_KP3;
+ case SDLK_KP_4: return Common::KEYCODE_KP4;
+ case SDLK_KP_5: return Common::KEYCODE_KP5;
+ case SDLK_KP_6: return Common::KEYCODE_KP6;
+ case SDLK_KP_7: return Common::KEYCODE_KP7;
+ case SDLK_KP_8: return Common::KEYCODE_KP8;
+ case SDLK_KP_9: return Common::KEYCODE_KP9;
+ case SDLK_PERCENT: return Common::KEYCODE_PERCENT;
+ case SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_GRAVE): return Common::KEYCODE_TILDE;
+ case SDLK_F16: return Common::KEYCODE_F16;
+ case SDLK_F17: return Common::KEYCODE_F17;
+ case SDLK_F18: return Common::KEYCODE_F18;
+ case SDLK_SLEEP: return Common::KEYCODE_SLEEP;
+ case SDLK_VOLUMEUP: return Common::KEYCODE_VOLUMEUP;
+ case SDLK_VOLUMEDOWN: return Common::KEYCODE_VOLUMEDOWN;
+ 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;
+ case SDLK_CUT: return Common::KEYCODE_CUT;
+ case SDLK_COPY: return Common::KEYCODE_COPY;
+ case SDLK_PASTE: return Common::KEYCODE_PASTE;
+ case SDLK_SELECT: return Common::KEYCODE_SELECT;
+ case SDLK_CANCEL: return Common::KEYCODE_CANCEL;
+ case SDLK_AC_SEARCH: return Common::KEYCODE_AC_SEARCH;
+ case SDLK_AC_HOME: return Common::KEYCODE_AC_HOME;
+ case SDLK_AC_BACK: return Common::KEYCODE_AC_BACK;
+ case SDLK_AC_FORWARD: return Common::KEYCODE_AC_FORWARD;
+ 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_MEDIA_REWIND: return Common::KEYCODE_AUDIOREWIND;
+ case SDLK_MEDIA_FAST_FORWARD: return Common::KEYCODE_AUDIOFASTFORWARD;
+ default: return Common::KEYCODE_INVALID;
+ }
+}
+
+void SdlEventSource::preprocessFingerDown(SDL_Event *event) {
+ // front (1) or back (2) panel
+ SDL_TouchID port = event->tfinger.touchID;
+ // id (for multitouch)
+ SDL_FingerID id = event->tfinger.fingerID;
+
+ int x = _mouseX;
+ int y = _mouseY;
+
+ if (!isTouchPortTouchpadMode(port)) {
+ convertTouchXYToGameXY(event->tfinger.x, event->tfinger.y, &x, &y);
+ }
+
+ // make sure each finger is not reported down multiple times
+ for (int i = 0; i < MAX_NUM_FINGERS; i++) {
+ if (_touchPanels[port]._finger[i].id == id) {
+ _touchPanels[port]._finger[i].id = -1;
+ }
+ }
+
+ // we need the timestamps to decide later if the user performed a short tap (click)
+ // or a long tap (drag)
+ // we also need the last coordinates for each finger to keep track of dragging
+ for (int i = 0; i < MAX_NUM_FINGERS; i++) {
+ if (_touchPanels[port]._finger[i].id == -1) {
+ _touchPanels[port]._finger[i].id = id;
+ _touchPanels[port]._finger[i].timeLastDown = event->tfinger.timestamp;
+ _touchPanels[port]._finger[i].lastDownX = event->tfinger.x;
+ _touchPanels[port]._finger[i].lastDownY = event->tfinger.y;
+ _touchPanels[port]._finger[i].lastX = x;
+ _touchPanels[port]._finger[i].lastY = y;
+ break;
+ }
+ }
+}
+
+void SdlEventSource::finishSimulatedMouseClicks() {
+ for (auto &panel : _touchPanels) {
+ for (int i = 0; i < 2; i++) {
+ if (panel._value._simulatedClickStartTime[i] != 0) {
+ Uint32 currentTime = SDL_GetTicks();
+ if (currentTime - panel._value._simulatedClickStartTime[i] >= SIMULATED_CLICK_DURATION) {
+ int simulatedButton;
+ if (i == 0) {
+ simulatedButton = SDL_BUTTON_LEFT;
+ } else {
+ simulatedButton = SDL_BUTTON_RIGHT;
+ }
+ SDL_Event ev;
+ ev.type = SDL_EVENT_MOUSE_BUTTON_UP;
+ ev.button.button = simulatedButton;
+ ev.button.x = _mouseX;
+ ev.button.y = _mouseY;
+ SDL_PushEvent(&ev);
+
+ panel._value._simulatedClickStartTime[i] = 0;
+ }
+ }
+ }
+ }
+}
+
+bool SdlEventSource::preprocessFingerUp(SDL_Event *event, Common::Event *ev) {
+ // front (1) or back (2) panel
+ SDL_TouchID port = event->tfinger.touchID;
+ // id (for multitouch)
+ SDL_FingerID id = event->tfinger.fingerID;
+
+ // find out how many fingers were down before this event
+ int numFingersDown = 0;
+ for (int i = 0; i < MAX_NUM_FINGERS; i++) {
+ if (_touchPanels[port]._finger[i].id >= 0) {
+ numFingersDown++;
+ }
+ }
+
+ int x = _mouseX;
+ int y = _mouseY;
+
+ for (int i = 0; i < MAX_NUM_FINGERS; i++) {
+ if (_touchPanels[port]._finger[i].id == id) {
+ _touchPanels[port]._finger[i].id = -1;
+ if (!_touchPanels[port]._multiFingerDragging) {
+ if ((event->tfinger.timestamp - _touchPanels[port]._finger[i].timeLastDown) <= MAX_TAP_TIME && !_touchPanels[port]._tapMade) {
+ // short (<MAX_TAP_TIME ms) tap is interpreted as right/left mouse click depending on # fingers already down
+ // but only if the finger hasn't moved since it was pressed down by more than MAX_TAP_MOTION_DISTANCE pixels
+ Common::Point touchscreenSize = getTouchscreenSize();
+ float xrel = ((event->tfinger.x * (float) touchscreenSize.x) - (_touchPanels[port]._finger[i].lastDownX * (float) touchscreenSize.x));
+ float yrel = ((event->tfinger.y * (float) touchscreenSize.y) - (_touchPanels[port]._finger[i].lastDownY * (float) touchscreenSize.y));
+ float maxRSquared = (float) (MAX_TAP_MOTION_DISTANCE * MAX_TAP_MOTION_DISTANCE);
+ if ((xrel * xrel + yrel * yrel) < maxRSquared) {
+ if (numFingersDown == 3) {
+ _touchPanels[port]._tapMade = true;
+ ev->type = Common::EVENT_VIRTUAL_KEYBOARD;
+ return true;
+ } else if (numFingersDown == 2 || numFingersDown == 1) {
+ Uint8 simulatedButton = 0;
+ if (numFingersDown == 2) {
+ simulatedButton = SDL_BUTTON_RIGHT;
+ // need to raise the button later
+ _touchPanels[port]._simulatedClickStartTime[1] = event->tfinger.timestamp;
+ _touchPanels[port]._tapMade = true;
+ } else if (numFingersDown == 1) {
+ simulatedButton = SDL_BUTTON_LEFT;
+ // need to raise the button later
+ _touchPanels[port]._simulatedClickStartTime[0] = event->tfinger.timestamp;
+ if (!isTouchPortTouchpadMode(port)) {
+ convertTouchXYToGameXY(event->tfinger.x, event->tfinger.y, &x, &y);
+ }
+ }
+
+ event->type = SDL_EVENT_MOUSE_BUTTON_DOWN;
+ event->button.button = simulatedButton;
+ event->button.x = x;
+ event->button.y = y;
+ }
+ }
+ }
+ } else if (numFingersDown == 1) {
+ // when dragging, and the last finger is lifted, the drag is over
+ if (!isTouchPortTouchpadMode(port)) {
+ convertTouchXYToGameXY(event->tfinger.x, event->tfinger.y, &x, &y);
+ }
+ Uint8 simulatedButton = 0;
+ if (_touchPanels[port]._multiFingerDragging == DRAG_THREE_FINGER)
+ simulatedButton = SDL_BUTTON_RIGHT;
+ else {
+ simulatedButton = SDL_BUTTON_LEFT;
+ }
+ event->type = SDL_EVENT_MOUSE_BUTTON_UP;
+ event->button.button = simulatedButton;
+ event->button.x = x;
+ event->button.y = y;
+ _touchPanels[port]._multiFingerDragging = DRAG_NONE;
+ }
+ }
+ }
+
+ if (numFingersDown == 1) {
+ _touchPanels[port]._tapMade = false;
+ }
+
+ return false;
+}
+
+void SdlEventSource::preprocessFingerMotion(SDL_Event *event) {
+ // front (1) or back (2) panel
+ SDL_TouchID port = event->tfinger.touchID;
+ // id (for multitouch)
+ SDL_FingerID id = event->tfinger.fingerID;
+
+ // find out how many fingers were down before this event
+ int numFingersDown = 0;
+ for (int i = 0; i < MAX_NUM_FINGERS; i++) {
+ if (_touchPanels[port]._finger[i].id >= 0) {
+ numFingersDown++;
+ }
+ }
+
+ if (numFingersDown >= 1) {
+ int x = _mouseX;
+ int y = _mouseY;
+ int xMax = _graphicsManager->getWindowWidth() - 1;
+ int yMax = _graphicsManager->getWindowHeight() - 1;
+
+ if (!isTouchPortTouchpadMode(port)) {
+ convertTouchXYToGameXY(event->tfinger.x, event->tfinger.y, &x, &y);
+ } else {
+ // for relative mode, use the pointer speed setting
+ const int kbdMouseSpeed = CLIP<int>(ConfMan.getInt("kbdmouse_speed"), 0, 7);
+ float speedFactor = (kbdMouseSpeed + 1) * 0.25;
+
+ // convert touch events to relative mouse pointer events
+ // track sub-pixel relative finger motion using the FINGER_SUBPIXEL_MULTIPLIER
+ _touchPanels[port]._hiresDX += (event->tfinger.dx * 1.25 * speedFactor * xMax * FINGER_SUBPIXEL_MULTIPLIER);
+ _touchPanels[port]._hiresDY += (event->tfinger.dy * 1.25 * speedFactor * yMax * FINGER_SUBPIXEL_MULTIPLIER);
+ int xRel = _touchPanels[port]._hiresDX / FINGER_SUBPIXEL_MULTIPLIER;
+ int yRel = _touchPanels[port]._hiresDY / FINGER_SUBPIXEL_MULTIPLIER;
+ x = _mouseX + xRel;
+ y = _mouseY + yRel;
+ _touchPanels[port]._hiresDX %= FINGER_SUBPIXEL_MULTIPLIER;
+ _touchPanels[port]._hiresDY %= FINGER_SUBPIXEL_MULTIPLIER;
+ }
+
+ x = CLIP(x, 0, xMax);
+ y = CLIP(y, 0, yMax);
+
+ // update the current finger's coordinates so we can track it later
+ for (int i = 0; i < MAX_NUM_FINGERS; i++) {
+ if (_touchPanels[port]._finger[i].id == id) {
+ _touchPanels[port]._finger[i].lastX = x;
+ _touchPanels[port]._finger[i].lastY = y;
+ }
+ }
+
+ // If we are starting a multi-finger drag, start holding down the mouse button
+ if (numFingersDown >= 2) {
+ if (!_touchPanels[port]._multiFingerDragging) {
+ // only start a multi-finger drag if at least two fingers have been down long enough
+ int numFingersDownLong = 0;
+ for (int i = 0; i < MAX_NUM_FINGERS; i++) {
+ if (_touchPanels[port]._finger[i].id >= 0) {
+ if (event->tfinger.timestamp - _touchPanels[port]._finger[i].timeLastDown > MAX_TAP_TIME) {
+ numFingersDownLong++;
+ }
+ }
+ }
+ if (numFingersDownLong >= 2) {
+ // starting drag, so push mouse down at current location (back)
+ // or location of "oldest" finger (front)
+ int mouseDownX = _mouseX;
+ int mouseDownY = _mouseY;
+ if (!isTouchPortTouchpadMode(port)) {
+ for (int i = 0; i < MAX_NUM_FINGERS; i++) {
+ if (_touchPanels[port]._finger[i].id == id) {
+ Uint32 earliestTime = _touchPanels[port]._finger[i].timeLastDown;
+ for (int j = 0; j < MAX_NUM_FINGERS; j++) {
+ if (_touchPanels[port]._finger[j].id >= 0 && (i != j) ) {
+ if (_touchPanels[port]._finger[j].timeLastDown < earliestTime) {
+ mouseDownX = _touchPanels[port]._finger[j].lastX;
+ mouseDownY = _touchPanels[port]._finger[j].lastY;
+ earliestTime = _touchPanels[port]._finger[j].timeLastDown;
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ Uint8 simulatedButton = 0;
+ if (numFingersDownLong == 2) {
+ simulatedButton = SDL_BUTTON_LEFT;
+ _touchPanels[port]._multiFingerDragging = DRAG_TWO_FINGER;
+ } else {
+ simulatedButton = SDL_BUTTON_RIGHT;
+ _touchPanels[port]._multiFingerDragging = DRAG_THREE_FINGER;
+ }
+ SDL_Event ev;
+ ev.type = SDL_EVENT_MOUSE_BUTTON_DOWN;
+ ev.button.button = simulatedButton;
+ ev.button.x = mouseDownX;
+ ev.button.y = mouseDownY;
+ SDL_PushEvent(&ev);
+ }
+ }
+ }
+
+ //check if this is the "oldest" finger down (or the only finger down), otherwise it will not affect mouse motion
+ bool updatePointer = true;
+ if (numFingersDown > 1) {
+ for (int i = 0; i < MAX_NUM_FINGERS; i++) {
+ if (_touchPanels[port]._finger[i].id == id) {
+ for (int j = 0; j < MAX_NUM_FINGERS; j++) {
+ if (_touchPanels[port]._finger[j].id >= 0 && (i != j) ) {
+ if (_touchPanels[port]._finger[j].timeLastDown < _touchPanels[port]._finger[i].timeLastDown) {
+ updatePointer = false;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (updatePointer) {
+ event->type = SDL_EVENT_MOUSE_MOTION;
+ event->motion.x = x;
+ event->motion.y = y;
+ }
+ }
+}
+
+bool SdlEventSource::pollEvent(Common::Event &event) {
+ finishSimulatedMouseClicks();
+
+ // In case we still need to send a key up event for a key down from a
+ // TEXTINPUT event we do this immediately.
+ if (_queuedFakeKeyUp) {
+ event = _fakeKeyUp;
+ _queuedFakeKeyUp = false;
+ return true;
+ }
+
+ // If the screen changed, send an Common::EVENT_SCREEN_CHANGED
+ int screenID = g_system->getScreenChangeID();
+ if (screenID != _lastScreenID) {
+ _lastScreenID = screenID;
+ event.type = Common::EVENT_SCREEN_CHANGED;
+ return true;
+ }
+
+ if (_queuedFakeMouseMove) {
+ event = _fakeMouseMove;
+ _queuedFakeMouseMove = false;
+ return true;
+ }
+
+ SDL_Event ev;
+ while (SDL_PollEvent(&ev)) {
+ preprocessEvents(&ev);
+
+ // 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;
+ }
+ }
+ }
+
+#if defined(USE_IMGUI)
+ ImGui_ImplSDL3_ProcessEvent(&ev);
+ ImGuiIO &io = ImGui::GetIO();
+ if (io.WantTextInput || io.WantCaptureMouse)
+ continue;
+#endif
+ if (dispatchSDLEvent(ev, event))
+ return true;
+ }
+
+ return false;
+}
+
+bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
+ 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);
+
+ case SDL_EVENT_MOUSE_WHEEL: {
+ 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
+ // last time.
+ if (!processMouseEvent(event, _mouseX, _mouseY)) {
+ return false;
+ }
+ if (yDir < 0) {
+ event.type = Common::EVENT_WHEELDOWN;
+ return true;
+ } else if (yDir > 0) {
+ event.type = Common::EVENT_WHEELUP;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ case SDL_EVENT_TEXT_INPUT: {
+ // 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.
+ event.type = Common::EVENT_KEYDOWN;
+
+ event.kbd = Common::KeyState(Common::KEYCODE_INVALID, convUTF8ToUTF32(ev.text.text), 0);
+
+ SDLModToOSystemKeyFlags(SDL_GetModState(), event);
+ // Set the scroll lock sticky flag
+ if (_scrollLock)
+ event.kbd.flags |= Common::KBD_SCRL;
+
+ // Fake a key up when we have a proper ascii value.
+ _queuedFakeKeyUp = (event.kbd.ascii != 0);
+ _fakeKeyUp = event;
+ _fakeKeyUp.type = Common::EVENT_KEYUP;
+
+ return _queuedFakeKeyUp;
+ }
+
+ case SDL_EVENT_WINDOW_EXPOSED:
+ if (_graphicsManager) {
+ _graphicsManager->notifyVideoExpose();
+ }
+ return false;
+
+ case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
+ return handleResizeEvent(event, ev.window.data1, ev.window.data2);
+
+ case SDL_EVENT_WINDOW_FOCUS_GAINED: {
+ // 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;
+ if (_engineRunning) {
+ SDL_DisableScreenSaver();
+ } else {
+ SDL_EnableScreenSaver();
+ }
+ return true;
+ }
+
+ case SDL_EVENT_WINDOW_FOCUS_LOST: {
+ // Always allow the display to turn off if ScummVM is out of focus
+ event.type = Common::EVENT_FOCUS_LOST;
+ SDL_EnableScreenSaver();
+ return true;
+ }
+
+ case SDL_EVENT_JOYSTICK_ADDED:
+ return handleJoystickAdded(ev.jdevice, event);
+
+ case SDL_EVENT_JOYSTICK_REMOVED:
+ return handleJoystickRemoved(ev.jdevice, event);
+
+ case SDL_EVENT_DROP_FILE:
+ event.type = Common::EVENT_DROP_FILE;
+ event.path = Common::Path(ev.drop.data, Common::Path::kNativeSeparator);
+ return true;
+
+ case SDL_EVENT_CLIPBOARD_UPDATE:
+ event.type = Common::EVENT_CLIPBOARD_UPDATE;
+ return true;
+
+ case SDL_EVENT_QUIT:
+ event.type = Common::EVENT_QUIT;
+ return true;
+
+ default:
+ break;
+ }
+
+ 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;
+ }
+ }
+
+ 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;
+ }
+ }
+ return false;
+}
+
+
+bool SdlEventSource::handleKeyDown(SDL_Event &ev, Common::Event &event) {
+
+ SDLModToOSystemKeyFlags(SDL_GetModState(), event);
+
+ SDL_Keycode sdlKeycode = ev.key.key;
+ Common::KeyCode key = SDLToOSystemKeycode(sdlKeycode);
+
+ // Handle scroll lock as a key modifier
+ if (key == Common::KEYCODE_SCROLLOCK)
+ _scrollLock = !_scrollLock;
+
+ if (_scrollLock)
+ event.kbd.flags |= Common::KBD_SCRL;
+
+ if (remapKey(ev, event))
+ return true;
+
+ event.type = Common::EVENT_KEYDOWN;
+ event.kbd.keycode = key;
+
+ SDL_Keymod mod = ev.key.mod;
+#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.
+ if (event.kbd.keycode >= Common::KEYCODE_KP0 && event.kbd.keycode <= Common::KEYCODE_KP9) {
+ event.kbd.flags |= Common::KBD_NUM;
+ mod = SDL_Keymod(mod | KMOD_NUM);
+ }
+#endif
+ event.kbd.ascii = mapKey(sdlKeycode, mod, obtainUnicode(ev.key));
+
+ event.kbdRepeat = ev.key.repeat;
+
+ return true;
+}
+
+bool SdlEventSource::handleKeyUp(SDL_Event &ev, Common::Event &event) {
+ if (remapKey(ev, event))
+ return true;
+
+ SDLModToOSystemKeyFlags(SDL_GetModState(), event);
+
+ SDL_Keycode sdlKeycode = ev.key.key;
+
+ // Set the scroll lock sticky flag
+ if (_scrollLock)
+ event.kbd.flags |= Common::KBD_SCRL;
+
+ event.type = Common::EVENT_KEYUP;
+ event.kbd.keycode = SDLToOSystemKeycode(sdlKeycode);
+
+ SDL_Keymod mod = ev.key.mod;
+
+#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.
+ if (event.kbd.keycode >= Common::KEYCODE_KP0 && event.kbd.keycode <= Common::KEYCODE_KP9) {
+ event.kbd.flags |= Common::KBD_NUM;
+ mod = SDL_Keymod(mod | KMOD_NUM);
+ }
+#endif
+ event.kbd.ascii = mapKey(sdlKeycode, mod, 0);
+
+ return true;
+}
+
+void SdlEventSource::openJoystick(int joystickIndex) {
+ int numJoysticks = 0;
+ SDL_GetJoysticks(&numJoysticks);
+ if (numJoysticks > joystickIndex) {
+ if (SDL_IsGamepad(joystickIndex)) {
+ _controller = SDL_OpenGamepad(joystickIndex);
+ debug("Using game controller: %s", SDL_GetGamepadName(_controller));
+ } else {
+ _joystick = SDL_OpenJoystick(joystickIndex);
+ debug("Using joystick: %s", SDL_GetJoystickName(_joystick));
+ }
+ } else {
+ debug(5, "Invalid joystick: %d", joystickIndex);
+ }
+}
+
+void SdlEventSource::closeJoystick() {
+ if (_controller) {
+ SDL_CloseGamepad(_controller);
+ _controller = nullptr;
+ }
+ if (_joystick) {
+ SDL_CloseJoystick(_joystick);
+ _joystick = nullptr;
+ }
+}
+
+bool SdlEventSource::handleJoystickAdded(const SDL_JoyDeviceEvent &device, Common::Event &event) {
+ debug(5, "SdlEventSource: Received joystick added event for index '%d'", device.which);
+
+ int joystick_num = ConfMan.getInt("joystick_num");
+ if (joystick_num != device.which) {
+ return false;
+ }
+
+ debug(5, "SdlEventSource: Newly added joystick with index '%d' matches 'joysticky_num', trying to use it", device.which);
+
+ closeJoystick();
+ openJoystick(joystick_num);
+
+ event.type = Common::EVENT_INPUT_CHANGED;
+ return true;
+}
+
+bool SdlEventSource::handleJoystickRemoved(const SDL_JoyDeviceEvent &device, Common::Event &event) {
+ debug(5, "SdlEventSource: Received joystick removed event for instance id '%d'", device.which);
+
+ SDL_Joystick *joystick;
+ if (_controller) {
+ joystick = SDL_GetGamepadJoystick(_controller);
+ } else {
+ joystick = _joystick;
+ }
+
+ if (!joystick) {
+ return false;
+ }
+
+ if (SDL_GetJoystickID(joystick) != device.which) {
+ return false;
+ }
+
+ debug(5, "SdlEventSource: Newly removed joystick with instance id '%d' matches currently used joystick, closing current joystick", device.which);
+
+ closeJoystick();
+
+ event.type = Common::EVENT_INPUT_CHANGED;
+ return true;
+}
+
+int SdlEventSource::mapSDLControllerButtonToOSystem(Uint8 sdlButton) {
+ Common::JoystickButton osystemButtons[] = {
+ Common::JOYSTICK_BUTTON_A,
+ Common::JOYSTICK_BUTTON_B,
+ Common::JOYSTICK_BUTTON_X,
+ Common::JOYSTICK_BUTTON_Y,
+ Common::JOYSTICK_BUTTON_BACK,
+ Common::JOYSTICK_BUTTON_GUIDE,
+ Common::JOYSTICK_BUTTON_START,
+ Common::JOYSTICK_BUTTON_LEFT_STICK,
+ Common::JOYSTICK_BUTTON_RIGHT_STICK,
+ Common::JOYSTICK_BUTTON_LEFT_SHOULDER,
+ Common::JOYSTICK_BUTTON_RIGHT_SHOULDER,
+ Common::JOYSTICK_BUTTON_DPAD_UP,
+ Common::JOYSTICK_BUTTON_DPAD_DOWN,
+ Common::JOYSTICK_BUTTON_DPAD_LEFT,
+ Common::JOYSTICK_BUTTON_DPAD_RIGHT
+ };
+
+ if (sdlButton >= ARRAYSIZE(osystemButtons)) {
+ return -1;
+ }
+
+ return osystemButtons[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;
+
+ event.type = buttonUp ? Common::EVENT_JOYBUTTON_UP : Common::EVENT_JOYBUTTON_DOWN;
+ event.joystick.button = button;
+
+ return true;
+}
+
+bool SdlEventSource::handleControllerAxisMotion(const SDL_Event &ev, Common::Event &event) {
+ event.joystick.axis = ev.gaxis.axis;
+ event.joystick.position = ev.gaxis.value;
+ event.type = Common::EVENT_JOYAXIS_MOTION;
+
+ return true;
+}
+
+bool SdlEventSource::isJoystickConnected() const {
+ return _joystick || _controller;
+}
+
+uint32 SdlEventSource::obtainUnicode(const SDL_KeyboardEvent &key) {
+ SDL_Event events[2];
+
+ // Update the event queue here to give SDL a chance to insert TEXTINPUT
+ // events for KEYDOWN events. Otherwise we have a high chance that on
+ // Windows the TEXTINPUT event is not in the event queue at this point.
+ // In this case we will get two events with ascii values due to mapKey
+ // and dispatchSDLEvent. This results in nasty double input of characters
+ // in the GUI.
+ //
+ // FIXME: This is all a bit fragile because in mapKey we derive the ascii
+ // value from the key code if no unicode value is given. This is legacy
+ // behavior and should be removed anyway. If that is removed, we might not
+ // even need to do this peeking here but instead can rely on the
+ // SDL_TEXTINPUT case in dispatchSDLEvent to introduce keydown/keyup with
+ // proper ASCII values (but with KEYCODE_INVALID as keycode).
+ SDL_PumpEvents();
+
+ // In SDL2, the unicode field has been removed from the keysym struct.
+ // 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.
+ 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)) {
+ // 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.
+ n = SDL_PeepEvents(events, 1, SDL_GETEVENT, SDL_EVENT_TEXT_INPUT, SDL_EVENT_TEXT_INPUT);
+ // This is basically a paranoia safety check because we know there
+ // must be a text input event in the queue.
+ if (n > 0) {
+ return convUTF8ToUTF32(events[0].text.text);
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+}
+
+#endif // SDL_BACKEND
diff --git a/backends/graphics/openglsdl/openglsdl-graphics.cpp b/backends/graphics/openglsdl/openglsdl-graphics.cpp
index 0a97b912efd..1e84e21be32 100644
--- a/backends/graphics/openglsdl/openglsdl-graphics.cpp
+++ b/backends/graphics/openglsdl/openglsdl-graphics.cpp
@@ -34,6 +34,36 @@
#include "common/translation.h"
#endif
+#if SDL_VERSION_ATLEAST(3, 0, 0)
+static void sdlGLDestroyContext(SDL_GLContext context) {
+ SDL_GL_DestroyContext(context);
+}
+#elif SDL_VERSION_ATLEAST(2, 0, 0)
+static void sdlGLDestroyContext(SDL_GLContext context) {
+ SDL_GL_DeleteContext(context);
+}
+#endif
+
+#if SDL_VERSION_ATLEAST(3, 0, 0)
+static bool sdlSetSwapInterval(int interval) {
+ return SDL_GL_SetSwapInterval(interval);
+}
+#elif SDL_VERSION_ATLEAST(2, 0, 0)
+static bool sdlSetSwapInterval(int interval) {
+ return SDL_GL_SetSwapInterval(interval) == 0;
+}
+#endif
+
+#if SDL_VERSION_ATLEAST(3, 0, 0)
+static bool sdlGetAttribute(SDL_GLAttr attr, int *value) {
+ return SDL_GL_GetAttribute(attr, value);
+}
+#else
+static bool sdlGetAttribute(SDL_GLattr attr, int *value) {
+ return SDL_GL_GetAttribute(attr, value) != 0;
+}
+#endif
+
OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(SdlEventSource *eventSource, SdlWindow *window)
: SdlGraphicsManager(eventSource, window), _lastRequestedHeight(0),
#if SDL_VERSION_ATLEAST(2, 0, 0)
@@ -91,16 +121,16 @@ OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(SdlEventSource *eventSource,
// because then we already set up what we want to use.
//
// In case no defaults are given we prefer OpenGL over OpenGL ES.
- if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &_glContextProfileMask) != 0) {
+ if (!sdlGetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &_glContextProfileMask)) {
_glContextProfileMask = 0;
noDefaults = true;
}
- if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &_glContextMajor) != 0) {
+ if (!sdlGetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &_glContextMajor)) {
noDefaults = true;
}
- if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &_glContextMinor) != 0) {
+ if (!sdlGetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &_glContextMinor)) {
noDefaults = true;
}
@@ -201,7 +231,7 @@ OpenGLSdlGraphicsManager::~OpenGLSdlGraphicsManager() {
#endif
notifyContextDestroy();
- SDL_GL_DeleteContext(_glContext);
+ sdlGLDestroyContext(_glContext);
#else
if (_hwScreen) {
notifyContextDestroy();
@@ -255,7 +285,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 +611,15 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) {
destroyImGui();
#endif
- SDL_GL_DeleteContext(_glContext);
+ sdlGLDestroyContext(_glContext);
_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 +637,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 +655,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;
@@ -622,7 +669,7 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) {
return false;
}
- if (SDL_GL_SetSwapInterval(_vsync ? 1 : 0)) {
+ if (!sdlSetSwapInterval(_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 d57fd73fe26..acf84aaf91b 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"
@@ -48,6 +56,18 @@
#endif
#endif
+
+static void getMouseState(int *x, int *y) {
+#if SDL_VERSION_ATLEAST(3, 0, 0)
+ float fx, fy;
+ SDL_GetMouseState(&fx, &fy);
+ *x = static_cast<int>(fx);
+ *y = static_cast<int>(fy);
+#else
+ SDL_GetMouseState(x, y);
+#endif
+}
+
SdlGraphicsManager::SdlGraphicsManager(SdlEventSource *source, SdlWindow *window)
: _eventSource(source), _window(window), _hwScreen(nullptr)
#if SDL_VERSION_ATLEAST(2, 0, 0)
@@ -56,7 +76,7 @@ SdlGraphicsManager::SdlGraphicsManager(SdlEventSource *source, SdlWindow *window
{
ConfMan.registerDefault("fullscreen_res", "desktop");
- SDL_GetMouseState(&_cursorX, &_cursorY);
+ getMouseState(&_cursorX, &_cursorY);
}
void SdlGraphicsManager::activateManager() {
@@ -220,7 +240,7 @@ bool SdlGraphicsManager::showMouse(bool visible) {
// area, so we need to ask SDL where the system's mouse cursor is
// instead
int x, y;
- SDL_GetMouseState(&x, &y);
+ getMouseState(&x, &y);
if (!_activeArea.drawRect.contains(Common::Point(x, y))) {
showCursor = true;
}
@@ -312,7 +332,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 +382,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 +400,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;
@@ -601,7 +641,11 @@ void SdlGraphicsManager::initImGui(SDL_Renderer *renderer, void *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;
}
@@ -613,7 +657,11 @@ void SdlGraphicsManager::initImGui(SDL_Renderer *renderer, void *glContext) {
glslVersion = "#version 110";
}
if (!ImGui_ImplOpenGL3_Init(glslVersion)) {
+#if SDL_VERSION_ATLEAST(3, 0, 0)
+ ImGui_ImplSDL3_Shutdown();
+#else
ImGui_ImplSDL2_Shutdown();
+#endif
ImGui::DestroyContext();
return;
}
@@ -621,7 +669,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();
@@ -662,7 +726,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 {
@@ -670,15 +738,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 {
@@ -692,7 +768,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
}
@@ -709,7 +785,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 {
@@ -717,10 +797,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 af5d76514f6..8225b2d4046 100644
--- a/backends/graphics/sdl/sdl-graphics.h
+++ b/backends/graphics/sdl/sdl-graphics.h
@@ -161,7 +161,10 @@ protected:
* 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 9a63088c700..7158d90c1a7 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
@@ -62,6 +62,61 @@
#define SDL_FULLSCREEN 0x40000000
#endif
+static void destroySurface(SDL_Surface *surface) {
+#if SDL_VERSION_ATLEAST(3, 0, 0)
+ SDL_DestroySurface(surface);
+#else
+ SDL_FreeSurface(surface);
+#endif
+}
+
+#if SDL_VERSION_ATLEAST(3, 0, 0)
+static bool blitSurface(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect) {
+ return SDL_BlitSurface(src, srcrect, dst, dstrect);
+}
+#elif SDL_VERSION_ATLEAST(2, 0, 0)
+static bool blitSurface(SDL_Surface * src, const SDL_Rect * srcrect, SDL_Surface * dst, SDL_Rect * dstrect) {
+ return SDL_BlitSurface(src, srcrect, dst, dstrect) != -1;
+}
+#else
+static bool blitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect) {
+ return SDL_BlitSurface(src, srcrect, dst, dstrect) != -1;
+}
+#endif
+
+
+static bool lockSurface(SDL_Surface *surface) {
+#if SDL_VERSION_ATLEAST(3, 0, 0)
+ return SDL_LockSurface(surface);
+#else
+ return SDL_LockSurface(surface) != -1;
+#endif
+}
+
+static SDL_Surface *createSurface(int width, int height, SDL_Surface *surface) {
+#if SDL_VERSION_ATLEAST(3, 0, 0)
+ const SDL_PixelFormatDetails *pixelFormatDetails = SDL_GetPixelFormatDetails(surface->format);
+ if (pixelFormatDetails == nullptr)
+ error("getting pixel format details failed");
+ return SDL_CreateSurface(width, height,
+ SDL_GetPixelFormatForMasks(
+ pixelFormatDetails->bits_per_pixel,
+ pixelFormatDetails->Rmask,
+ pixelFormatDetails->Gmask,
+ pixelFormatDetails->Bmask,
+ pixelFormatDetails->Amask));
+#else
+ return SDL_CreateRGBSurface(SDL_SWSURFACE,
+ width,
+ height,
+ surface->format->BitsPerPixel,
+ surface->format->Rmask,
+ surface->format->Gmask,
+ surface->format->Bmask,
+ surface->format->Amask);
+#endif
+}
+
static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
{"surfacesdl", _s("SDL Surface"), GFX_SURFACESDL},
{nullptr, nullptr, 0}
@@ -189,13 +244,13 @@ SurfaceSdlGraphicsManager::~SurfaceSdlGraphicsManager() {
delete _scaler;
delete _mouseScaler;
if (_mouseOrigSurface) {
- SDL_FreeSurface(_mouseOrigSurface);
+ destroySurface(_mouseOrigSurface);
if (_mouseOrigSurface == _mouseSurface) {
_mouseSurface = nullptr;
}
}
if (_mouseSurface) {
- SDL_FreeSurface(_mouseSurface);
+ destroySurface(_mouseOrigSurface);
}
free(_currentPalette);
free(_overlayPalette);
@@ -484,6 +539,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 +579,7 @@ Graphics::PixelFormat SurfaceSdlGraphicsManager::convertSDLPixelFormat(SDL_Pixel
return out;
}
+#endif
#ifdef USE_RGB_COLOR
Common::List<Graphics::PixelFormat> SurfaceSdlGraphicsManager::getSupportedFormats() const {
@@ -533,6 +611,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 +637,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 +947,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 +984,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 +1018,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 +1050,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,14 +1125,7 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() {
//
// Need some extra bytes around when using 2xSaI
- _tmpscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.screenWidth + _maxExtraPixels * 2,
- _videoMode.screenHeight + _maxExtraPixels * 2,
- _hwScreen->format->BitsPerPixel,
- _hwScreen->format->Rmask,
- _hwScreen->format->Gmask,
- _hwScreen->format->Bmask,
- _hwScreen->format->Amask);
-
+ _tmpscreen = createSurface(_videoMode.screenWidth + _maxExtraPixels * 2, _videoMode.screenHeight + _maxExtraPixels * 2, _hwScreen);
if (_tmpscreen == nullptr)
error("allocating _tmpscreen failed");
@@ -1030,13 +1135,7 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() {
_videoMode.screenWidth, _videoMode.screenHeight, _maxExtraPixels);
}
- _overlayscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.overlayWidth, _videoMode.overlayHeight,
- _hwScreen->format->BitsPerPixel,
- _hwScreen->format->Rmask,
- _hwScreen->format->Gmask,
- _hwScreen->format->Bmask,
- _hwScreen->format->Amask);
-
+ _overlayscreen = createSurface(_videoMode.overlayWidth, _videoMode.overlayHeight, _hwScreen);
if (_overlayscreen == nullptr)
error("allocating _overlayscreen failed");
@@ -1045,14 +1144,7 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() {
if (_overlayFormat.bytesPerPixel == 1 && _overlayFormat.rBits() == 0)
_overlayFormat = Graphics::PixelFormat(1, 3, 3, 2, 0, 5, 2, 0, 0);
- _tmpscreen2 = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.overlayWidth + _maxExtraPixels * 2,
- _videoMode.overlayHeight + _maxExtraPixels * 2,
- _hwScreen->format->BitsPerPixel,
- _hwScreen->format->Rmask,
- _hwScreen->format->Gmask,
- _hwScreen->format->Bmask,
- _hwScreen->format->Amask);
-
+ _tmpscreen2 = createSurface(_videoMode.overlayWidth + _maxExtraPixels * 2, _videoMode.overlayHeight + _maxExtraPixels * 2, _hwScreen);
if (_tmpscreen2 == nullptr)
error("allocating _tmpscreen2 failed");
@@ -1066,7 +1158,7 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() {
void SurfaceSdlGraphicsManager::unloadGFXMode() {
if (_screen) {
- SDL_FreeSurface(_screen);
+ destroySurface(_screen);
_screen = nullptr;
}
@@ -1075,33 +1167,33 @@ void SurfaceSdlGraphicsManager::unloadGFXMode() {
#endif
if (_hwScreen) {
- SDL_FreeSurface(_hwScreen);
+ destroySurface(_hwScreen);
_hwScreen = nullptr;
}
if (_tmpscreen) {
- SDL_FreeSurface(_tmpscreen);
+ destroySurface(_tmpscreen);
_tmpscreen = nullptr;
}
if (_tmpscreen2) {
- SDL_FreeSurface(_tmpscreen2);
+ destroySurface(_tmpscreen2);
_tmpscreen2 = nullptr;
}
if (_overlayscreen) {
- SDL_FreeSurface(_overlayscreen);
+ destroySurface(_overlayscreen);
_overlayscreen = nullptr;
}
#ifdef USE_OSD
if (_osdMessageSurface) {
- SDL_FreeSurface(_osdMessageSurface);
+ destroySurface(_osdMessageSurface);
_osdMessageSurface = nullptr;
}
if (_osdIconSurface) {
- SDL_FreeSurface(_osdIconSurface);
+ destroySurface(_osdIconSurface);
_osdIconSurface = nullptr;
}
#endif
@@ -1128,15 +1220,15 @@ bool SurfaceSdlGraphicsManager::hotswapGFXMode() {
// Release the HW screen surface
if (_hwScreen) {
- SDL_FreeSurface(_hwScreen);
+ destroySurface(_osdIconSurface);
_hwScreen = nullptr;
}
if (_tmpscreen) {
- SDL_FreeSurface(_tmpscreen);
+ destroySurface(_tmpscreen);
_tmpscreen = nullptr;
}
if (_tmpscreen2) {
- SDL_FreeSurface(_tmpscreen2);
+ destroySurface(_tmpscreen2);
_tmpscreen2 = nullptr;
}
@@ -1158,8 +1250,8 @@ bool SurfaceSdlGraphicsManager::hotswapGFXMode() {
SDL_BlitSurface(old_overlayscreen, nullptr, _overlayscreen, nullptr);
// Free the old surfaces
- SDL_FreeSurface(old_screen);
- SDL_FreeSurface(old_overlayscreen);
+ destroySurface(old_screen);
+ destroySurface(old_overlayscreen);
// Update cursor to new scale
blitCursor();
@@ -1318,7 +1410,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;
}
@@ -1360,14 +1452,21 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() {
dst.x += _maxExtraPixels; // Shift rect since some scalers need to access the data around
dst.y += _maxExtraPixels; // any pixel to scale it, and we want to avoid mem access crashes.
- if (SDL_BlitSurface(origSurf, r, srcSurf, &dst) != 0)
+ if (!blitSurface(origSurf, r, srcSurf, &dst))
error("SDL_BlitSurface failed: %s", SDL_GetError());
}
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 +1575,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 +1605,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 +1652,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
@@ -1564,8 +1675,7 @@ bool SurfaceSdlGraphicsManager::saveScreenshot(const Common::Path &filename) con
return false;
}
- int result = SDL_LockSurface(_hwScreen);
- if (result < 0) {
+ if (!lockSurface(_hwScreen)) {
warning("Could not lock RGB surface");
return false;
}
@@ -1576,7 +1686,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 +1792,7 @@ void SurfaceSdlGraphicsManager::copyRectToScreen(const void *buf, int pitch, int
addDirtyRect(x, y, w, h, false);
// Try to lock the screen surface
- if (SDL_LockSurface(_screen) == -1)
+ if (!lockSurface(_screen))
error("SDL_LockSurface failed: %s", SDL_GetError());
byte *dst = (byte *)_screen->pixels + y * _screen->pitch + x * _screenFormat.bytesPerPixel;
@@ -1708,7 +1822,7 @@ Graphics::Surface *SurfaceSdlGraphicsManager::lockScreen() {
_screenIsLocked = true;
// Try to lock the screen surface
- if (SDL_LockSurface(_screen) == -1)
+ if (!lockSurface(_screen))
error("SDL_LockSurface failed: %s", SDL_GetError());
_framebuffer.init(_screen->w, _screen->h, _screen->pitch, _screen->pixels, _screenFormat);
@@ -1941,15 +2055,25 @@ void SurfaceSdlGraphicsManager::clearOverlay() {
dst.x = dst.y = _maxExtraPixels;
src.w = dst.w = _videoMode.screenWidth;
src.h = dst.h = _videoMode.screenHeight;
- if (SDL_BlitSurface(_screen, &src, _tmpscreen, &dst) != 0)
+ if (!blitSurface(_screen, &src, _tmpscreen, &dst))
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 +2084,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 +2109,7 @@ void SurfaceSdlGraphicsManager::grabOverlay(Graphics::Surface &surface) const {
if (_overlayscreen == nullptr)
return;
- if (SDL_LockSurface(_overlayscreen) == -1)
+ if (!lockSurface(_overlayscreen))
error("SDL_LockSurface failed: %s", SDL_GetError());
assert(surface.w >= _videoMode.overlayWidth);
@@ -2003,7 +2131,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 +2167,7 @@ void SurfaceSdlGraphicsManager::copyRectToOverlay(const void *buf, int pitch, in
// Mark the modified region as dirty
addDirtyRect(x, y, w, h, true);
- if (SDL_LockSurface(_overlayscreen) == -1)
+ if (!lockSurface(_overlayscreen))
error("SDL_LockSurface failed: %s", SDL_GetError());
byte *dst = (byte *)_overlayscreen->pixels + y * _overlayscreen->pitch + x * bpp;
@@ -2185,7 +2320,7 @@ void SurfaceSdlGraphicsManager::setMouseCursor(const void *buf, uint w, uint h,
_mouseCurState.h = h;
if (_mouseOrigSurface) {
- SDL_FreeSurface(_mouseOrigSurface);
+ destroySurface(_mouseOrigSurface);
if (_mouseSurface == _mouseOrigSurface) {
_mouseSurface = nullptr;
@@ -2195,7 +2330,7 @@ void SurfaceSdlGraphicsManager::setMouseCursor(const void *buf, uint w, uint h,
}
if (formatChanged && _mouseSurface) {
- SDL_FreeSurface(_mouseSurface);
+ destroySurface(_mouseSurface);
_mouseSurface = nullptr;
}
@@ -2206,6 +2341,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 +2360,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 +2381,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 +2406,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 +2473,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 +2501,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 +2529,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
@@ -2445,7 +2659,7 @@ void SurfaceSdlGraphicsManager::drawMouse() {
// Note that SDL_BlitSurface() and addDirtyRect() will both perform any
// clipping necessary
- if (SDL_BlitSurface(_mouseSurface, nullptr, _hwScreen, &dst) != 0)
+ if (!blitSurface(_mouseSurface, nullptr, _hwScreen, &dst))
error("SDL_BlitSurface failed: %s", SDL_GetError());
}
@@ -2497,18 +2711,31 @@ 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))
+ if (!lockSurface(_osdMessageSurface))
error("displayMessageOnOSD: SDL_LockSurface failed: %s", SDL_GetError());
// 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 +2745,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 +2760,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 +2800,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,9 +2830,10 @@ void SurfaceSdlGraphicsManager::displayActivityIconOnOSD(const Graphics::Surface
((0xFF >> iconFormat.bLoss) << iconFormat.bShift),
((0xFF >> iconFormat.aLoss) << iconFormat.aShift)
);
+#endif
// Lock the surface
- if (SDL_LockSurface(_osdIconSurface))
+ if (!lockSurface(_osdIconSurface))
error("displayActivityIconOnOSD: SDL_LockSurface failed: %s", SDL_GetError());
byte *dst = (byte *) _osdIconSurface->pixels;
@@ -2609,7 +2861,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 +2891,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 +3084,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 +3101,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 +3121,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 +3150,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 +3188,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,21 +3248,55 @@ 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) {
+#if SDL_VERSION_ATLEAST(3, 0, 0)
+ if (!SDL_SetSurfaceAlphaMod(surface, alpha)) {
+ return -1;
+ }
+
+ if (alpha == 255 || !flag) {
+ if (!SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE)) {
+ return -1;
+ }
+ } else {
+ if (!SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND)) {
+ return -1;
+ }
+ }
+#else
if (SDL_SetSurfaceAlphaMod(surface, alpha)) {
return -1;
}
@@ -2968,15 +3310,21 @@ int SurfaceSdlGraphicsManager::SDL_SetAlpha(SDL_Surface *surface, Uint32 flag, U
return -1;
}
}
+#endif
+
return 0;
}
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 +3337,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 +3352,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 87abc4f7603..ce716bd5e19 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.h
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.h
@@ -95,7 +95,11 @@ protected:
* @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 @@ public:
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 f5983ac2668..e7aa3b81ad7 100644
--- a/backends/graphics3d/openglsdl/openglsdl-graphics3d.cpp
+++ b/backends/graphics3d/openglsdl/openglsdl-graphics3d.cpp
@@ -46,6 +46,36 @@
#include "image/bmp.h"
#endif
+#if SDL_VERSION_ATLEAST(3, 0, 0)
+static bool sdlGetAttribute(SDL_GLAttr attr, int *value) {
+ return SDL_GL_GetAttribute(attr, value);
+}
+#else
+static bool sdlGetAttribute(SDL_GLattr attr, int *value) {
+ return SDL_GL_GetAttribute(attr, value) != 0;
+}
+#endif
+
+#if SDL_VERSION_ATLEAST(3, 0, 0)
+static void sdlGLDestroyContext(SDL_GLContext context) {
+ SDL_GL_DestroyContext(context);
+}
+#elif SDL_VERSION_ATLEAST(2, 0, 0)
+static void sdlGLDestroyContext(SDL_GLContext context) {
+ SDL_GL_DeleteContext(context);
+}
+#endif
+
+#if SDL_VERSION_ATLEAST(3, 0, 0)
+static bool sdlSetSwapInterval(int interval) {
+ return SDL_GL_SetSwapInterval(interval);
+}
+#elif SDL_VERSION_ATLEAST(2, 0, 0)
+static bool sdlSetSwapInterval(int interval) {
+ return SDL_GL_SetSwapInterval(interval) == 0;
+}
+#endif
+
OpenGLSdlGraphics3dManager::OpenGLSdlGraphics3dManager(SdlEventSource *eventSource, SdlWindow *window, bool supportsFrameBuffer)
: SdlGraphicsManager(eventSource, window),
#if SDL_VERSION_ATLEAST(2, 0, 0)
@@ -98,16 +128,16 @@ OpenGLSdlGraphics3dManager::OpenGLSdlGraphics3dManager(SdlEventSource *eventSour
// because then we already set up what we want to use.
//
// In case no defaults are given we prefer OpenGL over OpenGL ES.
- if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &_glContextProfileMask) != 0) {
+ if (!sdlGetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &_glContextProfileMask)) {
_glContextProfileMask = 0;
noDefaults = true;
}
- if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &_glContextMajor) != 0) {
+ if (!sdlGetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &_glContextMajor)) {
noDefaults = true;
}
- if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &_glContextMinor) != 0) {
+ if (!sdlGetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &_glContextMinor)) {
noDefaults = true;
}
@@ -385,7 +415,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 +443,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 +495,7 @@ void OpenGLSdlGraphics3dManager::initializeOpenGLContext() const {
OpenGLContext.initialize(_glContextType);
#if SDL_VERSION_ATLEAST(2, 0, 0)
- if (SDL_GL_SetSwapInterval(_vsync ? 1 : 0)) {
+ if (!sdlSetSwapInterval(_vsync ? 1 : 0)) {
warning("Unable to %s VSync: %s", _vsync ? "enable" : "disable", SDL_GetError());
}
#endif
@@ -787,7 +825,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 +849,7 @@ void OpenGLSdlGraphics3dManager::deinitializeRenderer() {
destroyImGui();
#endif
- SDL_GL_DeleteContext(_glContext);
+ sdlGLDestroyContext(_glContext);
_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 00000000000..51576346409
--- /dev/null
+++ b/backends/imgui/backends/imgui_impl_sdl3.cpp
@@ -0,0 +1,1156 @@
+// 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 will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
+// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
+// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
+// [x] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable' -> the OS animation effect when window gets created/destroyed is problematic. SDL2 backend doesn't have issue.
+// Issues:
+// [ ] Platform: Multi-viewport: Minimized windows seems to break mouse wheel events (at least under Windows).
+
+// 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-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
+// 2024-09-11: (Docking) Added support for viewport->ParentViewportId field to support parenting at OS level. (#7973)
+// 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 <SDL3/SDL.h>
+#if defined(__APPLE__)
+#include <TargetConditionals.h>
+#endif
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#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;
+ bool UseVulkan;
+ bool WantUpdateMonitors;
+
+ // IME handling
+ SDL_Window* ImeWindow;
+
+ // Mouse handling
+ Uint32 MouseWindowID;
+ int MouseButtonsDown;
+ SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT];
+ SDL_Cursor* MouseLastCursor;
+ int MousePendingLeaveFrame;
+ bool MouseCanUseGlobalState;
+ bool MouseCanReportHoveredViewport; // This is hard to use/unreliable on SDL so we'll set ImGuiBackendFlags_HasMouseHoveredViewport dynamically based on state.
+
+ // Gamepad handling
+ ImVector<SDL_Gamepad*> 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;
+}
+
+// Forward Declarations
+static void ImGui_ImplSDL3_UpdateMonitors();
+static void ImGui_ImplSDL3_InitPlatformInterface(SDL_Window* window, void* sdl_gl_context);
+static void ImGui_ImplSDL3_ShutdownPlatformInterface();
+
+// 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) : NULL;
+ 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 != NULL)
+ {
+ SDL_StopTextInput(bd->ImeWindow);
+ bd->ImeWindow = nullptr;
+ }
+ if (data->WantVisible)
+ {
+ SDL_Rect r;
+ r.x = (int)(data->InputPos.x - viewport->Pos.x);
+ r.y = (int)(data->InputPos.y - viewport->Pos.y + data->InputLineHeight);
+ 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)
+{
+ // 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)
+{
+ return ImGui::FindViewportByPlatformHandle((void*)(intptr_t)window_id);
+}
+
+// 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.
+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) == NULL)
+ return false;
+ ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y);
+ if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
+ {
+ int window_x, window_y;
+ SDL_GetWindowPosition(SDL_GetWindowFromID(event->motion.windowID), &window_x, &window_y);
+ mouse_pos.x += window_x;
+ mouse_pos.y += window_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) == NULL)
+ 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;
+ #ifdef __EMSCRIPTEN__
+ wheel_x /= 100.0f;
+ #endif
+ 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) == NULL)
+ 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) == NULL)
+ 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) == NULL)
+ 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_DISPLAY_ORIENTATION:
+ case SDL_EVENT_DISPLAY_ADDED:
+ case SDL_EVENT_DISPLAY_REMOVED:
+ case SDL_EVENT_DISPLAY_MOVED:
+ case SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED:
+ {
+ bd->WantUpdateMonitors = true;
+ return true;
+ }
+ case SDL_EVENT_WINDOW_MOUSE_ENTER:
+ {
+ if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == NULL)
+ 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) == NULL)
+ 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) == NULL)
+ return false;
+ io.AddFocusEvent(event->type == SDL_EVENT_WINDOW_FOCUS_GAINED);
+ return true;
+ }
+ case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
+ case SDL_EVENT_WINDOW_MOVED:
+ case SDL_EVENT_WINDOW_RESIZED:
+ {
+ ImGuiViewport* viewport = ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID);
+ if (viewport == NULL)
+ return false;
+ if (event->type == SDL_EVENT_WINDOW_CLOSE_REQUESTED)
+ viewport->PlatformRequestClose = true;
+ if (event->type == SDL_EVENT_WINDOW_MOVED)
+ viewport->PlatformRequestMove = true;
+ if (event->type == SDL_EVENT_WINDOW_RESIZED)
+ viewport->PlatformRequestResize = true;
+ 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)
+ if (mouse_can_use_global_state)
+ io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional)
+
+ bd->Window = window;
+ bd->WindowID = SDL_GetWindowID(window);
+ bd->Renderer = renderer;
+
+ // SDL on Linux/OSX doesn't report events for unfocused windows (see https://github.com/ocornut/imgui/issues/4960)
+ // We will use 'MouseCanReportHoveredViewport' to set 'ImGuiBackendFlags_HasMouseHoveredViewport' dynamically each frame.
+ bd->MouseCanUseGlobalState = mouse_can_use_global_state;
+#ifndef __APPLE__
+ bd->MouseCanReportHoveredViewport = bd->MouseCanUseGlobalState;
+#else
+ bd->MouseCanReportHoveredViewport = false;
+#endif
+
+ 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;
+
+ // Update monitor a first time during init
+ ImGui_ImplSDL3_UpdateMonitors();
+
+ // 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)
+ SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
+
+ // From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710)
+ SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0");
+
+ // SDL 3.x : see https://github.com/libsdl-org/SDL/issues/6659
+ SDL_SetHint("SDL_BORDERLESS_WINDOWED_STYLE", "0");
+
+ // We need SDL_CaptureMouse(), SDL_GetGlobalMouseState() from SDL 2.0.4+ to support multiple viewports.
+ // We left the call to ImGui_ImplSDL3_InitPlatformInterface() outside of #ifdef to avoid unused-function warnings.
+ if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports))
+ ImGui_ImplSDL3_InitPlatformInterface(window, sdl_gl_context);
+
+ return true;
+}
+
+// Should technically be a SDL_GLContext but due to typedef it is sane to keep it void* in public interface.
+bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context)
+{
+ return ImGui_ImplSDL3_Init(window, nullptr, sdl_gl_context);
+}
+
+bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window)
+{
+ if (!ImGui_ImplSDL3_Init(window, nullptr, nullptr))
+ return false;
+ ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
+ bd->UseVulkan = true;
+ return true;
+}
+
+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();
+
+ ImGui_ImplSDL3_ShutdownPlatformInterface();
+
+ 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 | ImGuiBackendFlags_PlatformHasViewports | ImGuiBackendFlags_HasMouseHoveredViewport);
+ IM_DELETE(bd);
+}
+
+// This code is incredibly messy because some of the functions we need for full viewport support are not available in SDL < 2.0.4.
+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 = (focused_window && (bd->Window == focused_window || ImGui_ImplSDL3_GetViewportForWindowID(SDL_GetWindowID(focused_window)) != NULL));
+#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 ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
+ if (io.WantSetMousePos)
+ {
+#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
+ if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
+ SDL_WarpMouseGlobal(io.MousePos.x, io.MousePos.y);
+ else
+#endif
+ 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)
+ // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor)
+ float mouse_x, mouse_y;
+ int window_x, window_y;
+ SDL_GetGlobalMouseState(&mouse_x, &mouse_y);
+ if (!(io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable))
+ {
+ SDL_GetWindowPosition(focused_window, &window_x, &window_y);
+ mouse_x -= window_x;
+ mouse_y -= window_y;
+ }
+ io.AddMousePosEvent((float)mouse_x, (float)mouse_y);
+ }
+ }
+
+ // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering.
+ // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic.
+ // - [!] SDL backend does NOT correctly ignore viewports with the _NoInputs flag.
+ // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window
+ // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported
+ // by the backend, and use its flawed heuristic to guess the viewport behind.
+ // - [X] SDL backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target).
+ if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)
+ {
+ ImGuiID mouse_viewport_id = 0;
+ if (ImGuiViewport* mouse_viewport = ImGui_ImplSDL3_GetViewportForWindowID(bd->MouseWindowID))
+ mouse_viewport_id = mouse_viewport->ID;
+ io.AddMouseViewportEvent(mouse_viewport_id);
+ }
+}
+
+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);
+}
+
+static void ImGui_ImplSDL3_UpdateMonitors()
+{
+ ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
+ ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
+ platform_io.Monitors.resize(0);
+ bd->WantUpdateMonitors = false;
+
+ int display_count;
+ SDL_DisplayID* displays = SDL_GetDisplays(&display_count);
+ for (int n = 0; n < display_count; n++)
+ {
+ // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime.
+ SDL_DisplayID display_id = displays[n];
+ ImGuiPlatformMonitor monitor;
+ SDL_Rect r;
+ SDL_GetDisplayBounds(display_id, &r);
+ monitor.MainPos = monitor.WorkPos = ImVec2((float)r.x, (float)r.y);
+ monitor.MainSize = monitor.WorkSize = ImVec2((float)r.w, (float)r.h);
+ SDL_GetDisplayUsableBounds(display_id, &r);
+ monitor.WorkPos = ImVec2((float)r.x, (float)r.y);
+ monitor.WorkSize = ImVec2((float)r.w, (float)r.h);
+ // FIXME-VIEWPORT: On MacOS SDL reports actual monitor DPI scale, ignoring OS configuration. We may want to set
+ // DpiScale to cocoa_window.backingScaleFactor here.
+ monitor.DpiScale = SDL_GetDisplayContentScale(display_id);
+ monitor.PlatformHandle = (void*)(intptr_t)n;
+ if (monitor.DpiScale <= 0.0f)
+ continue; // Some accessibility applications are declaring virtual monitors with a DPI of 0, see #7902.
+ platform_io.Monitors.push_back(monitor);
+ }
+ SDL_free(displays);
+}
+
+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);
+
+ // Update monitors
+ if (bd->WantUpdateMonitors)
+ ImGui_ImplSDL3_UpdateMonitors();
+
+ // 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);
+ }
+
+ // Our io.AddMouseViewportEvent() calls will only be valid when not capturing.
+ // Technically speaking testing for 'bd->MouseButtonsDown == 0' would be more rigorous, but testing for payload reduces noise and potential side-effects.
+ if (bd->MouseCanReportHoveredViewport && ImGui::GetDragDropPayload() == nullptr)
+ io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport;
+ else
+ io.BackendFlags &= ~ImGuiBackendFlags_HasMouseHoveredViewport;
+
+ ImGui_ImplSDL3_UpdateMouseData();
+ ImGui_ImplSDL3_UpdateMouseCursor();
+
+ // Update game controllers (if enabled and available)
+ ImGui_ImplSDL3_UpdateGamepads();
+}
+
+//--------------------------------------------------------------------------------------------------------
+// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
+// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
+// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
+//--------------------------------------------------------------------------------------------------------
+
+// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data.
+struct ImGui_ImplSDL3_ViewportData
+{
+ SDL_Window* Window;
+ SDL_Window* ParentWindow;
+ Uint32 WindowID;
+ bool WindowOwned;
+ SDL_GLContext GLContext;
+
+ ImGui_ImplSDL3_ViewportData() { Window = ParentWindow = nullptr; WindowID = 0; WindowOwned = false; GLContext = nullptr; }
+ ~ImGui_ImplSDL3_ViewportData() { IM_ASSERT(Window == nullptr && GLContext == nullptr); }
+};
+
+static SDL_Window* ImGui_ImplSDL3_GetSDLWindowFromViewportID(ImGuiID viewport_id)
+{
+ if (viewport_id != 0)
+ if (ImGuiViewport* viewport = ImGui::FindViewportByID(viewport_id))
+ {
+ SDL_WindowID window_id = (SDL_WindowID)(intptr_t)viewport->PlatformHandle;
+ return SDL_GetWindowFromID(window_id);
+ }
+ return nullptr;
+}
+
+static void ImGui_ImplSDL3_CreateWindow(ImGuiViewport* viewport)
+{
+ ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
+ ImGui_ImplSDL3_ViewportData* vd = IM_NEW(ImGui_ImplSDL3_ViewportData)();
+ viewport->PlatformUserData = vd;
+
+ vd->ParentWindow = ImGui_ImplSDL3_GetSDLWindowFromViewportID(viewport->ParentViewportId);
+
+ ImGuiViewport* main_viewport = ImGui::GetMainViewport();
+ ImGui_ImplSDL3_ViewportData* main_viewport_data = (ImGui_ImplSDL3_ViewportData*)main_viewport->PlatformUserData;
+
+ // Share GL resources with main context
+ bool use_opengl = (main_viewport_data->GLContext != nullptr);
+ SDL_GLContext backup_context = nullptr;
+ if (use_opengl)
+ {
+ backup_context = SDL_GL_GetCurrentContext();
+ SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
+ SDL_GL_MakeCurrent(main_viewport_data->Window, main_viewport_data->GLContext);
+ }
+
+ Uint32 sdl_flags = 0;
+ sdl_flags |= use_opengl ? SDL_WINDOW_OPENGL : (bd->UseVulkan ? SDL_WINDOW_VULKAN : 0);
+ sdl_flags |= SDL_GetWindowFlags(bd->Window);
+ sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0;
+ sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE;
+ sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) ? SDL_WINDOW_UTILITY : 0;
+ sdl_flags |= (viewport->Flags & ImGuiViewportFlags_TopMost) ? SDL_WINDOW_ALWAYS_ON_TOP : 0;
+ vd->Window = SDL_CreateWindow("No Title Yet", (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags);
+ SDL_SetWindowParent(vd->Window, vd->ParentWindow);
+ SDL_SetWindowPosition(vd->Window, (int)viewport->Pos.x, (int)viewport->Pos.y);
+ vd->WindowOwned = true;
+ if (use_opengl)
+ {
+ vd->GLContext = SDL_GL_CreateContext(vd->Window);
+ SDL_GL_SetSwapInterval(0);
+ }
+ if (use_opengl && backup_context)
+ SDL_GL_MakeCurrent(vd->Window, backup_context);
+
+ ImGui_ImplSDL3_SetupPlatformHandles(viewport, vd->Window);
+}
+
+static void ImGui_ImplSDL3_DestroyWindow(ImGuiViewport* viewport)
+{
+ if (ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData)
+ {
+ if (vd->GLContext && vd->WindowOwned)
+ SDL_GL_DestroyContext(vd->GLContext);
+ if (vd->Window && vd->WindowOwned)
+ SDL_DestroyWindow(vd->Window);
+ vd->GLContext = nullptr;
+ vd->Window = nullptr;
+ IM_DELETE(vd);
+ }
+ viewport->PlatformUserData = viewport->PlatformHandle = nullptr;
+}
+
+static void ImGui_ImplSDL3_ShowWindow(ImGuiViewport* viewport)
+{
+ ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
+#if defined(_WIN32) && !(defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP || WINAPI_FAMILY == WINAPI_FAMILY_GAMES))
+ HWND hwnd = (HWND)viewport->PlatformHandleRaw;
+
+ // SDL hack: Show icon in task bar (#7989)
+ // Note: SDL_WINDOW_UTILITY can be used to control task bar visibility, but on Windows, it does not affect child windows.
+ if (!(viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon))
+ {
+ LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE);
+ ex_style |= WS_EX_APPWINDOW;
+ ex_style &= ~WS_EX_TOOLWINDOW;
+ ::ShowWindow(hwnd, SW_HIDE);
+ ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style);
+ }
+#endif
+
+ SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN, (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) ? "0" : "1");
+ SDL_ShowWindow(vd->Window);
+}
+
+static void ImGui_ImplSDL3_UpdateWindow(ImGuiViewport* viewport)
+{
+ ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
+
+ // Update SDL3 parent if it changed _after_ creation.
+ // This is for advanced apps that are manipulating ParentViewportID manually.
+ SDL_Window* new_parent = ImGui_ImplSDL3_GetSDLWindowFromViewportID(viewport->ParentViewportId);
+ if (new_parent != vd->ParentWindow)
+ {
+ vd->ParentWindow = new_parent;
+ SDL_SetWindowParent(vd->Window, vd->ParentWindow);
+ }
+}
+
+static ImVec2 ImGui_ImplSDL3_GetWindowPos(ImGuiViewport* viewport)
+{
+ ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
+ int x = 0, y = 0;
+ SDL_GetWindowPosition(vd->Window, &x, &y);
+ return ImVec2((float)x, (float)y);
+}
+
+static void ImGui_ImplSDL3_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos)
+{
+ ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
+ SDL_SetWindowPosition(vd->Window, (int)pos.x, (int)pos.y);
+}
+
+static ImVec2 ImGui_ImplSDL3_GetWindowSize(ImGuiViewport* viewport)
+{
+ ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
+ int w = 0, h = 0;
+ SDL_GetWindowSize(vd->Window, &w, &h);
+ return ImVec2((float)w, (float)h);
+}
+
+static void ImGui_ImplSDL3_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
+{
+ ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
+ SDL_SetWindowSize(vd->Window, (int)size.x, (int)size.y);
+}
+
+static void ImGui_ImplSDL3_SetWindowTitle(ImGuiViewport* viewport, const char* title)
+{
+ ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
+ SDL_SetWindowTitle(vd->Window, title);
+}
+
+static void ImGui_ImplSDL3_SetWindowAlpha(ImGuiViewport* viewport, float alpha)
+{
+ ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
+ SDL_SetWindowOpacity(vd->Window, alpha);
+}
+
+static void ImGui_ImplSDL3_SetWindowFocus(ImGuiViewport* viewport)
+{
+ ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
+ SDL_RaiseWindow(vd->Window);
+}
+
+static bool ImGui_ImplSDL3_GetWindowFocus(ImGuiViewport* viewport)
+{
+ ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
+ return (SDL_GetWindowFlags(vd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0;
+}
+
+static bool ImGui_ImplSDL3_GetWindowMinimized(ImGuiViewport* viewport)
+{
+ ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
+ return (SDL_GetWindowFlags(vd->Window) & SDL_WINDOW_MINIMIZED) != 0;
+}
+
+static void ImGui_ImplSDL3_RenderWindow(ImGuiViewport* viewport, void*)
+{
+ ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
+ if (vd->GLContext)
+ SDL_GL_MakeCurrent(vd->Window, vd->GLContext);
+}
+
+static void ImGui_ImplSDL3_SwapBuffers(ImGuiViewport* viewport, void*)
+{
+ ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
+ if (vd->GLContext)
+ {
+ SDL_GL_MakeCurrent(vd->Window, vd->GLContext);
+ SDL_GL_SwapWindow(vd->Window);
+ }
+}
+
+// Vulkan support (the Vulkan renderer needs to call a platform-side support function to create the surface)
+// SDL is graceful enough to _not_ need <vulkan/vulkan.h> so we can safely include this.
+#include <SDL3/SDL_vulkan.h>
+static int ImGui_ImplSDL3_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface)
+{
+ ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
+ (void)vk_allocator;
+ bool ret = SDL_Vulkan_CreateSurface(vd->Window, (VkInstance)vk_instance, (VkAllocationCallbacks*)vk_allocator, (VkSurfaceKHR*)out_vk_surface);
+ return ret ? 0 : 1; // ret ? VK_SUCCESS : VK_NOT_READY
+}
+
+static void ImGui_ImplSDL3_InitPlatformInterface(SDL_Window* window, void* sdl_gl_context)
+{
+ // Register platform interface (will be coupled with a renderer interface)
+ ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
+ platform_io.Platform_CreateWindow = ImGui_ImplSDL3_CreateWindow;
+ platform_io.Platform_DestroyWindow = ImGui_ImplSDL3_DestroyWindow;
+ platform_io.Platform_ShowWindow = ImGui_ImplSDL3_ShowWindow;
+ platform_io.Platform_UpdateWindow = ImGui_ImplSDL3_UpdateWindow;
+ platform_io.Platform_SetWindowPos = ImGui_ImplSDL3_SetWindowPos;
+ platform_io.Platform_GetWindowPos = ImGui_ImplSDL3_GetWindowPos;
+ platform_io.Platform_SetWindowSize = ImGui_ImplSDL3_SetWindowSize;
+ platform_io.Platform_GetWindowSize = ImGui_ImplSDL3_GetWindowSize;
+ platform_io.Platform_SetWindowFocus = ImGui_ImplSDL3_SetWindowFocus;
+ platform_io.Platform_GetWindowFocus = ImGui_ImplSDL3_GetWindowFocus;
+ platform_io.Platform_GetWindowMinimized = ImGui_ImplSDL3_GetWindowMinimized;
+ platform_io.Platform_SetWindowTitle = ImGui_ImplSDL3_SetWindowTitle;
+ platform_io.Platform_RenderWindow = ImGui_ImplSDL3_RenderWindow;
+ platform_io.Platform_SwapBuffers = ImGui_ImplSDL3_SwapBuffers;
+ platform_io.Platform_SetWindowAlpha = ImGui_ImplSDL3_SetWindowAlpha;
+ platform_io.Platform_CreateVkSurface = ImGui_ImplSDL3_CreateVkSurface;
+
+ // Register main window handle (which is owned by the main application, not by us)
+ // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports.
+ ImGuiViewport* main_viewport = ImGui::GetMainViewport();
+ ImGui_ImplSDL3_ViewportData* vd = IM_NEW(ImGui_ImplSDL3_ViewportData)();
+ vd->Window = window;
+ vd->WindowID = SDL_GetWindowID(window);
+ vd->WindowOwned = false;
+ vd->GLContext = (SDL_GLContext)sdl_gl_context;
+ main_viewport->PlatformUserData = vd;
+ main_viewport->PlatformHandle = (void*)(intptr_t)vd->WindowID;
+}
+
+static void ImGui_ImplSDL3_ShutdownPlatformInterface()
+{
+ ImGui::DestroyPlatformWindows();
+}
+
+//-----------------------------------------------------------------------------
+
+#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 00000000000..b9b89e671da
--- /dev/null
+++ b/backends/imgui/backends/imgui_impl_sdl3.h
@@ -0,0 +1,51 @@
+// 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 will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
+// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
+// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
+// [x] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable' -> the OS animation effect when window gets created/destroyed is problematic. SDL2 backend doesn't have issue.
+// Issues:
+// [ ] Platform: Multi-viewport: Minimized windows seems to break mouse wheel events (at least under Windows).
+// [x] Platform: Basic IME support. Position somehow broken in SDL3 + app needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!.
+
+// 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 = NULL, 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 00000000000..153d4b767c5
--- /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 <stdint.h> // 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 <SDL3/SDL.h>
+#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<SDL_FColor> 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<SDL_FColor>& 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 00000000000..6529469c025
--- /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/imgui/scummvm.patch b/backends/imgui/scummvm.patch
index 937ff9e9553..e6488e8f41b 100644
--- a/backends/imgui/scummvm.patch
+++ b/backends/imgui/scummvm.patch
@@ -1,6 +1,6 @@
diff --color -rbu ./backends/imgui_impl_opengl3.cpp ../scummvm/scummvm/backends/imgui/backends/imgui_impl_opengl3.cpp
---- ./backends/imgui_impl_opengl3.cpp 2024-10-07 18:21:01
-+++ ../scummvm/scummvm/backends/imgui/backends/imgui_impl_opengl3.cpp 2024-10-07 18:30:56
+--- ./backends/imgui_impl_opengl3.cpp 2025-02-18 20:51:22
++++ ../scummvm/scummvm/backends/imgui/backends/imgui_impl_opengl3.cpp 2025-01-25 16:39:54
@@ -114,7 +114,7 @@
#define _CRT_SECURE_NO_WARNINGS
#endif
@@ -19,8 +19,8 @@ diff --color -rbu ./backends/imgui_impl_opengl3.cpp ../scummvm/scummvm/backends/
#include "imgui_impl_opengl3_loader.h"
#endif
diff --color -rbu ./backends/imgui_impl_opengl3.h ../scummvm/scummvm/backends/imgui/backends/imgui_impl_opengl3.h
---- ./backends/imgui_impl_opengl3.h 2024-10-07 18:21:01
-+++ ../scummvm/scummvm/backends/imgui/backends/imgui_impl_opengl3.h 2024-10-07 18:30:56
+--- ./backends/imgui_impl_opengl3.h 2025-02-18 20:51:22
++++ ../scummvm/scummvm/backends/imgui/backends/imgui_impl_opengl3.h 2025-01-25 16:39:54
@@ -27,7 +27,7 @@
// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
@@ -40,8 +40,8 @@ diff --color -rbu ./backends/imgui_impl_opengl3.h ../scummvm/scummvm/backends/im
#else
// Otherwise imgui_impl_opengl3_loader.h will be used.
diff --color -rbu ./backends/imgui_impl_opengl3_loader.h ../scummvm/scummvm/backends/imgui/backends/imgui_impl_opengl3_loader.h
---- ./backends/imgui_impl_opengl3_loader.h 2024-05-17 22:26:35
-+++ ../scummvm/scummvm/backends/imgui/backends/imgui_impl_opengl3_loader.h 2024-10-07 18:30:56
+--- ./backends/imgui_impl_opengl3_loader.h 2025-02-18 20:51:22
++++ ../scummvm/scummvm/backends/imgui/backends/imgui_impl_opengl3_loader.h 2025-01-25 16:39:54
@@ -56,6 +56,11 @@
#ifndef __gl3w_h_
#define __gl3w_h_
@@ -77,8 +77,8 @@ diff --color -rbu ./backends/imgui_impl_opengl3_loader.h ../scummvm/scummvm/back
#define WIN32_LEAN_AND_MEAN 1
#endif
diff --color -rbu ./backends/imgui_impl_sdl2.cpp ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdl2.cpp
---- ./backends/imgui_impl_sdl2.cpp 2024-10-07 18:21:01
-+++ ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdl2.cpp 2024-10-07 18:30:56
+--- ./backends/imgui_impl_sdl2.cpp 2025-02-18 13:09:05
++++ ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdl2.cpp 2025-01-25 16:39:54
@@ -92,9 +92,11 @@
// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1).
// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
@@ -122,8 +122,8 @@ diff --color -rbu ./backends/imgui_impl_sdl2.cpp ../scummvm/scummvm/backends/img
+#endif
#endif // #ifndef IMGUI_DISABLE
diff --color -rbu ./backends/imgui_impl_sdl2.h ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdl2.h
---- ./backends/imgui_impl_sdl2.h 2024-10-07 18:21:01
-+++ ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdl2.h 2024-10-07 18:30:56
+--- ./backends/imgui_impl_sdl2.h 2025-02-18 13:09:05
++++ ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdl2.h 2025-01-25 16:39:54
@@ -23,7 +23,7 @@
// - Introduction, links and more at the top of imgui.cpp
@@ -141,9 +141,35 @@ diff --color -rbu ./backends/imgui_impl_sdl2.h ../scummvm/scummvm/backends/imgui
// 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.
+diff --color -rbu ./backends/imgui_impl_sdl3.cpp ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdl3.cpp
+--- ./backends/imgui_impl_sdl3.cpp 2025-01-31 20:18:22
++++ ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdl3.cpp 2025-01-31 20:20:20
+@@ -52,8 +52,9 @@
+ // 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 "imgui.h"
++#include "backends/imgui/imgui.h"
+ #ifndef IMGUI_DISABLE
++#include "backends/platform/sdl/sdl.h"
+ #include "imgui_impl_sdl3.h"
+
+ // Clang warnings with -Weverything
+diff --color -rbu ./backends/imgui_impl_sdl3.h ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdl3.h
+--- ./backends/imgui_impl_sdl3.h 2025-01-31 20:18:22
++++ ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdl3.h 2025-01-31 20:20:04
+@@ -24,7 +24,7 @@
+ // - Introduction, links and more at the top of imgui.cpp
+
+ #pragma once
+-#include "imgui.h" // IMGUI_IMPL_API
++#include "backends/imgui/imgui.h" // IMGUI_IMPL_API
+ #ifndef IMGUI_DISABLE
+
+ struct SDL_Window;
diff --color -rbu ./backends/imgui_impl_sdlrenderer2.cpp ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdlrenderer2.cpp
---- ./backends/imgui_impl_sdlrenderer2.cpp 2024-05-22 17:43:07
-+++ ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdlrenderer2.cpp 2024-10-07 18:30:56
+--- ./backends/imgui_impl_sdlrenderer2.cpp 2025-02-18 13:09:05
++++ ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdlrenderer2.cpp 2025-01-25 16:39:54
@@ -30,7 +30,7 @@
// 2021-10-06: Backup and restore modified ClipRect/Viewport.
// 2021-09-21: Initial version.
@@ -154,8 +180,8 @@ diff --color -rbu ./backends/imgui_impl_sdlrenderer2.cpp ../scummvm/scummvm/back
#include "imgui_impl_sdlrenderer2.h"
#include <stdint.h> // intptr_t
diff --color -rbu ./backends/imgui_impl_sdlrenderer2.h ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdlrenderer2.h
---- ./backends/imgui_impl_sdlrenderer2.h 2024-10-07 18:21:01
-+++ ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdlrenderer2.h 2024-10-07 18:30:56
+--- ./backends/imgui_impl_sdlrenderer2.h 2025-02-18 13:09:05
++++ ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdlrenderer2.h 2025-01-25 16:39:54
@@ -22,8 +22,8 @@
// - Introduction, links and more at the top of imgui.cpp
@@ -166,9 +192,95 @@ diff --color -rbu ./backends/imgui_impl_sdlrenderer2.h ../scummvm/scummvm/backen
struct SDL_Renderer;
+diff --color -rbu ./backends/imgui_impl_sdlrenderer3.cpp ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdlrenderer3.cpp
+--- ./backends/imgui_impl_sdlrenderer3.cpp 2025-02-18 13:09:05
++++ ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdlrenderer3.cpp 2025-01-25 16:39:54
+@@ -12,8 +12,6 @@
+ // 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.
+-// Missing features:
+-// [ ] Renderer: Multi-viewport support (multiple windows).
+
+ // 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.
+@@ -29,7 +27,7 @@
+ // 2024-02-12: Amend to query SDL_RenderViewportSet() and restore viewport accordingly.
+ // 2023-05-30: Initial version.
+
+-#include "imgui.h"
++#include "backends/imgui/imgui.h"
+ #ifndef IMGUI_DISABLE
+ #include "imgui_impl_sdlrenderer3.h"
+ #include <stdint.h> // intptr_t
+diff --color -rbu ./backends/imgui_impl_sdlrenderer3.h ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdlrenderer3.h
+--- ./backends/imgui_impl_sdlrenderer3.h 2025-02-18 13:09:05
++++ ../scummvm/scummvm/backends/imgui/backends/imgui_impl_sdlrenderer3.h 2025-01-25 16:39:54
+@@ -12,8 +12,6 @@
+ // 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.
+-// Missing features:
+-// [ ] Renderer: Multi-viewport support (multiple windows).
+
+ // 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.
+@@ -24,7 +22,7 @@
+ // - Introduction, links and more at the top of imgui.cpp
+
+ #pragma once
+-#include "imgui.h" // IMGUI_IMPL_API
++#include "backends/imgui/imgui.h" // IMGUI_IMPL_API
+ #ifndef IMGUI_DISABLE
+
+ struct SDL_Renderer;
+diff --color -rbu ./imconfig.h ../scummvm/scummvm/backends/imgui/imconfig.h
+--- ./imconfig.h 2025-02-18 13:09:05
++++ ../scummvm/scummvm/backends/imgui/imconfig.h 2025-01-25 16:39:54
+@@ -28,8 +28,8 @@
+ //#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden
+
+ //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
+-//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+-//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
++#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
++#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
+
+ //---- Disable all of Dear ImGui or don't implement standard windows/tools.
+ // It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
+@@ -43,7 +43,7 @@
+ //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
+ //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
+ //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
+-//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
++#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
+ //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
+ //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
+ //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
+diff --color -rbu ./imgui.cpp ../scummvm/scummvm/backends/imgui/imgui.cpp
+--- ./imgui.cpp 2025-02-18 13:09:05
++++ ../scummvm/scummvm/backends/imgui/imgui.cpp 2025-01-25 16:39:54
+@@ -15234,7 +15234,7 @@
+ line_end[-1] = 0;
+ const char* name_end = line_end - 1;
+ const char* type_start = line + 1;
+- char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
++ char* type_end = const_cast<char *>(ImStrchrRange(type_start, name_end, ']'));
+ const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
+ if (!type_end || !name_start)
+ continue;
+@@ -21345,7 +21345,7 @@
+
+ if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size()))
+ {
+- InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly);
++ InputTextMultiline("##Ini", const_cast<char *>(g.SettingsIniData.c_str()), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly);
+ TreePop();
+ }
+ TreePop();
diff --color -rbu ./imgui.h ../scummvm/scummvm/backends/imgui/imgui.h
---- ./imgui.h 2024-10-07 18:21:01
-+++ ../scummvm/scummvm/backends/imgui/imgui.h 2024-10-07 18:30:56
+--- ./imgui.h 2025-02-18 13:09:05
++++ ../scummvm/scummvm/backends/imgui/imgui.h 2025-01-25 16:39:54
@@ -105,8 +105,10 @@
#define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1)))
#define IM_FMTLIST(FMT) __attribute__((format(gnu_printf, FMT, 0)))
@@ -182,9 +294,33 @@ diff --color -rbu ./imgui.h ../scummvm/scummvm/backends/imgui/imgui.h
#else
#define IM_FMTARGS(FMT)
#define IM_FMTLIST(FMT)
+diff --color -rbu ./imgui_demo.cpp ../scummvm/scummvm/backends/imgui/imgui_demo.cpp
+--- ./imgui_demo.cpp 2025-02-18 13:09:05
++++ ../scummvm/scummvm/backends/imgui/imgui_demo.cpp 2025-01-25 16:39:54
+@@ -3926,7 +3926,7 @@
+ ImGui::TableNextColumn();
+ ImGui::SetNextItemWidth(-FLT_MIN);
+ ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
+- ImGui::InputText("###NoLabel", (char*)(void*)item_category, strlen(item_category), ImGuiInputTextFlags_ReadOnly);
++ ImGui::InputText("###NoLabel", const_cast<char *>(item_category), strlen(item_category), ImGuiInputTextFlags_ReadOnly);
+ ImGui::PopStyleVar();
+ }
+
+diff --color -rbu ./imgui_draw.cpp ../scummvm/scummvm/backends/imgui/imgui_draw.cpp
+--- ./imgui_draw.cpp 2025-02-18 13:09:05
++++ ../scummvm/scummvm/backends/imgui/imgui_draw.cpp 2025-01-25 16:39:54
+@@ -3727,7 +3727,7 @@
+
+ void ImFont::SetGlyphVisible(ImWchar c, bool visible)
+ {
+- if (ImFontGlyph* glyph = (ImFontGlyph*)(void*)FindGlyph((ImWchar)c))
++ if (ImFontGlyph* glyph = const_cast<ImFontGlyph *>(FindGlyph((ImWchar)c)))
+ glyph->Visible = visible ? 1 : 0;
+ }
+
diff --color -rbu ./misc/freetype/imgui_freetype.cpp ../scummvm/scummvm/backends/imgui/misc/freetype/imgui_freetype.cpp
---- ./misc/freetype/imgui_freetype.cpp 2024-10-07 18:21:01
-+++ ../scummvm/scummvm/backends/imgui/misc/freetype/imgui_freetype.cpp 2024-10-07 18:32:37
+--- ./misc/freetype/imgui_freetype.cpp 2025-02-18 13:09:05
++++ ../scummvm/scummvm/backends/imgui/misc/freetype/imgui_freetype.cpp 2025-01-25 16:39:54
@@ -34,10 +34,10 @@
// FIXME: cfg.OversampleH, OversampleV are not supported (but perhaps not so necessary with this rasterizer).
@@ -208,8 +344,8 @@ diff --color -rbu ./misc/freetype/imgui_freetype.cpp ../scummvm/scummvm/backends
#endif
diff --color -rbu ./misc/freetype/imgui_freetype.h ../scummvm/scummvm/backends/imgui/misc/freetype/imgui_freetype.h
---- ./misc/freetype/imgui_freetype.h 2024-05-13 22:08:39
-+++ ../scummvm/scummvm/backends/imgui/misc/freetype/imgui_freetype.h 2024-10-07 18:32:44
+--- ./misc/freetype/imgui_freetype.h 2025-02-18 13:09:05
++++ ../scummvm/scummvm/backends/imgui/misc/freetype/imgui_freetype.h 2025-01-25 16:39:54
@@ -2,7 +2,7 @@
// (headers)
diff --git a/backends/mixer/sdl/sdl-mixer.cpp b/backends/mixer/sdl/sdl-mixer.cpp
index 022d50e0598..7505ea74c1b 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,45 @@ 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 2c8ea2232b8..3dd0670396f 100644
--- a/backends/mixer/sdl/sdl-mixer.h
+++ b/backends/mixer/sdl/sdl-mixer.h
@@ -80,9 +80,16 @@ protected:
* 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 d1acc3d8d54..7d3c71cc11b 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -185,7 +185,7 @@ endif
# derive from the SDL backend, and they all need the following files.
ifdef SDL_BACKEND
MODULE_OBJS += \
- events/sdl/sdl-events.o \
+ events/sdl/sdl-common-events.o \
graphics/sdl/sdl-graphics.o \
graphics/surfacesdl/surfacesdl-graphics.o \
mixer/sdl/sdl-mixer.o \
@@ -193,6 +193,23 @@ MODULE_OBJS += \
mutex/sdl/sdl-mutex.o \
timer/sdl/sdl-timer.o
+ifndef USE_SDL3
+ifndef USE_SDL2
+MODULE_OBJS += \
+ events/sdl/sdl1-events.o
+endif
+endif
+
+ifdef USE_SDL2
+MODULE_OBJS += \
+ events/sdl/sdl2-events.o
+endif
+
+ifdef USE_SDL3
+MODULE_OBJS += \
+ events/sdl/sdl3-events.o
+endif
+
ifndef RISCOS
ifndef KOLIBRIOS
MODULE_OBJS += plugins/sdl/sdl-provider.o
@@ -510,5 +527,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 3ada8e5ecd0..11e951b4845 100644
--- a/backends/mutex/sdl/sdl-mutex.cpp
+++ b/backends/mutex/sdl/sdl-mutex.cpp
@@ -34,11 +34,29 @@ public:
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/amigaos/amigaos.cpp b/backends/platform/sdl/amigaos/amigaos.cpp
index 7d07d627137..db01f3cb2a1 100644
--- a/backends/platform/sdl/amigaos/amigaos.cpp
+++ b/backends/platform/sdl/amigaos/amigaos.cpp
@@ -29,6 +29,16 @@
static bool cleanupDone = false;
+#if SDL_VERSION_ATLEAST(3, 0, 0)
+static bool sdlGLLoadLibrary(const char *path) {
+ return SDL_GL_LoadLibrary(path);
+}
+#else
+static bool sdlGLLoadLibrary(const char *path) {
+ return SDL_GL_LoadLibrary(path) != 0;
+}
+#endif
+
static void cleanup() {
if (!cleanupDone)
g_system->destroy();
@@ -83,7 +93,7 @@ void OSystem_AmigaOS::initBackend() {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
- if (SDL_GL_LoadLibrary(NULL) < 0) {
+ if (!sdlGLLoadLibrary(NULL)) {
if (force) {
warning("OpenGL implementation chosen is unsupported, falling back");
force = 0;
@@ -101,7 +111,7 @@ void OSystem_AmigaOS::initBackend() {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
- if (SDL_GL_LoadLibrary(NULL) < 0) {
+ if (!sdlGLLoadLibrary(NULL)) {
if (force) {
warning("OpenGL implementation chosen is unsupported, falling back");
force = 0;
diff --git a/backends/platform/sdl/macosx/macosx-window.mm b/backends/platform/sdl/macosx/macosx-window.mm
index d8481eaee46..59e194d529f 100644
--- a/backends/platform/sdl/macosx/macosx-window.mm
+++ b/backends/platform/sdl/macosx/macosx-window.mm
@@ -27,7 +27,7 @@
#include <AppKit/NSWindow.h>
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 87bd6cabfab..1eb5129887f 100644
--- a/backends/platform/sdl/sdl-sys.h
+++ b/backends/platform/sdl/sdl-sys.h
@@ -187,12 +187,18 @@
#endif
+#ifdef USE_SDL3
+#include <SDL3/SDL.h>
+#else
#include <SDL.h>
+#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 <SDL_syswm.h>
+#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 508ffe8a6c8..16efad4d9a8 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,6 +346,15 @@ 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);
+
+ if (locked) {
+ if (!SDL_LockSurface(src)) {
+ return nullptr;
+ }
+ }
+#else
const bool locked = SDL_MUSTLOCK(src) == SDL_TRUE;
if (locked) {
@@ -323,11 +362,17 @@ SDL_Surface *copySDLSurface(SDL_Surface *src) {
return nullptr;
}
}
+#endif
+#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 +382,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<int>(displays[0]);
+ }
+ SDL_free(displays);
+ return display;
+#else
if (_window) {
int displayIndex = SDL_GetWindowDisplayIndex(_window);
if (displayIndex >= 0)
@@ -344,12 +399,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 +422,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 +453,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 +476,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 +508,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 +516,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 b3e4933874c..1aabe99df90 100644
--- a/backends/platform/sdl/sdl-window.h
+++ b/backends/platform/sdl/sdl-window.h
@@ -78,6 +78,7 @@ public:
*/
void iconifyWindow();
+#if !SDL_VERSION_ATLEAST(3, 0, 0)
/**
* Query platform specific SDL window manager information.
*
@@ -85,6 +86,7 @@ public:
* for accessing it in a version safe manner.
*/
bool getSDLWMInformation(SDL_SysWMinfo *info) const;
+#endif
/*
* Retrieve the current desktop resolution.
@@ -109,7 +111,11 @@ public:
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 @@ public:
}
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 3fc1f20b305..36e04dd0d10 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,10 +73,22 @@
#include <SDL_net.h>
#endif
-#if SDL_VERSION_ATLEAST(2, 0, 0)
+#if SDL_VERSION_ATLEAST(3, 0, 0)
+#include <SDL3/SDL_clipboard.h>
+#elif SDL_VERSION_ATLEAST(2, 0, 0)
#include <SDL_clipboard.h>
#endif
+#if SDL_VERSION_ATLEAST(3, 0, 0)
+static bool sdlGetAttribute(SDL_GLAttr attr, int *value) {
+ return SDL_GL_GetAttribute(attr, value);
+}
+#elif SDL_VERSION_ATLEAST(2, 0, 0)
+static bool sdlGetAttribute(SDL_GLattr attr, int *value) {
+ return SDL_GL_GetAttribute(attr, value) != 0;
+}
+#endif
+
OSystem_SDL::OSystem_SDL()
:
#ifdef USE_MULTIPLE_RENDERERS
@@ -97,7 +110,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 +179,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 +227,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,18 +410,28 @@ 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;
}
int glContextProfileMask, glContextMajor;
- if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &glContextProfileMask) != 0) {
+ if (!sdlGetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &glContextProfileMask)) {
SDL_DestroyWindow(window);
return;
}
if (glContextProfileMask == SDL_GL_CONTEXT_PROFILE_ES) {
- if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &glContextMajor) != 0) {
+ if (!sdlGetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &glContextMajor)) {
SDL_DestroyWindow(window);
return;
}
@@ -417,7 +454,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<char *>("SDL_VIDEO_WINDOW_POS=9000,9000"));
@@ -443,7 +484,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 +505,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 +588,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 +718,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 +740,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, "");
@@ -703,8 +776,13 @@ 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() {
@@ -748,7 +826,11 @@ void OSystem_SDL::messageBox(LogMessageType::Type type, const char *message) {
#if SDL_VERSION_ATLEAST(2, 0, 14)
bool OSystem_SDL::openUrl(const Common::String &url) {
+#if SDL_VERSION_ATLEAST(3, 0, 0)
+ if (!SDL_OpenURL(url.c_str())) {
+#else
if (SDL_OpenURL(url.c_str()) != 0) {
+#endif
warning("Failed to open URL: %s", SDL_GetError());
return false;
}
diff --git a/backends/timer/sdl/sdl-timer.cpp b/backends/timer/sdl/sdl-timer.cpp
index 81812888f8c..cda5923c3a7 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 44507806144..3dcdd2b3e72 100644
--- a/base/commandLine.cpp
+++ b/base/commandLine.cpp
@@ -1964,7 +1964,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 95f073d598d..3c8cded3c87 100755
--- a/configure
+++ b/configure
@@ -536,8 +536,8 @@ find_sdlconfig() {
IFS="$ac_save_ifs"
if test -z "$_sdlconfig"; then
- echo "none found!"
- exit 1
+ _sdlconfig="pkg-config sdl3"
+ echo "no sdl1, no sdl2 -> try sdl3"
fi
}
@@ -4257,21 +4257,31 @@ fi
# Setup SDL specifics for SDL based backends
#
if test "$_sdl" = auto ; then
- find_sdlconfig
+ if test "$_pkg_config" = "yes" && ($_pkgconfig --exists sdl || $_pkgconfig --exists sdl2); then
+ find_sdlconfig
+ _sdlversion=`$_sdlconfig --version`
+ cat > $TMPC << EOF
+#include "SDL.h"
+int main(int argc, char *argv[]) { SDL_Init(0); return 0; }
+EOF
+ elif 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
+ 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
@@ -4286,6 +4296,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"
@@ -4306,14 +4321,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`"
@@ -4339,7 +4366,6 @@ if test "$_sdlnet" = auto ; then
#include "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
if test "$_sdlnet" = yes ; then
set_var SDL_NET_LIBS "$SDL2_NET_LIBS"
@@ -4355,6 +4381,17 @@ EOF
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"
+ else
+ cat > $TMPC << EOF
+#include "SDL3/SDL_net.h"
+int main(int argc, char *argv[]) { SDLNet_Init(); return 0; }
+EOF
+ 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"
+ fi
fi
fi
@@ -6849,7 +6886,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 <SDL3/SDL.h>
+#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)
More information about the Scummvm-git-logs
mailing list