sdl12-compat: syswm: Added a magic hack so apps can access actual SDL2 SysWM information.

From b13166f31a1d1b315744e6cc45901173d99590c7 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Mon, 8 Aug 2022 11:02:38 -0400
Subject: [PATCH] syswm: Added a magic hack so apps can access actual SDL2
 SysWM information.

Fixes #186.
---
 CMakeLists.txt          |   1 +
 include/SDL/SDL_syswm.h |  16 +++++
 src/SDL12_compat.c      | 127 +++++++++++++++++++++++++++++++++++++++-
 test/README             |   1 +
 test/testsyswm2on12.c   | 113 +++++++++++++++++++++++++++++++++++
 5 files changed, 257 insertions(+), 1 deletion(-)
 create mode 100644 test/testsyswm2on12.c

diff --git a/CMakeLists.txt b/CMakeLists.txt
index e7a847cd7..70cdb5a33 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -217,6 +217,7 @@ if(SDL12TESTS)
     test_program(torturethread "test/torturethread.c")
     test_program(testdyngl "test/testdyngl.c")
     test_program(testgl "test/testgl.c")
+    test_program(testsyswm2on12 "test/testsyswm2on12.c")
     if(OPENGL_FOUND)
       if(CMAKE_VERSION VERSION_LESS 3.10 OR NOT OPENGL_opengl_LIBRARY)
         target_link_libraries(testgl ${OPENGL_gl_LIBRARY})
diff --git a/include/SDL/SDL_syswm.h b/include/SDL/SDL_syswm.h
index 007618d10..af1931126 100644
--- a/include/SDL/SDL_syswm.h
+++ b/include/SDL/SDL_syswm.h
@@ -120,6 +120,22 @@ real SDL-1.2 available to you. */
 
 #include "begin_code.h"
 
+/* SPECIAL CASE FOR SDL12-COMPAT: if version.major == 2, we'll fill in this struct with the native window handle. */
+/* This is not part of the real 1.2 API! */
+typedef struct SDL_SysWMinfo2on12
+{
+    SDL_version version;
+    SDL_SYSWM_TYPE subsystem;
+    void *data1;
+    void *data2;
+    void *data3;
+    void *data4;
+    void *data5;
+    void *data6;
+    void *data7;
+    void *data8;
+} SDL_SysWMinfo2on12;
+
 extern DECLSPEC int SDLCALL SDL_GetWMInfo(SDL_SysWMinfo *info);
 
 #include "close_code.h"
diff --git a/src/SDL12_compat.c b/src/SDL12_compat.c
index 1339644fd..022900341 100644
--- a/src/SDL12_compat.c
+++ b/src/SDL12_compat.c
@@ -641,6 +641,22 @@ typedef struct SDL12_SysWMinfo
 #endif
 } SDL12_SysWMinfo;
 
+/* SPECIAL CASE FOR SDL12-COMPAT: if version.major == 2, we'll fill in this struct with the native window handle. */
+/* This is not part of the real 1.2 API! */
+typedef struct SDL12_SysWMinfo2on12
+{
+    SDL_version version;
+    SDL12_SYSWM_TYPE subsystem;
+    void *data1;
+    void *data2;
+    void *data3;
+    void *data4;
+    void *data5;
+    void *data6;
+    void *data7;
+    void *data8;
+} SDL12_SysWMinfo2on12;
+
 
 typedef enum
 {
@@ -6451,9 +6467,10 @@ SDL_GetWMInfo(SDL12_SysWMinfo *info12)
     SDL_SysWMinfo info20;
     SDL_bool temp_window = SDL_FALSE;
     SDL_Window *win20 = VideoWindow20;
+    const SDL_bool do_2on12 = ((info12->version.major == 2) && (info12->version.minor == 0) && (info12->version.major == 2) && (info12->version.patch == 0));
     int rc;
 
-    if (info12->version.major > 1) {
+    if ((info12->version.major > 1) && !do_2on12) {
         SDL20_SetError("Requested version is unsupported");
         return 0;  /* some programs only test against 0, not -1 */
     } else if (!SupportSysWM) {
@@ -6482,6 +6499,114 @@ SDL_GetWMInfo(SDL12_SysWMinfo *info12)
         return 0;  /* some programs only test against 0, not -1 */
     }
 
+    /* SPECIAL CASE FOR SDL12-COMPAT: if version.major == 2, we'll fill in this struct with the native window handle. */
+    if (do_2on12) {
+        SDL12_SysWMinfo2on12 *wminfo2 = (SDL12_SysWMinfo2on12 *) info12;
+        SDL_zerop(wminfo2);
+        SDL20_memcpy(&wminfo2->version, &info20.version, sizeof (wminfo2->version));
+        wminfo2->subsystem = info20.subsystem;  /* these do not map to SDL 1.2 values! You're on your own! */
+
+        switch (info20.subsystem) {
+            #if defined(SDL_VIDEO_DRIVER_WINDOWS)
+            case SDL_SYSWM_WINDOWS:
+                wminfo2->data1 = (void *) info20.info.win.window;
+                wminfo2->data2 = (void *) info20.info.win.hdc;
+                wminfo2->data3 = (void *) info20.info.win.hinstance;
+                break;
+            #endif
+
+            #if defined(SDL_VIDEO_DRIVER_WINRT)
+            case SDL_SYSWM_WINRT:
+                wminfo2->data1 = (void *) info20.info.winrt.window;
+                break;
+            #endif
+
+            #if defined(SDL_VIDEO_DRIVER_X11)
+            case SDL_SYSWM_X11:
+                wminfo2->data1 = (void *) info20.info.x11.display;
+                wminfo2->data2 = (void *) info20.info.x11.window;
+                break;
+            #endif
+
+            #if defined(SDL_VIDEO_DRIVER_DIRECTFB)
+            case SDL_SYSWM_DIRECTFB:
+                wminfo2->data1 = (void *) info20.info.dfb.dfb;
+                wminfo2->data2 = (void *) info20.info.dfb.window;
+                wminfo2->data3 = (void *) info20.info.dfb.surface;
+                break;
+            #endif
+
+            #if defined(SDL_VIDEO_DRIVER_COCOA)
+            case SDL_SYSWM_COCOA:
+                wminfo2->data1 = (void *) info20.info.cocoa.window;
+                break;
+            #endif
+
+            #if defined(SDL_VIDEO_DRIVER_UIKIT)
+            case SDL_SYSWM_UIKIT:
+                wminfo2->data1 = (void *) info20.info.uikit.window;
+                wminfo2->data2 = (void *) info20.info.uikit.framebuffer;
+                wminfo2->data3 = (void *) info20.info.uikit.colorbuffer;
+                wminfo2->data4 = (void *) info20.info.uikit.resolveFramebuffer;
+                break;
+            #endif
+
+            #if defined(SDL_VIDEO_DRIVER_WAYLAND)
+            case SDL_SYSWM_WAYLAND:
+                wminfo2->data1 = (void *) info20.info.wl.display;
+                wminfo2->data2 = (void *) info20.info.wl.surface;
+                wminfo2->data3 = (void *) info20.info.wl.shell_surface;
+                wminfo2->data4 = (void *) info20.info.wl.egl_window;
+                wminfo2->data5 = (void *) info20.info.wl.xdg_surface;
+                wminfo2->data6 = (void *) info20.info.wl.xdg_toplevel;
+                wminfo2->data7 = (void *) info20.info.wl.xdg_popup;
+                wminfo2->data8 = (void *) info20.info.wl.xdg_positioner;
+                break;
+            #endif
+
+            #if defined(SDL_VIDEO_DRIVER_MIR)  /* no longer available, left for API/ABI compatibility. Remove in 2.1! */
+            case SDL_SYSWM_MIR:
+                wminfo2->data1 = (void *) info20.info.mir.connection;
+                wminfo2->data2 = (void *) info20.info.mir.surface;
+                break;
+            #endif
+
+            #if defined(SDL_VIDEO_DRIVER_ANDROID)
+            case SDL_SYSWM_ANDROID:
+                wminfo2->data1 = (void *) info20.info.android.window;
+                wminfo2->data2 = (void *) info20.info.android.surface;
+                break;
+            #endif
+
+            #if defined(SDL_VIDEO_DRIVER_OS2)
+            case SDL_SYSWM_OS2:
+                wminfo2->data1 = (void *) info20.info.os2.hwnd;
+                wminfo2->data2 = (void *) info20.info.os2.hwndFrame;
+                break;
+            #endif
+
+            #if defined(SDL_VIDEO_DRIVER_VIVANTE)
+            case SDL_SYSWM_VIVANTE:
+                wminfo2->data1 = (void *) info20.info.vivante.display;
+                wminfo2->data2 = (void *) info20.info.vivante.window;
+                break;
+            #endif
+
+            #if defined(SDL_VIDEO_DRIVER_KMSDRM)
+            case SDL_SYSWM_KMSDRM:
+                wminfo2->data1 = (void *) (size_t) info20.info.kmsdrm.dev_index;
+                wminfo2->data2 = (void *) (size_t) info20.info.kmsdrm.drm_fd;
+                wminfo2->data3 = (void *) (size_t) info20.info.kmsdrm.gbm_dev;
+                break;
+            #endif
+
+            default:
+                return 0;
+        }
+
+        return 1;
+    }
+
 #if defined(SDL_VIDEO_DRIVER_WINDOWS)
     SDL_assert(info20.subsystem == SDL_SYSWM_WINDOWS);
     info12->window = temp_window ? 0 : info20.info.win.window;
diff --git a/test/README b/test/README
index e158b4e42..a77450a7b 100644
--- a/test/README
+++ b/test/README
@@ -33,3 +33,4 @@ These are test programs for the SDL library:
 	testwm		Test window manager -- title, icon, events
 	threadwin	Test multi-threaded event handling
 	torturethread	Simple test for thread creation/destruction
+	testsyswm2on12	Simple test for SDL_SysWM2on12 compatibility magic.
\ No newline at end of file
diff --git a/test/testsyswm2on12.c b/test/testsyswm2on12.c
new file mode 100644
index 000000000..c4ee0dad2
--- /dev/null
+++ b/test/testsyswm2on12.c
@@ -0,0 +1,113 @@
+/* this isn't a classic SDL 1.2 test program, this tests a little
+   compatibility magic that only exists in sdl12-compat. */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "SDL.h"
+#include "SDL_syswm.h"
+
+/* so this will compile with classic 1.2 headers... */
+#ifndef SDL12_COMPAT_HEADERS
+#include "begin_code.h"
+typedef struct SDL_SysWMinfo2on12
+{
+    SDL_version version;
+    SDL_SYSWM_TYPE subsystem;
+    void *data1;
+    void *data2;
+    void *data3;
+    void *data4;
+    void *data5;
+    void *data6;
+    void *data7;
+    void *data8;
+} SDL_SysWMinfo2on12;
+#include "end_code.h"
+#endif
+
+typedef enum
+{
+    SDL20_SYSWM_UNKNOWN,
+    SDL20_SYSWM_WINDOWS,
+    SDL20_SYSWM_X11,
+    SDL20_SYSWM_DIRECTFB,
+    SDL20_SYSWM_COCOA,
+    SDL20_SYSWM_UIKIT,
+    SDL20_SYSWM_WAYLAND,
+    SDL20_SYSWM_MIR,
+    SDL20_SYSWM_WINRT,
+    SDL20_SYSWM_ANDROID,
+    SDL20_SYSWM_VIVANTE,
+    SDL20_SYSWM_OS2,
+    SDL20_SYSWM_HAIKU,
+    SDL20_SYSWM_KMSDRM,
+    SDL20_SYSWM_RISCOS
+} SDL20_SYSWM_TYPE;
+
+static const char *
+subsystem_name(const SDL20_SYSWM_TYPE typ)
+{
+    static char unrecognized[64];
+    switch (typ) {
+        case SDL20_SYSWM_UNKNOWN: return "Unknown";
+        case SDL20_SYSWM_WINDOWS: return "Windows";
+        case SDL20_SYSWM_X11: return "X11";
+        case SDL20_SYSWM_DIRECTFB: return "DirectFB";
+        case SDL20_SYSWM_COCOA: return "Cocoa";
+        case SDL20_SYSWM_UIKIT: return "UIKit";
+        case SDL20_SYSWM_WAYLAND: return "Wayland";
+        case SDL20_SYSWM_MIR: return "Mir";
+        case SDL20_SYSWM_WINRT: return "WinRT";
+        case SDL20_SYSWM_ANDROID: return "Android";
+        case SDL20_SYSWM_VIVANTE: return "Vivante";
+        case SDL20_SYSWM_OS2: return "OS/2";
+        case SDL20_SYSWM_HAIKU: return "Haiku";
+        case SDL20_SYSWM_KMSDRM: return "KMSDRM";
+        case SDL20_SYSWM_RISCOS: return "RiscOS";
+        default: break;
+    }
+
+    snprintf(unrecognized, sizeof (unrecognized), "Unrecognized (%d)", (int) typ);
+    return unrecognized;
+}
+
+int main(int argc, char **argv)
+{
+    SDL_SysWMinfo2on12 syswm_info;
+
+	if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+		fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
+		return 1;
+	}
+
+    if (SDL_SetVideoMode(640, 480, 32, 0) == NULL) {
+		fprintf(stderr, "Couldn't create an SDL window: %s\n", SDL_GetError());
+        SDL_Quit();
+		return 1;
+	}
+
+    SDL_memset(&syswm_info, '\0', sizeof (syswm_info));
+    syswm_info.version.major = 2;  /* this triggers magic in sdl12-compat */
+
+    /* obviously this should fail with classic SDL 1.2. */
+	if (SDL_GetWMInfo((SDL_SysWMinfo *) &syswm_info) != 1) {
+	    fprintf(stderr, "Failed to get syswm 2on12 info: %s\n", SDL_GetError());
+	} else {
+        printf("SysWM2on12 info:\n");
+        printf("Version: %d.%d.%d\n", syswm_info.version.major, syswm_info.version.minor, syswm_info.version.patch);
+        printf("Subsystem: %s\n", subsystem_name(syswm_info.subsystem));
+        printf("data1: %p\n", syswm_info.data1);
+        printf("data2: %p\n", syswm_info.data2);
+        printf("data3: %p\n", syswm_info.data3);
+        printf("data4: %p\n", syswm_info.data4);
+        printf("data5: %p\n", syswm_info.data5);
+        printf("data6: %p\n", syswm_info.data6);
+        printf("data7: %p\n", syswm_info.data7);
+        printf("data8: %p\n", syswm_info.data8);
+    }
+
+    SDL_Quit();
+    return 0;
+}
+