From 6813ed3185ca1f8778802243054a5bd987453d00 Mon Sep 17 00:00:00 2001 From: damusss Date: Sun, 11 May 2025 19:36:19 +0200 Subject: [PATCH 1/8] Start compiling _sdl2 with SDL3 --- src_c/_sdl2/controller.c | 57 +++++++++++++++++++++++++----- src_c/_sdl2/meson.build | 13 ++++++- src_c/cython/pygame/_sdl2/sdl2.pxd | 12 ++++++- src_c/meson.build | 3 -- src_py/_sdl2/__init__.py | 10 ++++-- 5 files changed, 80 insertions(+), 15 deletions(-) diff --git a/src_c/_sdl2/controller.c b/src_c/_sdl2/controller.c index 648af64a09..9db0cfa29d 100644 --- a/src_c/_sdl2/controller.c +++ b/src_c/_sdl2/controller.c @@ -26,11 +26,19 @@ static PyObject * controller_module_init(PyObject *module, PyObject *_null) { if (!SDL_WasInit(SDL_INIT_GAMECONTROLLER)) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == SDL_FALSE) { +#else if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER)) { +#endif return RAISE(pgExc_SDLError, SDL_GetError()); } } +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_SetGamepadEventsEnabled(SDL_TRUE); +#else SDL_GameControllerEventState(SDL_ENABLE); +#endif Py_RETURN_NONE; } @@ -87,7 +95,16 @@ controller_module_get_count(PyObject *module, PyObject *_null) { CONTROLLER_INIT_CHECK(); +#if SDL_VERSION_ATLEAST(3, 0, 0) + int count; + SDL_JoystickID *joysticks = SDL_GetJoysticks(&count); + if (!joysticks) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } + SDL_free(joysticks); +#else int count = SDL_NumJoysticks(); +#endif if (count < 0) { return RAISE(pgExc_SDLError, SDL_GetError()); } @@ -290,7 +307,11 @@ controller_set_mapping(pgControllerObject *self, PyObject *args, char guid_str[64]; SDL_Joystick *joy = SDL_GameControllerGetJoystick(self->controller); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_GUIDToString(SDL_JoystickGetGUID(joy), guid_str, 63); +#else SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joy), guid_str, 63); +#endif PyObject *key, *value; const char *key_str, *value_str; @@ -371,10 +392,17 @@ controller_rumble(pgControllerObject *self, PyObject *args, PyObject *kwargs) low_freq = MAX(MIN(low_freq, 1.0f), 0.0f) * 65535; high_freq = MAX(MIN(high_freq, 1.0f), 0.0f) * 65535; +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_bool success = SDL_GameControllerRumble( + self->controller, (Uint16)low_freq, (Uint16)high_freq, duration); + + return PyBool_FromLong(success == SDL_TRUE); +#else int success = SDL_GameControllerRumble(self->controller, (Uint16)low_freq, (Uint16)high_freq, duration); - return PyBool_FromLong(success == 0); + +#endif } static PyObject * @@ -384,7 +412,9 @@ controller_stop_rumble(pgControllerObject *self, PyObject *_null) if (!self->controller) { return RAISE(pgExc_SDLError, "Controller is not initialized"); } - SDL_GameControllerRumble(self->controller, 0, 0, 1); + if (SDL_GameControllerRumble(self->controller, 0, 0, 1) == SDL_FALSE) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } Py_RETURN_NONE; } @@ -437,7 +467,18 @@ controller_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) CONTROLLER_INIT_CHECK(); - if (id >= SDL_NumJoysticks() || !SDL_IsGameController(id)) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + int joycount; + SDL_JoystickID *joysticks = SDL_GetJoysticks(&joycount); + if (!joysticks) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } + SDL_free(joysticks); +#else + int joycount = SDL_NumJoysticks(); +#endif + + if (id >= joycount || !SDL_IsGameController(id)) { return RAISE(pgExc_SDLError, "Invalid index"); } @@ -557,11 +598,11 @@ MODINIT_DEFINE(controller) .m_doc = DOC_SDL2_CONTROLLER, .m_size = -1, .m_methods = _controller_module_methods, -/* -#if PY_VERSION_HEX >= 0x030D0000 - .m_slots = mod_controller_slots, -#endif -*/ + /* + #if PY_VERSION_HEX >= 0x030D0000 + .m_slots = mod_controller_slots, + #endif + */ }; import_pygame_base(); diff --git a/src_c/_sdl2/meson.build b/src_c/_sdl2/meson.build index 2ad443e993..9fbf6e6a8b 100644 --- a/src_c/_sdl2/meson.build +++ b/src_c/_sdl2/meson.build @@ -1,5 +1,10 @@ cython_base = '../cython/pygame/_sdl2' +sdl_cargs = [] +if sdl_api == 3 +sdl_cargs += ['-DUSE_SDL3'] +endif +if sdl_api != 3 _sdl2_audio = py.extension_module( 'audio', fs.is_file('audio.c') ? 'audio.c' : cython_base / 'audio.pyx', @@ -8,7 +13,9 @@ _sdl2_audio = py.extension_module( install: true, subdir: pg / '_sdl2', ) +endif +if sdl_api != 3 _sdl2_video = py.extension_module( 'video', fs.is_file('video.c') ? 'video.c' : cython_base / 'video.pyx', @@ -37,6 +44,7 @@ if sdl_mixer_dep.found() subdir: pg / '_sdl2', ) endif +endif _sdl2_sdl2 = py.extension_module( 'sdl2', @@ -45,9 +53,11 @@ _sdl2_sdl2 = py.extension_module( include_directories: '..', install: true, subdir: pg / '_sdl2', + c_args: sdl_cargs, ) -# This is not a cython file +# These are not a cython files +if sdl_api != 3 _sdl2_touch = py.extension_module( 'touch', 'touch.c', @@ -56,6 +66,7 @@ _sdl2_touch = py.extension_module( install: true, subdir: pg / '_sdl2', ) +endif _sdl2_controller = py.extension_module( 'controller', diff --git a/src_c/cython/pygame/_sdl2/sdl2.pxd b/src_c/cython/pygame/_sdl2/sdl2.pxd index a7cca25180..674a14f325 100644 --- a/src_c/cython/pygame/_sdl2/sdl2.pxd +++ b/src_c/cython/pygame/_sdl2/sdl2.pxd @@ -4,7 +4,17 @@ from libc.string cimport memset from libc.stdio cimport * -cdef extern from "SDL.h" nogil: +cdef extern from * nogil: + """ + #ifdef USE_SDL3 + #include + #define SDL_INIT_TIMER 0 + #define SDL_INIT_EVERYTHING 0 + #define SDL_INIT_NOPARACHUTE 0 + #else + #include + #endif + """ # SDL_stdinc.h provides the real ones based on platform. ctypedef char Sint8 ctypedef unsigned char Uint8 diff --git a/src_c/meson.build b/src_c/meson.build index 0c1479a812..377fdf517a 100644 --- a/src_c/meson.build +++ b/src_c/meson.build @@ -327,10 +327,7 @@ gfxdraw = py.extension_module( endif # pygame._sdl2 -# TODO: support SDL3 -if sdl_api != 3 subdir('_sdl2') -endif # pygame._camera pg_camera_sources = ['_camera.c'] diff --git a/src_py/_sdl2/__init__.py b/src_py/_sdl2/__init__.py index 6ad3b1b047..da67676fa3 100644 --- a/src_py/_sdl2/__init__.py +++ b/src_py/_sdl2/__init__.py @@ -1,4 +1,10 @@ +from pygame.version import SDL + +print(SDL) if __import__("sys").platform not in ("wasi", "emscripten"): - from .audio import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] + if SDL < (3, 0, 0): + from .audio import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] from .sdl2 import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] - from .video import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] + + if SDL < (3, 0, 0): + from .video import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] From 9f7700ba71b517e548f5bb88ca540054e22e7595 Mon Sep 17 00:00:00 2001 From: damusss Date: Sun, 18 May 2025 11:04:34 +0200 Subject: [PATCH 2/8] Compile touch with SDL3 --- src_c/_sdl2/meson.build | 2 -- src_c/_sdl2/touch.c | 75 +++++++++++++++++++++++++++++++++++------ 2 files changed, 64 insertions(+), 13 deletions(-) diff --git a/src_c/_sdl2/meson.build b/src_c/_sdl2/meson.build index 9fbf6e6a8b..5c5e309817 100644 --- a/src_c/_sdl2/meson.build +++ b/src_c/_sdl2/meson.build @@ -57,7 +57,6 @@ _sdl2_sdl2 = py.extension_module( ) # These are not a cython files -if sdl_api != 3 _sdl2_touch = py.extension_module( 'touch', 'touch.c', @@ -66,7 +65,6 @@ _sdl2_touch = py.extension_module( install: true, subdir: pg / '_sdl2', ) -endif _sdl2_controller = py.extension_module( 'controller', diff --git a/src_c/_sdl2/touch.c b/src_c/_sdl2/touch.c index 1c7bd83b57..c432d0555f 100644 --- a/src_c/_sdl2/touch.c +++ b/src_c/_sdl2/touch.c @@ -25,20 +25,48 @@ static PyObject * pg_touch_num_devices(PyObject *self, PyObject *_null) { - return PyLong_FromLong(SDL_GetNumTouchDevices()); + int count; +#if SDL_VERSION_ATLEAST(3, 0, 0) + + SDL_TouchID *devices = SDL_GetTouchDevices(&count); + if (devices == NULL) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } + SDL_free(devices); +#else + count = SDL_GetNumTouchDevices(); +#endif + return PyLong_FromLong(count); } static PyObject * -pg_touch_get_device(PyObject *self, PyObject *index) +pg_touch_get_device(PyObject *self, PyObject *index_obj) { SDL_TouchID touchid; - if (!PyLong_Check(index)) { + if (!PyLong_Check(index_obj)) { return RAISE(PyExc_TypeError, "index must be an integer " "specifying a device to get the ID for"); } - - touchid = SDL_GetTouchDevice(PyLong_AsLong(index)); + int index = PyLong_AsLong(index_obj); + if (PyErr_Occurred()) { + return NULL; // exception already set + } +#if SDL_VERSION_ATLEAST(3, 0, 0) + int count; + SDL_TouchID *devices = SDL_GetTouchDevices(&count); + if (devices == NULL) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } + if (index < 0 || index >= count) { + SDL_free(devices); + return RAISE(PyExc_IndexError, "index is out of bounds"); + } + touchid = devices[index]; + SDL_free(devices); +#else + touchid = SDL_GetTouchDevice(index); +#endif if (touchid == 0) { /* invalid index */ return RAISE(pgExc_SDLError, SDL_GetError()); @@ -47,21 +75,32 @@ pg_touch_get_device(PyObject *self, PyObject *index) } static PyObject * -pg_touch_num_fingers(PyObject *self, PyObject *device_id) +pg_touch_num_fingers(PyObject *self, PyObject *device_id_obj) { int fingercount; - if (!PyLong_Check(device_id)) { + if (!PyLong_Check(device_id_obj)) { return RAISE(PyExc_TypeError, "device_id must be an integer " "specifying a touch device"); } + int device_id = PyLong_AsLongLong(device_id_obj); + if (PyErr_Occurred()) { + return NULL; // exception already set + } VIDEO_INIT_CHECK(); - - fingercount = SDL_GetNumTouchFingers(PyLong_AsLongLong(device_id)); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Finger **fingers = SDL_GetTouchFingers(device_id, &fingercount); + if (fingers == NULL) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } + SDL_free(fingers); +#else + fingercount = SDL_GetNumTouchFingers(device_id); if (fingercount == 0) { return RAISE(pgExc_SDLError, SDL_GetError()); } +#endif return PyLong_FromLong(fingercount); } #if !defined(BUILD_STATIC) @@ -95,14 +134,28 @@ pg_touch_get_finger(PyObject *self, PyObject *args, PyObject *kwargs) } VIDEO_INIT_CHECK(); - +#if SDL_VERSION_ATLEAST(3, 0, 0) + int fingercount; + SDL_Finger **fingers = SDL_GetTouchFingers(touchid, &fingercount); + if (fingers == NULL) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } + if (index < 0 || index >= fingercount) { + SDL_free(fingers); + return RAISE(PyExc_IndexError, "index is out of bounds"); + } + finger = fingers[index]; + SDL_free(fingers); +#else if (!(finger = SDL_GetTouchFinger(touchid, index))) { Py_RETURN_NONE; } +#endif fingerobj = PyDict_New(); - if (!fingerobj) + if (!fingerobj) { return NULL; + } _pg_insobj(fingerobj, "id", PyLong_FromLongLong(finger->id)); _pg_insobj(fingerobj, "x", PyFloat_FromDouble(finger->x)); From a3e525045d5a94576fd36b6e7f813d4a012712d2 Mon Sep 17 00:00:00 2001 From: damusss Date: Sun, 18 May 2025 11:17:38 +0200 Subject: [PATCH 3/8] Start support for audio, to do in the next PR --- src_c/_sdl2/meson.build | 1 + src_c/cython/pygame/_sdl2/audio.pxd | 9 ++++++++- src_py/_sdl2/__init__.py | 1 - 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src_c/_sdl2/meson.build b/src_c/_sdl2/meson.build index 5c5e309817..c3faa06da8 100644 --- a/src_c/_sdl2/meson.build +++ b/src_c/_sdl2/meson.build @@ -12,6 +12,7 @@ _sdl2_audio = py.extension_module( include_directories: '..', install: true, subdir: pg / '_sdl2', + c_args: sdl_cargs, ) endif diff --git a/src_c/cython/pygame/_sdl2/audio.pxd b/src_c/cython/pygame/_sdl2/audio.pxd index 6985775428..640e90cc17 100644 --- a/src_c/cython/pygame/_sdl2/audio.pxd +++ b/src_c/cython/pygame/_sdl2/audio.pxd @@ -3,7 +3,14 @@ from .sdl2 cimport * -cdef extern from "SDL.h" nogil: +cdef extern from * nogil: + """ + #ifdef USE_SDL3 + #include + #else + #include + #endif + """ # https://wiki.libsdl.org/SDL_OpenAudioDevice # https://wiki.libsdl.org/SDL_CloseAudioDevice # https://wiki.libsdl.org/SDL_AudioSpec diff --git a/src_py/_sdl2/__init__.py b/src_py/_sdl2/__init__.py index da67676fa3..c29a5342a6 100644 --- a/src_py/_sdl2/__init__.py +++ b/src_py/_sdl2/__init__.py @@ -1,6 +1,5 @@ from pygame.version import SDL -print(SDL) if __import__("sys").platform not in ("wasi", "emscripten"): if SDL < (3, 0, 0): from .audio import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] From 382a881f662acaa62f67cc1b8eeffacabae88445 Mon Sep 17 00:00:00 2001 From: damusss Date: Sun, 18 May 2025 11:22:43 +0200 Subject: [PATCH 4/8] Correct comments --- src_c/_sdl2/controller.c | 10 +++++----- src_c/_sdl2/meson.build | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src_c/_sdl2/controller.c b/src_c/_sdl2/controller.c index 9db0cfa29d..740a0a80fd 100644 --- a/src_c/_sdl2/controller.c +++ b/src_c/_sdl2/controller.c @@ -598,11 +598,11 @@ MODINIT_DEFINE(controller) .m_doc = DOC_SDL2_CONTROLLER, .m_size = -1, .m_methods = _controller_module_methods, - /* - #if PY_VERSION_HEX >= 0x030D0000 - .m_slots = mod_controller_slots, - #endif - */ +/* +#if PY_VERSION_HEX >= 0x030D0000 + .m_slots = mod_controller_slots, +#endif +*/ }; import_pygame_base(); diff --git a/src_c/_sdl2/meson.build b/src_c/_sdl2/meson.build index c3faa06da8..9d028943be 100644 --- a/src_c/_sdl2/meson.build +++ b/src_c/_sdl2/meson.build @@ -57,7 +57,7 @@ _sdl2_sdl2 = py.extension_module( c_args: sdl_cargs, ) -# These are not a cython files +# These are not cython files _sdl2_touch = py.extension_module( 'touch', 'touch.c', From 6af411a1ecab4361cc6fb13f289db204677ef2fd Mon Sep 17 00:00:00 2001 From: damusss Date: Wed, 21 May 2025 22:12:37 +0200 Subject: [PATCH 5/8] Use PG_SDL3 + style enforcement --- src_c/_sdl2/controller.c | 2 +- src_c/_sdl2/meson.build | 6 ------ src_c/cython/pygame/_sdl2/audio.pxd | 2 +- src_c/cython/pygame/_sdl2/sdl2.pxd | 2 +- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src_c/_sdl2/controller.c b/src_c/_sdl2/controller.c index 740a0a80fd..20d6a08b13 100644 --- a/src_c/_sdl2/controller.c +++ b/src_c/_sdl2/controller.c @@ -27,7 +27,7 @@ controller_module_init(PyObject *module, PyObject *_null) { if (!SDL_WasInit(SDL_INIT_GAMECONTROLLER)) { #if SDL_VERSION_ATLEAST(3, 0, 0) - if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == SDL_FALSE) { + if (!SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER)) { #else if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER)) { #endif diff --git a/src_c/_sdl2/meson.build b/src_c/_sdl2/meson.build index 9d028943be..8d54cc6d28 100644 --- a/src_c/_sdl2/meson.build +++ b/src_c/_sdl2/meson.build @@ -1,8 +1,4 @@ cython_base = '../cython/pygame/_sdl2' -sdl_cargs = [] -if sdl_api == 3 -sdl_cargs += ['-DUSE_SDL3'] -endif if sdl_api != 3 _sdl2_audio = py.extension_module( @@ -12,7 +8,6 @@ _sdl2_audio = py.extension_module( include_directories: '..', install: true, subdir: pg / '_sdl2', - c_args: sdl_cargs, ) endif @@ -54,7 +49,6 @@ _sdl2_sdl2 = py.extension_module( include_directories: '..', install: true, subdir: pg / '_sdl2', - c_args: sdl_cargs, ) # These are not cython files diff --git a/src_c/cython/pygame/_sdl2/audio.pxd b/src_c/cython/pygame/_sdl2/audio.pxd index 640e90cc17..3447dabba5 100644 --- a/src_c/cython/pygame/_sdl2/audio.pxd +++ b/src_c/cython/pygame/_sdl2/audio.pxd @@ -5,7 +5,7 @@ from .sdl2 cimport * cdef extern from * nogil: """ - #ifdef USE_SDL3 + #ifdef PG_SDL3 #include #else #include diff --git a/src_c/cython/pygame/_sdl2/sdl2.pxd b/src_c/cython/pygame/_sdl2/sdl2.pxd index 674a14f325..2281787ba8 100644 --- a/src_c/cython/pygame/_sdl2/sdl2.pxd +++ b/src_c/cython/pygame/_sdl2/sdl2.pxd @@ -6,7 +6,7 @@ from libc.stdio cimport * cdef extern from * nogil: """ - #ifdef USE_SDL3 + #ifdef PG_SDL3 #include #define SDL_INIT_TIMER 0 #define SDL_INIT_EVERYTHING 0 From a75bcb71c10b03ed97b1819d4e139a392f19d8e1 Mon Sep 17 00:00:00 2001 From: damusss Date: Sat, 24 May 2025 08:56:28 +0200 Subject: [PATCH 6/8] Fix other true/false usecases --- src_c/_sdl2/controller.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src_c/_sdl2/controller.c b/src_c/_sdl2/controller.c index 20d6a08b13..cba8927d5e 100644 --- a/src_c/_sdl2/controller.c +++ b/src_c/_sdl2/controller.c @@ -396,7 +396,7 @@ controller_rumble(pgControllerObject *self, PyObject *args, PyObject *kwargs) SDL_bool success = SDL_GameControllerRumble( self->controller, (Uint16)low_freq, (Uint16)high_freq, duration); - return PyBool_FromLong(success == SDL_TRUE); + return PyBool_FromLong(success); #else int success = SDL_GameControllerRumble(self->controller, (Uint16)low_freq, (Uint16)high_freq, duration); @@ -412,7 +412,7 @@ controller_stop_rumble(pgControllerObject *self, PyObject *_null) if (!self->controller) { return RAISE(pgExc_SDLError, "Controller is not initialized"); } - if (SDL_GameControllerRumble(self->controller, 0, 0, 1) == SDL_FALSE) { + if (!SDL_GameControllerRumble(self->controller, 0, 0, 1)) { return RAISE(pgExc_SDLError, SDL_GetError()); } Py_RETURN_NONE; From 43bd25956ebc91493ead28364883c678234d7ece Mon Sep 17 00:00:00 2001 From: Damiano <97639432+damusss@users.noreply.github.com> Date: Sat, 24 May 2025 12:51:32 +0200 Subject: [PATCH 7/8] Discard changes to src_c/cython/pygame/_sdl2/audio.pxd --- src_c/cython/pygame/_sdl2/audio.pxd | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src_c/cython/pygame/_sdl2/audio.pxd b/src_c/cython/pygame/_sdl2/audio.pxd index 3447dabba5..6985775428 100644 --- a/src_c/cython/pygame/_sdl2/audio.pxd +++ b/src_c/cython/pygame/_sdl2/audio.pxd @@ -3,14 +3,7 @@ from .sdl2 cimport * -cdef extern from * nogil: - """ - #ifdef PG_SDL3 - #include - #else - #include - #endif - """ +cdef extern from "SDL.h" nogil: # https://wiki.libsdl.org/SDL_OpenAudioDevice # https://wiki.libsdl.org/SDL_CloseAudioDevice # https://wiki.libsdl.org/SDL_AudioSpec From 8b6960596b737177c7fd723829f0cc07526730cb Mon Sep 17 00:00:00 2001 From: damusss Date: Sat, 24 May 2025 12:51:47 +0200 Subject: [PATCH 8/8] Merge if conditions --- src_c/_sdl2/meson.build | 2 -- 1 file changed, 2 deletions(-) diff --git a/src_c/_sdl2/meson.build b/src_c/_sdl2/meson.build index 8d54cc6d28..5b6ff36893 100644 --- a/src_c/_sdl2/meson.build +++ b/src_c/_sdl2/meson.build @@ -9,9 +9,7 @@ _sdl2_audio = py.extension_module( install: true, subdir: pg / '_sdl2', ) -endif -if sdl_api != 3 _sdl2_video = py.extension_module( 'video', fs.is_file('video.c') ? 'video.c' : cython_base / 'video.pyx',