Bladeren bron

Cocoa metal layers need their size updated before renderer updates

Also refactored event watch code so it can be shared between internal window event dispatch and public event watchers.

Fixes https://github.com/libsdl-org/SDL/issues/12376
Sam Lantinga 1 maand geleden
bovenliggende
commit
db4e6c1931

+ 2 - 0
VisualC-GDK/SDL/SDL.vcxproj

@@ -442,6 +442,7 @@
     <ClInclude Include="..\..\src\events\SDL_displayevents_c.h" />
     <ClInclude Include="..\..\src\events\SDL_dropevents_c.h" />
     <ClInclude Include="..\..\src\events\SDL_events_c.h" />
+    <ClInclude Include="..\..\src\events\SDL_eventwatch_c.h" />
     <ClInclude Include="..\..\src\events\SDL_keyboard_c.h" />
     <ClInclude Include="..\..\src\events\SDL_keymap_c.h" />
     <ClInclude Include="..\..\src\events\SDL_mouse_c.h" />
@@ -676,6 +677,7 @@
     <ClCompile Include="..\..\src\events\SDL_displayevents.c" />
     <ClCompile Include="..\..\src\events\SDL_dropevents.c" />
     <ClCompile Include="..\..\src\events\SDL_events.c" />
+    <ClCompile Include="..\..\src\events\SDL_eventwatch.c" />
     <ClCompile Include="..\..\src\events\SDL_keyboard.c" />
     <ClCompile Include="..\..\src\events\SDL_keymap.c" />
     <ClCompile Include="..\..\src\events\SDL_mouse.c" />

+ 2 - 0
VisualC-GDK/SDL/SDL.vcxproj.filters

@@ -39,6 +39,7 @@
     <ClCompile Include="..\..\src\events\SDL_displayevents.c" />
     <ClCompile Include="..\..\src\events\SDL_dropevents.c" />
     <ClCompile Include="..\..\src\events\SDL_events.c" />
+    <ClCompile Include="..\..\src\events\SDL_eventwatch.c" />
     <ClCompile Include="..\..\src\events\SDL_keyboard.c" />
     <ClCompile Include="..\..\src\events\SDL_keymap.c" />
     <ClCompile Include="..\..\src\events\SDL_mouse.c" />
@@ -333,6 +334,7 @@
     <ClInclude Include="..\..\src\events\SDL_displayevents_c.h" />
     <ClInclude Include="..\..\src\events\SDL_dropevents_c.h" />
     <ClInclude Include="..\..\src\events\SDL_events_c.h" />
+    <ClInclude Include="..\..\src\events\SDL_eventwatch_c.h" />
     <ClInclude Include="..\..\src\events\SDL_keyboard_c.h" />
     <ClInclude Include="..\..\src\events\SDL_keymap_c.h" />
     <ClInclude Include="..\..\src\events\SDL_mouse_c.h" />

+ 2 - 0
VisualC/SDL/SDL.vcxproj

@@ -354,6 +354,7 @@
     <ClInclude Include="..\..\src\events\SDL_displayevents_c.h" />
     <ClInclude Include="..\..\src\events\SDL_dropevents_c.h" />
     <ClInclude Include="..\..\src\events\SDL_events_c.h" />
+    <ClInclude Include="..\..\src\events\SDL_eventwatch_c.h" />
     <ClInclude Include="..\..\src\events\SDL_keyboard_c.h" />
     <ClInclude Include="..\..\src\events\SDL_keymap_c.h" />
     <ClInclude Include="..\..\src\events\SDL_mouse_c.h" />
@@ -558,6 +559,7 @@
     <ClCompile Include="..\..\src\events\SDL_displayevents.c" />
     <ClCompile Include="..\..\src\events\SDL_dropevents.c" />
     <ClCompile Include="..\..\src\events\SDL_events.c" />
+    <ClCompile Include="..\..\src\events\SDL_eventwatch.c" />
     <ClCompile Include="..\..\src\events\SDL_keyboard.c" />
     <ClCompile Include="..\..\src\events\SDL_keymap.c" />
     <ClCompile Include="..\..\src\events\SDL_mouse.c" />

+ 6 - 0
VisualC/SDL/SDL.vcxproj.filters

@@ -534,6 +534,9 @@
     <ClInclude Include="..\..\src\events\SDL_events_c.h">
       <Filter>events</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\src\events\SDL_eventfilter_c.h">
+      <Filter>events</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\src\events\SDL_keyboard_c.h">
       <Filter>events</Filter>
     </ClInclude>
@@ -1076,6 +1079,9 @@
     <ClCompile Include="..\..\src\events\SDL_events.c">
       <Filter>events</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\events\SDL_eventfilter.c">
+      <Filter>events</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\src\events\SDL_keyboard.c">
       <Filter>events</Filter>
     </ClCompile>

+ 19 - 11
Xcode/SDL/SDL.xcodeproj/project.pbxproj

@@ -512,6 +512,8 @@
 		F3D46B122D20625800D9CBDF /* SDL_egl.h in Headers */ = {isa = PBXBuildFile; fileRef = F3D46A8E2D20625800D9CBDF /* SDL_egl.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		F3D46B132D20625800D9CBDF /* SDL_filesystem.h in Headers */ = {isa = PBXBuildFile; fileRef = F3D46A922D20625800D9CBDF /* SDL_filesystem.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		F3D60A8328C16A1900788A3A /* SDL_hidapi_wii.c in Sources */ = {isa = PBXBuildFile; fileRef = F3D60A8228C16A1800788A3A /* SDL_hidapi_wii.c */; };
+		F3D8BDFC2D6D2C7000B22FA1 /* SDL_eventwatch_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3D8BDFB2D6D2C7000B22FA1 /* SDL_eventwatch_c.h */; };
+		F3D8BDFD2D6D2C7000B22FA1 /* SDL_eventwatch.c in Sources */ = {isa = PBXBuildFile; fileRef = F3D8BDFA2D6D2C7000B22FA1 /* SDL_eventwatch.c */; };
 		F3DDCC562AFD42B600B0842B /* SDL_clipboard_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3DDCC4D2AFD42B500B0842B /* SDL_clipboard_c.h */; };
 		F3DDCC5B2AFD42B600B0842B /* SDL_video_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3DDCC522AFD42B600B0842B /* SDL_video_c.h */; };
 		F3DDCC5D2AFD42B600B0842B /* SDL_rect_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = F3DDCC542AFD42B600B0842B /* SDL_rect_impl.h */; };
@@ -1079,6 +1081,8 @@
 		F3D46AC82D20625800D9CBDF /* SDL_video.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_video.h; sourceTree = "<group>"; };
 		F3D46AC92D20625800D9CBDF /* SDL_vulkan.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_vulkan.h; sourceTree = "<group>"; };
 		F3D60A8228C16A1800788A3A /* SDL_hidapi_wii.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_wii.c; sourceTree = "<group>"; };
+		F3D8BDFA2D6D2C7000B22FA1 /* SDL_eventwatch.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_eventwatch.c; sourceTree = "<group>"; };
+		F3D8BDFB2D6D2C7000B22FA1 /* SDL_eventwatch_c.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_eventwatch_c.h; sourceTree = "<group>"; };
 		F3DDCC4D2AFD42B500B0842B /* SDL_clipboard_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_clipboard_c.h; sourceTree = "<group>"; };
 		F3DDCC522AFD42B600B0842B /* SDL_video_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_video_c.h; sourceTree = "<group>"; };
 		F3DDCC542AFD42B600B0842B /* SDL_rect_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_rect_impl.h; sourceTree = "<group>"; };
@@ -2209,29 +2213,31 @@
 				A7D8A93623E2514000DCD162 /* scancodes_linux.h */,
 				A7D8A92C23E2514000DCD162 /* scancodes_windows.h */,
 				A7D8A94123E2514000DCD162 /* scancodes_xfree86.h */,
-				F3C2CB202C5DDDB2004D7998 /* SDL_categories_c.h */,
 				F3C2CB212C5DDDB2004D7998 /* SDL_categories.c */,
-				A7D8A93923E2514000DCD162 /* SDL_clipboardevents_c.h */,
+				F3C2CB202C5DDDB2004D7998 /* SDL_categories_c.h */,
 				A7D8A93A23E2514000DCD162 /* SDL_clipboardevents.c */,
-				A7D8A93123E2514000DCD162 /* SDL_displayevents_c.h */,
+				A7D8A93923E2514000DCD162 /* SDL_clipboardevents_c.h */,
 				A7D8A92D23E2514000DCD162 /* SDL_displayevents.c */,
-				A7D8A92E23E2514000DCD162 /* SDL_dropevents_c.h */,
+				A7D8A93123E2514000DCD162 /* SDL_displayevents_c.h */,
 				A7D8A93B23E2514000DCD162 /* SDL_dropevents.c */,
-				A7D8A94223E2514000DCD162 /* SDL_events_c.h */,
+				A7D8A92E23E2514000DCD162 /* SDL_dropevents_c.h */,
 				A7D8A93523E2514000DCD162 /* SDL_events.c */,
-				A7D8A93D23E2514000DCD162 /* SDL_keyboard_c.h */,
+				A7D8A94223E2514000DCD162 /* SDL_events_c.h */,
+				F3D8BDFA2D6D2C7000B22FA1 /* SDL_eventwatch.c */,
+				F3D8BDFB2D6D2C7000B22FA1 /* SDL_eventwatch_c.h */,
 				A7D8A93823E2514000DCD162 /* SDL_keyboard.c */,
-				F31013C62C24E98200FBE946 /* SDL_keymap_c.h */,
+				A7D8A93D23E2514000DCD162 /* SDL_keyboard_c.h */,
 				F31013C52C24E98200FBE946 /* SDL_keymap.c */,
-				A7D8A92B23E2514000DCD162 /* SDL_mouse_c.h */,
+				F31013C62C24E98200FBE946 /* SDL_keymap_c.h */,
 				A7D8A92A23E2514000DCD162 /* SDL_mouse.c */,
-				63134A232A7902FD0021E9A6 /* SDL_pen_c.h */,
+				A7D8A92B23E2514000DCD162 /* SDL_mouse_c.h */,
 				63134A242A7902FD0021E9A6 /* SDL_pen.c */,
+				63134A232A7902FD0021E9A6 /* SDL_pen_c.h */,
 				A7D8A93C23E2514000DCD162 /* SDL_quit.c */,
-				A7D8A93723E2514000DCD162 /* SDL_touch_c.h */,
 				A7D8A93E23E2514000DCD162 /* SDL_touch.c */,
-				A7D8A94323E2514000DCD162 /* SDL_windowevents_c.h */,
+				A7D8A93723E2514000DCD162 /* SDL_touch_c.h */,
 				A7D8A92F23E2514000DCD162 /* SDL_windowevents.c */,
+				A7D8A94323E2514000DCD162 /* SDL_windowevents_c.h */,
 			);
 			path = events;
 			sourceTree = "<group>";
@@ -2707,6 +2713,7 @@
 				A7D8B3D423E2514300DCD162 /* yuv_rgb.h in Headers */,
 				F3FA5A252B59ACE000FEAD97 /* yuv_rgb_common.h in Headers */,
 				F3FA5A1D2B59ACE000FEAD97 /* yuv_rgb_internal.h in Headers */,
+				F3D8BDFC2D6D2C7000B22FA1 /* SDL_eventwatch_c.h in Headers */,
 				F3FA5A242B59ACE000FEAD97 /* yuv_rgb_lsx.h in Headers */,
 				F3FA5A1E2B59ACE000FEAD97 /* yuv_rgb_lsx_func.h in Headers */,
 				F3FA5A1F2B59ACE000FEAD97 /* yuv_rgb_sse.h in Headers */,
@@ -2965,6 +2972,7 @@
 				566E26CF246274CC00718109 /* SDL_syslocale.m in Sources */,
 				A7D8AFC023E2514200DCD162 /* SDL_egl.c in Sources */,
 				A7D8AC3323E2514100DCD162 /* SDL_RLEaccel.c in Sources */,
+				F3D8BDFD2D6D2C7000B22FA1 /* SDL_eventwatch.c in Sources */,
 				F3EFA5F02D5AB97300BCF22F /* SDL_stb.c in Sources */,
 				A7D8BBB123E2514500DCD162 /* SDL_assert.c in Sources */,
 				A7D8B3DA23E2514300DCD162 /* SDL_bmp.c in Sources */,

+ 25 - 115
src/events/SDL_events.c

@@ -23,6 +23,8 @@
 // General event handling code for SDL
 
 #include "SDL_events_c.h"
+#include "SDL_eventwatch_c.h"
+#include "SDL_windowevents_c.h"
 #include "../SDL_hints_c.h"
 #include "../audio/SDL_audio_c.h"
 #include "../camera/SDL_camera_c.h"
@@ -98,19 +100,7 @@ typedef struct SDL2_SysWMmsg
     } msg;
 } SDL2_SysWMmsg;
 
-typedef struct SDL_EventWatcher
-{
-    SDL_EventFilter callback;
-    void *userdata;
-    bool removed;
-} SDL_EventWatcher;
-
-static SDL_Mutex *SDL_event_watchers_lock;
-static SDL_EventWatcher SDL_EventOK;
-static SDL_EventWatcher *SDL_event_watchers = NULL;
-static int SDL_event_watchers_count = 0;
-static bool SDL_event_watchers_dispatching = false;
-static bool SDL_event_watchers_removed = false;
+static SDL_EventWatchList SDL_event_watchers;
 static SDL_AtomicInt SDL_sentinel_pending;
 static Uint32 SDL_last_event_id = 0;
 
@@ -938,16 +928,8 @@ void SDL_StopEventLoop(void)
         SDL_disabled_events[i] = NULL;
     }
 
-    if (SDL_event_watchers_lock) {
-        SDL_DestroyMutex(SDL_event_watchers_lock);
-        SDL_event_watchers_lock = NULL;
-    }
-    if (SDL_event_watchers) {
-        SDL_free(SDL_event_watchers);
-        SDL_event_watchers = NULL;
-        SDL_event_watchers_count = 0;
-    }
-    SDL_zero(SDL_EventOK);
+    SDL_QuitEventWatchList(&SDL_event_watchers);
+    SDL_QuitWindowEventWatch();
 
     SDL_Mutex *lock = NULL;
     if (SDL_EventQ.lock) {
@@ -981,17 +963,19 @@ bool SDL_StartEventLoop(void)
     }
     SDL_LockMutex(SDL_EventQ.lock);
 
-    if (SDL_event_watchers_lock == NULL) {
-        SDL_event_watchers_lock = SDL_CreateMutex();
-        if (SDL_event_watchers_lock == NULL) {
-            SDL_UnlockMutex(SDL_EventQ.lock);
-            return false;
-        }
+    if (!SDL_InitEventWatchList(&SDL_event_watchers)) {
+        SDL_UnlockMutex(SDL_EventQ.lock);
+        return false;
     }
 #endif // !SDL_THREADS_DISABLED
 
+    SDL_InitWindowEventWatch();
+
     SDL_EventQ.active = true;
+
+#ifndef SDL_THREADS_DISABLED
     SDL_UnlockMutex(SDL_EventQ.lock);
+#endif
     return true;
 }
 
@@ -1735,44 +1719,11 @@ bool SDL_WaitEventTimeoutNS(SDL_Event *event, Sint64 timeoutNS)
 
 static bool SDL_CallEventWatchers(SDL_Event *event)
 {
-    if ((SDL_EventOK.callback || SDL_event_watchers_count > 0) &&
-        (event->common.type != SDL_EVENT_POLL_SENTINEL)) {
-        SDL_LockMutex(SDL_event_watchers_lock);
-        {
-            if (SDL_EventOK.callback && !SDL_EventOK.callback(SDL_EventOK.userdata, event)) {
-                SDL_UnlockMutex(SDL_event_watchers_lock);
-                return false;
-            }
-
-            if (SDL_event_watchers_count > 0) {
-                // Make sure we only dispatch the current watcher list
-                int i, event_watchers_count = SDL_event_watchers_count;
-
-                SDL_event_watchers_dispatching = true;
-                for (i = 0; i < event_watchers_count; ++i) {
-                    if (!SDL_event_watchers[i].removed) {
-                        SDL_event_watchers[i].callback(SDL_event_watchers[i].userdata, event);
-                    }
-                }
-                SDL_event_watchers_dispatching = false;
-
-                if (SDL_event_watchers_removed) {
-                    for (i = SDL_event_watchers_count; i--;) {
-                        if (SDL_event_watchers[i].removed) {
-                            --SDL_event_watchers_count;
-                            if (i < SDL_event_watchers_count) {
-                                SDL_memmove(&SDL_event_watchers[i], &SDL_event_watchers[i + 1], (SDL_event_watchers_count - i) * sizeof(SDL_event_watchers[i]));
-                            }
-                        }
-                    }
-                    SDL_event_watchers_removed = false;
-                }
-            }
-        }
-        SDL_UnlockMutex(SDL_event_watchers_lock);
+    if (event->common.type == SDL_EVENT_POLL_SENTINEL) {
+        return true;
     }
 
-    return true;
+    return SDL_DispatchEventWatchList(&SDL_event_watchers, event);
 }
 
 bool SDL_PushEvent(SDL_Event *event)
@@ -1796,11 +1747,11 @@ bool SDL_PushEvent(SDL_Event *event)
 void SDL_SetEventFilter(SDL_EventFilter filter, void *userdata)
 {
     SDL_EventEntry *event, *next;
-    SDL_LockMutex(SDL_event_watchers_lock);
+    SDL_LockMutex(SDL_event_watchers.lock);
     {
         // Set filter and discard pending events
-        SDL_EventOK.callback = filter;
-        SDL_EventOK.userdata = userdata;
+        SDL_event_watchers.filter.callback = filter;
+        SDL_event_watchers.filter.userdata = userdata;
         if (filter) {
             // Cut all events not accepted by the filter
             SDL_LockMutex(SDL_EventQ.lock);
@@ -1815,18 +1766,18 @@ void SDL_SetEventFilter(SDL_EventFilter filter, void *userdata)
             SDL_UnlockMutex(SDL_EventQ.lock);
         }
     }
-    SDL_UnlockMutex(SDL_event_watchers_lock);
+    SDL_UnlockMutex(SDL_event_watchers.lock);
 }
 
 bool SDL_GetEventFilter(SDL_EventFilter *filter, void **userdata)
 {
     SDL_EventWatcher event_ok;
 
-    SDL_LockMutex(SDL_event_watchers_lock);
+    SDL_LockMutex(SDL_event_watchers.lock);
     {
-        event_ok = SDL_EventOK;
+        event_ok = SDL_event_watchers.filter;
     }
-    SDL_UnlockMutex(SDL_event_watchers_lock);
+    SDL_UnlockMutex(SDL_event_watchers.lock);
 
     if (filter) {
         *filter = event_ok.callback;
@@ -1839,53 +1790,12 @@ bool SDL_GetEventFilter(SDL_EventFilter *filter, void **userdata)
 
 bool SDL_AddEventWatch(SDL_EventFilter filter, void *userdata)
 {
-    bool result = true;
-
-    SDL_LockMutex(SDL_event_watchers_lock);
-    {
-        SDL_EventWatcher *event_watchers;
-
-        event_watchers = (SDL_EventWatcher *)SDL_realloc(SDL_event_watchers, (SDL_event_watchers_count + 1) * sizeof(*event_watchers));
-        if (event_watchers) {
-            SDL_EventWatcher *watcher;
-
-            SDL_event_watchers = event_watchers;
-            watcher = &SDL_event_watchers[SDL_event_watchers_count];
-            watcher->callback = filter;
-            watcher->userdata = userdata;
-            watcher->removed = false;
-            ++SDL_event_watchers_count;
-        } else {
-            result = false;
-        }
-    }
-    SDL_UnlockMutex(SDL_event_watchers_lock);
-
-    return result;
+    return SDL_AddEventWatchList(&SDL_event_watchers, filter, userdata);
 }
 
 void SDL_RemoveEventWatch(SDL_EventFilter filter, void *userdata)
 {
-    SDL_LockMutex(SDL_event_watchers_lock);
-    {
-        int i;
-
-        for (i = 0; i < SDL_event_watchers_count; ++i) {
-            if (SDL_event_watchers[i].callback == filter && SDL_event_watchers[i].userdata == userdata) {
-                if (SDL_event_watchers_dispatching) {
-                    SDL_event_watchers[i].removed = true;
-                    SDL_event_watchers_removed = true;
-                } else {
-                    --SDL_event_watchers_count;
-                    if (i < SDL_event_watchers_count) {
-                        SDL_memmove(&SDL_event_watchers[i], &SDL_event_watchers[i + 1], (SDL_event_watchers_count - i) * sizeof(SDL_event_watchers[i]));
-                    }
-                }
-                break;
-            }
-        }
-    }
-    SDL_UnlockMutex(SDL_event_watchers_lock);
+    SDL_RemoveEventWatchList(&SDL_event_watchers, filter, userdata);
 }
 
 void SDL_FilterEvents(SDL_EventFilter filter, void *userdata)

+ 143 - 0
src/events/SDL_eventwatch.c

@@ -0,0 +1,143 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#include "SDL_eventwatch_c.h"
+
+
+bool SDL_InitEventWatchList(SDL_EventWatchList *list)
+{
+    if (list->lock == NULL) {
+        list->lock = SDL_CreateMutex();
+        if (list->lock == NULL) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void SDL_QuitEventWatchList(SDL_EventWatchList *list)
+{
+    if (list->lock) {
+        SDL_DestroyMutex(list->lock);
+        list->lock = NULL;
+    }
+    if (list->watchers) {
+        SDL_free(list->watchers);
+        list->watchers = NULL;
+        list->count = 0;
+    }
+    SDL_zero(list->filter);
+}
+
+bool SDL_DispatchEventWatchList(SDL_EventWatchList *list, SDL_Event *event)
+{
+    SDL_EventWatcher *filter = &list->filter;
+
+    if (!filter->callback && list->count == 0) {
+        return true;
+    }
+
+    SDL_LockMutex(list->lock);
+    {
+        // Make sure we only dispatch the current watcher list
+        int i, count = list->count;
+
+        if (filter->callback && !filter->callback(filter->userdata, event)) {
+            SDL_UnlockMutex(list->lock);
+            return false;
+        }
+
+        list->dispatching = true;
+        for (i = 0; i < count; ++i) {
+            if (!list->watchers[i].removed) {
+                list->watchers[i].callback(list->watchers[i].userdata, event);
+            }
+        }
+        list->dispatching = false;
+
+        if (list->removed) {
+            for (i = list->count; i--;) {
+                if (list->watchers[i].removed) {
+                    --list->count;
+                    if (i < list->count) {
+                        SDL_memmove(&list->watchers[i], &list->watchers[i + 1], (list->count - i) * sizeof(list->watchers[i]));
+                    }
+                }
+            }
+            list->removed = false;
+        }
+    }
+    SDL_UnlockMutex(list->lock);
+
+    return true;
+}
+
+bool SDL_AddEventWatchList(SDL_EventWatchList *list, SDL_EventFilter filter, void *userdata)
+{
+    bool result = true;
+
+    SDL_LockMutex(list->lock);
+    {
+        SDL_EventWatcher *watchers;
+
+        watchers = (SDL_EventWatcher *)SDL_realloc(list->watchers, (list->count + 1) * sizeof(*watchers));
+        if (watchers) {
+            SDL_EventWatcher *watcher;
+
+            list->watchers = watchers;
+            watcher = &list->watchers[list->count];
+            watcher->callback = filter;
+            watcher->userdata = userdata;
+            watcher->removed = false;
+            ++list->count;
+        } else {
+            result = false;
+        }
+    }
+    SDL_UnlockMutex(list->lock);
+
+    return result;
+}
+
+void SDL_RemoveEventWatchList(SDL_EventWatchList *list, SDL_EventFilter filter, void *userdata)
+{
+    SDL_LockMutex(list->lock);
+    {
+        int i;
+
+        for (i = 0; i < list->count; ++i) {
+            if (list->watchers[i].callback == filter && list->watchers[i].userdata == userdata) {
+                if (list->dispatching) {
+                    list->watchers[i].removed = true;
+                    list->removed = true;
+                } else {
+                    --list->count;
+                    if (i < list->count) {
+                        SDL_memmove(&list->watchers[i], &list->watchers[i + 1], (list->count - i) * sizeof(list->watchers[i]));
+                    }
+                }
+                break;
+            }
+        }
+    }
+    SDL_UnlockMutex(list->lock);
+}

+ 45 - 0
src/events/SDL_eventwatch_c.h

@@ -0,0 +1,45 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+typedef struct SDL_EventWatcher
+{
+    SDL_EventFilter callback;
+    void *userdata;
+    bool removed;
+} SDL_EventWatcher;
+
+typedef struct SDL_EventWatchList
+{
+    SDL_Mutex *lock;
+    SDL_EventWatcher filter;
+    SDL_EventWatcher *watchers;
+    int count;
+    bool dispatching;
+    bool removed;
+} SDL_EventWatchList;
+
+
+extern bool SDL_InitEventWatchList(SDL_EventWatchList *list);
+extern void SDL_QuitEventWatchList(SDL_EventWatchList *list);
+extern bool SDL_DispatchEventWatchList(SDL_EventWatchList *list, SDL_Event *event);
+extern bool SDL_AddEventWatchList(SDL_EventWatchList *list, SDL_EventFilter filter, void *userdata);
+extern void SDL_RemoveEventWatchList(SDL_EventWatchList *list, SDL_EventFilter filter, void *userdata);

+ 32 - 5
src/events/SDL_windowevents.c

@@ -23,10 +23,39 @@
 // Window event handling code for SDL
 
 #include "SDL_events_c.h"
+#include "SDL_eventwatch_c.h"
 #include "SDL_mouse_c.h"
-#include "../render/SDL_sysrender.h"
 #include "../tray/SDL_tray_utils.h"
 
+
+#define NUM_WINDOW_EVENT_WATCH_PRIORITIES (SDL_WINDOW_EVENT_WATCH_NORMAL + 1)
+
+static SDL_EventWatchList SDL_window_event_watchers[NUM_WINDOW_EVENT_WATCH_PRIORITIES];
+
+void SDL_InitWindowEventWatch(void)
+{
+    for (int i = 0; i < SDL_arraysize(SDL_window_event_watchers); ++i) {
+        SDL_InitEventWatchList(&SDL_window_event_watchers[i]);
+    }
+}
+
+void SDL_QuitWindowEventWatch(void)
+{
+    for (int i = 0; i < SDL_arraysize(SDL_window_event_watchers); ++i) {
+        SDL_QuitEventWatchList(&SDL_window_event_watchers[i]);
+    }
+}
+
+void SDL_AddWindowEventWatch(SDL_WindowEventWatchPriority priority, SDL_EventFilter filter, void *userdata)
+{
+    SDL_AddEventWatchList(&SDL_window_event_watchers[priority], filter, userdata);
+}
+
+void SDL_RemoveWindowEventWatch(SDL_WindowEventWatchPriority priority, SDL_EventFilter filter, void *userdata)
+{
+    SDL_RemoveEventWatchList(&SDL_window_event_watchers[priority], filter, userdata);
+}
+
 static bool SDLCALL RemoveSupercededWindowEvents(void *userdata, SDL_Event *event)
 {
     SDL_Event *new_event = (SDL_Event *)userdata;
@@ -191,10 +220,8 @@ bool SDL_SendWindowEvent(SDL_Window *window, SDL_EventType windowevent, int data
     event.window.data2 = data2;
     event.window.windowID = window->id;
 
-    for (int i = 0; i < window->num_renderers; ++i) {
-        SDL_Renderer *renderer = window->renderers[i];
-        SDL_RendererEventWatch(renderer, &event);
-    }
+    SDL_DispatchEventWatchList(&SDL_window_event_watchers[SDL_WINDOW_EVENT_WATCH_EARLY], &event);
+    SDL_DispatchEventWatchList(&SDL_window_event_watchers[SDL_WINDOW_EVENT_WATCH_NORMAL], &event);
 
     if (SDL_EventEnabled(windowevent)) {
         // Fixes queue overflow with move/resize events that aren't processed

+ 11 - 0
src/events/SDL_windowevents_c.h

@@ -23,6 +23,17 @@
 #ifndef SDL_windowevents_c_h_
 #define SDL_windowevents_c_h_
 
+typedef enum
+{
+    SDL_WINDOW_EVENT_WATCH_EARLY,
+    SDL_WINDOW_EVENT_WATCH_NORMAL
+} SDL_WindowEventWatchPriority;
+
+extern void SDL_InitWindowEventWatch(void);
+extern void SDL_QuitWindowEventWatch(void);
+extern void SDL_AddWindowEventWatch(SDL_WindowEventWatchPriority priority, SDL_EventFilter filter, void *userdata);
+extern void SDL_RemoveWindowEventWatch(SDL_WindowEventWatchPriority priority, SDL_EventFilter filter, void *userdata);
+
 extern bool SDL_SendWindowEvent(SDL_Window *window, SDL_EventType windowevent, int data1, int data2);
 
 #endif // SDL_windowevents_c_h_

+ 10 - 1
src/render/SDL_render.c

@@ -25,6 +25,7 @@
 #include "SDL_sysrender.h"
 #include "SDL_render_debug_font.h"
 #include "software/SDL_render_sw_c.h"
+#include "../events/SDL_windowevents_c.h"
 #include "../video/SDL_pixels_c.h"
 #include "../video/SDL_video_c.h"
 
@@ -820,8 +821,9 @@ const char *SDL_GetRenderDriver(int index)
 #endif
 }
 
-void SDL_RendererEventWatch(SDL_Renderer *renderer, SDL_Event *event)
+static bool SDL_RendererEventWatch(void *userdata, SDL_Event *event)
 {
+    SDL_Renderer *renderer = (SDL_Renderer *)userdata;
     SDL_Window *window = renderer->window;
 
     if (renderer->WindowEvent) {
@@ -849,6 +851,7 @@ void SDL_RendererEventWatch(SDL_Renderer *renderer, SDL_Event *event)
                event->type == SDL_EVENT_WINDOW_HDR_STATE_CHANGED) {
         UpdateHDRProperties(renderer);
     }
+    return true;
 }
 
 bool SDL_CreateWindowAndRenderer(const char *title, int width, int height, SDL_WindowFlags window_flags, SDL_Window **window, SDL_Renderer **renderer)
@@ -1107,6 +1110,10 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props)
 
     SDL_SetRenderViewport(renderer, NULL);
 
+    if (window) {
+        SDL_AddWindowEventWatch(SDL_WINDOW_EVENT_WATCH_NORMAL, SDL_RendererEventWatch, renderer);
+    }
+
     int vsync = (int)SDL_GetNumberProperty(props, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, 0);
     if (!SDL_SetRenderVSync(renderer, vsync)) {
         if (vsync == 0) {
@@ -5224,6 +5231,8 @@ void SDL_DestroyRendererWithoutFreeing(SDL_Renderer *renderer)
 
     renderer->destroyed = true;
 
+    SDL_RemoveWindowEventWatch(SDL_WINDOW_EVENT_WATCH_NORMAL, SDL_RendererEventWatch, renderer);
+
     if (renderer->window) {
         SDL_PropertiesID props = SDL_GetWindowProperties(renderer->window);
         if (SDL_GetPointerProperty(props, SDL_PROP_WINDOW_RENDERER_POINTER, NULL) == renderer) {

+ 0 - 3
src/render/SDL_sysrender.h

@@ -339,9 +339,6 @@ extern SDL_RenderDriver GPU_RenderDriver;
 // Clean up any renderers at shutdown
 extern void SDL_QuitRender(void);
 
-// Handle window events for a renderer
-extern void SDL_RendererEventWatch(SDL_Renderer *renderer, SDL_Event *event);
-
 // Add a supported texture format to a renderer
 extern bool SDL_AddSupportedTextureFormat(SDL_Renderer *renderer, SDL_PixelFormat format);
 

+ 4 - 2
src/video/cocoa/SDL_cocoametalview.m

@@ -26,6 +26,8 @@
  */
 #include "SDL_internal.h"
 
+#include "../../events/SDL_windowevents_c.h"
+
 #import "SDL_cocoametalview.h"
 
 #if defined(SDL_VIDEO_DRIVER_COCOA) && (defined(SDL_VIDEO_VULKAN) || defined(SDL_VIDEO_METAL))
@@ -88,7 +90,7 @@ static bool SDLCALL SDL_MetalViewEventWatch(void *userdata, SDL_Event *event)
 
         self.layer.opaque = opaque;
 
-        SDL_AddEventWatch(SDL_MetalViewEventWatch, (__bridge void *)(self));
+        SDL_AddWindowEventWatch(SDL_WINDOW_EVENT_WATCH_EARLY, SDL_MetalViewEventWatch, (__bridge void *)(self));
 
         [self updateDrawableSize];
     }
@@ -98,7 +100,7 @@ static bool SDLCALL SDL_MetalViewEventWatch(void *userdata, SDL_Event *event)
 
 - (void)dealloc
 {
-    SDL_RemoveEventWatch(SDL_MetalViewEventWatch, (__bridge void *)(self));
+    SDL_RemoveWindowEventWatch(SDL_WINDOW_EVENT_WATCH_EARLY, SDL_MetalViewEventWatch, (__bridge void *)(self));
 }
 
 - (NSInteger)tag