SDL: add 8BitDo Controller sensor_timestamp (#13278)

From 6d3e8b749e0ab09f84eb54695319686bb219869e Mon Sep 17 00:00:00 2001
From: 8BitDo <[EMAIL REDACTED]>
Date: Thu, 26 Jun 2025 09:49:43 +0800
Subject: [PATCH] add 8BitDo Controller sensor_timestamp (#13278)

---
 src/joystick/hidapi/SDL_hidapi_8bitdo.c | 29 +++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/src/joystick/hidapi/SDL_hidapi_8bitdo.c b/src/joystick/hidapi/SDL_hidapi_8bitdo.c
index 10f19fc279df9..c556e0a65a7ab 100644
--- a/src/joystick/hidapi/SDL_hidapi_8bitdo.c
+++ b/src/joystick/hidapi/SDL_hidapi_8bitdo.c
@@ -22,6 +22,7 @@
 
 #ifdef SDL_JOYSTICK_HIDAPI
 
+#include "../../SDL_hints_c.h"
 #include "../SDL_sysjoystick.h"
 #include "SDL_hidapijoystick_c.h"
 #include "SDL_hidapi_rumble.h"
@@ -47,9 +48,17 @@ enum
 #define SDL_8BITDO_REPORTID_NOT_SUPPORTED_SDL_REPORTID          0x03
 #define SDL_8BITDO_BT_REPORTID_SDL_REPORTID                     0x01
 
+#define SDL_8BITDO_SENSOR_TIMESTAMP_ENABLE                      0xAA
 #define ABITDO_ACCEL_SCALE 4096.f
 #define ABITDO_GYRO_MAX_DEGREES_PER_SECOND 2000.f
 
+
+#define LOAD32(A, B, C, D) ((((Uint32)(A)) << 0) |  \
+                            (((Uint32)(B)) << 8) |  \
+                            (((Uint32)(C)) << 16) | \
+                            (((Uint32)(D)) << 24))
+
+
 typedef struct
 {
     bool sensors_supported;
@@ -61,6 +70,7 @@ typedef struct
     bool rgb_supported;
     bool player_led_supported;
     bool powerstate_supported;
+    bool sensor_timestamp_supported;
     Uint8 serial[6];
     Uint16 version;
     Uint16 version_beta;
@@ -69,6 +79,7 @@ typedef struct
     Uint8 last_state[USB_PACKET_LENGTH];
     Uint64 sensor_timestamp; // Nanoseconds.  Simulate onboard clock. Different models have different rates vs different connection styles.
     Uint64 sensor_timestamp_interval;
+    Uint32 last_tick;
 } SDL_Driver8BitDo_Context;
 
 #pragma pack(push,1)
@@ -181,6 +192,9 @@ static bool HIDAPI_Driver8BitDo_InitDevice(SDL_HIDAPI_Device *device)
                 ctx->rumble_supported = true;
                 ctx->powerstate_supported = true;
 
+                if (data[13] == SDL_8BITDO_SENSOR_TIMESTAMP_ENABLE) {
+                    ctx->sensor_timestamp_supported = true;
+                }
                 // Set the serial number to the Bluetooth MAC address
                 if (size >= 12 && data[10] != 0) {
                     char serial[18];
@@ -544,6 +558,21 @@ static void HIDAPI_Driver8BitDo_HandleStatePacket(SDL_Joystick *joystick, SDL_Dr
         float values[3];
         ABITDO_SENSORS *sensors = (ABITDO_SENSORS *)&data[15];
 
+         if (ctx->sensor_timestamp_supported) {
+            Uint32 delta;
+            Uint32 tick = LOAD32(data[27], data[28], data[29], data[30]);
+
+            if (ctx->last_tick) {
+                if (ctx->last_tick < tick) {
+                    delta = (tick - ctx->last_tick);
+                } else {
+                    delta = (SDL_MAX_UINT32 - ctx->last_tick + tick + 1);
+                }
+                ctx->sensor_timestamp_interval = SDL_US_TO_NS(delta);
+            } 
+            ctx->last_tick = tick;
+        }
+
         // Note: we cannot use the time stamp of the receiving computer due to packet delay creating "spiky" timings.
         // The imu time stamp is intended to be the sample time of the on-board hardware.
         // In the absence of time stamp data from the data[], we can simulate that by