sdl2-compat: events: Cleaned up SDL_Event incompatibility. Library works again!

From 06784e6ca9c72ac90fad1aca1809bda0f4b9dd6c Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Sun, 11 Dec 2022 23:35:28 -0500
Subject: [PATCH] events: Cleaned up SDL_Event incompatibility. Library works
 again!

Reference Issue #3.
---
 src/sdl2_compat.c | 677 ++++++++++++++++++++++++++++++++++++++++++++--
 src/sdl3_syms.h   |  20 +-
 2 files changed, 667 insertions(+), 30 deletions(-)

diff --git a/src/sdl2_compat.c b/src/sdl2_compat.c
index e4f6b23..013c94e 100644
--- a/src/sdl2_compat.c
+++ b/src/sdl2_compat.c
@@ -208,6 +208,10 @@ static char loaderror[256];
 #define DIRSEP "/"
 #endif
 
+/* init stuff we want to do after SDL3 is loaded but before the app has access to it. */
+static int SDL2Compat_InitOnStartup(void);
+
+
 static void *
 LoadSDL3Symbol(const char *fn, int *okay)
 {
@@ -401,6 +405,9 @@ LoadSDL3(void)
                     SDL2Compat_ApplyQuirks(force_x11);  /* Apply and maybe print a list of any enabled quirks. */
                 }
             }
+            if (okay) {
+                okay = SDL2Compat_InitOnStartup();
+            }
             if (!okay) {
                 UnloadSDL3();
             }
@@ -484,6 +491,447 @@ BOOL WINAPI _DllMainCRTStartup(HANDLE dllhandle, DWORD reason, LPVOID reserved)
     #error Please define an init procedure for your platform.
 #endif
 
+
+/* this enum changed in SDL3. */
+typedef enum
+{
+    SDL2_SYSWM_UNKNOWN,
+    SDL2_SYSWM_WINDOWS,
+    SDL2_SYSWM_X11,
+    SDL2_SYSWM_DIRECTFB,
+    SDL2_SYSWM_COCOA,
+    SDL2_SYSWM_UIKIT,
+    SDL2_SYSWM_WAYLAND,
+    SDL2_SYSWM_MIR,
+    SDL2_SYSWM_WINRT,
+    SDL2_SYSWM_ANDROID,
+    SDL2_SYSWM_VIVANTE,
+    SDL2_SYSWM_OS2,
+    SDL2_SYSWM_HAIKU,
+    SDL2_SYSWM_KMSDRM,
+    SDL2_SYSWM_RISCOS
+} SDL2_SYSWM_TYPE;
+
+/* Events changed in SDL3; notably, the `timestamp` field moved from
+   32 bit milliseconds to 64-bit nanoseconds, and the padding of the union
+   changed, so all the SDL2 structs have to be reproduced here. */
+
+/* Note that SDL_EventType _currently_ lines up (although some types have
+   come and gone in SDL3, so we don't manage an SDL2 copy here atm. */
+
+typedef struct SDL2_CommonEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+} SDL2_CommonEvent;
+
+/**
+ *  \brief Display state change event data (event.display.*)
+ */
+typedef struct SDL2_DisplayEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    Uint32 display;
+    Uint8 event;
+    Uint8 padding1;
+    Uint8 padding2;
+    Uint8 padding3;
+    Sint32 data1;
+} SDL2_DisplayEvent;
+
+typedef struct SDL2_WindowEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    Uint32 windowID;
+    Uint8 event;
+    Uint8 padding1;
+    Uint8 padding2;
+    Uint8 padding3;
+    Sint32 data1;
+    Sint32 data2;
+} SDL2_WindowEvent;
+
+typedef struct SDL2_KeyboardEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    Uint32 windowID;
+    Uint8 state;
+    Uint8 repeat;
+    Uint8 padding2;
+    Uint8 padding3;
+    SDL_Keysym keysym;
+} SDL2_KeyboardEvent;
+
+#define SDL2_TEXTEDITINGEVENT_TEXT_SIZE (32)
+typedef struct SDL2_TextEditingEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    Uint32 windowID;
+    char text[SDL2_TEXTEDITINGEVENT_TEXT_SIZE];
+    Sint32 start;
+    Sint32 length;
+} SDL2_TextEditingEvent;
+
+typedef struct SDL2_TextEditingExtEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    Uint32 windowID;
+    char* text;
+    Sint32 start;
+    Sint32 length;
+} SDL2_TextEditingExtEvent;
+
+#define SDL2_TEXTINPUTEVENT_TEXT_SIZE (32)
+typedef struct SDL2_TextInputEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    Uint32 windowID;
+    char text[SDL2_TEXTINPUTEVENT_TEXT_SIZE];
+} SDL2_TextInputEvent;
+
+typedef struct SDL2_MouseMotionEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    Uint32 windowID;
+    Uint32 which;
+    Uint32 state;
+    Sint32 x;
+    Sint32 y;
+    Sint32 xrel;
+    Sint32 yrel;
+} SDL2_MouseMotionEvent;
+
+typedef struct SDL2_MouseButtonEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    Uint32 windowID;
+    Uint32 which;
+    Uint8 button;
+    Uint8 state;
+    Uint8 clicks;
+    Uint8 padding1;
+    Sint32 x;
+    Sint32 y;
+} SDL2_MouseButtonEvent;
+
+typedef struct SDL2_MouseWheelEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    Uint32 windowID;
+    Uint32 which;
+    Sint32 x;
+    Sint32 y;
+    Uint32 direction;
+    float preciseX;
+    float preciseY;
+    Sint32 mouseX;
+    Sint32 mouseY;
+} SDL2_MouseWheelEvent;
+
+typedef struct SDL2_JoyAxisEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    SDL_JoystickID which;
+    Uint8 axis;
+    Uint8 padding1;
+    Uint8 padding2;
+    Uint8 padding3;
+    Sint16 value;
+    Uint16 padding4;
+} SDL2_JoyAxisEvent;
+
+typedef struct SDL2_JoyBallEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    SDL_JoystickID which;
+    Uint8 ball;
+    Uint8 padding1;
+    Uint8 padding2;
+    Uint8 padding3;
+    Sint16 xrel;
+    Sint16 yrel;
+} SDL2_JoyBallEvent;
+
+typedef struct SDL2_JoyHatEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    SDL_JoystickID which;
+    Uint8 hat;
+    Uint8 value;
+    Uint8 padding1;
+    Uint8 padding2;
+} SDL2_JoyHatEvent;
+
+typedef struct SDL2_JoyButtonEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    SDL_JoystickID which;
+    Uint8 button;
+    Uint8 state;
+    Uint8 padding1;
+    Uint8 padding2;
+} SDL2_JoyButtonEvent;
+
+typedef struct SDL2_JoyDeviceEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    Sint32 which;
+} SDL2_JoyDeviceEvent;
+
+typedef struct SDL2_JoyBatteryEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    SDL_JoystickID which;
+    SDL_JoystickPowerLevel level;
+} SDL2_JoyBatteryEvent;
+
+typedef struct SDL2_ControllerAxisEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    SDL_JoystickID which;
+    Uint8 axis;
+    Uint8 padding1;
+    Uint8 padding2;
+    Uint8 padding3;
+    Sint16 value;
+    Uint16 padding4;
+} SDL2_ControllerAxisEvent;
+
+typedef struct SDL2_ControllerButtonEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    SDL_JoystickID which;
+    Uint8 button;
+    Uint8 state;
+    Uint8 padding1;
+    Uint8 padding2;
+} SDL2_ControllerButtonEvent;
+
+typedef struct SDL2_ControllerDeviceEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    Sint32 which;
+} SDL2_ControllerDeviceEvent;
+
+typedef struct SDL2_ControllerTouchpadEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    SDL_JoystickID which;
+    Sint32 touchpad;
+    Sint32 finger;
+    float x;
+    float y;
+    float pressure;
+} SDL2_ControllerTouchpadEvent;
+
+typedef struct SDL2_ControllerSensorEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    SDL_JoystickID which;
+    Sint32 sensor;
+    float data[3];
+    Uint64 timestamp_us;
+} SDL2_ControllerSensorEvent;
+
+typedef struct SDL2_AudioDeviceEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    Uint32 which;
+    Uint8 iscapture;
+    Uint8 padding1;
+    Uint8 padding2;
+    Uint8 padding3;
+} SDL2_AudioDeviceEvent;
+
+typedef struct SDL2_TouchFingerEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    SDL_TouchID touchId;
+    SDL_FingerID fingerId;
+    float x;
+    float y;
+    float dx;
+    float dy;
+    float pressure;
+    Uint32 windowID;
+} SDL2_TouchFingerEvent;
+
+typedef struct SDL2_MultiGestureEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    SDL_TouchID touchId;
+    float dTheta;
+    float dDist;
+    float x;
+    float y;
+    Uint16 numFingers;
+    Uint16 padding;
+} SDL2_MultiGestureEvent;
+
+typedef struct SDL2_DollarGestureEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    SDL_TouchID touchId;
+    SDL_GestureID gestureId;
+    Uint32 numFingers;
+    float error;
+    float x;
+    float y;
+} SDL2_DollarGestureEvent;
+
+typedef struct SDL2_DropEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    char *file;
+    Uint32 windowID;
+} SDL2_DropEvent;
+
+typedef struct SDL2_SensorEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    Sint32 which;
+    float data[6];
+    Uint64 timestamp_us;
+} SDL2_SensorEvent;
+
+typedef struct SDL2_QuitEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+} SDL2_QuitEvent;
+
+typedef struct SDL2_OSEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+} SDL2_OSEvent;
+
+typedef struct SDL2_UserEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    Uint32 windowID;
+    Sint32 code;
+    void *data1;
+    void *data2;
+} SDL2_UserEvent;
+
+struct SDL2_SysWMmsg;
+typedef struct SDL2_SysWMmsg SDL2_SysWMmsg;
+
+typedef struct SDL2_SysWMEvent
+{
+    Uint32 type;
+    Uint32 timestamp;
+    SDL2_SysWMmsg *msg;
+} SDL2_SysWMEvent;
+
+typedef union SDL2_Event
+{
+    Uint32 type;
+    SDL2_CommonEvent common;
+    SDL2_DisplayEvent display;
+    SDL2_WindowEvent window;
+    SDL2_KeyboardEvent key;
+    SDL2_TextEditingEvent edit;
+    SDL2_TextEditingExtEvent editExt;
+    SDL2_TextInputEvent text;
+    SDL2_MouseMotionEvent motion;
+    SDL2_MouseButtonEvent button;
+    SDL2_MouseWheelEvent wheel;
+    SDL2_JoyAxisEvent jaxis;
+    SDL2_JoyBallEvent jball;
+    SDL2_JoyHatEvent jhat;
+    SDL2_JoyButtonEvent jbutton;
+    SDL2_JoyDeviceEvent jdevice;
+    SDL2_JoyBatteryEvent jbattery;
+    SDL2_ControllerAxisEvent caxis;
+    SDL2_ControllerButtonEvent cbutton;
+    SDL2_ControllerDeviceEvent cdevice;
+    SDL2_ControllerTouchpadEvent ctouchpad;
+    SDL2_ControllerSensorEvent csensor;
+    SDL2_AudioDeviceEvent adevice;
+    SDL2_SensorEvent sensor;
+    SDL2_QuitEvent quit;
+    SDL2_UserEvent user;
+    SDL2_SysWMEvent syswm;
+    SDL2_TouchFingerEvent tfinger;
+    SDL2_MultiGestureEvent mgesture;
+    SDL2_DollarGestureEvent dgesture;
+    SDL2_DropEvent drop;
+    Uint8 padding[sizeof(void *) <= 8 ? 56 : sizeof(void *) == 16 ? 64 : 3 * sizeof(void *)];
+} SDL2_Event;
+
+/* Make sure we haven't broken binary compatibility */
+SDL_COMPILE_TIME_ASSERT(SDL2_Event, sizeof(SDL2_Event) == sizeof(((SDL2_Event *)NULL)->padding));
+
+typedef int (SDLCALL *SDL2_EventFilter) (void *userdata, SDL2_Event * event);
+
+typedef struct EventFilterWrapperData
+{
+    SDL2_EventFilter filter2;
+    void *userdata;
+    struct EventFilterWrapperData *next;
+} EventFilterWrapperData;
+
+
+/* Some SDL2 state we need to keep... */
+
+static SDL2_EventFilter EventFilter2 = NULL;
+static void *EventFilterUserData2 = NULL;
+static SDL_mutex *EventWatchListMutex = NULL;
+static EventFilterWrapperData *EventWatchers2 = NULL;
+
+
+/* Functions! */
+
+static int SDLCALL EventFilter3to2(void *userdata, SDL_Event *event3);
+
+/* this stuff _might_ move to SDL_Init later */
+static int
+SDL2Compat_InitOnStartup(void)
+{
+    int okay = 1;
+    EventWatchListMutex = SDL3_CreateMutex();
+    if (!EventWatchListMutex) {
+        okay = 0;
+    } else {
+        SDL3_SetEventFilter(EventFilter3to2, NULL);
+    }
+
+    if (!okay) {
+        strcpy_fn(loaderror, "Failed to initialize sdl2-compat library.");
+    }
+    return okay;
+}
+
+
 /* obviously we have to override this so we don't report ourselves as SDL3. */
 DECLSPEC void SDLCALL
 SDL_GetVersion(SDL_version * ver)
@@ -611,26 +1059,6 @@ SDL_LOG_IMPL(Critical, CRITICAL)
 #undef SDL_LOG_IMPL
 
 
-/* this enum changed in SDL3. */
-typedef enum
-{
-    SDL2_SYSWM_UNKNOWN,
-    SDL2_SYSWM_WINDOWS,
-    SDL2_SYSWM_X11,
-    SDL2_SYSWM_DIRECTFB,
-    SDL2_SYSWM_COCOA,
-    SDL2_SYSWM_UIKIT,
-    SDL2_SYSWM_WAYLAND,
-    SDL2_SYSWM_MIR,
-    SDL2_SYSWM_WINRT,
-    SDL2_SYSWM_ANDROID,
-    SDL2_SYSWM_VIVANTE,
-    SDL2_SYSWM_OS2,
-    SDL2_SYSWM_HAIKU,
-    SDL2_SYSWM_KMSDRM,
-    SDL2_SYSWM_RISCOS
-} SDL2_SYSWM_TYPE;
-
 #if 0
 static SDL2_SYSWM_TYPE
 SysWmType3to2(const SDL_SYSWM_TYPE typ3)
@@ -655,6 +1083,215 @@ SysWmType3to2(const SDL_SYSWM_TYPE typ3)
 #endif
 
 
+/* (current) strategy for SDL_Events:
+   in sdl12-compat, we built our own event queue, so when the SDL2 queue is pumped, we
+   took the events we cared about and added them to the sdl12-compat queue, and otherwise
+   just cleared the real SDL2 queue when we were able.
+   for sdl2-compat, we're going to try to use the SDL3 queue directly, and simply convert
+   individual event structs when the SDL2-based app wants to consume or produce events.
+   The queue has gotten to be significantly more complex in the SDL2 era, so rather than
+   try to reproduce this (or outright copy this) and work in parallel with SDL3, we'll just
+   try to work _with_ it. */
+
+/* Note: as events change type (like the SDL2 window event is broken up into several events), we'll need to
+   convert and push the SDL2 equivalent into the queue, but we don't care about new SDL3 event types, as
+   any app could get an unknown event type anyhow, as SDL development progressed or a library registered
+   a user event, etc, so we don't bother filtering new SDL3 types out. */
+
+static SDL2_Event *
+Event3to2(const SDL_Event *event3, SDL2_Event *event2)
+{
+#if 0
+    if (event3->type == SDL_SYSWMEVENT) {
+        return SDL_FALSE;  /* !!! FIXME: figure out what to do with this. */
+    }
+#endif
+
+    /* currently everything _mostly_ matches up between SDL2 and SDL3, but this might
+       drift more as SDL3 development continues. */
+
+    /* for now, the timestamp field has grown in size (and precision), everything after it is currently the same, minus padding at the end, so bump the fields down. */
+    event2->common.type = event3->type;
+    event2->common.timestamp = (Uint32) SDL_NS_TO_MS(event3->common.timestamp);
+    SDL3_memcpy((&event2->common) + 1, (&event3->common) + 1, sizeof (SDL2_Event) - sizeof (SDL2_CommonEvent));
+    return event2;
+}
+
+static SDL_Event *
+Event2to3(const SDL2_Event *event2, SDL_Event *event3)
+{
+#if 0
+    if (event2->type == SDL_SYSWMEVENT) {
+        return SDL_FALSE;  /* !!! FIXME: figure out what to do with this. */
+    }
+#endif
+
+    /* currently everything _mostly_ matches up between SDL2 and SDL3, but this might
+       drift more as SDL3 development continues. */
+
+    /* for now, the timestamp field has grown in size (and precision), everything after it is currently the same, minus padding at the end, so bump the fields down. */
+    event3->common.type = event2->type;
+    event3->common.timestamp = (Uint64) SDL_MS_TO_NS(event2->common.timestamp);
+    SDL3_memcpy((&event3->common) + 1, (&event2->common) + 1, sizeof (SDL_Event) - sizeof (SDL_CommonEvent));
+    return event3;
+}
+
+static int SDLCALL
+EventFilter3to2(void *userdata, SDL_Event *event3)
+{
+    SDL2_Event event2;  /* note that event filters do not receive events as const! So we have to convert or copy it for each one! */
+    if (EventFilter2) {
+        return EventFilter2(EventFilterUserData2, Event3to2(event3, &event2));
+    }
+
+    /* !!! FIXME: eventually, push new events when we need to convert something, like toplevel SDL3 events generating the SDL2 SDL_WINDOWEVENT. */
+
+    if (EventWatchers2 != NULL) {
+        EventFilterWrapperData *i;
+        SDL3_LockMutex(EventWatchListMutex);
+        for (i = EventWatchers2; i != NULL; i = i->next) {
+            i->filter2(i->userdata, Event3to2(event3, &event2));
+        }
+        SDL3_UnlockMutex(EventWatchListMutex);
+    }
+    return 1;
+}
+
+
+DECLSPEC void SDLCALL
+SDL_SetEventFilter(SDL2_EventFilter filter2, void *userdata)
+{
+    EventFilter2 = filter2;
+    EventFilterUserData2 = userdata;
+}
+
+DECLSPEC SDL_bool SDLCALL
+SDL_GetEventFilter(SDL2_EventFilter *filter2, void **userdata)
+{
+    if (!EventFilter2) {
+        return SDL_FALSE;
+    }
+
+    if (filter2) {
+        *filter2 = EventFilter2;
+    }
+
+    if (userdata) {
+        *userdata = EventFilterUserData2;
+    }
+
+    return SDL_TRUE;
+}
+
+DECLSPEC int SDLCALL
+SDL_PeepEvents(SDL2_Event *events2, int numevents, SDL_eventaction action, Uint32 minType, Uint32 maxType)
+{
+    SDL_Event *events3 = (SDL_Event *) SDL3_malloc(numevents * sizeof (SDL_Event));
+    int retval = 0;
+    int i;
+
+    if (!events3) {
+        return SDL_OutOfMemory();
+    } else if (action == SDL_ADDEVENT) {
+        for (i = 0; i < numevents; i++) {
+            Event2to3(&events2[i], &events3[i]);
+        }
+        retval = SDL3_PeepEvents(events3, numevents, action, minType, maxType);
+    } else {  /* SDL2 assumes it's SDL_PEEKEVENT if it isn't SDL_ADDEVENT or SDL_GETEVENT. */
+        retval = SDL3_PeepEvents(events3, numevents, action, minType, maxType);
+        for (i = 0; i < retval; i++) {
+            Event3to2(&events3[i], &events2[i]);
+        }
+    }
+
+    SDL_free(events3);
+    return retval;
+}
+
+DECLSPEC int SDLCALL
+SDL_WaitEventTimeout(SDL2_Event *event2, int timeout)
+{
+    SDL_Event event3;
+    const int retval = SDL3_WaitEventTimeout(&event3, timeout);
+    if (retval == 1) {
+        Event3to2(&event3, event2);
+    }
+    return retval;
+}
+
+DECLSPEC int SDLCALL
+SDL_PollEvent(SDL2_Event *event2)
+{
+    return SDL_WaitEventTimeout(event2, 0);
+}
+
+DECLSPEC int SDLCALL
+SDL_WaitEvent(SDL2_Event *event2)
+{
+    return SDL_WaitEventTimeout(event2, -1);
+}
+
+DECLSPEC int SDLCALL
+SDL_PushEvent(SDL2_Event *event2)
+{
+    SDL_Event event3;
+    return SDL3_PushEvent(Event2to3(event2, &event3));
+}
+
+DECLSPEC void SDLCALL
+SDL_AddEventWatch(SDL2_EventFilter filter2, void *userdata)
+{
+    /* we set up an SDL3 event filter to manage things already; we will also use it to call all added SDL2 event watchers. Put this new one in that list. */
+    EventFilterWrapperData *wrapperdata = (EventFilterWrapperData *) SDL_malloc(sizeof (EventFilterWrapperData));
+    if (!wrapperdata) {
+        return;  /* oh well. */
+    }
+    wrapperdata->filter2 = filter2;
+    wrapperdata->userdata = userdata;
+    SDL3_LockMutex(EventWatchListMutex);
+    wrapperdata->next = EventWatchers2;
+    EventWatchers2 = wrapperdata;
+    SDL3_UnlockMutex(EventWatchListMutex);
+}
+
+DECLSPEC void SDLCALL
+SDL_DelEventWatch(SDL2_EventFilter filter2, void *userdata)
+{
+    EventFilterWrapperData *i;
+    EventFilterWrapperData *prev = NULL;
+    SDL3_LockMutex(EventWatchListMutex);
+    for (i = EventWatchers2; i != NULL; i = i->next) {
+        if ((i->filter2 == filter2) && (i->userdata == userdata)) {
+            if (prev) {
+                SDL_assert(i != EventWatchers2);
+                prev->next = i->next;
+            } else {
+                SDL_assert(i == EventWatchers2);
+                EventWatchers2 = i->next;
+            }
+            SDL3_free(i);
+            break;
+        }
+    }
+    SDL3_UnlockMutex(EventWatchListMutex);
+}
+
+static int SDLCALL
+EventFilterWrapper3to2(void *userdata, SDL_Event *event)
+{
+    const EventFilterWrapperData *wrapperdata = (const EventFilterWrapperData *) userdata;
+    SDL2_Event event2;
+    return wrapperdata->filter2(wrapperdata->userdata, Event3to2(event, &event2));
+}
+
+DECLSPEC void SDLCALL
+SDL_FilterEvents(SDL2_EventFilter filter2, void *userdata)
+{
+    EventFilterWrapperData wrapperdata = { filter2, userdata, NULL };
+    SDL3_FilterEvents(EventFilterWrapper3to2, &wrapperdata);
+}
+
+
 /* stdio SDL_RWops was removed from SDL3, to prevent incompatible C runtime issues */
 #if !HAVE_STDIO_H
 DECLSPEC SDL_RWops * SDLCALL
diff --git a/src/sdl3_syms.h b/src/sdl3_syms.h
index 1ff2949..52440cc 100644
--- a/src/sdl3_syms.h
+++ b/src/sdl3_syms.h
@@ -137,20 +137,20 @@ SDL3_SYM(const char*,GetError,(void),(),return)
 SDL3_SYM_PASSTHROUGH(void,ClearError,(void),(),)
 SDL3_SYM_PASSTHROUGH(int,Error,(SDL_errorcode a),(a),return)
 SDL3_SYM_PASSTHROUGH(void,PumpEvents,(void),(),)
-SDL3_SYM_PASSTHROUGH(int,PeepEvents,(SDL_Event *a, int b, SDL_eventaction c, Uint32 d, Uint32 e),(a,b,c,d,e),return)
+SDL3_SYM(int,PeepEvents,(SDL_Event *a, int b, SDL_eventaction c, Uint32 d, Uint32 e),(a,b,c,d,e),return)
 SDL3_SYM_PASSTHROUGH(SDL_bool,HasEvent,(Uint32 a),(a),return)
 SDL3_SYM_PASSTHROUGH(SDL_bool,HasEvents,(Uint32 a, Uint32 b),(a,b),return)
 SDL3_SYM_PASSTHROUGH(void,FlushEvent,(Uint32 a),(a),)
 SDL3_SYM_PASSTHROUGH(void,FlushEvents,(Uint32 a, Uint32 b),(a,b),)
-SDL3_SYM_PASSTHROUGH(int,PollEvent,(SDL_Event *a),(a),return)
-SDL3_SYM_PASSTHROUGH(int,WaitEvent,(SDL_Event *a),(a),return)
-SDL3_SYM_PASSTHROUGH(int,WaitEventTimeout,(SDL_Event *a, int b),(a,b),return)
-SDL3_SYM_PASSTHROUGH(int,PushEvent,(SDL_Event *a),(a),return)
-SDL3_SYM_PASSTHROUGH(void,SetEventFilter,(SDL_EventFilter a, void *b),(a,b),)
-SDL3_SYM_PASSTHROUGH(SDL_bool,GetEventFilter,(SDL_EventFilter *a, void **b),(a,b),return)
-SDL3_SYM_PASSTHROUGH(void,AddEventWatch,(SDL_EventFilter a, void *b),(a,b),)
-SDL3_SYM_PASSTHROUGH(void,DelEventWatch,(SDL_EventFilter a, void *b),(a,b),)
-SDL3_SYM_PASSTHROUGH(void,FilterEvents,(SDL_EventFilter a, void *b),(a,b),)
+SDL3_SYM(int,PollEvent,(SDL_Event *a),(a),return)
+SDL3_SYM(int,WaitEvent,(SDL_Event *a),(a),return)
+SDL3_SYM(int,WaitEventTimeout,(SDL_Event *a, int b),(a,b),return)
+SDL3_SYM(int,PushEvent,(SDL_Event *a),(a),return)
+SDL3_SYM(void,SetEventFilter,(SDL_EventFilter a, void *b),(a,b),)
+SDL3_SYM(SDL_bool,GetEventFilter,(SDL_EventFilter *a, void **b),(a,b),return)
+SDL3_SYM(void,AddEventWatch,(SDL_EventFilter a, void *b),(a,b),)
+SDL3_SYM(void,DelEventWatch,(SDL_EventFilter a, void *b),(a,b),)
+SDL3_SYM(void,FilterEvents,(SDL_EventFilter a, void *b),(a,b),)
 SDL3_SYM_PASSTHROUGH(Uint8,EventState,(Uint32 a, int b),(a,b),return)
 SDL3_SYM_PASSTHROUGH(Uint32,RegisterEvents,(int a),(a),return)
 SDL3_SYM_PASSTHROUGH(char*,GetBasePath,(void),(),return)