From 6a5202c8d3aba1ed9550f852654748f8c3dcce17 Mon Sep 17 00:00:00 2001 From: Starbuck5 <46412508+Starbuck5@users.noreply.github.com> Date: Sun, 16 Jun 2024 23:26:44 -0700 Subject: [PATCH 1/3] Fixes for rwobject (now iostream) SDL3 --- src_c/font.c | 8 +++- src_c/imageext.c | 22 +++++++++- src_c/mixer.c | 4 ++ src_c/music.c | 4 ++ src_c/rwobject.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 137 insertions(+), 3 deletions(-) diff --git a/src_c/font.c b/src_c/font.c index 5817e896f6..cae985f1cc 100644 --- a/src_c/font.c +++ b/src_c/font.c @@ -1174,15 +1174,19 @@ font_init(PyFontObject *self, PyObject *args, PyObject *kwds) if (fontsize <= 1) fontsize = 1; - if (rw->size(rw) <= 0) { + if (SDL_RWsize(rw) <= 0) { PyErr_Format(PyExc_ValueError, "Font file object has an invalid file size: %lld", - rw->size(rw)); + SDL_RWsize(rw)); goto error; } Py_BEGIN_ALLOW_THREADS; +#if SDL_VERSION_ATLEAST(3, 0, 0) + font = TTF_OpenFontIO(rw, 1, fontsize); +#else font = TTF_OpenFontRW(rw, 1, fontsize); +#endif Py_END_ALLOW_THREADS; Py_DECREF(obj); diff --git a/src_c/imageext.c b/src_c/imageext.c index 0d68d59070..d2823fcb00 100644 --- a/src_c/imageext.c +++ b/src_c/imageext.c @@ -117,10 +117,18 @@ image_load_ext(PyObject *self, PyObject *arg, PyObject *kwarg) SDL_UnlockMutex(_pg_img_mutex); */ +#if SDL_VERSION_ATLEAST(3, 0, 0) + surf = IMG_LoadTyped_IO(rw, 1, type); +#else surf = IMG_LoadTyped_RW(rw, 1, type); +#endif Py_END_ALLOW_THREADS; -#else /* ~WITH_THREAD */ +#else /* ~WITH_THREAD */ +#if SDL_VERSION_ATLEAST(3, 0, 0) + surf = IMG_LoadTyped_IO(rw, 1, type); +#else surf = IMG_LoadTyped_RW(rw, 1, type); +#endif #endif /* ~WITH_THREAD */ if (ext) { @@ -166,7 +174,11 @@ imageext_load_sized_svg(PyObject *self, PyObject *arg, PyObject *kwargs) } Py_BEGIN_ALLOW_THREADS; +#if SDL_VERSION_ATLEAST(3, 0, 0) + surf = IMG_LoadSizedSVG_IO(rw, width, height); +#else surf = IMG_LoadSizedSVG_RW(rw, width, height); +#endif SDL_RWclose(rw); Py_END_ALLOW_THREADS; if (surf == NULL) { @@ -231,7 +243,11 @@ image_save_ext(PyObject *self, PyObject *arg, PyObject *kwarg) char *ext = iext_find_extension(name); if (!strcasecmp(ext, "jpeg") || !strcasecmp(ext, "jpg")) { if (rw != NULL) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + result = IMG_SaveJPG_IO(surf, rw, 0, JPEG_QUALITY); +#else result = IMG_SaveJPG_RW(surf, rw, 0, JPEG_QUALITY); +#endif } else { result = IMG_SaveJPG(surf, name, JPEG_QUALITY); @@ -240,7 +256,11 @@ image_save_ext(PyObject *self, PyObject *arg, PyObject *kwarg) else if (!strcasecmp(ext, "png")) { /*Py_BEGIN_ALLOW_THREADS; */ if (rw != NULL) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + result = IMG_SavePNG_IO(surf, rw, 0); +#else result = IMG_SavePNG_RW(surf, rw, 0); +#endif } else { result = IMG_SavePNG(surf, name); diff --git a/src_c/mixer.c b/src_c/mixer.c index 0d3fa8f393..f342e18216 100644 --- a/src_c/mixer.c +++ b/src_c/mixer.c @@ -1875,7 +1875,11 @@ sound_init(PyObject *self, PyObject *arg, PyObject *kwarg) return -1; } Py_BEGIN_ALLOW_THREADS; +#if SDL_VERSION_ATLEAST(3, 0, 0) + chunk = Mix_LoadWAV_IO(rw, 1); +#else chunk = Mix_LoadWAV_RW(rw, 1); +#endif Py_END_ALLOW_THREADS; if (chunk == NULL) { PyErr_SetString(pgExc_SDLError, SDL_GetError()); diff --git a/src_c/music.c b/src_c/music.c index b6b733f90c..71bc776fb2 100644 --- a/src_c/music.c +++ b/src_c/music.c @@ -388,7 +388,11 @@ _load_music(PyObject *obj, char *namehint) } Py_BEGIN_ALLOW_THREADS; +#if SDL_VERSION_ATLEAST(3, 0, 0) + new_music = Mix_LoadMUSType_IO(rw, _get_type_from_hint(type), SDL_TRUE); +#else new_music = Mix_LoadMUSType_RW(rw, _get_type_from_hint(type), SDL_TRUE); +#endif Py_END_ALLOW_THREADS; if (ext) { diff --git a/src_c/rwobject.c b/src_c/rwobject.c index a88c2495b2..f750d90539 100644 --- a/src_c/rwobject.c +++ b/src_c/rwobject.c @@ -46,6 +46,18 @@ static const char pg_default_errors[] = "backslashreplace"; static PyObject *os_module = NULL; +#if SDL_VERSION_ATLEAST(3, 0, 0) +static Sint64 +_pg_rw_size(void *); +static Sint64 +_pg_rw_seek(void *, Sint64, SDL_IOWhence); +static size_t +_pg_rw_read(void *, void *, size_t, SDL_IOStatus *); +static size_t +_pg_rw_write(void *, const void *, size_t, SDL_IOStatus *); +static int +_pg_rw_close(void *); +#else static Sint64 _pg_rw_size(SDL_RWops *); static Sint64 @@ -56,6 +68,7 @@ static size_t _pg_rw_write(SDL_RWops *, const void *, size_t, size_t); static int _pg_rw_close(SDL_RWops *); +#endif /* Converter function used by PyArg_ParseTupleAndKeywords with the "O&" format. * @@ -291,13 +304,31 @@ pg_EncodeFilePath(PyObject *obj, PyObject *eclass) static int pgRWops_IsFileObject(SDL_RWops *rw) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_PropertiesID props = SDL_GetIOProperties(rw); + if (!props) { + // pgRWops_IsFileObject doesn't have any error checking facility + // so when in doubt let's say it isn't a file object. + return 0; + } + return SDL_GetBooleanProperty(props, "_pygame_is_file_object", 0); +#else return rw->close == _pg_rw_close; +#endif } +#if SDL_VERSION_ATLEAST(3, 0, 0) +static Sint64 +_pg_rw_size(void *userdata) +{ + pgRWHelper *helper = (pgRWHelper *)userdata; +#else static Sint64 _pg_rw_size(SDL_RWops *context) { pgRWHelper *helper = (pgRWHelper *)context->hidden.unknown.data1; +#endif + PyObject *pos = NULL; PyObject *tmp = NULL; Sint64 size; @@ -361,10 +392,19 @@ _pg_rw_size(SDL_RWops *context) return retval; } +#if SDL_VERSION_ATLEAST(3, 0, 0) +static size_t +_pg_rw_write(void *userdata, const void *ptr, size_t size, + SDL_IOStatus *status) +{ + pgRWHelper *helper = (pgRWHelper *)userdata; + size_t num = 1; +#else static size_t _pg_rw_write(SDL_RWops *context, const void *ptr, size_t size, size_t num) { pgRWHelper *helper = (pgRWHelper *)context->hidden.unknown.data1; +#endif PyObject *result; size_t retval; @@ -382,17 +422,28 @@ _pg_rw_write(SDL_RWops *context, const void *ptr, size_t size, size_t num) } Py_DECREF(result); +#if SDL_VERSION_ATLEAST(3, 0, 0) + retval = size; +#else retval = num; +#endif end: PyGILState_Release(state); return retval; } +#if SDL_VERSION_ATLEAST(3, 0, 0) +static int +_pg_rw_close(void *userdata) +{ + pgRWHelper *helper = (pgRWHelper *)userdata; +#else static int _pg_rw_close(SDL_RWops *context) { pgRWHelper *helper = (pgRWHelper *)context->hidden.unknown.data1; +#endif PyObject *result; int retval = 0; PyGILState_STATE state = PyGILState_Ensure(); @@ -414,7 +465,9 @@ _pg_rw_close(SDL_RWops *context) PyMem_Free(helper); PyGILState_Release(state); +#if !SDL_VERSION_ATLEAST(3, 0, 0) SDL_FreeRW(context); +#endif return retval; } @@ -437,6 +490,37 @@ pgRWops_FromFileObject(PyObject *obj) return NULL; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_IOStreamInterface iface; + iface.size = _pg_rw_size; + iface.seek = _pg_rw_seek; + iface.read = _pg_rw_read; + iface.write = _pg_rw_write; + iface.close = _pg_rw_close; + + // TODO: These should raise SDLError probably? + // rwobject.c hasn't required pygame.base before (the source of SDLError) + // so omitting that for now. + + rw = SDL_OpenIO(&iface, helper); + if (rw == NULL) { + iface.close(helper); + PyMem_Free(helper); + return (SDL_RWops *)RAISE(PyExc_IOError, SDL_GetError()); + } + + SDL_PropertiesID props = SDL_GetIOProperties(rw); + if (!props) { + PyMem_Free(helper); + return (SDL_RWops *)RAISE(PyExc_IOError, SDL_GetError()); + } + + if (SDL_SetBooleanProperty(props, "_pygame_is_file_object", 1) < 0) { + PyMem_Free(helper); + return (SDL_RWops *)RAISE(PyExc_IOError, SDL_GetError()); + } + +#else rw = SDL_AllocRW(); if (rw == NULL) { PyMem_Free(helper); @@ -450,14 +534,22 @@ pgRWops_FromFileObject(PyObject *obj) rw->read = _pg_rw_read; rw->write = _pg_rw_write; rw->close = _pg_rw_close; +#endif return rw; } +#if SDL_VERSION_ATLEAST(3, 0, 0) +static Sint64 +_pg_rw_seek(void *userdata, Sint64 offset, SDL_IOWhence whence) +{ + pgRWHelper *helper = (pgRWHelper *)userdata; +#else static Sint64 _pg_rw_seek(SDL_RWops *context, Sint64 offset, int whence) { pgRWHelper *helper = (pgRWHelper *)context->hidden.unknown.data1; +#endif PyObject *result; Sint64 retval; @@ -498,10 +590,18 @@ _pg_rw_seek(SDL_RWops *context, Sint64 offset, int whence) return retval; } +#if SDL_VERSION_ATLEAST(3, 0, 0) +static size_t +_pg_rw_read(void *userdata, void *ptr, size_t size, SDL_IOStatus *status) +{ + pgRWHelper *helper = (pgRWHelper *)userdata; + size_t maxnum = 1; +#else static size_t _pg_rw_read(SDL_RWops *context, void *ptr, size_t size, size_t maxnum) { pgRWHelper *helper = (pgRWHelper *)context->hidden.unknown.data1; +#endif PyObject *result; Py_ssize_t retval; @@ -527,7 +627,9 @@ _pg_rw_read(SDL_RWops *context, void *ptr, size_t size, size_t maxnum) retval = PyBytes_GET_SIZE(result); if (retval) { memcpy(ptr, PyBytes_AsString(result), retval); +#if !SDL_VERSION_ATLEAST(3, 0, 0) retval /= size; +#endif } Py_DECREF(result); From 4a3b6df7c286005f71892115d19a50d3fe9c2597 Mon Sep 17 00:00:00 2001 From: Starbuck5 <46412508+Starbuck5@users.noreply.github.com> Date: Sun, 6 Oct 2024 00:48:51 -0700 Subject: [PATCH 2/3] Enable rwobject SDL3 in meson --- meson.build | 3 +-- src_c/meson.build | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/meson.build b/meson.build index 042742aa07..f07a4bd0bf 100644 --- a/meson.build +++ b/meson.build @@ -85,8 +85,7 @@ sdl_ttf = '@0@_ttf'.format(sdl) sdl_image = '@0@_image'.format(sdl) if sdl_api == 3 - add_global_arguments('-DPG_SDL3=1', language: 'c') - add_global_arguments('-DSDL_ENABLE_OLD_NAMES=1', language: 'c') + add_global_arguments('-DPG_SDL3=1', '-DSDL_ENABLE_OLD_NAMES=1', language: 'c') endif pg_inc_dirs = [] diff --git a/src_c/meson.build b/src_c/meson.build index 9804e7cdfe..f2eb2a02d1 100644 --- a/src_c/meson.build +++ b/src_c/meson.build @@ -93,8 +93,6 @@ rect = py.extension_module( subdir: pg, ) -# TODO: support SDL3 -if sdl_api != 3 rwobject = py.extension_module( 'rwobject', 'rwobject.c', @@ -103,7 +101,6 @@ rwobject = py.extension_module( install: true, subdir: pg, ) -endif # TODO: support SDL3 if sdl_api != 3 From 7202d48cf482092fc4d2270d594e05fc35ef7952 Mon Sep 17 00:00:00 2001 From: Starbuck5 <46412508+Starbuck5@users.noreply.github.com> Date: Sun, 6 Oct 2024 01:28:48 -0700 Subject: [PATCH 3/3] Rwobject SDL3 fixes for 3.1.3 RC --- src_c/rwobject.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src_c/rwobject.c b/src_c/rwobject.c index f750d90539..172702ebb3 100644 --- a/src_c/rwobject.c +++ b/src_c/rwobject.c @@ -55,7 +55,7 @@ static size_t _pg_rw_read(void *, void *, size_t, SDL_IOStatus *); static size_t _pg_rw_write(void *, const void *, size_t, SDL_IOStatus *); -static int +static bool _pg_rw_close(void *); #else static Sint64 @@ -434,25 +434,30 @@ _pg_rw_write(SDL_RWops *context, const void *ptr, size_t size, size_t num) } #if SDL_VERSION_ATLEAST(3, 0, 0) -static int +static bool _pg_rw_close(void *userdata) { pgRWHelper *helper = (pgRWHelper *)userdata; + bool retval = true; #else static int _pg_rw_close(SDL_RWops *context) { pgRWHelper *helper = (pgRWHelper *)context->hidden.unknown.data1; + int retval = 0; #endif PyObject *result; - int retval = 0; PyGILState_STATE state = PyGILState_Ensure(); if (helper->close) { result = PyObject_CallNoArgs(helper->close); if (!result) { PyErr_Print(); +#if SDL_VERSION_ATLEAST(3, 0, 0) + retval = false; +#else retval = -1; +#endif } Py_XDECREF(result); } @@ -515,7 +520,7 @@ pgRWops_FromFileObject(PyObject *obj) return (SDL_RWops *)RAISE(PyExc_IOError, SDL_GetError()); } - if (SDL_SetBooleanProperty(props, "_pygame_is_file_object", 1) < 0) { + if (!SDL_SetBooleanProperty(props, "_pygame_is_file_object", 1)) { PyMem_Free(helper); return (SDL_RWops *)RAISE(PyExc_IOError, SDL_GetError()); } @@ -683,7 +688,11 @@ _rwops_from_pystr(PyObject *obj, char **extptr) /* If out of memory, decref oencoded to be safe, and try * to close out `rw` as well. */ Py_DECREF(oencoded); +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_RWclose(rw)) { +#else if (SDL_RWclose(rw) < 0) { +#endif PyErr_SetString(PyExc_IOError, SDL_GetError()); } return (SDL_RWops *)PyErr_NoMemory();