sdl2-compat: joystick: SDL_VirtualJoystickDesc struct changed in SDL3.

From 2fe62be59e449a6f25455097c394dc4111a79115 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Sat, 13 Apr 2024 14:14:25 -0400
Subject: [PATCH] joystick: SDL_VirtualJoystickDesc struct changed in SDL3.

---
 src/dynapi/SDL_dynapi_procs.h |  2 +-
 src/sdl2_compat.c             | 37 +++++++++++++++++++++++++++++++----
 src/sdl2_compat.h             | 27 +++++++++++++++++++++++++
 src/sdl2_protos.h             |  2 +-
 4 files changed, 62 insertions(+), 6 deletions(-)

diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index d4f664f..b0c3089 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -942,7 +942,7 @@ SDL_DYNAPI_PROC(const char*,SDL_GameControllerPathForIndex,(int a),(a),return)
 SDL_DYNAPI_PROC(const char*,SDL_GameControllerPath,(SDL_GameController *a),(a),return)
 SDL_DYNAPI_PROC(const char*,SDL_JoystickPathForIndex,(int a),(a),return)
 SDL_DYNAPI_PROC(const char*,SDL_JoystickPath,(SDL_Joystick *a),(a),return)
-SDL_DYNAPI_PROC(int,SDL_JoystickAttachVirtualEx,(const SDL_VirtualJoystickDesc *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_JoystickAttachVirtualEx,(const SDL2_VirtualJoystickDesc *a),(a),return)
 SDL_DYNAPI_PROC(Uint16,SDL_GameControllerGetFirmwareVersion,(SDL_GameController *a),(a),return)
 SDL_DYNAPI_PROC(Uint16,SDL_JoystickGetFirmwareVersion,(SDL_Joystick *a),(a),return)
 SDL_DYNAPI_PROC(void,SDL_GUIDToString,(SDL_GUID a, char *b, int c),(a,b,c),)
diff --git a/src/sdl2_compat.c b/src/sdl2_compat.c
index 88f0440..c8b5e11 100644
--- a/src/sdl2_compat.c
+++ b/src/sdl2_compat.c
@@ -7315,15 +7315,44 @@ SDL_GameControllerHasRumbleTriggers(SDL_Gamepad *gamepad)
 DECLSPEC int SDLCALL
 SDL_JoystickAttachVirtual(SDL_JoystickType type, int naxes, int nbuttons, int nhats)
 {
-    SDL_JoystickID jid = SDL3_AttachVirtualJoystick(type, naxes, nbuttons, nhats);
+    const SDL_JoystickID jid = SDL3_AttachVirtualJoystick(type, naxes, nbuttons, nhats);
     SDL_NumJoysticks(); /* Refresh */
     return GetIndexFromJoystickInstance(jid);
 }
 
 DECLSPEC int SDLCALL
-SDL_JoystickAttachVirtualEx(const SDL_VirtualJoystickDesc *desc)
-{
-    SDL_JoystickID jid = SDL3_AttachVirtualJoystickEx(desc);
+SDL_JoystickAttachVirtualEx(const SDL2_VirtualJoystickDesc *desc2)
+{
+    SDL_JoystickID jid;
+    SDL_VirtualJoystickDesc desc3;
+
+    if (!desc2) {
+        return SDL3_InvalidParamError("desc");
+    } else if (desc2->version != SDL_VIRTUAL_JOYSTICK_DESC_VERSION) { /* SDL2 only had one version. */
+        return SDL3_SetError("Unsupported virtual joystick description version %u", desc2->version);
+    }
+
+    SDL3_zero(desc3);
+    #define SETDESCFIELD(x) desc3.x = desc2->x
+    SETDESCFIELD(type);
+    SETDESCFIELD(naxes);
+    SETDESCFIELD(nbuttons);
+    SETDESCFIELD(nhats);
+    SETDESCFIELD(vendor_id);
+    SETDESCFIELD(product_id);
+    SETDESCFIELD(button_mask);
+    SETDESCFIELD(axis_mask);
+    SETDESCFIELD(name);
+    SETDESCFIELD(userdata);
+    SETDESCFIELD(Update);
+    SETDESCFIELD(SetPlayerIndex);
+    SETDESCFIELD(Rumble);
+    SETDESCFIELD(RumbleTriggers);
+    SETDESCFIELD(SetLED);
+    SETDESCFIELD(SendEffect);
+    #undef SETDESCFIELD
+
+    jid = SDL3_AttachVirtualJoystickEx(&desc3);
     SDL_NumJoysticks(); /* Refresh */
     return GetIndexFromJoystickInstance(jid);
 }
diff --git a/src/sdl2_compat.h b/src/sdl2_compat.h
index 9fe6ef4..830342b 100644
--- a/src/sdl2_compat.h
+++ b/src/sdl2_compat.h
@@ -359,4 +359,31 @@ typedef enum
     SDL_YUV_CONVERSION_AUTOMATIC    /**< BT.601 for SD content, BT.709 for HD content */
 } SDL_YUV_CONVERSION_MODE;
 
+#define SDL_VIRTUAL_JOYSTICK_DESC_VERSION 1
+
+typedef struct SDL2_VirtualJoystickDesc
+{
+    Uint16 version;     /**< `SDL_VIRTUAL_JOYSTICK_DESC_VERSION` */
+    Uint16 type;        /**< `SDL_JoystickType` */
+    Uint16 naxes;       /**< the number of axes on this joystick */
+    Uint16 nbuttons;    /**< the number of buttons on this joystick */
+    Uint16 nhats;       /**< the number of hats on this joystick */
+    Uint16 vendor_id;   /**< the USB vendor ID of this joystick */
+    Uint16 product_id;  /**< the USB product ID of this joystick */
+    Uint16 padding;     /**< unused */
+    Uint32 button_mask; /**< A mask of which buttons are valid for this controller
+                             e.g. (1 << SDL_GAMEPAD_BUTTON_SOUTH) */
+    Uint32 axis_mask;   /**< A mask of which axes are valid for this controller
+                             e.g. (1 << SDL_GAMEPAD_AXIS_LEFTX) */
+    const char *name;   /**< the name of the joystick */
+
+    void *userdata;     /**< User data pointer passed to callbacks */
+    void (SDLCALL *Update)(void *userdata); /**< Called when the joystick state should be updated */
+    void (SDLCALL *SetPlayerIndex)(void *userdata, int player_index); /**< Called when the player index is set */
+    int (SDLCALL *Rumble)(void *userdata, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble); /**< Implements SDL_RumbleJoystick() */
+    int (SDLCALL *RumbleTriggers)(void *userdata, Uint16 left_rumble, Uint16 right_rumble); /**< Implements SDL_RumbleJoystickTriggers() */
+    int (SDLCALL *SetLED)(void *userdata, Uint8 red, Uint8 green, Uint8 blue); /**< Implements SDL_SetJoystickLED() */
+    int (SDLCALL *SendEffect)(void *userdata, const void *data, int size); /**< Implements SDL_SendJoystickEffect() */
+} SDL2_VirtualJoystickDesc;
+
 #endif /* sdl2_compat_h */
diff --git a/src/sdl2_protos.h b/src/sdl2_protos.h
index cdc6772..648365d 100644
--- a/src/sdl2_protos.h
+++ b/src/sdl2_protos.h
@@ -927,7 +927,7 @@ SDL2_PROTO(const char*,GameControllerPathForIndex,(int a))
 SDL2_PROTO(const char*,GameControllerPath,(SDL_GameController *a))
 SDL2_PROTO(const char*,JoystickPathForIndex,(int a))
 SDL2_PROTO(const char*,JoystickPath,(SDL_Joystick *a))
-SDL2_PROTO(int,JoystickAttachVirtualEx,(const SDL_VirtualJoystickDesc *a))
+SDL2_PROTO(int,JoystickAttachVirtualEx,(const SDL2_VirtualJoystickDesc *a))
 SDL2_PROTO(Uint16,GameControllerGetFirmwareVersion,(SDL_GameController *a))
 SDL2_PROTO(Uint16,JoystickGetFirmwareVersion,(SDL_Joystick *a))
 SDL2_PROTO(void,GUIDToString,(SDL_GUID a, char *b, int c))