sdl2-compat: Build proper SDL2main static library for Windows

From 750ecd2f7c2d364ab6e127736c1bf6e7af5f02eb Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Thu, 12 Oct 2023 22:15:29 +0200
Subject: [PATCH] Build proper SDL2main static library for Windows

---
 CMakeLists.txt                         |  15 ++-
 src/SDLmain/windows/SDL_windows_main.c | 126 +++++++++++++++++++++++++
 test/testevdev.c                       |   2 +
 3 files changed, 134 insertions(+), 9 deletions(-)
 create mode 100644 src/SDLmain/windows/SDL_windows_main.c

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 83645bf..d061649 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -307,15 +307,12 @@ endif()
 
 # !!! FIXME: what needs to be done for sdl2-compat? Look into this.
 ## SDL2main library...
-#if(APPLE)
-#    add_library(SDL2main STATIC src/SDLmain/macosx/SDLMain.m)
-#    set_source_files_properties(src/SDLmain/macosx/SDLMain.m PROPERTIES LANGUAGE C)
-#elseif(WIN32)
-#    add_library(SDL2main STATIC src/SDLmain/win32/SDL_win32_main.c)
-#    set_target_properties(SDL2main PROPERTIES COMPILE_DEFINITIONS "_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE")  # !!! FIXME: don't use C runtime? We fixed this in SDL2.
-#else()
+if(WIN32)
+    add_library(SDL2main STATIC src/SDLmain/windows/SDL_windows_main.c)
+    target_link_libraries(SDL2main PRIVATE shell32)
+else()
     add_library(SDL2main STATIC src/SDLmain/dummy/SDL_dummy_main.c)
-#endif()
+endif()
 add_library(SDL2::SDL2main ALIAS SDL2main)
 target_include_directories(SDL2main
     PRIVATE
@@ -667,7 +664,7 @@ if(SDL2COMPAT_DEVEL)
     if(WIN32)
       set(SDL_CFLAGS "")
       set(SDL_RLD_FLAGS "")
-      set(SDL_LIBS "-lmingw32 -lSDL2main -lSDL2 -mwindows")
+      set(SDL_LIBS "-lmingw32 -lSDL2main -lshell32 -lSDL2 -mwindows")
       set(SDL_STATIC_LIBS "")
     elseif(APPLE)
       set(SDL_CFLAGS "-D_THREAD_SAFE")
diff --git a/src/SDLmain/windows/SDL_windows_main.c b/src/SDLmain/windows/SDL_windows_main.c
new file mode 100644
index 0000000..fedd6ba
--- /dev/null
+++ b/src/SDLmain/windows/SDL_windows_main.c
@@ -0,0 +1,126 @@
+/*
+    SDL_windows_main.c, placed in the public domain by Sam Lantinga  4/13/98
+
+    The WinMain function -- calls your program's main() function
+*/
+#include "SDL_config.h"
+
+#ifndef UNICODE
+#define UNICODE 1
+#endif
+
+#include <windows.h>
+#include <shellapi.h> /* CommandLineToArgvW() */
+
+#define WIN_StringToUTF8W(S) SDL_iconv_string("UTF-8", "UTF-16LE", (const char *)(S), (SDL_wcslen(S) + 1) * sizeof(WCHAR))
+
+/* See https://github.com/libsdl-org/SDL/pull/7607  */
+/* force_align_arg_pointer attribute requires gcc >= 4.2.x.  */
+#if defined(__clang__)
+#define HAVE_FORCE_ALIGN_ARG_POINTER
+#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
+#define HAVE_FORCE_ALIGN_ARG_POINTER
+#endif
+#if defined(__GNUC__) && defined(__i386__) && defined(HAVE_FORCE_ALIGN_ARG_POINTER)
+#define MINGW32_FORCEALIGN __attribute__((force_align_arg_pointer))
+#else
+#define MINGW32_FORCEALIGN
+#endif
+
+/* Include the SDL main definition header */
+#include "SDL.h"
+#include "SDL_main.h"
+
+#ifdef main
+#undef main
+#endif /* main */
+
+/* Pop up an out of memory message, returns to Windows */
+static BOOL OutOfMemory(void)
+{
+    SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Out of memory - aborting", NULL);
+    return FALSE;
+}
+
+#if defined(_MSC_VER)
+/* The VC++ compiler needs main/wmain defined */
+#define console_ansi_main main
+#if UNICODE
+#define console_wmain wmain
+#endif
+#endif
+
+/* Gets the arguments with GetCommandLine, converts them to argc and argv
+   and calls SDL_main */
+static int main_getcmdline(void)
+{
+    LPWSTR *argvw;
+    char **argv;
+    int i, argc, result;
+
+    argvw = CommandLineToArgvW(GetCommandLineW(), &argc);
+    if (argvw == NULL) {
+        return OutOfMemory();
+    }
+
+    /* Note that we need to be careful about how we allocate/free memory here.
+     * If the application calls SDL_SetMemoryFunctions(), we can't rely on
+     * SDL_free() to use the same allocator after SDL_main() returns.
+     */
+
+    /* Parse it into argv and argc */
+    argv = (char **)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (argc + 1) * sizeof(*argv));
+    if (argv == NULL) {
+        return OutOfMemory();
+    }
+    for (i = 0; i < argc; ++i) {
+        DWORD len;
+        char *arg = WIN_StringToUTF8W(argvw[i]);
+        if (arg == NULL) {
+            return OutOfMemory();
+        }
+        len = (DWORD)SDL_strlen(arg);
+        argv[i] = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (size_t)len + 1);
+        if (!argv[i]) {
+            return OutOfMemory();
+        }
+        SDL_memcpy(argv[i], arg, len);
+        SDL_free(arg);
+    }
+    argv[i] = NULL;
+    LocalFree(argvw);
+
+    SDL_SetMainReady();
+
+    /* Run the application main() code */
+    result = SDL_main(argc, argv);
+
+    /* Free argv, to avoid memory leak */
+    for (i = 0; i < argc; ++i) {
+        HeapFree(GetProcessHeap(), 0, argv[i]);
+    }
+    HeapFree(GetProcessHeap(), 0, argv);
+
+    return result;
+}
+
+/* This is where execution begins [console apps, ansi] */
+int console_ansi_main(int argc, char *argv[])
+{
+    return main_getcmdline();
+}
+
+#if UNICODE
+/* This is where execution begins [console apps, unicode] */
+int console_wmain(int argc, wchar_t *wargv[], wchar_t *wenvp)
+{
+    return main_getcmdline();
+}
+#endif
+
+/* This is where execution begins [windowed apps] */
+int WINAPI MINGW32_FORCEALIGN
+WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) /* NOLINT(readability-inconsistent-declaration-parameter-name) */
+{
+    return main_getcmdline();
+}
diff --git a/test/testevdev.c b/test/testevdev.c
index 50c13d1..69532f9 100644
--- a/test/testevdev.c
+++ b/test/testevdev.c
@@ -1030,6 +1030,8 @@ run_test(void)
 
 #else /* !(HAVE_LIBUDEV_H || defined(SDL_JOYSTICK_LINUX)) */
 
+#include "SDL.h"
+
 static int
 run_test(void)
 {