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
69extern bool SDL_HelperWindowCreate(void);
70extern void SDL_HelperWindowDestroy(void);
71#endif
72
73#ifdef SDL_BUILD_MAJOR_VERSION
74SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MAJOR_VERSION,
75 SDL_MAJOR_VERSION == SDL_BUILD_MAJOR_VERSION);
76SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MINOR_VERSION,
77 SDL_MINOR_VERSION == SDL_BUILD_MINOR_VERSION);
78SDL_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
83SDL_COMPILE_TIME_ASSERT(SDL_MAJOR_VERSION_min, SDL_MAJOR_VERSION >= 0);
84SDL_COMPILE_TIME_ASSERT(SDL_MAJOR_VERSION_max, SDL_MAJOR_VERSION <= 10);
85SDL_COMPILE_TIME_ASSERT(SDL_MINOR_VERSION_min, SDL_MINOR_VERSION >= 0);
86SDL_COMPILE_TIME_ASSERT(SDL_MINOR_VERSION_max, SDL_MINOR_VERSION <= 999);
87SDL_COMPILE_TIME_ASSERT(SDL_MICRO_VERSION_min, SDL_MICRO_VERSION >= 0);
88SDL_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. */
93extern SDL_NORETURN void SDL_ExitProcess(int exitcode);
94SDL_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
119bool 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
127static 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
145bool 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
154const 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
183static bool SDL_MainIsReady = false;
184#else
185static bool SDL_MainIsReady = true;
186#endif
187static SDL_ThreadID SDL_MainThreadID = 0;
188static bool SDL_bInMainQuit = false;
189static Uint8 SDL_SubsystemRefCount[32];
190
191// Private helper to increment a subsystem's ref counter.
192static 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.
202static 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.
215static 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.
223static 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. */
238static 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
252void SDL_SetMainReady(void)
253{
254 SDL_MainIsReady = true;
255
256 if (SDL_MainThreadID == 0) {
257 SDL_MainThreadID = SDL_GetCurrentThreadID();
258 }
259}
260
261bool 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
274void 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
298static void SDL_QuitMainThread(void)
299{
300 SDL_QuitFilesystem();
301 SDL_QuitTicks();
302 SDL_QuitEnvironment();
303 SDL_QuitTLSData();
304}
305
306bool 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
523quit_and_error:
524 {
525 SDL_PushError();
526 SDL_QuitSubSystem(flags_initialized);
527 SDL_PopError();
528 }
529 return false;
530}
531
532bool SDL_Init(SDL_InitFlags flags)
533{
534 return SDL_InitSubSystem(flags);
535}
536
537void 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
621Uint32 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
651void 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
691int SDL_GetVersion(void)
692{
693 return SDL_VERSION;
694}
695
696// Get the library source revision
697const char *SDL_GetRevision(void)
698{
699 return SDL_REVISION;
700}
701
702// Get the name of the platform
703const 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
770bool 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
782bool 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
794static 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
820SDL_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
837BOOL 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