From db4e6c1931d0d63d0ffed34d1d409699f921c3bb Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 24 Feb 2025 15:01:53 -0800
Subject: [PATCH] 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
---
VisualC-GDK/SDL/SDL.vcxproj | 2 +
VisualC-GDK/SDL/SDL.vcxproj.filters | 2 +
VisualC/SDL/SDL.vcxproj | 2 +
VisualC/SDL/SDL.vcxproj.filters | 6 +
Xcode/SDL/SDL.xcodeproj/project.pbxproj | 30 +++--
src/events/SDL_events.c | 140 +++++------------------
src/events/SDL_eventwatch.c | 143 ++++++++++++++++++++++++
src/events/SDL_eventwatch_c.h | 45 ++++++++
src/events/SDL_windowevents.c | 37 +++++-
src/events/SDL_windowevents_c.h | 11 ++
src/render/SDL_render.c | 11 +-
src/render/SDL_sysrender.h | 3 -
src/video/cocoa/SDL_cocoametalview.m | 6 +-
13 files changed, 301 insertions(+), 137 deletions(-)
create mode 100644 src/events/SDL_eventwatch.c
create mode 100644 src/events/SDL_eventwatch_c.h
diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj
index 09563a63d3303..5bf000436fb93 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj
+++ b/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" />
diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters
index dd2c4639cfc94..f760877e214e9 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj.filters
+++ b/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" />
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index 07a16f707970d..a15978a29cd71 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/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" />
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index c0a23b4318d5d..d653ee05f1d3d 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/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>
diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
index 8e4f9fa033929..2a63a2eead5ef 100644
--- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj
+++ b/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 */,
diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c
index ebce863bb5e00..e7216ee408b50 100644
--- a/src/events/SDL_events.c
+++ b/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)
diff --git a/src/events/SDL_eventwatch.c b/src/events/SDL_eventwatch.c
new file mode 100644
index 0000000000000..08e7248c6a487
--- /dev/null
+++ b/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);
+}
diff --git a/src/events/SDL_eventwatch_c.h b/src/events/SDL_eventwatch_c.h
new file mode 100644
index 0000000000000..c9aea38c0dd2b
--- /dev/null
+++ b/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);
diff --git a/src/events/SDL_windowevents.c b/src/events/SDL_windowevents.c
index e0364f8ee0333..e20cd3ab0da7a 100644
--- a/src/events/SDL_windowevents.c
+++ b/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
diff --git a/src/events/SDL_windowevents_c.h b/src/events/SDL_windowevents_c.h
index b7f0681e6d4ef..720430546afd7 100644
--- a/src/events/SDL_windowevents_c.h
+++ b/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_
diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index 4c438debc3272..52f0b38257ae9 100644
--- a/src/render/SDL_render.c
+++ b/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) {
diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h
index 375419a06acaf..9c39bb1de71f4 100644
--- a/src/render/SDL_sysrender.h
+++ b/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);
diff --git a/src/video/cocoa/SDL_cocoametalview.m b/src/video/cocoa/SDL_cocoametalview.m
index d8566451156e9..af84e935864d5 100644
--- a/src/video/cocoa/SDL_cocoametalview.m
+++ b/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 @@ - (instancetype)
(Patch may be truncated, please check the link at the top of this post.)