Skip to content

main: Change how SDL_RunApp() handles and overrides the provided argv #12676

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion VisualC-GDK/SDL/SDL.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,7 @@
<ClInclude Include="..\..\src\libm\math_private.h" />
<ClInclude Include="..\..\src\locale\SDL_syslocale.h" />
<ClInclude Include="..\..\src\main\SDL_main_callbacks.h" />
<ClInclude Include="..\..\src\main\SDL_runapp.h" />
<ClInclude Include="..\..\src\misc\SDL_sysurl.h" />
<ClInclude Include="..\..\src\power\SDL_syspower.h" />
<ClInclude Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.h" />
Expand Down Expand Up @@ -543,8 +544,11 @@
<ClCompile Include="..\..\src\io\generic\SDL_asyncio_generic.c" />
<ClCompile Include="..\..\src\io\SDL_asyncio.c" />
<ClCompile Include="..\..\src\io\windows\SDL_asyncio_windows_ioring.c" />
<ClCompile Include="..\..\src\main\gdk\SDL_sysmain_runapp.cpp" />
<ClCompile Include="..\..\src\main\generic\SDL_sysmain_callbacks.c" />
<ClCompile Include="..\..\src\main\gdk\SDL_sysmain_runapp.cpp" />
<ClCompile Include="..\..\src\main\windows\SDL_sysmain_runapp.c">
<ObjectFileName>$(IntDir)\src_main_windows_SDL_sysmain_runapp.obj</ObjectFileName>
</ClCompile>
<ClCompile Include="..\..\src\main\SDL_main_callbacks.c" />
<ClCompile Include="..\..\src\main\SDL_runapp.c" />
<ClCompile Include="..\..\src\SDL_guid.c" />
Expand Down
4 changes: 3 additions & 1 deletion VisualC-GDK/SDL/SDL.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
<ClCompile Include="..\..\src\render\direct3d12\SDL_shaders_d3d12_xboxone.cpp" />
<ClCompile Include="..\..\src\render\direct3d12\SDL_shaders_d3d12_xboxseries.cpp" />
<ClCompile Include="..\..\src\main\generic\SDL_sysmain_callbacks.c" />
<ClCompile Include="..\..\src\main\SDL_main_callbacks.c" />
<ClCompile Include="..\..\src\main\gdk\SDL_sysmain_runapp.cpp" />
<ClCompile Include="..\..\src\main\windows\SDL_sysmain_runapp.c" />
<ClCompile Include="..\..\src\main\SDL_main_callbacks.c" />
<ClCompile Include="..\..\src\main\SDL_runapp.c" />
<ClCompile Include="..\..\src\SDL_guid.c" />
<ClCompile Include="..\..\src\atomic\SDL_atomic.c" />
Expand Down Expand Up @@ -370,6 +371,7 @@
<ClInclude Include="..\..\src\libm\math_private.h" />
<ClInclude Include="..\..\src\locale\SDL_syslocale.h" />
<ClInclude Include="..\..\src\main\SDL_main_callbacks.h" />
<ClInclude Include="..\..\src\main\SDL_runapp.h" />
<ClInclude Include="..\..\src\misc\SDL_sysurl.h" />
<ClInclude Include="..\..\src\power\SDL_syspower.h" />
<ClInclude Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.h" />
Expand Down
1 change: 1 addition & 0 deletions VisualC/SDL/SDL.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@
<ClInclude Include="..\..\src\libm\math_private.h" />
<ClInclude Include="..\..\src\locale\SDL_syslocale.h" />
<ClInclude Include="..\..\src\main\SDL_main_callbacks.h" />
<ClInclude Include="..\..\src\main\SDL_runapp.h" />
<ClInclude Include="..\..\src\misc\SDL_sysurl.h" />
<ClInclude Include="..\..\src\power\SDL_syspower.h" />
<ClInclude Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.h" />
Expand Down
3 changes: 3 additions & 0 deletions VisualC/SDL/SDL.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,9 @@
<ClInclude Include="..\..\src\main\SDL_main_callbacks.h">
<Filter>main</Filter>
</ClInclude>
<ClInclude Include="..\..\src\main\SDL_runapp.h">
<Filter>main</Filter>
</ClInclude>
<ClInclude Include="..\..\src\SDL_error_c.h" />
<ClInclude Include="..\..\src\SDL_hashtable.h" />
<ClInclude Include="..\..\src\SDL_list.h" />
Expand Down
2 changes: 1 addition & 1 deletion docs/README-main-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
54 changes: 35 additions & 19 deletions include/SDL3/SDL_main.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
*
Expand All @@ -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
Expand Down Expand Up @@ -114,7 +117,7 @@
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_MAIN_AVAILABLE
#define SDL_MAIN_AVAILABLE 1
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, I changed these because I think the wiki does not currently pick up defines that don't have any values. For example, https://wiki.libsdl.org/SDL3/SDL_MAIN_AVAILABLE doesn't exist.


/**
* Defined if the target platform _requires_ a special mainline through SDL.
Expand All @@ -132,7 +135,7 @@
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_MAIN_NEEDED
#define SDL_MAIN_NEEDED 1

#endif

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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);

Expand All @@ -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`.
Expand All @@ -578,15 +594,15 @@ 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
* header-only library, and you should not call this directly unless you
* _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.
Expand Down
65 changes: 37 additions & 28 deletions src/core/android/SDL_android.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
}
Expand Down
31 changes: 23 additions & 8 deletions src/main/SDL_runapp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
32 changes: 32 additions & 0 deletions src/main/SDL_runapp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <[email protected]>

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_
4 changes: 3 additions & 1 deletion src/main/emscripten/SDL_sysmain_runapp.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

#include <emscripten/emscripten.h>

#include "../SDL_runapp.h"

EM_JS_DEPS(sdlrunapp, "$dynCall,$stringToNewUTF8");

int SDL_RunApp(int argc, char* argv[], SDL_main_func mainFunction, void * reserved)
Expand All @@ -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
Loading
Loading