First pass at Ryan's assertion code, minor tweaks to come.
--- a/Makefile.ds Wed Jan 13 06:39:44 2010 +0000
+++ b/Makefile.ds Wed Jan 13 06:47:17 2010 +0000
@@ -36,6 +36,7 @@
src/SDL_compat.c \
src/SDL_error.c \
src/SDL_fatal.c \
+src/SDL_assert.c \
src/audio/nds/SDL_ndsaudio.c \
src/audio/SDL_audio.c \
src/audio/SDL_audiocvt.c \
--- a/configure.in Wed Jan 13 06:39:44 2010 +0000
+++ b/configure.in Wed Jan 13 06:47:17 2010 +0000
@@ -134,6 +134,32 @@
AC_C_INLINE
AC_C_VOLATILE
+dnl See whether we want assertions for debugging/sanity checking SDL itself.
+AC_ARG_ENABLE(assertions,
+AC_HELP_STRING([--enable-assertions],
+ [Enable internal sanity checks (yes/no/release/paranoid) [[default=release]]]),
+ , enable_assertions=release)
+sdl_valid_assertion_level=no
+if test x$enable_assertions = xno; then
+ sdl_valid_assertion_level=yes
+ AC_DEFINE(SDL_ASSERT_LEVEL, 0)
+fi
+if test x$enable_assertions = xrelease; then
+ sdl_valid_assertion_level=yes
+ AC_DEFINE(SDL_ASSERT_LEVEL, 1)
+fi
+if test x$enable_assertions = xyes; then
+ sdl_valid_assertion_level=yes
+ AC_DEFINE(SDL_ASSERT_LEVEL, 2)
+fi
+if test x$enable_assertions = xparanoid; then
+ sdl_valid_assertion_level=yes
+ AC_DEFINE(SDL_ASSERT_LEVEL, 3)
+fi
+if test x$sdl_valid_assertion_level = xno; then
+ AC_MSG_ERROR([*** unknown assertion level. stop.])
+fi
+
dnl See whether we can use gcc style dependency tracking
AC_ARG_ENABLE(dependency-tracking,
AC_HELP_STRING([--enable-dependency-tracking],
--- a/include/SDL.h Wed Jan 13 06:39:44 2010 +0000
+++ b/include/SDL.h Wed Jan 13 06:47:17 2010 +0000
@@ -77,6 +77,7 @@
#include "SDL_main.h"
#include "SDL_stdinc.h"
+#include "SDL_assert.h"
#include "SDL_atomic.h"
#include "SDL_audio.h"
#include "SDL_cpuinfo.h"
@@ -89,8 +90,8 @@
#include "SDL_rwops.h"
#include "SDL_thread.h"
#include "SDL_timer.h"
+#include "SDL_version.h"
#include "SDL_video.h"
-#include "SDL_version.h"
#include "SDL_compat.h"
#include "begin_code.h"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/SDL_assert.h Wed Jan 13 06:47:17 2010 +0000
@@ -0,0 +1,151 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2009 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+/* This is an assert macro for SDL's internal use. Not for the public API! */
+
+#ifndef _SDL_assert_h
+#define _SDL_assert_h
+
+#ifndef SDL_ASSERT_LEVEL
+#error SDL_ASSERT_LEVEL is not defined. Please fix your SDL_config.h.
+#endif
+
+/*
+sizeof (x) makes the compiler still parse the expression even without
+assertions enabled, so the code is always checked at compile time, but
+doesn't actually generate code for it, so there are no side effects or
+expensive checks at run time, just the constant size of what x WOULD be,
+which presumably gets optimized out as unused.
+This also solves the problem of...
+
+ int somevalue = blah();
+ SDL_assert(somevalue == 1);
+
+...which would cause compiles to complain that somevalue is unused if we
+disable assertions.
+*/
+
+#define SDL_disabled_assert(condition) \
+ do { (void) sizeof ((condition)); } while (0)
+
+#if (SDL_ASSERT_LEVEL > 0)
+
+/*
+These are macros and not first class functions so that the debugger breaks
+on the assertion line and not in some random guts of SDL, and so each
+macro can have unique static variables associated with it.
+*/
+
+#if (defined(_MSC_VER) && ((_M_IX86) || (_M_X64)))
+ #define SDL_TriggerBreakpoint() __asm { int 3 }
+#elif (defined(__GNUC__) && ((__i386__) || (__x86_64__)))
+ #define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "int $3\n\t" )
+#elif defined(unix)
+ #include <signal.h>
+ #define SDL_TriggerBreakpoint() raise(SIGTRAP)
+#else
+ #error Please define your platform or set SDL_ASSERT_LEVEL to 0.
+#endif
+
+#if (__STDC_VERSION__ >= 199901L) /* C99 supports __func__ as a standard. */
+# define SDL_FUNCTION __func__
+#elif ((__GNUC__ >= 2) || defined(_MSC_VER))
+# define SDL_FUNCTION __FUNCTION__
+#else
+# define SDL_FUNCTION "???"
+#endif
+
+typedef enum
+{
+ SDL_ASSERTION_RETRY, /**< Retry the assert immediately. */
+ SDL_ASSERTION_BREAK, /**< Make the debugger trigger a breakpoint. */
+ SDL_ASSERTION_ABORT, /**< Terminate the program. */
+ SDL_ASSERTION_IGNORE, /**< Ignore the assert. */
+ SDL_ASSERTION_ALWAYS_IGNORE, /**< Ignore the assert from now on. */
+} SDL_assert_state;
+
+typedef struct SDL_assert_data
+{
+ int always_ignore;
+ unsigned int trigger_count;
+ const char *condition;
+ const char *filename;
+ int linenum;
+ const char *function;
+ struct SDL_assert_data *next;
+} SDL_assert_data;
+
+SDL_assert_state SDL_ReportAssertion(SDL_assert_data *, const char *, int);
+
+/* the do {} while(0) avoids dangling else problems:
+ if (x) SDL_assert(y); else blah();
+ ... without the do/while, the "else" could attach to this macro's "if".
+ We try to handle just the minimum we need here in a macro...the loop,
+ the static vars, and break points. The heavy lifting is handled in
+ SDL_ReportAssertion(), in SDL_assert.c.
+*/
+#define SDL_enabled_assert(condition) \
+ do { \
+ while ( !(condition) ) { \
+ static struct SDL_assert_data assert_data = { \
+ 0, 0, #condition, __FILE__, 0, 0, 0 \
+ }; \
+ const SDL_assert_state state = SDL_ReportAssertion(&assert_data, \
+ SDL_FUNCTION, \
+ __LINE__); \
+ if (state == SDL_ASSERTION_RETRY) { \
+ continue; /* go again. */ \
+ } else if (state == SDL_ASSERTION_BREAK) { \
+ SDL_TriggerBreakpoint(); \
+ } \
+ break; /* not retrying. */ \
+ } \
+ } while (0)
+
+#endif /* enabled assertions support code */
+
+/* Enable various levels of assertions. */
+#if SDL_ASSERT_LEVEL == 0 /* assertions disabled */
+# define SDL_assert(condition) SDL_disabled_assert(condition)
+# define SDL_assert_release(condition) SDL_disabled_assert(condition)
+# define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
+#elif SDL_ASSERT_LEVEL == 1 /* release settings. */
+# define SDL_assert(condition) SDL_enabled_assert(condition)
+# define SDL_assert_release(condition) SDL_enabled_assert(condition)
+# define SDL_assert_paranoid(condition) SDL_enabled_assert(condition)
+#elif SDL_ASSERT_LEVEL == 2 /* normal settings. */
+# define SDL_assert(condition) SDL_enabled_assert(condition)
+# define SDL_assert_release(condition) SDL_enabled_assert(condition)
+# define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
+#elif SDL_ASSERT_LEVEL == 3 /* paranoid settings. */
+# define SDL_assert(condition) SDL_enabled_assert(condition)
+# define SDL_assert_release(condition) SDL_enabled_assert(condition)
+# define SDL_assert_paranoid(condition) SDL_enabled_assert(condition)
+#else
+# error Unknown assertion level. Please fix your SDL_config.h.
+#endif
+
+#endif /* _SDL_assert_h */
+
+/* vi: set ts=4 sw=4 expandtab: */
+
--- a/include/SDL_config.h.in Wed Jan 13 06:39:44 2010 +0000
+++ b/include/SDL_config.h.in Wed Jan 13 06:47:17 2010 +0000
@@ -162,6 +162,9 @@
#include <stdarg.h>
#endif /* HAVE_LIBC */
+/* SDL internal assertion support */
+#undef SDL_ASSERT_LEVEL
+
/* Allow disabling of core subsystems */
#undef SDL_AUDIO_DISABLED
#undef SDL_CPUINFO_DISABLED
--- a/include/SDL_config_iphoneos.h Wed Jan 13 06:39:44 2010 +0000
+++ b/include/SDL_config_iphoneos.h Wed Jan 13 06:47:17 2010 +0000
@@ -25,6 +25,9 @@
#include "SDL_platform.h"
+/* SDL internal assertion support */
+#define SDL_ASSERT_LEVEL 1
+
#if !defined(_STDINT_H_) && (!defined(HAVE_STDINT_H) || !_HAVE_STDINT_H)
typedef signed char int8_t;
typedef unsigned char uint8_t;
--- a/include/SDL_config_macosx.h Wed Jan 13 06:39:44 2010 +0000
+++ b/include/SDL_config_macosx.h Wed Jan 13 06:47:17 2010 +0000
@@ -28,6 +28,9 @@
/* This gets us MAC_OS_X_VERSION_MIN_REQUIRED... */
#include <AvailabilityMacros.h>
+/* SDL internal assertion support */
+#define SDL_ASSERT_LEVEL 1
+
/* This is a set of defines to configure the SDL features */
#ifdef __LP64__
--- a/include/SDL_config_minimal.h Wed Jan 13 06:39:44 2010 +0000
+++ b/include/SDL_config_minimal.h Wed Jan 13 06:47:17 2010 +0000
@@ -33,6 +33,9 @@
#include <stdarg.h>
+/* SDL internal assertion support */
+#define SDL_ASSERT_LEVEL 1
+
#if !defined(_STDINT_H_) && (!defined(HAVE_STDINT_H) || !_HAVE_STDINT_H)
typedef signed char int8_t;
typedef unsigned char uint8_t;
--- a/include/SDL_config_nintendods.h Wed Jan 13 06:39:44 2010 +0000
+++ b/include/SDL_config_nintendods.h Wed Jan 13 06:47:17 2010 +0000
@@ -27,6 +27,9 @@
/* This is a set of defines to configure the SDL features */
+/* SDL internal assertion support */
+#define SDL_ASSERT_LEVEL 1
+
#if !defined(_STDINT_H_) && (!defined(HAVE_STDINT_H) || !_HAVE_STDINT_H)
typedef signed char int8_t;
typedef unsigned char uint8_t;
--- a/include/SDL_config_pandora.h Wed Jan 13 06:39:44 2010 +0000
+++ b/include/SDL_config_pandora.h Wed Jan 13 06:47:17 2010 +0000
@@ -28,6 +28,9 @@
/* General platform specific identifiers */
#include "SDL_platform.h"
+/* SDL internal assertion support */
+#define SDL_ASSERT_LEVEL 1
+
#define SDL_HAS_64BIT_TYPE 1
#define SDL_BYTEORDER 1234
--- a/include/SDL_config_win32.h Wed Jan 13 06:39:44 2010 +0000
+++ b/include/SDL_config_win32.h Wed Jan 13 06:47:17 2010 +0000
@@ -27,6 +27,9 @@
/* This is a set of defines to configure the SDL features */
+/* SDL internal assertion support */
+#define SDL_ASSERT_LEVEL 1
+
#if !defined(_STDINT_H_) && (!defined(HAVE_STDINT_H) || !_HAVE_STDINT_H)
#if defined(__GNUC__) || defined(__DMC__) || defined(__WATCOMC__)
#define HAVE_STDINT_H 1
--- a/include/SDL_config_wiz.h Wed Jan 13 06:39:44 2010 +0000
+++ b/include/SDL_config_wiz.h Wed Jan 13 06:47:17 2010 +0000
@@ -28,6 +28,9 @@
/* General platform specific identifiers */
#include "SDL_platform.h"
+/* SDL internal assertion support */
+#define SDL_ASSERT_LEVEL 1
+
/* Make sure that this isn't included by Visual C++ */
#ifdef _MSC_VER
#error You should copy include/SDL_config.h.default to include/SDL_config.h
--- a/src/SDL.c Wed Jan 13 06:39:44 2010 +0000
+++ b/src/SDL.c Wed Jan 13 06:47:17 2010 +0000
@@ -25,6 +25,8 @@
#include "SDL.h"
#include "SDL_fatal.h"
+#include "SDL_assert.h"
+
#if !SDL_VIDEO_DISABLED
#include "video/SDL_leaks.h"
#endif
@@ -52,6 +54,9 @@
extern int SDL_HelperWindowDestroy(void);
#endif
+extern int SDL_AssertionsInit(void);
+extern void SDL_AssertionsQuit(void);
+
/* The initialized subsystems */
static Uint32 SDL_initialized = 0;
static Uint32 ticks_started = 0;
@@ -153,6 +158,10 @@
}
#endif
+ if (SDL_AssertionsInit() < 0) {
+ return -1;
+ }
+
/* Clear the error message */
SDL_ClearError();
@@ -171,6 +180,21 @@
if (!(flags & SDL_INIT_NOPARACHUTE)) {
SDL_InstallParachute();
}
+
+ /* brief sanity checks for the sanity checks. :) */
+ SDL_assert(1);
+ SDL_assert_release(1);
+ SDL_assert_paranoid(1);
+ SDL_assert(0 || 1);
+ SDL_assert_release(0 || 1);
+ SDL_assert_paranoid(0 || 1);
+
+#if 0 /* enable this to test assertion failures. */
+ SDL_assert_release(1 == 2);
+ SDL_assert_release(5 < 4);
+ SDL_assert_release(0 && "This is a test");
+#endif
+
return (0);
}
@@ -239,6 +263,7 @@
fflush(stdout);
#endif
+ /* !!! FIXME: make this an assertion. */
/* Print the number of surfaces not freed */
if (surfaces_allocated != 0) {
fprintf(stderr, "SDL Warning: %d SDL surfaces extant\n",
@@ -253,6 +278,8 @@
/* Uninstall any parachute signal handlers */
SDL_UninstallParachute();
+ SDL_AssertionsQuit();
+
#if !SDL_THREADS_DISABLED && SDL_THREAD_PTH
pth_kill();
#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/SDL_assert.c Wed Jan 13 06:47:17 2010 +0000
@@ -0,0 +1,396 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2009 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+
+#include "SDL_assert.h"
+#include "SDL.h"
+
+#if (SDL_ASSERT_LEVEL > 0)
+
+#ifdef _WINDOWS
+#define WIN32_LEAN_AND_MEAN 1
+#include <windows.h>
+#else /* fprintf, _exit(), etc. */
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+/* We can keep all triggered assertions in a singly-linked list so we can
+ * generate a report later.
+ */
+#if !SDL_ASSERTION_REPORT_DISABLED
+static SDL_assert_data assertion_list_terminator = { 0, 0, 0, 0, 0, 0, 0 };
+static SDL_assert_data *triggered_assertions = &assertion_list_terminator;
+#endif
+
+static void
+debug_print(const char *fmt, ...)
+//#ifdef __GNUC__
+//__attribute__((format (printf, 1, 2)))
+//#endif
+{
+#ifdef _WINDOWS
+ /* Format into a buffer for OutputDebugStringA(). */
+ char buf[1024];
+ char *startptr;
+ char *ptr;
+ int len;
+ va_list ap;
+ va_start(ap, fmt);
+ len = (int) SDL_vsnprintf(buf, sizeof (buf), fmt, ap);
+ va_end(ap);
+
+ /* Visual C's vsnprintf() may not null-terminate the buffer. */
+ if ((len >= sizeof (buf)) || (len < 0)) {
+ buf[sizeof (buf) - 1] = '\0';
+ }
+
+ /* Write it, sorting out the Unix newlines... */
+ startptr = buf;
+ for (ptr = startptr; *ptr; ptr++) {
+ if (*ptr == '\n') {
+ *ptr = '\0';
+ OutputDebugStringA(startptr);
+ OutputDebugStringA("\r\n");
+ startptr = ptr+1;
+ }
+ }
+
+ /* catch that last piece if it didn't have a newline... */
+ if (startptr != ptr) {
+ OutputDebugStringA(startptr);
+ }
+#else
+ /* Unix has it easy. Just dump it to stderr. */
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, fmt, ap);
+ va_end(ap);
+ fflush(stderr);
+#endif
+}
+
+
+#ifdef _WINDOWS
+static SDL_assert_state SDL_Windows_AssertChoice = SDL_ASSERTION_ABORT;
+static const SDL_assert_data *SDL_Windows_AssertData = NULL;
+
+static LRESULT CALLBACK
+SDL_Assertion_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_CREATE:
+ {
+ /* !!! FIXME: all this code stinks. */
+ const SDL_assert_data *data = SDL_Windows_AssertData;
+ char buf[1024];
+ const int w = 100;
+ const int h = 25;
+ const int gap = 10;
+ int x = gap;
+ int y = 50;
+ int len;
+ int i;
+ static const struct {
+ const char *name;
+ SDL_assert_state state;
+ } buttons[] = {
+ {"Abort", SDL_ASSERTION_ABORT },
+ {"Break", SDL_ASSERTION_BREAK },
+ {"Retry", SDL_ASSERTION_RETRY },
+ {"Ignore", SDL_ASSERTION_IGNORE },
+ {"Always Ignore", SDL_ASSERTION_ALWAYS_IGNORE },
+ };
+
+ len = (int) SDL_snprintf(buf, sizeof (buf),
+ "Assertion failure at %s (%s:%d), triggered %u time%s:\r\n '%s'",
+ data->function, data->filename, data->linenum,
+ data->trigger_count, (data->trigger_count == 1) ? "" : "s",
+ data->condition);
+ if ((len < 0) || (len >= sizeof (buf))) {
+ buf[sizeof (buf) - 1] = '\0';
+ }
+
+ CreateWindowA("STATIC", buf,
+ WS_VISIBLE | WS_CHILD | SS_LEFT,
+ x, y, 550, 100,
+ hwnd, (HMENU) 1, NULL, NULL);
+ y += 110;
+
+ for (i = 0; i < (sizeof (buttons) / sizeof (buttons[0])); i++) {
+ CreateWindowA("BUTTON", buttons[i].name,
+ WS_VISIBLE | WS_CHILD,
+ x, y, w, h,
+ hwnd, (HMENU) buttons[i].state, NULL, NULL);
+ x += w + gap;
+ }
+ break;
+ }
+
+ case WM_COMMAND:
+ SDL_Windows_AssertChoice = ((SDL_assert_state) (LOWORD(wParam)));
+ SDL_Windows_AssertData = NULL;
+ break;
+
+ case WM_DESTROY:
+ SDL_Windows_AssertData = NULL;
+ break;
+ }
+
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+static SDL_assert_state
+SDL_PromptAssertion_windows(const SDL_assert_data *data)
+{
+ HINSTANCE hInstance = 0; /* !!! FIXME? */
+ HWND hwnd;
+ MSG msg;
+ WNDCLASS wc = {0};
+
+ SDL_Windows_AssertChoice = SDL_ASSERTION_ABORT;
+ SDL_Windows_AssertData = data;
+
+ wc.lpszClassName = TEXT("SDL_assert");
+ wc.hInstance = hInstance ;
+ wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
+ wc.lpfnWndProc = SDL_Assertion_WndProc;
+ wc.hCursor = LoadCursor(0, IDC_ARROW);
+
+ RegisterClass(&wc);
+ hwnd = CreateWindow(wc.lpszClassName, TEXT("SDL assertion failure"),
+ WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ 150, 150, 570, 260, 0, 0, hInstance, 0);
+
+ while (GetMessage(&msg, NULL, 0, 0) && (SDL_Windows_AssertData != NULL)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ DestroyWindow(hwnd);
+ UnregisterClass(wc.lpszClassName, hInstance);
+ return SDL_Windows_AssertChoice;
+}
+#endif
+
+
+static void SDL_AddAssertionToReport(SDL_assert_data *data)
+{
+#if !SDL_ASSERTION_REPORT_DISABLED
+ /* (data) is always a static struct defined with the assert macros, so
+ we don't have to worry about copying or allocating them. */
+ if (data->next == NULL) { /* not yet added? */
+ data->next = triggered_assertions;
+ triggered_assertions = data;
+ }
+#endif
+}
+
+static void SDL_GenerateAssertionReport(void)
+{
+#if !SDL_ASSERTION_REPORT_DISABLED
+ if (triggered_assertions != &assertion_list_terminator)
+ {
+ SDL_assert_data *item = triggered_assertions;
+
+ debug_print("\n\nSDL assertion report.\n");
+ debug_print("All SDL assertions between last init/quit:\n\n");
+
+ while (item != &assertion_list_terminator) {
+ debug_print(
+ "'%s'\n"
+ " * %s (%s:%d)\n"
+ " * triggered %u time%s.\n"
+ " * always ignore: %s.\n",
+ item->condition, item->function, item->filename,
+ item->linenum, item->trigger_count,
+ (item->trigger_count == 1) ? "" : "s",
+ item->always_ignore ? "yes" : "no");
+ item = item->next;
+ }
+ debug_print("\n");
+
+ triggered_assertions = &assertion_list_terminator;
+ }
+#endif
+}
+
+
+static void SDL_AbortAssertion(void)
+{
+ SDL_Quit();
+#ifdef _WINDOWS
+ ExitProcess(42);
+#elif unix || __APPLE__
+ _exit(42);
+#else
+ #error Please define your platform or set SDL_ASSERT_LEVEL to 0.
+#endif
+}
+
+
+static SDL_assert_state SDL_PromptAssertion(const SDL_assert_data *data)
+{
+ const char *envr;
+
+ debug_print("\n\n"
+ "Assertion failure at %s (%s:%d), triggered %u time%s:\n"
+ " '%s'\n"
+ "\n",
+ data->function, data->filename, data->linenum,
+ data->trigger_count, (data->trigger_count == 1) ? "" : "s",
+ data->condition);
+
+ /* let env. variable override, so unit tests won't block in a GUI. */
+ envr = SDL_getenv("SDL_ASSERT");
+ if (envr != NULL) {
+ if (SDL_strcmp(envr, "abort") == 0) {
+ return SDL_ASSERTION_ABORT;
+ } else if (SDL_strcmp(envr, "break") == 0) {
+ return SDL_ASSERTION_BREAK;
+ } else if (SDL_strcmp(envr, "retry") == 0) {
+ return SDL_ASSERTION_RETRY;
+ } else if (SDL_strcmp(envr, "ignore") == 0) {
+ return SDL_ASSERTION_IGNORE;
+ } else if (SDL_strcmp(envr, "always_ignore") == 0) {
+ return SDL_ASSERTION_ALWAYS_IGNORE;
+ } else {
+ return SDL_ASSERTION_ABORT; /* oh well. */
+ }
+ }
+
+ /* platform-specific UI... */
+
+#ifdef _WINDOWS
+ return SDL_PromptAssertion_windows(data);
+
+#elif __APPLE__
+ /* This has to be done in an Objective-C (*.m) file, so we call out. */
+ extern SDL_assert_state SDL_PromptAssertion_cocoa(const SDL_assert_data *);
+ return SDL_PromptAssertion_cocoa(data);
+
+#elif unix
+ /* this is a little hacky. */
+ for ( ; ; ) {
+ char buf[32];
+ fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
+ fflush(stderr);
+ if (fgets(buf, sizeof (buf), stdin) == NULL) {
+ return SDL_ASSERTION_ABORT;
+ }
+
+ if (SDL_strcmp(buf, "a") == 0) {
+ return SDL_ASSERTION_ABORT;
+ } else if (SDL_strcmp(envr, "b") == 0) {
+ return SDL_ASSERTION_BREAK;
+ } else if (SDL_strcmp(envr, "r") == 0) {
+ return SDL_ASSERTION_RETRY;
+ } else if (SDL_strcmp(envr, "i") == 0) {
+ return SDL_ASSERTION_IGNORE;
+ } else if (SDL_strcmp(envr, "A") == 0) {
+ return SDL_ASSERTION_ALWAYS_IGNORE;
+ }
+ }
+
+#else
+ #error Please define your platform or set SDL_ASSERT_LEVEL to 0.
+#endif
+
+ return SDL_ASSERTION_ABORT;
+}
+
+
+static SDL_mutex *assertion_mutex = NULL;
+
+SDL_assert_state
+SDL_ReportAssertion(SDL_assert_data *data, const char *func, int line)
+{
+ SDL_assert_state state;
+
+ if (SDL_LockMutex(assertion_mutex) < 0) {
+ return SDL_ASSERTION_IGNORE; /* oh well, I guess. */
+ }
+
+ /* doing this because Visual C is upset over assigning in the macro. */
+ if (data->trigger_count == 0) {
+ data->function = func;
+ data->linenum = line;
+ }
+
+ SDL_AddAssertionToReport(data);
+
+ data->trigger_count++;
+ if (data->always_ignore) {
+ SDL_UnlockMutex(assertion_mutex);
+ return SDL_ASSERTION_IGNORE;
+ }
+
+ state = SDL_PromptAssertion(data);
+
+ switch (state)
+ {
+ case SDL_ASSERTION_ABORT:
+ SDL_UnlockMutex(assertion_mutex); /* in case we assert in quit. */
+ SDL_AbortAssertion();
+ return SDL_ASSERTION_IGNORE; /* shouldn't return, but oh well. */
+
+ case SDL_ASSERTION_ALWAYS_IGNORE:
+ state = SDL_ASSERTION_IGNORE;
+ data->always_ignore = 1;
+ break;
+
+ case SDL_ASSERTION_IGNORE:
+ case SDL_ASSERTION_RETRY:
+ case SDL_ASSERTION_BREAK:
+ break; /* macro handles these. */
+ }
+
+ SDL_UnlockMutex(assertion_mutex);
+
+ return state;
+}
+
+#endif /* SDL_ASSERT_LEVEL > 0 */
+
+
+int SDL_AssertionsInit(void)
+{
+#if (SDL_ASSERT_LEVEL > 0)
+ assertion_mutex = SDL_CreateMutex();
+ if (assertion_mutex == NULL) {
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+void SDL_AssertionsQuit(void)
+{
+#if (SDL_ASSERT_LEVEL > 0)
+ SDL_GenerateAssertionReport();
+ SDL_DestroyMutex(assertion_mutex);
+ assertion_mutex = NULL;
+#endif
+}
+
+/* vi: set ts=4 sw=4 expandtab: */
+
--- a/src/video/cocoa/SDL_cocoavideo.m Wed Jan 13 06:39:44 2010 +0000
+++ b/src/video/cocoa/SDL_cocoavideo.m Wed Jan 13 06:47:17 2010 +0000
@@ -135,4 +135,44 @@
Cocoa_QuitMouse(_this);
}
+
+/*
+ * Mac OS X assertion support.
+ *
+ * This doesn't really have aything to do with the interfaces of the SDL video
+ * subsystem, but we need to stuff this into an Objective-C source code file.
+ */
+
+SDL_assert_state
+SDL_PromptAssertion_cocoa(const SDL_assert_data *data)
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSString *msg = [NSString stringWithFormat:
+ @"Assertion failure at %s (%s:%d), triggered %u time%s:\n '%s'",
+ data->function, data->filename, data->linenum,
+ data->trigger_count, (data->trigger_count == 1) ? "" : "s",
+ data->condition];
+
+ NSLog(msg);
+
+ /*
+ * !!! FIXME: this code needs to deal with fullscreen modes:
+ * !!! FIXME: reset to default desktop, runModal, reset to current?
+ */
+
+ NSAlert* alert = [[NSAlert alloc] init];
+ [alert setAlertStyle:NSCriticalAlertStyle];
+ [alert setMessageText:msg];
+ [alert addButtonWithTitle:@"Retry"];
+ [alert addButtonWithTitle:@"Break"];
+ [alert addButtonWithTitle:@"Abort"];
+ [alert addButtonWithTitle:@"Ignore"];
+ [alert addButtonWithTitle:@"Always Ignore"];
+ const NSInteger clicked = [alert runModal];
+ [pool release];
+ return (SDL_assert_state) (clicked - NSAlertFirstButtonReturn);
+}
+
/* vim: set ts=4 sw=4 expandtab: */
+