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) {