src/SDL_assert.c
changeset 8337 4a67a3cca43d
parent 8316 88f011703f39
parent 6621 25504f9ab078
child 8341 99c7c87774d5
equal deleted inserted replaced
8336:0b9aa42be7ae 8337:4a67a3cca43d
    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 #if defined(__WIN32__) || defined(__WINRT__)
    31 #ifdef __WIN32__
    30 #include "core/windows/SDL_windows.h"
    32 #include "core/windows/SDL_windows.h"
    31 
    33 
    32 #ifndef WS_OVERLAPPEDWINDOW
    34 #ifndef WS_OVERLAPPEDWINDOW
    33 #define WS_OVERLAPPEDWINDOW 0
    35 #define WS_OVERLAPPEDWINDOW 0
    34 #endif
    36 #endif
    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 #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;
    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 
       
   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 
    69 
   227 
    70 
   228 static void SDL_AddAssertionToReport(SDL_assert_data *data)
    71 static void SDL_AddAssertionToReport(SDL_assert_data *data)
   229 {
    72 {
   230     /* (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
   264     }
   107     }
   265 }
   108 }
   266 
   109 
   267 static void SDL_ExitProcess(int exitcode)
   110 static void SDL_ExitProcess(int exitcode)
   268 {
   111 {
   269 #if defined(__WIN32__)
   112 #ifdef __WIN32__
   270     ExitProcess(exitcode);
   113     ExitProcess(exitcode);
   271 #elif defined(__WINRT__)
       
   272     exit(exitcode);
       
   273 #else
   114 #else
   274     _exit(exitcode);
   115     _exit(exitcode);
   275 #endif
   116 #endif
   276 }
   117 }
   277 
   118 
   286 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
   127 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
   287 {
   128 {
   288     const char *envr;
   129     const char *envr;
   289     SDL_assert_state state = SDL_ASSERTION_ABORT;
   130     SDL_assert_state state = SDL_ASSERTION_ABORT;
   290     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;
   291 
   144 
   292     (void) userdata;  /* unused in default handler. */
   145     (void) userdata;  /* unused in default handler. */
   293 
   146 
   294     debug_print("\n\n"
   147     message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
   295                 "Assertion failure at %s (%s:%d), triggered %u time%s:\n"
   148     if (!message) {
   296                 "  '%s'\n"
   149         /* Uh oh, we're in real trouble now... */
   297                 "\n",
   150         return SDL_ASSERTION_ABORT;
   298                 data->function, data->filename, data->linenum,
   151     }
   299                 data->trigger_count, (data->trigger_count == 1) ? "" : "s",
   152     SDL_snprintf(message, SDL_MAX_LOG_MESSAGE,
   300                 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);
   301 
   159 
   302     /* 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. */
   303     envr = SDL_getenv("SDL_ASSERT");
   161     envr = SDL_getenv("SDL_ASSERT");
   304     if (envr != NULL) {
   162     if (envr != NULL) {
       
   163         SDL_stack_free(message);
       
   164 
   305         if (SDL_strcmp(envr, "abort") == 0) {
   165         if (SDL_strcmp(envr, "abort") == 0) {
   306             return SDL_ASSERTION_ABORT;
   166             return SDL_ASSERTION_ABORT;
   307         } else if (SDL_strcmp(envr, "break") == 0) {
   167         } else if (SDL_strcmp(envr, "break") == 0) {
   308             return SDL_ASSERTION_BREAK;
   168             return SDL_ASSERTION_BREAK;
   309         } else if (SDL_strcmp(envr, "retry") == 0) {
   169         } else if (SDL_strcmp(envr, "retry") == 0) {
   323         if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) {
   183         if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) {
   324             SDL_MinimizeWindow(window);
   184             SDL_MinimizeWindow(window);
   325         } else {
   185         } else {
   326             /* !!! FIXME: ungrab the input if we're not fullscreen? */
   186             /* !!! FIXME: ungrab the input if we're not fullscreen? */
   327             /* No need to mess with the window */
   187             /* No need to mess with the window */
   328             window = 0;
   188             window = NULL;
   329         }
   189         }
   330     }
   190     }
   331 
   191 
   332     /* platform-specific UI... */
   192     /* Show a messagebox if we can, otherwise fall back to stdio */
   333 
   193     SDL_zero(messagebox);
   334 #if defined(__WIN32__)
   194     messagebox.flags = SDL_MESSAGEBOX_WARNING;
   335     state = SDL_PromptAssertion_windows(data);
   195     messagebox.window = window;
   336 
   196     messagebox.title = "Assertion Failed";
   337 #elif defined(__WINRT__)
   197     messagebox.message = message;
   338     state = SDL_PromptAssertion_windowsrt(data);
   198     messagebox.numbuttons = SDL_arraysize(buttons);
   339 
   199     messagebox.buttons = buttons;
   340 #elif defined __MACOSX__ && defined SDL_VIDEO_DRIVER_COCOA
   200 
   341     /* This has to be done in an Objective-C (*.m) file, so we call out. */
   201     if (SDL_ShowMessageBox(&messagebox, &selected) == 0) {
   342     extern SDL_assert_state SDL_PromptAssertion_cocoa(const SDL_assert_data *);
   202         if (selected == -1) {
   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;
   203             state = SDL_ASSERTION_IGNORE;
   366             break;
   204         } else {
   367         } else if (SDL_strcmp(buf, "A") == 0) {
   205             state = (SDL_assert_state)selected;
   368             state = SDL_ASSERTION_ALWAYS_IGNORE;
   206         }
   369             break;
   207     }
   370         }
   208 #ifdef HAVE_STDIO_H
   371     }
   209     else
   372 #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 */
   373 
   239 
   374     /* Re-enter fullscreen mode */
   240     /* Re-enter fullscreen mode */
   375     if (window) {
   241     if (window) {
   376         SDL_RestoreWindow(window);
   242         SDL_RestoreWindow(window);
   377     }
   243     }
       
   244 
       
   245     SDL_stack_free(message);
   378 
   246 
   379     return state;
   247     return state;
   380 }
   248 }
   381 
   249 
   382 
   250