SDL: Make GDK's SDL_main header-only

From 28ecbbf0b544dab1a38e6e169ccf0b6961e6912d Mon Sep 17 00:00:00 2001
From: Daniel Gibson <[EMAIL REDACTED]>
Date: Sun, 11 Dec 2022 04:45:38 +0100
Subject: [PATCH] Make GDK's SDL_main header-only

---
 VisualC-GDK/tests/testgdk/src/testgdk.cpp |  5 +++--
 docs/README-gdk.md                        | 12 +++++-------
 include/SDL3/SDL_main.h                   |  7 ++++---
 include/SDL3/SDL_main_impl.h              | 21 ++++++++++++++-------
 src/main/gdk/SDL_gdk_main.c               | 19 +++----------------
 5 files changed, 29 insertions(+), 35 deletions(-)

diff --git a/VisualC-GDK/tests/testgdk/src/testgdk.cpp b/VisualC-GDK/tests/testgdk/src/testgdk.cpp
index 8395c8bb1caf..19a4fe67c96b 100644
--- a/VisualC-GDK/tests/testgdk/src/testgdk.cpp
+++ b/VisualC-GDK/tests/testgdk/src/testgdk.cpp
@@ -18,9 +18,10 @@
 #include <stdio.h>
 #include <time.h>
 
-#include "SDL_test.h"
-#include "SDL_test_common.h"
+#include <SDL3/SDL_test.h>
+#include <SDL3/SDL_test_common.h>
 #include "../src/core/windows/SDL_windows.h"
+#include <SDL3/SDL_main.h>
 
 extern "C" {
 #include "../test/testutils.h"
diff --git a/docs/README-gdk.md b/docs/README-gdk.md
index 32ab1412c8ff..5c9e45bc8978 100644
--- a/docs/README-gdk.md
+++ b/docs/README-gdk.md
@@ -25,7 +25,7 @@ The Windows GDK port supports the full set of Win32 APIs, renderers, controllers
     * Initializing/uninitializing the game runtime, and initializing Xbox Live services
     * Creating a global task queue and setting it as the default for the process. When running any async operations, passing in `NULL` as the task queue will make the task get added to the global task queue.
   
-  * An implementation on `WinMain` that performs the above GDK setup (you should link against SDL3_main.lib, as in Windows x64). If you are unable to do this, you can instead manually call `SDL_GDKRunApp` from your entry point, passing in your `SDL_main` function and `NULL` as the parameters.
+  * An implementation on `WinMain` that performs the above GDK setup that you can use by #include'ing SDL_main.h in the source file that includes your standard main() function. If you are unable to do this, you can instead manually call `SDL_GDKRunApp` from your entry point, passing in your `SDL_main` function and `NULL` as the parameters. To use `SDL_GDKRunApp`, `#define SDL_MAIN_HANDLED` before `#include <SDL3/SDL_main.h>`.
   * Global task queue callbacks are dispatched during `SDL_PumpEvents` (which is also called internally if using `SDL_PollEvent`).
   * You can get the handle of the global task queue through `SDL_GDKGetTaskQueue`, if needed. When done with the queue, be sure to use `XTaskQueueCloseHandle` to decrement the reference count (otherwise it will cause a resource leak).
   
@@ -37,7 +37,6 @@ The Windows GDK port supports the full set of Win32 APIs, renderers, controllers
 The included `VisualC-GDK/SDL.sln` solution includes the following targets for the Gaming.Desktop.x64 configuration:
 
 * SDL3 (DLL) - This is the typical SDL3.dll, but for Gaming.Desktop.x64.
-* SDL3_main (lib) - This contains a drop-in implementation of `WinMain` that is used as the entry point for GDK programs.
 * tests/testgamecontroller - Standard SDL test program demonstrating controller functionality.
 * tests/testgdk - GDK-specific test program that demonstrates using the global task queue to login a user into Xbox Live.
   *NOTE*: As of the June 2022 GDK, you cannot test user logins without a valid Title ID and MSAAppId. You will need to manually change the identifiers in the `MicrosoftGame.config` to your valid IDs from Partner Center if you wish to test this.
@@ -56,26 +55,25 @@ In your game's existing Visual Studio Solution, go to Build > Configuration Mana
 
 ### 2. Build SDL3 and SDL3_main for GDK ###
 
-Open `VisualC-GDK/SDL.sln` in Visual Studio, you need to build the SDL3 and SDL3_main targets for the Gaming.Desktop.x64 platform (Release is recommended). You will need to copy/keep track of the `SDL3.dll`, `XCurl.dll` (which is output by Gaming.Desktop.x64), `SDL3.lib`, and `SDL3_main.lib` output files for your game project.
+Open `VisualC-GDK/SDL.sln` in Visual Studio, you need to build the SDL3 target for the Gaming.Desktop.x64 platform (Release is recommended). You will need to copy/keep track of the `SDL3.dll`, `XCurl.dll` (which is output by Gaming.Desktop.x64), and `SDL3.lib` output files for your game project.
 
-*Alternatively*, you could setup your solution file to instead reference the SDL3/SDL3_main project file targets from the SDL source, and add those projects as a dependency. This would mean that SDL3 and SDL3_main would both be built when your game is built. 
+*Alternatively*, you could setup your solution file to instead reference the SDL3 project file targets from the SDL source, and add those projects as a dependency. This would mean that SDL3 would be built when your game is built.
 
 ### 3. Configuring Project Settings ###
 
 While the Gaming.Desktop.x64 configuration sets most of the required settings, there are some additional items to configure for your game project under the Gaming.Desktop.x64 Configuration:
 
 *  Under C/C++ > General > Additional Include Directories, make sure the `SDL/include` path is referenced
-* Under Linker > General > Additional Library Directories, make sure to reference the path where the newly-built SDL3.lib and SDL3_main.lib are
+* Under Linker > General > Additional Library Directories, make sure to reference the path where the newly-built SDL3.lib are
 * Under Linker > Input > Additional Dependencies, you need the following:
   * `SDL3.lib`
-  * `SDL3_main.lib` (unless not using)
   * `xgameruntime.lib`
   * `../Microsoft.Xbox.Services.141.GDK.C.Thunks.lib`
 * Note that in general, the GDK libraries depend on the MSVC C/C++ runtime, so there is no way to remove this dependency from a GDK program that links against GDK.
 
 ### 4. Setting up SDL_main ###
 
-Rather than using your own implementation of `WinMain`, it's recommended that you instead `#include "SDL_main.h"` and declare a standard main function. If you are unable to do this, you can instead manually call `SDL_GDKRunApp` from your entry point, passing in your `SDL_main` function and `NULL` as the parameters.
+Rather than using your own implementation of `WinMain`, it's recommended that you instead `#include <SDL3/SDL_main.h>` and declare a standard main function. If you are unable to do this, you can instead manually call `SDL_GDKRunApp` from your entry point, passing in your `SDL_main` function and `NULL` as the parameters; in that case `#define SDL_MAIN_HANDLED` before including SDL_main.h
 
 ### 5. Required DLLs ###
 
diff --git a/include/SDL3/SDL_main.h b/include/SDL3/SDL_main.h
index d90c0ecb432b..3a188d3aa60c 100644
--- a/include/SDL3/SDL_main.h
+++ b/include/SDL3/SDL_main.h
@@ -54,9 +54,10 @@
 #elif defined(__GDK__)
 /* On GDK, SDL provides a main function that initializes the game runtime.
 
-   Please note that #include'ing SDL_main.h is not enough to get a main()
-   function working. You must either link against SDL3_main or, if not possible,
-   call the SDL_GDKRunApp function from your entry point.
+   If you prefer to write your own WinMain-function instead of having SDL
+   provide one that calls your main() function,
+   #define SDL_MAIN_HANDLED before #include'ing SDL_main.h
+   and call the SDL_GDKRunApp function from your entry point.
 */
 #define SDL_MAIN_NEEDED
 
diff --git a/include/SDL3/SDL_main_impl.h b/include/SDL3/SDL_main_impl.h
index de4e8d972238..6221e5731d90 100644
--- a/include/SDL3/SDL_main_impl.h
+++ b/include/SDL3/SDL_main_impl.h
@@ -32,16 +32,13 @@
    not definition of SDL_MAIN_AVAILABLE etc in SDL_main.h) */
 #if !defined(SDL_MAIN_HANDLED) && !defined(_SDL_MAIN_NOIMPL)
 
-#if defined(__WIN32__)
+#if defined(__WIN32__) || defined(__GDK__)
 
 /* these defines/typedefs are needed for the WinMain() definition */
 #ifndef WINAPI
 #define WINAPI __stdcall
 #endif
 
-typedef struct HINSTANCE__ * HINSTANCE;
-typedef char* LPSTR;
-
 #ifdef main
 #  undef main
 #endif /* main */
@@ -52,6 +49,10 @@ typedef char* LPSTR;
 extern "C" {
 #endif
 
+typedef struct HINSTANCE__ * HINSTANCE;
+typedef char* LPSTR;
+
+#ifndef __GDK__ /* this is only needed for Win32 */
 
 #if defined(_MSC_VER)
 /* The VC++ compiler needs main/wmain defined */
@@ -79,11 +80,17 @@ console_ansi_main(int argc, char *argv[])
 }
 #endif /* UNICODE/ANSI */
 
-/* This is where execution begins [windowed apps] */
+#endif /* not __GDK__ */
+
+/* This is where execution begins [windowed apps and GDK] */
 int WINAPI
 WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
 {
+#ifdef __GDK__
+    return SDL_GDKRunApp(SDL_main, NULL);
+#else
     return SDL_Win32RunApp(SDL_main, NULL);
+#endif
 }
 
 #ifdef __cplusplus
@@ -95,8 +102,8 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
 /* rename users main() function to SDL_main() so it can be called from the wrapper above */
 #define main    SDL_main
 
-
-#elif 1 /* end of __WIN32__ impl - TODO: other platforms */
+/* end of __WIN32__ and __GDK__ impls */
+#elif 1 /* TODO: next platform */
 
 #endif /* __WIN32__ etc */
 
diff --git a/src/main/gdk/SDL_gdk_main.c b/src/main/gdk/SDL_gdk_main.c
index f1cc0bb410e4..aa9b4f64cb36 100644
--- a/src/main/gdk/SDL_gdk_main.c
+++ b/src/main/gdk/SDL_gdk_main.c
@@ -17,22 +17,9 @@
   2. Altered source versions must be plainly marked as such, and must not be
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
-*/
-#include <SDL3/SDL.h>
-#include <SDL3/SDL_main.h> /* until this SDL_main impl is converted to header-only.. */
-
-/* Include this so we define UNICODE properly */
-#include "../../core/windows/SDL_windows.h"
 
-#ifdef main
-#undef main
-#endif /* main */
-
-/* This is where execution begins */
-int WINAPI
-WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
-{
-    return SDL_GDKRunApp(SDL_main, NULL);
-}
+  Nothing to do here, the code moved into SDL_main_impl.h
+    TODO: remove this file
+*/
 
 /* vi: set ts=4 sw=4 expandtab: */