diff options
author | 3gg <3gg@shellblade.net> | 2025-08-30 16:53:58 -0700 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2025-08-30 16:53:58 -0700 |
commit | 6aaedb813fa11ba0679c3051bc2eb28646b9506c (patch) | |
tree | 34acbfc9840e02cb4753e6306ea7ce978bf8b58e /src/contrib/SDL-3.2.20/docs/README-main-functions.md | |
parent | 8f228ade99dd3d4c8da9b78ade1815c9adf85c8f (diff) |
Update to SDL3
Diffstat (limited to 'src/contrib/SDL-3.2.20/docs/README-main-functions.md')
-rw-r--r-- | src/contrib/SDL-3.2.20/docs/README-main-functions.md | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/src/contrib/SDL-3.2.20/docs/README-main-functions.md b/src/contrib/SDL-3.2.20/docs/README-main-functions.md new file mode 100644 index 0000000..75b9e2c --- /dev/null +++ b/src/contrib/SDL-3.2.20/docs/README-main-functions.md | |||
@@ -0,0 +1,236 @@ | |||
1 | # Where an SDL program starts running. | ||
2 | |||
3 | ## History | ||
4 | |||
5 | SDL has a long, complicated history with starting a program. | ||
6 | |||
7 | In most of the civilized world, an application starts in a C-callable | ||
8 | function named "main". You probably learned it a long time ago: | ||
9 | |||
10 | ```c | ||
11 | int main(int argc, char **argv) | ||
12 | { | ||
13 | printf("Hello world!\n"); | ||
14 | return 0; | ||
15 | } | ||
16 | ``` | ||
17 | |||
18 | But not all platforms work like this. Windows apps might want a different | ||
19 | function named "WinMain", for example, so SDL set out to paper over this | ||
20 | difference. | ||
21 | |||
22 | Generally how this would work is: your app would always use the "standard" | ||
23 | `main(argc, argv)` function as its entry point, and `#include` the proper | ||
24 | SDL header before that, which did some macro magic. On platforms that used | ||
25 | a standard `main`, it would do nothing and what you saw was what you got. | ||
26 | |||
27 | But those other platforms! If they needed something that _wasn't_ `main`, | ||
28 | SDL's macro magic would quietly rename your function to `SDL_main`, and | ||
29 | provide its own entry point that called it. Your app was none the wiser and | ||
30 | your code worked everywhere without changes. | ||
31 | |||
32 | |||
33 | ## The main entry point in SDL3 | ||
34 | |||
35 | Previous versions of SDL had a static library, SDLmain, that you would link | ||
36 | your app against. SDL3 still has the same macro tricks, but the static library | ||
37 | is gone. Now it's supplied by a "single-header library," which means you | ||
38 | `#include <SDL3/SDL_main.h>` and that header will insert a small amount of | ||
39 | code into the source file that included it, so you no longer have to worry | ||
40 | about linking against an extra library that you might need on some platforms. | ||
41 | You just build your app and it works. | ||
42 | |||
43 | You should _only_ include SDL_main.h from one file (the umbrella header, | ||
44 | SDL.h, does _not_ include it), and know that it will `#define main` to | ||
45 | something else, so if you use this symbol elsewhere as a variable name, etc, | ||
46 | it can cause you unexpected problems. | ||
47 | |||
48 | SDL_main.h will also include platform-specific code (WinMain or whatnot) that | ||
49 | calls your _actual_ main function. This is compiled directly into your | ||
50 | program. | ||
51 | |||
52 | If for some reason you need to include SDL_main.h in a file but also _don't_ | ||
53 | want it to generate this platform-specific code, you should define a special | ||
54 | macro before including the header: | ||
55 | |||
56 | |||
57 | ```c | ||
58 | #define SDL_MAIN_NOIMPL | ||
59 | ``` | ||
60 | |||
61 | If you are moving from SDL2, remove any references to the SDLmain static | ||
62 | library from your build system, and you should be done. Things should work as | ||
63 | they always have. | ||
64 | |||
65 | If you have never controlled your process's entry point (you are using SDL | ||
66 | as a module from a general-purpose scripting language interpreter, or you're | ||
67 | using SDL in a plugin for some otherwise-unrelated app), then there is nothing | ||
68 | required of you here; there is no startup code in SDL's entry point code that | ||
69 | is required, so using SDL_main.h is completely optional. Just start using | ||
70 | the SDL API when you are ready. | ||
71 | |||
72 | |||
73 | ## Main callbacks in SDL3 | ||
74 | |||
75 | There is a second option in SDL3 for how to structure your program. This is | ||
76 | completely optional and you can ignore it if you're happy using a standard | ||
77 | "main" function. | ||
78 | |||
79 | Some platforms would rather your program operate in chunks. Most of the time, | ||
80 | games tend to look like this at the highest level: | ||
81 | |||
82 | ```c | ||
83 | int main(int argc, char **argv) | ||
84 | { | ||
85 | initialize(); | ||
86 | while (keep_running()) { | ||
87 | handle_new_events(); | ||
88 | do_one_frame_of_stuff(); | ||
89 | } | ||
90 | deinitialize(); | ||
91 | } | ||
92 | ``` | ||
93 | |||
94 | There are platforms that would rather be in charge of that `while` loop: | ||
95 | iOS would rather you return from main() immediately and then it will let you | ||
96 | know that it's time to update and draw the next frame of video. Emscripten | ||
97 | (programs that run on a web page) absolutely requires this to function at all. | ||
98 | Video targets like Wayland can notify the app when to draw a new frame, to | ||
99 | save battery life and cooperate with the compositor more closely. | ||
100 | |||
101 | In most cases, you can add special-case code to your program to deal with this | ||
102 | on different platforms, but SDL3 offers a system to handle this transparently on | ||
103 | the app's behalf. | ||
104 | |||
105 | To use this, you have to redesign the highest level of your app a little. Once | ||
106 | you do, it'll work on all supported SDL platforms without problems and | ||
107 | `#ifdef`s in your code. | ||
108 | |||
109 | Instead of providing a "main" function, under this system, you would provide | ||
110 | several functions that SDL will call as appropriate. | ||
111 | |||
112 | Using the callback entry points works on every platform, because on platforms | ||
113 | that don't require them, we can fake them with a simple loop in an internal | ||
114 | implementation of the usual SDL_main. | ||
115 | |||
116 | The primary way we expect people to write SDL apps is still with SDL_main, and | ||
117 | this is not intended to replace it. If the app chooses to use this, it just | ||
118 | removes some platform-specific details they might have to otherwise manage, | ||
119 | and maybe removes a barrier to entry on some future platform. And you might | ||
120 | find you enjoy structuring your program like this more! | ||
121 | |||
122 | |||
123 | ## How to use main callbacks in SDL3 | ||
124 | |||
125 | To enable the callback entry points, you include SDL_main.h with an extra define, | ||
126 | from a single source file in your project: | ||
127 | |||
128 | ```c | ||
129 | #define SDL_MAIN_USE_CALLBACKS | ||
130 | #include <SDL3/SDL_main.h> | ||
131 | ``` | ||
132 | |||
133 | Once you do this, you do not write a "main" function at all (and if you do, | ||
134 | the app will likely fail to link). Instead, you provide the following | ||
135 | functions: | ||
136 | |||
137 | First: | ||
138 | |||
139 | ```c | ||
140 | SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv); | ||
141 | ``` | ||
142 | |||
143 | This will be called _once_ before anything else. argc/argv work like they | ||
144 | always do. If this returns SDL_APP_CONTINUE, the app runs. If it returns | ||
145 | SDL_APP_FAILURE, the app calls SDL_AppQuit and terminates with an exit | ||
146 | code that reports an error to the platform. If it returns SDL_APP_SUCCESS, | ||
147 | the app calls SDL_AppQuit and terminates with an exit code that reports | ||
148 | success to the platform. This function should not go into an infinite | ||
149 | mainloop; it should do any one-time startup it requires and then return. | ||
150 | |||
151 | If you want to, you can assign a pointer to `*appstate`, and this pointer | ||
152 | will be made available to you in later functions calls in their `appstate` | ||
153 | parameter. This allows you to avoid global variables, but is totally | ||
154 | optional. If you don't set this, the pointer will be NULL in later function | ||
155 | calls. | ||
156 | |||
157 | |||
158 | Then: | ||
159 | |||
160 | ```c | ||
161 | SDL_AppResult SDL_AppIterate(void *appstate); | ||
162 | ``` | ||
163 | |||
164 | This is called over and over, possibly at the refresh rate of the display or | ||
165 | some other metric that the platform dictates. This is where the heart of your | ||
166 | app runs. It should return as quickly as reasonably possible, but it's not a | ||
167 | "run one memcpy and that's all the time you have" sort of thing. The app | ||
168 | should do any game updates, and render a frame of video. If it returns | ||
169 | SDL_APP_FAILURE, SDL will call SDL_AppQuit and terminate the process with an | ||
170 | exit code that reports an error to the platform. If it returns | ||
171 | SDL_APP_SUCCESS, the app calls SDL_AppQuit and terminates with an exit code | ||
172 | that reports success to the platform. If it returns SDL_APP_CONTINUE, then | ||
173 | SDL_AppIterate will be called again at some regular frequency. The platform | ||
174 | may choose to run this more or less (perhaps less in the background, etc), | ||
175 | or it might just call this function in a loop as fast as possible. You do | ||
176 | not check the event queue in this function (SDL_AppEvent exists for that). | ||
177 | |||
178 | Next: | ||
179 | |||
180 | ```c | ||
181 | SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event); | ||
182 | ``` | ||
183 | |||
184 | This will be called whenever an SDL event arrives. Your app should not call | ||
185 | SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage all this for you. Return | ||
186 | values are the same as from SDL_AppIterate(), so you can terminate in response | ||
187 | to SDL_EVENT_QUIT, etc. | ||
188 | |||
189 | |||
190 | Finally: | ||
191 | |||
192 | ```c | ||
193 | void SDL_AppQuit(void *appstate, SDL_AppResult result); | ||
194 | ``` | ||
195 | |||
196 | This is called once before terminating the app--assuming the app isn't being | ||
197 | forcibly killed or crashed--as a last chance to clean up. After this returns, | ||
198 | SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app | ||
199 | to call it, too). Process termination proceeds as if the app returned normally | ||
200 | from main(), so atexit handles will run, if your platform supports that. | ||
201 | |||
202 | If you set `*appstate` during SDL_AppInit, this is where you should free that | ||
203 | data, as this pointer will not be provided to your app again. | ||
204 | |||
205 | The SDL_AppResult value that terminated the app is provided here, in case | ||
206 | it's useful to know if this was a successful or failing run of the app. | ||
207 | |||
208 | |||
209 | ## Summary and Best Practices | ||
210 | |||
211 | - **Always Include SDL_main.h in One Source File:** When working with SDL, | ||
212 | remember that SDL_main.h must only be included in one source file in your | ||
213 | project. Including it in multiple files will lead to conflicts and undefined | ||
214 | behavior. | ||
215 | |||
216 | - **Avoid Redefining main:** If you're using SDL's entry point system (which | ||
217 | renames `main` to `SDL_main`), do not define `main` yourself. SDL takes care | ||
218 | of this for you, and redefining it can cause issues, especially when linking | ||
219 | with SDL libraries. | ||
220 | |||
221 | - **Using SDL's Callback System:** If you're working with more complex | ||
222 | scenarios, such as requiring more control over your application's flow | ||
223 | (e.g., with games or apps that need extensive event handling), consider | ||
224 | using SDL's callback system. Define the necessary callbacks and SDL will | ||
225 | handle initialization, event processing, and cleanup automatically. | ||
226 | |||
227 | - **Platform-Specific Considerations:** On platforms like Windows, SDL handles | ||
228 | the platform-specific entry point (like `WinMain`) automatically. This means | ||
229 | you don't need to worry about writing platform-specific entry code when | ||
230 | using SDL. | ||
231 | |||
232 | - **When to Skip SDL_main.h:** If you do not require SDL's custom entry point | ||
233 | (for example, if you're integrating SDL into an existing application or a | ||
234 | scripting environment), you can omit SDL_main.h. However, this will limit | ||
235 | SDL's ability to abstract away platform-specific entry point details. | ||
236 | |||