src/SDL_assert.c
author Ryan C. Gordon <icculus@icculus.org>
Thu, 21 Apr 2016 03:16:44 -0400
changeset 11729 d1ce8396c356
parent 11626 2eaf345a2a30
child 11811 5d94cb6b24d3
permissions -rw-r--r--
Initial shot at a renderer target for Apple's Metal API.

This isn't complete, but is enough to run testsprite2. It's currently
Mac-only; with a little work to figure out how to properly glue in a Metal
layer to a UIView, this will likely work on iOS, too.

This is only wired up to the configure script right now, and disabled by
default. CMake and Xcode still need their bits filled in as appropriate.
slouken@3647
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@10737
     3
  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
slouken@3647
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@3647
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@3647
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@3647
    20
*/
icculus@8093
    21
#include "./SDL_internal.h"
slouken@3647
    22
slouken@7828
    23
#if defined(__WIN32__)
slouken@7828
    24
#include "core/windows/SDL_windows.h"
slouken@7828
    25
#endif
slouken@7828
    26
slouken@3651
    27
#include "SDL.h"
slouken@5006
    28
#include "SDL_atomic.h"
slouken@6621
    29
#include "SDL_messagebox.h"
slouken@6621
    30
#include "SDL_video.h"
slouken@3647
    31
#include "SDL_assert.h"
slouken@4472
    32
#include "SDL_assert_c.h"
slouken@3671
    33
#include "video/SDL_sysvideo.h"
slouken@3647
    34
slouken@5086
    35
#ifdef __WIN32__
slouken@5086
    36
#ifndef WS_OVERLAPPEDWINDOW
slouken@5086
    37
#define WS_OVERLAPPEDWINDOW 0
slouken@5086
    38
#endif
slouken@3647
    39
#else  /* fprintf, _exit(), etc. */
slouken@3647
    40
#include <stdio.h>
slouken@3647
    41
#include <stdlib.h>
dludwig@8341
    42
#if ! defined(__WINRT__)
icculus@3648
    43
#include <unistd.h>
slouken@3647
    44
#endif
dludwig@8341
    45
#endif
slouken@3647
    46
icculus@11017
    47
#if defined(__EMSCRIPTEN__)
icculus@11017
    48
#include <emscripten.h>
icculus@11017
    49
#endif
icculus@11017
    50
icculus@11017
    51
slouken@11272
    52
static SDL_assert_state SDLCALL
icculus@3670
    53
SDL_PromptAssertion(const SDL_assert_data *data, void *userdata);
icculus@3670
    54
icculus@3670
    55
/*
icculus@3670
    56
 * We keep all triggered assertions in a singly-linked list so we can
slouken@3647
    57
 *  generate a report later.
slouken@3647
    58
 */
icculus@5541
    59
static SDL_assert_data *triggered_assertions = NULL;
icculus@3670
    60
icculus@11015
    61
#ifndef SDL_THREADS_DISABLED
icculus@3670
    62
static SDL_mutex *assertion_mutex = NULL;
icculus@11015
    63
#endif
icculus@11015
    64
icculus@3670
    65
static SDL_AssertionHandler assertion_handler = SDL_PromptAssertion;
icculus@3670
    66
static void *assertion_userdata = NULL;
slouken@3647
    67
icculus@3648
    68
#ifdef __GNUC__
icculus@3661
    69
static void
icculus@3661
    70
debug_print(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
icculus@3648
    71
#endif
icculus@3648
    72
icculus@3648
    73
static void
icculus@3648
    74
debug_print(const char *fmt, ...)
slouken@3647
    75
{
slouken@3647
    76
    va_list ap;
slouken@3647
    77
    va_start(ap, fmt);
slouken@6621
    78
    SDL_LogMessageV(SDL_LOG_CATEGORY_ASSERT, SDL_LOG_PRIORITY_WARN, fmt, ap);
slouken@3647
    79
    va_end(ap);
slouken@3647
    80
}
slouken@3647
    81
slouken@3647
    82
slouken@3647
    83
static void SDL_AddAssertionToReport(SDL_assert_data *data)
slouken@3647
    84
{
slouken@3647
    85
    /* (data) is always a static struct defined with the assert macros, so
slouken@3647
    86
       we don't have to worry about copying or allocating them. */
icculus@5541
    87
    data->trigger_count++;
icculus@5541
    88
    if (data->trigger_count == 1) {  /* not yet added? */
slouken@3647
    89
        data->next = triggered_assertions;
slouken@3647
    90
        triggered_assertions = data;
slouken@3647
    91
    }
slouken@3647
    92
}
slouken@3647
    93
icculus@3670
    94
slouken@3647
    95
static void SDL_GenerateAssertionReport(void)
slouken@3647
    96
{
icculus@5541
    97
    const SDL_assert_data *item = triggered_assertions;
icculus@3670
    98
icculus@3670
    99
    /* only do this if the app hasn't assigned an assertion handler. */
icculus@5541
   100
    if ((item != NULL) && (assertion_handler != SDL_PromptAssertion)) {
slouken@3647
   101
        debug_print("\n\nSDL assertion report.\n");
slouken@3647
   102
        debug_print("All SDL assertions between last init/quit:\n\n");
slouken@3647
   103
icculus@5541
   104
        while (item != NULL) {
slouken@3647
   105
            debug_print(
slouken@3647
   106
                "'%s'\n"
slouken@3647
   107
                "    * %s (%s:%d)\n"
slouken@3647
   108
                "    * triggered %u time%s.\n"
slouken@3647
   109
                "    * always ignore: %s.\n",
slouken@3647
   110
                item->condition, item->function, item->filename,
slouken@3647
   111
                item->linenum, item->trigger_count,
slouken@3647
   112
                (item->trigger_count == 1) ? "" : "s",
slouken@3647
   113
                item->always_ignore ? "yes" : "no");
slouken@3647
   114
            item = item->next;
slouken@3647
   115
        }
slouken@3647
   116
        debug_print("\n");
slouken@3647
   117
icculus@3670
   118
        SDL_ResetAssertionReport();
slouken@3647
   119
    }
slouken@3647
   120
}
slouken@3647
   121
slouken@10616
   122
slouken@10616
   123
static SDL_NORETURN void SDL_ExitProcess(int exitcode)
slouken@3647
   124
{
slouken@5086
   125
#ifdef __WIN32__
icculus@6305
   126
    ExitProcess(exitcode);
icculus@11017
   127
#elif defined(__EMSCRIPTEN__)
icculus@11017
   128
    emscripten_cancel_main_loop();  /* this should "kill" the app. */
icculus@11017
   129
    emscripten_force_exit(exitcode);  /* this should "kill" the app. */
icculus@11017
   130
    exit(exitcode);
slouken@3656
   131
#else
icculus@6305
   132
    _exit(exitcode);
slouken@3647
   133
#endif
slouken@3647
   134
}
icculus@3661
   135
slouken@10616
   136
slouken@10616
   137
static SDL_NORETURN void SDL_AbortAssertion(void)
icculus@3661
   138
{
icculus@3661
   139
    SDL_Quit();
icculus@3661
   140
    SDL_ExitProcess(42);
icculus@3661
   141
}
icculus@3661
   142
slouken@3647
   143
slouken@11272
   144
static SDL_assert_state SDLCALL
icculus@3670
   145
SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
slouken@3647
   146
{
icculus@6759
   147
#ifdef __WIN32__
icculus@6759
   148
    #define ENDLINE "\r\n"
icculus@6759
   149
#else
icculus@6759
   150
    #define ENDLINE "\n"
icculus@6759
   151
#endif
icculus@6759
   152
slouken@3647
   153
    const char *envr;
slouken@3657
   154
    SDL_assert_state state = SDL_ASSERTION_ABORT;
slouken@3685
   155
    SDL_Window *window;
slouken@6621
   156
    SDL_MessageBoxData messagebox;
slouken@6621
   157
    SDL_MessageBoxButtonData buttons[] = {
slouken@6621
   158
        {   0,  SDL_ASSERTION_RETRY,            "Retry" },
slouken@6621
   159
        {   0,  SDL_ASSERTION_BREAK,            "Break" },
slouken@6621
   160
        {   0,  SDL_ASSERTION_ABORT,            "Abort" },
slouken@6621
   161
        {   SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
slouken@6621
   162
                SDL_ASSERTION_IGNORE,           "Ignore" },
slouken@6621
   163
        {   SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
slouken@6621
   164
                SDL_ASSERTION_ALWAYS_IGNORE,    "Always Ignore" }
slouken@6621
   165
    };
slouken@6621
   166
    char *message;
slouken@6621
   167
    int selected;
slouken@3647
   168
icculus@3670
   169
    (void) userdata;  /* unused in default handler. */
icculus@3670
   170
slouken@6621
   171
    message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
slouken@6621
   172
    if (!message) {
slouken@6621
   173
        /* Uh oh, we're in real trouble now... */
slouken@6621
   174
        return SDL_ASSERTION_ABORT;
slouken@6621
   175
    }
slouken@6621
   176
    SDL_snprintf(message, SDL_MAX_LOG_MESSAGE,
icculus@6759
   177
                 "Assertion failure at %s (%s:%d), triggered %u %s:" ENDLINE
icculus@6759
   178
                    "  '%s'",
slouken@6621
   179
                 data->function, data->filename, data->linenum,
slouken@6621
   180
                 data->trigger_count, (data->trigger_count == 1) ? "time" : "times",
slouken@6621
   181
                 data->condition);
slouken@6621
   182
slouken@6621
   183
    debug_print("\n\n%s\n\n", message);
slouken@3647
   184
slouken@3655
   185
    /* let env. variable override, so unit tests won't block in a GUI. */
slouken@3647
   186
    envr = SDL_getenv("SDL_ASSERT");
slouken@3647
   187
    if (envr != NULL) {
slouken@6621
   188
        SDL_stack_free(message);
slouken@6621
   189
slouken@3647
   190
        if (SDL_strcmp(envr, "abort") == 0) {
slouken@3647
   191
            return SDL_ASSERTION_ABORT;
slouken@3647
   192
        } else if (SDL_strcmp(envr, "break") == 0) {
slouken@3647
   193
            return SDL_ASSERTION_BREAK;
slouken@3647
   194
        } else if (SDL_strcmp(envr, "retry") == 0) {
slouken@3647
   195
            return SDL_ASSERTION_RETRY;
slouken@3647
   196
        } else if (SDL_strcmp(envr, "ignore") == 0) {
slouken@3647
   197
            return SDL_ASSERTION_IGNORE;
slouken@3647
   198
        } else if (SDL_strcmp(envr, "always_ignore") == 0) {
slouken@3647
   199
            return SDL_ASSERTION_ALWAYS_IGNORE;
slouken@3647
   200
        } else {
slouken@3647
   201
            return SDL_ASSERTION_ABORT;  /* oh well. */
slouken@3647
   202
        }
slouken@3647
   203
    }
slouken@3647
   204
slouken@3657
   205
    /* Leave fullscreen mode, if possible (scary!) */
slouken@3657
   206
    window = SDL_GetFocusWindow();
slouken@3657
   207
    if (window) {
slouken@3657
   208
        if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) {
slouken@3657
   209
            SDL_MinimizeWindow(window);
slouken@3657
   210
        } else {
icculus@3670
   211
            /* !!! FIXME: ungrab the input if we're not fullscreen? */
slouken@3657
   212
            /* No need to mess with the window */
slouken@6621
   213
            window = NULL;
slouken@3657
   214
        }
slouken@3657
   215
    }
slouken@3657
   216
slouken@6621
   217
    /* Show a messagebox if we can, otherwise fall back to stdio */
slouken@6621
   218
    SDL_zero(messagebox);
slouken@6621
   219
    messagebox.flags = SDL_MESSAGEBOX_WARNING;
slouken@6621
   220
    messagebox.window = window;
slouken@6621
   221
    messagebox.title = "Assertion Failed";
slouken@6621
   222
    messagebox.message = message;
slouken@6621
   223
    messagebox.numbuttons = SDL_arraysize(buttons);
slouken@6621
   224
    messagebox.buttons = buttons;
slouken@3647
   225
slouken@6621
   226
    if (SDL_ShowMessageBox(&messagebox, &selected) == 0) {
slouken@6621
   227
        if (selected == -1) {
slouken@3657
   228
            state = SDL_ASSERTION_IGNORE;
slouken@6621
   229
        } else {
slouken@6621
   230
            state = (SDL_assert_state)selected;
slouken@3647
   231
        }
slouken@3647
   232
    }
icculus@11017
   233
slouken@6621
   234
    else
slouken@6621
   235
    {
icculus@11017
   236
#if defined(__EMSCRIPTEN__)
icculus@11017
   237
        /* This is nasty, but we can't block on a custom UI. */
icculus@11017
   238
        for ( ; ; ) {
icculus@11017
   239
            SDL_bool okay = SDL_TRUE;
icculus@11017
   240
            char *buf = (char *) EM_ASM_INT({
icculus@11017
   241
                var str =
icculus@11017
   242
                    Pointer_stringify($0) + '\n\n' +
icculus@11017
   243
                    'Abort/Retry/Ignore/AlwaysIgnore? [ariA] :';
icculus@11017
   244
                var reply = window.prompt(str, "i");
icculus@11017
   245
                if (reply === null) {
icculus@11017
   246
                    reply = "i";
icculus@11017
   247
                }
icculus@11017
   248
                return allocate(intArrayFromString(reply), 'i8', ALLOC_NORMAL);
icculus@11017
   249
            }, message);
icculus@11017
   250
icculus@11017
   251
            if (SDL_strcmp(buf, "a") == 0) {
icculus@11017
   252
                state = SDL_ASSERTION_ABORT;
icculus@11017
   253
            /* (currently) no break functionality on Emscripten
icculus@11017
   254
            } else if (SDL_strcmp(buf, "b") == 0) {
icculus@11017
   255
                state = SDL_ASSERTION_BREAK; */
icculus@11017
   256
            } else if (SDL_strcmp(buf, "r") == 0) {
icculus@11017
   257
                state = SDL_ASSERTION_RETRY;
icculus@11017
   258
            } else if (SDL_strcmp(buf, "i") == 0) {
icculus@11017
   259
                state = SDL_ASSERTION_IGNORE;
icculus@11017
   260
            } else if (SDL_strcmp(buf, "A") == 0) {
icculus@11017
   261
                state = SDL_ASSERTION_ALWAYS_IGNORE;
icculus@11017
   262
            } else {
icculus@11017
   263
                okay = SDL_FALSE;
icculus@11017
   264
            }
icculus@11017
   265
            free(buf);
icculus@11017
   266
icculus@11017
   267
            if (okay) {
icculus@11017
   268
                break;
icculus@11017
   269
            }
icculus@11017
   270
        }
icculus@11017
   271
#elif defined(HAVE_STDIO_H)
slouken@6621
   272
        /* this is a little hacky. */
slouken@6621
   273
        for ( ; ; ) {
slouken@6621
   274
            char buf[32];
slouken@6621
   275
            fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
slouken@6621
   276
            fflush(stderr);
slouken@6621
   277
            if (fgets(buf, sizeof (buf), stdin) == NULL) {
slouken@6621
   278
                break;
slouken@6621
   279
            }
slouken@6621
   280
slouken@11626
   281
            if (SDL_strncmp(buf, "a", 1) == 0) {
slouken@6621
   282
                state = SDL_ASSERTION_ABORT;
slouken@6621
   283
                break;
slouken@11626
   284
            } else if (SDL_strncmp(buf, "b", 1) == 0) {
slouken@6621
   285
                state = SDL_ASSERTION_BREAK;
slouken@6621
   286
                break;
slouken@11626
   287
            } else if (SDL_strncmp(buf, "r", 1) == 0) {
slouken@6621
   288
                state = SDL_ASSERTION_RETRY;
slouken@6621
   289
                break;
slouken@11626
   290
            } else if (SDL_strncmp(buf, "i", 1) == 0) {
slouken@6621
   291
                state = SDL_ASSERTION_IGNORE;
slouken@6621
   292
                break;
slouken@11626
   293
            } else if (SDL_strncmp(buf, "A", 1) == 0) {
slouken@6621
   294
                state = SDL_ASSERTION_ALWAYS_IGNORE;
slouken@6621
   295
                break;
slouken@6621
   296
            }
slouken@6621
   297
        }
icculus@11017
   298
#endif /* HAVE_STDIO_H */
slouken@6621
   299
    }
slouken@3647
   300
slouken@3657
   301
    /* Re-enter fullscreen mode */
slouken@3657
   302
    if (window) {
slouken@3657
   303
        SDL_RestoreWindow(window);
slouken@3657
   304
    }
slouken@3657
   305
slouken@6621
   306
    SDL_stack_free(message);
slouken@6621
   307
slouken@3657
   308
    return state;
slouken@3647
   309
}
slouken@3647
   310
slouken@3647
   311
slouken@3647
   312
SDL_assert_state
slouken@3655
   313
SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file,
slouken@3655
   314
                    int line)
slouken@3647
   315
{
icculus@11015
   316
    SDL_assert_state state = SDL_ASSERTION_IGNORE;
icculus@3661
   317
    static int assertion_running = 0;
icculus@11015
   318
icculus@11015
   319
#ifndef SDL_THREADS_DISABLED
icculus@3662
   320
    static SDL_SpinLock spinlock = 0;
icculus@3662
   321
    SDL_AtomicLock(&spinlock);
icculus@3662
   322
    if (assertion_mutex == NULL) { /* never called SDL_Init()? */
icculus@3662
   323
        assertion_mutex = SDL_CreateMutex();
icculus@3662
   324
        if (assertion_mutex == NULL) {
icculus@3662
   325
            SDL_AtomicUnlock(&spinlock);
icculus@3662
   326
            return SDL_ASSERTION_IGNORE;   /* oh well, I guess. */
icculus@3662
   327
        }
icculus@3662
   328
    }
icculus@3662
   329
    SDL_AtomicUnlock(&spinlock);
icculus@3662
   330
slouken@3647
   331
    if (SDL_LockMutex(assertion_mutex) < 0) {
slouken@3647
   332
        return SDL_ASSERTION_IGNORE;   /* oh well, I guess. */
slouken@3647
   333
    }
icculus@11015
   334
#endif
slouken@3647
   335
slouken@3647
   336
    /* doing this because Visual C is upset over assigning in the macro. */
slouken@3647
   337
    if (data->trigger_count == 0) {
slouken@3647
   338
        data->function = func;
slouken@3655
   339
        data->filename = file;
slouken@3655
   340
        data->linenum = line;
slouken@3647
   341
    }
slouken@3647
   342
slouken@3647
   343
    SDL_AddAssertionToReport(data);
slouken@3647
   344
icculus@3661
   345
    assertion_running++;
icculus@3661
   346
    if (assertion_running > 1) {   /* assert during assert! Abort. */
icculus@3661
   347
        if (assertion_running == 2) {
icculus@3661
   348
            SDL_AbortAssertion();
icculus@3661
   349
        } else if (assertion_running == 3) {  /* Abort asserted! */
icculus@3661
   350
            SDL_ExitProcess(42);
icculus@3661
   351
        } else {
icculus@3661
   352
            while (1) { /* do nothing but spin; what else can you do?! */ }
icculus@3661
   353
        }
slouken@3647
   354
    }
slouken@3647
   355
icculus@3661
   356
    if (!data->always_ignore) {
icculus@3670
   357
        state = assertion_handler(data, assertion_userdata);
icculus@3661
   358
    }
slouken@3647
   359
slouken@3647
   360
    switch (state)
slouken@3647
   361
    {
slouken@3647
   362
        case SDL_ASSERTION_ABORT:
slouken@3647
   363
            SDL_AbortAssertion();
slouken@3647
   364
            return SDL_ASSERTION_IGNORE;  /* shouldn't return, but oh well. */
slouken@3647
   365
slouken@3647
   366
        case SDL_ASSERTION_ALWAYS_IGNORE:
slouken@3647
   367
            state = SDL_ASSERTION_IGNORE;
slouken@3647
   368
            data->always_ignore = 1;
slouken@3647
   369
            break;
slouken@3647
   370
slouken@3647
   371
        case SDL_ASSERTION_IGNORE:
slouken@3647
   372
        case SDL_ASSERTION_RETRY:
slouken@3647
   373
        case SDL_ASSERTION_BREAK:
slouken@3647
   374
            break;  /* macro handles these. */
slouken@3647
   375
    }
slouken@3647
   376
icculus@3661
   377
    assertion_running--;
icculus@11015
   378
icculus@11015
   379
#ifndef SDL_THREADS_DISABLED
slouken@3647
   380
    SDL_UnlockMutex(assertion_mutex);
icculus@11015
   381
#endif
slouken@3647
   382
slouken@3647
   383
    return state;
slouken@3647
   384
}
slouken@3647
   385
slouken@3647
   386
slouken@3647
   387
void SDL_AssertionsQuit(void)
slouken@3647
   388
{
slouken@3647
   389
    SDL_GenerateAssertionReport();
icculus@11015
   390
#ifndef SDL_THREADS_DISABLED
icculus@3664
   391
    if (assertion_mutex != NULL) {
icculus@3664
   392
        SDL_DestroyMutex(assertion_mutex);
icculus@3664
   393
        assertion_mutex = NULL;
icculus@3664
   394
    }
icculus@11015
   395
#endif
icculus@3670
   396
}
icculus@3670
   397
icculus@3670
   398
void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
icculus@3670
   399
{
icculus@3670
   400
    if (handler != NULL) {
icculus@3670
   401
        assertion_handler = handler;
icculus@3670
   402
        assertion_userdata = userdata;
icculus@3670
   403
    } else {
icculus@3670
   404
        assertion_handler = SDL_PromptAssertion;
icculus@3670
   405
        assertion_userdata = NULL;
icculus@3670
   406
    }
icculus@3670
   407
}
icculus@3670
   408
icculus@3670
   409
const SDL_assert_data *SDL_GetAssertionReport(void)
icculus@3670
   410
{
icculus@3670
   411
    return triggered_assertions;
icculus@3670
   412
}
icculus@3670
   413
icculus@3670
   414
void SDL_ResetAssertionReport(void)
icculus@3670
   415
{
icculus@3670
   416
    SDL_assert_data *next = NULL;
icculus@5541
   417
    SDL_assert_data *item;
icculus@5541
   418
    for (item = triggered_assertions; item != NULL; item = next) {
icculus@3670
   419
        next = (SDL_assert_data *) item->next;
icculus@3670
   420
        item->always_ignore = SDL_FALSE;
icculus@3670
   421
        item->trigger_count = 0;
icculus@3670
   422
        item->next = NULL;
icculus@3670
   423
    }
icculus@3670
   424
icculus@5541
   425
    triggered_assertions = NULL;
slouken@3647
   426
}
slouken@3647
   427
icculus@8167
   428
SDL_AssertionHandler SDL_GetDefaultAssertionHandler(void)
icculus@8167
   429
{
icculus@8167
   430
    return SDL_PromptAssertion;
icculus@8167
   431
}
icculus@8167
   432
icculus@8167
   433
SDL_AssertionHandler SDL_GetAssertionHandler(void **userdata)
icculus@8167
   434
{
icculus@8167
   435
    if (userdata != NULL) {
icculus@8167
   436
        *userdata = assertion_userdata;
icculus@8167
   437
    }
icculus@8167
   438
    return assertion_handler;
icculus@8167
   439
}
icculus@8167
   440
slouken@3647
   441
/* vi: set ts=4 sw=4 expandtab: */