From fa2af26bd5991970a5e39bc4574f6fba0da38091 Mon Sep 17 00:00:00 2001 From: scemino Date: Thu, 2 Jan 2025 18:30:06 +0100 Subject: [PATCH] BACKENDS: Add SDL3 backend + imgui --- backends/events/sdl/sdl-common-events.cpp | 254 ++++ backends/events/sdl/sdl-events.h | 11 +- backends/events/sdl/sdl1-events.cpp | 493 ++++++++ .../sdl/{sdl-events.cpp => sdl2-events.cpp} | 369 +----- backends/events/sdl/sdl3-events.cpp | 1070 +++++++++++++++++ .../graphics/openglsdl/openglsdl-graphics.cpp | 47 +- backends/graphics/sdl/sdl-graphics.cpp | 104 +- backends/graphics/sdl/sdl-graphics.h | 5 +- .../surfacesdl/surfacesdl-graphics.cpp | 439 ++++++- .../graphics/surfacesdl/surfacesdl-graphics.h | 6 +- .../openglsdl/openglsdl-graphics3d.cpp | 42 +- backends/imgui/backends/imgui_impl_sdl3.cpp | 772 ++++++++++++ backends/imgui/backends/imgui_impl_sdl3.h | 48 + .../backends/imgui_impl_sdlrenderer3.cpp | 283 +++++ .../imgui/backends/imgui_impl_sdlrenderer3.h | 42 + backends/imgui/scummvm.patch | 1025 +++++++++++++++- backends/mixer/sdl/sdl-mixer.cpp | 66 +- backends/mixer/sdl/sdl-mixer.h | 7 + backends/module.mk | 34 +- backends/mutex/sdl/sdl-mutex.cpp | 22 +- backends/platform/sdl/macosx/macosx-window.mm | 2 +- backends/platform/sdl/sdl-sys.h | 6 + backends/platform/sdl/sdl-window.cpp | 98 +- backends/platform/sdl/sdl-window.h | 12 +- backends/platform/sdl/sdl.cpp | 75 +- backends/timer/sdl/sdl-timer.cpp | 11 + base/commandLine.cpp | 5 +- configure | 91 +- 28 files changed, 4916 insertions(+), 523 deletions(-) create mode 100644 backends/events/sdl/sdl-common-events.cpp create mode 100644 backends/events/sdl/sdl1-events.cpp rename backends/events/sdl/{sdl-events.cpp => sdl2-events.cpp} (78%) create mode 100644 backends/events/sdl/sdl3-events.cpp create mode 100644 backends/imgui/backends/imgui_impl_sdl3.cpp create mode 100644 backends/imgui/backends/imgui_impl_sdl3.h create mode 100644 backends/imgui/backends/imgui_impl_sdlrenderer3.cpp create mode 100644 backends/imgui/backends/imgui_impl_sdlrenderer3.h diff --git a/backends/events/sdl/sdl-common-events.cpp b/backends/events/sdl/sdl-common-events.cpp new file mode 100644 index 000000000000..3f51c531768c --- /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 . + * + */ + +#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(_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(_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) { + 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 2fcf289e7bc7..02b548ee9de5 100644 --- a/backends/events/sdl/sdl-events.h +++ b/backends/events/sdl/sdl-events.h @@ -73,7 +73,10 @@ class SdlEventSource : public Common::EventSource { /** Joystick */ SDL_Joystick *_joystick; -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + /** Game controller */ + SDL_Gamepad *_controller; +#elif SDL_VERSION_ATLEAST(2, 0, 0) /** Game controller */ SDL_GameController *_controller; #endif @@ -186,15 +189,17 @@ class SdlEventSource : public Common::EventSource { bool handleResizeEvent(Common::Event &event, int w, int h); /** - * Extracts unicode information for the specific key sym. + * Extracts unicode information for the specific key. * May only be used for key down events. */ - uint32 obtainUnicode(const SDL_Keysym keySym); + uint32 obtainUnicode(const SDL_KeyboardEvent &key); +#if !SDL_VERSION_ATLEAST(3, 0, 0) /** * Extracts the keycode for the specified key sym. */ SDL_Keycode obtainKeycode(const SDL_Keysym keySym); +#endif /** * Whether _fakeMouseMove contains an event we need to send. diff --git a/backends/events/sdl/sdl1-events.cpp b/backends/events/sdl/sdl1-events.cpp new file mode 100644 index 000000000000..4546a6a00ba4 --- /dev/null +++ b/backends/events/sdl/sdl1-events.cpp @@ -0,0 +1,493 @@ +/* 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 . + * + */ + +#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; + } + } + +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (_controller) { + switch (ev.type) { + case SDL_CONTROLLERBUTTONDOWN: + return handleControllerButton(ev, event, false); + case SDL_CONTROLLERBUTTONUP: + return handleControllerButton(ev, event, true); + case SDL_CONTROLLERAXISMOTION: + return handleControllerAxisMotion(ev, event); + default: + break; + } + } +#endif + + 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 3cd124e6dfa7..af2a0acb60d2 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(_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(_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 000000000000..0c82e46b808e --- /dev/null +++ b/backends/events/sdl/sdl3-events.cpp @@ -0,0 +1,1070 @@ +/* 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 . + * + */ + +#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 + } + } + } + + // 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 & 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 (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(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; + + // SDL2 documentation indicate that SDL_WINDOWEVENT_SIZE_CHANGED is sent either as a result + // of the size being changed by an external event (for example the user resizing the window + // or going fullscreen) or a call to the SDL API (for example SDL_SetWindowSize). On the + // other hand SDL_WINDOWEVENT_RESIZED is only sent for resize resulting from an external event, + // and is always preceded by a SDL_WINDOWEVENT_SIZE_CHANGED event. + // We need to handle the programmatic resize as well so that the graphics manager always know + // the current size. See comments in SdlWindow::createOrUpdateWindow for details of one case + // where we need to call SDL_SetWindowSize and we need the resulting event to be processed. + // However if the documentation is correct we can ignore SDL_WINDOWEVENT_RESIZED since when we + // get one we should always get a SDL_WINDOWEVENT_SIZE_CHANGED as well. + case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: + //case SDL_WINDOWEVENT_RESIZED: + 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 diff --git a/backends/graphics/openglsdl/openglsdl-graphics.cpp b/backends/graphics/openglsdl/openglsdl-graphics.cpp index 35dd7e3101da..947ed4b977ae 100644 --- a/backends/graphics/openglsdl/openglsdl-graphics.cpp +++ b/backends/graphics/openglsdl/openglsdl-graphics.cpp @@ -34,6 +34,26 @@ #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 + OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(SdlEventSource *eventSource, SdlWindow *window) : SdlGraphicsManager(eventSource, window), _lastRequestedHeight(0), #if SDL_VERSION_ATLEAST(2, 0, 0) @@ -201,7 +221,7 @@ OpenGLSdlGraphicsManager::~OpenGLSdlGraphicsManager() { #endif notifyContextDestroy(); - SDL_GL_DeleteContext(_glContext); + sdlGLDestroyContext(_glContext); #else if (_hwScreen) { notifyContextDestroy(); @@ -255,7 +275,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 +601,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 +627,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 +645,7 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, _glContextMajor); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, _glContextMinor); -#ifdef NINTENDO_SWITCH +#if defined(NINTENDO_SWITCH) && !SDL_VERSION_ATLEAST(3, 0, 0) // Switch quirk: Switch seems to need this flag, otherwise the screen // is zoomed when switching from Normal graphics mode to OpenGL flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; @@ -627,7 +664,7 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) { initImGui(nullptr, _glContext); #endif - 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 e2fd1e9342a7..375a65e06436 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(fx); + *y = static_cast(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; @@ -594,13 +634,21 @@ void SdlGraphicsManager::initImGui(SDL_Renderer *renderer, void *glContext) { if (!_imGuiReady && glContext) { io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!ImGui_ImplSDL3_InitForOpenGL(_window->getSDLWindow(), glContext)) { +#else if (!ImGui_ImplSDL2_InitForOpenGL(_window->getSDLWindow(), glContext)) { +#endif ImGui::DestroyContext(); return; } if (!ImGui_ImplOpenGL3_Init("#version 110")) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + ImGui_ImplSDL3_Shutdown(); +#else ImGui_ImplSDL2_Shutdown(); +#endif ImGui::DestroyContext(); return; } @@ -608,7 +656,23 @@ void SdlGraphicsManager::initImGui(SDL_Renderer *renderer, void *glContext) { _imGuiReady = true; } #endif -#ifdef USE_IMGUI_SDLRENDERER2 +#ifdef USE_IMGUI_SDLRENDERER3 + if (!_imGuiReady && renderer) { + if (!ImGui_ImplSDL3_InitForSDLRenderer(_window->getSDLWindow(), renderer)) { + ImGui::DestroyContext(); + return; + } + + if (!ImGui_ImplSDLRenderer3_Init(renderer)) { + ImGui_ImplSDL3_Shutdown(); + ImGui::DestroyContext(); + return; + } + + _imGuiReady = true; + _imGuiSDLRenderer = renderer; + } +#elif defined(USE_IMGUI_SDLRENDERER2) if (!_imGuiReady && renderer) { if (!ImGui_ImplSDL2_InitForSDLRenderer(_window->getSDLWindow(), renderer)) { ImGui::DestroyContext(); @@ -649,7 +713,11 @@ void SdlGraphicsManager::renderImGui() { _imGuiInited = true; } -#ifdef USE_IMGUI_SDLRENDERER2 +#ifdef USE_IMGUI_SDLRENDERER3 + if (_imGuiSDLRenderer) { + ImGui_ImplSDLRenderer3_NewFrame(); + } else { +#elif defined(USE_IMGUI_SDLRENDERER2) if (_imGuiSDLRenderer) { ImGui_ImplSDLRenderer2_NewFrame(); } else { @@ -657,15 +725,23 @@ void SdlGraphicsManager::renderImGui() { #ifdef USE_OPENGL ImGui_ImplOpenGL3_NewFrame(); #endif -#ifdef USE_IMGUI_SDLRENDERER2 +#if defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3) } #endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + ImGui_ImplSDL3_NewFrame(); +#else ImGui_ImplSDL2_NewFrame(); +#endif ImGui::NewFrame(); _imGuiCallbacks.render(); ImGui::Render(); -#ifdef USE_IMGUI_SDLRENDERER2 +#ifdef USE_IMGUI_SDLRENDERER3 + if (_imGuiSDLRenderer) { + ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), _imGuiSDLRenderer); + } else { +#elif defined(USE_IMGUI_SDLRENDERER2) if (_imGuiSDLRenderer) { ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), _imGuiSDLRenderer); } else { @@ -679,7 +755,7 @@ void SdlGraphicsManager::renderImGui() { ImGui::RenderPlatformWindowsDefault(); SDL_GL_MakeCurrent(backup_current_window, backup_current_context); #endif -#ifdef USE_IMGUI_SDLRENDERER2 +#if defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3) } #endif } @@ -696,7 +772,11 @@ void SdlGraphicsManager::destroyImGui() { _imGuiInited = false; _imGuiReady = false; -#ifdef USE_IMGUI_SDLRENDERER2 +#ifdef USE_IMGUI_SDLRENDERER3 + if (_imGuiSDLRenderer) { + ImGui_ImplSDLRenderer3_Shutdown(); + } else { +#elif defined(USE_IMGUI_SDLRENDERER2) if (_imGuiSDLRenderer) { ImGui_ImplSDLRenderer2_Shutdown(); } else { @@ -704,10 +784,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 af5d76514f68..8225b2d4046e 100644 --- a/backends/graphics/sdl/sdl-graphics.h +++ b/backends/graphics/sdl/sdl-graphics.h @@ -161,7 +161,10 @@ class SdlGraphicsManager : virtual public WindowedGraphicsManager, public Common * values stored by the graphics manager. */ void getWindowSizeFromSdl(int *width, int *height) const { -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + assert(_window); + SDL_GetWindowSizeInPixels(_window->getSDLWindow(), width, height); +#elif SDL_VERSION_ATLEAST(2, 0, 0) assert(_window); SDL_GL_GetDrawableSize(_window->getSDLWindow(), width, height); #else diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp index 9a63088c7009..96af9e28cbb1 100644 --- a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp +++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp @@ -62,6 +62,38 @@ #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 +} + +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 +221,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 +516,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 +556,7 @@ Graphics::PixelFormat SurfaceSdlGraphicsManager::convertSDLPixelFormat(SDL_Pixel return out; } +#endif #ifdef USE_RGB_COLOR Common::List SurfaceSdlGraphicsManager::getSupportedFormats() const { @@ -533,6 +588,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 +614,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 +924,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 +961,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 +995,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 +1027,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 +1102,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 +1112,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 +1121,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 +1135,7 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() { void SurfaceSdlGraphicsManager::unloadGFXMode() { if (_screen) { - SDL_FreeSurface(_screen); + destroySurface(_screen); _screen = nullptr; } @@ -1075,33 +1144,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 +1197,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 +1227,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 +1387,7 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() { if (_isDoubleBuf && _numDirtyRects) _forceRedraw = true; -#if defined(USE_IMGUI) && defined(USE_IMGUI_SDLRENDERER2) +#if defined(USE_IMGUI) && (defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3)) if (_imGuiCallbacks.render) { _forceRedraw = true; } @@ -1367,7 +1436,14 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() { SDL_LockSurface(srcSurf); SDL_LockSurface(_hwScreen); +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_PixelFormatDetails *pixelFormatDetails = SDL_GetPixelFormatDetails(_hwScreen->format); + if (!pixelFormatDetails) + error("SDL_GetPixelFormatDetails failed: %s", SDL_GetError()); + bpp = pixelFormatDetails->bytes_per_pixel; +#else bpp = _hwScreen->format->BytesPerPixel; +#endif srcPitch = srcSurf->pitch; dstPitch = _hwScreen->pitch; @@ -1476,11 +1552,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 +1582,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 +1629,7 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() { #if SDL_VERSION_ATLEAST(2, 0, 0) -#if defined(USE_IMGUI) && defined(USE_IMGUI_SDLRENDERER2) +#if defined(USE_IMGUI) && (defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3)) renderImGui(); #endif @@ -1576,7 +1664,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 +1770,11 @@ void SurfaceSdlGraphicsManager::copyRectToScreen(const void *buf, int pitch, int addDirtyRect(x, y, w, h, false); // Try to lock the screen surface +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_LockSurface(_screen)) +#else if (SDL_LockSurface(_screen) == -1) +#endif error("SDL_LockSurface failed: %s", SDL_GetError()); byte *dst = (byte *)_screen->pixels + y * _screen->pitch + x * _screenFormat.bytesPerPixel; @@ -1708,7 +1804,11 @@ Graphics::Surface *SurfaceSdlGraphicsManager::lockScreen() { _screenIsLocked = true; // Try to lock the screen surface +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_LockSurface(_screen)) +#else if (SDL_LockSurface(_screen) == -1) +#endif error("SDL_LockSurface failed: %s", SDL_GetError()); _framebuffer.init(_screen->w, _screen->h, _screen->pitch, _screen->pixels, _screenFormat); @@ -1944,12 +2044,22 @@ void SurfaceSdlGraphicsManager::clearOverlay() { if (SDL_BlitSurface(_screen, &src, _tmpscreen, &dst) != 0) error("SDL_BlitSurface failed: %s", SDL_GetError()); +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_PixelFormatDetails *pixelFormatDetails = SDL_GetPixelFormatDetails(_tmpscreen->format); + if (!pixelFormatDetails) + error("SDL_GetPixelFormatDetails failed: %s", SDL_GetError()); +#endif + SDL_LockSurface(_tmpscreen); SDL_LockSurface(_overlayscreen); // Transpose from game palette to RGB332 (overlay palette) if (_isHwPalette) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + byte *p = (byte *)(_tmpscreen->pixels) + _maxExtraPixels * _tmpscreen->pitch + _maxExtraPixels * pixelFormatDetails->bytes_per_pixel; +#else byte *p = (byte *)(_tmpscreen->pixels) + _maxExtraPixels * _tmpscreen->pitch + _maxExtraPixels * _tmpscreen->format->BytesPerPixel; +#endif int pitchSkip = _tmpscreen->pitch - _videoMode.screenWidth; for (int y = 0; y < _videoMode.screenHeight; y++) { for (int x = 0; x < _videoMode.screenWidth; x++, p++) { @@ -1960,7 +2070,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 +2095,11 @@ void SurfaceSdlGraphicsManager::grabOverlay(Graphics::Surface &surface) const { if (_overlayscreen == nullptr) return; +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_LockSurface(_overlayscreen)) +#else if (SDL_LockSurface(_overlayscreen) == -1) +#endif error("SDL_LockSurface failed: %s", SDL_GetError()); assert(surface.w >= _videoMode.overlayWidth); @@ -2003,7 +2121,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 +2157,11 @@ void SurfaceSdlGraphicsManager::copyRectToOverlay(const void *buf, int pitch, in // Mark the modified region as dirty addDirtyRect(x, y, w, h, true); +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_LockSurface(_overlayscreen)) +#else if (SDL_LockSurface(_overlayscreen) == -1) +#endif error("SDL_LockSurface failed: %s", SDL_GetError()); byte *dst = (byte *)_overlayscreen->pixels + y * _overlayscreen->pitch + x * bpp; @@ -2185,7 +2314,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 +2324,7 @@ void SurfaceSdlGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, } if (formatChanged && _mouseSurface) { - SDL_FreeSurface(_mouseSurface); + destroySurface(_mouseSurface); _mouseSurface = nullptr; } @@ -2206,6 +2335,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 +2354,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 +2375,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 +2400,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 +2467,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 +2495,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 +2523,55 @@ void SurfaceSdlGraphicsManager::blitCursor() { // fall back on the Normal scaler when a smaller cursor is supplied. if (_mouseScaler && _scalerPlugin->canDrawCursor() && (uint)_mouseCurState.h >= _extraPixels) { _mouseScaler->setFactor(_videoMode.scaleFactor); +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_PixelFormatDetails *pixelFormatDetails = SDL_GetPixelFormatDetails(_mouseOrigSurface->format); + if (pixelFormatDetails == nullptr) + error("getting pixel format details failed"); + _mouseScaler->scale( + (byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch * _maxExtraPixels + _maxExtraPixels * pixelFormatDetails->bytes_per_pixel, + _mouseOrigSurface->pitch, (byte *)_mouseSurface->pixels, _mouseSurface->pitch, + _mouseCurState.w, _mouseCurState.h, 0, 0); +#else _mouseScaler->scale( (byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch * _maxExtraPixels + _maxExtraPixels * _mouseOrigSurface->format->BytesPerPixel, _mouseOrigSurface->pitch, (byte *)_mouseSurface->pixels, _mouseSurface->pitch, _mouseCurState.w, _mouseCurState.h, 0, 0); +#endif } else #endif { +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_PixelFormatDetails *pixelFormatDetails = SDL_GetPixelFormatDetails(_mouseOrigSurface->format); + if (pixelFormatDetails == nullptr) + error("getting pixel format details failed"); + Graphics::scaleBlit((byte *)_mouseSurface->pixels, (const byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch * _maxExtraPixels + _maxExtraPixels * pixelFormatDetails->bytes_per_pixel, + _mouseSurface->pitch, _mouseOrigSurface->pitch, + _mouseCurState.w * _videoMode.scaleFactor, _mouseCurState.h * _videoMode.scaleFactor, + _mouseCurState.w, _mouseCurState.h, convertSDLPixelFormat(_mouseSurface->format)); +#else Graphics::scaleBlit((byte *)_mouseSurface->pixels, (const byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch * _maxExtraPixels + _maxExtraPixels * _mouseOrigSurface->format->BytesPerPixel, _mouseSurface->pitch, _mouseOrigSurface->pitch, _mouseCurState.w * _videoMode.scaleFactor, _mouseCurState.h * _videoMode.scaleFactor, _mouseCurState.w, _mouseCurState.h, convertSDLPixelFormat(_mouseSurface->format)); +#endif } } else { +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_PixelFormatDetails *srcPixelFormatDetails = SDL_GetPixelFormatDetails(_mouseOrigSurface->format); + if (srcPixelFormatDetails == nullptr) + error("getting pixel format details failed"); + const SDL_PixelFormatDetails *dstPixelFormatDetails = SDL_GetPixelFormatDetails(_mouseSurface->format); + if (dstPixelFormatDetails == nullptr) + error("getting pixel format details failed"); + Graphics::copyBlit((byte *)_mouseSurface->pixels, (const byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch * _maxExtraPixels + _maxExtraPixels * srcPixelFormatDetails->bytes_per_pixel, + _mouseSurface->pitch, _mouseOrigSurface->pitch, + _mouseCurState.w, _mouseCurState.h, dstPixelFormatDetails->bytes_per_pixel); +#else Graphics::copyBlit((byte *)_mouseSurface->pixels, (const byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch * _maxExtraPixels + _maxExtraPixels * _mouseOrigSurface->format->BytesPerPixel, _mouseSurface->pitch, _mouseOrigSurface->pitch, _mouseCurState.w, _mouseCurState.h, _mouseSurface->format->BytesPerPixel); +#endif } #ifdef USE_ASPECT @@ -2497,10 +2705,19 @@ void SurfaceSdlGraphicsManager::displayMessageOnOSD(const Common::U32String &msg if (height > _hwScreen->h) height = _hwScreen->h; +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_PixelFormatDetails *pixelFormatDetails = SDL_GetPixelFormatDetails(_hwScreen->format); + if (pixelFormatDetails == nullptr) + error("getting pixel format details failed"); + _osdMessageSurface = SDL_CreateSurface( + width, height, + SDL_GetPixelFormatForMasks(pixelFormatDetails->bits_per_pixel, pixelFormatDetails->Rmask, pixelFormatDetails->Gmask, pixelFormatDetails->Bmask, pixelFormatDetails->Amask)); +#else _osdMessageSurface = SDL_CreateRGBSurface( SDL_SWSURFACE, width, height, _hwScreen->format->BitsPerPixel, _hwScreen->format->Rmask, _hwScreen->format->Gmask, _hwScreen->format->Bmask, _hwScreen->format->Amask ); +#endif // Lock the surface if (SDL_LockSurface(_osdMessageSurface)) @@ -2508,7 +2725,11 @@ void SurfaceSdlGraphicsManager::displayMessageOnOSD(const Common::U32String &msg // Draw a dark gray rect // TODO: Rounded corners ? Border? +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_FillSurfaceRect(_osdMessageSurface, nullptr, SDL_MapSurfaceRGB(_osdMessageSurface, 64, 64, 64)); +#else SDL_FillRect(_osdMessageSurface, nullptr, SDL_MapRGB(_osdMessageSurface->format, 64, 64, 64)); +#endif Graphics::Surface dst; dst.init(_osdMessageSurface->w, _osdMessageSurface->h, _osdMessageSurface->pitch, _osdMessageSurface->pixels, @@ -2518,7 +2739,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 +2754,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 +2794,28 @@ void SurfaceSdlGraphicsManager::displayActivityIconOnOSD(const Graphics::Surface } if (_osdIconSurface) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(_osdIconSurface); +#else SDL_FreeSurface(_osdIconSurface); +#endif _osdIconSurface = nullptr; } if (icon) { const Graphics::PixelFormat &iconFormat = icon->format; +#if SDL_VERSION_ATLEAST(3, 0, 0) + _osdIconSurface = SDL_CreateSurface( + icon->w, icon->h, + SDL_GetPixelFormatForMasks( + iconFormat.bytesPerPixel * 8, + ((0xFF >> iconFormat.rLoss) << iconFormat.rShift), + ((0xFF >> iconFormat.gLoss) << iconFormat.gShift), + ((0xFF >> iconFormat.bLoss) << iconFormat.bShift), + ((0xFF >> iconFormat.aLoss) << iconFormat.aShift)) + ); +#else _osdIconSurface = SDL_CreateRGBSurface( SDL_SWSURFACE, icon->w, icon->h, iconFormat.bytesPerPixel * 8, @@ -2579,6 +2824,7 @@ void SurfaceSdlGraphicsManager::displayActivityIconOnOSD(const Graphics::Surface ((0xFF >> iconFormat.bLoss) << iconFormat.bShift), ((0xFF >> iconFormat.aLoss) << iconFormat.aShift) ); +#endif // Lock the surface if (SDL_LockSurface(_osdIconSurface)) @@ -2609,7 +2855,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 +2885,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 +3078,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 +3095,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 +3115,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 +3144,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 +3182,36 @@ SDL_Surface *SurfaceSdlGraphicsManager::SDL_SetVideoMode(int width, int height, getWindowSizeFromSdl(&_windowWidth, &_windowHeight); handleResize(_windowWidth, _windowHeight); +#if !SDL_VERSION_ATLEAST(3, 0, 0) SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, _videoMode.filtering ? "linear" : "nearest"); +#endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_PixelFormat format = SDL_PIXELFORMAT_RGB565; +#else Uint32 format = SDL_PIXELFORMAT_RGB565; +#endif _screenTexture = SDL_CreateTexture(_renderer, format, SDL_TEXTUREACCESS_STREAMING, width, height); if (!_screenTexture) { deinitializeRenderer(); return nullptr; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_SetTextureScaleMode(_screenTexture, _videoMode.filtering ? SDL_SCALEMODE_LINEAR : SDL_SCALEMODE_NEAREST); +#endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Surface *screen = SDL_CreateSurface(width, height, format); +#else SDL_Surface *screen = SDL_CreateRGBSurfaceWithFormat(0, width, height, SDL_BITSPERPIXEL(format), format); +#endif if (!screen) { deinitializeRenderer(); return nullptr; } -#if defined(USE_IMGUI) && defined(USE_IMGUI_SDLRENDERER2) +#if defined(USE_IMGUI) && (defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3)) // Setup Dear ImGui initImGui(_renderer, nullptr); #endif @@ -2940,18 +3242,37 @@ void SurfaceSdlGraphicsManager::SDL_UpdateRects(SDL_Surface *screen, int numrect SDL_RenderClear(_renderer); - if (rotangle != 0) + if (rotangle != 0) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_FRect fViewport; + SDL_RectToFRect(&viewport, &fViewport); + SDL_RenderTextureRotated(_renderer, _screenTexture, nullptr, &fViewport, rotangle, nullptr, SDL_FLIP_NONE); +#else SDL_RenderCopyEx(_renderer, _screenTexture, nullptr, &viewport, rotangle, nullptr, SDL_FLIP_NONE); - else +#endif + } else { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_FRect fViewport; + SDL_RectToFRect(&viewport, &fViewport); + SDL_RenderTexture(_renderer, _screenTexture, nullptr, &fViewport); +#else SDL_RenderCopy(_renderer, _screenTexture, nullptr, &viewport); +#endif + } } int SurfaceSdlGraphicsManager::SDL_SetColors(SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Palette *palette = SDL_CreateSurfacePalette(surface); + if (palette) { + return !SDL_SetPaletteColors(palette, colors, firstcolor, ncolors) ? 1 : 0; + } +#else if (surface->format->palette) { return !SDL_SetPaletteColors(surface->format->palette, colors, firstcolor, ncolors) ? 1 : 0; - } else { - return 0; } +#endif + return 0; } int SurfaceSdlGraphicsManager::SDL_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha) { @@ -2973,10 +3294,14 @@ int SurfaceSdlGraphicsManager::SDL_SetAlpha(SDL_Surface *surface, Uint32 flag, U } int SurfaceSdlGraphicsManager::SDL_SetColorKey(SDL_Surface *surface, Uint32 flag, Uint32 key) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + return SDL_SetSurfaceColorKey(surface, flag, key) ? -1 : 0; +#else return ::SDL_SetColorKey(surface, flag ? SDL_TRUE : SDL_FALSE, key) ? -1 : 0; +#endif } -#if defined(USE_IMGUI) && defined(USE_IMGUI_SDLRENDERER2) +#if defined(USE_IMGUI) && (defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3)) void *SurfaceSdlGraphicsManager::getImGuiTexture(const Graphics::Surface &image, const byte *palette, int palCount) { // Upload pixels into texture @@ -2989,7 +3314,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 +3329,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 87abc4f76030..ce716bd5e192 100644 --- a/backends/graphics/surfacesdl/surfacesdl-graphics.h +++ b/backends/graphics/surfacesdl/surfacesdl-graphics.h @@ -95,7 +95,11 @@ class SurfaceSdlGraphicsManager : public SdlGraphicsManager { * @param in The SDL pixel format to convert * @param out A pixel format to be written to */ +#if SDL_VERSION_ATLEAST(3, 0, 0) + Graphics::PixelFormat convertSDLPixelFormat(SDL_PixelFormat in) const; +#else Graphics::PixelFormat convertSDLPixelFormat(SDL_PixelFormat *in) const; +#endif public: void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) override; Graphics::Surface *lockScreen() override; @@ -129,7 +133,7 @@ class SurfaceSdlGraphicsManager : public SdlGraphicsManager { void notifyVideoExpose() override; void notifyResize(const int width, const int height) override; -#if defined(USE_IMGUI) && defined(USE_IMGUI_SDLRENDERER2) +#if defined(USE_IMGUI) && (defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3)) void *getImGuiTexture(const Graphics::Surface &image, const byte *palette, int palCount) override; void freeImGuiTexture(void *texture) override; #endif diff --git a/backends/graphics3d/openglsdl/openglsdl-graphics3d.cpp b/backends/graphics3d/openglsdl/openglsdl-graphics3d.cpp index f530d599be1f..3ff7b76481bc 100644 --- a/backends/graphics3d/openglsdl/openglsdl-graphics3d.cpp +++ b/backends/graphics3d/openglsdl/openglsdl-graphics3d.cpp @@ -46,6 +46,26 @@ #include "image/bmp.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 + OpenGLSdlGraphics3dManager::OpenGLSdlGraphics3dManager(SdlEventSource *eventSource, SdlWindow *window, bool supportsFrameBuffer) : SdlGraphicsManager(eventSource, window), #if SDL_VERSION_ATLEAST(2, 0, 0) @@ -385,7 +405,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 +433,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 +485,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 +815,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 +839,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 000000000000..44536554eae2 --- /dev/null +++ b/backends/imgui/backends/imgui_impl_sdl3.cpp @@ -0,0 +1,772 @@ +// dear imgui: Platform Backend for SDL3 (*EXPERIMENTAL*) +// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) +// (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) + +// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) + +// Implemented features: +// [X] Platform: Clipboard support. +// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen. +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5] +// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: IME support. + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2024-10-24: Emscripten: SDL_EVENT_MOUSE_WHEEL event doesn't require dividing by 100.0f on Emscripten. +// 2024-09-03: Update for SDL3 api changes: SDL_GetGamepads() memory ownership revert. (#7918, #7898, #7807) +// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: +// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn +// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn +// - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn +// 2024-08-19: Storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. +// 2024-08-19: ImGui_ImplSDL3_ProcessEvent() now ignores events intended for other SDL windows. (#7853) +// 2024-07-22: Update for SDL3 api changes: SDL_GetGamepads() memory ownership change. (#7807) +// 2024-07-18: Update for SDL3 api changes: SDL_GetClipboardText() memory ownership change. (#7801) +// 2024-07-15: Update for SDL3 api changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794) +// 2024-07-02: Update for SDL3 api changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762). +// 2024-07-01: Update for SDL3 api changes: SDL_SetTextInputRect() changed to SDL_SetTextInputArea(). +// 2024-06-26: Update for SDL3 api changes: SDL_StartTextInput()/SDL_StopTextInput()/SDL_SetTextInputRect() functions signatures. +// 2024-06-24: Update for SDL3 api changes: SDL_EVENT_KEY_DOWN/SDL_EVENT_KEY_UP contents. +// 2024-06-03; Update for SDL3 api changes: SDL_SYSTEM_CURSOR_ renames. +// 2024-05-15: Update for SDL3 api changes: SDLK_ renames. +// 2024-04-15: Inputs: Re-enable calling SDL_StartTextInput()/SDL_StopTextInput() as SDL3 no longer enables it by default and should play nicer with IME. +// 2024-02-13: Inputs: Fixed gamepad support. Handle gamepad disconnection. Added ImGui_ImplSDL3_SetGamepadMode(). +// 2023-11-13: Updated for recent SDL3 API changes. +// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys. +// 2023-05-04: Fixed build on Emscripten/iOS/Android. (#6391) +// 2023-04-06: Inputs: Avoid calling SDL_StartTextInput()/SDL_StopTextInput() as they don't only pertain to IME. It's unclear exactly what their relation is to IME. (#6306) +// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen. (#2702) +// 2023-02-23: Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. (#6189, #6114, #3644) +// 2023-02-07: Forked "imgui_impl_sdl2" into "imgui_impl_sdl3". Removed version checks for old feature. Refer to imgui_impl_sdl2.cpp for older changelog. + +#include "backends/imgui/imgui.h" +#ifndef IMGUI_DISABLE +#include "backends/platform/sdl/sdl.h" +#include "imgui_impl_sdl3.h" + +// Clang warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision +#endif + +// SDL +#include +#if defined(__APPLE__) +#include +#endif +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#endif + +#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) && !defined(__amigaos4__) +#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 1 +#else +#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 0 +#endif + +// FIXME-LEGACY: remove when SDL 3.1.3 preview is released. +#ifndef SDLK_APOSTROPHE +#define SDLK_APOSTROPHE SDLK_QUOTE +#endif +#ifndef SDLK_GRAVE +#define SDLK_GRAVE SDLK_BACKQUOTE +#endif + +// SDL Data +struct ImGui_ImplSDL3_Data +{ + SDL_Window* Window; + SDL_WindowID WindowID; + SDL_Renderer* Renderer; + Uint64 Time; + char* ClipboardTextData; + + // IME handling + SDL_Window* ImeWindow; + + // Mouse handling + Uint32 MouseWindowID; + int MouseButtonsDown; + SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT]; + SDL_Cursor* MouseLastCursor; + int MousePendingLeaveFrame; + bool MouseCanUseGlobalState; + + // Gamepad handling + ImVector Gamepads; + ImGui_ImplSDL3_GamepadMode GamepadMode; + bool WantUpdateGamepadsList; + + ImGui_ImplSDL3_Data() { memset((void*)this, 0, sizeof(*this)); } +}; + +// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts +// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. +// FIXME: multi-context support is not well tested and probably dysfunctional in this backend. +// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context. +static ImGui_ImplSDL3_Data* ImGui_ImplSDL3_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplSDL3_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr; +} + +// Functions +static const char* ImGui_ImplSDL3_GetClipboardText(ImGuiContext*) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + if (bd->ClipboardTextData) + SDL_free(bd->ClipboardTextData); + const char* sdl_clipboard_text = SDL_GetClipboardText(); + bd->ClipboardTextData = sdl_clipboard_text ? SDL_strdup(sdl_clipboard_text) : nullptr; + return bd->ClipboardTextData; +} + +static void ImGui_ImplSDL3_SetClipboardText(ImGuiContext*, const char* text) +{ + SDL_SetClipboardText(text); +} + +static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + SDL_WindowID window_id = (SDL_WindowID)(intptr_t)viewport->PlatformHandle; + SDL_Window* window = SDL_GetWindowFromID(window_id); + if ((data->WantVisible == false || bd->ImeWindow != window) && bd->ImeWindow != nullptr) + { + SDL_StopTextInput(bd->ImeWindow); + bd->ImeWindow = nullptr; + } + if (data->WantVisible) + { + SDL_Rect r; + r.x = (int)data->InputPos.x; + r.y = (int)data->InputPos.y; + r.w = 1; + r.h = (int)data->InputLineHeight; + SDL_SetTextInputArea(window, &r, 0); + SDL_StartTextInput(window); + bd->ImeWindow = window; + } +} + +// Not static to allow third-party code to use that if they want to (but undocumented) +ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode); +ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode) +{ + // Keypad doesn't have individual key values in SDL3 + switch (scancode) + { + case SDL_SCANCODE_KP_0: return ImGuiKey_Keypad0; + case SDL_SCANCODE_KP_1: return ImGuiKey_Keypad1; + case SDL_SCANCODE_KP_2: return ImGuiKey_Keypad2; + case SDL_SCANCODE_KP_3: return ImGuiKey_Keypad3; + case SDL_SCANCODE_KP_4: return ImGuiKey_Keypad4; + case SDL_SCANCODE_KP_5: return ImGuiKey_Keypad5; + case SDL_SCANCODE_KP_6: return ImGuiKey_Keypad6; + case SDL_SCANCODE_KP_7: return ImGuiKey_Keypad7; + case SDL_SCANCODE_KP_8: return ImGuiKey_Keypad8; + case SDL_SCANCODE_KP_9: return ImGuiKey_Keypad9; + case SDL_SCANCODE_KP_PERIOD: return ImGuiKey_KeypadDecimal; + case SDL_SCANCODE_KP_DIVIDE: return ImGuiKey_KeypadDivide; + case SDL_SCANCODE_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; + case SDL_SCANCODE_KP_MINUS: return ImGuiKey_KeypadSubtract; + case SDL_SCANCODE_KP_PLUS: return ImGuiKey_KeypadAdd; + case SDL_SCANCODE_KP_ENTER: return ImGuiKey_KeypadEnter; + case SDL_SCANCODE_KP_EQUALS: return ImGuiKey_KeypadEqual; + default: break; + } + switch (keycode) + { + case SDLK_TAB: return ImGuiKey_Tab; + case SDLK_LEFT: return ImGuiKey_LeftArrow; + case SDLK_RIGHT: return ImGuiKey_RightArrow; + case SDLK_UP: return ImGuiKey_UpArrow; + case SDLK_DOWN: return ImGuiKey_DownArrow; + case SDLK_PAGEUP: return ImGuiKey_PageUp; + case SDLK_PAGEDOWN: return ImGuiKey_PageDown; + case SDLK_HOME: return ImGuiKey_Home; + case SDLK_END: return ImGuiKey_End; + case SDLK_INSERT: return ImGuiKey_Insert; + case SDLK_DELETE: return ImGuiKey_Delete; + case SDLK_BACKSPACE: return ImGuiKey_Backspace; + case SDLK_SPACE: return ImGuiKey_Space; + case SDLK_RETURN: return ImGuiKey_Enter; + case SDLK_ESCAPE: return ImGuiKey_Escape; + case SDLK_APOSTROPHE: return ImGuiKey_Apostrophe; + case SDLK_COMMA: return ImGuiKey_Comma; + case SDLK_MINUS: return ImGuiKey_Minus; + case SDLK_PERIOD: return ImGuiKey_Period; + case SDLK_SLASH: return ImGuiKey_Slash; + case SDLK_SEMICOLON: return ImGuiKey_Semicolon; + case SDLK_EQUALS: return ImGuiKey_Equal; + case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket; + case SDLK_BACKSLASH: return ImGuiKey_Backslash; + case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket; + case SDLK_GRAVE: return ImGuiKey_GraveAccent; + case SDLK_CAPSLOCK: return ImGuiKey_CapsLock; + case SDLK_SCROLLLOCK: return ImGuiKey_ScrollLock; + case SDLK_NUMLOCKCLEAR: return ImGuiKey_NumLock; + case SDLK_PRINTSCREEN: return ImGuiKey_PrintScreen; + case SDLK_PAUSE: return ImGuiKey_Pause; + case SDLK_LCTRL: return ImGuiKey_LeftCtrl; + case SDLK_LSHIFT: return ImGuiKey_LeftShift; + case SDLK_LALT: return ImGuiKey_LeftAlt; + case SDLK_LGUI: return ImGuiKey_LeftSuper; + case SDLK_RCTRL: return ImGuiKey_RightCtrl; + case SDLK_RSHIFT: return ImGuiKey_RightShift; + case SDLK_RALT: return ImGuiKey_RightAlt; + case SDLK_RGUI: return ImGuiKey_RightSuper; + case SDLK_APPLICATION: return ImGuiKey_Menu; + case SDLK_0: return ImGuiKey_0; + case SDLK_1: return ImGuiKey_1; + case SDLK_2: return ImGuiKey_2; + case SDLK_3: return ImGuiKey_3; + case SDLK_4: return ImGuiKey_4; + case SDLK_5: return ImGuiKey_5; + case SDLK_6: return ImGuiKey_6; + case SDLK_7: return ImGuiKey_7; + case SDLK_8: return ImGuiKey_8; + case SDLK_9: return ImGuiKey_9; + case SDLK_A: return ImGuiKey_A; + case SDLK_B: return ImGuiKey_B; + case SDLK_C: return ImGuiKey_C; + case SDLK_D: return ImGuiKey_D; + case SDLK_E: return ImGuiKey_E; + case SDLK_F: return ImGuiKey_F; + case SDLK_G: return ImGuiKey_G; + case SDLK_H: return ImGuiKey_H; + case SDLK_I: return ImGuiKey_I; + case SDLK_J: return ImGuiKey_J; + case SDLK_K: return ImGuiKey_K; + case SDLK_L: return ImGuiKey_L; + case SDLK_M: return ImGuiKey_M; + case SDLK_N: return ImGuiKey_N; + case SDLK_O: return ImGuiKey_O; + case SDLK_P: return ImGuiKey_P; + case SDLK_Q: return ImGuiKey_Q; + case SDLK_R: return ImGuiKey_R; + case SDLK_S: return ImGuiKey_S; + case SDLK_T: return ImGuiKey_T; + case SDLK_U: return ImGuiKey_U; + case SDLK_V: return ImGuiKey_V; + case SDLK_W: return ImGuiKey_W; + case SDLK_X: return ImGuiKey_X; + case SDLK_Y: return ImGuiKey_Y; + case SDLK_Z: return ImGuiKey_Z; + case SDLK_F1: return ImGuiKey_F1; + case SDLK_F2: return ImGuiKey_F2; + case SDLK_F3: return ImGuiKey_F3; + case SDLK_F4: return ImGuiKey_F4; + case SDLK_F5: return ImGuiKey_F5; + case SDLK_F6: return ImGuiKey_F6; + case SDLK_F7: return ImGuiKey_F7; + case SDLK_F8: return ImGuiKey_F8; + case SDLK_F9: return ImGuiKey_F9; + case SDLK_F10: return ImGuiKey_F10; + case SDLK_F11: return ImGuiKey_F11; + case SDLK_F12: return ImGuiKey_F12; + case SDLK_F13: return ImGuiKey_F13; + case SDLK_F14: return ImGuiKey_F14; + case SDLK_F15: return ImGuiKey_F15; + case SDLK_F16: return ImGuiKey_F16; + case SDLK_F17: return ImGuiKey_F17; + case SDLK_F18: return ImGuiKey_F18; + case SDLK_F19: return ImGuiKey_F19; + case SDLK_F20: return ImGuiKey_F20; + case SDLK_F21: return ImGuiKey_F21; + case SDLK_F22: return ImGuiKey_F22; + case SDLK_F23: return ImGuiKey_F23; + case SDLK_F24: return ImGuiKey_F24; + case SDLK_AC_BACK: return ImGuiKey_AppBack; + case SDLK_AC_FORWARD: return ImGuiKey_AppForward; + default: break; + } + return ImGuiKey_None; +} + +static void ImGui_ImplSDL3_UpdateKeyModifiers(SDL_Keymod sdl_key_mods) +{ + ImGuiIO& io = ImGui::GetIO(); + io.AddKeyEvent(ImGuiMod_Ctrl, (sdl_key_mods & SDL_KMOD_CTRL) != 0); + io.AddKeyEvent(ImGuiMod_Shift, (sdl_key_mods & SDL_KMOD_SHIFT) != 0); + io.AddKeyEvent(ImGuiMod_Alt, (sdl_key_mods & SDL_KMOD_ALT) != 0); + io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & SDL_KMOD_GUI) != 0); +} + + +static ImGuiViewport* ImGui_ImplSDL3_GetViewportForWindowID(SDL_WindowID window_id) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : nullptr; +} + +// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. +// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. +// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. +// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. +// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field. +bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL3_Init()?"); + ImGuiIO& io = ImGui::GetIO(); + + switch (event->type) + { + case SDL_EVENT_MOUSE_MOTION: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->motion.windowID) == nullptr) + return false; + ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y); + io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); + io.AddMousePosEvent(mouse_pos.x, mouse_pos.y); + return true; + } + case SDL_EVENT_MOUSE_WHEEL: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->wheel.windowID) == nullptr) + return false; + //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY); + float wheel_x = -event->wheel.x; + float wheel_y = event->wheel.y; + io.AddMouseSourceEvent(event->wheel.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); + io.AddMouseWheelEvent(wheel_x, wheel_y); + return true; + } + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->button.windowID) == nullptr) + return false; + int mouse_button = -1; + if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; } + if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; } + if (event->button.button == SDL_BUTTON_MIDDLE) { mouse_button = 2; } + if (event->button.button == SDL_BUTTON_X1) { mouse_button = 3; } + if (event->button.button == SDL_BUTTON_X2) { mouse_button = 4; } + if (mouse_button == -1) + break; + io.AddMouseSourceEvent(event->button.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); + io.AddMouseButtonEvent(mouse_button, (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN)); + bd->MouseButtonsDown = (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) ? (bd->MouseButtonsDown | (1 << mouse_button)) : (bd->MouseButtonsDown & ~(1 << mouse_button)); + return true; + } + case SDL_EVENT_TEXT_INPUT: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->text.windowID) == nullptr) + return false; + io.AddInputCharactersUTF8(event->text.text); + return true; + } + case SDL_EVENT_KEY_DOWN: + case SDL_EVENT_KEY_UP: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->key.windowID) == nullptr) + return false; + //IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%d: key=%d, scancode=%d, mod=%X\n", (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP", event->key.key, event->key.scancode, event->key.mod); + ImGui_ImplSDL3_UpdateKeyModifiers((SDL_Keymod)event->key.mod); + ImGuiKey key = ImGui_ImplSDL3_KeyEventToImGuiKey(event->key.key, event->key.scancode); + io.AddKeyEvent(key, (event->type == SDL_EVENT_KEY_DOWN)); + io.SetKeyEventNativeData(key, event->key.key, event->key.scancode, event->key.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions. + return true; + } + case SDL_EVENT_WINDOW_MOUSE_ENTER: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr) + return false; + bd->MouseWindowID = event->window.windowID; + bd->MousePendingLeaveFrame = 0; + return true; + } + // - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late, + // causing SDL_WINDOWEVENT_LEAVE on previous frame to interrupt drag operation by clear mouse position. This is why + // we delay process the SDL_WINDOWEVENT_LEAVE events by one frame. See issue #5012 for details. + // FIXME: Unconfirmed whether this is still needed with SDL3. + case SDL_EVENT_WINDOW_MOUSE_LEAVE: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr) + return false; + bd->MousePendingLeaveFrame = ImGui::GetFrameCount() + 1; + return true; + } + case SDL_EVENT_WINDOW_FOCUS_GAINED: + case SDL_EVENT_WINDOW_FOCUS_LOST: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr) + return false; + io.AddFocusEvent(event->type == SDL_EVENT_WINDOW_FOCUS_GAINED); + return true; + } + case SDL_EVENT_GAMEPAD_ADDED: + case SDL_EVENT_GAMEPAD_REMOVED: + { + bd->WantUpdateGamepadsList = true; + return true; + } + } + return false; +} + +static void ImGui_ImplSDL3_SetupPlatformHandles(ImGuiViewport* viewport, SDL_Window* window) +{ + viewport->PlatformHandle = (void*)(intptr_t)SDL_GetWindowID(window); + viewport->PlatformHandleRaw = nullptr; +#if defined(_WIN32) && !defined(__WINRT__) + viewport->PlatformHandleRaw = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr); +#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA) + viewport->PlatformHandleRaw = SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr); +#endif +} + +static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void* sdl_gl_context) +{ + ImGuiIO& io = ImGui::GetIO(); + IMGUI_CHECKVERSION(); + IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); + IM_UNUSED(sdl_gl_context); // Unused in this branch + + // Check and store if we are on a SDL backend that supports global mouse position + // ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list) + bool mouse_can_use_global_state = false; +#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE + const char* sdl_backend = SDL_GetCurrentVideoDriver(); + const char* global_mouse_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" }; + for (int n = 0; n < IM_ARRAYSIZE(global_mouse_whitelist); n++) + if (strncmp(sdl_backend, global_mouse_whitelist[n], strlen(global_mouse_whitelist[n])) == 0) + mouse_can_use_global_state = true; +#endif + + // Setup backend capabilities flags + ImGui_ImplSDL3_Data* bd = IM_NEW(ImGui_ImplSDL3_Data)(); + io.BackendPlatformUserData = (void*)bd; + io.BackendPlatformName = "imgui_impl_sdl3"; + io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) + io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + + bd->Window = window; + bd->WindowID = SDL_GetWindowID(window); + bd->Renderer = renderer; + bd->MouseCanUseGlobalState = mouse_can_use_global_state; + + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL3_SetClipboardText; + platform_io.Platform_GetClipboardTextFn = ImGui_ImplSDL3_GetClipboardText; + platform_io.Platform_SetImeDataFn = ImGui_ImplSDL3_PlatformSetImeData; + + // Gamepad handling + bd->GamepadMode = ImGui_ImplSDL3_GamepadMode_AutoFirst; + bd->WantUpdateGamepadsList = true; + + // Load mouse cursors + bd->MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT); + bd->MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_TEXT); + bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_MOVE); + bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NS_RESIZE); + bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_EW_RESIZE); + bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NESW_RESIZE); + bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NWSE_RESIZE); + bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER); + bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NOT_ALLOWED); + + // Set platform dependent data in viewport + // Our mouse update function expect PlatformHandle to be filled for the main viewport + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGui_ImplSDL3_SetupPlatformHandles(main_viewport, window); + + // From 2.0.5: Set SDL hint to receive mouse click events on window focus, otherwise SDL doesn't emit the event. + // Without this, when clicking to gain focus, our widgets wouldn't activate even though they showed as hovered. + // (This is unfortunately a global SDL setting, so enabling it might have a side-effect on your application. + // It is unlikely to make a difference, but if your app absolutely needs to ignore the initial on-focus click: + // you can ignore SDL_EVENT_MOUSE_BUTTON_DOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED) +#ifdef SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH + SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); +#endif + + // From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710) +#ifdef SDL_HINT_MOUSE_AUTO_CAPTURE + SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0"); +#endif + + return true; +} + +bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context) +{ + IM_UNUSED(sdl_gl_context); // Viewport branch will need this. + return ImGui_ImplSDL3_Init(window, nullptr, sdl_gl_context); +} + +bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window) +{ + return ImGui_ImplSDL3_Init(window, nullptr, nullptr); +} + +bool ImGui_ImplSDL3_InitForD3D(SDL_Window* window) +{ +#if !defined(_WIN32) + IM_ASSERT(0 && "Unsupported"); +#endif + return ImGui_ImplSDL3_Init(window, nullptr, nullptr); +} + +bool ImGui_ImplSDL3_InitForMetal(SDL_Window* window) +{ + return ImGui_ImplSDL3_Init(window, nullptr, nullptr); +} + +bool ImGui_ImplSDL3_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer) +{ + return ImGui_ImplSDL3_Init(window, renderer, nullptr); +} + +bool ImGui_ImplSDL3_InitForOther(SDL_Window* window) +{ + return ImGui_ImplSDL3_Init(window, nullptr, nullptr); +} + +static void ImGui_ImplSDL3_CloseGamepads(); + +void ImGui_ImplSDL3_Shutdown() +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + + if (bd->ClipboardTextData) + SDL_free(bd->ClipboardTextData); + for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) + SDL_DestroyCursor(bd->MouseCursors[cursor_n]); + ImGui_ImplSDL3_CloseGamepads(); + + io.BackendPlatformName = nullptr; + io.BackendPlatformUserData = nullptr; + io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); + IM_DELETE(bd); +} + +static void ImGui_ImplSDL3_UpdateMouseData() +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + ImGuiIO& io = ImGui::GetIO(); + + // We forward mouse input when hovered or captured (via SDL_EVENT_MOUSE_MOTION) or when focused (below) +#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE + // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside + SDL_CaptureMouse(bd->MouseButtonsDown != 0); + SDL_Window* focused_window = SDL_GetKeyboardFocus(); + const bool is_app_focused = (bd->Window == focused_window); +#else + SDL_Window* focused_window = bd->Window; + const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only +#endif + if (is_app_focused) + { + // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user) + if (io.WantSetMousePos) + SDL_WarpMouseInWindow(bd->Window, io.MousePos.x, io.MousePos.y); + + // (Optional) Fallback to provide mouse position when focused (SDL_EVENT_MOUSE_MOTION already provides this when hovered or captured) + if (bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0) + { + // Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) + float mouse_x_global, mouse_y_global; + int window_x, window_y; + SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global); + SDL_GetWindowPosition(focused_window, &window_x, &window_y); + io.AddMousePosEvent(mouse_x_global - window_x, mouse_y_global - window_y); + } + } +} + +static void ImGui_ImplSDL3_UpdateMouseCursor() +{ + ImGuiIO& io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) + return; + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + + ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); + if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) + { + // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor + SDL_HideCursor(); + } + else + { + // Show OS mouse cursor + SDL_Cursor* expected_cursor = bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]; + if (bd->MouseLastCursor != expected_cursor) + { + SDL_SetCursor(expected_cursor); // SDL function doesn't have an early out (see #6113) + bd->MouseLastCursor = expected_cursor; + } + SDL_ShowCursor(); + } +} + +static void ImGui_ImplSDL3_CloseGamepads() +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + if (bd->GamepadMode != ImGui_ImplSDL3_GamepadMode_Manual) + for (SDL_Gamepad* gamepad : bd->Gamepads) + SDL_CloseGamepad(gamepad); + bd->Gamepads.resize(0); +} + +void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array, int manual_gamepads_count) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + ImGui_ImplSDL3_CloseGamepads(); + if (mode == ImGui_ImplSDL3_GamepadMode_Manual) + { + IM_ASSERT(manual_gamepads_array != nullptr && manual_gamepads_count > 0); + for (int n = 0; n < manual_gamepads_count; n++) + bd->Gamepads.push_back(manual_gamepads_array[n]); + } + else + { + IM_ASSERT(manual_gamepads_array == nullptr && manual_gamepads_count <= 0); + bd->WantUpdateGamepadsList = true; + } + bd->GamepadMode = mode; +} + +static void ImGui_ImplSDL3_UpdateGamepadButton(ImGui_ImplSDL3_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GamepadButton button_no) +{ + bool merged_value = false; + for (SDL_Gamepad* gamepad : bd->Gamepads) + merged_value |= SDL_GetGamepadButton(gamepad, button_no) != 0; + io.AddKeyEvent(key, merged_value); +} + +static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; } +static void ImGui_ImplSDL3_UpdateGamepadAnalog(ImGui_ImplSDL3_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GamepadAxis axis_no, float v0, float v1) +{ + float merged_value = 0.0f; + for (SDL_Gamepad* gamepad : bd->Gamepads) + { + float vn = Saturate((float)(SDL_GetGamepadAxis(gamepad, axis_no) - v0) / (float)(v1 - v0)); + if (merged_value < vn) + merged_value = vn; + } + io.AddKeyAnalogEvent(key, merged_value > 0.1f, merged_value); +} + +static void ImGui_ImplSDL3_UpdateGamepads() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + + // Update list of gamepads to use + if (bd->WantUpdateGamepadsList && bd->GamepadMode != ImGui_ImplSDL3_GamepadMode_Manual) + { + ImGui_ImplSDL3_CloseGamepads(); + int sdl_gamepads_count = 0; + SDL_JoystickID* sdl_gamepads = SDL_GetGamepads(&sdl_gamepads_count); + for (int n = 0; n < sdl_gamepads_count; n++) + if (SDL_Gamepad* gamepad = SDL_OpenGamepad(sdl_gamepads[n])) + { + bd->Gamepads.push_back(gamepad); + if (bd->GamepadMode == ImGui_ImplSDL3_GamepadMode_AutoFirst) + break; + } + bd->WantUpdateGamepadsList = false; + SDL_free(sdl_gamepads); + } + + // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. + if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) + return; + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; + if (bd->Gamepads.Size == 0) + return; + io.BackendFlags |= ImGuiBackendFlags_HasGamepad; + + // Update gamepad inputs + const int thumb_dead_zone = 8000; // SDL_gamepad.h suggests using this value. + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadStart, SDL_GAMEPAD_BUTTON_START); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadBack, SDL_GAMEPAD_BUTTON_BACK); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceLeft, SDL_GAMEPAD_BUTTON_WEST); // Xbox X, PS Square + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceRight, SDL_GAMEPAD_BUTTON_EAST); // Xbox B, PS Circle + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceUp, SDL_GAMEPAD_BUTTON_NORTH); // Xbox Y, PS Triangle + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceDown, SDL_GAMEPAD_BUTTON_SOUTH); // Xbox A, PS Cross + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadLeft, SDL_GAMEPAD_BUTTON_DPAD_LEFT); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadRight, SDL_GAMEPAD_BUTTON_DPAD_RIGHT); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadUp, SDL_GAMEPAD_BUTTON_DPAD_UP); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadDown, SDL_GAMEPAD_BUTTON_DPAD_DOWN); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL1, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR1, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadL2, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, 0.0f, 32767); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadR2, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, 0.0f, 32767); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL3, SDL_GAMEPAD_BUTTON_LEFT_STICK); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR3, SDL_GAMEPAD_BUTTON_RIGHT_STICK); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickLeft, SDL_GAMEPAD_AXIS_LEFTX, -thumb_dead_zone, -32768); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickRight, SDL_GAMEPAD_AXIS_LEFTX, +thumb_dead_zone, +32767); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickUp, SDL_GAMEPAD_AXIS_LEFTY, -thumb_dead_zone, -32768); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickDown, SDL_GAMEPAD_AXIS_LEFTY, +thumb_dead_zone, +32767); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickLeft, SDL_GAMEPAD_AXIS_RIGHTX, -thumb_dead_zone, -32768); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickRight, SDL_GAMEPAD_AXIS_RIGHTX, +thumb_dead_zone, +32767); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickUp, SDL_GAMEPAD_AXIS_RIGHTY, -thumb_dead_zone, -32768); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickDown, SDL_GAMEPAD_AXIS_RIGHTY, +thumb_dead_zone, +32767); +} + +void ImGui_ImplSDL3_NewFrame() +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL3_Init()?"); + ImGuiIO& io = ImGui::GetIO(); + + // Setup display size (every frame to accommodate for window resizing) + int w, h; + int display_w, display_h; + SDL_GetWindowSize(bd->Window, &w, &h); + if (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_MINIMIZED) + w = h = 0; + SDL_GetWindowSizeInPixels(bd->Window, &display_w, &display_h); + io.DisplaySize = ImVec2((float)w, (float)h); + if (w > 0 && h > 0) + io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h); + + // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution) + // (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. Happens in VMs and Emscripten, see #6189, #6114, #3644) + static Uint64 frequency = SDL_GetPerformanceFrequency(); + Uint64 current_time = SDL_GetPerformanceCounter(); + if (current_time <= bd->Time) + current_time = bd->Time + 1; + io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f); + bd->Time = current_time; + + if (bd->MousePendingLeaveFrame && bd->MousePendingLeaveFrame >= ImGui::GetFrameCount() && bd->MouseButtonsDown == 0) + { + bd->MouseWindowID = 0; + bd->MousePendingLeaveFrame = 0; + io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); + } + + ImGui_ImplSDL3_UpdateMouseData(); + ImGui_ImplSDL3_UpdateMouseCursor(); + + // Update game controllers (if enabled and available) + ImGui_ImplSDL3_UpdateGamepads(); +} + +//----------------------------------------------------------------------------- + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui/backends/imgui_impl_sdl3.h b/backends/imgui/backends/imgui_impl_sdl3.h new file mode 100644 index 000000000000..6483fb5d06bd --- /dev/null +++ b/backends/imgui/backends/imgui_impl_sdl3.h @@ -0,0 +1,48 @@ +// dear imgui: Platform Backend for SDL3 (*EXPERIMENTAL*) +// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) +// (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) + +// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) + +// Implemented features: +// [X] Platform: Clipboard support. +// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen. +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5] +// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: IME support. + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +#pragma once +#include "backends/imgui/imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE + +struct SDL_Window; +struct SDL_Renderer; +struct SDL_Gamepad; +typedef union SDL_Event SDL_Event; + +// Follow "Getting Started" link and check examples/ folder to learn about using backends! +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context); +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window); +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForD3D(SDL_Window* window); +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForMetal(SDL_Window* window); +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer); +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForOther(SDL_Window* window); +IMGUI_IMPL_API void ImGui_ImplSDL3_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplSDL3_NewFrame(); +IMGUI_IMPL_API bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event); + +// Gamepad selection automatically starts in AutoFirst mode, picking first available SDL_Gamepad. You may override this. +// When using manual mode, caller is responsible for opening/closing gamepad. +enum ImGui_ImplSDL3_GamepadMode { ImGui_ImplSDL3_GamepadMode_AutoFirst, ImGui_ImplSDL3_GamepadMode_AutoAll, ImGui_ImplSDL3_GamepadMode_Manual }; +IMGUI_IMPL_API void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array = nullptr, int manual_gamepads_count = -1); + +#endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui/backends/imgui_impl_sdlrenderer3.cpp new file mode 100644 index 000000000000..153d4b767c5e --- /dev/null +++ b/backends/imgui/backends/imgui_impl_sdlrenderer3.cpp @@ -0,0 +1,283 @@ +// dear imgui: Renderer Backend for SDL_Renderer for SDL3 +// (Requires: SDL 3.0.0+) + +// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) + +// Note how SDL_Renderer is an _optional_ component of SDL3. +// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. +// If your application will want to render any non trivial amount of graphics other than UI, +// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and +// it might be difficult to step out of those boundaries. + +// Implemented features: +// [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. + +// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +// CHANGELOG +// 2024-07-01: Update for SDL3 api changes: SDL_RenderGeometryRaw() uint32 version was removed (SDL#9009). +// 2024-05-14: *BREAKING CHANGE* ImGui_ImplSDLRenderer3_RenderDrawData() requires SDL_Renderer* passed as parameter. +// 2024-02-12: Amend to query SDL_RenderViewportSet() and restore viewport accordingly. +// 2023-05-30: Initial version. + +#include "backends/imgui/imgui.h" +#ifndef IMGUI_DISABLE +#include "imgui_impl_sdlrenderer3.h" +#include // intptr_t + +// Clang warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#endif + +// SDL +#include +#if !SDL_VERSION_ATLEAST(3,0,0) +#error This backend requires SDL 3.0.0+ +#endif + +// SDL_Renderer data +struct ImGui_ImplSDLRenderer3_Data +{ + SDL_Renderer* Renderer; // Main viewport's renderer + SDL_Texture* FontTexture; + ImVector ColorBuffer; + + ImGui_ImplSDLRenderer3_Data() { memset((void*)this, 0, sizeof(*this)); } +}; + +// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts +// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. +static ImGui_ImplSDLRenderer3_Data* ImGui_ImplSDLRenderer3_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplSDLRenderer3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; +} + +// Functions +bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer) +{ + ImGuiIO& io = ImGui::GetIO(); + IMGUI_CHECKVERSION(); + IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); + IM_ASSERT(renderer != nullptr && "SDL_Renderer not initialized!"); + + // Setup backend capabilities flags + ImGui_ImplSDLRenderer3_Data* bd = IM_NEW(ImGui_ImplSDLRenderer3_Data)(); + io.BackendRendererUserData = (void*)bd; + io.BackendRendererName = "imgui_impl_sdlrenderer3"; + io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + + bd->Renderer = renderer; + + return true; +} + +void ImGui_ImplSDLRenderer3_Shutdown() +{ + ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); + IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + + ImGui_ImplSDLRenderer3_DestroyDeviceObjects(); + + io.BackendRendererName = nullptr; + io.BackendRendererUserData = nullptr; + io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + IM_DELETE(bd); +} + +static void ImGui_ImplSDLRenderer3_SetupRenderState(SDL_Renderer* renderer) +{ + // Clear out any viewports and cliprect set by the user + // FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process. + SDL_SetRenderViewport(renderer, nullptr); + SDL_SetRenderClipRect(renderer, nullptr); +} + +void ImGui_ImplSDLRenderer3_NewFrame() +{ + ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDLRenderer3_Init()?"); + + if (!bd->FontTexture) + ImGui_ImplSDLRenderer3_CreateDeviceObjects(); +} + +// https://github.com/libsdl-org/SDL/issues/9009 +static int SDL_RenderGeometryRaw8BitColor(SDL_Renderer* renderer, ImVector& colors_out, SDL_Texture* texture, const float* xy, int xy_stride, const SDL_Color* color, int color_stride, const float* uv, int uv_stride, int num_vertices, const void* indices, int num_indices, int size_indices) +{ + const Uint8* color2 = (const Uint8*)color; + colors_out.resize(num_vertices); + SDL_FColor* color3 = colors_out.Data; + for (int i = 0; i < num_vertices; i++) + { + color3[i].r = color->r / 255.0f; + color3[i].g = color->g / 255.0f; + color3[i].b = color->b / 255.0f; + color3[i].a = color->a / 255.0f; + color2 += color_stride; + color = (const SDL_Color*)color2; + } + return SDL_RenderGeometryRaw(renderer, texture, xy, xy_stride, color3, sizeof(*color3), uv, uv_stride, num_vertices, indices, num_indices, size_indices); +} + +void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* renderer) +{ + ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); + + // If there's a scale factor set by the user, use that instead + // If the user has specified a scale factor to SDL_Renderer already via SDL_RenderSetScale(), SDL will scale whatever we pass + // to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here. + float rsx = 1.0f; + float rsy = 1.0f; + SDL_GetRenderScale(renderer, &rsx, &rsy); + ImVec2 render_scale; + render_scale.x = (rsx == 1.0f) ? draw_data->FramebufferScale.x : 1.0f; + render_scale.y = (rsy == 1.0f) ? draw_data->FramebufferScale.y : 1.0f; + + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + int fb_width = (int)(draw_data->DisplaySize.x * render_scale.x); + int fb_height = (int)(draw_data->DisplaySize.y * render_scale.y); + if (fb_width == 0 || fb_height == 0) + return; + + // Backup SDL_Renderer state that will be modified to restore it afterwards + struct BackupSDLRendererState + { + SDL_Rect Viewport; + bool ViewportEnabled; + bool ClipEnabled; + SDL_Rect ClipRect; + }; + BackupSDLRendererState old = {}; + old.ViewportEnabled = SDL_RenderViewportSet(renderer); + old.ClipEnabled = SDL_RenderClipEnabled(renderer); + SDL_GetRenderViewport(renderer, &old.Viewport); + SDL_GetRenderClipRect(renderer, &old.ClipRect); + + // Will project scissor/clipping rectangles into framebuffer space + ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports + ImVec2 clip_scale = render_scale; + + // Render command lists + ImGui_ImplSDLRenderer3_SetupRenderState(renderer); + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; + const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; + + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback) + { + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + ImGui_ImplSDLRenderer3_SetupRenderState(renderer); + else + pcmd->UserCallback(cmd_list, pcmd); + } + else + { + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); + ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); + if (clip_min.x < 0.0f) { clip_min.x = 0.0f; } + if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } + if (clip_max.x > (float)fb_width) { clip_max.x = (float)fb_width; } + if (clip_max.y > (float)fb_height) { clip_max.y = (float)fb_height; } + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) + continue; + + SDL_Rect r = { (int)(clip_min.x), (int)(clip_min.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y) }; + SDL_SetRenderClipRect(renderer, &r); + + const float* xy = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, pos)); + const float* uv = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, uv)); + const SDL_Color* color = (const SDL_Color*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, col)); // SDL 2.0.19+ + + // Bind texture, Draw + SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID(); + SDL_RenderGeometryRaw8BitColor(renderer, bd->ColorBuffer, tex, + xy, (int)sizeof(ImDrawVert), + color, (int)sizeof(ImDrawVert), + uv, (int)sizeof(ImDrawVert), + cmd_list->VtxBuffer.Size - pcmd->VtxOffset, + idx_buffer + pcmd->IdxOffset, pcmd->ElemCount, sizeof(ImDrawIdx)); + } + } + } + + // Restore modified SDL_Renderer state + SDL_SetRenderViewport(renderer, old.ViewportEnabled ? &old.Viewport : nullptr); + SDL_SetRenderClipRect(renderer, old.ClipEnabled ? &old.ClipRect : nullptr); +} + +// Called by Init/NewFrame/Shutdown +bool ImGui_ImplSDLRenderer3_CreateFontsTexture() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); + + // Build texture atlas + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. + + // Upload texture to graphics system + // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + bd->FontTexture = SDL_CreateTexture(bd->Renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, height); + if (bd->FontTexture == nullptr) + { + SDL_Log("error creating texture"); + return false; + } + SDL_UpdateTexture(bd->FontTexture, nullptr, pixels, 4 * width); + SDL_SetTextureBlendMode(bd->FontTexture, SDL_BLENDMODE_BLEND); + SDL_SetTextureScaleMode(bd->FontTexture, SDL_SCALEMODE_LINEAR); + + // Store our identifier + io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture); + + return true; +} + +void ImGui_ImplSDLRenderer3_DestroyFontsTexture() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); + if (bd->FontTexture) + { + io.Fonts->SetTexID(0); + SDL_DestroyTexture(bd->FontTexture); + bd->FontTexture = nullptr; + } +} + +bool ImGui_ImplSDLRenderer3_CreateDeviceObjects() +{ + return ImGui_ImplSDLRenderer3_CreateFontsTexture(); +} + +void ImGui_ImplSDLRenderer3_DestroyDeviceObjects() +{ + ImGui_ImplSDLRenderer3_DestroyFontsTexture(); +} + +//----------------------------------------------------------------------------- + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui/backends/imgui_impl_sdlrenderer3.h b/backends/imgui/backends/imgui_impl_sdlrenderer3.h new file mode 100644 index 000000000000..6529469c0257 --- /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 937ff9e95535..e2868be77f4e 100644 --- a/backends/imgui/scummvm.patch +++ b/backends/imgui/scummvm.patch @@ -1,10 +1,11 @@ -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 +diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp +index 770f85f4..b363ba07 100644 +--- a/backends/imgui_impl_opengl3.cpp ++++ b/backends/imgui_impl_opengl3.cpp @@ -114,7 +114,7 @@ #define _CRT_SECURE_NO_WARNINGS #endif - + -#include "imgui.h" +#include "backends/imgui/imgui.h" #ifndef IMGUI_DISABLE @@ -18,19 +19,20 @@ diff --color -rbu ./backends/imgui_impl_opengl3.cpp ../scummvm/scummvm/backends/ #define IMGL3W_IMPL #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 +diff --git a/backends/imgui_impl_opengl3.h b/backends/imgui_impl_opengl3.h +index 7b1e98d2..ecb865db 100644 +--- a/backends/imgui_impl_opengl3.h ++++ b/backends/imgui_impl_opengl3.h @@ -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. - + #pragma once -#include "imgui.h" // IMGUI_IMPL_API +#include "backends/imgui/imgui.h" // IMGUI_IMPL_API #ifndef IMGUI_DISABLE - + // Follow "Getting Started" link and check examples/ folder to learn about using backends! -@@ -56,7 +56,7 @@ +@@ -56,7 +56,7 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); #endif #if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__)) #define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es" @@ -39,13 +41,14 @@ diff --color -rbu ./backends/imgui_impl_opengl3.h ../scummvm/scummvm/backends/im #define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100" #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 +diff --git a/backends/imgui_impl_opengl3_loader.h b/backends/imgui_impl_opengl3_loader.h +index 3fbc3481..2f290421 100644 +--- a/backends/imgui_impl_opengl3_loader.h ++++ b/backends/imgui_impl_opengl3_loader.h @@ -56,6 +56,11 @@ #ifndef __gl3w_h_ #define __gl3w_h_ - + +#if defined(IMGL3W_SCUMMVM_LOADER) +#define FORBIDDEN_SYMBOL_ALLOW_ALL +#include "common/system.h" @@ -54,10 +57,19 @@ diff --color -rbu ./backends/imgui_impl_opengl3_loader.h ../scummvm/scummvm/back // Adapted from KHR/khrplatform.h to avoid including entire file. #ifndef __khrplatform_h_ typedef float khronos_float_t; -@@ -614,7 +619,20 @@ - +@@ -118,7 +123,7 @@ extern "C" { + ** included as . + ** + ** glcorearb.h includes only APIs in the latest OpenGL core profile +-** implementation together with APIs in newer ARB extensions which ++** implementation together with APIs in newer ARB extensions which + ** can be supported by the core profile. It does not, and never will + ** include functionality removed from the core profile, such as + ** fixed-function vertex and fragment processing. +@@ -614,7 +619,20 @@ extern "C" { + #define GL3W_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - + -#if defined(_WIN32) +#if defined(IMGL3W_SCUMMVM_LOADER) +static int open_libgl(void) @@ -76,37 +88,39 @@ diff --color -rbu ./backends/imgui_impl_opengl3_loader.h ../scummvm/scummvm/back #ifndef WIN32_LEAN_AND_MEAN #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 +diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp +index 06e48d56..c4669b07 100644 +--- a/backends/imgui_impl_sdl2.cpp ++++ b/backends/imgui_impl_sdl2.cpp @@ -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. - + -#include "imgui.h" +#include "backends/imgui/imgui.h" #ifndef IMGUI_DISABLE +#include "backends/platform/sdl/sdl.h" #include "imgui_impl_sdl2.h" +#if SDL_VERSION_ATLEAST(2, 0, 0) - + // Clang warnings with -Weverything #if defined(__clang__) -@@ -128,7 +128,8 @@ +@@ -126,8 +128,9 @@ #define SDL_HAS_DISPLAY_EVENT SDL_VERSION_ATLEAST(2,0,9) #define SDL_HAS_SHOW_WINDOW_ACTIVATION_HINT SDL_VERSION_ATLEAST(2,0,18) #if SDL_HAS_VULKAN -extern "C" { extern DECLSPEC void SDLCALL SDL_Vulkan_GetDrawableSize(SDL_Window* window, int* w, int* h); } +-#elif +//extern "C" { extern DECLSPEC void SDLCALL SDL_Vulkan_GetDrawableSize(SDL_Window* window, int* w, int* h); } +#include --#elif +#else static const Uint32 SDL_WINDOW_VULKAN = 0x10000000; #endif -@@ -346,6 +348,11 @@ + +@@ -346,6 +349,11 @@ static ImGuiViewport* ImGui_ImplSDL2_GetViewportForWindowID(Uint32 window_id) return ImGui::FindViewportByPlatformHandle((void*)(intptr_t)window_id); } - + +bool ImGui_ImplSDL2_Ready() +{ + return ImGui_ImplSDL2_GetBackendData() != nullptr; @@ -115,61 +129,922 @@ diff --color -rbu ./backends/imgui_impl_sdl2.cpp ../scummvm/scummvm/backends/img // 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. -@@ -1201,4 +1208,5 @@ +@@ -1201,4 +1209,5 @@ static void ImGui_ImplSDL2_ShutdownPlatformInterface() #pragma clang diagnostic pop #endif - + +#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 +diff --git a/backends/imgui_impl_sdl2.h b/backends/imgui_impl_sdl2.h +index 78c76a84..d0313bbe 100644 +--- a/backends/imgui_impl_sdl2.h ++++ b/backends/imgui_impl_sdl2.h @@ -23,7 +23,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; -@@ -41,6 +41,7 @@ +@@ -41,6 +41,7 @@ IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOther(SDL_Window* window); IMGUI_IMPL_API void ImGui_ImplSDL2_Shutdown(); IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame(); IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event); +IMGUI_IMPL_API bool ImGui_ImplSDL2_Ready(); - + + // 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 --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp +index 7c7253a7..44536554 100644 +--- a/backends/imgui_impl_sdl3.cpp ++++ b/backends/imgui_impl_sdl3.cpp +@@ -7,12 +7,10 @@ + // 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: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5] + // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +-// [X] Platform: Mouse cursor shape and visibility. 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: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. ++// [X] Platform: IME support. + + // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. + // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +@@ -24,8 +22,7 @@ + + // 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-10-24: Emscripten: SDL_EVENT_MOUSE_WHEEL event doesn't require dividing by 100.0f on Emscripten. + // 2024-09-03: Update for SDL3 api changes: SDL_GetGamepads() memory ownership revert. (#7918, #7898, #7807) + // 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: + // - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn +@@ -52,8 +49,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 +@@ -96,8 +94,6 @@ struct ImGui_ImplSDL3_Data + SDL_Renderer* Renderer; + Uint64 Time; + char* ClipboardTextData; +- bool UseVulkan; +- bool WantUpdateMonitors; + + // IME handling + SDL_Window* ImeWindow; +@@ -109,12 +105,11 @@ struct ImGui_ImplSDL3_Data + 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 Gamepads; ++ ImVector Gamepads; + ImGui_ImplSDL3_GamepadMode GamepadMode; +- bool WantUpdateGamepadsList; ++ bool WantUpdateGamepadsList; + + ImGui_ImplSDL3_Data() { memset((void*)this, 0, sizeof(*this)); } + }; +@@ -128,11 +123,6 @@ 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*) + { +@@ -140,7 +130,7 @@ static const char* ImGui_ImplSDL3_GetClipboardText(ImGuiContext*) + 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; ++ bd->ClipboardTextData = sdl_clipboard_text ? SDL_strdup(sdl_clipboard_text) : nullptr; + return bd->ClipboardTextData; + } + +@@ -154,7 +144,7 @@ static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* view + 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) ++ if ((data->WantVisible == false || bd->ImeWindow != window) && bd->ImeWindow != nullptr) + { + SDL_StopTextInput(bd->ImeWindow); + bd->ImeWindow = nullptr; +@@ -162,8 +152,8 @@ static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* view + 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.x = (int)data->InputPos.x; ++ r.y = (int)data->InputPos.y; + r.w = 1; + r.h = (int)data->InputLineHeight; + SDL_SetTextInputArea(window, &r, 0); +@@ -173,6 +163,7 @@ static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* view + } + + // Not static to allow third-party code to use that if they want to (but undocumented) ++ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode); + ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode) + { + // Keypad doesn't have individual key values in SDL3 +@@ -315,15 +306,18 @@ static void ImGui_ImplSDL3_UpdateKeyModifiers(SDL_Keymod sdl_key_mods) + 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); ++ ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); ++ return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : nullptr; + } + + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. + // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. + // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. + // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. ++// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field. + bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) + { + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); +@@ -334,30 +328,20 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) + { + case SDL_EVENT_MOUSE_MOTION: + { +- if (ImGui_ImplSDL3_GetViewportForWindowID(event->motion.windowID) == NULL) ++ if (ImGui_ImplSDL3_GetViewportForWindowID(event->motion.windowID) == nullptr) + 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) ++ if (ImGui_ImplSDL3_GetViewportForWindowID(event->wheel.windowID) == nullptr) + return false; + //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY); + float wheel_x = -event->wheel.x; + float wheel_y = event->wheel.y; +- #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; +@@ -365,7 +349,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: + { +- if (ImGui_ImplSDL3_GetViewportForWindowID(event->button.windowID) == NULL) ++ if (ImGui_ImplSDL3_GetViewportForWindowID(event->button.windowID) == nullptr) + return false; + int mouse_button = -1; + if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; } +@@ -382,7 +366,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) + } + case SDL_EVENT_TEXT_INPUT: + { +- if (ImGui_ImplSDL3_GetViewportForWindowID(event->text.windowID) == NULL) ++ if (ImGui_ImplSDL3_GetViewportForWindowID(event->text.windowID) == nullptr) + return false; + io.AddInputCharactersUTF8(event->text.text); + return true; +@@ -390,7 +374,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) + case SDL_EVENT_KEY_DOWN: + case SDL_EVENT_KEY_UP: + { +- if (ImGui_ImplSDL3_GetViewportForWindowID(event->key.windowID) == NULL) ++ if (ImGui_ImplSDL3_GetViewportForWindowID(event->key.windowID) == nullptr) + return false; + //IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%d: key=%d, scancode=%d, mod=%X\n", (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP", event->key.key, event->key.scancode, event->key.mod); + ImGui_ImplSDL3_UpdateKeyModifiers((SDL_Keymod)event->key.mod); +@@ -399,18 +383,9 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) + 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) ++ if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr) + return false; + bd->MouseWindowID = event->window.windowID; + bd->MousePendingLeaveFrame = 0; +@@ -422,7 +397,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) + // FIXME: Unconfirmed whether this is still needed with SDL3. + case SDL_EVENT_WINDOW_MOUSE_LEAVE: + { +- if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == NULL) ++ if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr) + return false; + bd->MousePendingLeaveFrame = ImGui::GetFrameCount() + 1; + return true; +@@ -430,26 +405,11 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) + case SDL_EVENT_WINDOW_FOCUS_GAINED: + case SDL_EVENT_WINDOW_FOCUS_LOST: + { +- if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == NULL) ++ if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr) + 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: + { +@@ -495,30 +455,17 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void + 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; +@@ -544,35 +491,27 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void + // (This is unfortunately a global SDL setting, so enabling it might have a side-effect on your application. + // It is unlikely to make a difference, but if your app absolutely needs to ignore the initial on-focus click: + // you can ignore SDL_EVENT_MOUSE_BUTTON_DOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED) ++#ifdef SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH + SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); ++#endif + + // From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710) ++#ifdef SDL_HINT_MOUSE_AUTO_CAPTURE + SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0"); +- +- // 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); ++#endif + + 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) + { ++ IM_UNUSED(sdl_gl_context); // Viewport branch will need this. + return ImGui_ImplSDL3_Init(window, nullptr, sdl_gl_context); + } + + bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window) + { +- if (!ImGui_ImplSDL3_Init(window, nullptr, nullptr)) +- return false; +- ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); +- bd->UseVulkan = true; +- return true; ++ return ImGui_ImplSDL3_Init(window, nullptr, nullptr); + } + + bool ImGui_ImplSDL3_InitForD3D(SDL_Window* window) +@@ -606,8 +545,6 @@ void ImGui_ImplSDL3_Shutdown() + 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++) +@@ -616,11 +553,10 @@ void ImGui_ImplSDL3_Shutdown() + + io.BackendPlatformName = nullptr; + io.BackendPlatformUserData = nullptr; +- io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_PlatformHasViewports | ImGuiBackendFlags_HasMouseHoveredViewport); ++ io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); + 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(); +@@ -631,56 +567,28 @@ static void ImGui_ImplSDL3_UpdateMouseData() + // 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)); ++ const bool is_app_focused = (bd->Window == focused_window); + #else + SDL_Window* focused_window = bd->Window; + const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only + #endif + if (is_app_focused) + { +- // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) ++ // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos 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); +- } ++ 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; ++ float mouse_x_global, mouse_y_global; + 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); ++ SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global); ++ SDL_GetWindowPosition(focused_window, &window_x, &window_y); ++ io.AddMousePosEvent(mouse_x_global - window_x, mouse_y_global - window_y); + } + } +- +- // (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() +@@ -815,38 +723,6 @@ static void ImGui_ImplSDL3_UpdateGamepads() + 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(); +@@ -864,10 +740,6 @@ void ImGui_ImplSDL3_NewFrame() + 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(); +@@ -884,13 +756,6 @@ void ImGui_ImplSDL3_NewFrame() + 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(); + +@@ -898,254 +763,6 @@ void ImGui_ImplSDL3_NewFrame() + 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 so we can safely include this. +-#include +-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__) +diff --git a/backends/imgui_impl_sdl3.h b/backends/imgui_impl_sdl3.h +index 96f0e5d8..6483fb5d 100644 +--- a/backends/imgui_impl_sdl3.h ++++ b/backends/imgui_impl_sdl3.h +@@ -7,13 +7,10 @@ + // 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: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5] + // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +-// [X] Platform: Mouse cursor shape and visibility. 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()!. ++// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. ++// [X] Platform: IME support. + + // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. + // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +@@ -24,7 +21,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; +@@ -46,6 +43,6 @@ 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. -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 + 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); ++IMGUI_IMPL_API void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array = nullptr, int manual_gamepads_count = -1); + + #endif // #ifndef IMGUI_DISABLE +diff --git a/backends/imgui_impl_sdlrenderer2.cpp b/backends/imgui_impl_sdlrenderer2.cpp +index a046ed97..c385a0da 100644 +--- a/backends/imgui_impl_sdlrenderer2.cpp ++++ b/backends/imgui_impl_sdlrenderer2.cpp @@ -30,7 +30,7 @@ // 2021-10-06: Backup and restore modified ClipRect/Viewport. // 2021-09-21: Initial version. - + -#include "imgui.h" +#include "backends/imgui/imgui.h" #ifndef IMGUI_DISABLE #include "imgui_impl_sdlrenderer2.h" #include // 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 +diff --git a/backends/imgui_impl_sdlrenderer2.h b/backends/imgui_impl_sdlrenderer2.h +index 0a135ad0..ce00c5c6 100644 +--- a/backends/imgui_impl_sdlrenderer2.h ++++ b/backends/imgui_impl_sdlrenderer2.h @@ -22,8 +22,8 @@ // - Introduction, links and more at the top of imgui.cpp - + #pragma once +#include "backends/imgui/imgui.h" // IMGUI_IMPL_API #ifndef IMGUI_DISABLE -#include "imgui.h" // IMGUI_IMPL_API - + + struct SDL_Renderer; + +diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp +index b38d3385..153d4b76 100644 +--- a/backends/imgui_impl_sdlrenderer3.cpp ++++ b/backends/imgui_impl_sdlrenderer3.cpp +@@ -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 // intptr_t +diff --git a/backends/imgui_impl_sdlrenderer3.h b/backends/imgui_impl_sdlrenderer3.h +index 4ae96dc7..6529469c 100644 +--- a/backends/imgui_impl_sdlrenderer3.h ++++ b/backends/imgui_impl_sdlrenderer3.h +@@ -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 ./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 -@@ -105,8 +105,10 @@ +diff --git a/imconfig.h b/imconfig.h +index b8d55842..d6f0587d 100644 +--- a/imconfig.h ++++ b/imconfig.h +@@ -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 --git a/imgui.cpp b/imgui.cpp +index 50c8d362..5b139e30 100644 +--- a/imgui.cpp ++++ b/imgui.cpp +@@ -15234,7 +15234,7 @@ void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) + 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(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 @@ void ImGui::ShowMetricsWindow(bool* p_open) + + 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(g.SettingsIniData.c_str()), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly); + TreePop(); + } + TreePop(); +diff --git a/imgui.h b/imgui.h +index dc6ac6e5..24a004b6 100644 +--- a/imgui.h ++++ b/imgui.h +@@ -105,8 +105,10 @@ Index of this file: #define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1))) #define IM_FMTLIST(FMT) __attribute__((format(gnu_printf, FMT, 0))) #elif !defined(IMGUI_USE_STB_SPRINTF) && (defined(__clang__) || defined(__GNUC__)) @@ -182,13 +1057,40 @@ diff --color -rbu ./imgui.h ../scummvm/scummvm/backends/imgui/imgui.h #else #define IM_FMTARGS(FMT) #define IM_FMTLIST(FMT) -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 +diff --git a/imgui_demo.cpp b/imgui_demo.cpp +index 7ed778b5..3048b692 100644 +--- a/imgui_demo.cpp ++++ b/imgui_demo.cpp +@@ -3926,7 +3926,7 @@ static void ShowDemoWindowMultiSelect(ImGuiDemoWindowData* demo_data) + 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(item_category), strlen(item_category), ImGuiInputTextFlags_ReadOnly); + ImGui::PopStyleVar(); + } + +diff --git a/imgui_draw.cpp b/imgui_draw.cpp +index acd633eb..e4aa544f 100644 +--- a/imgui_draw.cpp ++++ b/imgui_draw.cpp +@@ -3727,7 +3727,7 @@ bool ImFont::IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last) + + void ImFont::SetGlyphVisible(ImWchar c, bool visible) + { +- if (ImFontGlyph* glyph = (ImFontGlyph*)(void*)FindGlyph((ImWchar)c)) ++ if (ImFontGlyph* glyph = const_cast(FindGlyph((ImWchar)c))) + glyph->Visible = visible ? 1 : 0; + } + +diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp +index d97605b2..d7256ce9 100644 +--- a/misc/freetype/imgui_freetype.cpp ++++ b/misc/freetype/imgui_freetype.cpp @@ -34,10 +34,10 @@ - + // FIXME: cfg.OversampleH, OversampleV are not supported (but perhaps not so necessary with this rasterizer). - + -#include "imgui.h" +#include "backends/imgui/imgui.h" #ifndef IMGUI_DISABLE @@ -198,7 +1100,7 @@ diff --color -rbu ./misc/freetype/imgui_freetype.cpp ../scummvm/scummvm/backends #include #include #include FT_FREETYPE_H // -@@ -396,7 +396,7 @@ +@@ -396,7 +396,7 @@ namespace #ifdef IMGUI_STB_RECT_PACK_FILENAME #include IMGUI_STB_RECT_PACK_FILENAME #else @@ -206,16 +1108,17 @@ diff --color -rbu ./misc/freetype/imgui_freetype.cpp ../scummvm/scummvm/backends +#include "backends/imgui/imstb_rectpack.h" #endif #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 + +diff --git a/misc/freetype/imgui_freetype.h b/misc/freetype/imgui_freetype.h +index b4e1d489..4f537f62 100644 +--- a/misc/freetype/imgui_freetype.h ++++ b/misc/freetype/imgui_freetype.h @@ -2,7 +2,7 @@ // (headers) - + #pragma once -#include "imgui.h" // IMGUI_API +#include "backends/imgui/imgui.h" // IMGUI_API #ifndef IMGUI_DISABLE - + // Forward declarations diff --git a/backends/mixer/sdl/sdl-mixer.cpp b/backends/mixer/sdl/sdl-mixer.cpp index 022d50e05988..7505ea74c1b8 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 2c8ea2232b8e..3dd0670396fa 100644 --- a/backends/mixer/sdl/sdl-mixer.h +++ b/backends/mixer/sdl/sdl-mixer.h @@ -80,9 +80,16 @@ class SdlMixerManager : public MixerManager { * by subclasses, so it invokes the non-static function callbackHandler() */ static void sdlCallback(void *this_, byte *samples, int len); +#if SDL_VERSION_ATLEAST(3, 0, 0) + static void sdl3Callback(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount); +#endif bool _isSubsystemInitialized; bool _isAudioOpen; + +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_AudioStream *_stream = nullptr; +#endif }; #endif diff --git a/backends/module.mk b/backends/module.mk index d1acc3d8d547..7d3c71cc11bf 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 3ada8e5ecd07..11e951b4845a 100644 --- a/backends/mutex/sdl/sdl-mutex.cpp +++ b/backends/mutex/sdl/sdl-mutex.cpp @@ -34,11 +34,29 @@ class SdlMutexInternal final : public Common::MutexInternal { SdlMutexInternal() { _mutex = SDL_CreateMutex(); } ~SdlMutexInternal() override { SDL_DestroyMutex(_mutex); } - bool lock() override { return (SDL_mutexP(_mutex) == 0); } - bool unlock() override { return (SDL_mutexV(_mutex) == 0); } + bool lock() override { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_LockMutex(_mutex); + return true; +#else + return (SDL_mutexP(_mutex) == 0); +#endif + } + bool unlock() override { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_UnlockMutex(_mutex); + return true; +#else + return (SDL_mutexV(_mutex) == 0); +#endif + } private: +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Mutex *_mutex; +#else SDL_mutex *_mutex; +#endif }; Common::MutexInternal *createSdlMutexInternal() { diff --git a/backends/platform/sdl/macosx/macosx-window.mm b/backends/platform/sdl/macosx/macosx-window.mm index d8481eaee465..59e194d529f0 100644 --- a/backends/platform/sdl/macosx/macosx-window.mm +++ b/backends/platform/sdl/macosx/macosx-window.mm @@ -27,7 +27,7 @@ #include float SdlWindow_MacOSX::getDpiScalingFactor() const { -#if SDL_VERSION_ATLEAST(2, 0, 0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 +#if !SDL_VERSION_ATLEAST(3, 0, 0) && SDL_VERSION_ATLEAST(2, 0, 0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 SDL_SysWMinfo wmInfo; if (getSDLWMInformation(&wmInfo)) { NSWindow *nswindow = wmInfo.info.cocoa.window; diff --git a/backends/platform/sdl/sdl-sys.h b/backends/platform/sdl/sdl-sys.h index 87bd6cabfab3..1eb5129887f0 100644 --- a/backends/platform/sdl/sdl-sys.h +++ b/backends/platform/sdl/sdl-sys.h @@ -187,12 +187,18 @@ #endif +#ifdef USE_SDL3 +#include +#else #include +#endif // Ignore warnings from system headers pulled by SDL #pragma warning(push) #pragma warning(disable:4121) // alignment of a member was sensitive to packing +#if !SDL_VERSION_ATLEAST(3, 0, 0) #include +#endif #pragma warning(pop) // Restore the forbidden exceptions from the hack above diff --git a/backends/platform/sdl/sdl-window.cpp b/backends/platform/sdl/sdl-window.cpp index 508ffe8a6c82..b5829d44bf77 100644 --- a/backends/platform/sdl/sdl-window.cpp +++ b/backends/platform/sdl/sdl-window.cpp @@ -29,7 +29,9 @@ #include "icons/scummvm.xpm" -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) +static const uint32 fullscreenMask = SDL_WINDOW_FULLSCREEN; +#elif SDL_VERSION_ATLEAST(2, 0, 0) static const uint32 fullscreenMask = SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN; #endif @@ -122,7 +124,11 @@ void SdlWindow::setupIcon() { } } +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Surface *sdl_surf = SDL_CreateSurfaceFrom(w, h, SDL_GetPixelFormatForMasks(32, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000), icon, w * 4); +#else SDL_Surface *sdl_surf = SDL_CreateRGBSurfaceFrom(icon, w, h, 32, w * 4, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000); +#endif if (!sdl_surf) { warning("SDL_CreateRGBSurfaceFrom(icon) failed"); } @@ -135,7 +141,11 @@ void SdlWindow::setupIcon() { SDL_WM_SetIcon(sdl_surf, NULL); #endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(sdl_surf); +#else SDL_FreeSurface(sdl_surf); +#endif free(icon); #endif } @@ -154,7 +164,11 @@ void SdlWindow::setWindowCaption(const Common::String &caption) { void SdlWindow::grabMouse(bool grab) { #if SDL_VERSION_ATLEAST(2, 0, 0) if (_window) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_SetWindowMouseGrab(_window, grab); +#else SDL_SetWindowGrab(_window, grab ? SDL_TRUE : SDL_FALSE); +#endif #if SDL_VERSION_ATLEAST(2, 0, 18) SDL_SetWindowMouseRect(_window, grab ? &grabRect : NULL); #endif @@ -187,7 +201,10 @@ void SdlWindow::setMouseRect(const Common::Rect &rect) { } bool SdlWindow::lockMouse(bool lock) { -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_SetWindowRelativeMouseMode(_window, lock); + _inputLockState = lock; +#elif SDL_VERSION_ATLEAST(2, 0, 0) SDL_SetRelativeMouseMode(lock ? SDL_TRUE : SDL_FALSE); _inputLockState = lock; #else @@ -244,6 +261,7 @@ void SdlWindow::iconifyWindow() { #endif } +#if !SDL_VERSION_ATLEAST(3, 0, 0) bool SdlWindow::getSDLWMInformation(SDL_SysWMinfo *info) const { SDL_VERSION(&info->version); #if SDL_VERSION_ATLEAST(2, 0, 0) @@ -254,9 +272,17 @@ bool SdlWindow::getSDLWMInformation(SDL_SysWMinfo *info) const { return false; #endif } +#endif Common::Rect SdlWindow::getDesktopResolution() { -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_DisplayMode* pDisplayMode = SDL_GetDesktopDisplayMode(getDisplayIndex()); + if (pDisplayMode) { + _desktopRes = Common::Rect(pDisplayMode->w, pDisplayMode->h); + } else { + warning("SDL_GetDesktopDisplayMode failed: %s", SDL_GetError()); + } +#elif SDL_VERSION_ATLEAST(2, 0, 0) SDL_DisplayMode displayMode; if (!SDL_GetDesktopDisplayMode(getDisplayIndex(), &displayMode)) { _desktopRes = Common::Rect(displayMode.w, displayMode.h); @@ -279,7 +305,9 @@ void SdlWindow::getDisplayDpi(float *dpi, float *defaultDpi) const { *defaultDpi = systemDpi; if (dpi) { -#if SDL_VERSION_ATLEAST(2, 0, 4) +#if SDL_VERSION_ATLEAST(3, 0, 0) + *dpi = SDL_GetWindowDisplayScale(_window) * systemDpi; +#elif SDL_VERSION_ATLEAST(2, 0, 4) if (SDL_GetDisplayDPI(getDisplayIndex(), nullptr, dpi, nullptr) != 0) { *dpi = systemDpi; } @@ -303,7 +331,9 @@ float SdlWindow::getDpiScalingFactor() const { } float SdlWindow::getSdlDpiScalingFactor() const { -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + return SDL_GetWindowDisplayScale(getSDLWindow()); +#elif SDL_VERSION_ATLEAST(2, 0, 0) int windowWidth, windowHeight; SDL_GetWindowSize(getSDLWindow(), &windowWidth, &windowHeight); int realWidth, realHeight; @@ -316,7 +346,11 @@ float SdlWindow::getSdlDpiScalingFactor() const { #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_Surface *copySDLSurface(SDL_Surface *src) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + const bool locked = SDL_MUSTLOCK(src); +#else const bool locked = SDL_MUSTLOCK(src) == SDL_TRUE; +#endif if (locked) { if (SDL_LockSurface(src) != 0) { @@ -324,10 +358,15 @@ SDL_Surface *copySDLSurface(SDL_Surface *src) { } } +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Surface *res = SDL_CreateSurfaceFrom(src->w, src->h, src->format, + src->pixels, src->pitch); +#else SDL_Surface *res = SDL_CreateRGBSurfaceFrom(src->pixels, src->w, src->h, src->format->BitsPerPixel, src->pitch, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask); +#endif if (locked) { SDL_UnlockSurface(src); @@ -337,6 +376,16 @@ SDL_Surface *copySDLSurface(SDL_Surface *src) { } int SdlWindow::getDisplayIndex() const { +#if SDL_VERSION_ATLEAST(3, 0, 0) + int display = 0; + int num_displays; + SDL_DisplayID *displays = SDL_GetDisplays(&num_displays); + if (num_displays > 0) { + display = static_cast(displays[0]); + } + SDL_free(displays); + return display; +#else if (_window) { int displayIndex = SDL_GetWindowDisplayIndex(_window); if (displayIndex >= 0) @@ -344,12 +393,19 @@ int SdlWindow::getDisplayIndex() const { } // Default to primary display return 0; +#endif } bool SdlWindow::createOrUpdateWindow(int width, int height, uint32 flags) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (_inputGrabState) { + flags |= SDL_WINDOW_MOUSE_GRABBED; + } +#else if (_inputGrabState) { flags |= SDL_WINDOW_INPUT_GRABBED; } +#endif // SDL_WINDOW_RESIZABLE can also be updated without recreating the window // starting with SDL 2.0.5, but it is not treated as updateable here @@ -360,7 +416,11 @@ bool SdlWindow::createOrUpdateWindow(int width, int height, uint32 flags) { // 2. Users (particularly on Windows) will sometimes swap older SDL DLLs // to avoid bugs, which would be impossible if the feature was enabled // at compile time using SDL_VERSION_ATLEAST. +#if SDL_VERSION_ATLEAST(3, 0, 0) + const uint32 updateableFlagsMask = fullscreenMask | SDL_WINDOW_MOUSE_GRABBED; +#else const uint32 updateableFlagsMask = fullscreenMask | SDL_WINDOW_INPUT_GRABBED; +#endif const uint32 oldNonUpdateableFlags = _lastFlags & ~updateableFlagsMask; const uint32 newNonUpdateableFlags = flags & ~updateableFlagsMask; @@ -387,7 +447,9 @@ bool SdlWindow::createOrUpdateWindow(int width, int height, uint32 flags) { ) { int top, left, bottom, right; -#if SDL_VERSION_ATLEAST(2, 0, 5) +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!_window || !SDL_GetWindowBordersSize(_window, &top, &left, &bottom, &right)) +#elif SDL_VERSION_ATLEAST(2, 0, 5) if (!_window || SDL_GetWindowBordersSize(_window, &top, &left, &bottom, &right) < 0) #endif { @@ -408,13 +470,31 @@ bool SdlWindow::createOrUpdateWindow(int width, int height, uint32 flags) { if (!_window || oldNonUpdateableFlags != newNonUpdateableFlags) { destroyWindow(); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, _windowCaption.c_str()); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, _lastX); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, _lastY); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, width); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, height); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, flags); + _window = SDL_CreateWindowWithProperties(props); + SDL_DestroyProperties(props); +#else _window = SDL_CreateWindow(_windowCaption.c_str(), _lastX, _lastY, width, height, flags); +#endif if (_window) { setupIcon(); } } else { if (fullscreenFlags) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_SetWindowFullscreenMode(_window, NULL)) + warning("SDL_SetWindowFullscreenMode failed: %s", SDL_GetError()); + if (!SDL_SyncWindow(_window)) + warning("SDL_SyncWindow failed: %s", SDL_GetError()); +#else SDL_DisplayMode fullscreenMode; fullscreenMode.w = width; fullscreenMode.h = height; @@ -422,6 +502,7 @@ bool SdlWindow::createOrUpdateWindow(int width, int height, uint32 flags) { fullscreenMode.format = 0; fullscreenMode.refresh_rate = 0; SDL_SetWindowDisplayMode(_window, &fullscreenMode); +#endif } else { SDL_SetWindowSize(_window, width, height); } @@ -429,8 +510,13 @@ bool SdlWindow::createOrUpdateWindow(int width, int height, uint32 flags) { SDL_SetWindowFullscreen(_window, fullscreenFlags); } +#if SDL_VERSION_ATLEAST(3, 0, 0) + const bool shouldGrab = (flags & SDL_WINDOW_MOUSE_GRABBED) || fullscreenFlags; + SDL_SetWindowMouseGrab(_window, shouldGrab); +#else const bool shouldGrab = (flags & SDL_WINDOW_INPUT_GRABBED) || fullscreenFlags; SDL_SetWindowGrab(_window, shouldGrab ? SDL_TRUE : SDL_FALSE); +#endif #if SDL_VERSION_ATLEAST(2, 0, 18) SDL_SetWindowMouseRect(_window, shouldGrab ? &grabRect : NULL); #endif diff --git a/backends/platform/sdl/sdl-window.h b/backends/platform/sdl/sdl-window.h index b3e4933874c0..1aabe99df902 100644 --- a/backends/platform/sdl/sdl-window.h +++ b/backends/platform/sdl/sdl-window.h @@ -78,6 +78,7 @@ class SdlWindow { */ void iconifyWindow(); +#if !SDL_VERSION_ATLEAST(3, 0, 0) /** * Query platform specific SDL window manager information. * @@ -85,6 +86,7 @@ class SdlWindow { * for accessing it in a version safe manner. */ bool getSDLWMInformation(SDL_SysWMinfo *info) const; +#endif /* * Retrieve the current desktop resolution. @@ -109,7 +111,11 @@ class SdlWindow { virtual float getDpiScalingFactor() const; bool mouseIsGrabbed() const { -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (_window) { + return SDL_GetWindowMouseGrab(_window); + } +#elif SDL_VERSION_ATLEAST(2, 0, 0) if (_window) { return SDL_GetWindowGrab(_window) == SDL_TRUE; } @@ -118,7 +124,9 @@ class SdlWindow { } bool mouseIsLocked() const { -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + return SDL_GetWindowRelativeMouseMode(_window); +#elif SDL_VERSION_ATLEAST(2, 0, 0) return SDL_GetRelativeMouseMode() == SDL_TRUE; #else return _inputLockState; diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp index 3fc1f20b3053..c2ee753ad8ac 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,7 +73,9 @@ #include #endif -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) +#include +#elif SDL_VERSION_ATLEAST(2, 0, 0) #include #endif @@ -97,7 +100,11 @@ OSystem_SDL::OSystem_SDL() } OSystem_SDL::~OSystem_SDL() { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_ShowCursor(); +#else SDL_ShowCursor(SDL_ENABLE); +#endif #ifdef USE_MULTIPLE_RENDERERS clearGraphicsModes(); @@ -162,8 +169,12 @@ void OSystem_SDL::init() { #endif #if !defined(OPENPANDORA) +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_HideCursor(); +#else // Disable OS cursor SDL_ShowCursor(SDL_DISABLE); +#endif #endif if (_window == nullptr) @@ -206,7 +217,13 @@ bool OSystem_SDL::hasFeature(Feature f) { #if defined(USE_SCUMMVMDLC) && defined(USE_LIBCURL) if (f == kFeatureDLC) return true; #endif -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (f == kFeatureTouchpadMode) { + int count = 0; + SDL_free(SDL_GetTouchDevices(&count)); + return count > 0; + } +#elif SDL_VERSION_ATLEAST(2, 0, 0) if (f == kFeatureTouchpadMode) { return SDL_GetNumTouchDevices() > 0; } @@ -383,7 +400,17 @@ void OSystem_SDL::detectOpenGLFeaturesSupport() { #else // Spawn a 32x32 window off-screen with a GL context to test if framebuffers are supported #if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, ""); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, 32); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, 32); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); + SDL_Window *window = SDL_CreateWindowWithProperties(props); + SDL_DestroyProperties(props); +#else SDL_Window *window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 32, 32, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); +#endif if (!window) { return; } @@ -417,7 +444,11 @@ void OSystem_SDL::detectOpenGLFeaturesSupport() { _supportsFrameBuffer = OpenGLContext.framebufferObjectSupported; _supportsShaders = OpenGLContext.enginesShadersSupported; OpenGLContext.reset(); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_GL_DestroyContext(glContext); +#else SDL_GL_DeleteContext(glContext); +#endif SDL_DestroyWindow(window); #else SDL_putenv(const_cast("SDL_VIDEO_WINDOW_POS=9000,9000")); @@ -443,7 +474,17 @@ void OSystem_SDL::detectAntiAliasingSupport() { SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, requestedSamples); #if SDL_VERSION_ATLEAST(2, 0, 0) +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, ""); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, 32); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, 32); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); + SDL_Window *window = SDL_CreateWindowWithProperties(props); + SDL_DestroyProperties(props); +#else SDL_Window *window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 32, 32, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); +#endif if (window) { SDL_GLContext glContext = SDL_GL_CreateContext(window); if (glContext) { @@ -454,7 +495,11 @@ void OSystem_SDL::detectAntiAliasingSupport() { _antiAliasLevels.push_back(requestedSamples); } +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_GL_DestroyContext(glContext); +#else SDL_GL_DeleteContext(glContext); +#endif } SDL_DestroyWindow(window); @@ -533,11 +578,17 @@ void OSystem_SDL::initSDL() { // or otherwise the application won't start. uint32 sdlFlags = SDL_INIT_VIDEO; +#if !SDL_VERSION_ATLEAST(3, 0, 0) if (ConfMan.hasKey("disable_sdl_parachute")) sdlFlags |= SDL_INIT_NOPARACHUTE; +#endif // Initialize SDL (SDL Subsystems are initialized in the corresponding sdl managers) +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_Init(sdlFlags)) +#else if (SDL_Init(sdlFlags) == -1) +#endif error("Could not initialize SDL: %s", SDL_GetError()); _initedSDL = true; @@ -657,7 +708,19 @@ Common::WriteStream *OSystem_SDL::createLogFile() { Common::String OSystem_SDL::getSystemLanguage() const { -#if SDL_VERSION_ATLEAST(2, 0, 14) +#if SDL_VERSION_ATLEAST(3, 0, 0) + int count = 0; + SDL_Locale **pLocales = SDL_GetPreferredLocales(&count); + if (pLocales) { + SDL_Locale *locales = *pLocales; + if (locales[0].language != NULL) { + Common::String str = Common::String::format("%s_%s", locales[0].country, locales[0].language); + SDL_free(locales); + return str; + } + SDL_free(pLocales); + } +#elif SDL_VERSION_ATLEAST(2, 0, 14) SDL_Locale *locales = SDL_GetPreferredLocales(); if (locales) { if (locales[0].language != NULL) { @@ -667,7 +730,7 @@ Common::String OSystem_SDL::getSystemLanguage() const { } SDL_free(locales); } -#endif // SDL_VERSION_ATLEAST(2, 0, 14) +#endif #if defined(USE_DETECTLANG) && !defined(WIN32) // Activating current locale settings const Common::String locale = setlocale(LC_ALL, ""); @@ -704,7 +767,11 @@ Common::String OSystem_SDL::getSystemLanguage() const { #if SDL_VERSION_ATLEAST(2, 0, 0) bool OSystem_SDL::hasTextInClipboard() { +#if SDL_VERSION_ATLEAST(3, 0, 0) + return SDL_HasClipboardText(); +#else return SDL_HasClipboardText() == SDL_TRUE; +#endif } Common::U32String OSystem_SDL::getTextFromClipboard() { diff --git a/backends/timer/sdl/sdl-timer.cpp b/backends/timer/sdl/sdl-timer.cpp index 81812888f8ce..cda5923c3a7d 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 234ab0b51fb2..bdb3174be336 100644 --- a/base/commandLine.cpp +++ b/base/commandLine.cpp @@ -1933,7 +1933,10 @@ bool processSettings(Common::String &command, Common::StringMap &settings, Commo } else if (command == "version") { printf("%s\n", gScummVMFullVersion); #ifdef SDL_BACKEND -#ifdef USE_SDL2 +#ifdef USE_SDL3 + int sdlLinkedVersion = SDL_GetVersion(); + printf("Using SDL backend with SDL %d.%d.%d\n", SDL_VERSIONNUM_MAJOR(sdlLinkedVersion), SDL_VERSIONNUM_MINOR(sdlLinkedVersion), SDL_VERSIONNUM_MICRO(sdlLinkedVersion)); +#elif defined(USE_SDL2) SDL_version sdlLinkedVersion; SDL_GetVersion(&sdlLinkedVersion); printf("Using SDL backend with SDL %d.%d.%d\n", sdlLinkedVersion.major, sdlLinkedVersion.minor, sdlLinkedVersion.patch); diff --git a/configure b/configure index b490265b5b96..b21d7d9a4b70 100755 --- a/configure +++ b/configure @@ -248,7 +248,7 @@ _staticlibpath= _xcodetoolspath= _sparklepath= _pkgconfig=pkg-config -_sdlconfig=sdl2-config + _sdlconfig=sdl2-config _libcurlconfig=curl-config _libmikmodconfig=libmikmod-config _freetypeconfig=freetype-config @@ -4144,21 +4144,31 @@ fi # Setup SDL specifics for SDL based backends # if test "$_sdl" = auto ; then - find_sdlconfig + if test "$_pkg_config" = "yes" && $_pkgconfig --exists sdl3; then + _sdlconfig="pkg-config sdl3" + _sdlversion=`$_sdlconfig --modversion` + cat > $TMPC << EOF +#include "SDL3/SDL.h" +int main(int argc, char *argv[]) { SDL_Init(0); return 0; } +EOF + else + find_sdlconfig + _sdlversion=`$_sdlconfig --version` + cat > $TMPC << EOF +#include "SDL.h" +int main(int argc, char *argv[]) { SDL_Init(0); return 0; } +EOF + fi + append_var SDL_CFLAGS "`$_sdlconfig --cflags | sed 's/[[:space:]]*-Dmain=SDL_main//g'`" if test "$_static_build" = yes ; then append_var SDL_LIBS "`$_sdlconfig --static-libs`" else append_var SDL_LIBS "`$_sdlconfig --libs`" fi - _sdlversion=`$_sdlconfig --version` echocheck "SDL" _sdl=no - cat > $TMPC << EOF -#include "SDL.h" -int main(int argc, char *argv[]) { SDL_Init(0); return 0; } -EOF cc_check $LIBS $SDL_LIBS $INCLUDES $SDL_CFLAGS && _sdl=yes echo "$_sdl" if test "$_sdl" = no ; then @@ -4173,6 +4183,11 @@ if test "$_sdl" = yes ; then append_var INCLUDES "$SDL_CFLAGS" append_var LIBS "$SDL_LIBS" case $_sdlversion in + 3.*.*) + append_var DEFINES "-DUSE_SDL3" + add_line_to_config_mk "USE_SDL3 = 1" + _sdlMajorVersionNumber=3 + ;; 2.*.*) append_var DEFINES "-DUSE_SDL2" add_line_to_config_mk "USE_SDL2 = 1" @@ -4193,14 +4208,26 @@ fi # of SDL-net or SDL2-net that does not require SDL or SDL2 respectively # if test "$_sdlnet" = auto ; then + # If SDL3 was detected, then test for SDL3_net exclusively # If SDL2 was detected, then test for SDL2_net exclusively # If SDL was detected, then test for SDL_net exclusively - # If neither SDL nor SDL2 detected, then test for both (SDL2_net success takes priority) + # If neither SDL nor SDL2 detected nor SDL3 detected, then test for both (SDL3_net success takes priority) + set_var SDL3_NET_LIBS "$SDL_NET_LIBS" + set_var SDL3_NET_CFLAGS "$SDL_NET_CFLAGS" set_var SDL2_NET_LIBS "$SDL_NET_LIBS" set_var SDL2_NET_CFLAGS "$SDL_NET_CFLAGS" set_var SDL1_NET_LIBS "$SDL_NET_LIBS" set_var SDL1_NET_CFLAGS "$SDL_NET_CFLAGS" + if test "$_sdl" = no || test "$_sdlMajorVersionNumber" = 3; then + if test "$_pkg_config" = "yes" && $_pkgconfig --exists SDL3_net; then + append_var SDL3_NET_LIBS "`$_pkgconfig --libs SDL3_net`" + append_var SDL3_NET_CFLAGS "`$_pkgconfig --cflags SDL3_net | sed 's/[[:space:]]*-Dmain=SDL_main//g'`" + else + append_var SDL3_NET_LIBS "-lSDL3_net" + fi + fi + if test "$_sdl" = no || test "$_sdlMajorVersionNumber" = 2; then if test "$_pkg_config" = "yes" && $_pkgconfig --exists SDL2_net; then append_var SDL2_NET_LIBS "`$_pkgconfig --libs SDL2_net`" @@ -4222,26 +4249,39 @@ if test "$_sdlnet" = auto ; then # Check for SDL_Net echocheck "SDL_Net" _sdlnet=no + cat > $TMPC << EOF -#include "SDL_net.h" +#include "SDL3/SDL_net.h" int main(int argc, char *argv[]) { SDLNet_Init(); return 0; } EOF - cc_check $SDL2_NET_LIBS $LIBS $INCLUDES $SDL2_NET_CFLAGS && _sdlnet=yes + cc_check $SDL3_NET_LIBS $LIBS $INCLUDES $SDL3_NET_CFLAGS && _sdlnet=yes if test "$_sdlnet" = yes ; then set_var SDL_NET_LIBS "$SDL2_NET_LIBS" set_var SDL_NET_CFLAGS "$SDL2_NET_CFLAGS" add_line_to_config_mk "SDL_NET_MAJOR = 2" else cat > $TMPC << EOF -#include "SDL_net.h" +#include "SDL3/SDL_net.h" int main(int argc, char *argv[]) { SDLNet_Init(); return 0; } EOF - cc_check $SDL1_NET_LIBS $LIBS $INCLUDES $SDL1_NET_CFLAGS && _sdlnet=yes + + cc_check $SDL2_NET_LIBS $LIBS $INCLUDES $SDL2_NET_CFLAGS && _sdlnet=yes if test "$_sdlnet" = yes ; then - set_var SDL_NET_LIBS "$SDL1_NET_LIBS" - set_var SDL_NET_CFLAGS "$SDL1_NET_CFLAGS" - add_line_to_config_mk "SDL_NET_MAJOR = 1" + set_var SDL_NET_LIBS "$SDL2_NET_LIBS" + set_var SDL_NET_CFLAGS "$SDL2_NET_CFLAGS" + add_line_to_config_mk "SDL_NET_MAJOR = 2" + else + cat > $TMPC << EOF +#include "SDL_net.h" +int main(int argc, char *argv[]) { SDLNet_Init(); return 0; } +EOF + cc_check $SDL1_NET_LIBS $LIBS $INCLUDES $SDL1_NET_CFLAGS && _sdlnet=yes + if test "$_sdlnet" = yes ; then + set_var SDL_NET_LIBS "$SDL1_NET_LIBS" + set_var SDL_NET_CFLAGS "$SDL1_NET_CFLAGS" + add_line_to_config_mk "SDL_NET_MAJOR = 1" + fi fi fi @@ -6745,7 +6785,26 @@ if test "$_imgui" != no ; then if test "$_freetype2" = yes ; then case $_backend in sdl | sailfish) - if test "$_sdlMajorVersionNumber" -ge 2 ; then + if test "$_sdlMajorVersionNumber" -ge 3 ; then + cat > $TMPC << EOF +#include +#if !SDL_VERSION_ATLEAST(3,0,0) +#error Missing SDL_RenderGeometryRaw() function +#endif +int main(int argc, char *argv[]) { return 0; } +EOF + if cc_check $LIBS $SDL_LIBS $INCLUDES $SDL_CFLAGS; then + define_in_config_if_yes yes 'USE_IMGUI_SDLRENDERER3' + _imgui=yes + echo "yes" + elif test "$_opengl" = yes; then + _imgui=yes + echo "yes" + else + _imgui=no + echo "no (requires OpenGL or recent SDL)" + fi + elif test "$_sdlMajorVersionNumber" -ge 2 ; then cat > $TMPC << EOF #include "SDL.h" #if !SDL_VERSION_ATLEAST(2,0,18)