diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj
index 52fe361a0a086..dbec42635dd10 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj
+++ b/VisualC-GDK/SDL/SDL.vcxproj
@@ -478,6 +478,7 @@
+
@@ -543,8 +544,11 @@
-
+
+
+ $(IntDir)\src_main_windows_SDL_sysmain_runapp.obj
+
diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters
index bbbf658450138..a095bd2d54948 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj.filters
+++ b/VisualC-GDK/SDL/SDL.vcxproj.filters
@@ -8,8 +8,9 @@
-
+
+
@@ -370,6 +371,7 @@
+
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index 7d4c3a201b275..2fc7a70605b8f 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -391,6 +391,7 @@
+
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index 614eafc2beee1..f1c92334d8b36 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/VisualC/SDL/SDL.vcxproj.filters
@@ -459,6 +459,9 @@
main
+
+ main
+
diff --git a/docs/README-main-functions.md b/docs/README-main-functions.md
index 75b9e2cc04e7e..759c7a8013d7a 100644
--- a/docs/README-main-functions.md
+++ b/docs/README-main-functions.md
@@ -55,7 +55,7 @@ macro before including the header:
```c
-#define SDL_MAIN_NOIMPL
+#define SDL_MAIN_HANDLED
```
If you are moving from SDL2, remove any references to the SDLmain static
diff --git a/include/SDL3/SDL_main.h b/include/SDL3/SDL_main.h
index 32775b445191e..147c529f9c621 100644
--- a/include/SDL3/SDL_main.h
+++ b/include/SDL3/SDL_main.h
@@ -41,9 +41,12 @@
* This is also where an app can be configured to use the main callbacks, via
* the SDL_MAIN_USE_CALLBACKS macro.
*
- * SDL_main.h is a "single-header library," which is to say that including
+ * `SDL_main.h` is a "single-header library," which is to say that including
* this header inserts code into your program, and you should only include it
- * once in most cases. SDL.h does not include this header automatically.
+ * once in most cases. `SDL.h` does not include this header automatically.
+ *
+ * If you want to include `SDL_main.h` but don't want SDL to redefine main(),
+ * you should define SDL_MAIN_HANDLED before including `SDL_main.h`.
*
* For more information, see:
*
@@ -66,7 +69,7 @@
* SDL does not define this macro, but will check if it is defined when
* including `SDL_main.h`. If defined, SDL will expect the app to provide the
* proper entry point for the platform, and all the other magic details
- * needed, like manually calling SDL_SetMainReady.
+ * needed, like manually calling SDL_RunApp() or SDL_SetMainReady().
*
* Please see [README/main-functions](README/main-functions), (or
* docs/README-main-functions.md in the source tree) for a more detailed
@@ -114,7 +117,7 @@
*
* \since This macro is available since SDL 3.2.0.
*/
-#define SDL_MAIN_AVAILABLE
+#define SDL_MAIN_AVAILABLE 1
/**
* Defined if the target platform _requires_ a special mainline through SDL.
@@ -132,7 +135,7 @@
*
* \since This macro is available since SDL 3.2.0.
*/
-#define SDL_MAIN_NEEDED
+#define SDL_MAIN_NEEDED 1
#endif
@@ -248,12 +251,12 @@
*
* \sa SDL_DECLSPEC
*/
-#define SDLMAIN_DECLSPEC
+#define SDLMAIN_DECLSPEC SDL_DECLSPEC
#elif defined(SDL_MAIN_EXPORTED)
/* We need to export SDL_main so it can be launched from external code,
like SDLActivity.java on Android */
-#define SDLMAIN_DECLSPEC SDL_DECLSPEC
+#define SDLMAIN_DECLSPEC SDL_DECLSPEC
#else
/* usually this is empty */
#define SDLMAIN_DECLSPEC
@@ -271,7 +274,7 @@ extern "C" {
/*
* You can (optionally!) define SDL_MAIN_USE_CALLBACKS before including
- * SDL_main.h, and then your application will _not_ have a standard
+ * `SDL_main.h`, and then your application will _not_ have a standard
* "main" entry point. Instead, it will operate as a collection of
* functions that are called as necessary by the system. On some
* platforms, this is just a layer where SDL drives your program
@@ -529,17 +532,22 @@ typedef int (SDLCALL *SDL_main_func)(int argc, char *argv[]);
extern SDLMAIN_DECLSPEC int SDLCALL SDL_main(int argc, char *argv[]);
/**
- * Circumvent failure of SDL_Init() when not using SDL_main() as an entry
- * point.
+ * Informs SDL that the app has performed all the due diligence required when
+ * providing its own entry point instead of using SDL_main().
*
- * This function is defined in SDL_main.h, along with the preprocessor rule to
- * redefine main() as SDL_main(). Thus to ensure that your main() function
- * will not be changed it is necessary to define SDL_MAIN_HANDLED before
- * including SDL.h.
+ * Apps that don't use SDL_main() should call SDL_SetMainReady() before
+ * calling SDL_Init(), otherwise SDL_Init() may fail on some platforms.
+ *
+ * If your app provides its own entry point, consider using SDL_RunApp(),
+ * which will perform all of the required platform-specific setup for you and
+ * is generally more portable and reliable than performing all the setup on
+ * your own. When using SDL_RunApp(), you do *not* need to call
+ * SDL_SetMainReady().
*
* \since This function is available since SDL 3.2.0.
*
* \sa SDL_Init
+ * \sa SDL_RunApp
*/
extern SDL_DECLSPEC void SDLCALL SDL_SetMainReady(void);
@@ -550,13 +558,21 @@ extern SDL_DECLSPEC void SDLCALL SDL_SetMainReady(void);
* mainFunction.
*
* You can use this if you want to use your own main() implementation without
- * using SDL_main (like when using SDL_MAIN_HANDLED). When using this, you do
- * *not* need SDL_SetMainReady().
+ * using SDL_main() (like when using SDL_MAIN_HANDLED). When using this, you
+ * do *not* need to call SDL_SetMainReady().
+ *
+ * If `argv` is not NULL, SDL will pass it forward to `mainFunction` as-is.
+ * Otherwise, if `argv` is NULL, the behavior is platform-dependent: on some
+ * platforms, SDL may try to get the command-line arguments for the current
+ * process and use those instead, and on others it may fall back on a simple
+ * dummy argv. Either way, SDL ensures that the final argv passed to
+ * `mainFunction` is never NULL.
*
* \param argc the argc parameter from the application's main() function, or 0
* if the platform's main-equivalent has no argc.
* \param argv the argv parameter from the application's main() function, or
- * NULL if the platform's main-equivalent has no argv.
+ * NULL if the platform's main-equivalent has no argv (in which
+ * case SDL will override it in a platform-specific manner).
* \param mainFunction your SDL app's C-style main(). NOT the function you're
* calling this from! Its name doesn't matter; it doesn't
* literally have to be `main`.
@@ -578,7 +594,7 @@ extern SDL_DECLSPEC int SDLCALL SDL_RunApp(int argc, char *argv[], SDL_main_func
*
* Generally, you should not call this function directly. This only exists to
* hand off work into SDL as soon as possible, where it has a lot more control
- * and functionality available, and make the inline code in SDL_main.h as
+ * and functionality available, and make the inline code in `SDL_main.h` as
* small as possible.
*
* Not all platforms use this, it's actual use is hidden in a magic
@@ -586,7 +602,7 @@ extern SDL_DECLSPEC int SDLCALL SDL_RunApp(int argc, char *argv[], SDL_main_func
* _really_ know what you're doing.
*
* \param argc standard Unix main argc.
- * \param argv standard Unix main argv.
+ * \param argv standard Unix main argv. Should not be NULL.
* \param appinit the application's SDL_AppInit function.
* \param appiter the application's SDL_AppIterate function.
* \param appevent the application's SDL_AppEvent function.
diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c
index 1fb6fbf65b0a5..31dcfadf94aad 100644
--- a/src/core/android/SDL_android.c
+++ b/src/core/android/SDL_android.c
@@ -25,6 +25,7 @@
#include "SDL_android.h"
#include "../../events/SDL_events_c.h"
+#include "../../main/SDL_runapp.h"
#include "../../video/android/SDL_androidkeyboard.h"
#include "../../video/android/SDL_androidmouse.h"
#include "../../video/android/SDL_androidtouch.h"
@@ -817,41 +818,49 @@ JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(JNIEnv *env, jclass cls,
char **argv;
bool isstack;
+ // Always use the name "app_process" for argv[0] so PHYSFS_platformCalcBaseDir() works.
+ // https://github.com/love2d/love-android/issues/24
+
// Prepare the arguments.
len = (*env)->GetArrayLength(env, array);
- argv = SDL_small_alloc(char *, 1 + len + 1, &isstack); // !!! FIXME: check for NULL
- argc = 0;
- /* Use the name "app_process" so PHYSFS_platformCalcBaseDir() works.
- https://github.com/love2d/love-android/issues/24
- */
- argv[argc++] = SDL_strdup("app_process");
- for (i = 0; i < len; ++i) {
- char *arg = NULL;
- jstring string = (*env)->GetObjectArrayElement(env, array, i);
- if (string) {
- const char *utf = (*env)->GetStringUTFChars(env, string, 0);
- if (utf) {
- arg = SDL_strdup(utf);
- (*env)->ReleaseStringUTFChars(env, string, utf);
+ argv = SDL_small_alloc(char *, 1 + len + 1, &isstack);
+ if (!argv) {
+ // Failed to allocate the argv (out of memory?). Use a dummy argv instead.
+ char dummyargv0[] = { 'a', 'p', 'p', '_', 'p', 'r', 'o', 'c', 'e', 's', 's', '\0' };
+ char *dummyargv[2] = { dummyargv0, NULL };
+
+ // Run the application (without arguments).
+ status = SDL_main(1, dummyargv);
+ } else {
+ argc = 0;
+ argv[argc++] = SDL_strdup("app_process");
+ for (i = 0; i < len; ++i) {
+ char *arg = NULL;
+ jstring string = (*env)->GetObjectArrayElement(env, array, i);
+ if (string) {
+ const char *utf = (*env)->GetStringUTFChars(env, string, 0);
+ if (utf) {
+ arg = SDL_strdup(utf);
+ (*env)->ReleaseStringUTFChars(env, string, utf);
+ }
+ (*env)->DeleteLocalRef(env, string);
}
- (*env)->DeleteLocalRef(env, string);
- }
- if (arg == NULL) {
- arg = SDL_strdup("");
+ if (arg == NULL) {
+ arg = SDL_strdup("");
+ }
+ argv[argc++] = arg;
}
- argv[argc++] = arg;
- }
- argv[argc] = NULL;
+ argv[argc] = NULL;
- // Run the application.
- status = SDL_main(argc, argv);
+ // Run the application.
+ status = SDL_main(argc, argv);
- // Release the arguments.
- for (i = 0; i < argc; ++i) {
- SDL_free(argv[i]);
+ // Release the arguments.
+ for (i = 0; i < argc; ++i) {
+ SDL_free(argv[i]);
+ }
+ SDL_small_free(argv, isstack);
}
- SDL_small_free(argv, isstack);
-
} else {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Couldn't find function %s in library %s", function_name, library_file);
}
diff --git a/src/main/SDL_runapp.c b/src/main/SDL_runapp.c
index ddda4a63a3366..61e3061f8d7f7 100644
--- a/src/main/SDL_runapp.c
+++ b/src/main/SDL_runapp.c
@@ -19,22 +19,37 @@
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
+#include "SDL_runapp.h"
-/* Most platforms that use/need SDL_main have their own SDL_RunApp() implementation.
- * If not, you can special case it here by appending || defined(__YOUR_PLATFORM__) */
+// Most platforms that use/need SDL_main have their own SDL_RunApp() implementations.
+// If not, you can special case it here by appending '|| defined(__YOUR_PLATFORM__)'.
#if ( !defined(SDL_MAIN_NEEDED) && !defined(SDL_MAIN_AVAILABLE) ) || defined(SDL_PLATFORM_ANDROID)
int SDL_RunApp(int argc, char* argv[], SDL_main_func mainFunction, void * reserved)
{
(void)reserved;
- if(!argv)
- {
- // make sure argv isn't NULL, in case some user code doesn't like that
- static char dummyargv0[] = { 'S', 'D', 'L', '_', 'a', 'p', 'p', '\0' };
- static char* argvdummy[2] = { dummyargv0, NULL };
+ return SDL_CallMain(argc, argv, mainFunction);
+}
+
+#endif
+
+// Most platforms receive a standard argv via their native entry points
+// and don't provide any other (reliable) ways of getting the command-line arguments.
+// For those platforms, we only try to ensure that the argv is not NULL
+// (which it might be if the user calls SDL_RunApp() directly instead of using SDL_main).
+// Platforms that don't use standard argc/argv entry points but provide other ways of
+// getting the command-line arguments have their own SDL_CallMain() implementations.
+#ifndef SDL_PLATFORM_WINDOWS
+
+int SDL_CallMain(int argc, char* argv[], SDL_main_func mainFunction)
+{
+ char dummyargv0[] = { 'S', 'D', 'L', '_', 'a', 'p', 'p', '\0' };
+ char *dummyargv[2] = { dummyargv0, NULL };
+
+ if (!argv || argc < 0) {
argc = 1;
- argv = argvdummy;
+ argv = dummyargv;
}
return mainFunction(argc, argv);
diff --git a/src/main/SDL_runapp.h b/src/main/SDL_runapp.h
new file mode 100644
index 0000000000000..f6f2062001424
--- /dev/null
+++ b/src/main/SDL_runapp.h
@@ -0,0 +1,32 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2025 Sam Lantinga
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 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.
+*/
+
+#ifndef SDL_runapp_h_
+#define SDL_runapp_h_
+
+// Call the provided main function.
+// If the provided argv is non-NULL, SDL will pass it forward to mainFunction as-is.
+// If the provided argv is NULL, the behavior is platform-dependent.
+// SDL may try to get the command-line arguments for the current process and use those instead,
+// or it may fall back on a simple dummy argv.
+int SDL_CallMain(int argc, char* argv[], SDL_main_func mainFunction);
+
+#endif // SDL_runapp_h_
diff --git a/src/main/emscripten/SDL_sysmain_runapp.c b/src/main/emscripten/SDL_sysmain_runapp.c
index 20dd6ebec8650..af529c89242da 100644
--- a/src/main/emscripten/SDL_sysmain_runapp.c
+++ b/src/main/emscripten/SDL_sysmain_runapp.c
@@ -24,6 +24,8 @@
#include
+#include "../SDL_runapp.h"
+
EM_JS_DEPS(sdlrunapp, "$dynCall,$stringToNewUTF8");
int SDL_RunApp(int argc, char* argv[], SDL_main_func mainFunction, void * reserved)
@@ -50,7 +52,7 @@ int SDL_RunApp(int argc, char* argv[], SDL_main_func mainFunction, void * reserv
}
}, SDL_setenv_unsafe);
- return mainFunction(argc, argv);
+ return SDL_CallMain(argc, argv, mainFunction);
}
#endif
diff --git a/src/main/gdk/SDL_sysmain_runapp.cpp b/src/main/gdk/SDL_sysmain_runapp.cpp
index d7bdea0a1a989..42a23aad25e1b 100644
--- a/src/main/gdk/SDL_sysmain_runapp.cpp
+++ b/src/main/gdk/SDL_sysmain_runapp.cpp
@@ -21,6 +21,7 @@
#include "SDL_internal.h"
extern "C" {
+#include "../SDL_runapp.h"
#include "../../core/gdk/SDL_gdk.h"
#include "../../core/windows/SDL_windows.h"
#include "../../events/SDL_events_c.h"
@@ -30,58 +31,14 @@ extern "C" {
#include // CommandLineToArgvW()
#include
-// 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;
-}
-
-/* Gets the arguments with GetCommandLine, converts them to argc and argv
- and calls SDL_main */
extern "C"
-int SDL_RunApp(int, char**, SDL_main_func mainFunction, void *reserved)
+int SDL_RunApp(int argc, char* argv[], SDL_main_func mainFunction, void *)
{
- LPWSTR *argvw;
- char **argv;
- int i, argc, result;
+ int result;
HRESULT hr;
XTaskQueueHandle taskQueue;
- 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) {
- const int utf8size = WideCharToMultiByte(CP_UTF8, 0, argvw[i], -1, NULL, 0, NULL, NULL);
- if (!utf8size) { // uhoh?
- SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Error processing command line arguments", NULL);
- return -1;
- }
-
- argv[i] = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, utf8size); // this size includes the null-terminator character.
- if (!argv[i]) {
- return OutOfMemory();
- }
-
- if (WideCharToMultiByte(CP_UTF8, 0, argvw[i], -1, argv[i], utf8size, NULL, NULL) == 0) { // failed? uhoh!
- SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Error processing command line arguments", NULL);
- return -1;
- }
- }
- argv[i] = NULL;
- LocalFree(argvw);
+ // !!! FIXME: This function does not currently properly deinitialize GDK resources on failure.
hr = XGameRuntimeInitialize();
@@ -99,6 +56,7 @@ int SDL_RunApp(int, char**, SDL_main_func mainFunction, void *reserved)
xblArgs.queue = taskQueue;
SDL_snprintf(scidBuffer, 64, "00000000-0000-0000-0000-0000%08X", titleid);
xblArgs.scid = scidBuffer;
+ // !!! FIXME: XblInitialize() should have a corresponding call to XblCleanup() according to the docs.
hr = XblInitialize(&xblArgs);
} else {
SDL_SetError("[GDK] Unable to get titleid. Will not call XblInitialize. Check MicrosoftGame.config!");
@@ -110,8 +68,9 @@ int SDL_RunApp(int, char**, SDL_main_func mainFunction, void *reserved)
return -1;
}
- // Run the application main() code
- result = mainFunction(argc, argv);
+ // The common Windows SDL_CallMain() implementation is responsible for handling the argv
+ // and substituting it with a new argv parsed from the command-line string, if needed.
+ result = SDL_CallMain(argc, argv, mainFunction);
GDK_UnregisterChangeNotifications();
@@ -137,12 +96,5 @@ int SDL_RunApp(int, char**, SDL_main_func mainFunction, void *reserved)
result = -1;
}
- // Free argv, to avoid memory leak
- for (i = 0; i < argc; ++i) {
- HeapFree(GetProcessHeap(), 0, argv[i]);
- }
- HeapFree(GetProcessHeap(), 0, argv);
-
return result;
}
-
diff --git a/src/main/n3ds/SDL_sysmain_runapp.c b/src/main/n3ds/SDL_sysmain_runapp.c
index 74ead997388d3..a20d42e277e18 100644
--- a/src/main/n3ds/SDL_sysmain_runapp.c
+++ b/src/main/n3ds/SDL_sysmain_runapp.c
@@ -25,15 +25,19 @@
#include <3ds.h>
+#include "../SDL_runapp.h"
+
int SDL_RunApp(int argc, char* argv[], SDL_main_func mainFunction, void * reserved)
{
int result;
+ (void)reserved;
+
// init
osSetSpeedupEnable(true);
romfsInit();
SDL_SetMainReady();
- result = mainFunction(argc, argv);
+ result = SDL_CallMain(argc, argv, mainFunction);
// quit
romfsExit();
@@ -41,8 +45,4 @@ int SDL_RunApp(int argc, char* argv[], SDL_main_func mainFunction, void * reserv
return result;
}
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
#endif
diff --git a/src/main/ps2/SDL_sysmain_runapp.c b/src/main/ps2/SDL_sysmain_runapp.c
index 23443c5a5fa4a..92deba86ba122 100644
--- a/src/main/ps2/SDL_sysmain_runapp.c
+++ b/src/main/ps2/SDL_sysmain_runapp.c
@@ -36,6 +36,8 @@
#include
#include
+#include "../SDL_runapp.h"
+
__attribute__((weak)) void reset_IOP(void)
{
SifInitRpc(0);
@@ -74,7 +76,7 @@ int SDL_RunApp(int argc, char* argv[], SDL_main_func mainFunction, void * reserv
SDL_SetMainReady();
- res = mainFunction(argc, argv);
+ res = SDL_CallMain(argc, argv, mainFunction);
deinit_drivers();
diff --git a/src/main/psp/SDL_sysmain_runapp.c b/src/main/psp/SDL_sysmain_runapp.c
index e89f5ffe7e6a0..e528872c3781a 100644
--- a/src/main/psp/SDL_sysmain_runapp.c
+++ b/src/main/psp/SDL_sysmain_runapp.c
@@ -27,6 +27,8 @@
#include
#include
+
+#include "../SDL_runapp.h"
#include "../../events/SDL_events_c.h"
/* If application's main() is redefined as SDL_main, and libSDL_main is
@@ -76,7 +78,7 @@ int SDL_RunApp(int argc, char* argv[], SDL_main_func mainFunction, void * reserv
SDL_SetMainReady();
- return mainFunction(argc, argv);
+ return SDL_CallMain(argc, argv, mainFunction);
}
#endif // SDL_PLATFORM_PSP
diff --git a/src/main/windows/SDL_sysmain_runapp.c b/src/main/windows/SDL_sysmain_runapp.c
index d8c3fac153744..44cf710d1c902 100644
--- a/src/main/windows/SDL_sysmain_runapp.c
+++ b/src/main/windows/SDL_sysmain_runapp.c
@@ -20,80 +20,104 @@
*/
#include "SDL_internal.h"
-#ifdef SDL_PLATFORM_WIN32
+#ifdef SDL_PLATFORM_WINDOWS
+#include "../SDL_runapp.h"
#include "../../core/windows/SDL_windows.h"
-/* Win32-specific SDL_RunApp(), which does most of the SDL_main work,
- based on SDL_windows_main.c, placed in the public domain by Sam Lantinga 4/13/98 */
-
#include // CommandLineToArgvW()
-// Pop up an out of memory message, returns to Windows
static int OutOfMemory(void)
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Out of memory - aborting", NULL);
return -1;
}
-int MINGW32_FORCEALIGN SDL_RunApp(int _argc, char* _argv[], SDL_main_func mainFunction, void * reserved)
+static int ErrorProcessingCommandLine(void)
{
- /* Gets the arguments with GetCommandLine, converts them to argc and argv
- and calls SDL_main */
-
- LPWSTR *argvw;
- char **argv;
- int i, argc, result;
-
- (void)_argc; (void)_argv; (void)reserved;
-
- argvw = CommandLineToArgvW(GetCommandLineW(), &argc);
- if (!argvw) {
- 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.
- */
+ SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Error processing command line arguments", NULL);
+ return -1;
+}
- // Parse it into argv and argc
- argv = (char **)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (argc + 1) * sizeof(*argv));
- if (!argv) {
- return OutOfMemory();
- }
- for (i = 0; i < argc; ++i) {
- const int utf8size = WideCharToMultiByte(CP_UTF8, 0, argvw[i], -1, NULL, 0, NULL, NULL);
- if (!utf8size) { // uhoh?
- SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Error processing command line arguments", NULL);
- return -1;
+// If the provided argv is NULL (which it will be when using SDL_main), this function parses
+// the command-line string for the current process into an argv and uses that instead.
+// Otherwise, the provided argv is used as-is (since that's probably what the user wants).
+int SDL_CallMain(int caller_argc, char* caller_argv[], SDL_main_func mainFunction)
+{
+ int result, argc;
+ LPWSTR *argvw = NULL;
+ char **argv = NULL;
+
+ // Note that we need to be careful about how we allocate/free memory in this function. If the application calls
+ // SDL_SetMemoryFunctions(), we can't rely on SDL_free() to use the same allocator after SDL_main() returns.
+
+ if (!caller_argv || caller_argc < 0) {
+ // If the passed argv is NULL (or argc is negative), SDL should get the command-line arguments
+ // using GetCommandLineW() and convert them to argc and argv before calling mainFunction().
+
+ // Because of how the Windows command-line works, we know for sure that the buffer size required to store all
+ // argument strings converted to UTF-8 (with null terminators) is guaranteed to be less than or equal to the
+ // size of the original command-line string converted to UTF-8.
+ const int argdata_size = WideCharToMultiByte(CP_UTF8, 0, GetCommandLineW(), -1, NULL, 0, NULL, NULL); // Includes the null terminator
+ if (!argdata_size) {
+ result = ErrorProcessingCommandLine();
+ goto cleanup;
}
- argv[i] = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, utf8size); // this size includes the null-terminator character.
- if (!argv[i]) {
- return OutOfMemory();
+ argvw = CommandLineToArgvW(GetCommandLineW(), &argc);
+ if (!argvw || argc < 0) {
+ result = OutOfMemory();
+ goto cleanup;
}
- if (WideCharToMultiByte(CP_UTF8, 0, argvw[i], -1, argv[i], utf8size, NULL, NULL) == 0) { // failed? uhoh!
- SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Error processing command line arguments", NULL);
- return -1;
+ // Allocate argv followed by the argument string buffer as one contiguous allocation.
+ argv = (char **)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (argc + 1) * sizeof(*argv) + argdata_size);
+ if (!argv) {
+ result = OutOfMemory();
+ goto cleanup;
}
+ char *argdata = ((char *)argv) + (argc + 1) * sizeof(*argv);
+ int argdata_index = 0;
+
+ for (int i = 0; i < argc; ++i) {
+ const int bytes_written = WideCharToMultiByte(CP_UTF8, 0, argvw[i], -1, argdata + argdata_index, argdata_size - argdata_index, NULL, NULL);
+ if (!bytes_written) {
+ result = ErrorProcessingCommandLine();
+ goto cleanup;
+ }
+ argv[i] = argdata + argdata_index;
+ argdata_index += bytes_written;
+ }
+ argv[argc] = NULL;
+
+ argvw = NULL;
+
+ caller_argc = argc;
+ caller_argv = argv;
}
- argv[i] = NULL;
- LocalFree(argvw);
- SDL_SetMainReady();
+ result = mainFunction(caller_argc, caller_argv);
- // Run the application main() code
- result = mainFunction(argc, argv);
+cleanup:
- // Free argv, to avoid memory leak
- for (i = 0; i < argc; ++i) {
- HeapFree(GetProcessHeap(), 0, argv[i]);
- }
HeapFree(GetProcessHeap(), 0, argv);
+ LocalFree(argvw);
return result;
}
-#endif // SDL_PLATFORM_WIN32
+// GDK uses the same SDL_CallMain() implementation as desktop Windows but has its own SDL_RunApp() implementation.
+#ifndef SDL_PLATFORM_GDK
+
+int SDL_RunApp(int argc, char* argv[], SDL_main_func mainFunction, void * reserved)
+{
+ (void)reserved;
+
+ SDL_SetMainReady();
+
+ return SDL_CallMain(argc, argv, mainFunction);
+}
+
+#endif // !SDL_PLATFORM_GDK
+
+#endif // SDL_PLATFORM_WINDOWS
diff --git a/src/video/uikit/SDL_uikitappdelegate.m b/src/video/uikit/SDL_uikitappdelegate.m
index 6a37e51129886..dd438cbc11740 100644
--- a/src/video/uikit/SDL_uikitappdelegate.m
+++ b/src/video/uikit/SDL_uikitappdelegate.m
@@ -29,6 +29,7 @@
#import "SDL_uikitwindow.h"
#include "../../events/SDL_events_c.h"
+#include "../../main/SDL_runapp.h"
#ifdef main
#undef main
@@ -42,6 +43,7 @@
int SDL_RunApp(int argc, char* argv[], SDL_main_func mainFunction, void * reserved)
{
int i;
+ (void)reserved;
// store arguments
/* Note that we need to be careful about how we allocate/free memory here.
@@ -49,24 +51,31 @@ int SDL_RunApp(int argc, char* argv[], SDL_main_func mainFunction, void * reserv
* SDL_free() to use the same allocator after SDL_main() returns.
*/
forward_main = mainFunction;
- forward_argc = argc;
- forward_argv = (char **)malloc((argc + 1) * sizeof(char *)); // This should NOT be SDL_malloc()
- for (i = 0; i < argc; i++) {
- forward_argv[i] = malloc((strlen(argv[i]) + 1) * sizeof(char)); // This should NOT be SDL_malloc()
- strcpy(forward_argv[i], argv[i]);
+ if (argv) {
+ forward_argc = argc;
+ forward_argv = (char **)malloc((argc + 1) * sizeof(char *)); // This should NOT be SDL_malloc()
+ for (i = 0; i < argc; i++) {
+ forward_argv[i] = malloc((strlen(argv[i]) + 1) * sizeof(char)); // This should NOT be SDL_malloc()
+ strcpy(forward_argv[i], argv[i]);
+ }
+ forward_argv[i] = NULL;
+ } else {
+ forward_argc = 0;
+ forward_argv = NULL;
}
- forward_argv[i] = NULL;
// Give over control to run loop, SDLUIKitDelegate will handle most things from here
@autoreleasepool {
UIApplicationMain(argc, argv, nil, [SDLUIKitDelegate getAppDelegateClassName]);
}
- // free the memory we used to hold copies of argc and argv
- for (i = 0; i < forward_argc; i++) {
- free(forward_argv[i]); // This should NOT be SDL_free()
+ if (forward_argv) {
+ // free the memory we used to hold copies of argc and argv
+ for (i = 0; i < forward_argc; i++) {
+ free(forward_argv[i]); // This should NOT be SDL_free()
+ }
+ free(forward_argv); // This should NOT be SDL_free()
}
- free(forward_argv); // This should NOT be SDL_free()
return exit_status;
}
@@ -395,7 +404,7 @@ - (void)postFinishLaunch
// run the user's application, passing argc and argv
SDL_SetiOSEventPump(true);
- exit_status = forward_main(forward_argc, forward_argv);
+ exit_status = SDL_CallMain(forward_argc, forward_argv, forward_main);
SDL_SetiOSEventPump(false);
if (launchWindow) {