src/SDL_assert.c
author David Ludwig <dludwig@pobox.com>
Wed, 17 Oct 2012 21:43:20 -0400
changeset 8316 88f011703f39
parent 6305 601b0e251822
child 8337 4a67a3cca43d
permissions -rw-r--r--
Got a bare-bones version of SDL compiling for Windows RT. Dummy drivers are used in some places. Very little Windows-specific code (from the Win32 version of SDL) is used.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
     4 
     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.
     8 
     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:
    12 
    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.
    20 */
    21 #include "SDL_config.h"
    22 
    23 #include "SDL.h"
    24 #include "SDL_atomic.h"
    25 #include "SDL_assert.h"
    26 #include "SDL_assert_c.h"
    27 #include "video/SDL_sysvideo.h"
    28 
    29 #if defined(__WIN32__) || defined(__WINRT__)
    30 #include "core/windows/SDL_windows.h"
    31 
    32 #ifndef WS_OVERLAPPEDWINDOW
    33 #define WS_OVERLAPPEDWINDOW 0
    34 #endif
    35 #else  /* fprintf, _exit(), etc. */
    36 #include <stdio.h>
    37 #include <stdlib.h>
    38 #include <unistd.h>
    39 #endif
    40 
    41 static SDL_assert_state
    42 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata);
    43 
    44 /*
    45  * We keep all triggered assertions in a singly-linked list so we can
    46  *  generate a report later.
    47  */
    48 static SDL_assert_data *triggered_assertions = NULL;
    49 
    50 static SDL_mutex *assertion_mutex = NULL;
    51 static SDL_AssertionHandler assertion_handler = SDL_PromptAssertion;
    52 static void *assertion_userdata = NULL;
    53 
    54 #ifdef __GNUC__
    55 static void
    56 debug_print(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
    57 #endif
    58 
    59 static void
    60 debug_print(const char *fmt, ...)
    61 {
    62 #if defined(__WIN32__) || defined(__WINRT__)
    63     /* Format into a buffer for OutputDebugStringA(). */
    64     char buf[1024];
    65     char *startptr;
    66     char *ptr;
    67     LPTSTR tstr;
    68     int len;
    69     va_list ap;
    70     va_start(ap, fmt);
    71     len = (int) SDL_vsnprintf(buf, sizeof (buf), fmt, ap);
    72     va_end(ap);
    73 
    74     /* Visual C's vsnprintf() may not null-terminate the buffer. */
    75     if ((len >= sizeof (buf)) || (len < 0)) {
    76         buf[sizeof (buf) - 1] = '\0';
    77     }
    78 
    79     /* Write it, sorting out the Unix newlines... */
    80     startptr = buf;
    81     for (ptr = startptr; *ptr; ptr++) {
    82         if (*ptr == '\n') {
    83             *ptr = '\0';
    84             tstr = WIN_UTF8ToString(startptr);
    85             OutputDebugString(tstr);
    86             SDL_free(tstr);
    87             OutputDebugString(TEXT("\r\n"));
    88             startptr = ptr+1;
    89         }
    90     }
    91 
    92     /* catch that last piece if it didn't have a newline... */
    93     if (startptr != ptr) {
    94         tstr = WIN_UTF8ToString(startptr);
    95         OutputDebugString(tstr);
    96         SDL_free(tstr);
    97     }
    98 #else
    99     /* Unix has it easy. Just dump it to stderr. */
   100     va_list ap;
   101     va_start(ap, fmt);
   102     vfprintf(stderr, fmt, ap);
   103     va_end(ap);
   104     fflush(stderr);
   105 #endif
   106 }
   107 
   108 
   109 #ifdef __WIN32__
   110 static SDL_assert_state SDL_Windows_AssertChoice = SDL_ASSERTION_ABORT;
   111 static const SDL_assert_data *SDL_Windows_AssertData = NULL;
   112 
   113 static LRESULT CALLBACK
   114 SDL_Assertion_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
   115 {
   116     switch (msg)
   117     {
   118         case WM_CREATE:
   119         {
   120             /* !!! FIXME: all this code stinks. */
   121             const SDL_assert_data *data = SDL_Windows_AssertData;
   122             char buf[1024];
   123             LPTSTR tstr;
   124             const int w = 100;
   125             const int h = 25;
   126             const int gap = 10;
   127             int x = gap;
   128             int y = 50;
   129             int len;
   130             int i;
   131             static const struct { 
   132                 LPCTSTR name;
   133                 SDL_assert_state state;
   134             } buttons[] = {
   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 },
   140             };
   141 
   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",
   146                          data->condition);
   147             if ((len < 0) || (len >= sizeof (buf))) {
   148                 buf[sizeof (buf) - 1] = '\0';
   149             }
   150 
   151             tstr = WIN_UTF8ToString(buf);
   152             CreateWindow(TEXT("STATIC"), tstr,
   153                          WS_VISIBLE | WS_CHILD | SS_LEFT,
   154                          x, y, 550, 100,
   155                          hwnd, (HMENU) 1, NULL, NULL);
   156             SDL_free(tstr);
   157             y += 110;
   158 
   159             for (i = 0; i < (sizeof (buttons) / sizeof (buttons[0])); i++) {
   160                 CreateWindow(TEXT("BUTTON"), buttons[i].name,
   161                          WS_VISIBLE | WS_CHILD,
   162                          x, y, w, h,
   163                          hwnd, (HMENU) buttons[i].state, NULL, NULL);
   164                 x += w + gap;
   165             }
   166             break;
   167         }
   168 
   169         case WM_COMMAND:
   170             SDL_Windows_AssertChoice = ((SDL_assert_state) (LOWORD(wParam)));
   171             SDL_Windows_AssertData = NULL;
   172             break;
   173 
   174         case WM_DESTROY:
   175             SDL_Windows_AssertData = NULL;
   176             break;
   177     }
   178 
   179     return DefWindowProc(hwnd, msg, wParam, lParam);
   180 }
   181 
   182 static SDL_assert_state
   183 SDL_PromptAssertion_windows(const SDL_assert_data *data)
   184 {
   185     HINSTANCE hInstance = 0;  /* !!! FIXME? */
   186     HWND hwnd;
   187     MSG msg;
   188     WNDCLASS wc = {0};
   189 
   190     SDL_Windows_AssertChoice = SDL_ASSERTION_ABORT;
   191     SDL_Windows_AssertData = data;
   192 
   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);
   198   
   199     RegisterClass(&wc);
   200     hwnd = CreateWindow(wc.lpszClassName, TEXT("SDL assertion failure"),
   201                  WS_OVERLAPPEDWINDOW | WS_VISIBLE,
   202                  150, 150, 570, 260, 0, 0, hInstance, 0);  
   203 
   204     while (GetMessage(&msg, NULL, 0, 0) && (SDL_Windows_AssertData != NULL)) {
   205         TranslateMessage(&msg);
   206         DispatchMessage(&msg);
   207     }
   208 
   209     DestroyWindow(hwnd);
   210     UnregisterClass(wc.lpszClassName, hInstance);
   211     return SDL_Windows_AssertChoice;
   212 }
   213 #endif
   214 
   215 
   216 #ifdef __WINRT__
   217 
   218 static SDL_assert_state
   219 SDL_PromptAssertion_windowsrt(const SDL_assert_data *data)
   220 {
   221     /* TODO, WinRT: implement SDL_PromptAssertion_windowsrt */
   222     return SDL_ASSERTION_ABORT;
   223 }
   224 
   225 #endif
   226 
   227 
   228 static void SDL_AddAssertionToReport(SDL_assert_data *data)
   229 {
   230     /* (data) is always a static struct defined with the assert macros, so
   231        we don't have to worry about copying or allocating them. */
   232     data->trigger_count++;
   233     if (data->trigger_count == 1) {  /* not yet added? */
   234         data->next = triggered_assertions;
   235         triggered_assertions = data;
   236     }
   237 }
   238 
   239 
   240 static void SDL_GenerateAssertionReport(void)
   241 {
   242     const SDL_assert_data *item = triggered_assertions;
   243 
   244     /* only do this if the app hasn't assigned an assertion handler. */
   245     if ((item != NULL) && (assertion_handler != SDL_PromptAssertion)) {
   246         debug_print("\n\nSDL assertion report.\n");
   247         debug_print("All SDL assertions between last init/quit:\n\n");
   248 
   249         while (item != NULL) {
   250             debug_print(
   251                 "'%s'\n"
   252                 "    * %s (%s:%d)\n"
   253                 "    * triggered %u time%s.\n"
   254                 "    * always ignore: %s.\n",
   255                 item->condition, item->function, item->filename,
   256                 item->linenum, item->trigger_count,
   257                 (item->trigger_count == 1) ? "" : "s",
   258                 item->always_ignore ? "yes" : "no");
   259             item = item->next;
   260         }
   261         debug_print("\n");
   262 
   263         SDL_ResetAssertionReport();
   264     }
   265 }
   266 
   267 static void SDL_ExitProcess(int exitcode)
   268 {
   269 #if defined(__WIN32__)
   270     ExitProcess(exitcode);
   271 #elif defined(__WINRT__)
   272     exit(exitcode);
   273 #else
   274     _exit(exitcode);
   275 #endif
   276 }
   277 
   278 static void SDL_AbortAssertion(void)
   279 {
   280     SDL_Quit();
   281     SDL_ExitProcess(42);
   282 }
   283 
   284 
   285 static SDL_assert_state
   286 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
   287 {
   288     const char *envr;
   289     SDL_assert_state state = SDL_ASSERTION_ABORT;
   290     SDL_Window *window;
   291 
   292     (void) userdata;  /* unused in default handler. */
   293 
   294     debug_print("\n\n"
   295                 "Assertion failure at %s (%s:%d), triggered %u time%s:\n"
   296                 "  '%s'\n"
   297                 "\n",
   298                 data->function, data->filename, data->linenum,
   299                 data->trigger_count, (data->trigger_count == 1) ? "" : "s",
   300                 data->condition);
   301 
   302     /* let env. variable override, so unit tests won't block in a GUI. */
   303     envr = SDL_getenv("SDL_ASSERT");
   304     if (envr != NULL) {
   305         if (SDL_strcmp(envr, "abort") == 0) {
   306             return SDL_ASSERTION_ABORT;
   307         } else if (SDL_strcmp(envr, "break") == 0) {
   308             return SDL_ASSERTION_BREAK;
   309         } else if (SDL_strcmp(envr, "retry") == 0) {
   310             return SDL_ASSERTION_RETRY;
   311         } else if (SDL_strcmp(envr, "ignore") == 0) {
   312             return SDL_ASSERTION_IGNORE;
   313         } else if (SDL_strcmp(envr, "always_ignore") == 0) {
   314             return SDL_ASSERTION_ALWAYS_IGNORE;
   315         } else {
   316             return SDL_ASSERTION_ABORT;  /* oh well. */
   317         }
   318     }
   319 
   320     /* Leave fullscreen mode, if possible (scary!) */
   321     window = SDL_GetFocusWindow();
   322     if (window) {
   323         if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) {
   324             SDL_MinimizeWindow(window);
   325         } else {
   326             /* !!! FIXME: ungrab the input if we're not fullscreen? */
   327             /* No need to mess with the window */
   328             window = 0;
   329         }
   330     }
   331 
   332     /* platform-specific UI... */
   333 
   334 #if defined(__WIN32__)
   335     state = SDL_PromptAssertion_windows(data);
   336 
   337 #elif defined(__WINRT__)
   338     state = SDL_PromptAssertion_windowsrt(data);
   339 
   340 #elif defined __MACOSX__ && defined SDL_VIDEO_DRIVER_COCOA
   341     /* This has to be done in an Objective-C (*.m) file, so we call out. */
   342     extern SDL_assert_state SDL_PromptAssertion_cocoa(const SDL_assert_data *);
   343     state = SDL_PromptAssertion_cocoa(data);
   344 
   345 #else
   346     /* this is a little hacky. */
   347     for ( ; ; ) {
   348         char buf[32];
   349         fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
   350         fflush(stderr);
   351         if (fgets(buf, sizeof (buf), stdin) == NULL) {
   352             break;
   353         }
   354 
   355         if (SDL_strcmp(buf, "a") == 0) {
   356             state = SDL_ASSERTION_ABORT;
   357             break;
   358         } else if (SDL_strcmp(buf, "b") == 0) {
   359             state = SDL_ASSERTION_BREAK;
   360             break;
   361         } else if (SDL_strcmp(buf, "r") == 0) {
   362             state = SDL_ASSERTION_RETRY;
   363             break;
   364         } else if (SDL_strcmp(buf, "i") == 0) {
   365             state = SDL_ASSERTION_IGNORE;
   366             break;
   367         } else if (SDL_strcmp(buf, "A") == 0) {
   368             state = SDL_ASSERTION_ALWAYS_IGNORE;
   369             break;
   370         }
   371     }
   372 #endif
   373 
   374     /* Re-enter fullscreen mode */
   375     if (window) {
   376         SDL_RestoreWindow(window);
   377     }
   378 
   379     return state;
   380 }
   381 
   382 
   383 SDL_assert_state
   384 SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file,
   385                     int line)
   386 {
   387     static int assertion_running = 0;
   388     static SDL_SpinLock spinlock = 0;
   389     SDL_assert_state state = SDL_ASSERTION_IGNORE;
   390 
   391     SDL_AtomicLock(&spinlock);
   392     if (assertion_mutex == NULL) { /* never called SDL_Init()? */
   393         assertion_mutex = SDL_CreateMutex();
   394         if (assertion_mutex == NULL) {
   395             SDL_AtomicUnlock(&spinlock);
   396             return SDL_ASSERTION_IGNORE;   /* oh well, I guess. */
   397         }
   398     }
   399     SDL_AtomicUnlock(&spinlock);
   400 
   401     if (SDL_LockMutex(assertion_mutex) < 0) {
   402         return SDL_ASSERTION_IGNORE;   /* oh well, I guess. */
   403     }
   404 
   405     /* doing this because Visual C is upset over assigning in the macro. */
   406     if (data->trigger_count == 0) {
   407         data->function = func;
   408         data->filename = file;
   409         data->linenum = line;
   410     }
   411 
   412     SDL_AddAssertionToReport(data);
   413 
   414     assertion_running++;
   415     if (assertion_running > 1) {   /* assert during assert! Abort. */
   416         if (assertion_running == 2) {
   417             SDL_AbortAssertion();
   418         } else if (assertion_running == 3) {  /* Abort asserted! */
   419             SDL_ExitProcess(42);
   420         } else {
   421             while (1) { /* do nothing but spin; what else can you do?! */ }
   422         }
   423     }
   424 
   425     if (!data->always_ignore) {
   426         state = assertion_handler(data, assertion_userdata);
   427     }
   428 
   429     switch (state)
   430     {
   431         case SDL_ASSERTION_ABORT:
   432             SDL_AbortAssertion();
   433             return SDL_ASSERTION_IGNORE;  /* shouldn't return, but oh well. */
   434 
   435         case SDL_ASSERTION_ALWAYS_IGNORE:
   436             state = SDL_ASSERTION_IGNORE;
   437             data->always_ignore = 1;
   438             break;
   439 
   440         case SDL_ASSERTION_IGNORE:
   441         case SDL_ASSERTION_RETRY:
   442         case SDL_ASSERTION_BREAK:
   443             break;  /* macro handles these. */
   444     }
   445 
   446     assertion_running--;
   447     SDL_UnlockMutex(assertion_mutex);
   448 
   449     return state;
   450 }
   451 
   452 
   453 int SDL_AssertionsInit(void)
   454 {
   455     /* this is a no-op at the moment. */
   456     return 0;
   457 }
   458 
   459 void SDL_AssertionsQuit(void)
   460 {
   461     SDL_GenerateAssertionReport();
   462     if (assertion_mutex != NULL) {
   463         SDL_DestroyMutex(assertion_mutex);
   464         assertion_mutex = NULL;
   465     }
   466 }
   467 
   468 void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
   469 {
   470     if (handler != NULL) {
   471         assertion_handler = handler;
   472         assertion_userdata = userdata;
   473     } else {
   474         assertion_handler = SDL_PromptAssertion;
   475         assertion_userdata = NULL;
   476     }
   477 }
   478 
   479 const SDL_assert_data *SDL_GetAssertionReport(void)
   480 {
   481     return triggered_assertions;
   482 }
   483 
   484 void SDL_ResetAssertionReport(void)
   485 {
   486     SDL_assert_data *next = NULL;
   487     SDL_assert_data *item;
   488     for (item = triggered_assertions; item != NULL; item = next) {
   489         next = (SDL_assert_data *) item->next;
   490         item->always_ignore = SDL_FALSE;
   491         item->trigger_count = 0;
   492         item->next = NULL;
   493     }
   494 
   495     triggered_assertions = NULL;
   496 }
   497 
   498 /* vi: set ts=4 sw=4 expandtab: */