diff --git a/src/common/delay.cpp b/src/common/delay.cpp index 06902f73d..ee35559df 100644 --- a/src/common/delay.cpp +++ b/src/common/delay.cpp @@ -21,15 +21,21 @@ #include "delay.h" #include +#include namespace love { -void sleep(unsigned int ms) +// TODO: use ns. +void sleep(double ms) { // We don't need to initialize the SDL timer subsystem for SDL_Delay to // function - and doing so causes SDL to create a worker thread. - SDL_Delay(ms); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DelayNS(SDL_MS_TO_NS(ms)); +#else + SDL_Delay((Uint32)ms); +#endif } } // love diff --git a/src/common/delay.h b/src/common/delay.h index 04105cabf..98976485c 100644 --- a/src/common/delay.h +++ b/src/common/delay.h @@ -24,7 +24,7 @@ namespace love { -void sleep(unsigned int ms); +void sleep(double ms); } // namespace love diff --git a/src/common/macos.mm b/src/common/macos.mm index 2bb426500..f63925b55 100644 --- a/src/common/macos.mm +++ b/src/common/macos.mm @@ -26,12 +26,9 @@ #import #import -#ifdef LOVE_MACOSX_SDL_DIRECT_INCLUDE #include +#if !SDL_VERSION_ATLEAST(3, 0, 0) #include -#else -#include -#include #endif namespace love @@ -63,6 +60,13 @@ SDL_InitSubSystem(SDL_INIT_VIDEO); SDL_PumpEvents(); +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_EVENT_DROP_FILE, SDL_EVENT_DROP_FILE) > 0) + { + if (event.type == SDL_EVENT_DROP_FILE) + dropstr = std::string(event.drop.data); + } +#else if (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_DROPFILE, SDL_DROPFILE) > 0) { if (event.type == SDL_DROPFILE) @@ -71,6 +75,7 @@ SDL_free(event.drop.file); } } +#endif SDL_QuitSubSystem(SDL_INIT_VIDEO); @@ -122,9 +127,15 @@ void setWindowSRGBColorSpace(SDL_Window *window) // (at least, it was back when I tested in December 2016). if (@available(macOS 11.0, *)) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_PropertiesID props = SDL_GetWindowProperties(window); + NSWindow *window = (__bridge NSWindow *) SDL_GetProperty(props, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr); + window.colorSpace = [NSColorSpace sRGBColorSpace]; +#else SDL_SysWMinfo info = {}; if (SDL_GetWindowWMInfo(window, &info)) info.info.cocoa.window.colorSpace = [NSColorSpace sRGBColorSpace]; +#endif } } } diff --git a/src/modules/event/sdl/Event.cpp b/src/modules/event/sdl/Event.cpp index 82972a069..31a5ba2ac 100644 --- a/src/modules/event/sdl/Event.cpp +++ b/src/modules/event/sdl/Event.cpp @@ -24,7 +24,6 @@ #include "filesystem/Filesystem.h" #include "keyboard/sdl/Keyboard.h" #include "joystick/JoystickModule.h" -#include "joystick/sdl/Joystick.h" #include "touch/sdl/Touch.h" #include "graphics/Graphics.h" #include "window/Window.h" @@ -38,6 +37,58 @@ #include +#if SDL_VERSION_ATLEAST(3, 0, 0) +#include "joystick/sdl/JoystickSDL3.h" +#else +#include "joystick/sdl/Joystick.h" +#endif + +#if !SDL_VERSION_ATLEAST(3, 0, 0) +#define SDL_EVENT_DID_ENTER_BACKGROUND SDL_APP_DIDENTERBACKGROUND +#define SDL_EVENT_WILL_ENTER_FOREGROUND SDL_APP_WILLENTERFOREGROUND +#define SDL_EVENT_KEY_DOWN SDL_KEYDOWN +#define SDL_EVENT_KEY_UP SDL_KEYUP +#define SDL_EVENT_TEXT_INPUT SDL_TEXTINPUT +#define SDL_EVENT_TEXT_EDITING SDL_TEXTEDITING +#define SDL_EVENT_MOUSE_MOTION SDL_MOUSEMOTION +#define SDL_EVENT_MOUSE_BUTTON_DOWN SDL_MOUSEBUTTONDOWN +#define SDL_EVENT_MOUSE_BUTTON_UP SDL_MOUSEBUTTONUP +#define SDL_EVENT_MOUSE_WHEEL SDL_MOUSEWHEEL +#define SDL_EVENT_FINGER_DOWN SDL_FINGERDOWN +#define SDL_EVENT_FINGER_UP SDL_FINGERUP +#define SDL_EVENT_FINGER_MOTION SDL_FINGERMOTION + +#define SDL_EVENT_DROP_FILE SDL_DROPFILE +#define SDL_EVENT_QUIT SDL_QUIT +#define SDL_EVENT_TERMINATING SDL_APP_TERMINATING +#define SDL_EVENT_LOW_MEMORY SDL_APP_LOWMEMORY +#define SDL_EVENT_LOCALE_CHANGED SDL_LOCALECHANGED +#define SDL_EVENT_SENSOR_UPDATE SDL_SENSORUPDATE + +#define SDL_EVENT_JOYSTICK_BUTTON_DOWN SDL_JOYBUTTONDOWN +#define SDL_EVENT_JOYSTICK_BUTTON_UP SDL_JOYBUTTONUP +#define SDL_EVENT_JOYSTICK_AXIS_MOTION SDL_JOYAXISMOTION +#define SDL_EVENT_JOYSTICK_HAT_MOTION SDL_JOYHATMOTION +#define SDL_EVENT_JOYSTICK_ADDED SDL_JOYDEVICEADDED +#define SDL_EVENT_JOYSTICK_REMOVED SDL_JOYDEVICEREMOVED +#define SDL_EVENT_GAMEPAD_BUTTON_DOWN SDL_CONTROLLERBUTTONDOWN +#define SDL_EVENT_GAMEPAD_BUTTON_UP SDL_CONTROLLERBUTTONUP +#define SDL_EVENT_GAMEPAD_AXIS_MOTION SDL_CONTROLLERAXISMOTION +#define SDL_EVENT_GAMEPAD_SENSOR_UPDATE SDL_CONTROLLERSENSORUPDATE + +#define SDL_EVENT_WINDOW_FOCUS_GAINED SDL_WINDOWEVENT_FOCUS_GAINED +#define SDL_EVENT_WINDOW_FOCUS_LOST SDL_WINDOWEVENT_FOCUS_LOST +#define SDL_EVENT_WINDOW_MOUSE_ENTER SDL_WINDOWEVENT_ENTER +#define SDL_EVENT_WINDOW_MOUSE_LEAVE SDL_WINDOWEVENT_LEAVE +#define SDL_EVENT_WINDOW_SHOWN SDL_WINDOWEVENT_SHOWN +#define SDL_EVENT_WINDOW_HIDDEN SDL_WINDOWEVENT_HIDDEN +#define SDL_EVENT_WINDOW_RESIZED SDL_WINDOWEVENT_RESIZED +#define SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED SDL_WINDOWEVENT_SIZE_CHANGED +#define SDL_EVENT_WINDOW_MINIMIZED SDL_WINDOWEVENT_MINIMIZED +#define SDL_EVENT_WINDOW_RESTORED SDL_WINDOWEVENT_RESTORED + +#endif + namespace love { namespace event @@ -93,10 +144,10 @@ static int SDLCALL watchAppEvents(void * /*udata*/, SDL_Event *event) // On iOS, calling any OpenGL ES function after the function which triggers // SDL_APP_DIDENTERBACKGROUND is called will kill the app, so we handle it // with an event watch callback, which will be called inside that function. - case SDL_APP_DIDENTERBACKGROUND: - case SDL_APP_WILLENTERFOREGROUND: + case SDL_EVENT_DID_ENTER_BACKGROUND: + case SDL_EVENT_WILL_ENTER_FOREGROUND: if (gfx) - gfx->setActive(event->type == SDL_APP_WILLENTERFOREGROUND); + gfx->setActive(event->type == SDL_EVENT_WILL_ENTER_FOREGROUND); break; default: break; @@ -202,7 +253,7 @@ Message *Event::convert(const SDL_Event &e) switch (e.type) { - case SDL_KEYDOWN: + case SDL_EVENT_KEY_DOWN: if (e.key.repeat) { auto kb = Module::getInstance(Module::M_KEYBOARD); @@ -226,7 +277,7 @@ Message *Event::convert(const SDL_Event &e) vargs.emplace_back(e.key.repeat != 0); msg = new Message("keypressed", vargs); break; - case SDL_KEYUP: + case SDL_EVENT_KEY_UP: keyit = keys.find(e.key.keysym.sym); if (keyit != keys.end()) key = keyit->second; @@ -242,19 +293,19 @@ Message *Event::convert(const SDL_Event &e) vargs.emplace_back(txt2, strlen(txt2)); msg = new Message("keyreleased", vargs); break; - case SDL_TEXTINPUT: + case SDL_EVENT_TEXT_INPUT: txt = e.text.text; vargs.emplace_back(txt, strlen(txt)); msg = new Message("textinput", vargs); break; - case SDL_TEXTEDITING: + case SDL_EVENT_TEXT_EDITING: txt = e.edit.text; vargs.emplace_back(txt, strlen(txt)); vargs.emplace_back((double) e.edit.start); vargs.emplace_back((double) e.edit.length); msg = new Message("textedited", vargs); break; - case SDL_MOUSEMOTION: + case SDL_EVENT_MOUSE_MOTION: { double x = (double) e.motion.x; double y = (double) e.motion.y; @@ -278,8 +329,8 @@ Message *Event::convert(const SDL_Event &e) msg = new Message("mousemoved", vargs); } break; - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: { // SDL uses button 3 for the right mouse button, but we use button 2 int button = e.button.button; @@ -305,14 +356,14 @@ Message *Event::convert(const SDL_Event &e) vargs.emplace_back(e.button.which == SDL_TOUCH_MOUSEID); vargs.emplace_back((double) e.button.clicks); - bool down = e.type == SDL_MOUSEBUTTONDOWN; + bool down = e.type == SDL_EVENT_MOUSE_BUTTON_DOWN; msg = new Message(down ? "mousepressed" : "mousereleased", vargs); } break; - case SDL_MOUSEWHEEL: + case SDL_EVENT_MOUSE_WHEEL: vargs.emplace_back((double) e.wheel.x); vargs.emplace_back((double) e.wheel.y); -#if SDL_VERSION_ATLEAST(2, 0, 18) +#if SDL_VERSION_ATLEAST(2, 0, 18) && !SDL_VERSION_ATLEAST(3, 0, 0) // These values will be garbage if 2.0.18+ headers are used but a lower // version of SDL is used at runtime, but other bits of code already // prevent running in that situation. @@ -328,9 +379,9 @@ Message *Event::convert(const SDL_Event &e) msg = new Message("wheelmoved", vargs); break; - case SDL_FINGERDOWN: - case SDL_FINGERUP: - case SDL_FINGERMOTION: + case SDL_EVENT_FINGER_DOWN: + case SDL_EVENT_FINGER_UP: + case SDL_EVENT_FINGER_MOTION: // Touch events are disabled in OS X because we only actually want touch // screen events, but most touch devices in OS X aren't touch screens // (and SDL doesn't differentiate.) Non-screen touch devices like Mac @@ -364,35 +415,51 @@ Message *Event::convert(const SDL_Event &e) vargs.emplace_back(touchinfo.dy); vargs.emplace_back(touchinfo.pressure); - if (e.type == SDL_FINGERDOWN) + if (e.type == SDL_EVENT_FINGER_DOWN) txt = "touchpressed"; - else if (e.type == SDL_FINGERUP) + else if (e.type == SDL_EVENT_FINGER_UP) txt = "touchreleased"; else txt = "touchmoved"; msg = new Message(txt, vargs); #endif break; - case SDL_JOYBUTTONDOWN: - case SDL_JOYBUTTONUP: - case SDL_JOYAXISMOTION: - case SDL_JOYBALLMOTION: - case SDL_JOYHATMOTION: - case SDL_JOYDEVICEADDED: - case SDL_JOYDEVICEREMOVED: - case SDL_CONTROLLERBUTTONDOWN: - case SDL_CONTROLLERBUTTONUP: - case SDL_CONTROLLERAXISMOTION: + case SDL_EVENT_JOYSTICK_BUTTON_DOWN: + case SDL_EVENT_JOYSTICK_BUTTON_UP: + case SDL_EVENT_JOYSTICK_AXIS_MOTION: + case SDL_EVENT_JOYSTICK_HAT_MOTION: + case SDL_EVENT_JOYSTICK_ADDED: + case SDL_EVENT_JOYSTICK_REMOVED: + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: + case SDL_EVENT_GAMEPAD_BUTTON_UP: + case SDL_EVENT_GAMEPAD_AXIS_MOTION: #if SDL_VERSION_ATLEAST(2, 0, 14) && defined(LOVE_ENABLE_SENSOR) - case SDL_CONTROLLERSENSORUPDATE: + case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: #endif msg = convertJoystickEvent(e); break; +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDL_EVENT_WINDOW_FOCUS_GAINED: + case SDL_EVENT_WINDOW_FOCUS_LOST: + case SDL_EVENT_WINDOW_MOUSE_ENTER: + case SDL_EVENT_WINDOW_MOUSE_LEAVE: + case SDL_EVENT_WINDOW_SHOWN: + case SDL_EVENT_WINDOW_HIDDEN: + case SDL_EVENT_WINDOW_RESIZED: + case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: + case SDL_EVENT_WINDOW_MINIMIZED: + case SDL_EVENT_WINDOW_RESTORED: +#else case SDL_WINDOWEVENT: +#endif msg = convertWindowEvent(e); break; +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDL_EVENT_DISPLAY_ORIENTATION: +#else case SDL_DISPLAYEVENT: if (e.display.event == SDL_DISPLAYEVENT_ORIENTATION) +#endif { auto orientation = window::Window::ORIENTATION_UNKNOWN; switch ((SDL_DisplayOrientation) e.display.data1) @@ -418,47 +485,70 @@ Message *Event::convert(const SDL_Event &e) if (!window::Window::getConstant(orientation, txt)) txt = "unknown"; +#if SDL_VERSION_ATLEAST(3, 0, 0) + int count = 0; + int displayindex = 0; + SDL_DisplayID *displays = SDL_GetDisplays(&count); + for (int i = 0; i < count; i++) + { + if (displays[i] == e.display.displayID) + { + displayindex = i; + break; + } + } + SDL_free(displays); + vargs.emplace_back((double)(displayindex + 1)); +#else vargs.emplace_back((double)(e.display.display + 1)); +#endif vargs.emplace_back(txt, strlen(txt)); msg = new Message("displayrotated", vargs); } break; - case SDL_DROPFILE: + case SDL_EVENT_DROP_FILE: filesystem = Module::getInstance(Module::M_FILESYSTEM); if (filesystem != nullptr) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + const char *filepath = e.drop.data; +#else + const char *filepath = e.drop.file; +#endif // Allow mounting any dropped path, so zips or dirs can be mounted. - filesystem->allowMountingForPath(e.drop.file); + filesystem->allowMountingForPath(filepath); - if (filesystem->isRealDirectory(e.drop.file)) + if (filesystem->isRealDirectory(filepath)) { - vargs.emplace_back(e.drop.file, strlen(e.drop.file)); + vargs.emplace_back(filepath, strlen(filepath)); msg = new Message("directorydropped", vargs); } else { - auto *file = new love::filesystem::NativeFile(e.drop.file, love::filesystem::File::MODE_CLOSED); + auto *file = new love::filesystem::NativeFile(filepath, love::filesystem::File::MODE_CLOSED); vargs.emplace_back(&love::filesystem::NativeFile::type, file); msg = new Message("filedropped", vargs); file->release(); } } +#if !SDL_VERSION_ATLEAST(3, 0, 0) SDL_free(e.drop.file); +#endif break; - case SDL_QUIT: - case SDL_APP_TERMINATING: + case SDL_EVENT_QUIT: + case SDL_EVENT_TERMINATING: msg = new Message("quit"); break; - case SDL_APP_LOWMEMORY: + case SDL_EVENT_LOW_MEMORY: msg = new Message("lowmemory"); break; #if SDL_VERSION_ATLEAST(2, 0, 14) - case SDL_LOCALECHANGED: + case SDL_EVENT_LOCALE_CHANGED: msg = new Message("localechanged"); break; #endif - case SDL_SENSORUPDATE: + case SDL_EVENT_SENSOR_UPDATE: sensorInstance = Module::getInstance(M_SENSOR); if (sensorInstance) { @@ -467,13 +557,22 @@ Message *Event::convert(const SDL_Event &e) for (void *s: sensors) { SDL_Sensor *sensor = (SDL_Sensor *) s; +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_SensorID id = SDL_GetSensorInstanceID(sensor); +#else SDL_SensorID id = SDL_SensorGetInstanceID(sensor); +#endif if (e.sensor.which == id) { // Found sensor const char *sensorType; - if (!sensor::Sensor::getConstant(sensor::sdl::Sensor::convert(SDL_SensorGetType(sensor)), sensorType)) +#if SDL_VERSION_ATLEAST(3, 0, 0) + auto sdltype = SDL_GetSensorType(sensor); +#else + auto sdltype = SDL_SensorGetType(sensor); +#endif + if (!sensor::Sensor::getConstant(sensor::sdl::Sensor::convert(sdltype), sensorType)) sensorType = "unknown"; vargs.emplace_back(sensorType, strlen(sensorType)); @@ -516,19 +615,19 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const switch (e.type) { - case SDL_JOYBUTTONDOWN: - case SDL_JOYBUTTONUP: + case SDL_EVENT_JOYSTICK_BUTTON_DOWN: + case SDL_EVENT_JOYSTICK_BUTTON_UP: stick = joymodule->getJoystickFromID(e.jbutton.which); if (!stick) break; vargs.emplace_back(joysticktype, stick); vargs.emplace_back((double)(e.jbutton.button+1)); - msg = new Message((e.type == SDL_JOYBUTTONDOWN) ? + msg = new Message((e.type == SDL_EVENT_JOYSTICK_BUTTON_DOWN) ? "joystickpressed" : "joystickreleased", vargs); break; - case SDL_JOYAXISMOTION: + case SDL_EVENT_JOYSTICK_AXIS_MOTION: { stick = joymodule->getJoystickFromID(e.jaxis.which); if (!stick) @@ -541,7 +640,7 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const msg = new Message("joystickaxis", vargs); } break; - case SDL_JOYHATMOTION: + case SDL_EVENT_JOYSTICK_HAT_MOTION: if (!joystick::sdl::Joystick::getConstant(e.jhat.value, hat) || !joystick::Joystick::getConstant(hat, txt)) break; @@ -554,41 +653,59 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const vargs.emplace_back(txt, strlen(txt)); msg = new Message("joystickhat", vargs); break; - case SDL_CONTROLLERBUTTONDOWN: - case SDL_CONTROLLERBUTTONUP: - if (!joystick::sdl::Joystick::getConstant((SDL_GameControllerButton) e.cbutton.button, padbutton)) - break; + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: + case SDL_EVENT_GAMEPAD_BUTTON_UP: + { +#if SDL_VERSION_ATLEAST(3, 0, 0) + const auto &b = e.gbutton; + if (!joystick::sdl::Joystick::getConstant((SDL_GamepadButton) b.button, padbutton)) +#else + const auto &b = e.cbutton; + if (!joystick::sdl::Joystick::getConstant((SDL_GameControllerButton) b.button, padbutton)) +#endif + break; - if (!joystick::Joystick::getConstant(padbutton, txt)) - break; + if (!joystick::Joystick::getConstant(padbutton, txt)) + break; - stick = joymodule->getJoystickFromID(e.cbutton.which); - if (!stick) - break; + stick = joymodule->getJoystickFromID(b.which); + if (!stick) + break; - vargs.emplace_back(joysticktype, stick); - vargs.emplace_back(txt, strlen(txt)); - msg = new Message(e.type == SDL_CONTROLLERBUTTONDOWN ? - "gamepadpressed" : "gamepadreleased", vargs); + vargs.emplace_back(joysticktype, stick); + vargs.emplace_back(txt, strlen(txt)); + msg = new Message(e.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN ? + "gamepadpressed" : "gamepadreleased", vargs); + } break; - case SDL_CONTROLLERAXISMOTION: + case SDL_EVENT_GAMEPAD_AXIS_MOTION: +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (joystick::sdl::Joystick::getConstant((SDL_GamepadAxis) e.gaxis.axis, padaxis)) +#else if (joystick::sdl::Joystick::getConstant((SDL_GameControllerAxis) e.caxis.axis, padaxis)) +#endif { if (!joystick::Joystick::getConstant(padaxis, txt)) break; - stick = joymodule->getJoystickFromID(e.caxis.which); +#if SDL_VERSION_ATLEAST(3, 0, 0) + const auto &a = e.gaxis; +#else + const auto &a = e.caxis; +#endif + + stick = joymodule->getJoystickFromID(a.which); if (!stick) break; vargs.emplace_back(joysticktype, stick); vargs.emplace_back(txt, strlen(txt)); - float value = joystick::Joystick::clampval(e.caxis.value / 32768.0f); + float value = joystick::Joystick::clampval(a.value / 32768.0f); vargs.emplace_back((double) value); msg = new Message("gamepadaxis", vargs); } break; - case SDL_JOYDEVICEADDED: + case SDL_EVENT_JOYSTICK_ADDED: // jdevice.which is the joystick device index. stick = joymodule->addJoystick(e.jdevice.which); if (stick) @@ -597,7 +714,7 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const msg = new Message("joystickadded", vargs); } break; - case SDL_JOYDEVICEREMOVED: + case SDL_EVENT_JOYSTICK_REMOVED: // jdevice.which is the joystick instance ID now. stick = joymodule->getJoystickFromID(e.jdevice.which); if (stick) @@ -608,23 +725,30 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const } break; #if SDL_VERSION_ATLEAST(2, 0, 14) && defined(LOVE_ENABLE_SENSOR) - case SDL_CONTROLLERSENSORUPDATE: - stick = joymodule->getJoystickFromID(e.csensor.which); - if (stick) + case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: { - using Sensor = love::sensor::Sensor; - - const char *sensorName; - Sensor::SensorType sensorType = love::sensor::sdl::Sensor::convert((SDL_SensorType) e.csensor.sensor); - if (!Sensor::getConstant(sensorType, sensorName)) - sensorName = "unknown"; - - vargs.emplace_back(joysticktype, stick); - vargs.emplace_back(sensorName, strlen(sensorName)); - vargs.emplace_back(e.csensor.data[0]); - vargs.emplace_back(e.csensor.data[1]); - vargs.emplace_back(e.csensor.data[2]); - msg = new Message("joysticksensorupdated", vargs); +#if SDL_VERSION_ATLEAST(3, 0, 0) + const auto &sens = e.gsensor; +#else + const auto &sens = e.csensor; +#endif + stick = joymodule->getJoystickFromID(sens.which); + if (stick) + { + using Sensor = love::sensor::Sensor; + + const char *sensorName; + Sensor::SensorType sensorType = love::sensor::sdl::Sensor::convert((SDL_SensorType) sens.sensor); + if (!Sensor::getConstant(sensorType, sensorName)) + sensorName = "unknown"; + + vargs.emplace_back(joysticktype, stick); + vargs.emplace_back(sensorName, strlen(sensorName)); + vargs.emplace_back(sens.data[0]); + vargs.emplace_back(sens.data[1]); + vargs.emplace_back(sens.data[2]); + msg = new Message("joysticksensorupdated", vargs); + } } break; #endif // SDL_VERSION_ATLEAST(2, 0, 14) && defined(LOVE_ENABLE_SENSOR) @@ -645,27 +769,30 @@ Message *Event::convertWindowEvent(const SDL_Event &e) window::Window *win = nullptr; graphics::Graphics *gfx = nullptr; - if (e.type != SDL_WINDOWEVENT) - return nullptr; +#if SDL_VERSION_ATLEAST(3, 0, 0) + auto event = e.type; +#else + auto event = e.window.event; +#endif - switch (e.window.event) + switch (event) { - case SDL_WINDOWEVENT_FOCUS_GAINED: - case SDL_WINDOWEVENT_FOCUS_LOST: - vargs.emplace_back(e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED); + case SDL_EVENT_WINDOW_FOCUS_GAINED: + case SDL_EVENT_WINDOW_FOCUS_LOST: + vargs.emplace_back(event == SDL_EVENT_WINDOW_FOCUS_GAINED); msg = new Message("focus", vargs); break; - case SDL_WINDOWEVENT_ENTER: - case SDL_WINDOWEVENT_LEAVE: - vargs.emplace_back(e.window.event == SDL_WINDOWEVENT_ENTER); + case SDL_EVENT_WINDOW_MOUSE_ENTER: + case SDL_EVENT_WINDOW_MOUSE_LEAVE: + vargs.emplace_back(event == SDL_EVENT_WINDOW_MOUSE_ENTER); msg = new Message("mousefocus", vargs); break; - case SDL_WINDOWEVENT_SHOWN: - case SDL_WINDOWEVENT_HIDDEN: - vargs.emplace_back(e.window.event == SDL_WINDOWEVENT_SHOWN); + case SDL_EVENT_WINDOW_SHOWN: + case SDL_EVENT_WINDOW_HIDDEN: + vargs.emplace_back(event == SDL_EVENT_WINDOW_SHOWN); msg = new Message("visible", vargs); break; - case SDL_WINDOWEVENT_RESIZED: + case SDL_EVENT_WINDOW_RESIZED: { double width = e.window.data1; double height = e.window.data2; @@ -693,19 +820,19 @@ Message *Event::convertWindowEvent(const SDL_Event &e) msg = new Message("resize", vargs); } break; - case SDL_WINDOWEVENT_SIZE_CHANGED: + case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: win = Module::getInstance(Module::M_WINDOW); if (win) win->onSizeChanged(e.window.data1, e.window.data2); break; - case SDL_WINDOWEVENT_MINIMIZED: - case SDL_WINDOWEVENT_RESTORED: + case SDL_EVENT_WINDOW_MINIMIZED: + case SDL_EVENT_WINDOW_RESTORED: #ifdef LOVE_ANDROID if (auto audio = Module::getInstance(Module::M_AUDIO)) { - if (e.window.event == SDL_WINDOWEVENT_MINIMIZED) + if (event == SDL_EVENT_WINDOW_MINIMIZED) audio->pauseContext(); - else if (e.window.event == SDL_WINDOWEVENT_RESTORED) + else if (event == SDL_EVENT_WINDOW_RESTORED) audio->resumeContext(); } #endif diff --git a/src/modules/filesystem/wrap_Filesystem.cpp b/src/modules/filesystem/wrap_Filesystem.cpp index 13d5e48f9..cb4e25dbe 100644 --- a/src/modules/filesystem/wrap_Filesystem.cpp +++ b/src/modules/filesystem/wrap_Filesystem.cpp @@ -35,6 +35,7 @@ // SDL #include +#include // STL #include @@ -987,7 +988,12 @@ int extloader(lua_State *L) // We look for both loveopen_ and luaopen_, so libraries with specific love support // can tell when they've been loaded by love. - void *func = SDL_LoadFunction(handle, ("loveopen_" + tokenized_function).c_str()); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_FunctionPointer func = nullptr; +#else + void *func = nullptr; +#endif + func = SDL_LoadFunction(handle, ("loveopen_" + tokenized_function).c_str()); if (!func) func = SDL_LoadFunction(handle, ("luaopen_" + tokenized_function).c_str()); diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index 4d99efb55..ff4220010 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -62,7 +62,7 @@ static void *LOVEGetProcAddress(const char *name) return proc; #endif - return SDL_GL_GetProcAddress(name); + return (void *) SDL_GL_GetProcAddress(name); } OpenGL::TempDebugGroup::TempDebugGroup(const char *name) diff --git a/src/modules/joystick/Joystick.h b/src/modules/joystick/Joystick.h index 4727c594a..db5de2d7e 100644 --- a/src/modules/joystick/Joystick.h +++ b/src/modules/joystick/Joystick.h @@ -24,6 +24,7 @@ // LOVE #include "common/Object.h" #include "common/StringMap.h" +#include "common/int.h" #include "sensor/Sensor.h" // stdlib @@ -172,7 +173,7 @@ class Joystick : public Object virtual ~Joystick() {} - virtual bool open(int deviceindex) = 0; + virtual bool open(int64 deviceid) = 0; virtual void close() = 0; virtual bool isConnected() const = 0; @@ -194,7 +195,7 @@ class Joystick : public Object virtual void setPlayerIndex(int index) = 0; virtual int getPlayerIndex() const = 0; - virtual bool openGamepad(int deviceindex) = 0; + virtual bool openGamepad(int64 deviceid) = 0; virtual bool isGamepad() const = 0; virtual GamepadType getGamepadType() const = 0; diff --git a/src/modules/joystick/JoystickModule.h b/src/modules/joystick/JoystickModule.h index dc890efa9..ef0f5f6db 100644 --- a/src/modules/joystick/JoystickModule.h +++ b/src/modules/joystick/JoystickModule.h @@ -43,7 +43,7 @@ class JoystickModule : public Module * Adds a connected Joystick device and opens it for use. * Returns NULL if the Joystick could not be added. **/ - virtual Joystick *addJoystick(int deviceindex) = 0; + virtual Joystick *addJoystick(int64 deviceid) = 0; /** * Removes a disconnected Joystick device. diff --git a/src/modules/joystick/sdl/Joystick.cpp b/src/modules/joystick/sdl/Joystick.cpp index def324747..826930444 100644 --- a/src/modules/joystick/sdl/Joystick.cpp +++ b/src/modules/joystick/sdl/Joystick.cpp @@ -31,6 +31,8 @@ #include #include +#if !SDL_VERSION_ATLEAST(3, 0, 0) + #ifndef SDL_TICKS_PASSED #define SDL_TICKS_PASSED(A, B) ((Sint32)((B) - (A)) <= 0) #endif @@ -69,8 +71,10 @@ Joystick::~Joystick() close(); } -bool Joystick::open(int deviceindex) +bool Joystick::open(int64 deviceid) { + int deviceindex = (int) deviceid; + close(); joyhandle = SDL_JoystickOpen(deviceindex); @@ -263,8 +267,10 @@ int Joystick::getPlayerIndex() const #endif } -bool Joystick::openGamepad(int deviceindex) +bool Joystick::openGamepad(int64 deviceid) { + int deviceindex = (int) deviceid; + if (!SDL_IsGameController(deviceindex)) return false; @@ -852,3 +858,5 @@ EnumMap +#if !SDL_VERSION_ATLEAST(3, 0, 0) + namespace love { namespace joystick @@ -44,7 +46,7 @@ class Joystick : public love::joystick::Joystick virtual ~Joystick(); - bool open(int deviceindex) override; + bool open(int64 deviceid) override; void close() override; bool isConnected() const override; @@ -66,7 +68,7 @@ class Joystick : public love::joystick::Joystick void setPlayerIndex(int index) override; int getPlayerIndex() const override; - bool openGamepad(int deviceindex) override; + bool openGamepad(int64 deviceid) override; bool isGamepad() const override; GamepadType getGamepadType() const override; @@ -155,4 +157,6 @@ class Joystick : public love::joystick::Joystick } // joystick } // love +#endif // !SDL_VERSION_ATLEAST(3, 0, 0) + #endif // LOVE_JOYSTICK_SDL_JOYSTICK_H diff --git a/src/modules/joystick/sdl/JoystickModule.cpp b/src/modules/joystick/sdl/JoystickModule.cpp index de45fe0ff..9c319a822 100644 --- a/src/modules/joystick/sdl/JoystickModule.cpp +++ b/src/modules/joystick/sdl/JoystickModule.cpp @@ -21,6 +21,7 @@ #include "common/config.h" #include "JoystickModule.h" #include "Joystick.h" +#include "JoystickSDL3.h" // SDL #include @@ -41,10 +42,26 @@ namespace sdl JoystickModule::JoystickModule() { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD) < 0) +#else if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) +#endif throw love::Exception("Could not initialize SDL joystick subsystem (%s)", SDL_GetError()); // Initialize any joysticks which are already connected. +#if SDL_VERSION_ATLEAST(3, 0, 0) + int count = 0; + SDL_JoystickID *sticks = SDL_GetJoysticks(&count); + for (int i = 0; i < count; i++) + addJoystick((int64) sticks[i]); + SDL_free(sticks); + + // Start joystick event watching. Joysticks are automatically added and + // removed via love.event. + SDL_SetJoystickEventsEnabled(SDL_TRUE); + SDL_SetGamepadEventsEnabled(SDL_TRUE); +#else for (int i = 0; i < SDL_NumJoysticks(); i++) addJoystick(i); @@ -52,6 +69,7 @@ JoystickModule::JoystickModule() // removed via love.event. SDL_JoystickEventState(SDL_ENABLE); SDL_GameControllerEventState(SDL_ENABLE); +#endif } JoystickModule::~JoystickModule() @@ -66,7 +84,11 @@ JoystickModule::~JoystickModule() if (SDL_WasInit(SDL_INIT_HAPTIC) != 0) SDL_QuitSubSystem(SDL_INIT_HAPTIC); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD); +#else SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); +#endif } const char *JoystickModule::getName() const @@ -110,12 +132,16 @@ love::joystick::Joystick *JoystickModule::getJoystickFromID(int instanceid) return nullptr; } -love::joystick::Joystick *JoystickModule::addJoystick(int deviceindex) +love::joystick::Joystick *JoystickModule::addJoystick(int64 deviceid) { - if (deviceindex < 0 || deviceindex >= SDL_NumJoysticks()) +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (deviceid == 0) +#else + if (deviceid < 0 || (int) deviceid >= SDL_NumJoysticks()) +#endif return nullptr; - std::string guidstr = getDeviceGUID(deviceindex); + std::string guidstr = getDeviceGUID(deviceid); joystick::Joystick *joystick = 0; bool reused = false; @@ -132,14 +158,18 @@ love::joystick::Joystick *JoystickModule::addJoystick(int deviceindex) if (!joystick) { +#if SDL_VERSION_ATLEAST(3, 0, 0) joystick = new Joystick((int) joysticks.size()); +#else + joystick = new Joystick((int) joysticks.size()); +#endif joysticks.push_back(joystick); } // Make sure the Joystick object isn't in the active list already. removeJoystick(joystick); - if (!joystick->open(deviceindex)) + if (!joystick->open(deviceid)) return nullptr; // Make sure multiple instances of the same physical joystick aren't added @@ -188,10 +218,17 @@ bool JoystickModule::setGamepadMapping(const std::string &guid, Joystick::Gamepa if (guid.length() != 32) throw love::Exception("Invalid joystick GUID: %s", guid.c_str()); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_JoystickGUID sdlguid = SDL_GetJoystickGUIDFromString(guid.c_str()); + std::string mapstr; + + char *sdlmapstr = SDL_GetGamepadMappingForGUID(sdlguid); +#else SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(guid.c_str()); std::string mapstr; char *sdlmapstr = SDL_GameControllerMappingForGUID(sdlguid); +#endif if (sdlmapstr) { mapstr = sdlmapstr; @@ -273,7 +310,11 @@ bool JoystickModule::setGamepadMapping(const std::string &guid, Joystick::Gamepa } // 1 == added, 0 == updated, -1 == error. +#if SDL_VERSION_ATLEAST(3, 0, 0) + int status = SDL_AddGamepadMapping(mapstr.c_str()); +#else int status = SDL_GameControllerAddMapping(mapstr.c_str()); +#endif if (status != -1) recentGamepadGUIDs[guid] = true; @@ -288,8 +329,13 @@ bool JoystickModule::setGamepadMapping(const std::string &guid, Joystick::Gamepa std::string JoystickModule::stringFromGamepadInput(Joystick::GamepadInput gpinput) const { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_GamepadAxis sdlaxis; + SDL_GamepadButton sdlbutton; +#else SDL_GameControllerAxis sdlaxis; SDL_GameControllerButton sdlbutton; +#endif const char *gpinputname = nullptr; @@ -297,11 +343,19 @@ std::string JoystickModule::stringFromGamepadInput(Joystick::GamepadInput gpinpu { case Joystick::INPUT_TYPE_AXIS: if (Joystick::getConstant(gpinput.axis, sdlaxis)) +#if SDL_VERSION_ATLEAST(3, 0, 0) + gpinputname = SDL_GetGamepadStringForAxis(sdlaxis); +#else gpinputname = SDL_GameControllerGetStringForAxis(sdlaxis); +#endif break; case Joystick::INPUT_TYPE_BUTTON: if (Joystick::getConstant(gpinput.button, sdlbutton)) +#if SDL_VERSION_ATLEAST(3, 0, 0) + gpinputname = SDL_GetGamepadStringForButton(sdlbutton); +#else gpinputname = SDL_GameControllerGetStringForButton(sdlbutton); +#endif break; default: break; @@ -351,12 +405,25 @@ void JoystickModule::checkGamepads(const std::string &guid) const // Make sure all connected joysticks of a certain guid that are // gamepad-capable are opened as such. +#if SDL_VERSION_ATLEAST(3, 0, 0) + int count = 0; + SDL_JoystickID *sdlsticks = SDL_GetJoysticks(&count); + for (int d_index = 0; d_index < count; d_index++) + { + if (!SDL_IsGamepad(sdlsticks[d_index])) + continue; + + auto sdlid = sdlsticks[d_index]; +#else for (int d_index = 0; d_index < SDL_NumJoysticks(); d_index++) { if (!SDL_IsGameController(d_index)) continue; - if (guid.compare(getDeviceGUID(d_index)) != 0) + auto sdlid = d_index; +#endif + + if (guid.compare(getDeviceGUID(sdlid)) != 0) continue; for (auto stick : activeSticks) @@ -366,6 +433,17 @@ void JoystickModule::checkGamepads(const std::string &guid) const // Big hack time: open the index as a game controller and compare // the underlying joystick handle to the active stick's. +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Gamepad *controller = SDL_OpenGamepad(sdlid); + if (controller == nullptr) + continue; + + // GameController objects are reference-counted in SDL, so we don't want to + // have a joystick open when trying to re-initialize it + SDL_Joystick *sdlstick = SDL_GetGamepadJoystick(controller); + bool open_gamepad = (sdlstick == (SDL_Joystick *) stick->getHandle()); + SDL_CloseGamepad(controller); +#else SDL_GameController *controller = SDL_GameControllerOpen(d_index); if (controller == nullptr) continue; @@ -375,25 +453,36 @@ void JoystickModule::checkGamepads(const std::string &guid) const SDL_Joystick *sdlstick = SDL_GameControllerGetJoystick(controller); bool open_gamepad = (sdlstick == (SDL_Joystick *) stick->getHandle()); SDL_GameControllerClose(controller); +#endif // open as gamepad if necessary if (open_gamepad) - stick->openGamepad(d_index); + stick->openGamepad(sdlid); } } } -std::string JoystickModule::getDeviceGUID(int deviceindex) const +std::string JoystickModule::getDeviceGUID(int64 deviceid) const { - if (deviceindex < 0 || deviceindex >= SDL_NumJoysticks()) - return std::string(""); - // SDL_JoystickGetGUIDString uses 32 bytes plus the null terminator. char guidstr[33] = {'\0'}; +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (deviceid <= 0) + return std::string(""); + + // SDL's GUIDs identify *classes* of devices, instead of unique devices. + SDL_JoystickGUID guid = SDL_GetJoystickInstanceGUID((SDL_JoystickID)deviceid); + SDL_GetJoystickGUIDString(guid, guidstr, sizeof(guidstr)); +#else + int deviceindex = (int) deviceid; + if (deviceindex < 0 || deviceindex >= SDL_NumJoysticks()) + return std::string(""); + // SDL2's GUIDs identify *classes* of devices, instead of unique devices. SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(deviceindex); SDL_JoystickGetGUIDString(guid, guidstr, sizeof(guidstr)); +#endif return std::string(guidstr); } @@ -438,7 +527,11 @@ void JoystickModule::loadGamepadMappings(const std::string &mappings) mapping.erase(pstartpos, pendpos - pstartpos + 1); } +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (SDL_AddGamepadMapping(mapping.c_str()) != -1) +#else if (SDL_GameControllerAddMapping(mapping.c_str()) != -1) +#endif { success = true; std::string guid = mapping.substr(0, mapping.find_first_of(',')); @@ -458,9 +551,13 @@ void JoystickModule::loadGamepadMappings(const std::string &mappings) std::string JoystickModule::getGamepadMappingString(const std::string &guid) const { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_JoystickGUID sdlguid = SDL_GetJoystickGUIDFromString(guid.c_str()); + char *sdlmapping = SDL_GetGamepadMappingForGUID(sdlguid); +#else SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(guid.c_str()); - char *sdlmapping = SDL_GameControllerMappingForGUID(sdlguid); +#endif if (sdlmapping == nullptr) return ""; @@ -483,9 +580,13 @@ std::string JoystickModule::saveGamepadMappings() for (const auto &g : recentGamepadGUIDs) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_JoystickGUID sdlguid = SDL_GetJoystickGUIDFromString(g.first.c_str()); + char *sdlmapping = SDL_GetGamepadMappingForGUID(sdlguid); +#else SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(g.first.c_str()); - char *sdlmapping = SDL_GameControllerMappingForGUID(sdlguid); +#endif if (sdlmapping == nullptr) continue; diff --git a/src/modules/joystick/sdl/JoystickModule.h b/src/modules/joystick/sdl/JoystickModule.h index 39b9548df..d543d44fb 100644 --- a/src/modules/joystick/sdl/JoystickModule.h +++ b/src/modules/joystick/sdl/JoystickModule.h @@ -48,7 +48,7 @@ class JoystickModule : public love::joystick::JoystickModule const char *getName() const override; // Implements JoystickModule. - love::joystick::Joystick *addJoystick(int deviceindex) override; + love::joystick::Joystick *addJoystick(int64 deviceid) override; void removeJoystick(love::joystick::Joystick *joystick) override; love::joystick::Joystick *getJoystickFromID(int instanceid) override; love::joystick::Joystick *getJoystick(int joyindex) override; @@ -69,7 +69,7 @@ class JoystickModule : public love::joystick::JoystickModule void checkGamepads(const std::string &guid) const; // SDL2's GUIDs identify *classes* of devices, instead of unique devices. - std::string getDeviceGUID(int deviceindex) const; + std::string getDeviceGUID(int64 deviceid) const; // Lists of currently connected Joysticks. std::vector activeSticks; diff --git a/src/modules/joystick/sdl/JoystickSDL3.cpp b/src/modules/joystick/sdl/JoystickSDL3.cpp new file mode 100644 index 000000000..d671cd4a5 --- /dev/null +++ b/src/modules/joystick/sdl/JoystickSDL3.cpp @@ -0,0 +1,649 @@ +/** + * Copyright (c) 2006-2023 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +// LOVE +#include "common/config.h" +#include "JoystickSDL3.h" +#include "common/int.h" +#include "sensor/sdl/Sensor.h" + +// SDL +#include + +// C++ +#include +#include + +namespace love +{ +namespace joystick +{ +namespace sdl +{ + +Joystick::Joystick(int id) + : joyhandle(nullptr) + , controller(nullptr) + , joystickType(JOYSTICK_TYPE_UNKNOWN) + , instanceid(-1) + , id(id) +{ +} + +Joystick::~Joystick() +{ + close(); +} + +bool Joystick::open(int64 deviceid) +{ + close(); + + joyhandle = SDL_OpenJoystick((SDL_JoystickID) deviceid); + + if (joyhandle) + { + instanceid = SDL_GetJoystickInstanceID(joyhandle); + + // SDL_JoystickGetGUIDString uses 32 bytes plus the null terminator. + char cstr[33]; + + SDL_JoystickGUID sdlguid = SDL_GetJoystickGUID(joyhandle); + SDL_GetJoystickGUIDString(sdlguid, cstr, (int) sizeof(cstr)); + + pguid = std::string(cstr); + + // See if SDL thinks this is a Game Controller. + openGamepad(deviceid); + + // Prefer the Joystick name for consistency. + const char *joyname = SDL_GetJoystickName(joyhandle); + if (!joyname && controller) + joyname = SDL_GetGamepadName(controller); + + if (joyname) + name = joyname; + + switch (SDL_GetJoystickType(joyhandle)) + { + case SDL_JOYSTICK_TYPE_GAMEPAD: + joystickType = JOYSTICK_TYPE_GAMEPAD; + break; + case SDL_JOYSTICK_TYPE_WHEEL: + joystickType = JOYSTICK_TYPE_WHEEL; + break; + case SDL_JOYSTICK_TYPE_ARCADE_STICK: + joystickType = JOYSTICK_TYPE_ARCADE_STICK; + break; + case SDL_JOYSTICK_TYPE_FLIGHT_STICK: + joystickType = JOYSTICK_TYPE_FLIGHT_STICK; + break; + case SDL_JOYSTICK_TYPE_DANCE_PAD: + joystickType = JOYSTICK_TYPE_DANCE_PAD; + break; + case SDL_JOYSTICK_TYPE_GUITAR: + joystickType = JOYSTICK_TYPE_GUITAR; + break; + case SDL_JOYSTICK_TYPE_DRUM_KIT: + joystickType = JOYSTICK_TYPE_DRUM_KIT; + break; + case SDL_JOYSTICK_TYPE_ARCADE_PAD: + joystickType = JOYSTICK_TYPE_ARCADE_PAD; + break; + case SDL_JOYSTICK_TYPE_THROTTLE: + joystickType = JOYSTICK_TYPE_THROTTLE; + break; + default: + joystickType = JOYSTICK_TYPE_UNKNOWN; + break; + } + } + + return isConnected(); +} + +void Joystick::close() +{ + if (controller) + SDL_CloseGamepad(controller); + + if (joyhandle) + SDL_CloseJoystick(joyhandle); + + joyhandle = nullptr; + controller = nullptr; + instanceid = -1; +} + +bool Joystick::isConnected() const +{ + return joyhandle != nullptr && SDL_JoystickConnected(joyhandle); +} + +const char *Joystick::getName() const +{ + return name.c_str(); +} + +Joystick::JoystickType Joystick::getJoystickType() const +{ + return joystickType; +} + +int Joystick::getAxisCount() const +{ + return isConnected() ? SDL_GetNumJoystickAxes(joyhandle) : 0; +} + +int Joystick::getButtonCount() const +{ + return isConnected() ? SDL_GetNumJoystickButtons(joyhandle) : 0; +} + +int Joystick::getHatCount() const +{ + return isConnected() ? SDL_GetNumJoystickHats(joyhandle) : 0; +} + +float Joystick::getAxis(int axisindex) const +{ + if (!isConnected() || axisindex < 0 || axisindex >= getAxisCount()) + return 0; + + return clampval(((float) SDL_GetJoystickAxis(joyhandle, axisindex))/32768.0f); +} + +std::vector Joystick::getAxes() const +{ + std::vector axes; + int count = getAxisCount(); + + if (!isConnected() || count <= 0) + return axes; + + axes.reserve(count); + + for (int i = 0; i < count; i++) + axes.push_back(clampval(((float) SDL_GetJoystickAxis(joyhandle, i))/32768.0f)); + + return axes; +} + +Joystick::Hat Joystick::getHat(int hatindex) const +{ + Hat h = HAT_INVALID; + + if (!isConnected() || hatindex < 0 || hatindex >= getHatCount()) + return h; + + getConstant(SDL_GetJoystickHat(joyhandle, hatindex), h); + + return h; +} + +bool Joystick::isDown(const std::vector &buttonlist) const +{ + if (!isConnected()) + return false; + + int numbuttons = getButtonCount(); + + for (int button : buttonlist) + { + if (button < 0 || button >= numbuttons) + continue; + + if (SDL_GetJoystickButton(joyhandle, button) == 1) + return true; + } + + return false; +} + +void Joystick::setPlayerIndex(int index) +{ + if (!isConnected()) + return; + + SDL_SetJoystickPlayerIndex(joyhandle, index); +} + +int Joystick::getPlayerIndex() const +{ + if (!isConnected()) + return -1; + + return SDL_GetJoystickPlayerIndex(joyhandle); +} + +bool Joystick::openGamepad(int64 deviceid) +{ + if (!SDL_IsGamepad((SDL_JoystickID)deviceid)) + return false; + + if (isGamepad()) + { + SDL_CloseGamepad(controller); + controller = nullptr; + } + + controller = SDL_OpenGamepad((SDL_JoystickID)deviceid); + return isGamepad(); +} + +bool Joystick::isGamepad() const +{ + return controller != nullptr; +} + +Joystick::GamepadType Joystick::getGamepadType() const +{ + if (controller == nullptr) + return GAMEPAD_TYPE_UNKNOWN; + + switch (SDL_GetGamepadType(controller)) + { + case SDL_GAMEPAD_TYPE_UNKNOWN: return GAMEPAD_TYPE_UNKNOWN; + case SDL_GAMEPAD_TYPE_XBOX360: return GAMEPAD_TYPE_XBOX360; + case SDL_GAMEPAD_TYPE_XBOXONE: return GAMEPAD_TYPE_XBOXONE; + case SDL_GAMEPAD_TYPE_PS3: return GAMEPAD_TYPE_PS3; + case SDL_GAMEPAD_TYPE_PS4: return GAMEPAD_TYPE_PS4; + case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO: return GAMEPAD_TYPE_NINTENDO_SWITCH_PRO; + case SDL_GAMEPAD_TYPE_PS5: return GAMEPAD_TYPE_PS5; + case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT: return GAMEPAD_TYPE_JOYCON_LEFT; + case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT: return GAMEPAD_TYPE_JOYCON_RIGHT; + case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR: return GAMEPAD_TYPE_JOYCON_PAIR; + default: return GAMEPAD_TYPE_UNKNOWN; + } + + return GAMEPAD_TYPE_UNKNOWN; +} + +float Joystick::getGamepadAxis(love::joystick::Joystick::GamepadAxis axis) const +{ + if (!isConnected() || !isGamepad()) + return 0.f; + + SDL_GamepadAxis sdlaxis; + if (!getConstant(axis, sdlaxis)) + return 0.f; + + Sint16 value = SDL_GetGamepadAxis(controller, sdlaxis); + + return clampval((float) value / 32768.0f); +} + +bool Joystick::isGamepadDown(const std::vector &blist) const +{ + if (!isConnected() || !isGamepad()) + return false; + + SDL_GamepadButton sdlbutton; + + for (GamepadButton button : blist) + { + if (!getConstant(button, sdlbutton)) + continue; + + if (SDL_GetGamepadButton(controller, sdlbutton) == 1) + return true; + } + + return false; +} + +Joystick::JoystickInput Joystick::getGamepadMapping(const GamepadInput &input) const +{ + Joystick::JoystickInput jinput; + jinput.type = INPUT_TYPE_MAX_ENUM; + + if (!isGamepad()) + return jinput; + + SDL_GamepadButton sdlbutton = SDL_GAMEPAD_BUTTON_INVALID; + SDL_GamepadAxis sdlaxis = SDL_GAMEPAD_AXIS_INVALID; + + switch (input.type) + { + case INPUT_TYPE_BUTTON: + getConstant(input.button, sdlbutton); + break; + case INPUT_TYPE_AXIS: + getConstant(input.axis, sdlaxis); + break; + default: + break; + } + + int bindcount = 0; + SDL_GamepadBinding **sdlbindings = SDL_GetGamepadBindings(controller, &bindcount); + const SDL_GamepadBinding *sdlbinding = nullptr; + for (int i = 0; i < bindcount; i++) + { + const SDL_GamepadBinding *b = sdlbindings[i]; + if ((input.type == INPUT_TYPE_BUTTON && b->outputType == SDL_GAMEPAD_BINDTYPE_BUTTON && b->output.button == sdlbutton) + || (input.type == INPUT_TYPE_AXIS && b->outputType == SDL_GAMEPAD_BINDTYPE_AXIS && b->output.axis.axis == sdlaxis)) + { + switch (b->inputType) + { + case SDL_GAMEPAD_BINDTYPE_BUTTON: + jinput.type = INPUT_TYPE_BUTTON; + jinput.button = b->input.button; + break; + case SDL_GAMEPAD_BINDTYPE_AXIS: + jinput.type = INPUT_TYPE_AXIS; + jinput.axis = b->input.axis.axis; + break; + case SDL_GAMEPAD_BINDTYPE_HAT: + if (getConstant(b->input.hat.hat_mask, jinput.hat.value)) + { + jinput.type = INPUT_TYPE_HAT; + jinput.hat.index = b->input.hat.hat; + } + break; + case SDL_GAMEPAD_BINDTYPE_NONE: + default: + break; + } + + break; + } + } + + SDL_free(sdlbindings); + + return jinput; +} + +std::string Joystick::getGamepadMappingString() const +{ + char *sdlmapping = nullptr; + + if (controller != nullptr) + sdlmapping = SDL_GetGamepadMapping(controller); + + if (sdlmapping == nullptr) + { + SDL_JoystickGUID sdlguid = SDL_GetJoystickGUIDFromString(pguid.c_str()); + sdlmapping = SDL_GetGamepadMappingForGUID(sdlguid); + } + + if (sdlmapping == nullptr) + return ""; + + std::string mappingstr(sdlmapping); + SDL_free(sdlmapping); + + // Matches SDL_GameControllerAddMappingsFromRW. + if (mappingstr.find_last_of(',') != mappingstr.length() - 1) + mappingstr += ","; + + if (mappingstr.find("platform:") == std::string::npos) + mappingstr += "platform:" + std::string(SDL_GetPlatform()); + + return mappingstr; +} + +void *Joystick::getHandle() const +{ + return joyhandle; +} + +std::string Joystick::getGUID() const +{ + // SDL2's GUIDs identify *classes* of devices, instead of unique devices. + return pguid; +} + +int Joystick::getInstanceID() const +{ + return instanceid; +} + +int Joystick::getID() const +{ + return id; +} + +void Joystick::getDeviceInfo(int &vendorID, int &productID, int &productVersion) const +{ + if (joyhandle != nullptr) + { + vendorID = SDL_GetJoystickVendor(joyhandle); + productID = SDL_GetJoystickProduct(joyhandle); + productVersion = SDL_GetJoystickProductVersion(joyhandle); + } + else + { + vendorID = 0; + productID = 0; + productVersion = 0; + } +} + +bool Joystick::isVibrationSupported() +{ + if (!isConnected()) + return false; + + SDL_PropertiesID props = SDL_GetJoystickProperties(joyhandle); + return SDL_GetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, SDL_FALSE); +} + +bool Joystick::setVibration(float left, float right, float duration) +{ + left = std::min(std::max(left, 0.0f), 1.0f); + right = std::min(std::max(right, 0.0f), 1.0f); + + if (left == 0.0f && right == 0.0f) + return setVibration(); + + if (!isConnected()) + return false; + + Uint32 length = LOVE_UINT32_MAX; + if (duration >= 0.0f) + { + float maxduration = (float) (std::numeric_limits::max() / 1000.0); + length = Uint32(std::min(duration, maxduration) * 1000); + } + + return SDL_RumbleJoystick(joyhandle, (Uint16)(left * LOVE_UINT16_MAX), (Uint16)(right * LOVE_UINT16_MAX), length) == 0; +} + +bool Joystick::setVibration() +{ + return isConnected() && SDL_RumbleJoystick(joyhandle, 0, 0, 0) == 0; +} + +void Joystick::getVibration(float &left, float &right) +{ + // Deprecated. + left = 0.0f; + right = 0.0f; +} + +bool Joystick::hasSensor(Sensor::SensorType type) const +{ +#if defined(LOVE_ENABLE_SENSOR) + using SDLSensor = love::sensor::sdl::Sensor; + + if (!isGamepad()) + return false; + + return SDL_GamepadHasSensor(controller, SDLSensor::convert(type)) == SDL_TRUE; +#else + return false; +#endif +} + +bool Joystick::isSensorEnabled(Sensor::SensorType type) const +{ +#if defined(LOVE_ENABLE_SENSOR) + using SDLSensor = love::sensor::sdl::Sensor; + + if (!isGamepad()) + return false; + + return SDL_GamepadSensorEnabled(controller, SDLSensor::convert(type)) == SDL_TRUE; +#else + return false; +#endif +} + +void Joystick::setSensorEnabled(Sensor::SensorType type, bool enabled) +{ +#if defined(LOVE_ENABLE_SENSOR) + using SDLSensor = love::sensor::sdl::Sensor; + + if (!isGamepad()) + throw love::Exception("Sensor is only supported on gamepad"); + + if (SDL_SetGamepadSensorEnabled(controller, SDLSensor::convert(type), enabled ? SDL_TRUE : SDL_FALSE) != 0) + { + const char *name = nullptr; + SDLSensor::getConstant(type, name); + + throw love::Exception("Could not open \"%s\" SDL gamepad sensor (%s)", name, SDL_GetError()); + } +#else + throw love::Exception("Compiled version of LOVE does not support gamepad sensor"); +#endif +} + +std::vector Joystick::getSensorData(Sensor::SensorType type) const +{ +#if defined(LOVE_ENABLE_SENSOR) + using SDLSensor = love::sensor::sdl::Sensor; + + if (!isGamepad()) + throw love::Exception("Sensor is only supported on gamepad"); + + std::vector data(3); + + if (!isSensorEnabled(type)) + { + const char *name = nullptr; + SDLSensor::getConstant(type, name); + + throw love::Exception("\"%s\" gamepad sensor is not enabled", name); + } + + if (SDL_GetGamepadSensorData(controller, SDLSensor::convert(type), data.data(), (int) data.size()) != 0) + { + const char *name = nullptr; + SDLSensor::getConstant(type, name); + + throw love::Exception("Could not get \"%s\" SDL gamepad sensor data (%s)", name, SDL_GetError()); + } + + return data; +#else + throw love::Exception("Compiled version of LOVE does not support gamepad sensor"); +#endif +} + +bool Joystick::getConstant(Uint8 in, Joystick::Hat &out) +{ + return hats.find(in, out); +} + +bool Joystick::getConstant(Joystick::Hat in, Uint8 &out) +{ + return hats.find(in, out); +} + +bool Joystick::getConstant(SDL_GamepadAxis in, Joystick::GamepadAxis &out) +{ + return gpAxes.find(in, out); +} + +bool Joystick::getConstant(Joystick::GamepadAxis in, SDL_GamepadAxis &out) +{ + return gpAxes.find(in, out); +} + +bool Joystick::getConstant(SDL_GamepadButton in, Joystick::GamepadButton &out) +{ + return gpButtons.find(in, out); +} + +bool Joystick::getConstant(Joystick::GamepadButton in, SDL_GamepadButton &out) +{ + return gpButtons.find(in, out); +} + +EnumMap::Entry Joystick::hatEntries[] = +{ + {Joystick::HAT_CENTERED, SDL_HAT_CENTERED}, + {Joystick::HAT_UP, SDL_HAT_UP}, + {Joystick::HAT_RIGHT, SDL_HAT_RIGHT}, + {Joystick::HAT_DOWN, SDL_HAT_DOWN}, + {Joystick::HAT_LEFT, SDL_HAT_LEFT}, + {Joystick::HAT_RIGHTUP, SDL_HAT_RIGHTUP}, + {Joystick::HAT_RIGHTDOWN, SDL_HAT_RIGHTDOWN}, + {Joystick::HAT_LEFTUP, SDL_HAT_LEFTUP}, + {Joystick::HAT_LEFTDOWN, SDL_HAT_LEFTDOWN}, +}; + +EnumMap Joystick::hats(Joystick::hatEntries, sizeof(Joystick::hatEntries)); + +EnumMap::Entry Joystick::gpAxisEntries[] = +{ + {Joystick::GAMEPAD_AXIS_LEFTX, SDL_GAMEPAD_AXIS_LEFTX}, + {Joystick::GAMEPAD_AXIS_LEFTY, SDL_GAMEPAD_AXIS_LEFTY}, + {Joystick::GAMEPAD_AXIS_RIGHTX, SDL_GAMEPAD_AXIS_RIGHTX}, + {Joystick::GAMEPAD_AXIS_RIGHTY, SDL_GAMEPAD_AXIS_RIGHTY}, + {Joystick::GAMEPAD_AXIS_TRIGGERLEFT, SDL_GAMEPAD_AXIS_LEFT_TRIGGER}, + {Joystick::GAMEPAD_AXIS_TRIGGERRIGHT, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER}, +}; + +EnumMap Joystick::gpAxes(Joystick::gpAxisEntries, sizeof(Joystick::gpAxisEntries)); + +EnumMap::Entry Joystick::gpButtonEntries[] = +{ + {Joystick::GAMEPAD_BUTTON_A, SDL_GAMEPAD_BUTTON_SOUTH}, + {Joystick::GAMEPAD_BUTTON_B, SDL_GAMEPAD_BUTTON_EAST}, + {Joystick::GAMEPAD_BUTTON_X, SDL_GAMEPAD_BUTTON_WEST}, + {Joystick::GAMEPAD_BUTTON_Y, SDL_GAMEPAD_BUTTON_NORTH}, + {Joystick::GAMEPAD_BUTTON_BACK, SDL_GAMEPAD_BUTTON_BACK}, + {Joystick::GAMEPAD_BUTTON_GUIDE, SDL_GAMEPAD_BUTTON_GUIDE}, + {Joystick::GAMEPAD_BUTTON_START, SDL_GAMEPAD_BUTTON_START}, + {Joystick::GAMEPAD_BUTTON_LEFTSTICK, SDL_GAMEPAD_BUTTON_LEFT_STICK}, + {Joystick::GAMEPAD_BUTTON_RIGHTSTICK, SDL_GAMEPAD_BUTTON_RIGHT_STICK}, + {Joystick::GAMEPAD_BUTTON_LEFTSHOULDER, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER}, + {Joystick::GAMEPAD_BUTTON_RIGHTSHOULDER, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER}, + {Joystick::GAMEPAD_BUTTON_DPAD_UP, SDL_GAMEPAD_BUTTON_DPAD_UP}, + {Joystick::GAMEPAD_BUTTON_DPAD_DOWN, SDL_GAMEPAD_BUTTON_DPAD_DOWN}, + {Joystick::GAMEPAD_BUTTON_DPAD_LEFT, SDL_GAMEPAD_BUTTON_DPAD_LEFT}, + {Joystick::GAMEPAD_BUTTON_DPAD_RIGHT, SDL_GAMEPAD_BUTTON_DPAD_RIGHT}, + {Joystick::GAMEPAD_BUTTON_MISC1, SDL_GAMEPAD_BUTTON_MISC1}, + {Joystick::GAMEPAD_BUTTON_PADDLE1, SDL_GAMEPAD_BUTTON_LEFT_PADDLE1}, + {Joystick::GAMEPAD_BUTTON_PADDLE2, SDL_GAMEPAD_BUTTON_LEFT_PADDLE2}, + {Joystick::GAMEPAD_BUTTON_PADDLE3, SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1}, + {Joystick::GAMEPAD_BUTTON_PADDLE4, SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2}, + {Joystick::GAMEPAD_BUTTON_TOUCHPAD, SDL_GAMEPAD_BUTTON_TOUCHPAD}, +}; + +EnumMap Joystick::gpButtons(Joystick::gpButtonEntries, sizeof(Joystick::gpButtonEntries)); + +} // sdl +} // joystick +} // love diff --git a/src/modules/joystick/sdl/JoystickSDL3.h b/src/modules/joystick/sdl/JoystickSDL3.h new file mode 100644 index 000000000..62992c39b --- /dev/null +++ b/src/modules/joystick/sdl/JoystickSDL3.h @@ -0,0 +1,141 @@ +/** + * Copyright (c) 2006-2023 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#ifndef LOVE_JOYSTICK_SDL_JOYSTICK_SDL3_H +#define LOVE_JOYSTICK_SDL_JOYSTICK_SDL3_H + +// LOVE +#include "joystick/Joystick.h" +#include "common/EnumMap.h" +#include "common/int.h" + +// SDL +#include + +#if SDL_VERSION_ATLEAST(3, 0, 0) + +namespace love +{ +namespace joystick +{ +namespace sdl +{ + +class Joystick : public love::joystick::Joystick +{ +public: + + Joystick(int id); + + virtual ~Joystick(); + + bool open(int64 deviceid) override; + void close() override; + + bool isConnected() const override; + + const char *getName() const override; + + JoystickType getJoystickType() const override; + + int getAxisCount() const override; + int getButtonCount() const override; + int getHatCount() const override; + + float getAxis(int axisindex) const override; + std::vector getAxes() const override; + Hat getHat(int hatindex) const override; + + bool isDown(const std::vector &buttonlist) const override; + + void setPlayerIndex(int index) override; + int getPlayerIndex() const override; + + bool openGamepad(int64 deviceid) override; + bool isGamepad() const override; + + GamepadType getGamepadType() const override; + + float getGamepadAxis(GamepadAxis axis) const override; + bool isGamepadDown(const std::vector &blist) const override; + + JoystickInput getGamepadMapping(const GamepadInput &input) const override; + std::string getGamepadMappingString() const override; + + void *getHandle() const override; + + std::string getGUID() const override; + int getInstanceID() const override; + int getID() const override; + + void getDeviceInfo(int &vendorID, int &productID, int &productVersion) const override; + + bool isVibrationSupported() override; + bool setVibration(float left, float right, float duration = -1.0f) override; + bool setVibration() override; + void getVibration(float &left, float &right) override; + + bool hasSensor(Sensor::SensorType type) const override; + bool isSensorEnabled(Sensor::SensorType type) const override; + void setSensorEnabled(Sensor::SensorType type, bool enabled) override; + std::vector getSensorData(Sensor::SensorType type) const override; + + static bool getConstant(Hat in, Uint8 &out); + static bool getConstant(Uint8 in, Hat &out); + + static bool getConstant(SDL_GamepadAxis in, GamepadAxis &out); + static bool getConstant(GamepadAxis in, SDL_GamepadAxis &out); + + static bool getConstant(SDL_GamepadButton in, GamepadButton &out); + static bool getConstant(GamepadButton in, SDL_GamepadButton &out); + +private: + + Joystick() {} + + SDL_Joystick *joyhandle; + SDL_Gamepad *controller; + + JoystickType joystickType; + + SDL_JoystickID instanceid; + std::string pguid; + int id; + + std::string name; + + static EnumMap::Entry hatEntries[]; + static EnumMap hats; + + static EnumMap::Entry gpAxisEntries[]; + static EnumMap gpAxes; + + static EnumMap::Entry gpButtonEntries[]; + static EnumMap gpButtons; + +}; + +} // sdl +} // joystick +} // love + +#endif // SDL_VERSION_ATLEAST(3, 0, 0) + +#endif // LOVE_JOYSTICK_SDL_JOYSTICK_SDL3_H diff --git a/src/modules/keyboard/sdl/Keyboard.cpp b/src/modules/keyboard/sdl/Keyboard.cpp index fdf4713b1..d7a7977c0 100644 --- a/src/modules/keyboard/sdl/Keyboard.cpp +++ b/src/modules/keyboard/sdl/Keyboard.cpp @@ -91,6 +91,16 @@ bool Keyboard::isModifierActive(ModifierKey key) const switch (key) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + case MODKEY_NUMLOCK: + return (modstate & SDL_KMOD_NUM) != 0; + case MODKEY_CAPSLOCK: + return (modstate & SDL_KMOD_CAPS) != 0; + case MODKEY_SCROLLLOCK: + return (modstate & SDL_KMOD_SCROLL) != 0; + case MODKEY_MODE: + return (modstate & SDL_KMOD_MODE) != 0; +#else case MODKEY_NUMLOCK: return (modstate & KMOD_NUM) != 0; case MODKEY_CAPSLOCK: @@ -99,6 +109,7 @@ bool Keyboard::isModifierActive(ModifierKey key) const return (modstate & KMOD_SCROLL) != 0; case MODKEY_MODE: return (modstate & KMOD_MODE) != 0; +#endif default: break; } @@ -164,7 +175,11 @@ void Keyboard::setTextInput(bool enable, double x, double y, double w, double h) bool Keyboard::hasTextInput() const { +#if SDL_VERSION_ATLEAST(3, 0, 0) + return SDL_TextInputActive(); +#else return SDL_IsTextInputActive() != SDL_FALSE; +#endif } bool Keyboard::hasScreenKeyboard() const diff --git a/src/modules/mouse/sdl/Cursor.cpp b/src/modules/mouse/sdl/Cursor.cpp index 6e17c00cf..a3c388099 100644 --- a/src/modules/mouse/sdl/Cursor.cpp +++ b/src/modules/mouse/sdl/Cursor.cpp @@ -22,6 +22,8 @@ #include "Cursor.h" #include "common/config.h" +#include + namespace love { namespace mouse @@ -34,6 +36,13 @@ Cursor::Cursor(image::ImageData *data, int hotx, int hoty) , type(CURSORTYPE_IMAGE) , systemType(CURSOR_MAX_ENUM) { + int w = data->getWidth(); + int h = data->getHeight(); + int pitch = w * 4; + +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Surface *surface = SDL_CreateSurfaceFrom(data->getData(), w, h, pitch, SDL_PIXELFORMAT_RGBA8888); +#else Uint32 rmask, gmask, bmask, amask; #ifdef LOVE_BIG_ENDIAN rmask = 0xFF000000; @@ -47,17 +56,19 @@ Cursor::Cursor(image::ImageData *data, int hotx, int hoty) amask = 0xFF000000; #endif - int w = data->getWidth(); - int h = data->getHeight(); - int pitch = w * 4; - SDL_Surface *surface = SDL_CreateRGBSurfaceFrom(data->getData(), w, h, 32, pitch, rmask, gmask, bmask, amask); +#endif if (!surface) throw love::Exception("Cannot create cursor: out of memory!"); cursor = SDL_CreateColorCursor(surface, hotx, hoty); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(surface); +#else SDL_FreeSurface(surface); +#endif + if (!cursor) throw love::Exception("Cannot create cursor: %s", SDL_GetError()); } @@ -80,8 +91,13 @@ Cursor::Cursor(mouse::Cursor::SystemCursor cursortype) Cursor::~Cursor() { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (cursor) + SDL_DestroyCursor(cursor); +#else if (cursor) SDL_FreeCursor(cursor); +#endif } void *Cursor::getHandle() const diff --git a/src/modules/mouse/sdl/Mouse.cpp b/src/modules/mouse/sdl/Mouse.cpp index c2a3e53a4..6bcae186e 100644 --- a/src/modules/mouse/sdl/Mouse.cpp +++ b/src/modules/mouse/sdl/Mouse.cpp @@ -24,6 +24,7 @@ // SDL #include +#include namespace love { @@ -126,8 +127,13 @@ bool Mouse::isCursorSupported() const void Mouse::getPosition(double &x, double &y) const { +#if SDL_VERSION_ATLEAST(3, 0, 0) + float mx, my; + SDL_GetMouseState(&mx, &my); +#else int mx, my; SDL_GetMouseState(&mx, &my); +#endif x = (double) mx; y = (double) my; @@ -161,12 +167,35 @@ void Mouse::setPosition(double x, double y) void Mouse::getGlobalPosition(double &x, double &y, int &displayindex) const { +#if SDL_VERSION_ATLEAST(3, 0, 0) + float globalx, globaly; +#else int globalx, globaly; +#endif SDL_GetGlobalMouseState(&globalx, &globaly); - int mx = globalx; - int my = globaly; + auto mx = globalx; + auto my = globaly; + +#if SDL_VERSION_ATLEAST(3, 0, 0) + int displaycount = 0; + SDL_DisplayID *displays = SDL_GetDisplays(&displaycount); + + for (displayindex = 0; displayindex < displaycount; displayindex++) + { + SDL_Rect r = {}; + SDL_GetDisplayBounds(displays[displayindex], &r); + + SDL_FRect frect = {(float)r.x, (float)r.y, (float)r.w, (float)r.h}; + + mx -= frect.x; + my -= frect.y; + SDL_FPoint p = { globalx, globaly }; + if (SDL_PointInRectFloat(&p, &frect)) + break; + } +#else int displaycount = SDL_GetNumVideoDisplays(); for (displayindex = 0; displayindex < displaycount; displayindex++) @@ -181,6 +210,7 @@ void Mouse::getGlobalPosition(double &x, double &y, int &displayindex) const if (SDL_PointInRect(&p, &rect)) break; } +#endif if (displayindex >= displaycount) displayindex = 0; @@ -191,7 +221,14 @@ void Mouse::getGlobalPosition(double &x, double &y, int &displayindex) const void Mouse::setVisible(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 } bool Mouse::isDown(const std::vector &buttons) const @@ -224,7 +261,11 @@ bool Mouse::isDown(const std::vector &buttons) const bool Mouse::isVisible() const { +#if SDL_VERSION_ATLEAST(3, 0, 0) + return SDL_CursorVisible(); +#else return SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE; +#endif } void Mouse::setGrabbed(bool grab) diff --git a/src/modules/sensor/sdl/Sensor.cpp b/src/modules/sensor/sdl/Sensor.cpp index 3483f0179..caf4a5701 100644 --- a/src/modules/sensor/sdl/Sensor.cpp +++ b/src/modules/sensor/sdl/Sensor.cpp @@ -51,11 +51,25 @@ const char *Sensor::getName() const bool Sensor::hasSensor(SensorType type) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + int count = 0; + SDL_SensorID *sensorIDs = SDL_GetSensors(&count); + for (int i = 0; i < count; i++) + { + if (convert(SDL_GetSensorInstanceType(sensorIDs[i])) == type) + { + SDL_free(sensorIDs); + return true; + } + } + SDL_free(sensorIDs); +#else for (int i = 0; i < SDL_NumSensors(); i++) { if (convert(SDL_SensorGetDeviceType(i)) == type) return true; } +#endif return false; } @@ -69,11 +83,38 @@ void Sensor::setEnabled(SensorType type, bool enable) { if (sensors[type] && !enable) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_CloseSensor(sensors[type]); +#else SDL_SensorClose(sensors[type]); +#endif sensors[type] = nullptr; } else if (sensors[type] == nullptr && enable) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + int count = 0; + SDL_SensorID *sensorIDs = SDL_GetSensors(&count); + for (int i = 0; i < count; i++) + { + if (convert(SDL_GetSensorInstanceType(sensorIDs[i])) == type) + { + SDL_Sensor *sensorHandle = SDL_OpenSensor(sensorIDs[i]); + + if (sensorHandle == nullptr) + { + SDL_free(sensorIDs); + + const char *name = nullptr; + getConstant(type, name); + throw love::Exception("Could not open \"%s\" SDL sensor (%s)", name, SDL_GetError()); + } + + sensors[type] = sensorHandle; + } + } + SDL_free(sensorIDs); +#else for (int i = 0; i < SDL_NumSensors(); i++) { if (convert(SDL_SensorGetDeviceType(i)) == type) @@ -84,13 +125,13 @@ void Sensor::setEnabled(SensorType type, bool enable) { const char *name = nullptr; getConstant(type, name); - throw love::Exception("Could not open \"%s\" SDL sensor (%s)", name, SDL_GetError()); } sensors[type] = sensorHandle; } } +#endif } } @@ -106,7 +147,11 @@ std::vector Sensor::getData(SensorType type) std::vector values(3); +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (SDL_GetSensorData(sensors[type], values.data(), (int) values.size()) != 0) +#else if (SDL_SensorGetData(sensors[type], values.data(), (int) values.size()) != 0) +#endif { const char *name = nullptr; getConstant(type, name); @@ -140,7 +185,11 @@ const char *Sensor::getSensorName(SensorType type) throw love::Exception("\"%s\" sensor is not enabled", name); } +#if SDL_VERSION_ATLEAST(3, 0, 0) + return SDL_GetSensorName(sensors[type]); +#else return SDL_SensorGetName(sensors[type]); +#endif } Sensor::SensorType Sensor::convert(SDL_SensorType type) diff --git a/src/modules/thread/sdl/threads.cpp b/src/modules/thread/sdl/threads.cpp index 64fd5063d..9805a7f83 100644 --- a/src/modules/thread/sdl/threads.cpp +++ b/src/modules/thread/sdl/threads.cpp @@ -50,22 +50,38 @@ void Mutex::unlock() Conditional::Conditional() { +#if SDL_VERSION_ATLEAST(3, 0, 0) + cond = SDL_CreateCondition(); +#else cond = SDL_CreateCond(); +#endif } Conditional::~Conditional() { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroyCondition(cond); +#else SDL_DestroyCond(cond); +#endif } void Conditional::signal() { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_SignalCondition(cond); +#else SDL_CondSignal(cond); +#endif } void Conditional::broadcast() { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_BroadcastCondition(cond); +#else SDL_CondBroadcast(cond); +#endif } bool Conditional::wait(thread::Mutex *_mutex, int timeout) @@ -74,10 +90,17 @@ bool Conditional::wait(thread::Mutex *_mutex, int timeout) // however, you're asking for it if you're // mixing thread implementations. Mutex *mutex = (Mutex *) _mutex; +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (timeout < 0) + return !SDL_WaitCondition(cond, mutex->mutex); + else + return (SDL_WaitConditionTimeout(cond, mutex->mutex, timeout) == 0); +#else if (timeout < 0) return !SDL_CondWait(cond, mutex->mutex); else return (SDL_CondWaitTimeout(cond, mutex->mutex, timeout) == 0); +#endif } } // sdl diff --git a/src/modules/thread/sdl/threads.h b/src/modules/thread/sdl/threads.h index 8c2726566..374585e0e 100644 --- a/src/modules/thread/sdl/threads.h +++ b/src/modules/thread/sdl/threads.h @@ -25,6 +25,7 @@ #include "thread/threads.h" #include +#include namespace love { @@ -47,7 +48,11 @@ class Mutex : public thread::Mutex private: +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Mutex *mutex; +#else SDL_mutex *mutex; +#endif Mutex(const Mutex&/* mutex*/) {} friend class Conditional; @@ -67,7 +72,11 @@ class Conditional : public thread::Conditional private: +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Condition *cond; +#else SDL_cond *cond; +#endif }; // Conditional diff --git a/src/modules/timer/Timer.cpp b/src/modules/timer/Timer.cpp index c1cf900da..09aeb3d24 100644 --- a/src/modules/timer/Timer.cpp +++ b/src/modules/timer/Timer.cpp @@ -84,7 +84,7 @@ double Timer::step() void Timer::sleep(double seconds) const { if (seconds >= 0) - love::sleep((unsigned int)(seconds*1000)); + love::sleep(seconds*1000); } double Timer::getDelta() const diff --git a/src/modules/touch/sdl/Touch.cpp b/src/modules/touch/sdl/Touch.cpp index cd5915233..cd65c0173 100644 --- a/src/modules/touch/sdl/Touch.cpp +++ b/src/modules/touch/sdl/Touch.cpp @@ -23,6 +23,8 @@ #include "common/Exception.h" #include "Touch.h" +#include + // C++ #include @@ -63,11 +65,19 @@ void Touch::onEvent(Uint32 eventtype, const TouchInfo &info) switch (eventtype) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDL_EVENT_FINGER_DOWN: +#else case SDL_FINGERDOWN: +#endif touches.erase(std::remove_if(touches.begin(), touches.end(), compare), touches.end()); touches.push_back(info); break; +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDL_EVENT_FINGER_MOTION: +#else case SDL_FINGERMOTION: +#endif { for (TouchInfo &touch : touches) { @@ -76,7 +86,11 @@ void Touch::onEvent(Uint32 eventtype, const TouchInfo &info) } break; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + case SDL_EVENT_FINGER_UP: +#else case SDL_FINGERUP: +#endif touches.erase(std::remove_if(touches.begin(), touches.end(), compare), touches.end()); break; default: diff --git a/src/modules/window/sdl/Window.cpp b/src/modules/window/sdl/Window.cpp index fa8be97d0..dc43c2d65 100644 --- a/src/modules/window/sdl/Window.cpp +++ b/src/modules/window/sdl/Window.cpp @@ -44,7 +44,9 @@ #include // SDL +#if !SDL_VERSION_ATLEAST(3, 0, 0) #include +#endif #ifdef LOVE_GRAPHICS_VULKAN #include @@ -333,11 +335,19 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla if (window) { SDL_DestroyWindow(window); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_FlushEvents(SDL_EVENT_WINDOW_FIRST, SDL_EVENT_WINDOW_LAST); +#else SDL_FlushEvent(SDL_WINDOWEVENT); +#endif window = nullptr; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + window = SDL_CreateWindow(title.c_str(), w, h, windowflags); +#else window = SDL_CreateWindow(title.c_str(), x, y, w, h, windowflags); +#endif if (!window) { @@ -345,6 +355,10 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla return false; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_SetWindowPosition(window, x, y); +#endif + if (attribs != nullptr && renderer == love::graphics::Renderer::RENDERER_OPENGL) { #ifdef LOVE_MACOS @@ -463,6 +477,33 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla return true; } +#if SDL_VERSION_ATLEAST(3, 0, 0) +struct SDLDisplayIDs +{ + SDLDisplayIDs() + { + ids = SDL_GetDisplays(&count); + } + + ~SDLDisplayIDs() + { + if (ids) + SDL_free(ids); + } + + int count = 0; + SDL_DisplayID *ids = nullptr; +}; + +static SDL_DisplayID GetSDLDisplayIDForIndex(int displayindex) +{ + SDLDisplayIDs displayids; + if (displayindex < 0 || displayindex >= displayids.count) + return (SDL_DisplayID) 0; + return displayids.ids[displayindex]; +} +#endif + bool Window::setWindow(int width, int height, WindowSettings *settings) { if (!graphics.get()) @@ -484,15 +525,28 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) f.minwidth = std::max(f.minwidth, 1); f.minheight = std::max(f.minheight, 1); - f.displayindex = std::min(std::max(f.displayindex, 0), getDisplayCount() - 1); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDLDisplayIDs displays; + int displaycount = displays.count; +#else + int displaycount = getDisplayCount(); +#endif + + f.displayindex = std::min(std::max(f.displayindex, 0), displaycount - 1); // Use the desktop resolution if a width or height of 0 is specified. if (width == 0 || height == 0) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_DisplayMode *mode = SDL_GetDesktopDisplayMode(displays.ids[f.displayindex]); + width = mode->w; + height = mode->h; +#else SDL_DisplayMode mode = {}; SDL_GetDesktopDisplayMode(f.displayindex, &mode); width = mode.w; height = mode.h; +#endif } // On Android, disable fullscreen first on window creation so it's @@ -527,43 +581,72 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) x = y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(f.displayindex); } - SDL_DisplayMode fsmode = {0, width, height, 0, nullptr}; + Uint32 sdlflags = 0; +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_DisplayMode *fsmode = nullptr; +#endif - if (f.fullscreen && f.fstype == FULLSCREEN_EXCLUSIVE) + if (f.fullscreen) { - // Fullscreen window creation will bug out if no mode can be used. - if (SDL_GetClosestDisplayMode(f.displayindex, &fsmode, &fsmode) == nullptr) +#if SDL_VERSION_ATLEAST(3, 0, 0) + sdlflags |= SDL_WINDOW_FULLSCREEN; + + if (f.fstype == FULLSCREEN_EXCLUSIVE) { - // GetClosestDisplayMode will fail if we request a size larger - // than the largest available display mode, so we'll try to use - // the largest (first) mode in that case. - if (SDL_GetDisplayMode(f.displayindex, 0, &fsmode) < 0) - return false; + SDL_DisplayID display = displays.ids[f.displayindex]; + fsmode = SDL_GetClosestFullscreenDisplayMode(display, width, height, 0, isHighDPIAllowed()); + if (fsmode == nullptr) + { + // GetClosestDisplayMode will fail if we request a size larger + // than the largest available display mode, so we'll try to use + // the largest (first) mode in that case. + int modecount = 0; + const SDL_DisplayMode **modes = SDL_GetFullscreenDisplayModes(display, &modecount); + fsmode = modecount > 0 ? modes[0] : nullptr; + SDL_free(modes); + if (fsmode == nullptr) + return false; + } } - } - - bool needsetmode = false; +#else + if (f.fstype == FULLSCREEN_EXCLUSIVE) + { + SDL_DisplayMode fsmode = {0, width, height, 0, nullptr}; - Uint32 sdlflags = 0; + // Fullscreen window creation will bug out if no mode can be used. + if (SDL_GetClosestDisplayMode(f.displayindex, &fsmode, &fsmode) == nullptr) + { + // GetClosestDisplayMode will fail if we request a size larger + // than the largest available display mode, so we'll try to use + // the largest (first) mode in that case. + if (SDL_GetDisplayMode(f.displayindex, 0, &fsmode) < 0) + return false; + } - if (f.fullscreen) - { - if (f.fstype == FULLSCREEN_DESKTOP) - sdlflags |= SDL_WINDOW_FULLSCREEN_DESKTOP; - else - { sdlflags |= SDL_WINDOW_FULLSCREEN; width = fsmode.w; height = fsmode.h; } + else + { + sdlflags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + } +#endif } + bool needsetmode = false; + if (renderer != windowRenderer && isOpen()) close(); if (isOpen()) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_SetWindowFullscreenMode(window, fsmode); + if (SDL_SetWindowFullscreen(window, (sdlflags & SDL_WINDOW_FULLSCREEN) != 0) == 0 && renderer == graphics::RENDERER_OPENGL) +#else if (SDL_SetWindowFullscreen(window, sdlflags) == 0 && renderer == graphics::RENDERER_OPENGL) +#endif SDL_GL_MakeCurrent(window, glcontext); if (!f.fullscreen) @@ -594,11 +677,29 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) sdlflags |= SDL_WINDOW_BORDERLESS; // Note: this flag is ignored on Windows. +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (isHighDPIAllowed()) + sdlflags |= SDL_WINDOW_HIGH_PIXEL_DENSITY; +#else if (isHighDPIAllowed()) sdlflags |= SDL_WINDOW_ALLOW_HIGHDPI; +#endif + +#if SDL_VERSION_ATLEAST(3, 0, 0) + Uint32 createflags = sdlflags & (~SDL_WINDOW_FULLSCREEN); + + if (!createWindowAndContext(x, y, width, height, createflags, renderer)) + return false; + if (f.fullscreen) + { + SDL_SetWindowFullscreenMode(window, fsmode); + SDL_SetWindowFullscreen(window, SDL_TRUE); + } +#else if (!createWindowAndContext(x, y, width, height, sdlflags, renderer)) return false; +#endif needsetmode = true; } @@ -662,9 +763,16 @@ bool Window::onSizeChanged(int width, int height) if (!window) return false; +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_GetWindowSize(window, &windowWidth, &windowHeight); +#else windowWidth = width; windowHeight = height; +#endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (SDL_GetWindowSizeInPixels(window, &pixelWidth, &pixelHeight) < 0) +#else // TODO: Use SDL_GetWindowSizeInPixels here when supported. if (glcontext != nullptr) SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight); @@ -677,6 +785,7 @@ bool Window::onSizeChanged(int width, int height) SDL_Vulkan_GetDrawableSize(window, &pixelWidth, &pixelHeight); #endif else +#endif { pixelWidth = width; pixelHeight = height; @@ -702,6 +811,9 @@ void Window::updateSettings(const WindowSettings &newsettings, bool updateGraphi pixelWidth = windowWidth; pixelHeight = windowHeight; +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_GetWindowSizeInPixels(window, &pixelWidth, &pixelHeight); +#else // TODO: Use SDL_GetWindowSizeInPixels here when supported. if ((wflags & SDL_WINDOW_OPENGL) != 0) SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight); @@ -713,8 +825,13 @@ void Window::updateSettings(const WindowSettings &newsettings, bool updateGraphi else if ((wflags & SDL_WINDOW_VULKAN) != 0) SDL_Vulkan_GetDrawableSize(window, &pixelWidth, &pixelHeight); #endif +#endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (((wflags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN) && SDL_GetWindowFullscreenMode(window) == nullptr) +#else if ((wflags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) +#endif { settings.fullscreen = true; settings.fstype = FULLSCREEN_DESKTOP; @@ -744,7 +861,11 @@ void Window::updateSettings(const WindowSettings &newsettings, bool updateGraphi getPosition(settings.x, settings.y, settings.displayindex); +#if SDL_VERSION_ATLEAST(3, 0, 0) + setHighDPIAllowed((wflags & SDL_WINDOW_HIGH_PIXEL_DENSITY) != 0); +#else setHighDPIAllowed((wflags & SDL_WINDOW_ALLOW_HIGHDPI) != 0); +#endif settings.usedpiscale = newsettings.usedpiscale; @@ -759,11 +880,19 @@ void Window::updateSettings(const WindowSettings &newsettings, bool updateGraphi settings.stencil = newsettings.stencil; settings.depth = newsettings.depth; +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDLDisplayIDs displayids; + const SDL_DisplayMode *dmode = SDL_GetCurrentDisplayMode(displayids.ids[settings.displayindex]); + + // May be 0 if the refresh rate can't be determined. + settings.refreshrate = dmode->refresh_rate; +#else SDL_DisplayMode dmode = {}; SDL_GetCurrentDisplayMode(settings.displayindex, &dmode); // May be 0 if the refresh rate can't be determined. settings.refreshrate = (double) dmode.refresh_rate; +#endif // Update the viewport size now instead of waiting for event polling. if (updateGraphicsViewport && graphics.get()) @@ -821,7 +950,11 @@ void Window::close(bool allowExceptions) // The old window may have generated pending events which are no longer // relevant. Destroy them all! +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_FlushEvents(SDL_EVENT_WINDOW_FIRST, SDL_EVENT_WINDOW_LAST); +#else SDL_FlushEvent(SDL_WINDOWEVENT); +#endif } open = false; @@ -839,6 +972,21 @@ bool Window::setFullscreen(bool fullscreen, FullscreenType fstype) newsettings.fullscreen = fullscreen; newsettings.fstype = fstype; +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_bool sdlflags = fullscreen; + if (fullscreen) + { + if (fstype == FULLSCREEN_DESKTOP) + SDL_SetWindowFullscreenMode(window, nullptr); + else + { + SDL_DisplayID displayid = SDL_GetDisplayForWindow(window); + const SDL_DisplayMode *mode = SDL_GetClosestFullscreenDisplayMode(displayid, windowWidth, windowHeight, 0, isHighDPIAllowed()); + if (mode != nullptr) + SDL_SetWindowFullscreenMode(window, mode); + } + } +#else Uint32 sdlflags = 0; if (fullscreen) @@ -857,6 +1005,7 @@ bool Window::setFullscreen(bool fullscreen, FullscreenType fstype) SDL_SetWindowDisplayMode(window, &mode); } } +#endif #ifdef LOVE_ANDROID love::android::setImmersive(fullscreen); @@ -881,12 +1030,21 @@ bool Window::setFullscreen(bool fullscreen) int Window::getDisplayCount() const { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDLDisplayIDs displayids; + return displayids.count; +#else return SDL_GetNumVideoDisplays(); +#endif } const char *Window::getDisplayName(int displayindex) const { +#if SDL_VERSION_ATLEAST(3, 0, 0) + const char *name = SDL_GetDisplayName(GetSDLDisplayIDForIndex(displayindex)); +#else const char *name = SDL_GetDisplayName(displayindex); +#endif if (name == nullptr) throw love::Exception("Invalid display index: %d", displayindex + 1); @@ -896,7 +1054,11 @@ const char *Window::getDisplayName(int displayindex) const Window::DisplayOrientation Window::getDisplayOrientation(int displayindex) const { +#if SDL_VERSION_ATLEAST(3, 0, 0) + switch (SDL_GetCurrentDisplayOrientation(GetSDLDisplayIDForIndex(displayindex))) +#else switch (SDL_GetDisplayOrientation(displayindex)) +#endif { case SDL_ORIENTATION_UNKNOWN: return ORIENTATION_UNKNOWN; case SDL_ORIENTATION_LANDSCAPE: return ORIENTATION_LANDSCAPE; @@ -912,12 +1074,22 @@ std::vector Window::getFullscreenSizes(int displayindex) con { std::vector sizes; +#if SDL_VERSION_ATLEAST(3, 0, 0) + int count = 0; + const SDL_DisplayMode **modes = SDL_GetFullscreenDisplayModes(GetSDLDisplayIDForIndex(displayindex), &count); + + for (int i = 0; i < count; i++) + { + // TODO: other mode properties? + WindowSize w = {modes[i]->w, modes[i]->h}; +#else for (int i = 0; i < SDL_GetNumDisplayModes(displayindex); i++) { SDL_DisplayMode mode = {}; SDL_GetDisplayMode(displayindex, i, &mode); WindowSize w = {mode.w, mode.h}; +#endif // SDL2's display mode list has multiple entries for modes of the same // size with different bits per pixel, so we need to filter those out. @@ -925,11 +1097,24 @@ std::vector Window::getFullscreenSizes(int displayindex) con sizes.push_back(w); } +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_free(modes); +#endif + return sizes; } void Window::getDesktopDimensions(int displayindex, int &width, int &height) const { +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_DisplayMode *mode = SDL_GetDesktopDisplayMode(GetSDLDisplayIDForIndex(displayindex)); + if (mode != nullptr) + { + // TODO: other properties? + width = mode->w; + height = mode->h; + } +#else if (displayindex >= 0 && displayindex < getDisplayCount()) { SDL_DisplayMode mode = {}; @@ -937,6 +1122,7 @@ void Window::getDesktopDimensions(int displayindex, int &width, int &height) con width = mode.w; height = mode.h; } +#endif else { width = 0; @@ -972,7 +1158,21 @@ void Window::getPosition(int &x, int &y, int &displayindex) return; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DisplayID displayid = SDL_GetDisplayForWindow(window); + SDLDisplayIDs displayids; + displayindex = 0; + for (int i = 0; i < displayids.count; i++) + { + if (displayids.ids[i] == displayid) + { + displayindex = i; + break; + } + } +#else displayindex = std::max(SDL_GetWindowDisplayIndex(window), 0); +#endif SDL_GetWindowPosition(window, &x, &y); @@ -982,7 +1182,11 @@ void Window::getPosition(int &x, int &y, int &displayindex) if (x != 0 || y != 0) { SDL_Rect displaybounds = {}; +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_GetDisplayBounds(displayid, &displaybounds); +#else SDL_GetDisplayBounds(displayindex, &displaybounds); +#endif x -= displaybounds.x; y -= displaybounds.y; @@ -1047,6 +1251,7 @@ bool Window::setIcon(love::image::ImageData *imgd) if (!window) return false; +#if !SDL_VERSION_ATLEAST(3, 0, 0) Uint32 rmask, gmask, bmask, amask; #ifdef LOVE_BIG_ENDIAN rmask = 0xFF000000; @@ -1058,6 +1263,7 @@ bool Window::setIcon(love::image::ImageData *imgd) gmask = 0x0000FF00; bmask = 0x00FF0000; amask = 0xFF000000; +#endif #endif int w = imgd->getWidth(); @@ -1070,14 +1276,22 @@ bool Window::setIcon(love::image::ImageData *imgd) { // We don't want another thread modifying the ImageData mid-copy. love::thread::Lock lock(imgd->getMutex()); +#if SDL_VERSION_ATLEAST(3, 0, 0) + sdlicon = SDL_CreateSurfaceFrom(imgd->getData(), w, h, pitch, SDL_PIXELFORMAT_RGBA8888); +#else sdlicon = SDL_CreateRGBSurfaceFrom(imgd->getData(), w, h, bytesperpixel * 8, pitch, rmask, gmask, bmask, amask); +#endif } if (!sdlicon) return false; SDL_SetWindowIcon(window, sdlicon); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DestroySurface(sdlicon); +#else SDL_FreeSurface(sdlicon); +#endif return true; } @@ -1095,8 +1309,18 @@ void Window::setVSync(int vsync) // Check if adaptive vsync was requested but not supported, and fall // back to regular vsync if so. +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (vsync == -1) + { + int actualvsync = 0; + SDL_GL_GetSwapInterval(&actualvsync); + if (actualvsync != -1) + SDL_GL_SetSwapInterval(1); + } +#else if (vsync == -1 && SDL_GL_GetSwapInterval() != -1) SDL_GL_SetSwapInterval(1); +#endif } #ifdef LOVE_GRAPHICS_VULKAN @@ -1119,7 +1343,15 @@ void Window::setVSync(int vsync) int Window::getVSync() const { if (glcontext != nullptr) + { +#if SDL_VERSION_ATLEAST(3, 0, 0) + int interval = 0; + SDL_GL_GetSwapInterval(&interval); + return interval; +#else return SDL_GL_GetSwapInterval(); +#endif + } #if defined(LOVE_GRAPHICS_METAL) if (metalView != nullptr) @@ -1154,7 +1386,11 @@ void Window::setDisplaySleepEnabled(bool enable) bool Window::isDisplaySleepEnabled() const { +#if SDL_VERSION_ATLEAST(3, 0, 0) + return SDL_ScreenSaverEnabled() != SDL_FALSE; +#else return SDL_IsScreenSaverEnabled() != SDL_FALSE; +#endif } void Window::minimize() @@ -1270,7 +1506,11 @@ bool Window::hasMouseFocus() const bool Window::isVisible() const { +#if SDL_VERSION_ATLEAST(3, 0, 0) + return window && (SDL_GetWindowFlags(window) & SDL_WINDOW_HIDDEN) == 0; +#else return window && (SDL_GetWindowFlags(window) & SDL_WINDOW_SHOWN) != 0; +#endif } void Window::setMouseGrab(bool grab)