cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_assert.c (11073B)


      1/*
      2  Simple DirectMedia Layer
      3  Copyright (C) 1997-2014 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_internal.h"
     22
     23#if defined(__WIN32__)
     24#include "core/windows/SDL_windows.h"
     25#endif
     26
     27#include "SDL.h"
     28#include "SDL_atomic.h"
     29#include "SDL_messagebox.h"
     30#include "SDL_video.h"
     31#include "SDL_assert.h"
     32#include "SDL_assert_c.h"
     33#include "video/SDL_sysvideo.h"
     34
     35#ifdef __WIN32__
     36#ifndef WS_OVERLAPPEDWINDOW
     37#define WS_OVERLAPPEDWINDOW 0
     38#endif
     39#else  /* fprintf, _exit(), etc. */
     40#include <stdio.h>
     41#include <stdlib.h>
     42#if ! defined(__WINRT__)
     43#include <unistd.h>
     44#endif
     45#endif
     46
     47static SDL_assert_state
     48SDL_PromptAssertion(const SDL_assert_data *data, void *userdata);
     49
     50/*
     51 * We keep all triggered assertions in a singly-linked list so we can
     52 *  generate a report later.
     53 */
     54static SDL_assert_data *triggered_assertions = NULL;
     55
     56static SDL_mutex *assertion_mutex = NULL;
     57static SDL_AssertionHandler assertion_handler = SDL_PromptAssertion;
     58static void *assertion_userdata = NULL;
     59
     60#ifdef __GNUC__
     61static void
     62debug_print(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
     63#endif
     64
     65static void
     66debug_print(const char *fmt, ...)
     67{
     68    va_list ap;
     69    va_start(ap, fmt);
     70    SDL_LogMessageV(SDL_LOG_CATEGORY_ASSERT, SDL_LOG_PRIORITY_WARN, fmt, ap);
     71    va_end(ap);
     72}
     73
     74
     75static void SDL_AddAssertionToReport(SDL_assert_data *data)
     76{
     77    /* (data) is always a static struct defined with the assert macros, so
     78       we don't have to worry about copying or allocating them. */
     79    data->trigger_count++;
     80    if (data->trigger_count == 1) {  /* not yet added? */
     81        data->next = triggered_assertions;
     82        triggered_assertions = data;
     83    }
     84}
     85
     86
     87static void SDL_GenerateAssertionReport(void)
     88{
     89    const SDL_assert_data *item = triggered_assertions;
     90
     91    /* only do this if the app hasn't assigned an assertion handler. */
     92    if ((item != NULL) && (assertion_handler != SDL_PromptAssertion)) {
     93        debug_print("\n\nSDL assertion report.\n");
     94        debug_print("All SDL assertions between last init/quit:\n\n");
     95
     96        while (item != NULL) {
     97            debug_print(
     98                "'%s'\n"
     99                "    * %s (%s:%d)\n"
    100                "    * triggered %u time%s.\n"
    101                "    * always ignore: %s.\n",
    102                item->condition, item->function, item->filename,
    103                item->linenum, item->trigger_count,
    104                (item->trigger_count == 1) ? "" : "s",
    105                item->always_ignore ? "yes" : "no");
    106            item = item->next;
    107        }
    108        debug_print("\n");
    109
    110        SDL_ResetAssertionReport();
    111    }
    112}
    113
    114static void SDL_ExitProcess(int exitcode)
    115{
    116#ifdef __WIN32__
    117    ExitProcess(exitcode);
    118#else
    119    _exit(exitcode);
    120#endif
    121}
    122
    123static void SDL_AbortAssertion(void)
    124{
    125    SDL_Quit();
    126    SDL_ExitProcess(42);
    127}
    128
    129
    130static SDL_assert_state
    131SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
    132{
    133#ifdef __WIN32__
    134    #define ENDLINE "\r\n"
    135#else
    136    #define ENDLINE "\n"
    137#endif
    138
    139    const char *envr;
    140    SDL_assert_state state = SDL_ASSERTION_ABORT;
    141    SDL_Window *window;
    142    SDL_MessageBoxData messagebox;
    143    SDL_MessageBoxButtonData buttons[] = {
    144        {   0,  SDL_ASSERTION_RETRY,            "Retry" },
    145        {   0,  SDL_ASSERTION_BREAK,            "Break" },
    146        {   0,  SDL_ASSERTION_ABORT,            "Abort" },
    147        {   SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
    148                SDL_ASSERTION_IGNORE,           "Ignore" },
    149        {   SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
    150                SDL_ASSERTION_ALWAYS_IGNORE,    "Always Ignore" }
    151    };
    152    char *message;
    153    int selected;
    154
    155    (void) userdata;  /* unused in default handler. */
    156
    157    message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
    158    if (!message) {
    159        /* Uh oh, we're in real trouble now... */
    160        return SDL_ASSERTION_ABORT;
    161    }
    162    SDL_snprintf(message, SDL_MAX_LOG_MESSAGE,
    163                 "Assertion failure at %s (%s:%d), triggered %u %s:" ENDLINE
    164                    "  '%s'",
    165                 data->function, data->filename, data->linenum,
    166                 data->trigger_count, (data->trigger_count == 1) ? "time" : "times",
    167                 data->condition);
    168
    169    debug_print("\n\n%s\n\n", message);
    170
    171    /* let env. variable override, so unit tests won't block in a GUI. */
    172    envr = SDL_getenv("SDL_ASSERT");
    173    if (envr != NULL) {
    174        SDL_stack_free(message);
    175
    176        if (SDL_strcmp(envr, "abort") == 0) {
    177            return SDL_ASSERTION_ABORT;
    178        } else if (SDL_strcmp(envr, "break") == 0) {
    179            return SDL_ASSERTION_BREAK;
    180        } else if (SDL_strcmp(envr, "retry") == 0) {
    181            return SDL_ASSERTION_RETRY;
    182        } else if (SDL_strcmp(envr, "ignore") == 0) {
    183            return SDL_ASSERTION_IGNORE;
    184        } else if (SDL_strcmp(envr, "always_ignore") == 0) {
    185            return SDL_ASSERTION_ALWAYS_IGNORE;
    186        } else {
    187            return SDL_ASSERTION_ABORT;  /* oh well. */
    188        }
    189    }
    190
    191    /* Leave fullscreen mode, if possible (scary!) */
    192    window = SDL_GetFocusWindow();
    193    if (window) {
    194        if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) {
    195            SDL_MinimizeWindow(window);
    196        } else {
    197            /* !!! FIXME: ungrab the input if we're not fullscreen? */
    198            /* No need to mess with the window */
    199            window = NULL;
    200        }
    201    }
    202
    203    /* Show a messagebox if we can, otherwise fall back to stdio */
    204    SDL_zero(messagebox);
    205    messagebox.flags = SDL_MESSAGEBOX_WARNING;
    206    messagebox.window = window;
    207    messagebox.title = "Assertion Failed";
    208    messagebox.message = message;
    209    messagebox.numbuttons = SDL_arraysize(buttons);
    210    messagebox.buttons = buttons;
    211
    212    if (SDL_ShowMessageBox(&messagebox, &selected) == 0) {
    213        if (selected == -1) {
    214            state = SDL_ASSERTION_IGNORE;
    215        } else {
    216            state = (SDL_assert_state)selected;
    217        }
    218    }
    219#ifdef HAVE_STDIO_H
    220    else
    221    {
    222        /* this is a little hacky. */
    223        for ( ; ; ) {
    224            char buf[32];
    225            fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
    226            fflush(stderr);
    227            if (fgets(buf, sizeof (buf), stdin) == NULL) {
    228                break;
    229            }
    230
    231            if (SDL_strcmp(buf, "a") == 0) {
    232                state = SDL_ASSERTION_ABORT;
    233                break;
    234            } else if (SDL_strcmp(buf, "b") == 0) {
    235                state = SDL_ASSERTION_BREAK;
    236                break;
    237            } else if (SDL_strcmp(buf, "r") == 0) {
    238                state = SDL_ASSERTION_RETRY;
    239                break;
    240            } else if (SDL_strcmp(buf, "i") == 0) {
    241                state = SDL_ASSERTION_IGNORE;
    242                break;
    243            } else if (SDL_strcmp(buf, "A") == 0) {
    244                state = SDL_ASSERTION_ALWAYS_IGNORE;
    245                break;
    246            }
    247        }
    248    }
    249#endif /* HAVE_STDIO_H */
    250
    251    /* Re-enter fullscreen mode */
    252    if (window) {
    253        SDL_RestoreWindow(window);
    254    }
    255
    256    SDL_stack_free(message);
    257
    258    return state;
    259}
    260
    261
    262SDL_assert_state
    263SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file,
    264                    int line)
    265{
    266    static int assertion_running = 0;
    267    static SDL_SpinLock spinlock = 0;
    268    SDL_assert_state state = SDL_ASSERTION_IGNORE;
    269
    270    SDL_AtomicLock(&spinlock);
    271    if (assertion_mutex == NULL) { /* never called SDL_Init()? */
    272        assertion_mutex = SDL_CreateMutex();
    273        if (assertion_mutex == NULL) {
    274            SDL_AtomicUnlock(&spinlock);
    275            return SDL_ASSERTION_IGNORE;   /* oh well, I guess. */
    276        }
    277    }
    278    SDL_AtomicUnlock(&spinlock);
    279
    280    if (SDL_LockMutex(assertion_mutex) < 0) {
    281        return SDL_ASSERTION_IGNORE;   /* oh well, I guess. */
    282    }
    283
    284    /* doing this because Visual C is upset over assigning in the macro. */
    285    if (data->trigger_count == 0) {
    286        data->function = func;
    287        data->filename = file;
    288        data->linenum = line;
    289    }
    290
    291    SDL_AddAssertionToReport(data);
    292
    293    assertion_running++;
    294    if (assertion_running > 1) {   /* assert during assert! Abort. */
    295        if (assertion_running == 2) {
    296            SDL_AbortAssertion();
    297        } else if (assertion_running == 3) {  /* Abort asserted! */
    298            SDL_ExitProcess(42);
    299        } else {
    300            while (1) { /* do nothing but spin; what else can you do?! */ }
    301        }
    302    }
    303
    304    if (!data->always_ignore) {
    305        state = assertion_handler(data, assertion_userdata);
    306    }
    307
    308    switch (state)
    309    {
    310        case SDL_ASSERTION_ABORT:
    311            SDL_AbortAssertion();
    312            return SDL_ASSERTION_IGNORE;  /* shouldn't return, but oh well. */
    313
    314        case SDL_ASSERTION_ALWAYS_IGNORE:
    315            state = SDL_ASSERTION_IGNORE;
    316            data->always_ignore = 1;
    317            break;
    318
    319        case SDL_ASSERTION_IGNORE:
    320        case SDL_ASSERTION_RETRY:
    321        case SDL_ASSERTION_BREAK:
    322            break;  /* macro handles these. */
    323    }
    324
    325    assertion_running--;
    326    SDL_UnlockMutex(assertion_mutex);
    327
    328    return state;
    329}
    330
    331
    332void SDL_AssertionsQuit(void)
    333{
    334    SDL_GenerateAssertionReport();
    335    if (assertion_mutex != NULL) {
    336        SDL_DestroyMutex(assertion_mutex);
    337        assertion_mutex = NULL;
    338    }
    339}
    340
    341void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
    342{
    343    if (handler != NULL) {
    344        assertion_handler = handler;
    345        assertion_userdata = userdata;
    346    } else {
    347        assertion_handler = SDL_PromptAssertion;
    348        assertion_userdata = NULL;
    349    }
    350}
    351
    352const SDL_assert_data *SDL_GetAssertionReport(void)
    353{
    354    return triggered_assertions;
    355}
    356
    357void SDL_ResetAssertionReport(void)
    358{
    359    SDL_assert_data *next = NULL;
    360    SDL_assert_data *item;
    361    for (item = triggered_assertions; item != NULL; item = next) {
    362        next = (SDL_assert_data *) item->next;
    363        item->always_ignore = SDL_FALSE;
    364        item->trigger_count = 0;
    365        item->next = NULL;
    366    }
    367
    368    triggered_assertions = NULL;
    369}
    370
    371SDL_AssertionHandler SDL_GetDefaultAssertionHandler(void)
    372{
    373    return SDL_PromptAssertion;
    374}
    375
    376SDL_AssertionHandler SDL_GetAssertionHandler(void **userdata)
    377{
    378    if (userdata != NULL) {
    379        *userdata = assertion_userdata;
    380    }
    381    return assertion_handler;
    382}
    383
    384/* vi: set ts=4 sw=4 expandtab: */