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