1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org> |
4 | |
5 | This software is provided 'as-is', without any express or implied |
6 | warranty. In no event will the authors be held liable for any damages |
7 | arising from the use of this software. |
8 | |
9 | Permission is granted to anyone to use this software for any purpose, |
10 | including commercial applications, and to alter it and redistribute it |
11 | freely, subject to the following restrictions: |
12 | |
13 | 1. The origin of this software must not be misrepresented; you must not |
14 | claim that you wrote the original software. If you use this software |
15 | in a product, an acknowledgment in the product documentation would be |
16 | appreciated but is not required. |
17 | 2. Altered source versions must be plainly marked as such, and must not be |
18 | misrepresented as being the original software. |
19 | 3. This notice may not be removed or altered from any source distribution. |
20 | */ |
21 | #include "SDL_internal.h" |
22 | #include "SDL3/SDL_revision.h" |
23 | |
24 | #if defined(SDL_PLATFORM_WINDOWS) |
25 | #include "core/windows/SDL_windows.h" |
26 | #else |
27 | #include <unistd.h> // _exit(), etc. |
28 | #endif |
29 | |
30 | // this checks for HAVE_DBUS_DBUS_H internally. |
31 | #include "core/linux/SDL_dbus.h" |
32 | |
33 | #ifdef SDL_PLATFORM_EMSCRIPTEN |
34 | #include <emscripten.h> |
35 | #endif |
36 | |
37 | // Initialization code for SDL |
38 | |
39 | #include "SDL_assert_c.h" |
40 | #include "SDL_hints_c.h" |
41 | #include "SDL_log_c.h" |
42 | #include "SDL_properties_c.h" |
43 | #include "audio/SDL_sysaudio.h" |
44 | #include "camera/SDL_camera_c.h" |
45 | #include "cpuinfo/SDL_cpuinfo_c.h" |
46 | #include "events/SDL_events_c.h" |
47 | #include "haptic/SDL_haptic_c.h" |
48 | #include "joystick/SDL_gamepad_c.h" |
49 | #include "joystick/SDL_joystick_c.h" |
50 | #include "render/SDL_sysrender.h" |
51 | #include "sensor/SDL_sensor_c.h" |
52 | #include "stdlib/SDL_getenv_c.h" |
53 | #include "thread/SDL_thread_c.h" |
54 | #include "tray/SDL_tray_utils.h" |
55 | #include "video/SDL_pixels_c.h" |
56 | #include "video/SDL_surface_c.h" |
57 | #include "video/SDL_video_c.h" |
58 | #include "filesystem/SDL_filesystem_c.h" |
59 | #include "io/SDL_asyncio_c.h" |
60 | #ifdef SDL_PLATFORM_ANDROID |
61 | #include "core/android/SDL_android.h" |
62 | #endif |
63 | |
64 | #define SDL_INIT_EVERYTHING ~0U |
65 | |
66 | // Initialization/Cleanup routines |
67 | #include "timer/SDL_timer_c.h" |
68 | #ifdef SDL_VIDEO_DRIVER_WINDOWS |
69 | extern bool SDL_HelperWindowCreate(void); |
70 | extern void SDL_HelperWindowDestroy(void); |
71 | #endif |
72 | |
73 | #ifdef SDL_BUILD_MAJOR_VERSION |
74 | SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MAJOR_VERSION, |
75 | SDL_MAJOR_VERSION == SDL_BUILD_MAJOR_VERSION); |
76 | SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MINOR_VERSION, |
77 | SDL_MINOR_VERSION == SDL_BUILD_MINOR_VERSION); |
78 | SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MICRO_VERSION, |
79 | SDL_MICRO_VERSION == SDL_BUILD_MICRO_VERSION); |
80 | #endif |
81 | |
82 | // Limited by its encoding in SDL_VERSIONNUM |
83 | SDL_COMPILE_TIME_ASSERT(SDL_MAJOR_VERSION_min, SDL_MAJOR_VERSION >= 0); |
84 | SDL_COMPILE_TIME_ASSERT(SDL_MAJOR_VERSION_max, SDL_MAJOR_VERSION <= 10); |
85 | SDL_COMPILE_TIME_ASSERT(SDL_MINOR_VERSION_min, SDL_MINOR_VERSION >= 0); |
86 | SDL_COMPILE_TIME_ASSERT(SDL_MINOR_VERSION_max, SDL_MINOR_VERSION <= 999); |
87 | SDL_COMPILE_TIME_ASSERT(SDL_MICRO_VERSION_min, SDL_MICRO_VERSION >= 0); |
88 | SDL_COMPILE_TIME_ASSERT(SDL_MICRO_VERSION_max, SDL_MICRO_VERSION <= 999); |
89 | |
90 | /* This is not declared in any header, although it is shared between some |
91 | parts of SDL, because we don't want anything calling it without an |
92 | extremely good reason. */ |
93 | extern SDL_NORETURN void SDL_ExitProcess(int exitcode); |
94 | SDL_NORETURN void SDL_ExitProcess(int exitcode) |
95 | { |
96 | #if defined(SDL_PLATFORM_WINDOWS) |
97 | /* "if you do not know the state of all threads in your process, it is |
98 | better to call TerminateProcess than ExitProcess" |
99 | https://msdn.microsoft.com/en-us/library/windows/desktop/ms682658(v=vs.85).aspx */ |
100 | TerminateProcess(GetCurrentProcess(), exitcode); |
101 | /* MingW doesn't have TerminateProcess marked as noreturn, so add an |
102 | ExitProcess here that will never be reached but make MingW happy. */ |
103 | ExitProcess(exitcode); |
104 | #elif defined(SDL_PLATFORM_EMSCRIPTEN) |
105 | emscripten_cancel_main_loop(); // this should "kill" the app. |
106 | emscripten_force_exit(exitcode); // this should "kill" the app. |
107 | exit(exitcode); |
108 | #elif defined(SDL_PLATFORM_HAIKU) // Haiku has _Exit, but it's not marked noreturn. |
109 | _exit(exitcode); |
110 | #elif defined(HAVE__EXIT) // Upper case _Exit() |
111 | _Exit(exitcode); |
112 | #else |
113 | _exit(exitcode); |
114 | #endif |
115 | } |
116 | |
117 | // App metadata |
118 | |
119 | bool SDL_SetAppMetadata(const char *appname, const char *appversion, const char *appidentifier) |
120 | { |
121 | SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING, appname); |
122 | SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_VERSION_STRING, appversion); |
123 | SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_IDENTIFIER_STRING, appidentifier); |
124 | return true; |
125 | } |
126 | |
127 | static bool SDL_ValidMetadataProperty(const char *name) |
128 | { |
129 | if (!name || !*name) { |
130 | return false; |
131 | } |
132 | |
133 | if (SDL_strcmp(name, SDL_PROP_APP_METADATA_NAME_STRING) == 0 || |
134 | SDL_strcmp(name, SDL_PROP_APP_METADATA_VERSION_STRING) == 0 || |
135 | SDL_strcmp(name, SDL_PROP_APP_METADATA_IDENTIFIER_STRING) == 0 || |
136 | SDL_strcmp(name, SDL_PROP_APP_METADATA_CREATOR_STRING) == 0 || |
137 | SDL_strcmp(name, SDL_PROP_APP_METADATA_COPYRIGHT_STRING) == 0 || |
138 | SDL_strcmp(name, SDL_PROP_APP_METADATA_URL_STRING) == 0 || |
139 | SDL_strcmp(name, SDL_PROP_APP_METADATA_TYPE_STRING) == 0) { |
140 | return true; |
141 | } |
142 | return false; |
143 | } |
144 | |
145 | bool SDL_SetAppMetadataProperty(const char *name, const char *value) |
146 | { |
147 | if (!SDL_ValidMetadataProperty(name)) { |
148 | return SDL_InvalidParamError("name" ); |
149 | } |
150 | |
151 | return SDL_SetStringProperty(SDL_GetGlobalProperties(), name, value); |
152 | } |
153 | |
154 | const char *SDL_GetAppMetadataProperty(const char *name) |
155 | { |
156 | if (!SDL_ValidMetadataProperty(name)) { |
157 | SDL_InvalidParamError("name" ); |
158 | return NULL; |
159 | } |
160 | |
161 | const char *value = NULL; |
162 | if (SDL_strcmp(name, SDL_PROP_APP_METADATA_NAME_STRING) == 0) { |
163 | value = SDL_GetHint(SDL_HINT_APP_NAME); |
164 | } else if (SDL_strcmp(name, SDL_PROP_APP_METADATA_IDENTIFIER_STRING) == 0) { |
165 | value = SDL_GetHint(SDL_HINT_APP_ID); |
166 | } |
167 | if (!value || !*value) { |
168 | value = SDL_GetStringProperty(SDL_GetGlobalProperties(), name, NULL); |
169 | } |
170 | if (!value || !*value) { |
171 | if (SDL_strcmp(name, SDL_PROP_APP_METADATA_NAME_STRING) == 0) { |
172 | value = "SDL Application" ; |
173 | } else if (SDL_strcmp(name, SDL_PROP_APP_METADATA_TYPE_STRING) == 0) { |
174 | value = "application" ; |
175 | } |
176 | } |
177 | return value; |
178 | } |
179 | |
180 | |
181 | // The initialized subsystems |
182 | #ifdef SDL_MAIN_NEEDED |
183 | static bool SDL_MainIsReady = false; |
184 | #else |
185 | static bool SDL_MainIsReady = true; |
186 | #endif |
187 | static SDL_ThreadID SDL_MainThreadID = 0; |
188 | static bool SDL_bInMainQuit = false; |
189 | static Uint8 SDL_SubsystemRefCount[32]; |
190 | |
191 | // Private helper to increment a subsystem's ref counter. |
192 | static void SDL_IncrementSubsystemRefCount(Uint32 subsystem) |
193 | { |
194 | const int subsystem_index = SDL_MostSignificantBitIndex32(subsystem); |
195 | SDL_assert((subsystem_index < 0) || (SDL_SubsystemRefCount[subsystem_index] < 255)); |
196 | if (subsystem_index >= 0) { |
197 | ++SDL_SubsystemRefCount[subsystem_index]; |
198 | } |
199 | } |
200 | |
201 | // Private helper to decrement a subsystem's ref counter. |
202 | static void SDL_DecrementSubsystemRefCount(Uint32 subsystem) |
203 | { |
204 | const int subsystem_index = SDL_MostSignificantBitIndex32(subsystem); |
205 | if ((subsystem_index >= 0) && (SDL_SubsystemRefCount[subsystem_index] > 0)) { |
206 | if (SDL_bInMainQuit) { |
207 | SDL_SubsystemRefCount[subsystem_index] = 0; |
208 | } else { |
209 | --SDL_SubsystemRefCount[subsystem_index]; |
210 | } |
211 | } |
212 | } |
213 | |
214 | // Private helper to check if a system needs init. |
215 | static bool SDL_ShouldInitSubsystem(Uint32 subsystem) |
216 | { |
217 | const int subsystem_index = SDL_MostSignificantBitIndex32(subsystem); |
218 | SDL_assert((subsystem_index < 0) || (SDL_SubsystemRefCount[subsystem_index] < 255)); |
219 | return ((subsystem_index >= 0) && (SDL_SubsystemRefCount[subsystem_index] == 0)); |
220 | } |
221 | |
222 | // Private helper to check if a system needs to be quit. |
223 | static bool SDL_ShouldQuitSubsystem(Uint32 subsystem) |
224 | { |
225 | const int subsystem_index = SDL_MostSignificantBitIndex32(subsystem); |
226 | if ((subsystem_index >= 0) && (SDL_SubsystemRefCount[subsystem_index] == 0)) { |
227 | return false; |
228 | } |
229 | |
230 | /* If we're in SDL_Quit, we shut down every subsystem, even if refcount |
231 | * isn't zero. |
232 | */ |
233 | return (((subsystem_index >= 0) && (SDL_SubsystemRefCount[subsystem_index] == 1)) || SDL_bInMainQuit); |
234 | } |
235 | |
236 | /* Private helper to either increment's existing ref counter, |
237 | * or fully init a new subsystem. */ |
238 | static bool SDL_InitOrIncrementSubsystem(Uint32 subsystem) |
239 | { |
240 | int subsystem_index = SDL_MostSignificantBitIndex32(subsystem); |
241 | SDL_assert((subsystem_index < 0) || (SDL_SubsystemRefCount[subsystem_index] < 255)); |
242 | if (subsystem_index < 0) { |
243 | return false; |
244 | } |
245 | if (SDL_SubsystemRefCount[subsystem_index] > 0) { |
246 | ++SDL_SubsystemRefCount[subsystem_index]; |
247 | return true; |
248 | } |
249 | return SDL_InitSubSystem(subsystem); |
250 | } |
251 | |
252 | void SDL_SetMainReady(void) |
253 | { |
254 | SDL_MainIsReady = true; |
255 | |
256 | if (SDL_MainThreadID == 0) { |
257 | SDL_MainThreadID = SDL_GetCurrentThreadID(); |
258 | } |
259 | } |
260 | |
261 | bool SDL_IsMainThread(void) |
262 | { |
263 | if (SDL_MainThreadID == 0) { |
264 | // Not initialized yet? |
265 | return true; |
266 | } |
267 | if (SDL_MainThreadID == SDL_GetCurrentThreadID()) { |
268 | return true; |
269 | } |
270 | return false; |
271 | } |
272 | |
273 | // Initialize all the subsystems that require initialization before threads start |
274 | void SDL_InitMainThread(void) |
275 | { |
276 | static bool done_info = false; |
277 | |
278 | SDL_InitTLSData(); |
279 | SDL_InitEnvironment(); |
280 | SDL_InitTicks(); |
281 | SDL_InitFilesystem(); |
282 | |
283 | if (!done_info) { |
284 | const char *value; |
285 | |
286 | value = SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING); |
287 | SDL_LogInfo(SDL_LOG_CATEGORY_SYSTEM, "App name: %s" , value ? value : "<unspecified>" ); |
288 | value = SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_VERSION_STRING); |
289 | SDL_LogInfo(SDL_LOG_CATEGORY_SYSTEM, "App version: %s" , value ? value : "<unspecified>" ); |
290 | value = SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_IDENTIFIER_STRING); |
291 | SDL_LogInfo(SDL_LOG_CATEGORY_SYSTEM, "App ID: %s" , value ? value : "<unspecified>" ); |
292 | SDL_LogInfo(SDL_LOG_CATEGORY_SYSTEM, "SDL revision: %s" , SDL_REVISION); |
293 | |
294 | done_info = true; |
295 | } |
296 | } |
297 | |
298 | static void SDL_QuitMainThread(void) |
299 | { |
300 | SDL_QuitFilesystem(); |
301 | SDL_QuitTicks(); |
302 | SDL_QuitEnvironment(); |
303 | SDL_QuitTLSData(); |
304 | } |
305 | |
306 | bool SDL_InitSubSystem(SDL_InitFlags flags) |
307 | { |
308 | Uint32 flags_initialized = 0; |
309 | |
310 | if (!SDL_MainIsReady) { |
311 | return SDL_SetError("Application didn't initialize properly, did you include SDL_main.h in the file containing your main() function?" ); |
312 | } |
313 | |
314 | SDL_InitMainThread(); |
315 | |
316 | #ifdef SDL_USE_LIBDBUS |
317 | SDL_DBus_Init(); |
318 | #endif |
319 | |
320 | #ifdef SDL_VIDEO_DRIVER_WINDOWS |
321 | if (flags & (SDL_INIT_HAPTIC | SDL_INIT_JOYSTICK)) { |
322 | if (!SDL_HelperWindowCreate()) { |
323 | goto quit_and_error; |
324 | } |
325 | } |
326 | #endif |
327 | |
328 | // Initialize the event subsystem |
329 | if (flags & SDL_INIT_EVENTS) { |
330 | if (SDL_ShouldInitSubsystem(SDL_INIT_EVENTS)) { |
331 | SDL_IncrementSubsystemRefCount(SDL_INIT_EVENTS); |
332 | if (!SDL_InitEvents()) { |
333 | SDL_DecrementSubsystemRefCount(SDL_INIT_EVENTS); |
334 | goto quit_and_error; |
335 | } |
336 | } else { |
337 | SDL_IncrementSubsystemRefCount(SDL_INIT_EVENTS); |
338 | } |
339 | flags_initialized |= SDL_INIT_EVENTS; |
340 | } |
341 | |
342 | // Initialize the video subsystem |
343 | if (flags & SDL_INIT_VIDEO) { |
344 | #ifndef SDL_VIDEO_DISABLED |
345 | if (SDL_ShouldInitSubsystem(SDL_INIT_VIDEO)) { |
346 | // video implies events |
347 | if (!SDL_InitOrIncrementSubsystem(SDL_INIT_EVENTS)) { |
348 | goto quit_and_error; |
349 | } |
350 | |
351 | // We initialize video on the main thread |
352 | // On Apple platforms this is a requirement. |
353 | // On other platforms, this is the definition. |
354 | SDL_MainThreadID = SDL_GetCurrentThreadID(); |
355 | |
356 | SDL_IncrementSubsystemRefCount(SDL_INIT_VIDEO); |
357 | if (!SDL_VideoInit(NULL)) { |
358 | SDL_DecrementSubsystemRefCount(SDL_INIT_VIDEO); |
359 | SDL_PushError(); |
360 | SDL_QuitSubSystem(SDL_INIT_EVENTS); |
361 | SDL_PopError(); |
362 | goto quit_and_error; |
363 | } |
364 | } else { |
365 | SDL_IncrementSubsystemRefCount(SDL_INIT_VIDEO); |
366 | } |
367 | flags_initialized |= SDL_INIT_VIDEO; |
368 | #else |
369 | SDL_SetError("SDL not built with video support" ); |
370 | goto quit_and_error; |
371 | #endif |
372 | } |
373 | |
374 | // Initialize the audio subsystem |
375 | if (flags & SDL_INIT_AUDIO) { |
376 | #ifndef SDL_AUDIO_DISABLED |
377 | if (SDL_ShouldInitSubsystem(SDL_INIT_AUDIO)) { |
378 | // audio implies events |
379 | if (!SDL_InitOrIncrementSubsystem(SDL_INIT_EVENTS)) { |
380 | goto quit_and_error; |
381 | } |
382 | |
383 | SDL_IncrementSubsystemRefCount(SDL_INIT_AUDIO); |
384 | if (!SDL_InitAudio(NULL)) { |
385 | SDL_DecrementSubsystemRefCount(SDL_INIT_AUDIO); |
386 | SDL_PushError(); |
387 | SDL_QuitSubSystem(SDL_INIT_EVENTS); |
388 | SDL_PopError(); |
389 | goto quit_and_error; |
390 | } |
391 | } else { |
392 | SDL_IncrementSubsystemRefCount(SDL_INIT_AUDIO); |
393 | } |
394 | flags_initialized |= SDL_INIT_AUDIO; |
395 | #else |
396 | SDL_SetError("SDL not built with audio support" ); |
397 | goto quit_and_error; |
398 | #endif |
399 | } |
400 | |
401 | // Initialize the joystick subsystem |
402 | if (flags & SDL_INIT_JOYSTICK) { |
403 | #ifndef SDL_JOYSTICK_DISABLED |
404 | if (SDL_ShouldInitSubsystem(SDL_INIT_JOYSTICK)) { |
405 | // joystick implies events |
406 | if (!SDL_InitOrIncrementSubsystem(SDL_INIT_EVENTS)) { |
407 | goto quit_and_error; |
408 | } |
409 | |
410 | SDL_IncrementSubsystemRefCount(SDL_INIT_JOYSTICK); |
411 | if (!SDL_InitJoysticks()) { |
412 | SDL_DecrementSubsystemRefCount(SDL_INIT_JOYSTICK); |
413 | SDL_PushError(); |
414 | SDL_QuitSubSystem(SDL_INIT_EVENTS); |
415 | SDL_PopError(); |
416 | goto quit_and_error; |
417 | } |
418 | } else { |
419 | SDL_IncrementSubsystemRefCount(SDL_INIT_JOYSTICK); |
420 | } |
421 | flags_initialized |= SDL_INIT_JOYSTICK; |
422 | #else |
423 | SDL_SetError("SDL not built with joystick support" ); |
424 | goto quit_and_error; |
425 | #endif |
426 | } |
427 | |
428 | if (flags & SDL_INIT_GAMEPAD) { |
429 | #ifndef SDL_JOYSTICK_DISABLED |
430 | if (SDL_ShouldInitSubsystem(SDL_INIT_GAMEPAD)) { |
431 | // game controller implies joystick |
432 | if (!SDL_InitOrIncrementSubsystem(SDL_INIT_JOYSTICK)) { |
433 | goto quit_and_error; |
434 | } |
435 | |
436 | SDL_IncrementSubsystemRefCount(SDL_INIT_GAMEPAD); |
437 | if (!SDL_InitGamepads()) { |
438 | SDL_DecrementSubsystemRefCount(SDL_INIT_GAMEPAD); |
439 | SDL_PushError(); |
440 | SDL_QuitSubSystem(SDL_INIT_JOYSTICK); |
441 | SDL_PopError(); |
442 | goto quit_and_error; |
443 | } |
444 | } else { |
445 | SDL_IncrementSubsystemRefCount(SDL_INIT_GAMEPAD); |
446 | } |
447 | flags_initialized |= SDL_INIT_GAMEPAD; |
448 | #else |
449 | SDL_SetError("SDL not built with joystick support" ); |
450 | goto quit_and_error; |
451 | #endif |
452 | } |
453 | |
454 | // Initialize the haptic subsystem |
455 | if (flags & SDL_INIT_HAPTIC) { |
456 | #ifndef SDL_HAPTIC_DISABLED |
457 | if (SDL_ShouldInitSubsystem(SDL_INIT_HAPTIC)) { |
458 | SDL_IncrementSubsystemRefCount(SDL_INIT_HAPTIC); |
459 | if (!SDL_InitHaptics()) { |
460 | SDL_DecrementSubsystemRefCount(SDL_INIT_HAPTIC); |
461 | goto quit_and_error; |
462 | } |
463 | } else { |
464 | SDL_IncrementSubsystemRefCount(SDL_INIT_HAPTIC); |
465 | } |
466 | flags_initialized |= SDL_INIT_HAPTIC; |
467 | #else |
468 | SDL_SetError("SDL not built with haptic (force feedback) support" ); |
469 | goto quit_and_error; |
470 | #endif |
471 | } |
472 | |
473 | // Initialize the sensor subsystem |
474 | if (flags & SDL_INIT_SENSOR) { |
475 | #ifndef SDL_SENSOR_DISABLED |
476 | if (SDL_ShouldInitSubsystem(SDL_INIT_SENSOR)) { |
477 | SDL_IncrementSubsystemRefCount(SDL_INIT_SENSOR); |
478 | if (!SDL_InitSensors()) { |
479 | SDL_DecrementSubsystemRefCount(SDL_INIT_SENSOR); |
480 | goto quit_and_error; |
481 | } |
482 | } else { |
483 | SDL_IncrementSubsystemRefCount(SDL_INIT_SENSOR); |
484 | } |
485 | flags_initialized |= SDL_INIT_SENSOR; |
486 | #else |
487 | SDL_SetError("SDL not built with sensor support" ); |
488 | goto quit_and_error; |
489 | #endif |
490 | } |
491 | |
492 | // Initialize the camera subsystem |
493 | if (flags & SDL_INIT_CAMERA) { |
494 | #ifndef SDL_CAMERA_DISABLED |
495 | if (SDL_ShouldInitSubsystem(SDL_INIT_CAMERA)) { |
496 | // camera implies events |
497 | if (!SDL_InitOrIncrementSubsystem(SDL_INIT_EVENTS)) { |
498 | goto quit_and_error; |
499 | } |
500 | |
501 | SDL_IncrementSubsystemRefCount(SDL_INIT_CAMERA); |
502 | if (!SDL_CameraInit(NULL)) { |
503 | SDL_DecrementSubsystemRefCount(SDL_INIT_CAMERA); |
504 | SDL_PushError(); |
505 | SDL_QuitSubSystem(SDL_INIT_EVENTS); |
506 | SDL_PopError(); |
507 | goto quit_and_error; |
508 | } |
509 | } else { |
510 | SDL_IncrementSubsystemRefCount(SDL_INIT_CAMERA); |
511 | } |
512 | flags_initialized |= SDL_INIT_CAMERA; |
513 | #else |
514 | SDL_SetError("SDL not built with camera support" ); |
515 | goto quit_and_error; |
516 | #endif |
517 | } |
518 | |
519 | (void)flags_initialized; // make static analysis happy, since this only gets used in error cases. |
520 | |
521 | return SDL_ClearError(); |
522 | |
523 | quit_and_error: |
524 | { |
525 | SDL_PushError(); |
526 | SDL_QuitSubSystem(flags_initialized); |
527 | SDL_PopError(); |
528 | } |
529 | return false; |
530 | } |
531 | |
532 | bool SDL_Init(SDL_InitFlags flags) |
533 | { |
534 | return SDL_InitSubSystem(flags); |
535 | } |
536 | |
537 | void SDL_QuitSubSystem(SDL_InitFlags flags) |
538 | { |
539 | // Shut down requested initialized subsystems |
540 | |
541 | #ifndef SDL_CAMERA_DISABLED |
542 | if (flags & SDL_INIT_CAMERA) { |
543 | if (SDL_ShouldQuitSubsystem(SDL_INIT_CAMERA)) { |
544 | SDL_QuitCamera(); |
545 | // camera implies events |
546 | SDL_QuitSubSystem(SDL_INIT_EVENTS); |
547 | } |
548 | SDL_DecrementSubsystemRefCount(SDL_INIT_CAMERA); |
549 | } |
550 | #endif |
551 | |
552 | #ifndef SDL_SENSOR_DISABLED |
553 | if (flags & SDL_INIT_SENSOR) { |
554 | if (SDL_ShouldQuitSubsystem(SDL_INIT_SENSOR)) { |
555 | SDL_QuitSensors(); |
556 | } |
557 | SDL_DecrementSubsystemRefCount(SDL_INIT_SENSOR); |
558 | } |
559 | #endif |
560 | |
561 | #ifndef SDL_JOYSTICK_DISABLED |
562 | if (flags & SDL_INIT_GAMEPAD) { |
563 | if (SDL_ShouldQuitSubsystem(SDL_INIT_GAMEPAD)) { |
564 | SDL_QuitGamepads(); |
565 | // game controller implies joystick |
566 | SDL_QuitSubSystem(SDL_INIT_JOYSTICK); |
567 | } |
568 | SDL_DecrementSubsystemRefCount(SDL_INIT_GAMEPAD); |
569 | } |
570 | |
571 | if (flags & SDL_INIT_JOYSTICK) { |
572 | if (SDL_ShouldQuitSubsystem(SDL_INIT_JOYSTICK)) { |
573 | SDL_QuitJoysticks(); |
574 | // joystick implies events |
575 | SDL_QuitSubSystem(SDL_INIT_EVENTS); |
576 | } |
577 | SDL_DecrementSubsystemRefCount(SDL_INIT_JOYSTICK); |
578 | } |
579 | #endif |
580 | |
581 | #ifndef SDL_HAPTIC_DISABLED |
582 | if (flags & SDL_INIT_HAPTIC) { |
583 | if (SDL_ShouldQuitSubsystem(SDL_INIT_HAPTIC)) { |
584 | SDL_QuitHaptics(); |
585 | } |
586 | SDL_DecrementSubsystemRefCount(SDL_INIT_HAPTIC); |
587 | } |
588 | #endif |
589 | |
590 | #ifndef SDL_AUDIO_DISABLED |
591 | if (flags & SDL_INIT_AUDIO) { |
592 | if (SDL_ShouldQuitSubsystem(SDL_INIT_AUDIO)) { |
593 | SDL_QuitAudio(); |
594 | // audio implies events |
595 | SDL_QuitSubSystem(SDL_INIT_EVENTS); |
596 | } |
597 | SDL_DecrementSubsystemRefCount(SDL_INIT_AUDIO); |
598 | } |
599 | #endif |
600 | |
601 | #ifndef SDL_VIDEO_DISABLED |
602 | if (flags & SDL_INIT_VIDEO) { |
603 | if (SDL_ShouldQuitSubsystem(SDL_INIT_VIDEO)) { |
604 | SDL_QuitRender(); |
605 | SDL_VideoQuit(); |
606 | // video implies events |
607 | SDL_QuitSubSystem(SDL_INIT_EVENTS); |
608 | } |
609 | SDL_DecrementSubsystemRefCount(SDL_INIT_VIDEO); |
610 | } |
611 | #endif |
612 | |
613 | if (flags & SDL_INIT_EVENTS) { |
614 | if (SDL_ShouldQuitSubsystem(SDL_INIT_EVENTS)) { |
615 | SDL_QuitEvents(); |
616 | } |
617 | SDL_DecrementSubsystemRefCount(SDL_INIT_EVENTS); |
618 | } |
619 | } |
620 | |
621 | Uint32 SDL_WasInit(SDL_InitFlags flags) |
622 | { |
623 | int i; |
624 | int num_subsystems = SDL_arraysize(SDL_SubsystemRefCount); |
625 | Uint32 initialized = 0; |
626 | |
627 | // Fast path for checking one flag |
628 | if (SDL_HasExactlyOneBitSet32(flags)) { |
629 | int subsystem_index = SDL_MostSignificantBitIndex32(flags); |
630 | return SDL_SubsystemRefCount[subsystem_index] ? flags : 0; |
631 | } |
632 | |
633 | if (!flags) { |
634 | flags = SDL_INIT_EVERYTHING; |
635 | } |
636 | |
637 | num_subsystems = SDL_min(num_subsystems, SDL_MostSignificantBitIndex32(flags) + 1); |
638 | |
639 | // Iterate over each bit in flags, and check the matching subsystem. |
640 | for (i = 0; i < num_subsystems; ++i) { |
641 | if ((flags & 1) && SDL_SubsystemRefCount[i] > 0) { |
642 | initialized |= (1 << i); |
643 | } |
644 | |
645 | flags >>= 1; |
646 | } |
647 | |
648 | return initialized; |
649 | } |
650 | |
651 | void SDL_Quit(void) |
652 | { |
653 | SDL_bInMainQuit = true; |
654 | |
655 | // Quit all subsystems |
656 | #ifdef SDL_VIDEO_DRIVER_WINDOWS |
657 | SDL_HelperWindowDestroy(); |
658 | #endif |
659 | SDL_QuitSubSystem(SDL_INIT_EVERYTHING); |
660 | SDL_CleanupTrays(); |
661 | |
662 | #ifdef SDL_USE_LIBDBUS |
663 | SDL_DBus_Quit(); |
664 | #endif |
665 | |
666 | SDL_QuitTimers(); |
667 | SDL_QuitAsyncIO(); |
668 | |
669 | SDL_SetObjectsInvalid(); |
670 | SDL_AssertionsQuit(); |
671 | |
672 | SDL_QuitPixelFormatDetails(); |
673 | |
674 | SDL_QuitCPUInfo(); |
675 | |
676 | /* Now that every subsystem has been quit, we reset the subsystem refcount |
677 | * and the list of initialized subsystems. |
678 | */ |
679 | SDL_memset(SDL_SubsystemRefCount, 0x0, sizeof(SDL_SubsystemRefCount)); |
680 | |
681 | SDL_QuitLog(); |
682 | SDL_QuitHints(); |
683 | SDL_QuitProperties(); |
684 | |
685 | SDL_QuitMainThread(); |
686 | |
687 | SDL_bInMainQuit = false; |
688 | } |
689 | |
690 | // Get the library version number |
691 | int SDL_GetVersion(void) |
692 | { |
693 | return SDL_VERSION; |
694 | } |
695 | |
696 | // Get the library source revision |
697 | const char *SDL_GetRevision(void) |
698 | { |
699 | return SDL_REVISION; |
700 | } |
701 | |
702 | // Get the name of the platform |
703 | const char *SDL_GetPlatform(void) |
704 | { |
705 | #if defined(SDL_PLATFORM_PRIVATE) |
706 | return SDL_PLATFORM_PRIVATE_NAME; |
707 | #elif defined(SDL_PLATFORM_AIX) |
708 | return "AIX" ; |
709 | #elif defined(SDL_PLATFORM_ANDROID) |
710 | return "Android" ; |
711 | #elif defined(SDL_PLATFORM_BSDI) |
712 | return "BSDI" ; |
713 | #elif defined(SDL_PLATFORM_EMSCRIPTEN) |
714 | return "Emscripten" ; |
715 | #elif defined(SDL_PLATFORM_FREEBSD) |
716 | return "FreeBSD" ; |
717 | #elif defined(SDL_PLATFORM_HAIKU) |
718 | return "Haiku" ; |
719 | #elif defined(SDL_PLATFORM_HPUX) |
720 | return "HP-UX" ; |
721 | #elif defined(SDL_PLATFORM_IRIX) |
722 | return "Irix" ; |
723 | #elif defined(SDL_PLATFORM_LINUX) |
724 | return "Linux" ; |
725 | #elif defined(__MINT__) |
726 | return "Atari MiNT" ; |
727 | #elif defined(SDL_PLATFORM_MACOS) |
728 | return "macOS" ; |
729 | #elif defined(SDL_PLATFORM_NETBSD) |
730 | return "NetBSD" ; |
731 | #elif defined(SDL_PLATFORM_OPENBSD) |
732 | return "OpenBSD" ; |
733 | #elif defined(SDL_PLATFORM_OS2) |
734 | return "OS/2" ; |
735 | #elif defined(SDL_PLATFORM_OSF) |
736 | return "OSF/1" ; |
737 | #elif defined(SDL_PLATFORM_QNXNTO) |
738 | return "QNX Neutrino" ; |
739 | #elif defined(SDL_PLATFORM_RISCOS) |
740 | return "RISC OS" ; |
741 | #elif defined(SDL_PLATFORM_SOLARIS) |
742 | return "Solaris" ; |
743 | #elif defined(SDL_PLATFORM_WIN32) |
744 | return "Windows" ; |
745 | #elif defined(SDL_PLATFORM_WINGDK) |
746 | return "WinGDK" ; |
747 | #elif defined(SDL_PLATFORM_XBOXONE) |
748 | return "Xbox One" ; |
749 | #elif defined(SDL_PLATFORM_XBOXSERIES) |
750 | return "Xbox Series X|S" ; |
751 | #elif defined(SDL_PLATFORM_IOS) |
752 | return "iOS" ; |
753 | #elif defined(SDL_PLATFORM_TVOS) |
754 | return "tvOS" ; |
755 | #elif defined(SDL_PLATFORM_PS2) |
756 | return "PlayStation 2" ; |
757 | #elif defined(SDL_PLATFORM_PSP) |
758 | return "PlayStation Portable" ; |
759 | #elif defined(SDL_PLATFORM_VITA) |
760 | return "PlayStation Vita" ; |
761 | #elif defined(SDL_PLATFORM_3DS) |
762 | return "Nintendo 3DS" ; |
763 | #elif defined(__managarm__) |
764 | return "Managarm" ; |
765 | #else |
766 | return "Unknown (see SDL_platform.h)" ; |
767 | #endif |
768 | } |
769 | |
770 | bool SDL_IsTablet(void) |
771 | { |
772 | #ifdef SDL_PLATFORM_ANDROID |
773 | return SDL_IsAndroidTablet(); |
774 | #elif defined(SDL_PLATFORM_IOS) |
775 | extern bool SDL_IsIPad(void); |
776 | return SDL_IsIPad(); |
777 | #else |
778 | return false; |
779 | #endif |
780 | } |
781 | |
782 | bool SDL_IsTV(void) |
783 | { |
784 | #ifdef SDL_PLATFORM_ANDROID |
785 | return SDL_IsAndroidTV(); |
786 | #elif defined(SDL_PLATFORM_IOS) |
787 | extern bool SDL_IsAppleTV(void); |
788 | return SDL_IsAppleTV(); |
789 | #else |
790 | return false; |
791 | #endif |
792 | } |
793 | |
794 | static SDL_Sandbox SDL_DetectSandbox(void) |
795 | { |
796 | #if defined(SDL_PLATFORM_LINUX) |
797 | if (access("/.flatpak-info" , F_OK) == 0) { |
798 | return SDL_SANDBOX_FLATPAK; |
799 | } |
800 | |
801 | /* For Snap, we check multiple variables because they might be set for |
802 | * unrelated reasons. This is the same thing WebKitGTK does. */ |
803 | if (SDL_getenv("SNAP" ) && SDL_getenv("SNAP_NAME" ) && SDL_getenv("SNAP_REVISION" )) { |
804 | return SDL_SANDBOX_SNAP; |
805 | } |
806 | |
807 | if (access("/run/host/container-manager" , F_OK) == 0) { |
808 | return SDL_SANDBOX_UNKNOWN_CONTAINER; |
809 | } |
810 | |
811 | #elif defined(SDL_PLATFORM_MACOS) |
812 | if (SDL_getenv("APP_SANDBOX_CONTAINER_ID" )) { |
813 | return SDL_SANDBOX_MACOS; |
814 | } |
815 | #endif |
816 | |
817 | return SDL_SANDBOX_NONE; |
818 | } |
819 | |
820 | SDL_Sandbox SDL_GetSandbox(void) |
821 | { |
822 | static SDL_Sandbox sandbox; |
823 | static bool sandbox_initialized; |
824 | |
825 | if (!sandbox_initialized) { |
826 | sandbox = SDL_DetectSandbox(); |
827 | sandbox_initialized = true; |
828 | } |
829 | return sandbox; |
830 | } |
831 | |
832 | #ifdef SDL_PLATFORM_WIN32 |
833 | |
834 | #if (!defined(HAVE_LIBC) || defined(__WATCOMC__)) && !defined(SDL_STATIC_LIB) |
835 | // FIXME: Still need to include DllMain() on Watcom C ? |
836 | |
837 | BOOL APIENTRY MINGW32_FORCEALIGN _DllMainCRTStartup(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) |
838 | { |
839 | switch (ul_reason_for_call) { |
840 | case DLL_PROCESS_ATTACH: |
841 | case DLL_THREAD_ATTACH: |
842 | case DLL_THREAD_DETACH: |
843 | case DLL_PROCESS_DETACH: |
844 | break; |
845 | } |
846 | return TRUE; |
847 | } |
848 | #endif // Building DLL |
849 | |
850 | #endif // defined(SDL_PLATFORM_WIN32) |
851 | |