src/SDL_assert.c
changeset 6621 25504f9ab078
parent 6305 601b0e251822
child 6759 b74da0b33530
child 8337 4a67a3cca43d
equal deleted inserted replaced
6620:62fe18dc6841 6621:25504f9ab078
    20 */
    20 */
    21 #include "SDL_config.h"
    21 #include "SDL_config.h"
    22 
    22 
    23 #include "SDL.h"
    23 #include "SDL.h"
    24 #include "SDL_atomic.h"
    24 #include "SDL_atomic.h"
       
    25 #include "SDL_messagebox.h"
       
    26 #include "SDL_video.h"
    25 #include "SDL_assert.h"
    27 #include "SDL_assert.h"
    26 #include "SDL_assert_c.h"
    28 #include "SDL_assert_c.h"
    27 #include "video/SDL_sysvideo.h"
    29 #include "video/SDL_sysvideo.h"
    28 
    30 
    29 #ifdef __WIN32__
    31 #ifdef __WIN32__
    57 #endif
    59 #endif
    58 
    60 
    59 static void
    61 static void
    60 debug_print(const char *fmt, ...)
    62 debug_print(const char *fmt, ...)
    61 {
    63 {
    62 #ifdef __WIN32__
       
    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;
    64     va_list ap;
    70     va_start(ap, fmt);
    65     va_start(ap, fmt);
    71     len = (int) SDL_vsnprintf(buf, sizeof (buf), fmt, ap);
    66     SDL_LogMessageV(SDL_LOG_CATEGORY_ASSERT, SDL_LOG_PRIORITY_WARN, fmt, ap);
    72     va_end(ap);
    67     va_end(ap);
    73 
    68 }
    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 
    69 
   215 
    70 
   216 static void SDL_AddAssertionToReport(SDL_assert_data *data)
    71 static void SDL_AddAssertionToReport(SDL_assert_data *data)
   217 {
    72 {
   218     /* (data) is always a static struct defined with the assert macros, so
    73     /* (data) is always a static struct defined with the assert macros, so
   272 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
   127 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
   273 {
   128 {
   274     const char *envr;
   129     const char *envr;
   275     SDL_assert_state state = SDL_ASSERTION_ABORT;
   130     SDL_assert_state state = SDL_ASSERTION_ABORT;
   276     SDL_Window *window;
   131     SDL_Window *window;
       
   132     SDL_MessageBoxData messagebox;
       
   133     SDL_MessageBoxButtonData buttons[] = {
       
   134         {   0,  SDL_ASSERTION_RETRY,            "Retry" },
       
   135         {   0,  SDL_ASSERTION_BREAK,            "Break" },
       
   136         {   0,  SDL_ASSERTION_ABORT,            "Abort" },
       
   137         {   SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
       
   138                 SDL_ASSERTION_IGNORE,           "Ignore" },
       
   139         {   SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
       
   140                 SDL_ASSERTION_ALWAYS_IGNORE,    "Always Ignore" }
       
   141     };
       
   142     char *message;
       
   143     int selected;
   277 
   144 
   278     (void) userdata;  /* unused in default handler. */
   145     (void) userdata;  /* unused in default handler. */
   279 
   146 
   280     debug_print("\n\n"
   147     message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
   281                 "Assertion failure at %s (%s:%d), triggered %u time%s:\n"
   148     if (!message) {
   282                 "  '%s'\n"
   149         /* Uh oh, we're in real trouble now... */
   283                 "\n",
   150         return SDL_ASSERTION_ABORT;
   284                 data->function, data->filename, data->linenum,
   151     }
   285                 data->trigger_count, (data->trigger_count == 1) ? "" : "s",
   152     SDL_snprintf(message, SDL_MAX_LOG_MESSAGE,
   286                 data->condition);
   153                  "Assertion failure at %s (%s:%d), triggered %u %s:\r\n  '%s'",
       
   154                  data->function, data->filename, data->linenum,
       
   155                  data->trigger_count, (data->trigger_count == 1) ? "time" : "times",
       
   156                  data->condition);
       
   157 
       
   158     debug_print("\n\n%s\n\n", message);
   287 
   159 
   288     /* let env. variable override, so unit tests won't block in a GUI. */
   160     /* let env. variable override, so unit tests won't block in a GUI. */
   289     envr = SDL_getenv("SDL_ASSERT");
   161     envr = SDL_getenv("SDL_ASSERT");
   290     if (envr != NULL) {
   162     if (envr != NULL) {
       
   163         SDL_stack_free(message);
       
   164 
   291         if (SDL_strcmp(envr, "abort") == 0) {
   165         if (SDL_strcmp(envr, "abort") == 0) {
   292             return SDL_ASSERTION_ABORT;
   166             return SDL_ASSERTION_ABORT;
   293         } else if (SDL_strcmp(envr, "break") == 0) {
   167         } else if (SDL_strcmp(envr, "break") == 0) {
   294             return SDL_ASSERTION_BREAK;
   168             return SDL_ASSERTION_BREAK;
   295         } else if (SDL_strcmp(envr, "retry") == 0) {
   169         } else if (SDL_strcmp(envr, "retry") == 0) {
   309         if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) {
   183         if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) {
   310             SDL_MinimizeWindow(window);
   184             SDL_MinimizeWindow(window);
   311         } else {
   185         } else {
   312             /* !!! FIXME: ungrab the input if we're not fullscreen? */
   186             /* !!! FIXME: ungrab the input if we're not fullscreen? */
   313             /* No need to mess with the window */
   187             /* No need to mess with the window */
   314             window = 0;
   188             window = NULL;
   315         }
   189         }
   316     }
   190     }
   317 
   191 
   318     /* platform-specific UI... */
   192     /* Show a messagebox if we can, otherwise fall back to stdio */
   319 
   193     SDL_zero(messagebox);
   320 #ifdef __WIN32__
   194     messagebox.flags = SDL_MESSAGEBOX_WARNING;
   321     state = SDL_PromptAssertion_windows(data);
   195     messagebox.window = window;
   322 
   196     messagebox.title = "Assertion Failed";
   323 #elif defined __MACOSX__ && defined SDL_VIDEO_DRIVER_COCOA
   197     messagebox.message = message;
   324     /* This has to be done in an Objective-C (*.m) file, so we call out. */
   198     messagebox.numbuttons = SDL_arraysize(buttons);
   325     extern SDL_assert_state SDL_PromptAssertion_cocoa(const SDL_assert_data *);
   199     messagebox.buttons = buttons;
   326     state = SDL_PromptAssertion_cocoa(data);
   200 
   327 
   201     if (SDL_ShowMessageBox(&messagebox, &selected) == 0) {
   328 #else
   202         if (selected == -1) {
   329     /* this is a little hacky. */
       
   330     for ( ; ; ) {
       
   331         char buf[32];
       
   332         fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
       
   333         fflush(stderr);
       
   334         if (fgets(buf, sizeof (buf), stdin) == NULL) {
       
   335             break;
       
   336         }
       
   337 
       
   338         if (SDL_strcmp(buf, "a") == 0) {
       
   339             state = SDL_ASSERTION_ABORT;
       
   340             break;
       
   341         } else if (SDL_strcmp(buf, "b") == 0) {
       
   342             state = SDL_ASSERTION_BREAK;
       
   343             break;
       
   344         } else if (SDL_strcmp(buf, "r") == 0) {
       
   345             state = SDL_ASSERTION_RETRY;
       
   346             break;
       
   347         } else if (SDL_strcmp(buf, "i") == 0) {
       
   348             state = SDL_ASSERTION_IGNORE;
   203             state = SDL_ASSERTION_IGNORE;
   349             break;
   204         } else {
   350         } else if (SDL_strcmp(buf, "A") == 0) {
   205             state = (SDL_assert_state)selected;
   351             state = SDL_ASSERTION_ALWAYS_IGNORE;
   206         }
   352             break;
   207     }
   353         }
   208 #ifdef HAVE_STDIO_H
   354     }
   209     else
   355 #endif
   210     {
       
   211         /* this is a little hacky. */
       
   212         for ( ; ; ) {
       
   213             char buf[32];
       
   214             fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
       
   215             fflush(stderr);
       
   216             if (fgets(buf, sizeof (buf), stdin) == NULL) {
       
   217                 break;
       
   218             }
       
   219 
       
   220             if (SDL_strcmp(buf, "a") == 0) {
       
   221                 state = SDL_ASSERTION_ABORT;
       
   222                 break;
       
   223             } else if (SDL_strcmp(buf, "b") == 0) {
       
   224                 state = SDL_ASSERTION_BREAK;
       
   225                 break;
       
   226             } else if (SDL_strcmp(buf, "r") == 0) {
       
   227                 state = SDL_ASSERTION_RETRY;
       
   228                 break;
       
   229             } else if (SDL_strcmp(buf, "i") == 0) {
       
   230                 state = SDL_ASSERTION_IGNORE;
       
   231                 break;
       
   232             } else if (SDL_strcmp(buf, "A") == 0) {
       
   233                 state = SDL_ASSERTION_ALWAYS_IGNORE;
       
   234                 break;
       
   235             }
       
   236         }
       
   237     }
       
   238 #endif /* HAVE_STDIO_H */
   356 
   239 
   357     /* Re-enter fullscreen mode */
   240     /* Re-enter fullscreen mode */
   358     if (window) {
   241     if (window) {
   359         SDL_RestoreWindow(window);
   242         SDL_RestoreWindow(window);
   360     }
   243     }
       
   244 
       
   245     SDL_stack_free(message);
   361 
   246 
   362     return state;
   247     return state;
   363 }
   248 }
   364 
   249 
   365 
   250