First pass at Ryan's assertion code, minor tweaks to come.
authorSam Lantinga <slouken@libsdl.org>
Wed, 13 Jan 2010 06:47:17 +0000
changeset 3647c5925cd41955
parent 3646 88235d40b135
child 3648 a9d830c05998
First pass at Ryan's assertion code, minor tweaks to come.
Makefile.ds
configure.in
include/SDL.h
include/SDL_assert.h
include/SDL_config.h.in
include/SDL_config_iphoneos.h
include/SDL_config_macosx.h
include/SDL_config_minimal.h
include/SDL_config_nintendods.h
include/SDL_config_pandora.h
include/SDL_config_win32.h
include/SDL_config_wiz.h
src/SDL.c
src/SDL_assert.c
src/video/cocoa/SDL_cocoavideo.m
     1.1 --- a/Makefile.ds	Wed Jan 13 06:39:44 2010 +0000
     1.2 +++ b/Makefile.ds	Wed Jan 13 06:47:17 2010 +0000
     1.3 @@ -36,6 +36,7 @@
     1.4  src/SDL_compat.c \
     1.5  src/SDL_error.c \
     1.6  src/SDL_fatal.c \
     1.7 +src/SDL_assert.c \
     1.8  src/audio/nds/SDL_ndsaudio.c \
     1.9  src/audio/SDL_audio.c \
    1.10  src/audio/SDL_audiocvt.c \
     2.1 --- a/configure.in	Wed Jan 13 06:39:44 2010 +0000
     2.2 +++ b/configure.in	Wed Jan 13 06:47:17 2010 +0000
     2.3 @@ -134,6 +134,32 @@
     2.4  AC_C_INLINE
     2.5  AC_C_VOLATILE
     2.6  
     2.7 +dnl See whether we want assertions for debugging/sanity checking SDL itself.
     2.8 +AC_ARG_ENABLE(assertions,
     2.9 +AC_HELP_STRING([--enable-assertions],
    2.10 +               [Enable internal sanity checks (yes/no/release/paranoid) [[default=release]]]),
    2.11 +              , enable_assertions=release)
    2.12 +sdl_valid_assertion_level=no
    2.13 +if test x$enable_assertions = xno; then
    2.14 +    sdl_valid_assertion_level=yes
    2.15 +    AC_DEFINE(SDL_ASSERT_LEVEL, 0)
    2.16 +fi
    2.17 +if test x$enable_assertions = xrelease; then
    2.18 +    sdl_valid_assertion_level=yes
    2.19 +    AC_DEFINE(SDL_ASSERT_LEVEL, 1)
    2.20 +fi
    2.21 +if test x$enable_assertions = xyes; then
    2.22 +    sdl_valid_assertion_level=yes
    2.23 +    AC_DEFINE(SDL_ASSERT_LEVEL, 2)
    2.24 +fi
    2.25 +if test x$enable_assertions = xparanoid; then
    2.26 +    sdl_valid_assertion_level=yes
    2.27 +    AC_DEFINE(SDL_ASSERT_LEVEL, 3)
    2.28 +fi
    2.29 +if test x$sdl_valid_assertion_level = xno; then
    2.30 +    AC_MSG_ERROR([*** unknown assertion level. stop.])
    2.31 +fi
    2.32 +
    2.33  dnl See whether we can use gcc style dependency tracking
    2.34  AC_ARG_ENABLE(dependency-tracking,
    2.35  AC_HELP_STRING([--enable-dependency-tracking],
     3.1 --- a/include/SDL.h	Wed Jan 13 06:39:44 2010 +0000
     3.2 +++ b/include/SDL.h	Wed Jan 13 06:47:17 2010 +0000
     3.3 @@ -77,6 +77,7 @@
     3.4  
     3.5  #include "SDL_main.h"
     3.6  #include "SDL_stdinc.h"
     3.7 +#include "SDL_assert.h"
     3.8  #include "SDL_atomic.h"
     3.9  #include "SDL_audio.h"
    3.10  #include "SDL_cpuinfo.h"
    3.11 @@ -89,8 +90,8 @@
    3.12  #include "SDL_rwops.h"
    3.13  #include "SDL_thread.h"
    3.14  #include "SDL_timer.h"
    3.15 +#include "SDL_version.h"
    3.16  #include "SDL_video.h"
    3.17 -#include "SDL_version.h"
    3.18  #include "SDL_compat.h"
    3.19  
    3.20  #include "begin_code.h"
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/include/SDL_assert.h	Wed Jan 13 06:47:17 2010 +0000
     4.3 @@ -0,0 +1,151 @@
     4.4 +/*
     4.5 +    SDL - Simple DirectMedia Layer
     4.6 +    Copyright (C) 1997-2009 Sam Lantinga
     4.7 +
     4.8 +    This library is free software; you can redistribute it and/or
     4.9 +    modify it under the terms of the GNU Lesser General Public
    4.10 +    License as published by the Free Software Foundation; either
    4.11 +    version 2.1 of the License, or (at your option) any later version.
    4.12 +
    4.13 +    This library is distributed in the hope that it will be useful,
    4.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    4.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    4.16 +    Lesser General Public License for more details.
    4.17 +
    4.18 +    You should have received a copy of the GNU Lesser General Public
    4.19 +    License along with this library; if not, write to the Free Software
    4.20 +    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    4.21 +
    4.22 +    Sam Lantinga
    4.23 +    slouken@libsdl.org
    4.24 +*/
    4.25 +#include "SDL_config.h"
    4.26 +
    4.27 +/* This is an assert macro for SDL's internal use. Not for the public API! */
    4.28 +
    4.29 +#ifndef _SDL_assert_h
    4.30 +#define _SDL_assert_h
    4.31 +
    4.32 +#ifndef SDL_ASSERT_LEVEL
    4.33 +#error SDL_ASSERT_LEVEL is not defined. Please fix your SDL_config.h.
    4.34 +#endif
    4.35 +
    4.36 +/*
    4.37 +sizeof (x) makes the compiler still parse the expression even without
    4.38 +assertions enabled, so the code is always checked at compile time, but
    4.39 +doesn't actually generate code for it, so there are no side effects or
    4.40 +expensive checks at run time, just the constant size of what x WOULD be,
    4.41 +which presumably gets optimized out as unused.
    4.42 +This also solves the problem of...
    4.43 +
    4.44 +    int somevalue = blah();
    4.45 +    SDL_assert(somevalue == 1);
    4.46 +
    4.47 +...which would cause compiles to complain that somevalue is unused if we
    4.48 +disable assertions.
    4.49 +*/
    4.50 +
    4.51 +#define SDL_disabled_assert(condition) \
    4.52 +    do { (void) sizeof ((condition)); } while (0)
    4.53 +
    4.54 +#if (SDL_ASSERT_LEVEL > 0)
    4.55 +
    4.56 +/*
    4.57 +These are macros and not first class functions so that the debugger breaks
    4.58 +on the assertion line and not in some random guts of SDL, and so each
    4.59 +macro can have unique static variables associated with it.
    4.60 +*/
    4.61 +
    4.62 +#if (defined(_MSC_VER) && ((_M_IX86) || (_M_X64)))
    4.63 +    #define SDL_TriggerBreakpoint() __asm { int 3 }
    4.64 +#elif (defined(__GNUC__) && ((__i386__) || (__x86_64__)))
    4.65 +    #define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "int $3\n\t" )
    4.66 +#elif defined(unix)
    4.67 +    #include <signal.h>
    4.68 +    #define SDL_TriggerBreakpoint() raise(SIGTRAP)
    4.69 +#else
    4.70 +    #error Please define your platform or set SDL_ASSERT_LEVEL to 0.
    4.71 +#endif
    4.72 +
    4.73 +#if (__STDC_VERSION__ >= 199901L) /* C99 supports __func__ as a standard. */
    4.74 +#   define SDL_FUNCTION __func__
    4.75 +#elif ((__GNUC__ >= 2) || defined(_MSC_VER))
    4.76 +#   define SDL_FUNCTION __FUNCTION__
    4.77 +#else
    4.78 +#   define SDL_FUNCTION "???"
    4.79 +#endif
    4.80 +
    4.81 +typedef enum
    4.82 +{
    4.83 +    SDL_ASSERTION_RETRY,  /**< Retry the assert immediately. */
    4.84 +    SDL_ASSERTION_BREAK,  /**< Make the debugger trigger a breakpoint. */
    4.85 +    SDL_ASSERTION_ABORT,  /**< Terminate the program. */
    4.86 +    SDL_ASSERTION_IGNORE,  /**< Ignore the assert. */
    4.87 +    SDL_ASSERTION_ALWAYS_IGNORE,  /**< Ignore the assert from now on. */
    4.88 +} SDL_assert_state;
    4.89 +
    4.90 +typedef struct SDL_assert_data
    4.91 +{
    4.92 +    int always_ignore;
    4.93 +    unsigned int trigger_count;
    4.94 +    const char *condition;
    4.95 +    const char *filename;
    4.96 +    int linenum;
    4.97 +    const char *function;
    4.98 +    struct SDL_assert_data *next;
    4.99 +} SDL_assert_data;
   4.100 +
   4.101 +SDL_assert_state SDL_ReportAssertion(SDL_assert_data *, const char *, int);
   4.102 +
   4.103 +/* the do {} while(0) avoids dangling else problems:
   4.104 +    if (x) SDL_assert(y); else blah();
   4.105 +       ... without the do/while, the "else" could attach to this macro's "if".
   4.106 +   We try to handle just the minimum we need here in a macro...the loop,
   4.107 +   the static vars, and break points. The heavy lifting is handled in
   4.108 +   SDL_ReportAssertion(), in SDL_assert.c.
   4.109 +*/
   4.110 +#define SDL_enabled_assert(condition) \
   4.111 +    do { \
   4.112 +        while ( !(condition) ) { \
   4.113 +			static struct SDL_assert_data assert_data = { \
   4.114 +                0, 0, #condition, __FILE__, 0, 0, 0 \
   4.115 +            }; \
   4.116 +			const SDL_assert_state state = SDL_ReportAssertion(&assert_data, \
   4.117 +                                                               SDL_FUNCTION, \
   4.118 +															   __LINE__); \
   4.119 +            if (state == SDL_ASSERTION_RETRY) { \
   4.120 +                continue; /* go again. */ \
   4.121 +            } else if (state == SDL_ASSERTION_BREAK) { \
   4.122 +                SDL_TriggerBreakpoint(); \
   4.123 +            } \
   4.124 +            break; /* not retrying. */ \
   4.125 +        } \
   4.126 +    } while (0)
   4.127 +
   4.128 +#endif  /* enabled assertions support code */
   4.129 +
   4.130 +/* Enable various levels of assertions. */
   4.131 +#if SDL_ASSERT_LEVEL == 0   /* assertions disabled */
   4.132 +#   define SDL_assert(condition) SDL_disabled_assert(condition)
   4.133 +#   define SDL_assert_release(condition) SDL_disabled_assert(condition)
   4.134 +#   define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
   4.135 +#elif SDL_ASSERT_LEVEL == 1  /* release settings. */
   4.136 +#   define SDL_assert(condition) SDL_enabled_assert(condition)
   4.137 +#   define SDL_assert_release(condition) SDL_enabled_assert(condition)
   4.138 +#   define SDL_assert_paranoid(condition) SDL_enabled_assert(condition)
   4.139 +#elif SDL_ASSERT_LEVEL == 2  /* normal settings. */
   4.140 +#   define SDL_assert(condition) SDL_enabled_assert(condition)
   4.141 +#   define SDL_assert_release(condition) SDL_enabled_assert(condition)
   4.142 +#   define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
   4.143 +#elif SDL_ASSERT_LEVEL == 3  /* paranoid settings. */
   4.144 +#   define SDL_assert(condition) SDL_enabled_assert(condition)
   4.145 +#   define SDL_assert_release(condition) SDL_enabled_assert(condition)
   4.146 +#   define SDL_assert_paranoid(condition) SDL_enabled_assert(condition)
   4.147 +#else
   4.148 +#   error Unknown assertion level. Please fix your SDL_config.h.
   4.149 +#endif
   4.150 +
   4.151 +#endif /* _SDL_assert_h */
   4.152 +
   4.153 +/* vi: set ts=4 sw=4 expandtab: */
   4.154 +
     5.1 --- a/include/SDL_config.h.in	Wed Jan 13 06:39:44 2010 +0000
     5.2 +++ b/include/SDL_config.h.in	Wed Jan 13 06:47:17 2010 +0000
     5.3 @@ -162,6 +162,9 @@
     5.4  #include <stdarg.h>
     5.5  #endif /* HAVE_LIBC */
     5.6  
     5.7 +/* SDL internal assertion support */
     5.8 +#undef SDL_ASSERT_LEVEL
     5.9 +
    5.10  /* Allow disabling of core subsystems */
    5.11  #undef SDL_AUDIO_DISABLED
    5.12  #undef SDL_CPUINFO_DISABLED
     6.1 --- a/include/SDL_config_iphoneos.h	Wed Jan 13 06:39:44 2010 +0000
     6.2 +++ b/include/SDL_config_iphoneos.h	Wed Jan 13 06:47:17 2010 +0000
     6.3 @@ -25,6 +25,9 @@
     6.4  
     6.5  #include "SDL_platform.h"
     6.6  
     6.7 +/* SDL internal assertion support */
     6.8 +#define SDL_ASSERT_LEVEL 1
     6.9 +
    6.10  #if !defined(_STDINT_H_) && (!defined(HAVE_STDINT_H) || !_HAVE_STDINT_H)
    6.11  typedef signed char int8_t;
    6.12  typedef unsigned char uint8_t;
     7.1 --- a/include/SDL_config_macosx.h	Wed Jan 13 06:39:44 2010 +0000
     7.2 +++ b/include/SDL_config_macosx.h	Wed Jan 13 06:47:17 2010 +0000
     7.3 @@ -28,6 +28,9 @@
     7.4  /* This gets us MAC_OS_X_VERSION_MIN_REQUIRED... */
     7.5  #include <AvailabilityMacros.h>
     7.6  
     7.7 +/* SDL internal assertion support */
     7.8 +#define SDL_ASSERT_LEVEL 1
     7.9 +
    7.10  /* This is a set of defines to configure the SDL features */
    7.11  
    7.12  #ifdef __LP64__
     8.1 --- a/include/SDL_config_minimal.h	Wed Jan 13 06:39:44 2010 +0000
     8.2 +++ b/include/SDL_config_minimal.h	Wed Jan 13 06:47:17 2010 +0000
     8.3 @@ -33,6 +33,9 @@
     8.4  
     8.5  #include <stdarg.h>
     8.6  
     8.7 +/* SDL internal assertion support */
     8.8 +#define SDL_ASSERT_LEVEL 1
     8.9 +
    8.10  #if !defined(_STDINT_H_) && (!defined(HAVE_STDINT_H) || !_HAVE_STDINT_H)
    8.11  typedef signed char int8_t;
    8.12  typedef unsigned char uint8_t;
     9.1 --- a/include/SDL_config_nintendods.h	Wed Jan 13 06:39:44 2010 +0000
     9.2 +++ b/include/SDL_config_nintendods.h	Wed Jan 13 06:47:17 2010 +0000
     9.3 @@ -27,6 +27,9 @@
     9.4  
     9.5  /* This is a set of defines to configure the SDL features */
     9.6  
     9.7 +/* SDL internal assertion support */
     9.8 +#define SDL_ASSERT_LEVEL 1
     9.9 +
    9.10  #if !defined(_STDINT_H_) && (!defined(HAVE_STDINT_H) || !_HAVE_STDINT_H)
    9.11  typedef signed char int8_t;
    9.12  typedef unsigned char uint8_t;
    10.1 --- a/include/SDL_config_pandora.h	Wed Jan 13 06:39:44 2010 +0000
    10.2 +++ b/include/SDL_config_pandora.h	Wed Jan 13 06:47:17 2010 +0000
    10.3 @@ -28,6 +28,9 @@
    10.4  /* General platform specific identifiers */
    10.5  #include "SDL_platform.h"
    10.6  
    10.7 +/* SDL internal assertion support */
    10.8 +#define SDL_ASSERT_LEVEL 1
    10.9 +
   10.10  #define SDL_HAS_64BIT_TYPE 1
   10.11  #define SDL_BYTEORDER 1234
   10.12  
    11.1 --- a/include/SDL_config_win32.h	Wed Jan 13 06:39:44 2010 +0000
    11.2 +++ b/include/SDL_config_win32.h	Wed Jan 13 06:47:17 2010 +0000
    11.3 @@ -27,6 +27,9 @@
    11.4  
    11.5  /* This is a set of defines to configure the SDL features */
    11.6  
    11.7 +/* SDL internal assertion support */
    11.8 +#define SDL_ASSERT_LEVEL 1
    11.9 +
   11.10  #if !defined(_STDINT_H_) && (!defined(HAVE_STDINT_H) || !_HAVE_STDINT_H)
   11.11  #if defined(__GNUC__) || defined(__DMC__) || defined(__WATCOMC__)
   11.12  #define HAVE_STDINT_H	1
    12.1 --- a/include/SDL_config_wiz.h	Wed Jan 13 06:39:44 2010 +0000
    12.2 +++ b/include/SDL_config_wiz.h	Wed Jan 13 06:47:17 2010 +0000
    12.3 @@ -28,6 +28,9 @@
    12.4  /* General platform specific identifiers */
    12.5  #include "SDL_platform.h"
    12.6  
    12.7 +/* SDL internal assertion support */
    12.8 +#define SDL_ASSERT_LEVEL 1
    12.9 +
   12.10  /* Make sure that this isn't included by Visual C++ */
   12.11  #ifdef _MSC_VER
   12.12  #error You should copy include/SDL_config.h.default to include/SDL_config.h
    13.1 --- a/src/SDL.c	Wed Jan 13 06:39:44 2010 +0000
    13.2 +++ b/src/SDL.c	Wed Jan 13 06:47:17 2010 +0000
    13.3 @@ -25,6 +25,8 @@
    13.4  
    13.5  #include "SDL.h"
    13.6  #include "SDL_fatal.h"
    13.7 +#include "SDL_assert.h"
    13.8 +
    13.9  #if !SDL_VIDEO_DISABLED
   13.10  #include "video/SDL_leaks.h"
   13.11  #endif
   13.12 @@ -52,6 +54,9 @@
   13.13  extern int SDL_HelperWindowDestroy(void);
   13.14  #endif
   13.15  
   13.16 +extern int SDL_AssertionsInit(void);
   13.17 +extern void SDL_AssertionsQuit(void);
   13.18 +
   13.19  /* The initialized subsystems */
   13.20  static Uint32 SDL_initialized = 0;
   13.21  static Uint32 ticks_started = 0;
   13.22 @@ -153,6 +158,10 @@
   13.23      }
   13.24  #endif
   13.25  
   13.26 +    if (SDL_AssertionsInit() < 0) {
   13.27 +        return -1;
   13.28 +    }
   13.29 +
   13.30      /* Clear the error message */
   13.31      SDL_ClearError();
   13.32  
   13.33 @@ -171,6 +180,21 @@
   13.34      if (!(flags & SDL_INIT_NOPARACHUTE)) {
   13.35          SDL_InstallParachute();
   13.36      }
   13.37 +
   13.38 +    /* brief sanity checks for the sanity checks.  :)  */
   13.39 +    SDL_assert(1);
   13.40 +    SDL_assert_release(1);
   13.41 +    SDL_assert_paranoid(1);
   13.42 +    SDL_assert(0 || 1);
   13.43 +    SDL_assert_release(0 || 1);
   13.44 +    SDL_assert_paranoid(0 || 1);
   13.45 +
   13.46 +#if 0   /* enable this to test assertion failures. */
   13.47 +    SDL_assert_release(1 == 2);
   13.48 +    SDL_assert_release(5 < 4);
   13.49 +    SDL_assert_release(0 && "This is a test");
   13.50 +#endif
   13.51 +
   13.52      return (0);
   13.53  }
   13.54  
   13.55 @@ -239,6 +263,7 @@
   13.56      fflush(stdout);
   13.57  #endif
   13.58  
   13.59 +    /* !!! FIXME: make this an assertion. */
   13.60      /* Print the number of surfaces not freed */
   13.61      if (surfaces_allocated != 0) {
   13.62          fprintf(stderr, "SDL Warning: %d SDL surfaces extant\n",
   13.63 @@ -253,6 +278,8 @@
   13.64      /* Uninstall any parachute signal handlers */
   13.65      SDL_UninstallParachute();
   13.66  
   13.67 +    SDL_AssertionsQuit();
   13.68 +
   13.69  #if !SDL_THREADS_DISABLED && SDL_THREAD_PTH
   13.70      pth_kill();
   13.71  #endif
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/src/SDL_assert.c	Wed Jan 13 06:47:17 2010 +0000
    14.3 @@ -0,0 +1,396 @@
    14.4 +/*
    14.5 +    SDL - Simple DirectMedia Layer
    14.6 +    Copyright (C) 1997-2009 Sam Lantinga
    14.7 +
    14.8 +    This library is free software; you can redistribute it and/or
    14.9 +    modify it under the terms of the GNU Lesser General Public
   14.10 +    License as published by the Free Software Foundation; either
   14.11 +    version 2.1 of the License, or (at your option) any later version.
   14.12 +
   14.13 +    This library is distributed in the hope that it will be useful,
   14.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
   14.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   14.16 +    Lesser General Public License for more details.
   14.17 +
   14.18 +    You should have received a copy of the GNU Lesser General Public
   14.19 +    License along with this library; if not, write to the Free Software
   14.20 +    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
   14.21 +
   14.22 +    Sam Lantinga
   14.23 +    slouken@libsdl.org
   14.24 +*/
   14.25 +
   14.26 +#include "SDL_assert.h"
   14.27 +#include "SDL.h"
   14.28 +
   14.29 +#if (SDL_ASSERT_LEVEL > 0)
   14.30 +
   14.31 +#ifdef _WINDOWS
   14.32 +#define WIN32_LEAN_AND_MEAN 1
   14.33 +#include <windows.h>
   14.34 +#else  /* fprintf, _exit(), etc. */
   14.35 +#include <stdio.h>
   14.36 +#include <stdlib.h>
   14.37 +#endif
   14.38 +
   14.39 +/* We can keep all triggered assertions in a singly-linked list so we can
   14.40 + *  generate a report later.
   14.41 + */
   14.42 +#if !SDL_ASSERTION_REPORT_DISABLED
   14.43 +static SDL_assert_data assertion_list_terminator = { 0, 0, 0, 0, 0, 0, 0 };
   14.44 +static SDL_assert_data *triggered_assertions = &assertion_list_terminator;
   14.45 +#endif
   14.46 +
   14.47 +static void 
   14.48 +debug_print(const char *fmt, ...)
   14.49 +//#ifdef __GNUC__
   14.50 +//__attribute__((format (printf, 1, 2)))
   14.51 +//#endif
   14.52 +{
   14.53 +#ifdef _WINDOWS
   14.54 +    /* Format into a buffer for OutputDebugStringA(). */
   14.55 +    char buf[1024];
   14.56 +    char *startptr;
   14.57 +    char *ptr;
   14.58 +    int len;
   14.59 +    va_list ap;
   14.60 +    va_start(ap, fmt);
   14.61 +    len = (int) SDL_vsnprintf(buf, sizeof (buf), fmt, ap);
   14.62 +    va_end(ap);
   14.63 +
   14.64 +    /* Visual C's vsnprintf() may not null-terminate the buffer. */
   14.65 +    if ((len >= sizeof (buf)) || (len < 0)) {
   14.66 +        buf[sizeof (buf) - 1] = '\0';
   14.67 +    }
   14.68 +
   14.69 +    /* Write it, sorting out the Unix newlines... */
   14.70 +    startptr = buf;
   14.71 +    for (ptr = startptr; *ptr; ptr++) {
   14.72 +        if (*ptr == '\n') {
   14.73 +            *ptr = '\0';
   14.74 +            OutputDebugStringA(startptr);
   14.75 +            OutputDebugStringA("\r\n");
   14.76 +            startptr = ptr+1;
   14.77 +        }
   14.78 +    }
   14.79 +
   14.80 +    /* catch that last piece if it didn't have a newline... */
   14.81 +    if (startptr != ptr) {
   14.82 +        OutputDebugStringA(startptr);
   14.83 +    }
   14.84 +#else
   14.85 +    /* Unix has it easy. Just dump it to stderr. */
   14.86 +    va_list ap;
   14.87 +    va_start(ap, fmt);
   14.88 +    fprintf(stderr, fmt, ap);
   14.89 +    va_end(ap);
   14.90 +    fflush(stderr);
   14.91 +#endif
   14.92 +}
   14.93 +
   14.94 +
   14.95 +#ifdef _WINDOWS
   14.96 +static SDL_assert_state SDL_Windows_AssertChoice = SDL_ASSERTION_ABORT;
   14.97 +static const SDL_assert_data *SDL_Windows_AssertData = NULL;
   14.98 +
   14.99 +static LRESULT CALLBACK
  14.100 +SDL_Assertion_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  14.101 +{
  14.102 +    switch (msg)
  14.103 +    {
  14.104 +        case WM_CREATE:
  14.105 +        {
  14.106 +            /* !!! FIXME: all this code stinks. */
  14.107 +            const SDL_assert_data *data = SDL_Windows_AssertData;
  14.108 +            char buf[1024];
  14.109 +            const int w = 100;
  14.110 +            const int h = 25;
  14.111 +            const int gap = 10;
  14.112 +            int x = gap;
  14.113 +            int y = 50;
  14.114 +            int len;
  14.115 +            int i;
  14.116 +            static const struct { 
  14.117 +                const char *name;
  14.118 +                SDL_assert_state state;
  14.119 +            } buttons[] = {
  14.120 +                {"Abort", SDL_ASSERTION_ABORT },
  14.121 +                {"Break", SDL_ASSERTION_BREAK },
  14.122 +                {"Retry", SDL_ASSERTION_RETRY },
  14.123 +                {"Ignore", SDL_ASSERTION_IGNORE },
  14.124 +                {"Always Ignore", SDL_ASSERTION_ALWAYS_IGNORE },
  14.125 +            };
  14.126 +
  14.127 +            len = (int) SDL_snprintf(buf, sizeof (buf), 
  14.128 +                         "Assertion failure at %s (%s:%d), triggered %u time%s:\r\n  '%s'",
  14.129 +                         data->function, data->filename, data->linenum,
  14.130 +                         data->trigger_count, (data->trigger_count == 1) ? "" : "s",
  14.131 +                         data->condition);
  14.132 +            if ((len < 0) || (len >= sizeof (buf))) {
  14.133 +                buf[sizeof (buf) - 1] = '\0';
  14.134 +            }
  14.135 +
  14.136 +            CreateWindowA("STATIC", buf,
  14.137 +                         WS_VISIBLE | WS_CHILD | SS_LEFT,
  14.138 +                         x, y, 550, 100,
  14.139 +                         hwnd, (HMENU) 1, NULL, NULL);
  14.140 +            y += 110;
  14.141 +
  14.142 +            for (i = 0; i < (sizeof (buttons) / sizeof (buttons[0])); i++) {
  14.143 +                CreateWindowA("BUTTON", buttons[i].name,
  14.144 +                         WS_VISIBLE | WS_CHILD,
  14.145 +                         x, y, w, h,
  14.146 +                         hwnd, (HMENU) buttons[i].state, NULL, NULL);
  14.147 +                x += w + gap;
  14.148 +            }
  14.149 +            break;
  14.150 +        }
  14.151 +
  14.152 +        case WM_COMMAND:
  14.153 +            SDL_Windows_AssertChoice = ((SDL_assert_state) (LOWORD(wParam)));
  14.154 +            SDL_Windows_AssertData = NULL;
  14.155 +            break;
  14.156 +
  14.157 +        case WM_DESTROY:
  14.158 +            SDL_Windows_AssertData = NULL;
  14.159 +            break;
  14.160 +    }
  14.161 +
  14.162 +    return DefWindowProc(hwnd, msg, wParam, lParam);
  14.163 +}
  14.164 +
  14.165 +static SDL_assert_state
  14.166 +SDL_PromptAssertion_windows(const SDL_assert_data *data)
  14.167 +{
  14.168 +    HINSTANCE hInstance = 0;  /* !!! FIXME? */
  14.169 +    HWND hwnd;
  14.170 +    MSG msg;
  14.171 +    WNDCLASS wc = {0};
  14.172 +
  14.173 +    SDL_Windows_AssertChoice = SDL_ASSERTION_ABORT;
  14.174 +    SDL_Windows_AssertData = data;
  14.175 +
  14.176 +    wc.lpszClassName = TEXT("SDL_assert");
  14.177 +    wc.hInstance = hInstance ;
  14.178 +    wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  14.179 +    wc.lpfnWndProc = SDL_Assertion_WndProc;
  14.180 +    wc.hCursor = LoadCursor(0, IDC_ARROW);
  14.181 +  
  14.182 +    RegisterClass(&wc);
  14.183 +    hwnd = CreateWindow(wc.lpszClassName, TEXT("SDL assertion failure"),
  14.184 +                 WS_OVERLAPPEDWINDOW | WS_VISIBLE,
  14.185 +                 150, 150, 570, 260, 0, 0, hInstance, 0);  
  14.186 +
  14.187 +    while (GetMessage(&msg, NULL, 0, 0) && (SDL_Windows_AssertData != NULL)) {
  14.188 +        TranslateMessage(&msg);
  14.189 +        DispatchMessage(&msg);
  14.190 +    }
  14.191 +
  14.192 +    DestroyWindow(hwnd);
  14.193 +    UnregisterClass(wc.lpszClassName, hInstance);
  14.194 +    return SDL_Windows_AssertChoice;
  14.195 +}
  14.196 +#endif
  14.197 +
  14.198 +
  14.199 +static void SDL_AddAssertionToReport(SDL_assert_data *data)
  14.200 +{
  14.201 +#if !SDL_ASSERTION_REPORT_DISABLED
  14.202 +    /* (data) is always a static struct defined with the assert macros, so
  14.203 +       we don't have to worry about copying or allocating them. */
  14.204 +    if (data->next == NULL) {  /* not yet added? */
  14.205 +        data->next = triggered_assertions;
  14.206 +        triggered_assertions = data;
  14.207 +    }
  14.208 +#endif
  14.209 +}
  14.210 +
  14.211 +static void SDL_GenerateAssertionReport(void)
  14.212 +{
  14.213 +#if !SDL_ASSERTION_REPORT_DISABLED
  14.214 +    if (triggered_assertions != &assertion_list_terminator)
  14.215 +    {
  14.216 +        SDL_assert_data *item = triggered_assertions;
  14.217 +
  14.218 +        debug_print("\n\nSDL assertion report.\n");
  14.219 +        debug_print("All SDL assertions between last init/quit:\n\n");
  14.220 +
  14.221 +        while (item != &assertion_list_terminator) {
  14.222 +            debug_print(
  14.223 +                "'%s'\n"
  14.224 +                "    * %s (%s:%d)\n"
  14.225 +                "    * triggered %u time%s.\n"
  14.226 +                "    * always ignore: %s.\n",
  14.227 +                item->condition, item->function, item->filename,
  14.228 +                item->linenum, item->trigger_count,
  14.229 +                (item->trigger_count == 1) ? "" : "s",
  14.230 +                item->always_ignore ? "yes" : "no");
  14.231 +            item = item->next;
  14.232 +        }
  14.233 +        debug_print("\n");
  14.234 +
  14.235 +        triggered_assertions = &assertion_list_terminator;
  14.236 +    }
  14.237 +#endif
  14.238 +}
  14.239 +
  14.240 +
  14.241 +static void SDL_AbortAssertion(void)
  14.242 +{
  14.243 +    SDL_Quit();
  14.244 +#ifdef _WINDOWS
  14.245 +    ExitProcess(42);
  14.246 +#elif unix || __APPLE__
  14.247 +    _exit(42);
  14.248 +#else
  14.249 +    #error Please define your platform or set SDL_ASSERT_LEVEL to 0.
  14.250 +#endif
  14.251 +}
  14.252 +    
  14.253 +
  14.254 +static SDL_assert_state SDL_PromptAssertion(const SDL_assert_data *data)
  14.255 +{
  14.256 +    const char *envr;
  14.257 +
  14.258 +    debug_print("\n\n"
  14.259 +                "Assertion failure at %s (%s:%d), triggered %u time%s:\n"
  14.260 +                "  '%s'\n"
  14.261 +                "\n",
  14.262 +                data->function, data->filename, data->linenum,
  14.263 +                data->trigger_count, (data->trigger_count == 1) ? "" : "s",
  14.264 +                data->condition);
  14.265 +
  14.266 +	/* let env. variable override, so unit tests won't block in a GUI. */
  14.267 +    envr = SDL_getenv("SDL_ASSERT");
  14.268 +    if (envr != NULL) {
  14.269 +        if (SDL_strcmp(envr, "abort") == 0) {
  14.270 +            return SDL_ASSERTION_ABORT;
  14.271 +        } else if (SDL_strcmp(envr, "break") == 0) {
  14.272 +            return SDL_ASSERTION_BREAK;
  14.273 +        } else if (SDL_strcmp(envr, "retry") == 0) {
  14.274 +            return SDL_ASSERTION_RETRY;
  14.275 +        } else if (SDL_strcmp(envr, "ignore") == 0) {
  14.276 +            return SDL_ASSERTION_IGNORE;
  14.277 +        } else if (SDL_strcmp(envr, "always_ignore") == 0) {
  14.278 +            return SDL_ASSERTION_ALWAYS_IGNORE;
  14.279 +        } else {
  14.280 +            return SDL_ASSERTION_ABORT;  /* oh well. */
  14.281 +        }
  14.282 +    }
  14.283 +
  14.284 +    /* platform-specific UI... */
  14.285 +
  14.286 +#ifdef _WINDOWS
  14.287 +    return SDL_PromptAssertion_windows(data);
  14.288 +
  14.289 +#elif __APPLE__
  14.290 +    /* This has to be done in an Objective-C (*.m) file, so we call out. */
  14.291 +    extern SDL_assert_state SDL_PromptAssertion_cocoa(const SDL_assert_data *);
  14.292 +    return SDL_PromptAssertion_cocoa(data);
  14.293 +
  14.294 +#elif unix
  14.295 +    /* this is a little hacky. */
  14.296 +    for ( ; ; ) {
  14.297 +        char buf[32];
  14.298 +        fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
  14.299 +        fflush(stderr);
  14.300 +        if (fgets(buf, sizeof (buf), stdin) == NULL) {
  14.301 +            return SDL_ASSERTION_ABORT;
  14.302 +        }
  14.303 +
  14.304 +        if (SDL_strcmp(buf, "a") == 0) {
  14.305 +            return SDL_ASSERTION_ABORT;
  14.306 +        } else if (SDL_strcmp(envr, "b") == 0) {
  14.307 +            return SDL_ASSERTION_BREAK;
  14.308 +        } else if (SDL_strcmp(envr, "r") == 0) {
  14.309 +            return SDL_ASSERTION_RETRY;
  14.310 +        } else if (SDL_strcmp(envr, "i") == 0) {
  14.311 +            return SDL_ASSERTION_IGNORE;
  14.312 +        } else if (SDL_strcmp(envr, "A") == 0) {
  14.313 +            return SDL_ASSERTION_ALWAYS_IGNORE;
  14.314 +        }
  14.315 +    }
  14.316 +
  14.317 +#else
  14.318 +    #error Please define your platform or set SDL_ASSERT_LEVEL to 0.
  14.319 +#endif
  14.320 +
  14.321 +    return SDL_ASSERTION_ABORT;
  14.322 +}
  14.323 +
  14.324 +
  14.325 +static SDL_mutex *assertion_mutex = NULL;
  14.326 +
  14.327 +SDL_assert_state
  14.328 +SDL_ReportAssertion(SDL_assert_data *data, const char *func, int line)
  14.329 +{
  14.330 +    SDL_assert_state state;
  14.331 +
  14.332 +    if (SDL_LockMutex(assertion_mutex) < 0) {
  14.333 +        return SDL_ASSERTION_IGNORE;   /* oh well, I guess. */
  14.334 +    }
  14.335 +
  14.336 +    /* doing this because Visual C is upset over assigning in the macro. */
  14.337 +    if (data->trigger_count == 0) {
  14.338 +        data->function = func;
  14.339 +		data->linenum = line;
  14.340 +    }
  14.341 +
  14.342 +    SDL_AddAssertionToReport(data);
  14.343 +
  14.344 +    data->trigger_count++;
  14.345 +    if (data->always_ignore) {
  14.346 +        SDL_UnlockMutex(assertion_mutex);
  14.347 +        return SDL_ASSERTION_IGNORE;
  14.348 +    }
  14.349 +
  14.350 +    state = SDL_PromptAssertion(data);
  14.351 +
  14.352 +    switch (state)
  14.353 +    {
  14.354 +        case SDL_ASSERTION_ABORT:
  14.355 +            SDL_UnlockMutex(assertion_mutex);  /* in case we assert in quit. */
  14.356 +            SDL_AbortAssertion();
  14.357 +            return SDL_ASSERTION_IGNORE;  /* shouldn't return, but oh well. */
  14.358 +
  14.359 +        case SDL_ASSERTION_ALWAYS_IGNORE:
  14.360 +            state = SDL_ASSERTION_IGNORE;
  14.361 +            data->always_ignore = 1;
  14.362 +            break;
  14.363 +
  14.364 +        case SDL_ASSERTION_IGNORE:
  14.365 +        case SDL_ASSERTION_RETRY:
  14.366 +        case SDL_ASSERTION_BREAK:
  14.367 +            break;  /* macro handles these. */
  14.368 +    }
  14.369 +
  14.370 +    SDL_UnlockMutex(assertion_mutex);
  14.371 +
  14.372 +    return state;
  14.373 +}
  14.374 +
  14.375 +#endif  /* SDL_ASSERT_LEVEL > 0 */
  14.376 +
  14.377 +
  14.378 +int SDL_AssertionsInit(void)
  14.379 +{
  14.380 +#if (SDL_ASSERT_LEVEL > 0)
  14.381 +    assertion_mutex = SDL_CreateMutex();
  14.382 +    if (assertion_mutex == NULL) {
  14.383 +        return -1;
  14.384 +    }
  14.385 +#endif
  14.386 +    return 0;
  14.387 +}
  14.388 +
  14.389 +void SDL_AssertionsQuit(void)
  14.390 +{
  14.391 +#if (SDL_ASSERT_LEVEL > 0)
  14.392 +    SDL_GenerateAssertionReport();
  14.393 +    SDL_DestroyMutex(assertion_mutex);
  14.394 +    assertion_mutex = NULL;
  14.395 +#endif
  14.396 +}
  14.397 +
  14.398 +/* vi: set ts=4 sw=4 expandtab: */
  14.399 +
    15.1 --- a/src/video/cocoa/SDL_cocoavideo.m	Wed Jan 13 06:39:44 2010 +0000
    15.2 +++ b/src/video/cocoa/SDL_cocoavideo.m	Wed Jan 13 06:47:17 2010 +0000
    15.3 @@ -135,4 +135,44 @@
    15.4      Cocoa_QuitMouse(_this);
    15.5  }
    15.6  
    15.7 +
    15.8 +/*
    15.9 + * Mac OS X assertion support.
   15.10 + *
   15.11 + * This doesn't really have aything to do with the interfaces of the SDL video
   15.12 + *  subsystem, but we need to stuff this into an Objective-C source code file.
   15.13 + */
   15.14 +
   15.15 +SDL_assert_state
   15.16 +SDL_PromptAssertion_cocoa(const SDL_assert_data *data)
   15.17 +{
   15.18 +    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   15.19 +
   15.20 +    NSString *msg = [NSString stringWithFormat:
   15.21 +            @"Assertion failure at %s (%s:%d), triggered %u time%s:\n  '%s'",
   15.22 +                data->function, data->filename, data->linenum,
   15.23 +                data->trigger_count, (data->trigger_count == 1) ? "" : "s",
   15.24 +                data->condition];
   15.25 +
   15.26 +    NSLog(msg);
   15.27 +
   15.28 +    /*
   15.29 +     * !!! FIXME: this code needs to deal with fullscreen modes:
   15.30 +     * !!! FIXME:  reset to default desktop, runModal, reset to current?
   15.31 +     */
   15.32 +
   15.33 +    NSAlert* alert = [[NSAlert alloc] init];
   15.34 +    [alert setAlertStyle:NSCriticalAlertStyle];
   15.35 +    [alert setMessageText:msg];
   15.36 +    [alert addButtonWithTitle:@"Retry"];
   15.37 +    [alert addButtonWithTitle:@"Break"];
   15.38 +    [alert addButtonWithTitle:@"Abort"];
   15.39 +    [alert addButtonWithTitle:@"Ignore"];
   15.40 +    [alert addButtonWithTitle:@"Always Ignore"];
   15.41 +    const NSInteger clicked = [alert runModal];
   15.42 +    [pool release];
   15.43 +    return (SDL_assert_state) (clicked - NSAlertFirstButtonReturn);
   15.44 +}
   15.45 +
   15.46  /* vim: set ts=4 sw=4 expandtab: */
   15.47 +