SDL: Pass the OS event timestamp for keyboard, mouse, and touch events where possible

From 0a3262e819edc695a764702e8127bbd1b09944ef Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 2 Dec 2022 09:03:13 -0800
Subject: [PATCH] Pass the OS event timestamp for keyboard, mouse, and touch
 events where possible

Currently implemented for Windows and Apple platforms
---
 WhatsNew.txt                                |   2 +-
 docs/README-migration.md                    |   3 +
 src/audio/SDL_audio.c                       |  12 +--
 src/core/android/SDL_android.c              |   2 +-
 src/core/linux/SDL_evdev.c                  |  18 ++--
 src/core/openbsd/SDL_wscons_kbd.c           |  12 +--
 src/core/openbsd/SDL_wscons_mouse.c         |  20 ++--
 src/core/winrt/SDL_winrtapp_direct3d.cpp    |   6 +-
 src/events/SDL_clipboardevents.c            |   2 +-
 src/events/SDL_displayevents.c              |   1 +
 src/events/SDL_dropevents.c                 |   8 +-
 src/events/SDL_events.c                     |   8 +-
 src/events/SDL_gesture.c                    |   9 +-
 src/events/SDL_keyboard.c                   |  40 +++----
 src/events/SDL_keyboard_c.h                 |   8 +-
 src/events/SDL_mouse.c                      |  39 +++----
 src/events/SDL_mouse_c.h                    |   8 +-
 src/events/SDL_touch.c                      |  26 ++---
 src/events/SDL_touch_c.h                    |   6 +-
 src/events/SDL_windowevents.c               |   1 +
 src/joystick/SDL_gamecontroller.c           |   8 ++
 src/joystick/SDL_joystick.c                 |  14 ++-
 src/joystick/android/SDL_sysjoystick.c      |   4 +-
 src/main/haiku/SDL_BApp.h                   |  10 +-
 src/render/direct3d/SDL_render_d3d.c        |   1 +
 src/render/direct3d11/SDL_render_d3d11.c    |   1 +
 src/render/direct3d12/SDL_render_d3d12.c    |   1 +
 src/sensor/SDL_sensor.c                     |   1 +
 src/video/android/SDL_androidevents.c       |   1 +
 src/video/android/SDL_androidkeyboard.c     |   4 +-
 src/video/android/SDL_androidmouse.c        |  12 +--
 src/video/android/SDL_androidtouch.c        |   6 +-
 src/video/cocoa/SDL_cocoaevents.h           |   1 +
 src/video/cocoa/SDL_cocoaevents.m           |  18 ++++
 src/video/cocoa/SDL_cocoakeyboard.m         |   8 +-
 src/video/cocoa/SDL_cocoamouse.m            |   6 +-
 src/video/cocoa/SDL_cocoawindow.m           |  22 ++--
 src/video/emscripten/SDL_emscriptenevents.c |  16 +--
 src/video/haiku/SDL_BApp.h                  |  10 +-
 src/video/kmsdrm/SDL_kmsdrmmouse.c          |   2 +-
 src/video/n3ds/SDL_n3dsevents.c             |   1 +
 src/video/n3ds/SDL_n3dstouch.c              |   4 +-
 src/video/ngage/SDL_ngageevents.cpp         |   4 +-
 src/video/psp/SDL_pspevents.c               |   4 +-
 src/video/raspberry/SDL_rpimouse.c          |   2 +-
 src/video/riscos/SDL_riscosevents.c         |   8 +-
 src/video/uikit/SDL_uikitevents.h           |   3 +
 src/video/uikit/SDL_uikitevents.m           |  26 ++++-
 src/video/uikit/SDL_uikitview.m             |  29 ++---
 src/video/uikit/SDL_uikitviewcontroller.m   |  10 +-
 src/video/vita/SDL_vitakeyboard.c           |  56 +++++-----
 src/video/vita/SDL_vitamouse.c              |  14 +--
 src/video/vita/SDL_vitatouch.c              |   6 +-
 src/video/vita/SDL_vitavideo.c              |   8 +-
 src/video/wayland/SDL_waylandevents.c       |  28 ++---
 src/video/wayland/SDL_waylandtouch.c        |   4 +-
 src/video/windows/SDL_windowsevents.c       | 112 ++++++++++++++------
 src/video/windows/SDL_windowsmouse.c        |   2 +-
 src/video/winrt/SDL_winrtkeyboard.cpp       |   4 +-
 src/video/winrt/SDL_winrtpointerinput.cpp   |  18 ++--
 src/video/x11/SDL_x11events.c               |  24 ++---
 src/video/x11/SDL_x11xinput2.c              |  10 +-
 62 files changed, 441 insertions(+), 313 deletions(-)

diff --git a/WhatsNew.txt b/WhatsNew.txt
index de7b540a281a..1b25fc9f833d 100644
--- a/WhatsNew.txt
+++ b/WhatsNew.txt
@@ -35,4 +35,4 @@ General:
 * SDL_GetTicks() now returns a 64-bit value and the tick values should be directly compared instead of using the SDL_TICKS_PASSED macro
 * Added SDL_GetTicksNS() to return the number of nanoseconds since the SDL library initialized
 * Added SDL_DelayNS() to specify a delay in nanoseconds, to the highest precision the system will support
-* The timestamp member of the SDL_Event structure is now in nanoseconds, filled in with SDL_GetTicksNS()
+* The timestamp member of the SDL_Event structure is now in nanoseconds, filled in with the time the event was generated, or the time it was queued if that's not available
diff --git a/docs/README-migration.md b/docs/README-migration.md
index 079d22fe160a..cb0b4159db5d 100644
--- a/docs/README-migration.md
+++ b/docs/README-migration.md
@@ -35,6 +35,9 @@ The SDL3main and SDL3test libraries have been renamed SDL3_main and SDL3_test, r
 
 The `timestamp` member of the SDL_Event structure now represents nanoseconds, and is populated with `SDL_GetTicksNS()`
 
+You should set the `event.common.timestamp` field before passing an event to `SDL_PushEvent()`. If the timestamp is 0 it will be filled in with `SDL_GetTicksNS()`.
+
+
 ## SDL_platform.h
 
 The preprocessor symbol __MACOSX__ has been renamed __MACOS__, and __IPHONEOS__ has been renamed __IOS__
diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
index 151b799768a5..275fd15ee367 100644
--- a/src/audio/SDL_audio.c
+++ b/src/audio/SDL_audio.c
@@ -414,8 +414,8 @@ void SDL_AddAudioDevice(const SDL_bool iscapture, const char *name, SDL_AudioSpe
         /* Post the event, if desired */
         if (SDL_GetEventState(SDL_AUDIODEVICEADDED) == SDL_ENABLE) {
             SDL_Event event;
-            SDL_zero(event);
-            event.adevice.type = SDL_AUDIODEVICEADDED;
+            event.type = SDL_AUDIODEVICEADDED;
+            event.common.timestamp = 0;
             event.adevice.which = device_index;
             event.adevice.iscapture = iscapture;
             SDL_PushEvent(&event);
@@ -445,8 +445,8 @@ void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
     /* Post the event, if desired */
     if (SDL_GetEventState(SDL_AUDIODEVICEREMOVED) == SDL_ENABLE) {
         SDL_Event event;
-        SDL_zero(event);
-        event.adevice.type = SDL_AUDIODEVICEREMOVED;
+        event.type = SDL_AUDIODEVICEREMOVED;
+        event.common.timestamp = 0;
         event.adevice.which = device->id;
         event.adevice.iscapture = device->iscapture ? 1 : 0;
         SDL_PushEvent(&event);
@@ -497,8 +497,8 @@ void SDL_RemoveAudioDevice(const SDL_bool iscapture, void *handle)
     if (!device_was_opened) {
         if (SDL_GetEventState(SDL_AUDIODEVICEREMOVED) == SDL_ENABLE) {
             SDL_Event event;
-            SDL_zero(event);
-            event.adevice.type = SDL_AUDIODEVICEREMOVED;
+            event.type = SDL_AUDIODEVICEREMOVED;
+            event.common.timestamp = 0;
             event.adevice.which = 0;
             event.adevice.iscapture = iscapture ? 1 : 0;
             SDL_PushEvent(&event);
diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c
index adaf4b17ed4a..caa2ea18f9a7 100644
--- a/src/core/android/SDL_android.c
+++ b/src/core/android/SDL_android.c
@@ -1257,7 +1257,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancod
     JNIEnv *env, jclass cls,
     jchar chUnicode)
 {
-    SDL_SendKeyboardUnicodeKey(chUnicode);
+    SDL_SendKeyboardUnicodeKey(0, chUnicode);
 }
 
 JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)(
diff --git a/src/core/linux/SDL_evdev.c b/src/core/linux/SDL_evdev.c
index be9b94503c03..f337999979a1 100644
--- a/src/core/linux/SDL_evdev.c
+++ b/src/core/linux/SDL_evdev.c
@@ -302,9 +302,9 @@ void SDL_EVDEV_Poll(void)
                     if (events[i].code >= BTN_MOUSE && events[i].code < BTN_MOUSE + SDL_arraysize(EVDEV_MouseButtons)) {
                         mouse_button = events[i].code - BTN_MOUSE;
                         if (events[i].value == 0) {
-                            SDL_SendMouseButton(mouse->focus, (SDL_MouseID)item->fd, SDL_RELEASED, EVDEV_MouseButtons[mouse_button]);
+                            SDL_SendMouseButton(0, mouse->focus, (SDL_MouseID)item->fd, SDL_RELEASED, EVDEV_MouseButtons[mouse_button]);
                         } else if (events[i].value == 1) {
-                            SDL_SendMouseButton(mouse->focus, (SDL_MouseID)item->fd, SDL_PRESSED, EVDEV_MouseButtons[mouse_button]);
+                            SDL_SendMouseButton(0, mouse->focus, (SDL_MouseID)item->fd, SDL_PRESSED, EVDEV_MouseButtons[mouse_button]);
                         }
                         break;
                     }
@@ -328,9 +328,9 @@ void SDL_EVDEV_Poll(void)
                     scan_code = SDL_EVDEV_translate_keycode(events[i].code);
                     if (scan_code != SDL_SCANCODE_UNKNOWN) {
                         if (events[i].value == 0) {
-                            SDL_SendKeyboardKey(SDL_RELEASED, scan_code);
+                            SDL_SendKeyboardKey(0, SDL_RELEASED, scan_code);
                         } else if (events[i].value == 1 || events[i].value == 2 /* key repeated */) {
-                            SDL_SendKeyboardKey(SDL_PRESSED, scan_code);
+                            SDL_SendKeyboardKey(0, SDL_PRESSED, scan_code);
                         }
                     }
                     SDL_EVDEV_kbd_keycode(_this->kbd, events[i].code, events[i].value);
@@ -446,11 +446,11 @@ void SDL_EVDEV_Poll(void)
                     case SYN_REPORT:
                         /* Send mouse axis changes together to ensure consistency and reduce event processing overhead */
                         if (item->mouse_x != 0 || item->mouse_y != 0) {
-                            SDL_SendMouseMotion(mouse->focus, (SDL_MouseID)item->fd, item->relative_mouse, item->mouse_x, item->mouse_y);
+                            SDL_SendMouseMotion(0, mouse->focus, (SDL_MouseID)item->fd, item->relative_mouse, item->mouse_x, item->mouse_y);
                             item->mouse_x = item->mouse_y = 0;
                         }
                         if (item->mouse_wheel != 0 || item->mouse_hwheel != 0) {
-                            SDL_SendMouseWheel(mouse->focus, (SDL_MouseID)item->fd,
+                            SDL_SendMouseWheel(0, mouse->focus, (SDL_MouseID)item->fd,
                                                item->mouse_hwheel / (item->high_res_hwheel ? 120.0f : 1.0f),
                                                item->mouse_wheel / (item->high_res_wheel ? 120.0f : 1.0f),
                                                SDL_MOUSEWHEEL_NORMAL);
@@ -480,16 +480,16 @@ void SDL_EVDEV_Poll(void)
                              * be window-relative in that case. */
                             switch (item->touchscreen_data->slots[j].delta) {
                             case EVDEV_TOUCH_SLOTDELTA_DOWN:
-                                SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, SDL_TRUE, norm_x, norm_y, norm_pressure);
+                                SDL_SendTouch(0, item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, SDL_TRUE, norm_x, norm_y, norm_pressure);
                                 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
                                 break;
                             case EVDEV_TOUCH_SLOTDELTA_UP:
-                                SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, SDL_FALSE, norm_x, norm_y, norm_pressure);
+                                SDL_SendTouch(0, item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, SDL_FALSE, norm_x, norm_y, norm_pressure);
                                 item->touchscreen_data->slots[j].tracking_id = -1;
                                 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
                                 break;
                             case EVDEV_TOUCH_SLOTDELTA_MOVE:
-                                SDL_SendTouchMotion(item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, norm_x, norm_y, norm_pressure);
+                                SDL_SendTouchMotion(0, item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, norm_x, norm_y, norm_pressure);
                                 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
                                 break;
                             default:
diff --git a/src/core/openbsd/SDL_wscons_kbd.c b/src/core/openbsd/SDL_wscons_kbd.c
index 47dfbeba8bd9..75120e4dbb55 100644
--- a/src/core/openbsd/SDL_wscons_kbd.c
+++ b/src/core/openbsd/SDL_wscons_kbd.c
@@ -553,22 +553,22 @@ static void Translate_to_keycode(SDL_WSCONS_input_data *input, int type, keysym_
     switch (keyDesc.command) {
     case KS_Cmd_ScrollBack:
     {
-        SDL_SendKeyboardKey(type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED, SDL_SCANCODE_PAGEUP);
+        SDL_SendKeyboardKey(0, type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED, SDL_SCANCODE_PAGEUP);
         return;
     }
     case KS_Cmd_ScrollFwd:
     {
-        SDL_SendKeyboardKey(type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED, SDL_SCANCODE_PAGEDOWN);
+        SDL_SendKeyboardKey(0, type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED, SDL_SCANCODE_PAGEDOWN);
         return;
     }
     }
     for (i = 0; i < sizeof(conversion_table) / sizeof(struct wscons_keycode_to_SDL); i++) {
         if (conversion_table[i].sourcekey == group[0]) {
-            SDL_SendKeyboardKey(type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED, conversion_table[i].targetKey);
+            SDL_SendKeyboardKey(0, type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED, conversion_table[i].targetKey);
             return;
         }
     }
-    SDL_SendKeyboardKey(type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED, SDL_SCANCODE_UNKNOWN);
+    SDL_SendKeyboardKey(0, type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED, SDL_SCANCODE_UNKNOWN);
 }
 
 static void updateKeyboard(SDL_WSCONS_input_data *input)
@@ -802,13 +802,13 @@ static void updateKeyboard(SDL_WSCONS_input_data *input)
             } break;
             case WSCONS_EVENT_ALL_KEYS_UP:
                 for (i = 0; i < SDL_NUM_SCANCODES; i++) {
-                    SDL_SendKeyboardKey(SDL_RELEASED, i);
+                    SDL_SendKeyboardKey(0, SDL_RELEASED, i);
                 }
                 break;
             }
 
             if (input->type == WSKBD_TYPE_USB && events[i].value <= 0xE7)
-                SDL_SendKeyboardKey(type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED, (SDL_Scancode)events[i].value);
+                SDL_SendKeyboardKey(0, type == WSCONS_EVENT_KEY_DOWN ? SDL_PRESSED : SDL_RELEASED, (SDL_Scancode)events[i].value);
             else
                 Translate_to_keycode(input, type, events[i].value);
 
diff --git a/src/core/openbsd/SDL_wscons_mouse.c b/src/core/openbsd/SDL_wscons_mouse.c
index d3e021dde575..58d2ab58b7d0 100644
--- a/src/core/openbsd/SDL_wscons_mouse.c
+++ b/src/core/openbsd/SDL_wscons_mouse.c
@@ -74,13 +74,13 @@ void updateMouse(SDL_WSCONS_mouse_input_data *inputData)
             {
                 switch (events[i].value) {
                 case 0: /* Left Mouse Button. */
-                    SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_PRESSED, SDL_BUTTON_LEFT);
+                    SDL_SendMouseButton(0, mouse->focus, mouse->mouseID, SDL_PRESSED, SDL_BUTTON_LEFT);
                     break;
                 case 1: /* Middle Mouse Button. */
-                    SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_PRESSED, SDL_BUTTON_MIDDLE);
+                    SDL_SendMouseButton(0, mouse->focus, mouse->mouseID, SDL_PRESSED, SDL_BUTTON_MIDDLE);
                     break;
                 case 2: /* Right Mouse Button. */
-                    SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_PRESSED, SDL_BUTTON_RIGHT);
+                    SDL_SendMouseButton(0, mouse->focus, mouse->mouseID, SDL_PRESSED, SDL_BUTTON_RIGHT);
                     break;
                 }
             } break;
@@ -88,34 +88,34 @@ void updateMouse(SDL_WSCONS_mouse_input_data *inputData)
             {
                 switch (events[i].value) {
                 case 0: /* Left Mouse Button. */
-                    SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_RELEASED, SDL_BUTTON_LEFT);
+                    SDL_SendMouseButton(0, mouse->focus, mouse->mouseID, SDL_RELEASED, SDL_BUTTON_LEFT);
                     break;
                 case 1: /* Middle Mouse Button. */
-                    SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_RELEASED, SDL_BUTTON_MIDDLE);
+                    SDL_SendMouseButton(0, mouse->focus, mouse->mouseID, SDL_RELEASED, SDL_BUTTON_MIDDLE);
                     break;
                 case 2: /* Right Mouse Button. */
-                    SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_RELEASED, SDL_BUTTON_RIGHT);
+                    SDL_SendMouseButton(0, mouse->focus, mouse->mouseID, SDL_RELEASED, SDL_BUTTON_RIGHT);
                     break;
                 }
             } break;
             case WSCONS_EVENT_MOUSE_DELTA_X:
             {
-                SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, events[i].value, 0);
+                SDL_SendMouseMotion(0, mouse->focus, mouse->mouseID, 1, events[i].value, 0);
                 break;
             }
             case WSCONS_EVENT_MOUSE_DELTA_Y:
             {
-                SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, 0, -events[i].value);
+                SDL_SendMouseMotion(0, mouse->focus, mouse->mouseID, 1, 0, -events[i].value);
                 break;
             }
             case WSCONS_EVENT_MOUSE_DELTA_W:
             {
-                SDL_SendMouseWheel(mouse->focus, mouse->mouseID, events[i].value, 0, SDL_MOUSEWHEEL_NORMAL);
+                SDL_SendMouseWheel(0, mouse->focus, mouse->mouseID, events[i].value, 0, SDL_MOUSEWHEEL_NORMAL);
                 break;
             }
             case WSCONS_EVENT_MOUSE_DELTA_Z:
             {
-                SDL_SendMouseWheel(mouse->focus, mouse->mouseID, 0, -events[i].value, SDL_MOUSEWHEEL_NORMAL);
+                SDL_SendMouseWheel(0, mouse->focus, mouse->mouseID, 0, -events[i].value, SDL_MOUSEWHEEL_NORMAL);
                 break;
             }
             }
diff --git a/src/core/winrt/SDL_winrtapp_direct3d.cpp b/src/core/winrt/SDL_winrtapp_direct3d.cpp
index 191f51514427..fca112afe725 100644
--- a/src/core/winrt/SDL_winrtapp_direct3d.cpp
+++ b/src/core/winrt/SDL_winrtapp_direct3d.cpp
@@ -552,7 +552,7 @@ void SDL_WinRTApp::OnWindowActivated(CoreWindow ^ sender, WindowActivatedEventAr
              */
 #if (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP) || (NTDDI_VERSION >= NTDDI_WINBLUE)
             Point cursorPos = WINRT_TransformCursorPosition(window, sender->PointerPosition, TransformToSDLWindowSize);
-            SDL_SendMouseMotion(window, 0, 0, (int)cursorPos.X, (int)cursorPos.Y);
+            SDL_SendMouseMotion(0, window, 0, 0, (int)cursorPos.X, (int)cursorPos.Y);
 #endif
 
             /* TODO, WinRT: see if the Win32 bugfix from https://hg.libsdl.org/SDL/rev/d278747da408 needs to be applied (on window activation) */
@@ -743,8 +743,8 @@ void SDL_WinRTApp::OnCharacterReceived(Windows::UI::Core::CoreWindow ^ sender, W
 template <typename BackButtonEventArgs>
 static void WINRT_OnBackButtonPressed(BackButtonEventArgs ^ args)
 {
-    SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_AC_BACK);
-    SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_AC_BACK);
+    SDL_SendKeyboardKey(0, SDL_PRESSED, SDL_SCANCODE_AC_BACK);
+    SDL_SendKeyboardKey(0, SDL_RELEASED, SDL_SCANCODE_AC_BACK);
 
     if (SDL_GetHintBoolean(SDL_HINT_WINRT_HANDLE_BACK_BUTTON, SDL_FALSE)) {
         args->Handled = true;
diff --git a/src/events/SDL_clipboardevents.c b/src/events/SDL_clipboardevents.c
index e0331324be2f..95443738dd18 100644
--- a/src/events/SDL_clipboardevents.c
+++ b/src/events/SDL_clipboardevents.c
@@ -34,7 +34,7 @@ int SDL_SendClipboardUpdate(void)
     if (SDL_GetEventState(SDL_CLIPBOARDUPDATE) == SDL_ENABLE) {
         SDL_Event event;
         event.type = SDL_CLIPBOARDUPDATE;
-
+        event.common.timestamp = 0;
         posted = (SDL_PushEvent(&event) > 0);
     }
     return posted;
diff --git a/src/events/SDL_displayevents.c b/src/events/SDL_displayevents.c
index 6135ffe59210..8073656d9d0d 100644
--- a/src/events/SDL_displayevents.c
+++ b/src/events/SDL_displayevents.c
@@ -45,6 +45,7 @@ int SDL_SendDisplayEvent(SDL_VideoDisplay *display, Uint8 displayevent, int data
     if (SDL_GetEventState(SDL_DISPLAYEVENT) == SDL_ENABLE) {
         SDL_Event event;
         event.type = SDL_DISPLAYEVENT;
+        event.common.timestamp = 0;
         event.display.event = displayevent;
         event.display.display = SDL_GetIndexOfDisplay(display);
         event.display.data1 = data1;
diff --git a/src/events/SDL_dropevents.c b/src/events/SDL_dropevents.c
index 3a9c19e1bd0f..30fe4110ac8b 100644
--- a/src/events/SDL_dropevents.c
+++ b/src/events/SDL_dropevents.c
@@ -40,11 +40,8 @@ static int SDL_SendDrop(SDL_Window *window, const SDL_EventType evtype, const ch
         if (need_begin) {
             SDL_zero(event);
             event.type = SDL_DROPBEGIN;
-
-            if (window) {
-                event.drop.windowID = window->id;
-            }
-
+            event.common.timestamp = 0;
+            event.drop.windowID = window ? window->id : 0;
             posted = (SDL_PushEvent(&event) > 0);
             if (!posted) {
                 return 0;
@@ -58,6 +55,7 @@ static int SDL_SendDrop(SDL_Window *window, const SDL_EventType evtype, const ch
 
         SDL_zero(event);
         event.type = evtype;
+        event.common.timestamp = 0;
         event.drop.file = data ? SDL_strdup(data) : NULL;
         event.drop.windowID = window ? window->id : 0;
         posted = (SDL_PushEvent(&event) > 0);
diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c
index 4a418a140931..f0b7022fadbc 100644
--- a/src/events/SDL_events.c
+++ b/src/events/SDL_events.c
@@ -948,8 +948,8 @@ static void SDL_PumpEventsInternal(SDL_bool push_sentinel)
     if (push_sentinel && SDL_GetEventState(SDL_POLLSENTINEL) == SDL_ENABLE) {
         SDL_Event sentinel;
 
-        SDL_zero(sentinel);
         sentinel.type = SDL_POLLSENTINEL;
+        sentinel.common.timestamp = 0;
         SDL_PushEvent(&sentinel);
     }
 }
@@ -1192,7 +1192,9 @@ int SDL_WaitEventTimeoutNS(SDL_Event *event, Sint64 timeoutNS)
 
 int SDL_PushEvent(SDL_Event *event)
 {
-    event->common.timestamp = SDL_GetTicksNS();
+    if (!event->common.timestamp) {
+        event->common.timestamp = SDL_GetTicksNS();
+    }
 
     if (SDL_EventOK.callback || SDL_event_watchers_count > 0) {
         if (SDL_event_watchers_lock == NULL || SDL_LockMutex(SDL_event_watchers_lock) == 0) {
@@ -1414,6 +1416,7 @@ int SDL_SendAppEvent(SDL_EventType eventType)
     if (SDL_GetEventState(eventType) == SDL_ENABLE) {
         SDL_Event event;
         event.type = eventType;
+        event.common.timestamp = 0;
         posted = (SDL_PushEvent(&event) > 0);
     }
     return posted;
@@ -1428,6 +1431,7 @@ int SDL_SendSysWMEvent(SDL_SysWMmsg *message)
         SDL_Event event;
         SDL_memset(&event, 0, sizeof(event));
         event.type = SDL_SYSWMEVENT;
+        event.common.timestamp = 0;
         event.syswm.msg = message;
         posted = (SDL_PushEvent(&event) > 0);
     }
diff --git a/src/events/SDL_gesture.c b/src/events/SDL_gesture.c
index 57ef31d70edf..8421529ea225 100644
--- a/src/events/SDL_gesture.c
+++ b/src/events/SDL_gesture.c
@@ -525,7 +525,8 @@ static void SDL_SendGestureMulti(SDL_GestureTouch *touch, float dTheta, float dD
 {
     if (SDL_GetEventState(SDL_MULTIGESTURE) == SDL_ENABLE) {
         SDL_Event event;
-        event.mgesture.type = SDL_MULTIGESTURE;
+        event.type = SDL_MULTIGESTURE;
+        event.common.timestamp = 0;
         event.mgesture.touchId = touch->id;
         event.mgesture.x = touch->centroid.x;
         event.mgesture.y = touch->centroid.y;
@@ -542,7 +543,8 @@ static void SDL_SendGestureDollar(SDL_GestureTouch *touch,
 {
     if (SDL_GetEventState(SDL_DOLLARGESTURE) == SDL_ENABLE) {
         SDL_Event event;
-        event.dgesture.type = SDL_DOLLARGESTURE;
+        event.type = SDL_DOLLARGESTURE;
+        event.common.timestamp = 0;
         event.dgesture.touchId = touch->id;
         event.dgesture.x = touch->centroid.x;
         event.dgesture.y = touch->centroid.y;
@@ -558,7 +560,8 @@ static void SDL_SendDollarRecord(SDL_GestureTouch *touch, SDL_GestureID gestureI
 {
     if (SDL_GetEventState(SDL_DOLLARRECORD) == SDL_ENABLE) {
         SDL_Event event;
-        event.dgesture.type = SDL_DOLLARRECORD;
+        event.type = SDL_DOLLARRECORD;
+        event.common.timestamp = 0;
         event.dgesture.touchId = touch->id;
         event.dgesture.gestureId = gestureId;
         SDL_PushEvent(&event);
diff --git a/src/events/SDL_keyboard.c b/src/events/SDL_keyboard.c
index f0135487052e..dcbab5f082e8 100644
--- a/src/events/SDL_keyboard.c
+++ b/src/events/SDL_keyboard.c
@@ -681,7 +681,7 @@ void SDL_ResetKeyboard(void)
 #endif
     for (scancode = (SDL_Scancode)0; scancode < SDL_NUM_SCANCODES; ++scancode) {
         if (keyboard->keystate[scancode] == SDL_PRESSED) {
-            SDL_SendKeyboardKey(SDL_RELEASED, scancode);
+            SDL_SendKeyboardKey(0, SDL_RELEASED, scancode);
         }
     }
 }
@@ -789,7 +789,7 @@ void SDL_SetKeyboardFocus(SDL_Window *window)
     }
 }
 
-static int SDL_SendKeyboardKeyInternal(Uint8 source, Uint8 state, SDL_Scancode scancode, SDL_Keycode keycode)
+static int SDL_SendKeyboardKeyInternal(Uint64 timestamp, Uint8 source, Uint8 state, SDL_Scancode scancode, SDL_Keycode keycode)
 {
     SDL_Keyboard *keyboard = &SDL_keyboard;
     int posted;
@@ -903,7 +903,8 @@ static int SDL_SendKeyboardKeyInternal(Uint8 source, Uint8 state, SDL_Scancode s
     posted = 0;
     if (SDL_GetEventState(type) == SDL_ENABLE) {
         SDL_Event event;
-        event.key.type = type;
+        event.type = type;
+        event.common.timestamp = timestamp;
         event.key.state = state;
         event.key.repeat = repeat;
         event.key.keysym.scancode = scancode;
@@ -931,7 +932,7 @@ static int SDL_SendKeyboardKeyInternal(Uint8 source, Uint8 state, SDL_Scancode s
     return posted;
 }
 
-int SDL_SendKeyboardUnicodeKey(Uint32 ch)
+int SDL_SendKeyboardUnicodeKey(Uint64 timestamp, Uint32 ch)
 {
     SDL_Scancode code = SDL_SCANCODE_UNKNOWN;
     uint16_t mod = 0;
@@ -943,33 +944,33 @@ int SDL_SendKeyboardUnicodeKey(Uint32 ch)
 
     if (mod & KMOD_SHIFT) {
         /* If the character uses shift, press shift down */
-        SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LSHIFT);
+        SDL_SendKeyboardKey(timestamp, SDL_PRESSED, SDL_SCANCODE_LSHIFT);
     }
 
     /* Send a keydown and keyup for the character */
-    SDL_SendKeyboardKey(SDL_PRESSED, code);
-    SDL_SendKeyboardKey(SDL_RELEASED, code);
+    SDL_SendKeyboardKey(timestamp, SDL_PRESSED, code);
+    SDL_SendKeyboardKey(timestamp, SDL_RELEASED, code);
 
     if (mod & KMOD_SHIFT) {
         /* If the character uses shift, release shift */
-        SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT);
+        SDL_SendKeyboardKey(timestamp, SDL_RELEASED, SDL_SCANCODE_LSHIFT);
     }
     return 0;
 }
 
-int SDL_SendKeyboardKey(Uint8 state, SDL_Scancode scancode)
+int SDL_SendKeyboardKey(Uint64 timestamp, Uint8 state, SDL_Scancode scancode)
 {
-    return SDL_SendKeyboardKeyInternal(KEYBOARD_HARDWARE, state, scancode, SDLK_UNKNOWN);
+    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE, state, scancode, SDLK_UNKNOWN);
 }
 
-int SDL_SendKeyboardKeyAndKeycode(Uint8 state, SDL_Scancode scancode, SDL_Keycode keycode)
+int SDL_SendKeyboardKeyAndKeycode(Uint64 timestamp, Uint8 state, SDL_Scancode scancode, SDL_Keycode keycode)
 {
-    return SDL_SendKeyboardKeyInternal(KEYBOARD_HARDWARE, state, scancode, keycode);
+    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE, state, scancode, keycode);
 }
 
-int SDL_SendKeyboardKeyAutoRelease(SDL_Scancode scancode)
+int SDL_SendKeyboardKeyAutoRelease(Uint64 timestamp, SDL_Scancode scancode)
 {
-    return SDL_SendKeyboardKeyInternal(KEYBOARD_AUTORELEASE, SDL_PRESSED, scancode, SDLK_UNKNOWN);
+    return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_AUTORELEASE, SDL_PRESSED, scancode, SDLK_UNKNOWN);
 }
 
 void SDL_ReleaseAutoReleaseKeys(void)
@@ -980,7 +981,7 @@ void SDL_ReleaseAutoReleaseKeys(void)
     if (keyboard->autorelease_pending) {
         for (scancode = SDL_SCANCODE_UNKNOWN; scancode < SDL_NUM_SCANCODES; ++scancode) {
             if (keyboard->keysource[scancode] == KEYBOARD_AUTORELEASE) {
-                SDL_SendKeyboardKeyInternal(KEYBOARD_AUTORELEASE, SDL_RELEASED, scancode, SDLK_UNKNOWN);
+                SDL_SendKeyboardKeyInternal(0, KEYBOARD_AUTORELEASE, SDL_RELEASED, scancode, SDLK_UNKNOWN);
             }
         }
         keyboard->autorelease_pending = SDL_FALSE;
@@ -1017,7 +1018,8 @@ int SDL_SendKeyboardText(const char *text)
         SDL_Event event;
         size_t pos = 0, advance, length = SDL_strlen(text);
 
-        event.text.type = SDL_TEXTINPUT;
+        event.type = SDL_TEXTINPUT;
+        event.common.timestamp = 0;
         event.text.windowID = keyboard->focus ? keyboard->focus->id : 0;
         while (pos < length) {
             advance = SDL_utf8strlcpy(event.text.text, text + pos, SDL_arraysize(event.text.text));
@@ -1043,13 +1045,15 @@ int SDL_SendEditingText(const char *text, int start, int length)
 
         if (SDL_GetHintBoolean(SDL_HINT_IME_SUPPORT_EXTENDED_TEXT, SDL_FALSE) &&
             SDL_strlen(text) >= SDL_arraysize(event.text.text)) {
-            event.editExt.type = SDL_TEXTEDITING_EXT;
+            event.type = SDL_TEXTEDITING_EXT;
+            event.common.timestamp = 0;
             event.editExt.windowID = keyboard->focus ? keyboard->focus->id : 0;
             event.editExt.text = text ? SDL_strdup(text) : NULL;
             event.editExt.start = start;
             event.editExt.length = length;
         } else {
-            event.edit.type = SDL_TEXTEDITING;
+            event.type = SDL_TEXTEDITING;
+            event.common.timestamp = 0;
             event.edit.windowID = keyboard->focus ? keyboard->focus->id : 0;
             event.edit.start = start;
             event.edit.length = length;
diff --git a/src/events/SDL_keyboard_c.h b/src/events/SDL_keyboard_c.h
index c008f7817804..f587812c3295 100644
--- a/src/events/SDL_keyboard_c.h
+++ b/src/events/SDL_keyboard_c.h
@@ -47,15 +47,15 @@ extern void SDL_SetKeyboardFocus(SDL_Window *window);
 /* Send a character from an on-screen keyboard as scancode and modifier key events,
    currently assuming ASCII characters on a US keyboard layout
  */
-extern int SDL_SendKeyboardUnicodeKey(Uint32 ch);
+extern int SDL_SendKeyboardUnicodeKey(Uint64 timestamp, Uint32 ch);
 
 /* Send a keyboard key event */
-extern int SDL_SendKeyboardKey(Uint8 state, SDL_Scancode scancode);
-extern int SDL_SendKeyboardKeyAutoRelease(SDL_Scancode scancode);
+extern int SDL_SendKeyboardKey(Uint64 timestamp, Uint8 state, SDL_Scancode scancode);
+extern int SDL_SendKeyboardKeyAutoRelease(Uint64 timestamp, SDL_Scancode scancode);
 
 /* This is for platforms that don't know the keymap but can report scancode and keycode directly.
    Most platforms should prefer to optionally call SDL_SetKeymap and then use SDL_SendKeyboardKey. */
-extern int SDL_SendKeyboardKeyAndKeycode(Uint8 state, SDL_Scancode sc

(Patch may be truncated, please check the link at the top of this post.)