From 6c65578baa5eeba411c72b51303fd31692acc6f6 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Mon, 3 Feb 2025 16:38:27 -0500
Subject: [PATCH] Refuse to SDL_FreeSurface() a pointer from
SDL_GetWindowSurface().
It was never legal in SDL2 to do this, but due to circumstances, SDL2 wouldn't
_crash_ if you did this, so this fixes games that do.
Reference Issue #279.
---
src/sdl2_compat.c | 41 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 40 insertions(+), 1 deletion(-)
diff --git a/src/sdl2_compat.c b/src/sdl2_compat.c
index d4cef01..b55b457 100644
--- a/src/sdl2_compat.c
+++ b/src/sdl2_compat.c
@@ -802,6 +802,7 @@ BOOL WINAPI _DllMainCRTStartup(HANDLE dllhandle, DWORD reason, LPVOID reserved)
/* Some SDL2 state we need to keep... */
/* !!! FIXME: unify coding convention on the globals: some are MyVariableName and some are my_variable_name */
+static SDL2_Surface *OldWindowSurfaces[16];
static SDL2_EventFilter EventFilter2 = NULL;
static void *EventFilterUserData2 = NULL;
static SDL_mutex *EventWatchListMutex = NULL;
@@ -3136,10 +3137,23 @@ SDL_CreateRGBSurfaceWithFormatFrom(void *pixels, int width, int height, int dept
SDL_DECLSPEC void SDLCALL
SDL_FreeSurface(SDL2_Surface *surface)
{
+ const int total = (int) (SDL_arraysize(OldWindowSurfaces));
+ int i;
+
if (!surface) {
return;
}
+ for (i = 0; (i < total) && OldWindowSurfaces[i]; i++) {
+ if (OldWindowSurfaces[i] == surface) {
+ return; /* this is a pointer from SDL_GetWindowSurface--a bug in the app--refuse to free it. */
+ }
+ }
+
+ if (surface->flags & SDL_DONTFREE) {
+ return;
+ }
+
if (--surface->refcount > 0) {
return;
}
@@ -8300,7 +8314,32 @@ SDL_GetSurfaceBlendMode(SDL2_Surface *surface, SDL_BlendMode *blendMode)
SDL_DECLSPEC SDL2_Surface * SDLCALL
SDL_GetWindowSurface(SDL_Window *window)
{
- return Surface3to2(SDL3_GetWindowSurface(window));
+ SDL2_Surface *surface2 = Surface3to2(SDL3_GetWindowSurface(window));
+ if (surface2) {
+ surface2->flags |= SDL_DONTFREE;
+ }
+
+ if (surface2) {
+ /* if the window was resized, SDL_GetWindowSurface() will destroy the previous surface and create a new one.
+ This takes the previous SDL2 surface with it. It's not legal to SDL_FreeSurface() a window surface, but
+ in SDL2, it didn't dereference free'd memory to do so, so we need to keep track of old pointers in case
+ an app tries to free them. */
+ int i;
+ const int total = (int) (SDL_arraysize(OldWindowSurfaces));
+ for (i = 0; i < total; i++) {
+ if (OldWindowSurfaces[i] == surface2) {
+ break;
+ }
+ }
+
+ /* just keep the last X surfaces; this is a hack to keep buggy legacy code running. */
+ if (i == total) {
+ SDL3_memmove(&OldWindowSurfaces[1], &OldWindowSurfaces[0], sizeof (OldWindowSurfaces) - sizeof (OldWindowSurfaces[0]));
+ OldWindowSurfaces[0] = surface2;
+ }
+ }
+
+ return surface2;
}
SDL_DECLSPEC int SDLCALL