diff --git a/src_c/display.c b/src_c/display.c index 8f72dcbcc6..254d46710d 100644 --- a/src_c/display.c +++ b/src_c/display.c @@ -31,7 +31,9 @@ #include "doc/display_doc.h" +#ifndef PG_SDL3 #include +#endif static PyTypeObject pgVidInfo_Type; @@ -44,7 +46,9 @@ static SDL_Texture *pg_texture = NULL; typedef struct _display_state_s { char *title; PyObject *icon; +#if !SDL_VERSION_ATLEAST(3, 0, 0) Uint16 *gamma_ramp; +#endif SDL_GLContext gl_context; int toggle_windowed_w; int toggle_windowed_h; @@ -87,10 +91,12 @@ _display_state_cleanup(_DisplayState *state) SDL_GL_DeleteContext(state->gl_context); state->gl_context = NULL; } +#if !SDL_VERSION_ATLEAST(3, 0, 0) if (state->gamma_ramp) { free(state->gamma_ramp); state->gamma_ramp = NULL; } +#endif } // prevent this code block from being linked twice @@ -235,7 +241,11 @@ pg_display_init(PyObject *self, PyObject *_null) drivername = SDL_getenv("SDL_VIDEODRIVER"); if (drivername && !SDL_strncasecmp("windib", drivername, SDL_strlen(drivername))) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_setenv_unsafe("SDL_VIDEODRIVER", "windows", 1); +#else SDL_setenv("SDL_VIDEODRIVER", "windows", 1); +#endif } if (!SDL_WasInit(SDL_INIT_VIDEO)) { if (!_pg_mac_display_init()) { @@ -269,9 +279,15 @@ pg_get_init(PyObject *self, PyObject *_null) static PyObject * pg_get_active(PyObject *self, PyObject *_null) { - Uint32 flags = SDL_GetWindowFlags(pg_GetDefaultWindow()); + SDL_WindowFlags flags = SDL_GetWindowFlags(pg_GetDefaultWindow()); + +#if SDL_VERSION_ATLEAST(3, 0, 0) + return PyBool_FromLong(!(flags & SDL_WINDOW_HIDDEN) && + !(flags & SDL_WINDOW_MINIMIZED)); +#else return PyBool_FromLong((flags & SDL_WINDOW_SHOWN) && !(flags & SDL_WINDOW_MINIMIZED)); +#endif } /* vidinfo object */ @@ -331,8 +347,10 @@ pg_vidinfo_getattr(PyObject *self, char *name) info->vfmt->Bshift, info->vfmt->Ashift); } else if (!strcmp(name, "losses")) { - return Py_BuildValue("(iiii)", info->vfmt->Rloss, info->vfmt->Gloss, - info->vfmt->Bloss, info->vfmt->Aloss); + return Py_BuildValue("(iiii)", PG_FORMAT_R_LOSS(info->vfmt), + PG_FORMAT_G_LOSS(info->vfmt), + PG_FORMAT_B_LOSS(info->vfmt), + PG_FORMAT_A_LOSS(info->vfmt)); } else if (!strcmp(name, "current_h")) { return PyLong_FromLong(info->current_h); @@ -378,8 +396,9 @@ pg_vidinfo_str(PyObject *self) PG_FORMAT_BytesPerPixel(info->vfmt), info->vfmt->Rmask, info->vfmt->Gmask, info->vfmt->Bmask, info->vfmt->Amask, info->vfmt->Rshift, info->vfmt->Gshift, info->vfmt->Bshift, - info->vfmt->Ashift, info->vfmt->Rloss, info->vfmt->Gloss, - info->vfmt->Bloss, info->vfmt->Aloss, info->current_w, info->current_h, + info->vfmt->Ashift, PG_FORMAT_R_LOSS(info->vfmt), + PG_FORMAT_G_LOSS(info->vfmt), PG_FORMAT_B_LOSS(info->vfmt), + PG_FORMAT_A_LOSS(info->vfmt), info->current_w, info->current_h, pixel_format_name); } @@ -410,8 +429,12 @@ pgVidInfo_New(const pg_VideoInfo *i) static pg_VideoInfo * pg_GetVideoInfo(pg_VideoInfo *info) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_DisplayMode *mode_ptr; +#else SDL_DisplayMode mode; - SDL_PixelFormat *tempformat; +#endif + PG_PixelFormat *tempformat; Uint32 formatenum; pgSurfaceObject *winsurfobj; SDL_Surface *winsurf; @@ -427,15 +450,28 @@ pg_GetVideoInfo(pg_VideoInfo *info) winsurf = pgSurface_AsSurface(winsurfobj); info->current_w = winsurf->w; info->current_h = winsurf->h; - info->vfmt_data = *(winsurf->format); + PG_PixelFormat *fmt = PG_GetSurfaceFormat(winsurf); + if (!fmt) { + PyErr_SetString(pgExc_SDLError, SDL_GetError()); + return (pg_VideoInfo *)NULL; + } + info->vfmt_data = *fmt; info->vfmt = &info->vfmt_data; } else { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if ((mode_ptr = SDL_GetCurrentDisplayMode(0))) { + info->current_w = mode_ptr->w; + info->current_h = mode_ptr->h; + formatenum = mode_ptr->format; + } +#else if (SDL_GetCurrentDisplayMode(0, &mode) == 0) { info->current_w = mode.w; info->current_h = mode.h; formatenum = mode.format; } +#endif else { info->current_w = -1; info->current_h = -1; @@ -445,7 +481,9 @@ pg_GetVideoInfo(pg_VideoInfo *info) if ((tempformat = SDL_AllocFormat(formatenum))) { info->vfmt_data = *tempformat; info->vfmt = &info->vfmt_data; +#if !SDL_VERSION_ATLEAST(3, 0, 0) SDL_FreeFormat(tempformat); +#endif } else { PyErr_SetString(pgExc_SDLError, SDL_GetError()); @@ -469,12 +507,14 @@ pg_get_wm_info(PyObject *self, PyObject *_null) { PyObject *dict; PyObject *tmp; - SDL_SysWMinfo info; SDL_Window *win; VIDEO_INIT_CHECK(); +#if !SDL_VERSION_ATLEAST(3, 0, 0) + SDL_SysWMinfo info; SDL_VERSION(&(info.version)) +#endif dict = PyDict_New(); if (!dict) { return NULL; @@ -484,12 +524,32 @@ pg_get_wm_info(PyObject *self, PyObject *_null) if (!win) { return dict; } +#if !SDL_VERSION_ATLEAST(3, 0, 0) if (!SDL_GetWindowWMInfo(win, &info)) { return dict; } +#endif (void)tmp; #if defined(SDL_VIDEO_DRIVER_WINDOWS) +#if SDL_VERSION_ATLEAST(3, 0, 0) + tmp = PyLong_FromLongLong((long long)SDL_GetPointerProperty( + SDL_GetWindowProperties(win), SDL_PROP_WINDOW_WIN32_HWND_POINTER, + NULL)); + PyDict_SetItemString(dict, "window", tmp); + Py_DECREF(tmp); + + tmp = PyLong_FromLongLong((long long)SDL_GetPointerProperty( + SDL_GetWindowProperties(win), SDL_PROP_WINDOW_WIN32_HDC_POINTER, + NULL)); + PyDict_SetItemString(dict, "hdc", tmp); + Py_DECREF(tmp); + tmp = PyLong_FromLongLong((long long)SDL_GetPointerProperty( + SDL_GetWindowProperties(win), SDL_PROP_WINDOW_WIN32_INSTANCE_POINTER, + NULL)); + PyDict_SetItemString(dict, "hinstance", tmp); + Py_DECREF(tmp); +#else tmp = PyLong_FromLongLong((long long)info.info.win.window); PyDict_SetItemString(dict, "window", tmp); Py_DECREF(tmp); @@ -501,12 +561,26 @@ pg_get_wm_info(PyObject *self, PyObject *_null) PyDict_SetItemString(dict, "hinstance", tmp); Py_DECREF(tmp); #endif -#if defined(SDL_VIDEO_DRIVER_WINRT) +#endif +#if defined(SDL_VIDEO_DRIVER_WINRT) && !SDL_VERSION_ATLEAST(3, 0, 0) tmp = PyCapsule_New(info.info.winrt.window, "window", NULL); PyDict_SetItemString(dict, "window", tmp); Py_DECREF(tmp); #endif #if defined(SDL_VIDEO_DRIVER_X11) +#if SDL_VERSION_ATLEAST(3, 0, 0) + tmp = PyLong_FromLong(SDL_GetNumberProperty( + SDL_GetWindowProperties(win), SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0)); + PyDict_SetItemString(dict, "window", tmp); + Py_DECREF(tmp); + + tmp = PyCapsule_New( + SDL_GetPointerProperty(SDL_GetWindowProperties(win), + SDL_PROP_WINDOW_X11_DISPLAY_POINTER, NULL), + "display", NULL); + PyDict_SetItemString(dict, "display", tmp); + Py_DECREF(tmp); +#else tmp = PyLong_FromLong(info.info.x11.window); PyDict_SetItemString(dict, "window", tmp); Py_DECREF(tmp); @@ -515,7 +589,8 @@ pg_get_wm_info(PyObject *self, PyObject *_null) PyDict_SetItemString(dict, "display", tmp); Py_DECREF(tmp); #endif -#if defined(SDL_VIDEO_DRIVER_DIRECTFB) +#endif +#if defined(SDL_VIDEO_DRIVER_DIRECTFB) && !SDL_VERSION_ATLEAST(3, 0, 0) tmp = PyCapsule_New(info.info.dfb.dfb, "dfb", NULL); PyDict_SetItemString(dict, "dfb", tmp); Py_DECREF(tmp); @@ -529,11 +604,46 @@ pg_get_wm_info(PyObject *self, PyObject *_null) Py_DECREF(tmp); #endif #if defined(SDL_VIDEO_DRIVER_COCOA) +#if SDL_VERSION_ATLEAST(3, 0, 0) + tmp = PyCapsule_New( + SDL_GetPointerProperty(SDL_GetWindowProperties(win), + SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, NULL), + "window", NULL); + PyDict_SetItemString(dict, "window", tmp); + Py_DECREF(tmp); +#else tmp = PyCapsule_New(info.info.cocoa.window, "window", NULL); PyDict_SetItemString(dict, "window", tmp); Py_DECREF(tmp); #endif +#endif #if defined(SDL_VIDEO_DRIVER_UIKIT) +#if SDL_VERSION_ATLEAST(3, 0, 0) + tmp = PyCapsule_New( + SDL_GetPointerProperty(SDL_GetWindowProperties(win), + SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER, NULL), + "window", NULL); + PyDict_SetItemString(dict, "window", tmp); + Py_DECREF(tmp); + + tmp = PyLong_FromLong(SDL_GetPointerNumber( + SDL_GetWindowProperties(win), + SDL_PROP_WINDOW_UIKIT_OPENGL_FRAMEBUFFER_NUMBER, 0)); + PyDict_SetItemString(dict, "framebuffer", tmp); + Py_DECREF(tmp); + + tmp = PyLong_FromLong(SDL_GetPointerNumber( + SDL_GetWindowProperties(win), + SDL_PROP_WINDOW_UIKIT_OPENGL_RENDERBUFFER_NUMBER, 0)); + PyDict_SetItemString(dict, "colorbuffer", tmp); + Py_DECREF(tmp); + + tmp = PyLong_FromLong(SDL_GetPointerNumber( + SDL_GetWindowProperties(win), + SDL_PROP_WINDOW_UIKIT_OPENGL_RESOLVE_FRAMEBUFFER_NUMBER, 0)); + PyDict_SetItemString(dict, "resolveFramebuffer", tmp); + Py_DECREF(tmp); +#else tmp = PyCapsule_New(info.info.uikit.window, "window", NULL); PyDict_SetItemString(dict, "window", tmp); Py_DECREF(tmp); @@ -550,7 +660,23 @@ pg_get_wm_info(PyObject *self, PyObject *_null) PyDict_SetItemString(dict, "resolveFramebuffer", tmp); Py_DECREF(tmp); #endif +#endif #if defined(SDL_VIDEO_DRIVER_WAYLAND) +#if SDL_VERSION_ATLEAST(3, 0, 0) + tmp = PyCapsule_New( + SDL_GetPointerProperty(SDL_GetWindowProperties(win), + SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, NULL), + "display", NULL); + PyDict_SetItemString(dict, "display", tmp); + Py_DECREF(tmp); + + tmp = PyCapsule_New( + SDL_GetPointerProperty(SDL_GetWindowProperties(win), + SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, NULL), + "surface", NULL); + PyDict_SetItemString(dict, "surface", tmp); + Py_DECREF(tmp); +#else tmp = PyCapsule_New(info.info.wl.display, "display", NULL); PyDict_SetItemString(dict, "display", tmp); Py_DECREF(tmp); @@ -563,7 +689,22 @@ pg_get_wm_info(PyObject *self, PyObject *_null) PyDict_SetItemString(dict, "shell_surface", tmp); Py_DECREF(tmp); #endif +#endif #if defined(SDL_VIDEO_DRIVER_ANDROID) +#if SDL_VERSION_ATLEAST(3, 0, 0) + tmp = PyCapsule_New( + SDL_GetPointerProperty(SDL_GetWindowProperties(win), + SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER, NULL), + "window", NULL); + PyDict_SetItemString(dict, "window", tmp); + Py_DECREF(tmp); + + tmp = PyLong_FromLong((long)SDL_GetPointerProperty( + SDL_GetWindowProperties(win), SDL_PROP_WINDOW_ANDROID_SURFACE_POINTER, + NULL)); + PyDict_SetItemString(dict, "surface", tmp); + Py_DECREF(tmp); +#else tmp = PyCapsule_New(info.info.android.window, "window", NULL); PyDict_SetItemString(dict, "window", tmp); Py_DECREF(tmp); @@ -572,7 +713,21 @@ pg_get_wm_info(PyObject *self, PyObject *_null) PyDict_SetItemString(dict, "surface", tmp); Py_DECREF(tmp); #endif +#endif #if defined(SDL_VIDEO_DRIVER_VIVANTE) +#if SDL_VERSION_ATLEAST(3, 0, 0) + tmp = PyLong_FromLong((long)SDL_GetPointerProperty( + SDL_GetWindowProperties(win), SDL_PROP_WINDOW_VIVANTE_DISPLAY_POINTER, + NULL)); + PyDict_SetItemString(dict, "display", tmp); + Py_DECREF(tmp); + + tmp = PyLong_FromLong((long)SDL_GetPointerProperty( + SDL_GetWindowProperties(win), SDL_PROP_WINDOW_VIVANTE_WINDOW_POINTER, + NULL)); + PyDict_SetItemString(dict, "window", tmp); + Py_DECREF(tmp); +#else tmp = PyLong_FromLong((long)info.info.vivante.display); PyDict_SetItemString(dict, "display", tmp); Py_DECREF(tmp); @@ -580,6 +735,7 @@ pg_get_wm_info(PyObject *self, PyObject *_null) tmp = PyLong_FromLong((long)info.info.vivante.window); PyDict_SetItemString(dict, "window", tmp); Py_DECREF(tmp); +#endif #endif return dict; @@ -707,14 +863,43 @@ _get_video_window_pos(int *x, int *y, int *center_window) return 0; } +static inline int +PG_RenderSetIntegerScale(SDL_Renderer *renderer, int enable) +{ +#if SDL_VERSION_ATLEAST(3, 0, 0) + int w = 0, h = 0; + if (!SDL_GetRenderLogicalPresentation(renderer, &w, &h, NULL)) { + return -1; + } + return SDL_SetRenderLogicalPresentation( + renderer, w, h, + enable ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE + : SDL_LOGICAL_PRESENTATION_LETTERBOX) + ? 0 + : -1; +#else + return SDL_RenderSetIntegerScale(renderer, enable); +#endif +} + +#if SDL_VERSION_ATLEAST(3, 0, 0) +static bool +#else static int SDLCALL +#endif pg_ResizeEventWatch(void *userdata, SDL_Event *event) { SDL_Window *pygame_window; _DisplayState *state; SDL_Window *window; - if (event->type != SDL_WINDOWEVENT) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (event->type >= SDL_EVENT_WINDOW_FIRST && + event->type <= SDL_EVENT_WINDOW_LAST) +#else + if (event->type == SDL_WINDOWEVENT) +#endif + { return 0; } @@ -727,7 +912,11 @@ pg_ResizeEventWatch(void *userdata, SDL_Event *event) } if (state->unscaled_render && pg_renderer != NULL) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (event->type == SDL_WINDOWEVENT_SIZE_CHANGED) { +#else if (event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { +#endif int w = event->window.data1; int h = event->window.data2; pgSurfaceObject *display_surface = pg_GetDefaultWindowSurface(); @@ -747,11 +936,19 @@ pg_ResizeEventWatch(void *userdata, SDL_Event *event) } if (pg_renderer != NULL) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (event->type == SDL_WINDOWEVENT_MAXIMIZED) { +#else if (event->window.event == SDL_WINDOWEVENT_MAXIMIZED) { - SDL_RenderSetIntegerScale(pg_renderer, SDL_FALSE); +#endif + PG_RenderSetIntegerScale(pg_renderer, SDL_FALSE); } +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (event->type == SDL_WINDOWEVENT_RESTORED) { +#else if (event->window.event == SDL_WINDOWEVENT_RESTORED) { - SDL_RenderSetIntegerScale( +#endif + PG_RenderSetIntegerScale( pg_renderer, !(SDL_GetHintBoolean( "SDL_HINT_RENDER_SCALE_QUALITY", SDL_FALSE))); } @@ -759,7 +956,11 @@ pg_ResizeEventWatch(void *userdata, SDL_Event *event) } if (state->using_gl) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (event->type == SDL_WINDOWEVENT_SIZE_CHANGED) { +#else if (event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { +#endif GL_glViewport_Func p_glViewport = (GL_glViewport_Func)SDL_GL_GetProcAddress("glViewport"); int wnew = event->window.data1; @@ -785,7 +986,11 @@ pg_ResizeEventWatch(void *userdata, SDL_Event *event) return 0; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (event->type == SDL_WINDOWEVENT_SIZE_CHANGED) { +#else if (event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { +#endif SDL_Surface *sdl_surface = SDL_GetWindowSurface(window); pgSurfaceObject *old_surface = pg_GetDefaultWindowSurface(); if (sdl_surface != old_surface->surf) { @@ -821,7 +1026,7 @@ pg_display_set_autoresize(PyObject *self, PyObject *arg) int _get_display(SDL_Window *win) { - char *display_env = SDL_getenv("PYGAME_DISPLAY"); + const char *display_env = SDL_getenv("PYGAME_DISPLAY"); int display = 0; /* default display 0 */ if (win != NULL) { @@ -844,6 +1049,24 @@ _get_display(SDL_Window *win) int num_displays, i; SDL_Rect display_bounds; SDL_Point mouse_position; +#if SDL_VERSION_ATLEAST(3, 0, 0) + float x, y; + SDL_GetGlobalMouseState(&x, &y); + mouse_position.x = (int)x; + mouse_position.y = (int)y; + SDL_DisplayID *displays = SDL_GetDisplays(&num_displays); + if (displays) { + for (i = 0; i < num_displays; i++) { + if (SDL_GetDisplayBounds(displays[i], &display_bounds) == 0) { + if (SDL_PointInRect(&mouse_position, &display_bounds)) { + display = displays[i]; + break; + } + } + } + SDL_free(displays); + } +#else SDL_GetGlobalMouseState(&mouse_position.x, &mouse_position.y); num_displays = SDL_GetNumVideoDisplays(); @@ -855,10 +1078,87 @@ _get_display(SDL_Window *win) } } } +#endif } return display; } +/* Based on code from sdl2-compat */ +static SDL_Window * +PG_CreateWindowCompat(const char *title, int x, int y, int w, int h, + SDL_WindowFlags 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 +} + +#if SDL_VERSION_ATLEAST(3, 0, 0) +/* Returns 0 on success, negative on failure. */ +static int +PG_SetWindowFullscreen(SDL_Window *window, bool fullscreen, + bool non_desktop_fullscreen) +{ + int ret = -1; + SDL_DisplayMode **modes = NULL; + SDL_DisplayMode *chosen_mode = NULL; + if (!SDL_SetWindowFullscreen(window, fullscreen)) { + goto end; + } + if (fullscreen) { + if (non_desktop_fullscreen) { + /* 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; + } + } + + ret = 0; +end: + SDL_free(modes); + return ret; +} +#endif + static PyObject * pg_set_mode(PyObject *self, PyObject *arg, PyObject *kwds) { @@ -881,10 +1181,10 @@ pg_set_mode(PyObject *self, PyObject *arg, PyObject *kwds) screen as the old one */ int display = _get_display(win); char *title = state->title; - char *scale_env, *winid_env; - SDL_SysWMinfo wm_info; - - SDL_VERSION(&wm_info.version); + const char *scale_env, *winid_env; +#if SDL_VERSION_ATLEAST(3, 0, 0) + int non_desktop_fullscreen = 0; +#endif char *keywords[] = {"size", "flags", "depth", "display", "vsync", NULL}; @@ -902,10 +1202,13 @@ pg_set_mode(PyObject *self, PyObject *arg, PyObject *kwds) if (scale_env != NULL) { flags |= PGS_SCALED; + /* TODO: figure out SDL3 equivalent */ +#if !SDL_VERSION_ATLEAST(3, 0, 0) if (strcmp(scale_env, "photo") == 0) { SDL_SetHintWithPriority(SDL_HINT_RENDER_SCALE_QUALITY, "best", SDL_HINT_NORMAL); } +#endif } if (size != NULL) { @@ -976,30 +1279,49 @@ pg_set_mode(PyObject *self, PyObject *arg, PyObject *kwds) { Uint32 sdl_flags = 0; - SDL_DisplayMode display_mode; - if (SDL_GetDesktopDisplayMode(display, &display_mode) != 0) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_DisplayMode *display_mode; + if (!(display_mode = SDL_GetDesktopDisplayMode(display))) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } +#else + SDL_DisplayMode display_mode_local; + SDL_DisplayMode *display_mode = &display_mode_local; + + if (SDL_GetDesktopDisplayMode(display, display_mode) != 0) { return RAISE(pgExc_SDLError, SDL_GetError()); } +#endif if (w == 0 && h == 0 && !(flags & PGS_SCALED)) { /* We are free to choose a resolution in this case, so we can avoid changing the physical resolution. This used to default to the max supported by the monitor, but we can use current desktop resolution without breaking compatibility. */ - w = display_mode.w; - h = display_mode.h; + w = display_mode->w; + h = display_mode->h; zero_size = 1; } if (flags & PGS_FULLSCREEN) { if (flags & PGS_SCALED) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + sdl_flags |= SDL_WINDOW_FULLSCREEN; + non_desktop_fullscreen = 1; +#else sdl_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; +#endif } - else if (w == display_mode.w && h == display_mode.h) { + else if (w == display_mode->w && h == display_mode->h) { /* No need to change physical resolution. Borderless fullscreen is preferred when possible */ +#if SDL_VERSION_ATLEAST(3, 0, 0) + sdl_flags |= SDL_WINDOW_FULLSCREEN; + non_desktop_fullscreen = 1; +#else sdl_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; +#endif } else { sdl_flags |= SDL_WINDOW_FULLSCREEN; @@ -1025,15 +1347,19 @@ pg_set_mode(PyObject *self, PyObject *arg, PyObject *kwds) SDL_AddEventWatch(pg_ResizeEventWatch, self); } } +#if !SDL_VERSION_ATLEAST(3, 0, 0) if (flags & PGS_SHOWN) { sdl_flags |= SDL_WINDOW_SHOWN; } +#endif if (flags & PGS_HIDDEN) { sdl_flags |= SDL_WINDOW_HIDDEN; } +#if !SDL_VERSION_ATLEAST(3, 0, 0) if (!(sdl_flags & SDL_WINDOW_HIDDEN)) { sdl_flags |= SDL_WINDOW_SHOWN; } +#endif if (flags & PGS_OPENGL) { /* Must be called before creating context */ if (flags & PGS_DOUBLEBUF) { @@ -1062,9 +1388,14 @@ pg_set_mode(PyObject *self, PyObject *arg, PyObject *kwds) if (win) { if (SDL_GetWindowDisplayIndex(win) == display) { // fullscreen windows don't hold window x and y as needed + +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (SDL_GetWindowFlags(win) & SDL_WINDOW_FULLSCREEN) { +#else if (SDL_GetWindowFlags(win) & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)) { +#endif x = state->fullscreen_backup_x; y = state->fullscreen_backup_y; @@ -1153,8 +1484,12 @@ pg_set_mode(PyObject *self, PyObject *arg, PyObject *kwds) // SDL doesn't preserve window position in fullscreen mode // However, windows coming out of fullscreen need these to go back // into the correct position +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (sdl_flags & SDL_WINDOW_FULLSCREEN) { +#else if (sdl_flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)) { +#endif state->fullscreen_backup_x = x; state->fullscreen_backup_y = y; } @@ -1162,10 +1497,14 @@ pg_set_mode(PyObject *self, PyObject *arg, PyObject *kwds) if (!win) { /*open window*/ if (hwnd != 0) { +/* TODO: figure SDL3 equivalent */ +#if !SDL_VERSION_ATLEAST(3, 0, 0) win = SDL_CreateWindowFrom((void *)hwnd); +#endif } else { - win = SDL_CreateWindow(title, x, y, w_1, h_1, sdl_flags); + win = PG_CreateWindowCompat(title, x, y, w_1, h_1, + sdl_flags); w_actual = w_1; h_actual = h_1; } @@ -1190,12 +1529,20 @@ pg_set_mode(PyObject *self, PyObject *arg, PyObject *kwds) * resize/bordered/hidden changes due to SDL ignoring those * changes if the window is fullscreen * See https://github.com/pygame/pygame/issues/2711 */ +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (0 != PG_SetWindowFullscreen( + win, sdl_flags & SDL_WINDOW_FULLSCREEN, + non_desktop_fullscreen)) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } +#else if (0 != SDL_SetWindowFullscreen( win, sdl_flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP))) { return RAISE(pgExc_SDLError, SDL_GetError()); } +#endif SDL_SetWindowResizable(win, flags & PGS_RESIZABLE); SDL_SetWindowBordered(win, (flags & PGS_NOFRAME) == 0); @@ -1275,10 +1622,12 @@ pg_set_mode(PyObject *self, PyObject *arg, PyObject *kwds) if (flags & PGS_SCALED || state->unscaled_render) { if (pg_renderer == NULL) { - SDL_RendererInfo info; - +#if !SDL_VERSION_ATLEAST(3, 0, 0) + /* TODO: check if this behaviour is still to be retained + * in SDL3 */ SDL_SetHintWithPriority(SDL_HINT_RENDER_SCALE_QUALITY, "nearest", SDL_HINT_DEFAULT); +#endif #if SDL_VERSION_ATLEAST(2, 28, 0) /* If the window has a surface associated with it already, @@ -1289,6 +1638,12 @@ pg_set_mode(PyObject *self, PyObject *arg, PyObject *kwds) } #endif +#if SDL_VERSION_ATLEAST(3, 0, 0) + pg_renderer = SDL_CreateRenderer(win, NULL); + if (vsync && pg_renderer) { + SDL_SetRenderVSync(pg_renderer, 1); + } +#else if (vsync) { pg_renderer = SDL_CreateRenderer( win, -1, SDL_RENDERER_PRESENTVSYNC); @@ -1296,6 +1651,7 @@ pg_set_mode(PyObject *self, PyObject *arg, PyObject *kwds) else { pg_renderer = SDL_CreateRenderer(win, -1, 0); } +#endif if (pg_renderer == NULL) { return RAISE(pgExc_SDLError, SDL_GetError()); @@ -1305,16 +1661,48 @@ pg_set_mode(PyObject *self, PyObject *arg, PyObject *kwds) /* use whole screen with uneven pixels on fullscreen, exact scale otherwise. we chose the window size for this to work */ +#if SDL_VERSION_ATLEAST(3, 0, 0) + int enable_intscale = + !(flags & PGS_FULLSCREEN || + SDL_GetHintBoolean( + "SDL_HINT_RENDER_SCALE_QUALITY", SDL_FALSE)); + SDL_SetRenderLogicalPresentation( + pg_renderer, w, h, + enable_intscale + ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE + : SDL_LOGICAL_PRESENTATION_LETTERBOX); +#else SDL_RenderSetIntegerScale( pg_renderer, !(flags & PGS_FULLSCREEN || SDL_GetHintBoolean( "SDL_HINT_RENDER_SCALE_QUALITY", SDL_FALSE))); SDL_RenderSetLogicalSize(pg_renderer, w, h); +#endif /* this must be called after creating the renderer!*/ SDL_SetWindowMinimumSize(win, w, h); } +#if SDL_VERSION_ATLEAST(3, 0, 0) + int has_vsync = 0; + SDL_GetRenderVSync(pg_renderer, &has_vsync); + if (vsync && has_vsync) { + PyErr_SetString(pgExc_SDLError, + "could not enable vsync"); + _display_state_cleanup(state); + goto DESTROY_WINDOW; + } + const char *name = SDL_GetRendererName(pg_renderer); + if (name && !strcmp(name, SDL_SOFTWARE_RENDERER)) { + if (PyErr_WarnEx(PyExc_Warning, + "no fast renderer available", + 1) != 0) { + _display_state_cleanup(state); + goto DESTROY_WINDOW; + } + } +#else + SDL_RendererInfo info; SDL_GetRendererInfo(pg_renderer, &info); if (vsync && !(info.flags & SDL_RENDERER_PRESENTVSYNC)) { PyErr_SetString(pgExc_SDLError, @@ -1330,6 +1718,7 @@ pg_set_mode(PyObject *self, PyObject *arg, PyObject *kwds) goto DESTROY_WINDOW; } } +#endif pg_texture = SDL_CreateTexture( pg_renderer, SDL_PIXELFORMAT_ARGB8888, @@ -1342,6 +1731,8 @@ pg_set_mode(PyObject *self, PyObject *arg, PyObject *kwds) surf = SDL_GetWindowSurface(win); } } + +#if !SDL_VERSION_ATLEAST(3, 0, 0) if (state->gamma_ramp) { int result = SDL_SetWindowGammaRamp(win, state->gamma_ramp, state->gamma_ramp + 256, @@ -1356,6 +1747,7 @@ pg_set_mode(PyObject *self, PyObject *arg, PyObject *kwds) goto DESTROY_WINDOW; } } +#endif if (state->using_gl && pg_renderer != NULL) { _display_state_cleanup(state); @@ -1391,7 +1783,11 @@ pg_set_mode(PyObject *self, PyObject *arg, PyObject *kwds) Py_DECREF(surface); /* ensure window is always black after a set_mode call */ +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_FillRect(surf, NULL, SDL_MapSurfaceRGB(surf, 0, 0, 0)); +#else SDL_FillRect(surf, NULL, SDL_MapRGB(surf->format, 0, 0, 0)); +#endif } /*set the window icon*/ @@ -1437,7 +1833,11 @@ pg_set_mode(PyObject *self, PyObject *arg, PyObject *kwds) !vsync && (((flags & PGS_RESIZABLE) == 0) || !zero_size)) { if (((surface->surf->w != w_actual) || (surface->surf->h != h_actual)) && +#if SDL_VERSION_ATLEAST(3, 0, 0) + ((surface->surf->flags & SDL_WINDOW_FULLSCREEN) != 0)) { +#else ((surface->surf->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0)) { +#endif char buffer[150]; char *format_string = "Requested window was forcibly resized by the OS.\n\t" @@ -1557,7 +1957,11 @@ pg_mode_ok(PyObject *self, PyObject *args, PyObject *kwds) { SDL_DisplayMode desired, closest; int bpp = 0; +#if SDL_VERSION_ATLEAST(3, 0, 0) + int flags = 0; +#else int flags = SDL_SWSURFACE; +#endif int display_index = 0; char *keywords[] = {"size", "flags", "depth", "display", NULL}; @@ -1569,14 +1973,17 @@ pg_mode_ok(PyObject *self, PyObject *args, PyObject *kwds) &display_index)) { return NULL; } +#if !SDL_VERSION_ATLEAST(3, 0, 0) + /* Display ID is not bounded by number of displays in SDL3 */ if (display_index < 0 || display_index >= SDL_GetNumVideoDisplays()) { return RAISE(PyExc_ValueError, "The display index must be between 0" " and the number of displays."); } + desired.driverdata = 0; +#endif #pragma PG_WARN(Ignoring most flags) - desired.driverdata = 0; desired.refresh_rate = 0; if (bpp == 0) { @@ -1591,7 +1998,14 @@ pg_mode_ok(PyObject *self, PyObject *args, PyObject *kwds) desired.format = SDL_MasksToPixelFormatEnum(bpp, Rmask, Gmask, Bmask, 0); } + +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (!SDL_GetClosestFullscreenDisplayMode(display_index, desired.w, + desired.h, desired.refresh_rate, + 1, &closest)) { +#else if (!SDL_GetClosestDisplayMode(display_index, &desired, &closest)) { +#endif if (flags & PGS_FULLSCREEN) { return PyLong_FromLong((long)0); } @@ -1625,13 +2039,32 @@ pg_list_modes(PyObject *self, PyObject *args, PyObject *kwds) return NULL; } +#if !SDL_VERSION_ATLEAST(3, 0, 0) + /* Display ID is not bounded by number of displays in SDL3 */ if (display_index < 0 || display_index >= SDL_GetNumVideoDisplays()) { return RAISE(PyExc_ValueError, "The display index must be between 0" " and the number of displays."); } +#endif + #pragma PG_WARN(Ignoring flags) +#if SDL_VERSION_ATLEAST(3, 0, 0) + if (bpp == 0) { + const SDL_DisplayMode *curmode; + if (!(curmode = SDL_GetCurrentDisplayMode(display_index))) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } + bpp = SDL_BITSPERPIXEL(curmode->format); + } + + SDL_DisplayMode **modes = + SDL_GetFullscreenDisplayModes(display_index, &nummodes); + if (nummodes < 0) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } +#else if (bpp == 0) { SDL_DisplayMode curmode; if (SDL_GetCurrentDisplayMode(display_index, &curmode)) { @@ -1644,16 +2077,21 @@ pg_list_modes(PyObject *self, PyObject *args, PyObject *kwds) if (nummodes < 0) { return RAISE(pgExc_SDLError, SDL_GetError()); } +#endif if (!(list = PyList_New(0))) { return NULL; } for (i = 0; i < nummodes; i++) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + mode = *modes[i]; +#else if (SDL_GetDisplayMode(display_index, i, &mode) < 0) { Py_DECREF(list); return RAISE(pgExc_SDLError, SDL_GetError()); } +#endif /* use reasonable defaults (cf. SDL_video.c) */ if (!mode.format) { mode.format = SDL_PIXELFORMAT_XRGB8888; @@ -1684,6 +2122,9 @@ pg_list_modes(PyObject *self, PyObject *args, PyObject *kwds) } Py_DECREF(size); } +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_free(modes); +#endif return list; } @@ -1754,7 +2195,12 @@ pg_flip(PyObject *self, PyObject *_null) static PyObject * pg_num_displays(PyObject *self, PyObject *_null) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + int ret = -1; + SDL_free(SDL_GetDisplays(&ret)); +#else int ret = SDL_GetNumVideoDisplays(); +#endif if (ret < 0) { return RAISE(pgExc_SDLError, SDL_GetError()); } @@ -1936,7 +2382,7 @@ pg_set_palette(PyObject *self, PyObject *args) Py_INCREF(surface); surf = pgSurface_AsSurface(surface); - pal = surf->format->palette; + pal = PG_GetSurfacePalette(surf); if (PG_SURF_BytesPerPixel(surf) != 1 || !pal) { Py_DECREF(surface); return RAISE(pgExc_SDLError, "Display mode is not colormapped"); @@ -2011,6 +2457,24 @@ pg_set_palette(PyObject *self, PyObject *args) Py_RETURN_NONE; } +#if SDL_VERSION_ATLEAST(3, 0, 0) +static PyObject * +pg_set_gamma(PyObject *self, PyObject *arg) +{ + return RAISE(pgExc_SDLError, + "set_gamma not supported with SDL3, this functionality has " + "been removed."); +} + +static PyObject * +pg_set_gamma_ramp(PyObject *self, PyObject *arg) +{ + return RAISE( + pgExc_SDLError, + "pg_set_gamma_ramp not supported with SDL3, this functionality has " + "been removed."); +} +#else static PyObject * pg_set_gamma(PyObject *self, PyObject *arg) { @@ -2150,6 +2614,7 @@ pg_set_gamma_ramp(PyObject *self, PyObject *arg) } return PyBool_FromLong(result == 0); } +#endif static PyObject * pg_set_caption(PyObject *self, PyObject *arg) @@ -2246,6 +2711,21 @@ pg_iconify(PyObject *self, PyObject *_null) static PyObject * pg_get_scaled_renderer_info(PyObject *self, PyObject *_null) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + VIDEO_INIT_CHECK(); + if (!pg_renderer) { + Py_RETURN_NONE; + } + + const char *name = SDL_GetRendererName(pg_renderer); + if (!name) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } + + /* flags are not being handled here but it shouldn't matter as this is + * undocumented API */ + return Py_BuildValue("(si)", name, 0); +#else SDL_RendererInfo r_info; VIDEO_INIT_CHECK(); @@ -2258,21 +2738,28 @@ pg_get_scaled_renderer_info(PyObject *self, PyObject *_null) } return Py_BuildValue("(si)", r_info.name, r_info.flags); +#endif } static PyObject * pg_get_desktop_screen_sizes(PyObject *self, PyObject *_null) { int display_count, i; - SDL_DisplayMode dm; PyObject *result, *size_tuple; VIDEO_INIT_CHECK(); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DisplayID *displays = SDL_GetDisplays(&display_count); + if (!displays) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } +#else display_count = SDL_GetNumVideoDisplays(); if (display_count < 0) { return RAISE(pgExc_SDLError, SDL_GetError()); } +#endif result = PyList_New(display_count); if (!result) { @@ -2280,12 +2767,21 @@ pg_get_desktop_screen_sizes(PyObject *self, PyObject *_null) } for (i = 0; i < display_count; i++) { - if (SDL_GetDesktopDisplayMode(i, &dm)) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_DisplayMode *dm = SDL_GetDesktopDisplayMode(displays[i]); + if (!dm) { Py_DECREF(result); return RAISE(pgExc_SDLError, SDL_GetError()); } - - size_tuple = pg_tuple_couple_from_values_int(dm.w, dm.h); +#else + SDL_DisplayMode dm_local; + SDL_DisplayMode *dm = &dm_local; + if (SDL_GetDesktopDisplayMode(i, dm)) { + Py_DECREF(result); + return RAISE(pgExc_SDLError, SDL_GetError()); + } +#endif + size_tuple = pg_tuple_couple_from_values_int(dm->w, dm->h); if (!size_tuple) { Py_DECREF(result); return NULL; @@ -2293,6 +2789,9 @@ pg_get_desktop_screen_sizes(PyObject *self, PyObject *_null) PyList_SET_ITEM(result, i, size_tuple); } +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_free(displays); +#endif return result; } @@ -2300,14 +2799,18 @@ static PyObject * pg_is_fullscreen(PyObject *self, PyObject *_null) { SDL_Window *win = pg_GetDefaultWindow(); - int flags; + SDL_WindowFlags flags; VIDEO_INIT_CHECK(); if (!win) { return RAISE(pgExc_SDLError, "No open window"); } +#if SDL_VERSION_ATLEAST(3, 0, 0) + flags = SDL_GetWindowFlags(win); +#else flags = SDL_GetWindowFlags(win) & SDL_WINDOW_FULLSCREEN_DESKTOP; +#endif if (flags & SDL_WINDOW_FULLSCREEN) { Py_RETURN_TRUE; @@ -2329,6 +2832,19 @@ pg_is_vsync(PyObject *self, PyObject *_null) } if (pg_renderer != NULL) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + int has_vsync = 0; + if (!SDL_GetRenderVSync(pg_renderer, &has_vsync)) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } + + if (has_vsync) { + Py_RETURN_TRUE; + } + else { + Py_RETURN_FALSE; + } +#else SDL_RendererInfo info; if (SDL_GetRendererInfo(pg_renderer, &info) != 0) { @@ -2341,15 +2857,29 @@ pg_is_vsync(PyObject *self, PyObject *_null) else { Py_RETURN_FALSE; } +#endif } if (state->using_gl) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + int interval; + if (!SDL_GL_GetSwapInterval(&interval)) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } + if (interval != 0) { + Py_RETURN_TRUE; + } + else { + Py_RETURN_FALSE; + } +#else if (SDL_GL_GetSwapInterval() != 0) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } +#endif } Py_RETURN_FALSE; @@ -2359,7 +2889,6 @@ static PyObject * pg_current_refresh_rate(PyObject *self, PyObject *_null) { SDL_Window *win = pg_GetDefaultWindow(); - SDL_DisplayMode mode; int display_index; VIDEO_INIT_CHECK(); @@ -2372,26 +2901,42 @@ pg_current_refresh_rate(PyObject *self, PyObject *_null) return RAISE(pgExc_SDLError, SDL_GetError()); } +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_DisplayMode *mode = SDL_GetCurrentDisplayMode(display_index); + if (!mode) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } + /* TODO: Consider returning a float here in an API breaking release */ + return PyLong_FromLong((long)mode->refresh_rate); +#else + SDL_DisplayMode mode; if (SDL_GetCurrentDisplayMode(display_index, &mode) != 0) { return RAISE(pgExc_SDLError, SDL_GetError()); } return PyLong_FromLong(mode.refresh_rate); +#endif } static PyObject * pg_desktop_refresh_rates(PyObject *self, PyObject *_null) { int display_count, i; - SDL_DisplayMode dm; PyObject *result, *refresh_rate; VIDEO_INIT_CHECK(); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_DisplayID *displays = SDL_GetDisplays(&display_count); + if (!displays) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } +#else display_count = SDL_GetNumVideoDisplays(); if (display_count < 0) { return RAISE(pgExc_SDLError, SDL_GetError()); } +#endif result = PyList_New(display_count); if (!result) { @@ -2399,12 +2944,23 @@ pg_desktop_refresh_rates(PyObject *self, PyObject *_null) } for (i = 0; i < display_count; i++) { - if (SDL_GetDesktopDisplayMode(i, &dm)) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_DisplayMode *dm = SDL_GetDesktopDisplayMode(displays[i]); + if (!dm) { + Py_DECREF(result); + return RAISE(pgExc_SDLError, SDL_GetError()); + } +#else + SDL_DisplayMode dm_local; + SDL_DisplayMode *dm = &dm_local; + if (SDL_GetDesktopDisplayMode(i, dm)) { Py_DECREF(result); return RAISE(pgExc_SDLError, SDL_GetError()); } +#endif - refresh_rate = PyLong_FromLong(dm.refresh_rate); + /* TODO: Consider returning a float here in an API breaking release */ + refresh_rate = PyLong_FromLong((long)dm->refresh_rate); if (!refresh_rate) { Py_DECREF(result); return NULL; @@ -2412,21 +2968,81 @@ pg_desktop_refresh_rates(PyObject *self, PyObject *_null) PyList_SET_ITEM(result, i, refresh_rate); } +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_free(displays); +#endif return result; } +#if SDL_VERSION_ATLEAST(3, 0, 0) +typedef enum { + SDL_SYSWM_UNKNOWN, + SDL_SYSWM_WINDOWS, + SDL_SYSWM_X11, + SDL_SYSWM_DIRECTFB, + SDL_SYSWM_COCOA, + SDL_SYSWM_UIKIT, + SDL_SYSWM_WAYLAND, + SDL_SYSWM_MIR, /* no longer available, left for API/ABI compatibility. */ + SDL_SYSWM_WINRT, + SDL_SYSWM_ANDROID, + SDL_SYSWM_VIVANTE, + SDL_SYSWM_OS2, + SDL_SYSWM_HAIKU, + SDL_SYSWM_KMSDRM, + SDL_SYSWM_RISCOS +} SDL_SYSWM_TYPE; + +static SDL_SYSWM_TYPE +get_syswm_type() +{ + const char *driver = SDL_GetCurrentVideoDriver(); + if (!driver) { + return SDL_SYSWM_UNKNOWN; + } + + if (strcmp(driver, "android") == 0) { + return SDL_SYSWM_ANDROID; + } + else if (strcmp(driver, "cocoa") == 0) { + return SDL_SYSWM_COCOA; + } + else if (strcmp(driver, "kmsdrm") == 0) { + return SDL_SYSWM_KMSDRM; + } + else if (strcmp(driver, "uikit") == 0) { + return SDL_SYSWM_UIKIT; + } + else if (strcmp(driver, "vivante") == 0) { + return SDL_SYSWM_VIVANTE; + } + else if (strcmp(driver, "wayland") == 0) { + return SDL_SYSWM_WAYLAND; + } + else if (strcmp(driver, "windows") == 0) { + return SDL_SYSWM_WINDOWS; + } + else if (strcmp(driver, "x11") == 0) { + return SDL_SYSWM_X11; + } + else { + return SDL_SYSWM_UNKNOWN; + } +} +#endif + static PyObject * pg_toggle_fullscreen(PyObject *self, PyObject *_null) { SDL_Window *win = pg_GetDefaultWindow(); - int result, flags; + int result; + SDL_WindowFlags flags; int window_w, window_h, w, h, window_display, x, y; - SDL_DisplayMode display_mode; pgSurfaceObject *display_surface; _DisplayState *state = DISPLAY_MOD_STATE(self); GL_glViewport_Func p_glViewport = NULL; - SDL_SysWMinfo wm_info; - SDL_RendererInfo r_info; + SDL_SYSWM_TYPE subsystem; + int is_renderer_software = 0; VIDEO_INIT_CHECK(); if (!win) { @@ -2435,10 +3051,16 @@ pg_toggle_fullscreen(PyObject *self, PyObject *_null) flags = SDL_GetWindowFlags(win); +#if SDL_VERSION_ATLEAST(3, 0, 0) + subsystem = get_syswm_type(); +#else + SDL_SysWMinfo wm_info; SDL_VERSION(&wm_info.version); if (!SDL_GetWindowWMInfo(win, &wm_info)) { return RAISE(pgExc_SDLError, SDL_GetError()); } + subsystem = wm_info.subsystem; +#endif if (state->using_gl && pg_renderer != NULL) { return RAISE(pgExc_SDLError, @@ -2446,12 +3068,22 @@ pg_toggle_fullscreen(PyObject *self, PyObject *_null) } if (pg_renderer != NULL) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + const char *r_name; + if (!(r_name = SDL_GetRendererName(pg_renderer))) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } + is_renderer_software = !strcmp(r_name, SDL_SOFTWARE_RENDERER); +#else + SDL_RendererInfo r_info; if (SDL_GetRendererInfo(pg_renderer, &r_info) != 0) { return RAISE(pgExc_SDLError, SDL_GetError()); } + is_renderer_software = r_info.flags & SDL_RENDERER_SOFTWARE; +#endif } - switch (wm_info.subsystem) { + switch (subsystem) { // if we get this to work correctly with more systems, move them here case SDL_SYSWM_WINDOWS: case SDL_SYSWM_X11: @@ -2497,10 +3129,19 @@ pg_toggle_fullscreen(PyObject *self, PyObject *_null) SDL_GetWindowSize(win, &window_w, &window_h); SDL_GetWindowPosition(win, &x, &y); window_display = SDL_GetWindowDisplayIndex(win); - if (SDL_GetDesktopDisplayMode(window_display, &display_mode) != 0) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + const SDL_DisplayMode *display_mode = + SDL_GetDesktopDisplayMode(window_display); + if (!display_mode) { return RAISE(pgExc_SDLError, SDL_GetError()); } - +#else + SDL_DisplayMode dm_local; + SDL_DisplayMode *display_mode = &dm_local; + if (SDL_GetDesktopDisplayMode(window_display, display_mode)) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } +#endif /* if (pg_renderer != NULL) { SDL_RenderGetLogicalSize(pg_renderer, &w, &h); @@ -2508,7 +3149,11 @@ pg_toggle_fullscreen(PyObject *self, PyObject *_null) */ if (state->using_gl) { p_glViewport = (GL_glViewport_Func)SDL_GL_GetProcAddress("glViewport"); +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_GetWindowSizeInPixels(win, &w, &h); +#else SDL_GL_GetDrawableSize(win, &w, &h); +#endif } else { w = display_surface->surf->w; @@ -2540,23 +3185,35 @@ pg_toggle_fullscreen(PyObject *self, PyObject *_null) } SDL_SetWindowSize(win, w * scale, h * scale); - if (r_info.flags & SDL_RENDERER_SOFTWARE && - wm_info.subsystem == SDL_SYSWM_X11) { + if (is_renderer_software && subsystem == SDL_SYSWM_X11) { /* display surface lost? */ SDL_DestroyTexture(pg_texture); SDL_DestroyRenderer(pg_renderer); +#if SDL_VERSION_ATLEAST(3, 0, 0) + pg_renderer = SDL_CreateRenderer(win, SDL_SOFTWARE_RENDERER); +#else pg_renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_SOFTWARE); +#endif pg_texture = SDL_CreateTexture(pg_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, w, h); } +#if SDL_VERSION_ATLEAST(3, 0, 0) + int enable_intscale = !SDL_GetHintBoolean( + "SDL_HINT_RENDER_SCALE_QUALITY", SDL_FALSE); + SDL_SetRenderLogicalPresentation( + pg_renderer, w, h, + enable_intscale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE + : SDL_LOGICAL_PRESENTATION_LETTERBOX); +#else SDL_RenderSetLogicalSize(pg_renderer, w, h); /* use exact integer scale in windowed mode */ SDL_RenderSetIntegerScale( pg_renderer, !SDL_GetHintBoolean( "SDL_HINT_RENDER_SCALE_QUALITY", SDL_FALSE)); +#endif SDL_SetWindowMinimumSize(win, w, h); } else if (state->using_gl) { @@ -2574,7 +3231,7 @@ pg_toggle_fullscreen(PyObject *self, PyObject *_null) ((float)state->toggle_windowed_w) / (float)state->toggle_windowed_h; float window_aspect_ratio = - ((float)display_mode.w) / (float)display_mode.h; + ((float)display_mode->w) / (float)display_mode->h; if (window_aspect_ratio > saved_aspect_ratio) { int width = (int)(state->toggle_windowed_h * @@ -2594,15 +3251,19 @@ pg_toggle_fullscreen(PyObject *self, PyObject *_null) } } } +#if SDL_VERSION_ATLEAST(3, 0, 0) + else if ((flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN) { +#else else if ((flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) { +#endif result = SDL_SetWindowFullscreen(win, 0); if (result != 0) { return RAISE(pgExc_SDLError, SDL_GetError()); } display_surface->surf = SDL_GetWindowSurface(win); } - else if (wm_info.subsystem == SDL_SYSWM_X11) { + else if (subsystem == SDL_SYSWM_X11) { /* This is a HACK, specifically to work around faulty behaviour of * SDL_SetWindowFullscreen on X11 when switching out of fullscreen * would change the physical resolution of the display back to the @@ -2616,9 +3277,13 @@ pg_toggle_fullscreen(PyObject *self, PyObject *_null) 1) != 0) { return NULL; } +#if SDL_VERSION_ATLEAST(3, 0, 0) + flags &= ~SDL_WINDOW_FULLSCREEN; +#else flags &= ~SDL_WINDOW_FULLSCREEN_DESKTOP; +#endif /* SDL_WINDOW_FULLSCREEN_DESKTOP includes SDL_WINDOW_FULLSCREEN */ - win = SDL_CreateWindow(state->title, wx, wy, w, h, flags); + win = PG_CreateWindowCompat(state->title, wx, wy, w, h, flags); if (win == NULL) { return RAISE(pgExc_SDLError, SDL_GetError()); } @@ -2665,20 +3330,27 @@ pg_toggle_fullscreen(PyObject *self, PyObject *_null) state->fullscreen_backup_y = y; if (state->unscaled_render) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + result = PG_SetWindowFullscreen(win, 1, 0); +#else result = SDL_SetWindowFullscreen(win, SDL_WINDOW_FULLSCREEN_DESKTOP); +#endif if (result != 0) { return RAISE(pgExc_SDLError, SDL_GetError()); } } else if (pg_renderer != NULL) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + result = PG_SetWindowFullscreen(win, 1, 0); +#else result = SDL_SetWindowFullscreen(win, SDL_WINDOW_FULLSCREEN_DESKTOP); +#endif if (result != 0) { return RAISE(pgExc_SDLError, SDL_GetError()); } - if (r_info.flags & SDL_RENDERER_SOFTWARE && - wm_info.subsystem == SDL_SYSWM_X11) { + if (is_renderer_software && subsystem == SDL_SYSWM_X11) { if (PyErr_WarnEx( PyExc_Warning, "recreating software renderer in toggle_fullscreen", @@ -2688,19 +3360,32 @@ pg_toggle_fullscreen(PyObject *self, PyObject *_null) /* display surface lost? only on x11? */ SDL_DestroyTexture(pg_texture); SDL_DestroyRenderer(pg_renderer); +#if SDL_VERSION_ATLEAST(3, 0, 0) + pg_renderer = SDL_CreateRenderer(win, SDL_SOFTWARE_RENDERER); +#else pg_renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_SOFTWARE); +#endif pg_texture = SDL_CreateTexture(pg_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, w, h); } +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_SetRenderLogicalPresentation( + pg_renderer, w, h, SDL_LOGICAL_PRESENTATION_LETTERBOX); +#else SDL_RenderSetLogicalSize(pg_renderer, w, h); SDL_RenderSetIntegerScale(pg_renderer, SDL_FALSE); +#endif } else if (state->using_gl) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + result = PG_SetWindowFullscreen(win, 1, 0); +#else result = SDL_SetWindowFullscreen(win, SDL_WINDOW_FULLSCREEN_DESKTOP); +#endif if (result != 0) { return RAISE(pgExc_SDLError, SDL_GetError()); } @@ -2709,31 +3394,35 @@ pg_toggle_fullscreen(PyObject *self, PyObject *_null) float saved_aspect_ratio = ((float)state->scaled_gl_w) / (float)state->scaled_gl_h; float window_aspect_ratio = - ((float)display_mode.w) / (float)display_mode.h; + ((float)display_mode->w) / (float)display_mode->h; if (window_aspect_ratio > saved_aspect_ratio) { - int width = (int)(display_mode.h * saved_aspect_ratio); - p_glViewport((display_mode.w - width) / 2, 0, width, - display_mode.h); + int width = (int)(display_mode->h * saved_aspect_ratio); + p_glViewport((display_mode->w - width) / 2, 0, width, + display_mode->h); } else { - p_glViewport(0, 0, display_mode.w, - (int)(display_mode.w / saved_aspect_ratio)); + p_glViewport(0, 0, display_mode->w, + (int)(display_mode->w / saved_aspect_ratio)); } } else { - p_glViewport(0, 0, display_mode.w, display_mode.h); + p_glViewport(0, 0, display_mode->w, display_mode->h); } } - else if (w == display_mode.w && h == display_mode.h) { + else if (w == display_mode->w && h == display_mode->h) { +#if SDL_VERSION_ATLEAST(3, 0, 0) + result = PG_SetWindowFullscreen(win, 1, 0); +#else result = SDL_SetWindowFullscreen(win, SDL_WINDOW_FULLSCREEN_DESKTOP); +#endif if (result != 0) { return RAISE(pgExc_SDLError, SDL_GetError()); } display_surface->surf = SDL_GetWindowSurface(win); } - else if (wm_info.subsystem == SDL_SYSWM_WAYLAND) { + else if (subsystem == SDL_SYSWM_WAYLAND) { /* This only happens AFTER other options have been exhausted. * with GL, Renderer, or the correct window size, toggling works. * Only entering a hard fullscreen state is unsupported. */ @@ -2754,7 +3443,7 @@ pg_toggle_fullscreen(PyObject *self, PyObject *_null) h != display_surface->surf->h) { int wx = SDL_WINDOWPOS_UNDEFINED_DISPLAY(window_display); int wy = SDL_WINDOWPOS_UNDEFINED_DISPLAY(window_display); - win = SDL_CreateWindow(state->title, wx, wy, w, h, flags); + win = PG_CreateWindowCompat(state->title, wx, wy, w, h, flags); if (win == NULL) { return RAISE(pgExc_SDLError, SDL_GetError()); } @@ -2787,9 +3476,8 @@ pg_display_resize_event(PyObject *self, PyObject *event) int wnew = PyLong_AsLong(PyObject_GetAttrString(event, "w")); int hnew = PyLong_AsLong(PyObject_GetAttrString(event, "h")); SDL_Window *win = pg_GetDefaultWindow(); - int flags; - int window_w, window_h, w, h, window_display, result; - SDL_DisplayMode display_mode; + SDL_WindowFlags flags; + int w, h, result; _DisplayState *state = DISPLAY_MOD_STATE(self); GL_glViewport_Func p_glViewport = NULL; @@ -2797,21 +3485,18 @@ pg_display_resize_event(PyObject *self, PyObject *event) if (!win) { return RAISE(pgExc_SDLError, "No open window"); } +#if SDL_VERSION_ATLEAST(3, 0, 0) + flags = SDL_GetWindowFlags(win) & SDL_WINDOW_FULLSCREEN; +#else flags = SDL_GetWindowFlags(win) & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP); +#endif if (flags) { return PyLong_FromLong(-1); } - // could also take the size of the old display surface - SDL_GetWindowSize(win, &window_w, &window_h); - window_display = SDL_GetWindowDisplayIndex(win); - if (SDL_GetDesktopDisplayMode(window_display, &display_mode) != 0) { - return RAISE(pgExc_SDLError, SDL_GetError()); - } - if (state->using_gl) { p_glViewport = (GL_glViewport_Func)SDL_GL_GetProcAddress("glViewport"); SDL_SetWindowSize(win, wnew, hnew); @@ -2834,9 +3519,18 @@ pg_display_resize_event(PyObject *self, PyObject *event) } } else if (pg_renderer != NULL) { + /* TODO: verify why this block exists and whether SDL3 port is + * equivalent */ +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_RendererLogicalPresentation mode; + SDL_GetRenderLogicalPresentation(pg_renderer, &w, &h, &mode); + SDL_SetWindowSize(win, (w > wnew) ? w : wnew, (h > hnew) ? h : hnew); + result = SDL_SetRenderLogicalPresentation(pg_renderer, w, h, mode); +#else SDL_RenderGetLogicalSize(pg_renderer, &w, &h); SDL_SetWindowSize(win, (w > wnew) ? w : wnew, (h > hnew) ? h : hnew); result = SDL_RenderSetLogicalSize(pg_renderer, w, h); +#endif if (result != 0) { return RAISE(pgExc_SDLError, SDL_GetError()); } @@ -2967,7 +3661,11 @@ pg_message_box(PyObject *self, PyObject *arg, PyObject *kwargs) if (buttons == NULL) { buttons_data = malloc(sizeof(SDL_MessageBoxButtonData)); buttons_data->flags = 0; +#if SDL_VERSION_ATLEAST(3, 0, 0) + buttons_data->buttonID = 0; +#else buttons_data->buttonid = 0; +#endif buttons_data->text = "OK"; msgbox_data.numbuttons = 1; @@ -3041,6 +3739,19 @@ pg_message_box(PyObject *self, PyObject *arg, PyObject *kwargs) } buttons_data[i].text = btn_name; +#if SDL_VERSION_ATLEAST(3, 0, 0) + buttons_data[i].buttonID = (int)i; + buttons_data[i].flags = 0; + if (return_button_index == buttons_data[i].buttonID) { + buttons_data[i].flags |= + SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT; + } + if (escape_button_used && + escape_button_index == buttons_data[i].buttonID) { + buttons_data[i].flags |= + SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT; + } +#else buttons_data[i].buttonid = (int)i; buttons_data[i].flags = 0; if (return_button_index == buttons_data[i].buttonid) { @@ -3052,6 +3763,7 @@ pg_message_box(PyObject *self, PyObject *arg, PyObject *kwargs) buttons_data[i].flags |= SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT; } +#endif } } @@ -3207,7 +3919,9 @@ MODINIT_DEFINE(display) state = DISPLAY_MOD_STATE(module); state->title = NULL; state->icon = NULL; +#if !SDL_VERSION_ATLEAST(3, 0, 0) state->gamma_ramp = NULL; +#endif state->using_gl = 0; state->auto_resize = SDL_TRUE; diff --git a/src_c/include/_pygame.h b/src_c/include/_pygame.h index 6f9942cb6c..7045c49d54 100644 --- a/src_c/include/_pygame.h +++ b/src_c/include/_pygame.h @@ -284,8 +284,14 @@ typedef struct { Uint32 blit_sw_A : 1; Uint32 blit_fill : 1; Uint32 video_mem; +#if PG_SDL3 + /* We cannot use PG_PixelFormat here because it aliases to const */ + SDL_PixelFormatDetails *vfmt; + SDL_PixelFormatDetails vfmt_data; +#else SDL_PixelFormat *vfmt; SDL_PixelFormat vfmt_data; +#endif int current_w; int current_h; } pg_VideoInfo; diff --git a/src_c/meson.build b/src_c/meson.build index 0c1479a812..ff66e90f58 100644 --- a/src_c/meson.build +++ b/src_c/meson.build @@ -27,8 +27,6 @@ constants = py.extension_module( subdir: pg, ) -# TODO: support SDL3 -if sdl_api != 3 display = py.extension_module( 'display', 'display.c', @@ -37,7 +35,6 @@ display = py.extension_module( install: true, subdir: pg, ) -endif event = py.extension_module( 'event', diff --git a/src_c/window.c b/src_c/window.c index f2976df502..757d54109d 100644 --- a/src_c/window.c +++ b/src_c/window.c @@ -331,6 +331,10 @@ pg_window_set_fullscreen(SDL_Window *window, int desktop) goto end; } } + + if (!SDL_SetWindowFullscreen(window, 1)) { + goto end; + } if (!SDL_SetWindowFullscreenMode(window, chosen_mode)) { goto end; }