Clean up assertion API for public use.
authorRyan C. Gordon <icculus@icculus.org>
Wed, 13 Jan 2010 19:29:33 +0000
changeset 367062b6a5b99918
parent 3669 46d27a9571fa
child 3671 0d6f520c0eb9
Clean up assertion API for public use.
include/SDL_assert.h
src/SDL_assert.c
     1.1 --- a/include/SDL_assert.h	Wed Jan 13 16:58:24 2010 +0000
     1.2 +++ b/include/SDL_assert.h	Wed Jan 13 19:29:33 2010 +0000
     1.3 @@ -109,7 +109,7 @@
     1.4      const char *filename;
     1.5      int linenum;
     1.6      const char *function;
     1.7 -    struct SDL_assert_data *next;
     1.8 +    const struct SDL_assert_data *next;
     1.9  } SDL_assert_data;
    1.10  
    1.11  /* Never call this directly. Use the SDL_assert* macros. */
    1.12 @@ -166,6 +166,68 @@
    1.13  #   error Unknown assertion level.
    1.14  #endif
    1.15  
    1.16 +
    1.17 +typedef SDL_assert_state (SDLCALL *SDL_AssertionHandler)(
    1.18 +                                    const SDL_assert_data *, void *userdata);
    1.19 +
    1.20 +/**
    1.21 + *  \brief Set an application-defined assertion handler.
    1.22 + *
    1.23 + *  This allows an app to show its own assertion UI and/or force the
    1.24 + *  response to an assertion failure. If the app doesn't provide this, SDL
    1.25 + *  will try to do the right thing, popping up a system-specific GUI dialog,
    1.26 + *  and probably minimizing any fullscreen windows.
    1.27 + *
    1.28 + *  This callback may fire from any thread, but it runs wrapped in a mutex, so
    1.29 + *  it will only fire from one thread at a time.
    1.30 + *
    1.31 + *  Setting the callback to NULL restores SDL's original internal handler.
    1.32 + *
    1.33 + *  This callback is NOT reset to SDL's internal handler upon SDL_Quit()!
    1.34 + *
    1.35 + *  \return SDL_assert_state value of how to handle the assertion failure.
    1.36 + *  
    1.37 + *  \param handler Callback function, called when an assertion fails.
    1.38 + *  \param userdata A pointer passed to the callback as-is.
    1.39 + */
    1.40 +extern DECLSPEC void SDLCALL SDL_SetAssertionHandler(
    1.41 +                                            SDL_AssertionHandler handler,
    1.42 +                                            void *userdata);
    1.43 +
    1.44 +/**
    1.45 + *  \brief Get a list of all assertion failures.
    1.46 + *
    1.47 + *  Get all assertions triggered since last call to SDL_ResetAssertionReport(),
    1.48 + *  or the start of the program.
    1.49 + *
    1.50 + *  The proper way to examine this data looks something like this:
    1.51 + *
    1.52 + *  <code>
    1.53 + *  const SDL_assert_data *item = SDL_GetAssertionReport();
    1.54 + *  while (item->condition) {
    1.55 + *      printf("'%s', %s (%s:%d), triggered %u times, always ignore: %s.\n",
    1.56 + *             item->condition, item->function, item->filename,
    1.57 + *             item->linenum, item->trigger_count,
    1.58 + *             item->always_ignore ? "yes" : "no");
    1.59 + *      item = item->next;
    1.60 + *  }
    1.61 + *  </code>
    1.62 + *
    1.63 + *  \return List of all assertions. This never returns NULL,
    1.64 + *          even if there are no items.
    1.65 + *  \sa SDL_ResetAssertionReport
    1.66 + */
    1.67 +extern DECLSPEC const SDL_assert_data * SDLCALL SDL_GetAssertionReport(void);
    1.68 +
    1.69 +/**
    1.70 + *  \brief Reset the list of all assertion failures.
    1.71 + *
    1.72 + *  Reset list of all assertions triggered.
    1.73 + *
    1.74 + *  \sa SDL_GetAssertionReport
    1.75 + */
    1.76 +extern DECLSPEC void SDLCALL SDL_ResetAssertionReport(void);
    1.77 +
    1.78  /* Ends C function definitions when using C++ */
    1.79  #ifdef __cplusplus
    1.80  /* *INDENT-OFF* */
     2.1 --- a/src/SDL_assert.c	Wed Jan 13 16:58:24 2010 +0000
     2.2 +++ b/src/SDL_assert.c	Wed Jan 13 19:29:33 2010 +0000
     2.3 @@ -23,8 +23,6 @@
     2.4  #include "SDL.h"
     2.5  #include "SDL_assert.h"
     2.6  
     2.7 -#if (SDL_ASSERT_LEVEL > 0)
     2.8 -
     2.9  #ifdef _WINDOWS
    2.10  #define WIN32_LEAN_AND_MEAN 1
    2.11  #include <windows.h>
    2.12 @@ -34,13 +32,19 @@
    2.13  #include <unistd.h>
    2.14  #endif
    2.15  
    2.16 -/* We can keep all triggered assertions in a singly-linked list so we can
    2.17 +static SDL_assert_state
    2.18 +SDL_PromptAssertion(const SDL_assert_data *data, void *userdata);
    2.19 +
    2.20 +/*
    2.21 + * We keep all triggered assertions in a singly-linked list so we can
    2.22   *  generate a report later.
    2.23   */
    2.24 -#if !SDL_ASSERTION_REPORT_DISABLED
    2.25  static SDL_assert_data assertion_list_terminator = { 0, 0, 0, 0, 0, 0, 0 };
    2.26  static SDL_assert_data *triggered_assertions = &assertion_list_terminator;
    2.27 -#endif
    2.28 +
    2.29 +static SDL_mutex *assertion_mutex = NULL;
    2.30 +static SDL_AssertionHandler assertion_handler = SDL_PromptAssertion;
    2.31 +static void *assertion_userdata = NULL;
    2.32  
    2.33  #ifdef __GNUC__
    2.34  static void
    2.35 @@ -198,27 +202,30 @@
    2.36  
    2.37  static void SDL_AddAssertionToReport(SDL_assert_data *data)
    2.38  {
    2.39 -#if !SDL_ASSERTION_REPORT_DISABLED
    2.40      /* (data) is always a static struct defined with the assert macros, so
    2.41         we don't have to worry about copying or allocating them. */
    2.42      if (data->next == NULL) {  /* not yet added? */
    2.43          data->next = triggered_assertions;
    2.44          triggered_assertions = data;
    2.45      }
    2.46 -#endif
    2.47  }
    2.48  
    2.49 +
    2.50  static void SDL_GenerateAssertionReport(void)
    2.51  {
    2.52 -#if !SDL_ASSERTION_REPORT_DISABLED
    2.53 -    if (triggered_assertions != &assertion_list_terminator)
    2.54 +    const SDL_assert_data *item;
    2.55 +
    2.56 +    /* only do this if the app hasn't assigned an assertion handler. */
    2.57 +    if (assertion_handler != SDL_PromptAssertion)
    2.58 +        return;
    2.59 +
    2.60 +    item = SDL_GetAssertionReport();
    2.61 +    if (item->condition)
    2.62      {
    2.63 -        SDL_assert_data *item = triggered_assertions;
    2.64 -
    2.65          debug_print("\n\nSDL assertion report.\n");
    2.66          debug_print("All SDL assertions between last init/quit:\n\n");
    2.67  
    2.68 -        while (item != &assertion_list_terminator) {
    2.69 +        while (item->condition) {
    2.70              debug_print(
    2.71                  "'%s'\n"
    2.72                  "    * %s (%s:%d)\n"
    2.73 @@ -232,9 +239,8 @@
    2.74          }
    2.75          debug_print("\n");
    2.76  
    2.77 -        triggered_assertions = &assertion_list_terminator;
    2.78 +        SDL_ResetAssertionReport();
    2.79      }
    2.80 -#endif
    2.81  }
    2.82  
    2.83  static void SDL_ExitProcess(int exitcode)
    2.84 @@ -253,12 +259,15 @@
    2.85  }
    2.86  
    2.87  
    2.88 -static SDL_assert_state SDL_PromptAssertion(const SDL_assert_data *data)
    2.89 +static SDL_assert_state
    2.90 +SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
    2.91  {
    2.92      const char *envr;
    2.93      SDL_assert_state state = SDL_ASSERTION_ABORT;
    2.94      SDL_WindowID window;
    2.95  
    2.96 +    (void) userdata;  /* unused in default handler. */
    2.97 +
    2.98      debug_print("\n\n"
    2.99                  "Assertion failure at %s (%s:%d), triggered %u time%s:\n"
   2.100                  "  '%s'\n"
   2.101 @@ -291,6 +300,7 @@
   2.102          if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) {
   2.103              SDL_MinimizeWindow(window);
   2.104          } else {
   2.105 +            /* !!! FIXME: ungrab the input if we're not fullscreen? */
   2.106              /* No need to mess with the window */
   2.107              window = 0;
   2.108          }
   2.109 @@ -344,8 +354,6 @@
   2.110  }
   2.111  
   2.112  
   2.113 -static SDL_mutex *assertion_mutex = NULL;
   2.114 -
   2.115  SDL_assert_state
   2.116  SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file,
   2.117                      int line)
   2.118 @@ -391,7 +399,7 @@
   2.119      }
   2.120  
   2.121      if (!data->always_ignore) {
   2.122 -        state = SDL_PromptAssertion(data);
   2.123 +        state = assertion_handler(data, assertion_userdata);
   2.124      }
   2.125  
   2.126      switch (state)
   2.127 @@ -417,8 +425,6 @@
   2.128      return state;
   2.129  }
   2.130  
   2.131 -#endif  /* SDL_ASSERT_LEVEL > 0 */
   2.132 -
   2.133  
   2.134  int SDL_AssertionsInit(void)
   2.135  {
   2.136 @@ -428,13 +434,41 @@
   2.137  
   2.138  void SDL_AssertionsQuit(void)
   2.139  {
   2.140 -#if (SDL_ASSERT_LEVEL > 0)
   2.141      SDL_GenerateAssertionReport();
   2.142      if (assertion_mutex != NULL) {
   2.143          SDL_DestroyMutex(assertion_mutex);
   2.144          assertion_mutex = NULL;
   2.145      }
   2.146 -#endif
   2.147 +}
   2.148 +
   2.149 +void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
   2.150 +{
   2.151 +    if (handler != NULL) {
   2.152 +        assertion_handler = handler;
   2.153 +        assertion_userdata = userdata;
   2.154 +    } else {
   2.155 +        assertion_handler = SDL_PromptAssertion;
   2.156 +        assertion_userdata = NULL;
   2.157 +    }
   2.158 +}
   2.159 +
   2.160 +const SDL_assert_data *SDL_GetAssertionReport(void)
   2.161 +{
   2.162 +    return triggered_assertions;
   2.163 +}
   2.164 +
   2.165 +void SDL_ResetAssertionReport(void)
   2.166 +{
   2.167 +    SDL_assert_data *item = triggered_assertions;
   2.168 +    SDL_assert_data *next = NULL;
   2.169 +    for (item = triggered_assertions; item->condition; item = next) {
   2.170 +        next = (SDL_assert_data *) item->next;
   2.171 +        item->always_ignore = SDL_FALSE;
   2.172 +        item->trigger_count = 0;
   2.173 +        item->next = NULL;
   2.174 +    }
   2.175 +
   2.176 +    triggered_assertions = &assertion_list_terminator;
   2.177  }
   2.178  
   2.179  /* vi: set ts=4 sw=4 expandtab: */