SDL_ExitProcess() was ignoring exit code parameter.
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2012 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_config.h"
24 #include "SDL_atomic.h"
25 #include "SDL_assert.h"
26 #include "SDL_assert_c.h"
27 #include "video/SDL_sysvideo.h"
30 #include "core/windows/SDL_windows.h"
32 #ifndef WS_OVERLAPPEDWINDOW
33 #define WS_OVERLAPPEDWINDOW 0
35 #else /* fprintf, _exit(), etc. */
41 static SDL_assert_state
42 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata);
45 * We keep all triggered assertions in a singly-linked list so we can
46 * generate a report later.
48 static SDL_assert_data *triggered_assertions = NULL;
50 static SDL_mutex *assertion_mutex = NULL;
51 static SDL_AssertionHandler assertion_handler = SDL_PromptAssertion;
52 static void *assertion_userdata = NULL;
56 debug_print(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
60 debug_print(const char *fmt, ...)
63 /* Format into a buffer for OutputDebugStringA(). */
71 len = (int) SDL_vsnprintf(buf, sizeof (buf), fmt, ap);
74 /* Visual C's vsnprintf() may not null-terminate the buffer. */
75 if ((len >= sizeof (buf)) || (len < 0)) {
76 buf[sizeof (buf) - 1] = '\0';
79 /* Write it, sorting out the Unix newlines... */
81 for (ptr = startptr; *ptr; ptr++) {
84 tstr = WIN_UTF8ToString(startptr);
85 OutputDebugString(tstr);
87 OutputDebugString(TEXT("\r\n"));
92 /* catch that last piece if it didn't have a newline... */
93 if (startptr != ptr) {
94 tstr = WIN_UTF8ToString(startptr);
95 OutputDebugString(tstr);
99 /* Unix has it easy. Just dump it to stderr. */
102 vfprintf(stderr, fmt, ap);
110 static SDL_assert_state SDL_Windows_AssertChoice = SDL_ASSERTION_ABORT;
111 static const SDL_assert_data *SDL_Windows_AssertData = NULL;
113 static LRESULT CALLBACK
114 SDL_Assertion_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
120 /* !!! FIXME: all this code stinks. */
121 const SDL_assert_data *data = SDL_Windows_AssertData;
131 static const struct {
133 SDL_assert_state state;
135 {TEXT("Abort"), SDL_ASSERTION_ABORT },
136 {TEXT("Break"), SDL_ASSERTION_BREAK },
137 {TEXT("Retry"), SDL_ASSERTION_RETRY },
138 {TEXT("Ignore"), SDL_ASSERTION_IGNORE },
139 {TEXT("Always Ignore"), SDL_ASSERTION_ALWAYS_IGNORE },
142 len = (int) SDL_snprintf(buf, sizeof (buf),
143 "Assertion failure at %s (%s:%d), triggered %u time%s:\r\n '%s'",
144 data->function, data->filename, data->linenum,
145 data->trigger_count, (data->trigger_count == 1) ? "" : "s",
147 if ((len < 0) || (len >= sizeof (buf))) {
148 buf[sizeof (buf) - 1] = '\0';
151 tstr = WIN_UTF8ToString(buf);
152 CreateWindow(TEXT("STATIC"), tstr,
153 WS_VISIBLE | WS_CHILD | SS_LEFT,
155 hwnd, (HMENU) 1, NULL, NULL);
159 for (i = 0; i < (sizeof (buttons) / sizeof (buttons[0])); i++) {
160 CreateWindow(TEXT("BUTTON"), buttons[i].name,
161 WS_VISIBLE | WS_CHILD,
163 hwnd, (HMENU) buttons[i].state, NULL, NULL);
170 SDL_Windows_AssertChoice = ((SDL_assert_state) (LOWORD(wParam)));
171 SDL_Windows_AssertData = NULL;
175 SDL_Windows_AssertData = NULL;
179 return DefWindowProc(hwnd, msg, wParam, lParam);
182 static SDL_assert_state
183 SDL_PromptAssertion_windows(const SDL_assert_data *data)
185 HINSTANCE hInstance = 0; /* !!! FIXME? */
190 SDL_Windows_AssertChoice = SDL_ASSERTION_ABORT;
191 SDL_Windows_AssertData = data;
193 wc.lpszClassName = TEXT("SDL_assert");
194 wc.hInstance = hInstance ;
195 wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
196 wc.lpfnWndProc = SDL_Assertion_WndProc;
197 wc.hCursor = LoadCursor(0, IDC_ARROW);
200 hwnd = CreateWindow(wc.lpszClassName, TEXT("SDL assertion failure"),
201 WS_OVERLAPPEDWINDOW | WS_VISIBLE,
202 150, 150, 570, 260, 0, 0, hInstance, 0);
204 while (GetMessage(&msg, NULL, 0, 0) && (SDL_Windows_AssertData != NULL)) {
205 TranslateMessage(&msg);
206 DispatchMessage(&msg);
210 UnregisterClass(wc.lpszClassName, hInstance);
211 return SDL_Windows_AssertChoice;
216 static void SDL_AddAssertionToReport(SDL_assert_data *data)
218 /* (data) is always a static struct defined with the assert macros, so
219 we don't have to worry about copying or allocating them. */
220 data->trigger_count++;
221 if (data->trigger_count == 1) { /* not yet added? */
222 data->next = triggered_assertions;
223 triggered_assertions = data;
228 static void SDL_GenerateAssertionReport(void)
230 const SDL_assert_data *item = triggered_assertions;
232 /* only do this if the app hasn't assigned an assertion handler. */
233 if ((item != NULL) && (assertion_handler != SDL_PromptAssertion)) {
234 debug_print("\n\nSDL assertion report.\n");
235 debug_print("All SDL assertions between last init/quit:\n\n");
237 while (item != NULL) {
241 " * triggered %u time%s.\n"
242 " * always ignore: %s.\n",
243 item->condition, item->function, item->filename,
244 item->linenum, item->trigger_count,
245 (item->trigger_count == 1) ? "" : "s",
246 item->always_ignore ? "yes" : "no");
251 SDL_ResetAssertionReport();
255 static void SDL_ExitProcess(int exitcode)
258 ExitProcess(exitcode);
264 static void SDL_AbortAssertion(void)
271 static SDL_assert_state
272 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
275 SDL_assert_state state = SDL_ASSERTION_ABORT;
278 (void) userdata; /* unused in default handler. */
281 "Assertion failure at %s (%s:%d), triggered %u time%s:\n"
284 data->function, data->filename, data->linenum,
285 data->trigger_count, (data->trigger_count == 1) ? "" : "s",
288 /* let env. variable override, so unit tests won't block in a GUI. */
289 envr = SDL_getenv("SDL_ASSERT");
291 if (SDL_strcmp(envr, "abort") == 0) {
292 return SDL_ASSERTION_ABORT;
293 } else if (SDL_strcmp(envr, "break") == 0) {
294 return SDL_ASSERTION_BREAK;
295 } else if (SDL_strcmp(envr, "retry") == 0) {
296 return SDL_ASSERTION_RETRY;
297 } else if (SDL_strcmp(envr, "ignore") == 0) {
298 return SDL_ASSERTION_IGNORE;
299 } else if (SDL_strcmp(envr, "always_ignore") == 0) {
300 return SDL_ASSERTION_ALWAYS_IGNORE;
302 return SDL_ASSERTION_ABORT; /* oh well. */
306 /* Leave fullscreen mode, if possible (scary!) */
307 window = SDL_GetFocusWindow();
309 if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) {
310 SDL_MinimizeWindow(window);
312 /* !!! FIXME: ungrab the input if we're not fullscreen? */
313 /* No need to mess with the window */
318 /* platform-specific UI... */
321 state = SDL_PromptAssertion_windows(data);
323 #elif defined __MACOSX__ && defined SDL_VIDEO_DRIVER_COCOA
324 /* This has to be done in an Objective-C (*.m) file, so we call out. */
325 extern SDL_assert_state SDL_PromptAssertion_cocoa(const SDL_assert_data *);
326 state = SDL_PromptAssertion_cocoa(data);
329 /* this is a little hacky. */
332 fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
334 if (fgets(buf, sizeof (buf), stdin) == NULL) {
338 if (SDL_strcmp(buf, "a") == 0) {
339 state = SDL_ASSERTION_ABORT;
341 } else if (SDL_strcmp(buf, "b") == 0) {
342 state = SDL_ASSERTION_BREAK;
344 } else if (SDL_strcmp(buf, "r") == 0) {
345 state = SDL_ASSERTION_RETRY;
347 } else if (SDL_strcmp(buf, "i") == 0) {
348 state = SDL_ASSERTION_IGNORE;
350 } else if (SDL_strcmp(buf, "A") == 0) {
351 state = SDL_ASSERTION_ALWAYS_IGNORE;
357 /* Re-enter fullscreen mode */
359 SDL_RestoreWindow(window);
367 SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file,
370 static int assertion_running = 0;
371 static SDL_SpinLock spinlock = 0;
372 SDL_assert_state state = SDL_ASSERTION_IGNORE;
374 SDL_AtomicLock(&spinlock);
375 if (assertion_mutex == NULL) { /* never called SDL_Init()? */
376 assertion_mutex = SDL_CreateMutex();
377 if (assertion_mutex == NULL) {
378 SDL_AtomicUnlock(&spinlock);
379 return SDL_ASSERTION_IGNORE; /* oh well, I guess. */
382 SDL_AtomicUnlock(&spinlock);
384 if (SDL_LockMutex(assertion_mutex) < 0) {
385 return SDL_ASSERTION_IGNORE; /* oh well, I guess. */
388 /* doing this because Visual C is upset over assigning in the macro. */
389 if (data->trigger_count == 0) {
390 data->function = func;
391 data->filename = file;
392 data->linenum = line;
395 SDL_AddAssertionToReport(data);
398 if (assertion_running > 1) { /* assert during assert! Abort. */
399 if (assertion_running == 2) {
400 SDL_AbortAssertion();
401 } else if (assertion_running == 3) { /* Abort asserted! */
404 while (1) { /* do nothing but spin; what else can you do?! */ }
408 if (!data->always_ignore) {
409 state = assertion_handler(data, assertion_userdata);
414 case SDL_ASSERTION_ABORT:
415 SDL_AbortAssertion();
416 return SDL_ASSERTION_IGNORE; /* shouldn't return, but oh well. */
418 case SDL_ASSERTION_ALWAYS_IGNORE:
419 state = SDL_ASSERTION_IGNORE;
420 data->always_ignore = 1;
423 case SDL_ASSERTION_IGNORE:
424 case SDL_ASSERTION_RETRY:
425 case SDL_ASSERTION_BREAK:
426 break; /* macro handles these. */
430 SDL_UnlockMutex(assertion_mutex);
436 int SDL_AssertionsInit(void)
438 /* this is a no-op at the moment. */
442 void SDL_AssertionsQuit(void)
444 SDL_GenerateAssertionReport();
445 if (assertion_mutex != NULL) {
446 SDL_DestroyMutex(assertion_mutex);
447 assertion_mutex = NULL;
451 void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
453 if (handler != NULL) {
454 assertion_handler = handler;
455 assertion_userdata = userdata;
457 assertion_handler = SDL_PromptAssertion;
458 assertion_userdata = NULL;
462 const SDL_assert_data *SDL_GetAssertionReport(void)
464 return triggered_assertions;
467 void SDL_ResetAssertionReport(void)
469 SDL_assert_data *next = NULL;
470 SDL_assert_data *item;
471 for (item = triggered_assertions; item != NULL; item = next) {
472 next = (SDL_assert_data *) item->next;
473 item->always_ignore = SDL_FALSE;
474 item->trigger_count = 0;
478 triggered_assertions = NULL;
481 /* vi: set ts=4 sw=4 expandtab: */