2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "./SDL_internal.h"
23 #if defined(__WIN32__)
24 #include "core/windows/SDL_windows.h"
28 #include "SDL_atomic.h"
29 #include "SDL_messagebox.h"
30 #include "SDL_video.h"
31 #include "SDL_assert.h"
32 #include "SDL_assert_c.h"
33 #include "video/SDL_sysvideo.h"
36 #ifndef WS_OVERLAPPEDWINDOW
37 #define WS_OVERLAPPEDWINDOW 0
39 #else /* fprintf, _exit(), etc. */
42 #if ! defined(__WINRT__)
47 static SDL_assert_state
48 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata);
51 * We keep all triggered assertions in a singly-linked list so we can
52 * generate a report later.
54 static SDL_assert_data *triggered_assertions = NULL;
56 static SDL_mutex *assertion_mutex = NULL;
57 static SDL_AssertionHandler assertion_handler = SDL_PromptAssertion;
58 static void *assertion_userdata = NULL;
62 debug_print(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
66 debug_print(const char *fmt, ...)
70 SDL_LogMessageV(SDL_LOG_CATEGORY_ASSERT, SDL_LOG_PRIORITY_WARN, fmt, ap);
75 static void SDL_AddAssertionToReport(SDL_assert_data *data)
77 /* (data) is always a static struct defined with the assert macros, so
78 we don't have to worry about copying or allocating them. */
79 data->trigger_count++;
80 if (data->trigger_count == 1) { /* not yet added? */
81 data->next = triggered_assertions;
82 triggered_assertions = data;
87 static void SDL_GenerateAssertionReport(void)
89 const SDL_assert_data *item = triggered_assertions;
91 /* only do this if the app hasn't assigned an assertion handler. */
92 if ((item != NULL) && (assertion_handler != SDL_PromptAssertion)) {
93 debug_print("\n\nSDL assertion report.\n");
94 debug_print("All SDL assertions between last init/quit:\n\n");
96 while (item != NULL) {
100 " * triggered %u time%s.\n"
101 " * always ignore: %s.\n",
102 item->condition, item->function, item->filename,
103 item->linenum, item->trigger_count,
104 (item->trigger_count == 1) ? "" : "s",
105 item->always_ignore ? "yes" : "no");
110 SDL_ResetAssertionReport();
114 static void SDL_ExitProcess(int exitcode)
117 ExitProcess(exitcode);
123 static void SDL_AbortAssertion(void)
130 static SDL_assert_state
131 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
134 #define ENDLINE "\r\n"
140 SDL_assert_state state = SDL_ASSERTION_ABORT;
142 SDL_MessageBoxData messagebox;
143 SDL_MessageBoxButtonData buttons[] = {
144 { 0, SDL_ASSERTION_RETRY, "Retry" },
145 { 0, SDL_ASSERTION_BREAK, "Break" },
146 { 0, SDL_ASSERTION_ABORT, "Abort" },
147 { SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
148 SDL_ASSERTION_IGNORE, "Ignore" },
149 { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
150 SDL_ASSERTION_ALWAYS_IGNORE, "Always Ignore" }
155 (void) userdata; /* unused in default handler. */
157 message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
159 /* Uh oh, we're in real trouble now... */
160 return SDL_ASSERTION_ABORT;
162 SDL_snprintf(message, SDL_MAX_LOG_MESSAGE,
163 "Assertion failure at %s (%s:%d), triggered %u %s:" ENDLINE
165 data->function, data->filename, data->linenum,
166 data->trigger_count, (data->trigger_count == 1) ? "time" : "times",
169 debug_print("\n\n%s\n\n", message);
171 /* let env. variable override, so unit tests won't block in a GUI. */
172 envr = SDL_getenv("SDL_ASSERT");
174 SDL_stack_free(message);
176 if (SDL_strcmp(envr, "abort") == 0) {
177 return SDL_ASSERTION_ABORT;
178 } else if (SDL_strcmp(envr, "break") == 0) {
179 return SDL_ASSERTION_BREAK;
180 } else if (SDL_strcmp(envr, "retry") == 0) {
181 return SDL_ASSERTION_RETRY;
182 } else if (SDL_strcmp(envr, "ignore") == 0) {
183 return SDL_ASSERTION_IGNORE;
184 } else if (SDL_strcmp(envr, "always_ignore") == 0) {
185 return SDL_ASSERTION_ALWAYS_IGNORE;
187 return SDL_ASSERTION_ABORT; /* oh well. */
191 /* Leave fullscreen mode, if possible (scary!) */
192 window = SDL_GetFocusWindow();
194 if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) {
195 SDL_MinimizeWindow(window);
197 /* !!! FIXME: ungrab the input if we're not fullscreen? */
198 /* No need to mess with the window */
203 /* Show a messagebox if we can, otherwise fall back to stdio */
204 SDL_zero(messagebox);
205 messagebox.flags = SDL_MESSAGEBOX_WARNING;
206 messagebox.window = window;
207 messagebox.title = "Assertion Failed";
208 messagebox.message = message;
209 messagebox.numbuttons = SDL_arraysize(buttons);
210 messagebox.buttons = buttons;
212 if (SDL_ShowMessageBox(&messagebox, &selected) == 0) {
213 if (selected == -1) {
214 state = SDL_ASSERTION_IGNORE;
216 state = (SDL_assert_state)selected;
222 /* this is a little hacky. */
225 fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
227 if (fgets(buf, sizeof (buf), stdin) == NULL) {
231 if (SDL_strcmp(buf, "a") == 0) {
232 state = SDL_ASSERTION_ABORT;
234 } else if (SDL_strcmp(buf, "b") == 0) {
235 state = SDL_ASSERTION_BREAK;
237 } else if (SDL_strcmp(buf, "r") == 0) {
238 state = SDL_ASSERTION_RETRY;
240 } else if (SDL_strcmp(buf, "i") == 0) {
241 state = SDL_ASSERTION_IGNORE;
243 } else if (SDL_strcmp(buf, "A") == 0) {
244 state = SDL_ASSERTION_ALWAYS_IGNORE;
249 #endif /* HAVE_STDIO_H */
251 /* Re-enter fullscreen mode */
253 SDL_RestoreWindow(window);
256 SDL_stack_free(message);
263 SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file,
266 static int assertion_running = 0;
267 static SDL_SpinLock spinlock = 0;
268 SDL_assert_state state = SDL_ASSERTION_IGNORE;
270 SDL_AtomicLock(&spinlock);
271 if (assertion_mutex == NULL) { /* never called SDL_Init()? */
272 assertion_mutex = SDL_CreateMutex();
273 if (assertion_mutex == NULL) {
274 SDL_AtomicUnlock(&spinlock);
275 return SDL_ASSERTION_IGNORE; /* oh well, I guess. */
278 SDL_AtomicUnlock(&spinlock);
280 if (SDL_LockMutex(assertion_mutex) < 0) {
281 return SDL_ASSERTION_IGNORE; /* oh well, I guess. */
284 /* doing this because Visual C is upset over assigning in the macro. */
285 if (data->trigger_count == 0) {
286 data->function = func;
287 data->filename = file;
288 data->linenum = line;
291 SDL_AddAssertionToReport(data);
294 if (assertion_running > 1) { /* assert during assert! Abort. */
295 if (assertion_running == 2) {
296 SDL_AbortAssertion();
297 } else if (assertion_running == 3) { /* Abort asserted! */
300 while (1) { /* do nothing but spin; what else can you do?! */ }
304 if (!data->always_ignore) {
305 state = assertion_handler(data, assertion_userdata);
310 case SDL_ASSERTION_ABORT:
311 SDL_AbortAssertion();
312 return SDL_ASSERTION_IGNORE; /* shouldn't return, but oh well. */
314 case SDL_ASSERTION_ALWAYS_IGNORE:
315 state = SDL_ASSERTION_IGNORE;
316 data->always_ignore = 1;
319 case SDL_ASSERTION_IGNORE:
320 case SDL_ASSERTION_RETRY:
321 case SDL_ASSERTION_BREAK:
322 break; /* macro handles these. */
326 SDL_UnlockMutex(assertion_mutex);
332 void SDL_AssertionsQuit(void)
334 SDL_GenerateAssertionReport();
335 if (assertion_mutex != NULL) {
336 SDL_DestroyMutex(assertion_mutex);
337 assertion_mutex = NULL;
341 void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
343 if (handler != NULL) {
344 assertion_handler = handler;
345 assertion_userdata = userdata;
347 assertion_handler = SDL_PromptAssertion;
348 assertion_userdata = NULL;
352 const SDL_assert_data *SDL_GetAssertionReport(void)
354 return triggered_assertions;
357 void SDL_ResetAssertionReport(void)
359 SDL_assert_data *next = NULL;
360 SDL_assert_data *item;
361 for (item = triggered_assertions; item != NULL; item = next) {
362 next = (SDL_assert_data *) item->next;
363 item->always_ignore = SDL_FALSE;
364 item->trigger_count = 0;
368 triggered_assertions = NULL;
371 SDL_AssertionHandler SDL_GetDefaultAssertionHandler(void)
373 return SDL_PromptAssertion;
376 SDL_AssertionHandler SDL_GetAssertionHandler(void **userdata)
378 if (userdata != NULL) {
379 *userdata = assertion_userdata;
381 return assertion_handler;
384 /* vi: set ts=4 sw=4 expandtab: */