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
23// General quit handling code for SDL
24
25#ifdef HAVE_SIGNAL_H
26#include <signal.h>
27#endif
28
29#include "SDL_events_c.h"
30
31#if defined(HAVE_SIGNAL_H) || defined(HAVE_SIGACTION)
32#define HAVE_SIGNAL_SUPPORT 1
33#endif
34
35#ifdef HAVE_SIGNAL_SUPPORT
36static bool disable_signals = false;
37static bool send_quit_pending = false;
38
39#ifdef SDL_BACKGROUNDING_SIGNAL
40static bool send_backgrounding_pending = false;
41#endif
42
43#ifdef SDL_FOREGROUNDING_SIGNAL
44static bool send_foregrounding_pending = false;
45#endif
46
47static void SDL_HandleSIG(int sig)
48{
49 // Reset the signal handler
50 (void)signal(sig, SDL_HandleSIG);
51
52 // Send a quit event next time the event loop pumps.
53 // We can't send it in signal handler; SDL_malloc() might be interrupted!
54 if ((sig == SIGINT) || (sig == SIGTERM)) {
55 send_quit_pending = true;
56 }
57
58#ifdef SDL_BACKGROUNDING_SIGNAL
59 else if (sig == SDL_BACKGROUNDING_SIGNAL) {
60 send_backgrounding_pending = true;
61 }
62#endif
63
64#ifdef SDL_FOREGROUNDING_SIGNAL
65 else if (sig == SDL_FOREGROUNDING_SIGNAL) {
66 send_foregrounding_pending = true;
67 }
68#endif
69}
70
71static void SDL_EventSignal_Init(const int sig)
72{
73#ifdef HAVE_SIGACTION
74 struct sigaction action;
75
76 sigaction(sig, NULL, &action);
77#ifdef HAVE_SA_SIGACTION
78 if (action.sa_handler == SIG_DFL && (void (*)(int))action.sa_sigaction == SIG_DFL) {
79#else
80 if (action.sa_handler == SIG_DFL) {
81#endif
82 action.sa_handler = SDL_HandleSIG;
83 sigaction(sig, &action, NULL);
84 }
85#elif defined(HAVE_SIGNAL_H)
86 void (*ohandler)(int) = signal(sig, SDL_HandleSIG);
87 if (ohandler != SIG_DFL) {
88 signal(sig, ohandler);
89 }
90#endif
91}
92
93static void SDL_EventSignal_Quit(const int sig)
94{
95#ifdef HAVE_SIGACTION
96 struct sigaction action;
97 sigaction(sig, NULL, &action);
98 if (action.sa_handler == SDL_HandleSIG) {
99 action.sa_handler = SIG_DFL;
100 sigaction(sig, &action, NULL);
101 }
102#elif defined(HAVE_SIGNAL_H)
103 void (*ohandler)(int) = signal(sig, SIG_DFL);
104 if (ohandler != SDL_HandleSIG) {
105 signal(sig, ohandler);
106 }
107#endif // HAVE_SIGNAL_H
108}
109
110// Public functions
111static bool SDL_QuitInit_Internal(void)
112{
113 // Both SIGINT and SIGTERM are translated into quit interrupts
114 // and SDL can be built to simulate iOS/Android semantics with arbitrary signals.
115 SDL_EventSignal_Init(SIGINT);
116 SDL_EventSignal_Init(SIGTERM);
117
118#ifdef SDL_BACKGROUNDING_SIGNAL
119 SDL_EventSignal_Init(SDL_BACKGROUNDING_SIGNAL);
120#endif
121
122#ifdef SDL_FOREGROUNDING_SIGNAL
123 SDL_EventSignal_Init(SDL_FOREGROUNDING_SIGNAL);
124#endif
125
126 // That's it!
127 return true;
128}
129
130static void SDL_QuitQuit_Internal(void)
131{
132 SDL_EventSignal_Quit(SIGINT);
133 SDL_EventSignal_Quit(SIGTERM);
134
135#ifdef SDL_BACKGROUNDING_SIGNAL
136 SDL_EventSignal_Quit(SDL_BACKGROUNDING_SIGNAL);
137#endif
138
139#ifdef SDL_FOREGROUNDING_SIGNAL
140 SDL_EventSignal_Quit(SDL_FOREGROUNDING_SIGNAL);
141#endif
142}
143#endif
144
145bool SDL_InitQuit(void)
146{
147#ifdef HAVE_SIGNAL_SUPPORT
148 if (!SDL_GetHintBoolean(SDL_HINT_NO_SIGNAL_HANDLERS, false)) {
149 return SDL_QuitInit_Internal();
150 }
151#endif
152 return true;
153}
154
155void SDL_QuitQuit(void)
156{
157#ifdef HAVE_SIGNAL_SUPPORT
158 if (!disable_signals) {
159 SDL_QuitQuit_Internal();
160 }
161#endif
162}
163
164void SDL_SendPendingSignalEvents(void)
165{
166#ifdef HAVE_SIGNAL_SUPPORT
167 if (send_quit_pending) {
168 SDL_SendQuit();
169 SDL_assert(!send_quit_pending);
170 }
171
172#ifdef SDL_BACKGROUNDING_SIGNAL
173 if (send_backgrounding_pending) {
174 send_backgrounding_pending = false;
175 SDL_OnApplicationWillEnterBackground();
176 }
177#endif
178
179#ifdef SDL_FOREGROUNDING_SIGNAL
180 if (send_foregrounding_pending) {
181 send_foregrounding_pending = false;
182 SDL_OnApplicationDidEnterForeground();
183 }
184#endif
185#endif
186}
187
188void SDL_SendQuit(void)
189{
190#ifdef HAVE_SIGNAL_SUPPORT
191 send_quit_pending = false;
192#endif
193 SDL_SendAppEvent(SDL_EVENT_QUIT);
194}
195