mirror of
				https://github.com/libsdl-org/SDL.git
				synced 2025-11-04 01:34:38 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			237 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			237 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
# Where an SDL program starts running.
 | 
						|
 | 
						|
## History
 | 
						|
 | 
						|
SDL has a long, complicated history with starting a program.
 | 
						|
 | 
						|
In most of the civilized world, an application starts in a C-callable
 | 
						|
function named "main". You probably learned it a long time ago:
 | 
						|
 | 
						|
```c
 | 
						|
int main(int argc, char **argv)
 | 
						|
{
 | 
						|
    printf("Hello world!\n");
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
But not all platforms work like this. Windows apps might want a different
 | 
						|
function named "WinMain", for example, so SDL set out to paper over this
 | 
						|
difference.
 | 
						|
 | 
						|
Generally how this would work is: your app would always use the "standard"
 | 
						|
`main(argc, argv)` function as its entry point, and `#include` the proper
 | 
						|
SDL header before that, which did some macro magic. On platforms that used
 | 
						|
a standard `main`, it would do nothing and what you saw was what you got.
 | 
						|
 | 
						|
But those other platforms! If they needed something that _wasn't_ `main`,
 | 
						|
SDL's macro magic would quietly rename your function to `SDL_main`, and
 | 
						|
provide its own entry point that called it. Your app was none the wiser and
 | 
						|
your code worked everywhere without changes.
 | 
						|
 | 
						|
 | 
						|
## The main entry point in SDL3
 | 
						|
 | 
						|
Previous versions of SDL had a static library, SDLmain, that you would link
 | 
						|
your app against. SDL3 still has the same macro tricks, but the static library
 | 
						|
is gone. Now it's supplied by a "single-header library," which means you
 | 
						|
`#include <SDL3/SDL_main.h>` and that header will insert a small amount of
 | 
						|
code into the source file that included it, so you no longer have to worry
 | 
						|
about linking against an extra library that you might need on some platforms.
 | 
						|
You just build your app and it works.
 | 
						|
 | 
						|
You should _only_ include SDL_main.h from one file (the umbrella header,
 | 
						|
SDL.h, does _not_ include it), and know that it will `#define main` to
 | 
						|
something else, so if you use this symbol elsewhere as a variable name, etc,
 | 
						|
it can cause you unexpected problems.
 | 
						|
 | 
						|
SDL_main.h will also include platform-specific code (WinMain or whatnot) that
 | 
						|
calls your _actual_ main function. This is compiled directly into your
 | 
						|
program.
 | 
						|
 | 
						|
If for some reason you need to include SDL_main.h in a file but also _don't_
 | 
						|
want it to generate this platform-specific code, you should define a special
 | 
						|
macro before including the header:
 | 
						|
 | 
						|
 | 
						|
```c
 | 
						|
#define SDL_MAIN_NOIMPL
 | 
						|
```
 | 
						|
 | 
						|
If you are moving from SDL2, remove any references to the SDLmain static
 | 
						|
library from your build system, and you should be done. Things should work as
 | 
						|
they always have.
 | 
						|
 | 
						|
If you have never controlled your process's entry point (you are using SDL
 | 
						|
as a module from a general-purpose scripting language interpreter, or you're
 | 
						|
using SDL in a plugin for some otherwise-unrelated app), then there is nothing
 | 
						|
required of you here; there is no startup code in SDL's entry point code that
 | 
						|
is required, so using SDL_main.h is completely optional. Just start using
 | 
						|
the SDL API when you are ready.
 | 
						|
 | 
						|
 | 
						|
## Main callbacks in SDL3
 | 
						|
 | 
						|
There is a second option in SDL3 for how to structure your program. This is
 | 
						|
completely optional and you can ignore it if you're happy using a standard
 | 
						|
"main" function.
 | 
						|
 | 
						|
Some platforms would rather your program operate in chunks. Most of the time,
 | 
						|
games tend to look like this at the highest level:
 | 
						|
 | 
						|
```c
 | 
						|
int main(int argc, char **argv)
 | 
						|
{
 | 
						|
    initialize();
 | 
						|
    while (keep_running()) {
 | 
						|
        handle_new_events();
 | 
						|
        do_one_frame_of_stuff();
 | 
						|
    }
 | 
						|
    deinitialize();
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
There are platforms that would rather be in charge of that `while` loop:
 | 
						|
iOS would rather you return from main() immediately and then it will let you
 | 
						|
know that it's time to update and draw the next frame of video. Emscripten
 | 
						|
(programs that run on a web page) absolutely requires this to function at all.
 | 
						|
Video targets like Wayland can notify the app when to draw a new frame, to
 | 
						|
save battery life and cooperate with the compositor more closely.
 | 
						|
 | 
						|
In most cases, you can add special-case code to your program to deal with this
 | 
						|
on different platforms, but SDL3 offers a system to handle this transparently on
 | 
						|
the app's behalf.
 | 
						|
 | 
						|
To use this, you have to redesign the highest level of your app a little. Once
 | 
						|
you do, it'll work on all supported SDL platforms without problems and
 | 
						|
`#ifdef`s in your code.
 | 
						|
 | 
						|
Instead of providing a "main" function, under this system, you would provide
 | 
						|
several functions that SDL will call as appropriate.
 | 
						|
 | 
						|
Using the callback entry points works on every platform, because on platforms
 | 
						|
that don't require them, we can fake them with a simple loop in an internal
 | 
						|
implementation of the usual SDL_main.
 | 
						|
 | 
						|
The primary way we expect people to write SDL apps is still with SDL_main, and
 | 
						|
this is not intended to replace it. If the app chooses to use this, it just
 | 
						|
removes some platform-specific details they might have to otherwise manage,
 | 
						|
and maybe removes a barrier to entry on some future platform. And you might
 | 
						|
find you enjoy structuring your program like this more!
 | 
						|
 | 
						|
 | 
						|
## How to use main callbacks in SDL3
 | 
						|
 | 
						|
To enable the callback entry points, you include SDL_main.h with an extra define,
 | 
						|
from a single source file in your project:
 | 
						|
 | 
						|
```c
 | 
						|
#define SDL_MAIN_USE_CALLBACKS
 | 
						|
#include <SDL3/SDL_main.h>
 | 
						|
```
 | 
						|
 | 
						|
Once you do this, you do not write a "main" function at all (and if you do,
 | 
						|
the app will likely fail to link). Instead, you provide the following
 | 
						|
functions:
 | 
						|
 | 
						|
First:
 | 
						|
 | 
						|
```c
 | 
						|
SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv);
 | 
						|
```
 | 
						|
 | 
						|
This will be called _once_ before anything else. argc/argv work like they
 | 
						|
always do. If this returns SDL_APP_CONTINUE, the app runs. If it returns
 | 
						|
SDL_APP_FAILURE, the app calls SDL_AppQuit and terminates with an exit
 | 
						|
code that reports an error to the platform. If it returns SDL_APP_SUCCESS,
 | 
						|
the app calls SDL_AppQuit and terminates with an exit code that reports
 | 
						|
success to the platform. This function should not go into an infinite
 | 
						|
mainloop; it should do any one-time startup it requires and then return.
 | 
						|
 | 
						|
If you want to, you can assign a pointer to `*appstate`, and this pointer
 | 
						|
will be made available to you in later functions calls in their `appstate`
 | 
						|
parameter. This allows you to avoid global variables, but is totally
 | 
						|
optional. If you don't set this, the pointer will be NULL in later function
 | 
						|
calls.
 | 
						|
 | 
						|
 | 
						|
Then:
 | 
						|
 | 
						|
```c
 | 
						|
SDL_AppResult SDL_AppIterate(void *appstate);
 | 
						|
```
 | 
						|
 | 
						|
This is called over and over, possibly at the refresh rate of the display or
 | 
						|
some other metric that the platform dictates. This is where the heart of your
 | 
						|
app runs. It should return as quickly as reasonably possible, but it's not a
 | 
						|
"run one memcpy and that's all the time you have" sort of thing. The app
 | 
						|
should do any game updates, and render a frame of video. If it returns
 | 
						|
SDL_APP_FAILURE, SDL will call SDL_AppQuit and terminate the process with an
 | 
						|
exit code that reports an error to the platform. If it returns
 | 
						|
SDL_APP_SUCCESS, the app calls SDL_AppQuit and terminates with an exit code
 | 
						|
that reports success to the platform. If it returns SDL_APP_CONTINUE, then
 | 
						|
SDL_AppIterate will be called again at some regular frequency. The platform
 | 
						|
may choose to run this more or less (perhaps less in the background, etc),
 | 
						|
or it might just call this function in a loop as fast as possible. You do
 | 
						|
not check the  event queue in this function (SDL_AppEvent exists for that).
 | 
						|
 | 
						|
Next:
 | 
						|
 | 
						|
```c
 | 
						|
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event);
 | 
						|
```
 | 
						|
 | 
						|
This will be called whenever an SDL event arrives. Your app should not call
 | 
						|
SDL_PollEvent, SDL_PumpEvent, etc, as  SDL will manage all this for you. Return
 | 
						|
values are the same as from SDL_AppIterate(), so you can terminate in response
 | 
						|
to SDL_EVENT_QUIT, etc.
 | 
						|
 | 
						|
 | 
						|
Finally:
 | 
						|
 | 
						|
```c
 | 
						|
void SDL_AppQuit(void *appstate, SDL_AppResult result);
 | 
						|
```
 | 
						|
 | 
						|
This is called once before terminating the app--assuming the app isn't being
 | 
						|
forcibly killed or crashed--as a last chance to clean up. After this returns,
 | 
						|
SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app
 | 
						|
to call it, too). Process termination proceeds as if the app returned normally
 | 
						|
from main(), so atexit handles will run, if your platform supports that.
 | 
						|
 | 
						|
If you set `*appstate` during SDL_AppInit, this is where you should free that
 | 
						|
data, as this pointer will not be provided to your app again.
 | 
						|
 | 
						|
The SDL_AppResult value that terminated the app is provided here, in case
 | 
						|
it's useful to know if this was a successful or failing run of the app.
 | 
						|
 | 
						|
 | 
						|
## Summary and Best Practices
 | 
						|
 | 
						|
- **Always Include SDL_main.h in One Source File:** When working with SDL,
 | 
						|
  remember that SDL_main.h must only be included in one source file in your
 | 
						|
  project. Including it in multiple files will lead to conflicts and undefined
 | 
						|
  behavior.
 | 
						|
 | 
						|
- **Avoid Redefining main:** If you're using SDL's entry point system (which
 | 
						|
  renames `main` to `SDL_main`), do not define `main` yourself. SDL takes care
 | 
						|
  of this for you, and redefining it can cause issues, especially when linking
 | 
						|
  with SDL libraries.
 | 
						|
 | 
						|
- **Using SDL's Callback System:** If you're working with more complex
 | 
						|
  scenarios, such as requiring more control over your application's flow
 | 
						|
  (e.g., with games or apps that need extensive event handling), consider
 | 
						|
  using SDL's callback system. Define the necessary callbacks and SDL will
 | 
						|
  handle initialization, event processing, and cleanup automatically.
 | 
						|
 | 
						|
- **Platform-Specific Considerations:** On platforms like Windows, SDL handles
 | 
						|
  the platform-specific entry point (like `WinMain`) automatically. This means
 | 
						|
  you don't need to worry about writing platform-specific entry code when
 | 
						|
  using SDL.
 | 
						|
 | 
						|
- **When to Skip SDL_main.h:** If you do not require SDL's custom entry point
 | 
						|
  (for example, if you're integrating SDL into an existing application or a
 | 
						|
  scripting environment), you can omit SDL_main.h. However, this will limit
 | 
						|
  SDL's ability to abstract away platform-specific entry point details.
 | 
						|
 |