From a6eb1f498323b23df633989c68d21e8463caca06 Mon Sep 17 00:00:00 2001 From: Ankith Date: Mon, 2 Dec 2024 22:37:57 +0530 Subject: [PATCH] Port window.c to SDL3 --- src_c/meson.build | 3 - src_c/window.c | 223 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 208 insertions(+), 18 deletions(-) diff --git a/src_c/meson.build b/src_c/meson.build index bb94eddcb2..f66915b120 100644 --- a/src_c/meson.build +++ b/src_c/meson.build @@ -319,8 +319,6 @@ geometry = py.extension_module( subdir: pg, ) -# TODO: support SDL3 -if sdl_api != 3 window = py.extension_module( 'window', 'window.c', @@ -329,7 +327,6 @@ window = py.extension_module( install: true, subdir: pg, ) -endif # TODO: support SDL3 if sdl_api != 3 diff --git a/src_c/window.c b/src_c/window.c index aec40e8688..19e7680d3a 100644 --- a/src_c/window.c +++ b/src_c/window.c @@ -91,13 +91,34 @@ static GL_glViewport_Func p_glViewport = NULL; #define pgWindow_Check(x) \ (PyObject_IsInstance((x), (PyObject *)&pgWindow_Type)) +static inline PyObject * +pg_get_pg_window(SDL_Window *win) +{ +#if SDL_VERSION_ATLEAST(3, 0, 0) + return SDL_GetPointerProperty(SDL_GetWindowProperties(win), "pg_window", + NULL); +#else + return SDL_GetWindowData(win, "pg_window"); +#endif +} + +static inline void +pg_set_pg_window(SDL_Window *win, PyObject *pg_win) +{ +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_SetPointerProperty(SDL_GetWindowProperties(win), "pg_window", pg_win); +#else + SDL_SetWindowData(win, "pg_window", pg_win); +#endif +} + static PyObject * get_grabbed_window(PyObject *self, PyObject *_null) { SDL_Window *grabbed = SDL_GetGrabbedWindow(); PyObject *win_obj = NULL; if (grabbed) { - win_obj = SDL_GetWindowData(grabbed, "pg_window"); + win_obj = pg_get_pg_window(grabbed); if (!win_obj) { Py_RETURN_NONE; } @@ -158,7 +179,11 @@ window_get_surface(pgWindowObject *self, PyObject *_null) } if (pg_GetDefaultConvertFormat() == 0) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + pg_SetDefaultConvertFormat(_surf->format); +#else pg_SetDefaultConvertFormat(_surf->format->format); +#endif } if (self->surf == NULL) { @@ -204,7 +229,12 @@ static int _window_opengl_set_viewport(SDL_Window *window, SDL_GLContext context, int wnew, int hnew) { - if (SDL_GL_MakeCurrent(window, context) < 0) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_GL_MakeCurrent(window, context)) +#else + if (SDL_GL_MakeCurrent(window, context) < 0) +#endif + { PyErr_SetString(pgExc_SDLError, SDL_GetError()); return -1; } @@ -217,17 +247,26 @@ _window_opengl_set_viewport(SDL_Window *window, SDL_GLContext context, } // Callback function for surface auto resize or OpenGL viewport update +#if SDL_VERSION_ATLEAST(3, 0, 0) +static bool SDLCALL +#else static int SDLCALL +#endif _resize_event_watch(void *userdata, SDL_Event *event) { pgWindowObject *event_window_pg; SDL_Window *event_window; +#if SDL_VERSION_ATLEAST(3, 0, 0) + if ((event->type != SDL_WINDOWEVENT_SIZE_CHANGED)) + return 0; +#else if ((event->type != SDL_WINDOWEVENT)) return 0; if (event->window.event != SDL_WINDOWEVENT_SIZE_CHANGED) return 0; +#endif event_window = SDL_GetWindowFromID(event->window.windowID); - event_window_pg = SDL_GetWindowData(event_window, "pg_window"); + event_window_pg = (pgWindowObject *)pg_get_pg_window(event_window); if (!event_window_pg) return 0; @@ -262,9 +301,57 @@ window_set_windowed(pgWindowObject *self, PyObject *_null) Py_RETURN_NONE; } +#if SDL_VERSION_ATLEAST(3, 0, 0) +/* Returns 1 on success, 0 on failure */ +static int +pg_window_set_fullscreen(SDL_Window *window, int desktop) +{ + int ret = 0; + SDL_DisplayMode **modes = NULL; + SDL_DisplayMode *chosen_mode = NULL; + if (!desktop) { + /* if not desktop fullscreen, get the first display mode available */ + SDL_DisplayID disp = SDL_GetDisplayForWindow(window); + if (!disp) { + goto end; + } + modes = SDL_GetFullscreenDisplayModes(disp, NULL); + if (!modes) { + goto end; + } + chosen_mode = modes[0]; + if (!chosen_mode) { + SDL_SetError("Could not get fullscreen display mode"); + goto end; + } + } + if (!SDL_SetWindowFullscreenMode(window, chosen_mode)) { + goto end; + } + + if (!SDL_SetWindowFullscreen(window, 1)) { + goto end; + } + ret = 1; +end: + SDL_free(modes); + return ret; +} +#endif + static PyObject * window_set_fullscreen(pgWindowObject *self, PyObject *args, PyObject *kwargs) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + int desktop = 0; + char *kwids[] = {"desktop", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|p", kwids, &desktop)) { + return NULL; + } + if (!pg_window_set_fullscreen(self->_win, desktop)) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } +#else SDL_bool desktop = SDL_FALSE; int flags = SDL_WINDOW_FULLSCREEN; char *kwids[] = {"desktop", NULL}; @@ -277,6 +364,7 @@ window_set_fullscreen(pgWindowObject *self, PyObject *args, PyObject *kwargs) if (SDL_SetWindowFullscreen(self->_win, flags)) { return RAISE(pgExc_SDLError, SDL_GetError()); } +#endif Py_RETURN_NONE; } @@ -288,6 +376,10 @@ window_focus(pgWindowObject *self, PyObject *args, PyObject *kwargs) if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|p", kwids, &input_only)) { return NULL; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + /* input_only ignored on SDL3 */ + SDL_RaiseWindow(self->_win); +#else if (input_only) { if (PyErr_WarnEx(PyExc_DeprecationWarning, "The input_only kwarg has been deprecated and may be " @@ -302,14 +394,15 @@ window_focus(pgWindowObject *self, PyObject *args, PyObject *kwargs) else { SDL_RaiseWindow(self->_win); } +#endif Py_RETURN_NONE; } static PyObject * window_get_focused(pgWindowObject *self, void *v) { - uint32_t flags = SDL_GetWindowFlags(self->_win); - return PyBool_FromLong((flags & SDL_WINDOW_INPUT_FOCUS) != 0); + return PyBool_FromLong( + (SDL_GetWindowFlags(self->_win) & SDL_WINDOW_INPUT_FOCUS) != 0); } static PyObject * @@ -347,6 +440,32 @@ window_minimize(pgWindowObject *self, PyObject *_null) Py_RETURN_NONE; } +/* Based on code from sdl2-compat */ +static int SDLCALL +PG_SetWindowModalFor(SDL_Window *modal_window, SDL_Window *parent_window) +{ +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!modal_window) { + SDL_SetError("Invalid window"); + return -1; + } + if (SDL_GetWindowFlags(modal_window) & SDL_WINDOW_MODAL) { + SDL_SetWindowModal(modal_window, false); + } + if (SDL_SetWindowParent(modal_window, parent_window)) { + int ret = 0; + if (parent_window) { + ret = SDL_SetWindowModal(modal_window, true) ? 0 : -1; + } + return ret; + } + + return -1; +#else + return SDL_SetWindowModalFor(modal_window, parent_window); +#endif +} + static PyObject * window_set_modal_for(pgWindowObject *self, PyObject *arg) { @@ -354,7 +473,7 @@ window_set_modal_for(pgWindowObject *self, PyObject *arg) return RAISE(PyExc_TypeError, "Argument to set_modal_for must be a Window."); } - if (!SDL_SetWindowModalFor(self->_win, ((pgWindowObject *)arg)->_win)) { + if (!PG_SetWindowModalFor(self->_win, ((pgWindowObject *)arg)->_win)) { return RAISE(pgExc_SDLError, SDL_GetError()); } Py_RETURN_NONE; @@ -562,7 +681,11 @@ window_set_mouse_rect(pgWindowObject *self, PyObject *arg, void *v) "mouse_rect should be a Rect-like object or None"); return -1; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_SetWindowMouseRect(self->_win, mouse_rect_p)) { +#else if (SDL_SetWindowMouseRect(self->_win, mouse_rect_p) < 0) { +#endif PyErr_SetString(pgExc_SDLError, SDL_GetError()); return -1; } @@ -764,10 +887,17 @@ window_set_opacity(pgWindowObject *self, PyObject *arg, void *v) static PyObject * window_get_opacity(pgWindowObject *self, void *v) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + float opacity = SDL_GetWindowOpacity(self->_win); + if (opacity < 0) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } +#else float opacity; if (SDL_GetWindowOpacity(self->_win, &opacity)) { return RAISE(pgExc_SDLError, SDL_GetError()); } +#endif return PyFloat_FromDouble((double)opacity); } @@ -779,8 +909,7 @@ window_get_opengl(pgWindowObject *self, void *v) hasGL = self->context != NULL; } else { - int flags = SDL_GetWindowFlags(self->_win); - hasGL = (flags & SDL_WINDOW_OPENGL) > 0; + hasGL = (SDL_GetWindowFlags(self->_win) & SDL_WINDOW_OPENGL) > 0; } return PyBool_FromLong(hasGL); } @@ -802,8 +931,8 @@ window_dealloc(pgWindowObject *self, PyObject *_null) } SDL_DestroyWindow(self->_win); } - else if (SDL_GetWindowData(self->_win, "pg_window") != NULL) { - SDL_SetWindowData(self->_win, "pg_window", NULL); + else if (pg_get_pg_window(self->_win) != NULL) { + pg_set_pg_window(self->_win, NULL); } } if (self->surf) { @@ -818,6 +947,39 @@ window_dealloc(pgWindowObject *self, PyObject *_null) Py_TYPE(self)->tp_free(self); } +/* Based on code from sdl2-compat */ +static SDL_Window * +PG_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags) +{ +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_Window *window = NULL; + + SDL_PropertiesID props = SDL_CreateProperties(); + if (!props) { + return NULL; + } + + if (title && *title) { + SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, + title); + } + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, x); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, y); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, w); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, h); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, flags); + SDL_SetBooleanProperty( + props, SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN, + SDL_GetHintBoolean("SDL_VIDEO_EXTERNAL_CONTEXT", false)); + + window = SDL_CreateWindowWithProperties(props); + SDL_DestroyProperties(props); + return window; +#else + return SDL_CreateWindow(title, x, y, w, h, flags); +#endif +} + static int window_init(pgWindowObject *self, PyObject *args, PyObject *kwargs) { @@ -844,6 +1006,9 @@ window_init(pgWindowObject *self, PyObject *args, PyObject *kwargs) if (!_kw) return -1; +#if SDL_VERSION_ATLEAST(3, 0, 0) + int fullscreen_non_desktop = 0; +#endif if (kwargs) { while (PyDict_Next(kwargs, &dict_pos, &_key, &_value)) { if (!PyUnicode_Check(_key)) { @@ -876,12 +1041,20 @@ window_init(pgWindowObject *self, PyObject *args, PyObject *kwargs) } } else if (!strcmp(_key_str, "fullscreen")) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + fullscreen_non_desktop = 1; +#endif if (_value_bool) flags |= SDL_WINDOW_FULLSCREEN; } else if (!strcmp(_key_str, "fullscreen_desktop")) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (_value_bool) + flags |= SDL_WINDOW_FULLSCREEN; +#else if (_value_bool) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; +#endif } else if (!strcmp(_key_str, "hidden")) { if (_value_bool) @@ -943,7 +1116,11 @@ window_init(pgWindowObject *self, PyObject *args, PyObject *kwargs) return -1; } if (_value_bool) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + flags |= SDL_WINDOW_EXTERNAL; +#else flags |= SDL_WINDOW_FOREIGN; +#endif } } else if (!strcmp(_key_str, "allow_high_dpi")) { @@ -1016,11 +1193,19 @@ window_init(pgWindowObject *self, PyObject *args, PyObject *kwargs) } } - _win = SDL_CreateWindow(title, pos_x, pos_y, size_w, size_h, flags); + _win = PG_CreateWindow(title, pos_x, pos_y, size_w, size_h, flags); if (!_win) { PyErr_SetString(pgExc_SDLError, SDL_GetError()); return -1; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (fullscreen_non_desktop) { + if (!pg_window_set_fullscreen(_win, 0)) { + PyErr_SetString(pgExc_SDLError, SDL_GetError()); + return -1; + } + } +#endif self->_win = _win; self->_is_borrowed = SDL_FALSE; self->surf = NULL; @@ -1042,15 +1227,20 @@ window_init(pgWindowObject *self, PyObject *args, PyObject *kwargs) self->context = NULL; } - SDL_SetWindowData(_win, "pg_window", self); + pg_set_pg_window(_win, (PyObject *)self); PyObject *icon = pg_display_resource(icon_defaultname); if (!icon) { return -1; } if (icon_colorkey != -1) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_SetColorKey(pgSurface_AsSurface(icon), SDL_TRUE, + icon_colorkey)) { +#else if (SDL_SetColorKey(pgSurface_AsSurface(icon), SDL_TRUE, icon_colorkey) < 0) { +#endif PyErr_SetString(pgExc_SDLError, SDL_GetError()); return -1; } @@ -1082,8 +1272,7 @@ window_from_display_module(PyTypeObject *cls, PyObject *_null) "display.set_mode has not been called yet."); } - pgWindowObject *self = - (pgWindowObject *)SDL_GetWindowData(window, "pg_window"); + pgWindowObject *self = (pgWindowObject *)pg_get_pg_window(window); if (self != NULL) { Py_INCREF(self); return (PyObject *)self; @@ -1092,7 +1281,7 @@ window_from_display_module(PyTypeObject *cls, PyObject *_null) self = (pgWindowObject *)(cls->tp_new(cls, NULL, NULL)); self->_win = window; self->_is_borrowed = SDL_TRUE; - SDL_SetWindowData(window, "pg_window", self); + pg_set_pg_window(window, (PyObject *)self); return (PyObject *)self; } @@ -1113,7 +1302,11 @@ window_flash(pgWindowObject *self, PyObject *arg) return RAISE(PyExc_ValueError, "Unsupported window flash operation."); } +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_FlashWindow(self->_win, operation)) { +#else if (SDL_FlashWindow(self->_win, operation) < 0) { +#endif return RAISE(pgExc_SDLError, SDL_GetError()); } Py_RETURN_NONE;