mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-12-16 03:15:38 +00:00
Sync SDL3 wiki -> header
This commit is contained in:
@@ -1,213 +1,213 @@
|
|||||||
# Where an SDL program starts running.
|
# Where an SDL program starts running.
|
||||||
|
|
||||||
## History
|
## History
|
||||||
|
|
||||||
SDL has a long, complicated history with starting a program.
|
SDL has a long, complicated history with starting a program.
|
||||||
|
|
||||||
In most of the civilized world, an application starts in a C-callable
|
In most of the civilized world, an application starts in a C-callable
|
||||||
function named "main". You probably learned it a long time ago:
|
function named "main". You probably learned it a long time ago:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
printf("Hello world!\n");
|
printf("Hello world!\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
But not all platforms work like this. Windows apps might want a different
|
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
|
function named "WinMain", for example, so SDL set out to paper over this
|
||||||
difference.
|
difference.
|
||||||
|
|
||||||
Generally how this would work is: your app would always use the "standard"
|
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
|
`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
|
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.
|
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`,
|
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
|
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
|
provide its own entry point that called it. Your app was none the wiser and
|
||||||
your code worked everywhere without changes.
|
your code worked everywhere without changes.
|
||||||
|
|
||||||
In SDL1, you linked with a static library that had startup code that _had_ to
|
In SDL1, you linked with a static library that had startup code that _had_ to
|
||||||
run before you hit SDL_main(). For example, on macOS it would do various
|
run before you hit SDL_main(). For example, on macOS it would do various
|
||||||
magic to the process to make sure it was in the right state. Windows would
|
magic to the process to make sure it was in the right state. Windows would
|
||||||
register win32 window classes and such. Things would break if you tried to
|
register win32 window classes and such. Things would break if you tried to
|
||||||
circumvent this, and you were in for a lot of trouble if you tried to use
|
circumvent this, and you were in for a lot of trouble if you tried to use
|
||||||
SDL on a platform that needed this when you didn't control the entry point
|
SDL on a platform that needed this when you didn't control the entry point
|
||||||
(for example, as a plugin, or an SDL binding in a scripting language).
|
(for example, as a plugin, or an SDL binding in a scripting language).
|
||||||
|
|
||||||
In SDL2, the necessary support code moved into the main library, and the tiny
|
In SDL2, the necessary support code moved into the main library, and the tiny
|
||||||
static library _only_ handled the basics of getting from the platform's real
|
static library _only_ handled the basics of getting from the platform's real
|
||||||
entry point (like WinMain) to SDL_main; if the real entry was _already_
|
entry point (like WinMain) to SDL_main; if the real entry was _already_
|
||||||
standard main, the static library and macro magic was unnecessary. The goal
|
standard main, the static library and macro magic was unnecessary. The goal
|
||||||
was to make it so you didn't have to change _your_ code to work on multiple
|
was to make it so you didn't have to change _your_ code to work on multiple
|
||||||
platforms and remove the original limitations.
|
platforms and remove the original limitations.
|
||||||
|
|
||||||
In SDL3, we've taken this much, much further.
|
In SDL3, we've taken this much, much further.
|
||||||
|
|
||||||
## The main entry point in SDL3
|
## The main entry point in SDL3
|
||||||
|
|
||||||
SDL3 still has the same macro tricks, but the static library is gone. Now it's
|
SDL3 still has the same macro tricks, but the static library is gone. Now it's
|
||||||
supplied by a "single-header library," which is to say you
|
supplied by a "single-header library," which is to say you
|
||||||
`#include <SDL3/SDL_main.h>` and that header will insert a small amount of
|
`#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
|
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.
|
about linking against an extra library that you might need on some platforms.
|
||||||
You just build your app and it works.
|
You just build your app and it works.
|
||||||
|
|
||||||
You should _only_ include SDL_main.h from one file (the umbrella header,
|
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
|
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,
|
something else, so if you use this symbol elsewhere as a variable name, etc,
|
||||||
it can cause you unexpected problems.
|
it can cause you unexpected problems.
|
||||||
|
|
||||||
SDL_main.h will also include platform-specific code (WinMain or whatnot) that
|
SDL_main.h will also include platform-specific code (WinMain or whatnot) that
|
||||||
calls your _actual_ main function. This is compiled directly into your
|
calls your _actual_ main function. This is compiled directly into your
|
||||||
program.
|
program.
|
||||||
|
|
||||||
If for some reason you need to include SDL_main.h in a file but also _don't_
|
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
|
want it to generate this platform-specific code, you should define a special
|
||||||
macro before includin the header:
|
macro before includin the header:
|
||||||
|
|
||||||
|
|
||||||
```c
|
```c
|
||||||
#define SDL_MAIN_NOIMPL
|
#define SDL_MAIN_NOIMPL
|
||||||
```
|
```
|
||||||
|
|
||||||
If you are moving from SDL2, remove any references to the SDLmain static
|
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
|
library from your build system, and you should be done. Things should work as
|
||||||
they always have.
|
they always have.
|
||||||
|
|
||||||
If you have never controlled your process's entry point (you are using SDL
|
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
|
as a module from a general-purpose scripting language interpreter, or you're
|
||||||
SDL in a plugin for some otherwise-unrelated app), then there is nothing
|
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
|
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
|
is required, so using SDL_main.h is completely optional. Just start using
|
||||||
the SDL API when you are ready.
|
the SDL API when you are ready.
|
||||||
|
|
||||||
|
|
||||||
## Main callbacks in SDL3
|
## Main callbacks in SDL3
|
||||||
|
|
||||||
There is a second option in SDL3 for how to structure your program. This is
|
There is a second option in SDL3 for how to structure your program. This is
|
||||||
completly optional and you can ignore it if you're happy using a standard
|
completly optional and you can ignore it if you're happy using a standard
|
||||||
"main" function.
|
"main" function.
|
||||||
|
|
||||||
Some platforms would rather your program operate in chunks. Most of the time,
|
Some platforms would rather your program operate in chunks. Most of the time,
|
||||||
games tend to look like this at the highest level:
|
games tend to look like this at the highest level:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
initialize();
|
initialize();
|
||||||
while (keep_running()) {
|
while (keep_running()) {
|
||||||
handle_new_events();
|
handle_new_events();
|
||||||
do_one_frame_of_stuff();
|
do_one_frame_of_stuff();
|
||||||
}
|
}
|
||||||
deinitialize();
|
deinitialize();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
There are platforms that would rather be in charge of that `while` loop:
|
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
|
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
|
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.
|
(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
|
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.
|
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
|
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 transparently on
|
on different platforms, but SDL3 offers a system to handle transparently on
|
||||||
the app's behalf.
|
the app's behalf.
|
||||||
|
|
||||||
To use this, you have to redesign the highest level of your app a little. Once
|
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
|
you do, it'll work on all supported SDL platforms without problems and
|
||||||
`#ifdef`s in your code.
|
`#ifdef`s in your code.
|
||||||
|
|
||||||
Instead of providing a "main" function, under this system, you would provide
|
Instead of providing a "main" function, under this system, you would provide
|
||||||
several functions that SDL will call as appropriate.
|
several functions that SDL will call as appropriate.
|
||||||
|
|
||||||
Using the callback entry points works on every platform, because on platforms
|
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
|
that don't require them, we can fake them with a simple loop in an internal
|
||||||
implementation of the usual SDL_main.
|
implementation of the usual SDL_main.
|
||||||
|
|
||||||
The primary way we expect people to write SDL apps is still with SDL_main, and
|
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
|
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,
|
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
|
and maybe removes a barrier to entry on some future platform. And you might
|
||||||
find you enjoy structuring your program like this more!
|
find you enjoy structuring your program like this more!
|
||||||
|
|
||||||
|
|
||||||
## How to use main callbacks in SDL3
|
## How to use main callbacks in SDL3
|
||||||
|
|
||||||
To enable the callback entry points, you include SDL_main with an extra define
|
To enable the callback entry points, you include SDL_main with an extra define
|
||||||
from a single source file in your project:
|
from a single source file in your project:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
#define SDL_MAIN_USE_CALLBACKS
|
#define SDL_MAIN_USE_CALLBACKS
|
||||||
#include <SDL3/SDL_main.h>
|
#include <SDL3/SDL_main.h>
|
||||||
```
|
```
|
||||||
|
|
||||||
Once you do this, you do not include a "main" function at all (and if you do,
|
Once you do this, you do not include a "main" function at all (and if you do,
|
||||||
the app will likely fail to link). Instead, you provide the following
|
the app will likely fail to link). Instead, you provide the following
|
||||||
functions:
|
functions:
|
||||||
|
|
||||||
First:
|
First:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
int SDL_AppInit(int argc, char **argv);
|
int SDL_AppInit(int argc, char **argv);
|
||||||
```
|
```
|
||||||
|
|
||||||
This will be called _once_ before anything else. argc/argv work like they
|
This will be called _once_ before anything else. argc/argv work like they
|
||||||
always do. If this returns 0, the app runs. If it returns < 0, the app calls
|
always do. If this returns 0, the app runs. If it returns < 0, the app calls
|
||||||
SDL_AppQuit and terminates with an exit code that reports an error to the
|
SDL_AppQuit and terminates with an exit code that reports an error to the
|
||||||
platform. If it returns > 0, the app calls SDL_AppQuit and terminates with
|
platform. If it returns > 0, the app calls SDL_AppQuit and terminates with
|
||||||
an exit code that reports success to the platform. This function should not
|
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
|
go into an infinite mainloop; it should do any one-time startup it requires
|
||||||
and then return.
|
and then return.
|
||||||
|
|
||||||
Then:
|
Then:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
int SDL_AppIterate(void);
|
int SDL_AppIterate(void);
|
||||||
```
|
```
|
||||||
|
|
||||||
This is called over and over, possibly at the refresh rate of the display or
|
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
|
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
|
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
|
"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 < 0,
|
should do any game updates, and render a frame of video. If it returns < 0,
|
||||||
SDL will call SDL_AppQuit and terminate the process with an exit code that
|
SDL will call SDL_AppQuit and terminate the process with an exit code that
|
||||||
reports an error to the platform. If it returns > 0, the app calls
|
reports an error to the platform. If it returns > 0, the app calls
|
||||||
SDL_AppQuit and terminates with an exit code that reports success to the
|
SDL_AppQuit and terminates with an exit code that reports success to the
|
||||||
platform. If it returns 0, then SDL_AppIterate will be called again at some
|
platform. If it returns 0, then SDL_AppIterate will be called again at some
|
||||||
regular frequency. The platform may choose to run this more or less (perhaps
|
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
|
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
|
as fast as possible. You do not check the event queue in this function
|
||||||
(SDL_AppEvent exists for that).
|
(SDL_AppEvent exists for that).
|
||||||
|
|
||||||
Next:
|
Next:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
int SDL_AppEvent(const SDL_Event *event);
|
int SDL_AppEvent(const SDL_Event *event);
|
||||||
```
|
```
|
||||||
|
|
||||||
This will be called once for each event pushed into the SDL queue. This may be
|
This will be called once for each event pushed into the SDL queue. This may be
|
||||||
called from any thread, and possibly in parallel to SDL_AppIterate. The fields
|
called from any thread, and possibly in parallel to SDL_AppIterate. The fields
|
||||||
in `event` do not need to be free'd (as you would normally need to do for
|
in `event` do not need to be free'd (as you would normally need to do for
|
||||||
SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent,
|
SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent,
|
||||||
SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the
|
SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the
|
||||||
same as from SDL_AppIterate(), so you can terminate in response to
|
same as from SDL_AppIterate(), so you can terminate in response to
|
||||||
SDL_EVENT_QUIT, etc.
|
SDL_EVENT_QUIT, etc.
|
||||||
|
|
||||||
|
|
||||||
Finally:
|
Finally:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void SDL_AppQuit(void);
|
void SDL_AppQuit(void);
|
||||||
```
|
```
|
||||||
|
|
||||||
This is called once before terminating the app--assuming the app isn't being
|
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,
|
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
|
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
|
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.
|
from main(), so atexit handles will run, if your platform supports that.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user