From 21bd3bacd94e3198c9d9d9a381d1b7aa27eba841 Mon Sep 17 00:00:00 2001 From: yunline Date: Wed, 26 Jul 2023 16:17:00 +0800 Subject: [PATCH 01/17] Add `get_surface()` and `update_from_surface` --- src_c/include/_pygame.h | 1 + src_c/window.c | 52 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src_c/include/_pygame.h b/src_c/include/_pygame.h index 88d4fc0db0..040d90ac47 100644 --- a/src_c/include/_pygame.h +++ b/src_c/include/_pygame.h @@ -474,6 +474,7 @@ typedef struct pgColorObject pgColorObject; typedef struct { PyObject_HEAD SDL_Window *_win; SDL_bool _is_borrowed; + pgSurfaceObject *surf; } pgWindowObject; #ifndef PYGAMEAPI_WINDOW_INTERNAL diff --git a/src_c/window.c b/src_c/window.c index 21535a8433..11bed3ac34 100644 --- a/src_c/window.c +++ b/src_c/window.c @@ -127,6 +127,54 @@ window_destroy(pgWindowObject *self, PyObject *_null) Py_RETURN_NONE; } +static PyObject * +window_get_surface(pgWindowObject *self) +{ + PyObject *surf = NULL; + SDL_Surface *_surf = SDL_GetWindowSurface(self->_win); + if (!_surf) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } + surf = (PyObject *)pgSurface_New2(_surf, SDL_FALSE); + if (!surf) { + return NULL; + } + Py_INCREF(surf); + Py_XDECREF(self->surf); + self->surf = (pgSurfaceObject *)surf; + Py_INCREF(surf); + return surf; +} + +static PyObject * +window_update_from_surface(pgWindowObject *self, PyObject *const *args, + Py_ssize_t nargs) +{ + int i; + SDL_Rect *rects = NULL, tmp, *r; + if (nargs == 0) { + if (SDL_UpdateWindowSurface(self->_win)) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } + } + else { + rects = malloc(nargs * sizeof(SDL_Rect)); + for (i = 0; i < nargs; i++) { + r = pgRect_FromObject(args[i], &tmp); + if (!r) { + return RAISE(PyExc_TypeError, + "Arguments must be rect or rect-like objects."); + } + rects[i] = *r; + if (SDL_UpdateWindowSurfaceRects(self->_win, rects, (int)nargs)) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } + } + free(rects); + } + Py_RETURN_NONE; +} + static PyObject * window_set_windowed(pgWindowObject *self, PyObject *_null) { @@ -739,6 +787,7 @@ window_init(pgWindowObject *self, PyObject *args, PyObject *kwargs) } self->_win = _win; self->_is_borrowed = SDL_FALSE; + self->surf = NULL; SDL_SetWindowData(_win, "pg_window", self); @@ -827,6 +876,9 @@ static PyMethodDef window_methods[] = { DOC_SDL2_VIDEO_WINDOW_SETMODALFOR}, {"set_icon", (PyCFunction)window_set_icon, METH_O, DOC_SDL2_VIDEO_WINDOW_SETICON}, + {"update_from_surface", (PyCFunction)window_update_from_surface, + METH_FASTCALL, "docs"}, + {"get_surface", (PyCFunction)window_get_surface, METH_NOARGS, "docs"}, {"from_display_module", (PyCFunction)window_from_display_module, METH_CLASS | METH_NOARGS, DOC_SDL2_VIDEO_WINDOW_FROMDISPLAYMODULE}, {NULL, NULL, 0, NULL}}; From b16dbb724a8d8e0ebc8f3428dafad4c795bf57a5 Mon Sep 17 00:00:00 2001 From: yunline Date: Wed, 26 Jul 2023 19:50:24 +0800 Subject: [PATCH 02/17] update stubs --- buildconfig/stubs/pygame/_window.pyi | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/buildconfig/stubs/pygame/_window.pyi b/buildconfig/stubs/pygame/_window.pyi index bc17adc3d5..1de76d11de 100644 --- a/buildconfig/stubs/pygame/_window.pyi +++ b/buildconfig/stubs/pygame/_window.pyi @@ -1,6 +1,6 @@ from typing import Optional, Tuple, Union, final -from pygame._common import Coordinate +from pygame._common import Coordinate, RectValue from pygame.locals import WINDOWPOS_UNDEFINED from pygame.surface import Surface @@ -25,7 +25,8 @@ class Window: def minimize(self) -> None: ... def set_modal_for(self, parent: Window) -> None: ... def set_icon(self, icon: Surface) -> None: ... - + def get_surface(self) -> Surface: ... + def update_from_surface(self, *rects: RectValue) -> None: ... grab: bool title: str resizable: bool From e49540f4cecdace7582d750165cefc40327c404c Mon Sep 17 00:00:00 2001 From: yunline Date: Wed, 26 Jul 2023 23:05:11 +0800 Subject: [PATCH 03/17] Update window.c --- src_c/window.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src_c/window.c b/src_c/window.c index 11bed3ac34..b9efc4574f 100644 --- a/src_c/window.c +++ b/src_c/window.c @@ -131,19 +131,30 @@ static PyObject * window_get_surface(pgWindowObject *self) { PyObject *surf = NULL; - SDL_Surface *_surf = SDL_GetWindowSurface(self->_win); + SDL_Surface *_surf; + + if(self->_is_borrowed){ + surf = pg_GetDefaultWindowSurface(); + if(!surf){ + return RAISE(pgExc_SDLError, + "display.set_mode has not been called yet."); + } + Py_INCREF(surf); + return surf; + } + + _surf = SDL_GetWindowSurface(self->_win); if (!_surf) { return RAISE(pgExc_SDLError, SDL_GetError()); } - surf = (PyObject *)pgSurface_New2(_surf, SDL_FALSE); - if (!surf) { - return NULL; + if(self->surf == NULL){ + self->surf = pgSurface_New2(_surf, SDL_FALSE); + if (!self->surf) + return NULL; } - Py_INCREF(surf); - Py_XDECREF(self->surf); - self->surf = (pgSurfaceObject *)surf; - Py_INCREF(surf); - return surf; + self->surf->surf = _surf; + Py_INCREF(self->surf); + return self->surf; } static PyObject * @@ -601,6 +612,7 @@ window_dealloc(pgWindowObject *self, PyObject *_null) SDL_SetWindowData(self->_win, "pg_window", NULL); } } + Py_XDECREF(self->surf); Py_TYPE(self)->tp_free(self); } From a4ceb6b8a85d1f3304e7aced8b597d2ed6d8872e Mon Sep 17 00:00:00 2001 From: yunline Date: Thu, 27 Jul 2023 00:25:40 +0800 Subject: [PATCH 04/17] Fix segfault --- src_c/window.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src_c/window.c b/src_c/window.c index b9efc4574f..e0b0447c44 100644 --- a/src_c/window.c +++ b/src_c/window.c @@ -124,6 +124,10 @@ window_destroy(pgWindowObject *self, PyObject *_null) SDL_DestroyWindow(self->_win); self->_win = NULL; } + if (self->surf) { + self->surf->surf = NULL; + Py_DECREF(self->surf); + } Py_RETURN_NONE; } @@ -133,11 +137,11 @@ window_get_surface(pgWindowObject *self) PyObject *surf = NULL; SDL_Surface *_surf; - if(self->_is_borrowed){ + if (self->_is_borrowed) { surf = pg_GetDefaultWindowSurface(); - if(!surf){ + if (!surf) { return RAISE(pgExc_SDLError, - "display.set_mode has not been called yet."); + "display.set_mode has not been called yet."); } Py_INCREF(surf); return surf; @@ -147,7 +151,7 @@ window_get_surface(pgWindowObject *self) if (!_surf) { return RAISE(pgExc_SDLError, SDL_GetError()); } - if(self->surf == NULL){ + if (self->surf == NULL) { self->surf = pgSurface_New2(_surf, SDL_FALSE); if (!self->surf) return NULL; @@ -173,11 +177,13 @@ window_update_from_surface(pgWindowObject *self, PyObject *const *args, for (i = 0; i < nargs; i++) { r = pgRect_FromObject(args[i], &tmp); if (!r) { + free(rects); return RAISE(PyExc_TypeError, "Arguments must be rect or rect-like objects."); } rects[i] = *r; if (SDL_UpdateWindowSurfaceRects(self->_win, rects, (int)nargs)) { + free(rects); return RAISE(pgExc_SDLError, SDL_GetError()); } } @@ -612,7 +618,11 @@ window_dealloc(pgWindowObject *self, PyObject *_null) SDL_SetWindowData(self->_win, "pg_window", NULL); } } - Py_XDECREF(self->surf); + if (self->surf) { + self->surf->surf = NULL; + Py_DECREF(self->surf); + } + Py_TYPE(self)->tp_free(self); } From 50d733d2d1327595fdb16267c93a9d96eddeefa8 Mon Sep 17 00:00:00 2001 From: yunline Date: Thu, 27 Jul 2023 02:19:42 +0800 Subject: [PATCH 05/17] Modifiedwindow_update_from_surface --- src_c/window.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src_c/window.c b/src_c/window.c index e0b0447c44..d0325af583 100644 --- a/src_c/window.c +++ b/src_c/window.c @@ -165,30 +165,37 @@ static PyObject * window_update_from_surface(pgWindowObject *self, PyObject *const *args, Py_ssize_t nargs) { - int i; + int i, result; SDL_Rect *rects = NULL, tmp, *r; if (nargs == 0) { - if (SDL_UpdateWindowSurface(self->_win)) { + Py_BEGIN_ALLOW_THREADS; + result = SDL_UpdateWindowSurface(self->_win); + Py_END_ALLOW_THREADS; + if (result) { return RAISE(pgExc_SDLError, SDL_GetError()); } + Py_RETURN_NONE; } - else { - rects = malloc(nargs * sizeof(SDL_Rect)); - for (i = 0; i < nargs; i++) { - r = pgRect_FromObject(args[i], &tmp); - if (!r) { - free(rects); - return RAISE(PyExc_TypeError, - "Arguments must be rect or rect-like objects."); - } - rects[i] = *r; - if (SDL_UpdateWindowSurfaceRects(self->_win, rects, (int)nargs)) { - free(rects); - return RAISE(pgExc_SDLError, SDL_GetError()); - } + + rects = malloc(nargs * sizeof(SDL_Rect)); + for (i = 0; i < nargs; i++) { + r = pgRect_FromObject(args[i], &tmp); + if (!r) { + free(rects); + return RAISE(PyExc_TypeError, + "Arguments must be rect or rect-like objects."); } + rects[i] = *r; + } + + Py_BEGIN_ALLOW_THREADS; + result = SDL_UpdateWindowSurfaceRects(self->_win, rects, (int)nargs); + Py_END_ALLOW_THREADS; + if (result) { free(rects); + return RAISE(pgExc_SDLError, SDL_GetError()); } + free(rects); Py_RETURN_NONE; } From 4e937e7f89dc1c622764986013920f20a27a9a15 Mon Sep 17 00:00:00 2001 From: yunline Date: Thu, 27 Jul 2023 15:01:26 +0800 Subject: [PATCH 06/17] Fix CI warning --- src_c/window.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src_c/window.c b/src_c/window.c index d0325af583..ffe05c9700 100644 --- a/src_c/window.c +++ b/src_c/window.c @@ -138,7 +138,7 @@ window_get_surface(pgWindowObject *self) SDL_Surface *_surf; if (self->_is_borrowed) { - surf = pg_GetDefaultWindowSurface(); + surf = (PyObject *)pg_GetDefaultWindowSurface(); if (!surf) { return RAISE(pgExc_SDLError, "display.set_mode has not been called yet."); @@ -158,7 +158,7 @@ window_get_surface(pgWindowObject *self) } self->surf->surf = _surf; Py_INCREF(self->surf); - return self->surf; + return (PyObject *)self->surf; } static PyObject * From 7783bc1d2b116787af5b327ffa6fca921f0b82c3 Mon Sep 17 00:00:00 2001 From: yunline Date: Thu, 27 Jul 2023 23:51:32 +0800 Subject: [PATCH 07/17] Add auto resize --- src_c/window.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src_c/window.c b/src_c/window.c index ffe05c9700..caf62e522c 100644 --- a/src_c/window.c +++ b/src_c/window.c @@ -127,6 +127,7 @@ window_destroy(pgWindowObject *self, PyObject *_null) if (self->surf) { self->surf->surf = NULL; Py_DECREF(self->surf); + self->surf = NULL; } Py_RETURN_NONE; } @@ -199,6 +200,39 @@ window_update_from_surface(pgWindowObject *self, PyObject *const *args, Py_RETURN_NONE; } +// Callback function for surface auto resize +static int SDLCALL +_resize_event_watch(void *userdata, SDL_Event *event) +{ + pgSurfaceObject *display_surf; + pgWindowObject *event_window_pg; + SDL_Window *event_window; + if ((event->type != SDL_WINDOWEVENT)) + return 0; + if (event->window.event != SDL_WINDOWEVENT_SIZE_CHANGED) + return 0; + event_window = SDL_GetWindowFromID(event->window.windowID); + event_window_pg = SDL_GetWindowData(event_window, "pg_window"); + + if (!event_window_pg) + return 0; + + if (event_window_pg->_is_borrowed) { + display_surf = pg_GetDefaultWindowSurface(); + if (!display_surf) { + return 0; + } + display_surf->surf = SDL_GetWindowSurface(event_window); + return 0; + } + + if (!event_window_pg->surf) + return 0; + + event_window_pg->surf->surf = SDL_GetWindowSurface(event_window); + return 0; +} + static PyObject * window_set_windowed(pgWindowObject *self, PyObject *_null) { @@ -1017,5 +1051,7 @@ MODINIT_DEFINE(_window) return NULL; } + SDL_AddEventWatch(_resize_event_watch, NULL); + return module; } From c586b8830aaccac0b28aa1404bf801278f5ef16d Mon Sep 17 00:00:00 2001 From: yunline Date: Sat, 29 Jul 2023 23:09:31 +0800 Subject: [PATCH 08/17] update docs --- docs/reST/ref/sdl2_video.rst | 24 ++++++++++++++++++++++++ src_c/doc/sdl2_video_doc.h | 2 ++ src_c/window.c | 5 +++-- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/docs/reST/ref/sdl2_video.rst b/docs/reST/ref/sdl2_video.rst index 939cf3c665..a1010c6b24 100644 --- a/docs/reST/ref/sdl2_video.rst +++ b/docs/reST/ref/sdl2_video.rst @@ -224,6 +224,30 @@ Create a Window object that uses the same window data from the :mod:`pygame.display` module, created upon calling :func:`pygame.display.set_mode`. + .. method:: get_surface + + | :sl:`Get the window surface` + | :sg:`get_surface() -> Surface` + + Return a reference to the surface associated with the window. + + .. seealso:: :func:`update_from_surface` + + .. versionaddedold:: 2.4.0 + + .. method:: update_from_surface + + | :sl:`Set the window as a modal for a parent window` + | :sg:`update_from_surface() -> None` + | :sg:`update_from_surface(rect1, rect2, ...) -> None` + + Update content from the window surface to the window. + + By default, this function will update the entire area of window. + Passing several rect like objects to update portions of the window. + + .. versionaddedold:: 2.4.0 + .. method:: set_windowed | :sl:`Enable windowed mode (exit fullscreen)` diff --git a/src_c/doc/sdl2_video_doc.h b/src_c/doc/sdl2_video_doc.h index 04448e7506..7c8fc50c17 100644 --- a/src_c/doc/sdl2_video_doc.h +++ b/src_c/doc/sdl2_video_doc.h @@ -19,6 +19,8 @@ #define DOC_SDL2_VIDEO_WINDOW_OPACITY "opacity -> float\nGet or set the window opacity, between 0.0 (fully transparent) and 1.0 (fully opaque)" #define DOC_SDL2_VIDEO_WINDOW_DISPLAYINDEX "get_display_index -> int\nGet the index of the display that owns the window (**read-only**)" #define DOC_SDL2_VIDEO_WINDOW_FROMDISPLAYMODULE "from_display_module() -> Window\nCreate a Window object using window data from display module" +#define DOC_SDL2_VIDEO_WINDOW_GETSURFACE "get_surface() -> Surface\nGet the window surface" +#define DOC_SDL2_VIDEO_WINDOW_UPDATEFROMSURFACE "update_from_surface() -> None\nupdate_from_surface(rect1, rect2, ...) -> None\nSet the window as a modal for a parent window" #define DOC_SDL2_VIDEO_WINDOW_SETWINDOWED "set_windowed() -> None\nEnable windowed mode (exit fullscreen)" #define DOC_SDL2_VIDEO_WINDOW_SETFULLSCREEN "set_fullscreen(desktop=False) -> None\nEnter fullscreen" #define DOC_SDL2_VIDEO_WINDOW_DESTROY "destroy() -> None\nDestroy the window" diff --git a/src_c/window.c b/src_c/window.c index caf62e522c..259b4e28eb 100644 --- a/src_c/window.c +++ b/src_c/window.c @@ -940,8 +940,9 @@ static PyMethodDef window_methods[] = { {"set_icon", (PyCFunction)window_set_icon, METH_O, DOC_SDL2_VIDEO_WINDOW_SETICON}, {"update_from_surface", (PyCFunction)window_update_from_surface, - METH_FASTCALL, "docs"}, - {"get_surface", (PyCFunction)window_get_surface, METH_NOARGS, "docs"}, + METH_FASTCALL, DOC_SDL2_VIDEO_WINDOW_UPDATEFROMSURFACE}, + {"get_surface", (PyCFunction)window_get_surface, METH_NOARGS, + DOC_SDL2_VIDEO_WINDOW_GETSURFACE}, {"from_display_module", (PyCFunction)window_from_display_module, METH_CLASS | METH_NOARGS, DOC_SDL2_VIDEO_WINDOW_FROMDISPLAYMODULE}, {NULL, NULL, 0, NULL}}; From 4b787f12338b4341b8d3b20ba85874a7dee84300 Mon Sep 17 00:00:00 2001 From: yunline Date: Sun, 30 Jul 2023 03:21:19 +0800 Subject: [PATCH 09/17] Update `update_from_surface` --- buildconfig/stubs/pygame/_sdl2/video.pyi | 4 +- buildconfig/stubs/pygame/_window.pyi | 9 ++-- docs/reST/ref/sdl2_video.rst | 11 +++-- src_c/doc/sdl2_video_doc.h | 2 +- src_c/window.c | 53 ++++++++++++++++++------ 5 files changed, 57 insertions(+), 22 deletions(-) diff --git a/buildconfig/stubs/pygame/_sdl2/video.pyi b/buildconfig/stubs/pygame/_sdl2/video.pyi index fabf97f309..95fb212e02 100644 --- a/buildconfig/stubs/pygame/_sdl2/video.pyi +++ b/buildconfig/stubs/pygame/_sdl2/video.pyi @@ -1,10 +1,10 @@ -from typing import Any, Generator, Iterable, Optional, Tuple, Union, final +from typing import Any, Generator, Iterable, Optional, Tuple, Union from pygame.color import Color from pygame.rect import Rect from pygame.surface import Surface -from .._common import RectValue, ColorValue +from .._common import ColorValue, RectValue WINDOWPOS_UNDEFINED: int WINDOWPOS_CENTERED: int diff --git a/buildconfig/stubs/pygame/_window.pyi b/buildconfig/stubs/pygame/_window.pyi index 1de76d11de..e2b5eedfa1 100644 --- a/buildconfig/stubs/pygame/_window.pyi +++ b/buildconfig/stubs/pygame/_window.pyi @@ -1,6 +1,6 @@ -from typing import Optional, Tuple, Union, final +from typing import Optional, Tuple, Union, final, overload -from pygame._common import Coordinate, RectValue +from pygame._common import Coordinate, RectValue, Sequence from pygame.locals import WINDOWPOS_UNDEFINED from pygame.surface import Surface @@ -26,7 +26,10 @@ class Window: def set_modal_for(self, parent: Window) -> None: ... def set_icon(self, icon: Surface) -> None: ... def get_surface(self) -> Surface: ... - def update_from_surface(self, *rects: RectValue) -> None: ... + @overload + def update_from_surface(self) -> None: ... + @overload + def update_from_surface(self, rects: Sequence[RectValue]) -> None: ... grab: bool title: str resizable: bool diff --git a/docs/reST/ref/sdl2_video.rst b/docs/reST/ref/sdl2_video.rst index a1010c6b24..4678ed8521 100644 --- a/docs/reST/ref/sdl2_video.rst +++ b/docs/reST/ref/sdl2_video.rst @@ -237,14 +237,17 @@ .. method:: update_from_surface - | :sl:`Set the window as a modal for a parent window` + | :sl:`Update the window surface to the window.` | :sg:`update_from_surface() -> None` - | :sg:`update_from_surface(rect1, rect2, ...) -> None` + | :sg:`update_from_surface(rects) -> None` Update content from the window surface to the window. - By default, this function will update the entire area of window. - Passing several rect like objects to update portions of the window. + You can pass the function a sequence of rectangles to update portions + of window. If no argument is passed it updates the entire window. + + Unlike :func:`pygame.display.update`, if ``None`` is included in the + sequence, a ``ValueError`` is raised. .. versionaddedold:: 2.4.0 diff --git a/src_c/doc/sdl2_video_doc.h b/src_c/doc/sdl2_video_doc.h index 7c8fc50c17..3a9c976bac 100644 --- a/src_c/doc/sdl2_video_doc.h +++ b/src_c/doc/sdl2_video_doc.h @@ -20,7 +20,7 @@ #define DOC_SDL2_VIDEO_WINDOW_DISPLAYINDEX "get_display_index -> int\nGet the index of the display that owns the window (**read-only**)" #define DOC_SDL2_VIDEO_WINDOW_FROMDISPLAYMODULE "from_display_module() -> Window\nCreate a Window object using window data from display module" #define DOC_SDL2_VIDEO_WINDOW_GETSURFACE "get_surface() -> Surface\nGet the window surface" -#define DOC_SDL2_VIDEO_WINDOW_UPDATEFROMSURFACE "update_from_surface() -> None\nupdate_from_surface(rect1, rect2, ...) -> None\nSet the window as a modal for a parent window" +#define DOC_SDL2_VIDEO_WINDOW_UPDATEFROMSURFACE "update_from_surface() -> None\nupdate_from_surface(rects) -> None\nUpdate the window surface to the window." #define DOC_SDL2_VIDEO_WINDOW_SETWINDOWED "set_windowed() -> None\nEnable windowed mode (exit fullscreen)" #define DOC_SDL2_VIDEO_WINDOW_SETFULLSCREEN "set_fullscreen(desktop=False) -> None\nEnter fullscreen" #define DOC_SDL2_VIDEO_WINDOW_DESTROY "destroy() -> None\nDestroy the window" diff --git a/src_c/window.c b/src_c/window.c index 259b4e28eb..311bbf60c0 100644 --- a/src_c/window.c +++ b/src_c/window.c @@ -166,8 +166,17 @@ static PyObject * window_update_from_surface(pgWindowObject *self, PyObject *const *args, Py_ssize_t nargs) { - int i, result; - SDL_Rect *rects = NULL, tmp, *r; + int result; + + if (nargs > 1) { + PyErr_Format(PyExc_TypeError, + "update_from_surface() takes from 0 to 1 positional " + "arguments but %d were given", + nargs); + return NULL; + } + + // no argument if (nargs == 0) { Py_BEGIN_ALLOW_THREADS; result = SDL_UpdateWindowSurface(self->_win); @@ -178,25 +187,45 @@ window_update_from_surface(pgWindowObject *self, PyObject *const *args, Py_RETURN_NONE; } - rects = malloc(nargs * sizeof(SDL_Rect)); - for (i = 0; i < nargs; i++) { - r = pgRect_FromObject(args[i], &tmp); - if (!r) { - free(rects); + // argument is a sequence + + PyObject *arg = args[0]; + + if (!PySequence_Check(arg)) { + return RAISE( + PyExc_ValueError, + "update_from_surface() requires a sequence of rect like objects"); + } + + Py_ssize_t seq_size = PySequence_Size(arg); + SDL_Rect tmp, *rect; + PyObject *item; + SDL_Rect *rect_array = malloc(seq_size * sizeof(SDL_Rect)); + for (Py_ssize_t i = 0; i < seq_size; i++) { + item = PySequence_GetItem(arg, i); + if (!item) { + free(rect_array); + return NULL; + } + rect = pgRect_FromObject(item, &tmp); + Py_DECREF(item); + if (!rect) { + free(rect_array); return RAISE(PyExc_TypeError, - "Arguments must be rect or rect-like objects."); + "update_from_surface() requires a sequence of rect " + "like objects"); } - rects[i] = *r; + rect_array[i] = *rect; } Py_BEGIN_ALLOW_THREADS; - result = SDL_UpdateWindowSurfaceRects(self->_win, rects, (int)nargs); + result = SDL_UpdateWindowSurfaceRects(self->_win, rect_array, seq_size); Py_END_ALLOW_THREADS; if (result) { - free(rects); + free(rect_array); return RAISE(pgExc_SDLError, SDL_GetError()); } - free(rects); + free(rect_array); Py_RETURN_NONE; } From 48b7fa6f8f2f8e0d43c117ea7fbc7d0706b2e795 Mon Sep 17 00:00:00 2001 From: yunline Date: Sun, 30 Jul 2023 03:31:33 +0800 Subject: [PATCH 10/17] fix CI warning --- src_c/window.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src_c/window.c b/src_c/window.c index 311bbf60c0..aa0c6945b3 100644 --- a/src_c/window.c +++ b/src_c/window.c @@ -219,7 +219,8 @@ window_update_from_surface(pgWindowObject *self, PyObject *const *args, } Py_BEGIN_ALLOW_THREADS; - result = SDL_UpdateWindowSurfaceRects(self->_win, rect_array, seq_size); + result = + SDL_UpdateWindowSurfaceRects(self->_win, rect_array, (int)seq_size); Py_END_ALLOW_THREADS; if (result) { free(rect_array); From 5af5e6ae2b3ffe4e96d2165cea806a438d578c11 Mon Sep 17 00:00:00 2001 From: yunline Date: Sun, 27 Aug 2023 17:13:05 +0800 Subject: [PATCH 11/17] versionaddedold -> versionadded --- docs/reST/ref/sdl2_video.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reST/ref/sdl2_video.rst b/docs/reST/ref/sdl2_video.rst index 4678ed8521..c58cf7aa36 100644 --- a/docs/reST/ref/sdl2_video.rst +++ b/docs/reST/ref/sdl2_video.rst @@ -233,7 +233,7 @@ .. seealso:: :func:`update_from_surface` - .. versionaddedold:: 2.4.0 + .. versionadded:: 2.4.0 .. method:: update_from_surface @@ -249,7 +249,7 @@ Unlike :func:`pygame.display.update`, if ``None`` is included in the sequence, a ``ValueError`` is raised. - .. versionaddedold:: 2.4.0 + .. versionadded:: 2.4.0 .. method:: set_windowed From 892e8dc26457da2832f9fbcbe0174c4fb4660c67 Mon Sep 17 00:00:00 2001 From: yunline Date: Sun, 15 Oct 2023 12:55:27 +0800 Subject: [PATCH 12/17] Don't handle resize event for borrowed window --- src_c/window.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src_c/window.c b/src_c/window.c index aa0c6945b3..3b3934b05f 100644 --- a/src_c/window.c +++ b/src_c/window.c @@ -234,7 +234,6 @@ window_update_from_surface(pgWindowObject *self, PyObject *const *args, static int SDLCALL _resize_event_watch(void *userdata, SDL_Event *event) { - pgSurfaceObject *display_surf; pgWindowObject *event_window_pg; SDL_Window *event_window; if ((event->type != SDL_WINDOWEVENT)) @@ -248,11 +247,7 @@ _resize_event_watch(void *userdata, SDL_Event *event) return 0; if (event_window_pg->_is_borrowed) { - display_surf = pg_GetDefaultWindowSurface(); - if (!display_surf) { - return 0; - } - display_surf->surf = SDL_GetWindowSurface(event_window); + // have been handled by event watch in display.c return 0; } From f998095046a6e8ea4457be29b46510d85de0b8f6 Mon Sep 17 00:00:00 2001 From: yunline Date: Sun, 15 Oct 2023 13:36:33 +0800 Subject: [PATCH 13/17] Add comments --- src_c/window.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src_c/window.c b/src_c/window.c index 3b3934b05f..f837d6f499 100644 --- a/src_c/window.c +++ b/src_c/window.c @@ -125,7 +125,11 @@ window_destroy(pgWindowObject *self, PyObject *_null) self->_win = NULL; } if (self->surf) { + // Set the internal surface to NULL to make pygame surface invalid + // since this surface will be deallocated by SDL when the window is + // destroyed. self->surf->surf = NULL; + Py_DECREF(self->surf); self->surf = NULL; } @@ -685,7 +689,11 @@ window_dealloc(pgWindowObject *self, PyObject *_null) } } if (self->surf) { + // Set the internal surface to NULL to make pygame surface invalid + // since this surface will be deallocated by SDL when the window is + // destroyed. self->surf->surf = NULL; + Py_DECREF(self->surf); } From 23aa2876490a9145fd9410731873e97fbc787fc0 Mon Sep 17 00:00:00 2001 From: yunline Date: Mon, 16 Oct 2023 09:40:22 +0800 Subject: [PATCH 14/17] Remove partial screen updates and update docs --- buildconfig/stubs/pygame/_window.pyi | 7 +--- docs/reST/ref/sdl2_video.rst | 20 ++++++--- src_c/doc/sdl2_video_doc.h | 2 +- src_c/window.c | 63 ++-------------------------- 4 files changed, 22 insertions(+), 70 deletions(-) diff --git a/buildconfig/stubs/pygame/_window.pyi b/buildconfig/stubs/pygame/_window.pyi index f0a87a5807..0fcb31467b 100644 --- a/buildconfig/stubs/pygame/_window.pyi +++ b/buildconfig/stubs/pygame/_window.pyi @@ -1,6 +1,6 @@ -from typing import Optional, Tuple, Union, final, overload +from typing import Optional, Tuple, Union, final -from pygame._common import Coordinate, RectValue, Sequence +from pygame._common import Coordinate from pygame.locals import WINDOWPOS_UNDEFINED from pygame.surface import Surface @@ -26,10 +26,7 @@ class Window: def set_modal_for(self, parent: Window) -> None: ... def set_icon(self, icon: Surface) -> None: ... def get_surface(self) -> Surface: ... - @overload def update_from_surface(self) -> None: ... - @overload - def update_from_surface(self, rects: Sequence[RectValue]) -> None: ... grab_mouse: bool grab_keyboard: bool diff --git a/docs/reST/ref/sdl2_video.rst b/docs/reST/ref/sdl2_video.rst index 7771a6e186..b5084f197b 100644 --- a/docs/reST/ref/sdl2_video.rst +++ b/docs/reST/ref/sdl2_video.rst @@ -301,6 +301,10 @@ Return a reference to the surface associated with the window. + The size of surface will automatically change to fit the window size. + + The window surface become invalid when the window is destroyed. + .. seealso:: :func:`update_from_surface` .. versionadded:: 2.4.0 @@ -309,15 +313,21 @@ | :sl:`Update the window surface to the window.` | :sg:`update_from_surface() -> None` - | :sg:`update_from_surface(rects) -> None` Update content from the window surface to the window. - You can pass the function a sequence of rectangles to update portions - of window. If no argument is passed it updates the entire window. + Here is an example of using ``get_surface`` and ``update_from_surface``: + + .. code-block:: python + + win = video.Window() + surf = win.get_surface() # get the window surface + + # draw something on the surface + surf.fill((255,0,0)) + + win.update_from_surface() # update the surface to the window - Unlike :func:`pygame.display.update`, if ``None`` is included in the - sequence, a ``ValueError`` is raised. .. versionadded:: 2.4.0 diff --git a/src_c/doc/sdl2_video_doc.h b/src_c/doc/sdl2_video_doc.h index 5ab0794b7f..434a39fd12 100644 --- a/src_c/doc/sdl2_video_doc.h +++ b/src_c/doc/sdl2_video_doc.h @@ -23,7 +23,7 @@ #define DOC_SDL2_VIDEO_WINDOW_DISPLAYINDEX "get_display_index -> int\nGet the index of the display that owns the window (**read-only**)" #define DOC_SDL2_VIDEO_WINDOW_FROMDISPLAYMODULE "from_display_module() -> Window\nCreate a Window object using window data from display module" #define DOC_SDL2_VIDEO_WINDOW_GETSURFACE "get_surface() -> Surface\nGet the window surface" -#define DOC_SDL2_VIDEO_WINDOW_UPDATEFROMSURFACE "update_from_surface() -> None\nupdate_from_surface(rects) -> None\nUpdate the window surface to the window." +#define DOC_SDL2_VIDEO_WINDOW_UPDATEFROMSURFACE "update_from_surface() -> None\nUpdate the window surface to the window." #define DOC_SDL2_VIDEO_WINDOW_SETWINDOWED "set_windowed() -> None\nEnable windowed mode (exit fullscreen)" #define DOC_SDL2_VIDEO_WINDOW_SETFULLSCREEN "set_fullscreen(desktop=False) -> None\nEnter fullscreen" #define DOC_SDL2_VIDEO_WINDOW_DESTROY "destroy() -> None\nDestroy the window" diff --git a/src_c/window.c b/src_c/window.c index a21cedd7cb..9629d0c325 100644 --- a/src_c/window.c +++ b/src_c/window.c @@ -162,70 +162,15 @@ window_get_surface(pgWindowObject *self) } static PyObject * -window_update_from_surface(pgWindowObject *self, PyObject *const *args, - Py_ssize_t nargs) +window_update_from_surface(pgWindowObject *self) { int result; - if (nargs > 1) { - PyErr_Format(PyExc_TypeError, - "update_from_surface() takes from 0 to 1 positional " - "arguments but %d were given", - nargs); - return NULL; - } - - // no argument - if (nargs == 0) { - Py_BEGIN_ALLOW_THREADS; - result = SDL_UpdateWindowSurface(self->_win); - Py_END_ALLOW_THREADS; - if (result) { - return RAISE(pgExc_SDLError, SDL_GetError()); - } - Py_RETURN_NONE; - } - - // argument is a sequence - - PyObject *arg = args[0]; - - if (!PySequence_Check(arg)) { - return RAISE( - PyExc_ValueError, - "update_from_surface() requires a sequence of rect like objects"); - } - - Py_ssize_t seq_size = PySequence_Size(arg); - SDL_Rect tmp, *rect; - PyObject *item; - SDL_Rect *rect_array = malloc(seq_size * sizeof(SDL_Rect)); - for (Py_ssize_t i = 0; i < seq_size; i++) { - item = PySequence_GetItem(arg, i); - if (!item) { - free(rect_array); - return NULL; - } - rect = pgRect_FromObject(item, &tmp); - Py_DECREF(item); - if (!rect) { - free(rect_array); - return RAISE(PyExc_TypeError, - "update_from_surface() requires a sequence of rect " - "like objects"); - } - rect_array[i] = *rect; - } - Py_BEGIN_ALLOW_THREADS; - result = - SDL_UpdateWindowSurfaceRects(self->_win, rect_array, (int)seq_size); + result = SDL_UpdateWindowSurface(self->_win); Py_END_ALLOW_THREADS; - if (result) { - free(rect_array); + if (result) return RAISE(pgExc_SDLError, SDL_GetError()); - } - free(rect_array); Py_RETURN_NONE; } @@ -1049,7 +994,7 @@ static PyMethodDef window_methods[] = { {"set_icon", (PyCFunction)window_set_icon, METH_O, DOC_SDL2_VIDEO_WINDOW_SETICON}, {"update_from_surface", (PyCFunction)window_update_from_surface, - METH_FASTCALL, DOC_SDL2_VIDEO_WINDOW_UPDATEFROMSURFACE}, + METH_NOARGS, DOC_SDL2_VIDEO_WINDOW_UPDATEFROMSURFACE}, {"get_surface", (PyCFunction)window_get_surface, METH_NOARGS, DOC_SDL2_VIDEO_WINDOW_GETSURFACE}, {"from_display_module", (PyCFunction)window_from_display_module, From 025aaec0caa5d162ce9a26bad3adf92063b37684 Mon Sep 17 00:00:00 2001 From: yunline Date: Mon, 16 Oct 2023 10:26:01 +0800 Subject: [PATCH 15/17] Add tests for window surface --- test/window_test.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/test/window_test.py b/test/window_test.py index 5eb045e9d4..bd72e5c93c 100644 --- a/test/window_test.py +++ b/test/window_test.py @@ -301,6 +301,47 @@ def test_from_display_module(self): pygame.display.quit() pygame.init() + def test_window_surface(self): + win = Window(size=(640, 480)) + surf = win.get_surface() + + self.assertIsInstance(surf, pygame.Surface) + + # test auto resize + self.assertTupleEqual(win.size, surf.get_size()) + win.size = (100, 100) + self.assertTupleEqual(win.size, surf.get_size()) + win.size = (1280, 720) + self.assertTupleEqual(win.size, surf.get_size()) + + # window surface should be invalid after the window is destroyed + win.destroy() + self.assertRaises(pygame.error, lambda: surf.fill((0, 0, 0))) + + def test_window_surface_with_display_module(self): + # get_surface() should raise an error if the set_mode() is not called. + pygame.display.set_mode((640, 480)) + win1 = Window.from_display_module() + pygame.display.quit() + pygame.init() + self.assertRaises(pygame.error, lambda: win1.get_surface()) + + # the surface returned by get_surface() should be + # the surface returned by set_mode() + surf1 = pygame.display.set_mode((640, 480)) + win2 = Window.from_display_module() + surf2 = win2.get_surface() + self.assertIs(surf1, surf2) + + def test_window_update_from_surface(self): + win = Window(size=(640, 480)) + surf = win.get_surface() + surf.fill((255, 0, 0)) + + self.assertRaises(TypeError, lambda: win.update_from_surface("an argument")) + self.assertIs(win.update_from_surface(), None) + win.destroy() + def tearDown(self): self.win.destroy() From 146bd31b222d44e94b51b16d4d6080e8db10e22d Mon Sep 17 00:00:00 2001 From: yunline Date: Fri, 27 Oct 2023 10:46:35 +0800 Subject: [PATCH 16/17] add "init-quit hook" for window module --- src_c/base.c | 3 ++- src_c/window.c | 20 ++++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src_c/base.c b/src_c/base.c index 36903b8656..61627b8a9d 100644 --- a/src_c/base.c +++ b/src_c/base.c @@ -326,7 +326,7 @@ pg_init(PyObject *self, PyObject *_null) const char *modnames[] = { IMPPREFIX "display", /* Display first, this also inits event,time */ IMPPREFIX "joystick", IMPPREFIX "font", IMPPREFIX "freetype", - IMPPREFIX "mixer", + IMPPREFIX "mixer", IMPPREFIX "_window", /* IMPPREFIX "_sdl2.controller", Is this required? Comment for now*/ NULL}; @@ -409,6 +409,7 @@ _pg_quit(void) IMPPREFIX "freetype", IMPPREFIX "font", IMPPREFIX "joystick", + IMPPREFIX "_window", IMPPREFIX "display", /* Display last, this also quits event,time */ NULL}; diff --git a/src_c/window.c b/src_c/window.c index 9629d0c325..d13a5de4ff 100644 --- a/src_c/window.c +++ b/src_c/window.c @@ -970,6 +970,20 @@ window_repr(pgWindowObject *self) return PyUnicode_FromFormat("", title, win_id); } +static PyObject * +_window_internal_mod_init(PyObject *self, PyObject *_null) +{ + SDL_AddEventWatch(_resize_event_watch, NULL); + Py_RETURN_NONE; +} + +static PyObject * +_window_internal_mod_quit(PyObject *self, PyObject *_null) +{ + SDL_DelEventWatch(_resize_event_watch, NULL); + Py_RETURN_NONE; +} + static PyMethodDef window_methods[] = { {"destroy", (PyCFunction)window_destroy, METH_NOARGS, DOC_SDL2_VIDEO_WINDOW_DESTROY}, @@ -1053,6 +1067,10 @@ static PyTypeObject pgWindow_Type = { static PyMethodDef _window_methods[] = { {"get_grabbed_window", (PyCFunction)get_grabbed_window, METH_NOARGS, DOC_SDL2_VIDEO_GETGRABBEDWINDOW}, + {"_internal_mod_init", (PyCFunction)_window_internal_mod_init, METH_NOARGS, + "auto initialize for _window module"}, + {"_internal_mod_quit", (PyCFunction)_window_internal_mod_quit, METH_NOARGS, + "auto quit for _window module"}, {NULL, NULL, 0, NULL}}; MODINIT_DEFINE(_window) @@ -1113,7 +1131,5 @@ MODINIT_DEFINE(_window) return NULL; } - SDL_AddEventWatch(_resize_event_watch, NULL); - return module; } From e48d7149033b1e23aa506938f7489bca73993aff Mon Sep 17 00:00:00 2001 From: yunline Date: Tue, 31 Oct 2023 15:54:33 +0800 Subject: [PATCH 17/17] init window module when `display.init()` --- src_c/base.c | 3 +-- src_c/display.c | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src_c/base.c b/src_c/base.c index 61627b8a9d..36903b8656 100644 --- a/src_c/base.c +++ b/src_c/base.c @@ -326,7 +326,7 @@ pg_init(PyObject *self, PyObject *_null) const char *modnames[] = { IMPPREFIX "display", /* Display first, this also inits event,time */ IMPPREFIX "joystick", IMPPREFIX "font", IMPPREFIX "freetype", - IMPPREFIX "mixer", IMPPREFIX "_window", + IMPPREFIX "mixer", /* IMPPREFIX "_sdl2.controller", Is this required? Comment for now*/ NULL}; @@ -409,7 +409,6 @@ _pg_quit(void) IMPPREFIX "freetype", IMPPREFIX "font", IMPPREFIX "joystick", - IMPPREFIX "_window", IMPPREFIX "display", /* Display last, this also quits event,time */ NULL}; diff --git a/src_c/display.c b/src_c/display.c index b991368ec2..e64e73c4db 100644 --- a/src_c/display.c +++ b/src_c/display.c @@ -196,6 +196,7 @@ pg_display_quit(PyObject *self, PyObject *_null) pg_mod_autoquit(IMPPREFIX "event"); pg_mod_autoquit(IMPPREFIX "time"); + pg_mod_autoquit(IMPPREFIX "_window"); if (SDL_WasInit(SDL_INIT_VIDEO)) { SDL_QuitSubSystem(SDL_INIT_VIDEO); @@ -251,6 +252,8 @@ pg_display_init(PyObject *self, PyObject *_null) return NULL; if (!pg_mod_autoinit(IMPPREFIX "event")) return NULL; + if (!pg_mod_autoinit(IMPPREFIX "_window")) + return NULL; Py_RETURN_NONE; }