src/SDL_log.c
author David Ludwig <dludwig@pobox.com>
Tue, 13 Aug 2013 20:09:52 -0400
changeset 8478 337b5dc0797b
parent 8477 ad08f0d710f3
child 8535 e8ee0708ef5c
permissions -rw-r--r--
WinRT: build fixes and additional WinRT-related integrations with SDL 2.0.0
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 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 /* Simple log messages in SDL */
    24 
    25 #include "SDL_log.h"
    26 
    27 #if HAVE_STDIO_H
    28 #include <stdio.h>
    29 #endif
    30 
    31 #if defined(__WIN32__) || defined(__WINRT__)
    32 #include "core/windows/SDL_windows.h"
    33 #elif defined(__ANDROID__)
    34 #include <android/log.h>
    35 #endif
    36 
    37 #define DEFAULT_PRIORITY                SDL_LOG_PRIORITY_CRITICAL
    38 #define DEFAULT_ASSERT_PRIORITY         SDL_LOG_PRIORITY_WARN
    39 #define DEFAULT_APPLICATION_PRIORITY    SDL_LOG_PRIORITY_INFO
    40 #define DEFAULT_TEST_PRIORITY           SDL_LOG_PRIORITY_VERBOSE
    41 
    42 /* Forward definition of error function */
    43 extern int SDL_SetError(const char *fmt, ...);
    44 
    45 typedef struct SDL_LogLevel
    46 {
    47     int category;
    48     SDL_LogPriority priority;
    49     struct SDL_LogLevel *next;
    50 } SDL_LogLevel;
    51 
    52 /* The default log output function */
    53 static void SDL_LogOutput(void *userdata,
    54                           int category, SDL_LogPriority priority,
    55                           const char *message);
    56 
    57 static SDL_LogLevel *SDL_loglevels;
    58 static SDL_LogPriority SDL_default_priority = DEFAULT_PRIORITY;
    59 static SDL_LogPriority SDL_assert_priority = DEFAULT_ASSERT_PRIORITY;
    60 static SDL_LogPriority SDL_application_priority = DEFAULT_APPLICATION_PRIORITY;
    61 static SDL_LogPriority SDL_test_priority = DEFAULT_TEST_PRIORITY;
    62 static SDL_LogOutputFunction SDL_log_function = SDL_LogOutput;
    63 static void *SDL_log_userdata = NULL;
    64 
    65 static const char *SDL_priority_prefixes[SDL_NUM_LOG_PRIORITIES] = {
    66     NULL,
    67     "VERBOSE",
    68     "DEBUG",
    69     "INFO",
    70     "WARN",
    71     "ERROR",
    72     "CRITICAL"
    73 };
    74 
    75 #ifdef __ANDROID__
    76 static const char *SDL_category_prefixes[SDL_LOG_CATEGORY_RESERVED1] = {
    77     "APP",
    78     "ERROR",
    79     "SYSTEM",
    80     "AUDIO",
    81     "VIDEO",
    82     "RENDER",
    83     "INPUT"
    84 };
    85 
    86 static int SDL_android_priority[SDL_NUM_LOG_PRIORITIES] = {
    87     ANDROID_LOG_VERBOSE,
    88     ANDROID_LOG_DEBUG,
    89     ANDROID_LOG_INFO,
    90     ANDROID_LOG_WARN,
    91     ANDROID_LOG_ERROR,
    92     ANDROID_LOG_FATAL
    93 };
    94 #endif /* __ANDROID__ */
    95 
    96 
    97 void
    98 SDL_LogSetAllPriority(SDL_LogPriority priority)
    99 {
   100     SDL_LogLevel *entry;
   101 
   102     for (entry = SDL_loglevels; entry; entry = entry->next) {
   103         entry->priority = priority;
   104     }
   105     SDL_default_priority = priority;
   106     SDL_assert_priority = priority;
   107     SDL_application_priority = priority;
   108 }
   109 
   110 void
   111 SDL_LogSetPriority(int category, SDL_LogPriority priority)
   112 {
   113     SDL_LogLevel *entry;
   114 
   115     for (entry = SDL_loglevels; entry; entry = entry->next) {
   116         if (entry->category == category) {
   117             entry->priority = priority;
   118             return;
   119         }
   120     }
   121 
   122     /* Create a new entry */
   123     entry = (SDL_LogLevel *)SDL_malloc(sizeof(*entry));
   124     if (entry) {
   125         entry->category = category;
   126         entry->priority = priority;
   127         entry->next = SDL_loglevels;
   128         SDL_loglevels = entry;
   129     }
   130 }
   131 
   132 SDL_LogPriority
   133 SDL_LogGetPriority(int category)
   134 {
   135     SDL_LogLevel *entry;
   136 
   137     for (entry = SDL_loglevels; entry; entry = entry->next) {
   138         if (entry->category == category) {
   139             return entry->priority;
   140         }
   141     }
   142 
   143     if (category == SDL_LOG_CATEGORY_TEST) {
   144         return SDL_test_priority;
   145     } else if (category == SDL_LOG_CATEGORY_APPLICATION) {
   146         return SDL_application_priority;
   147     } else if (category == SDL_LOG_CATEGORY_ASSERT) {
   148         return SDL_assert_priority;
   149     } else {
   150         return SDL_default_priority;
   151     }
   152 }
   153 
   154 void
   155 SDL_LogResetPriorities(void)
   156 {
   157     SDL_LogLevel *entry;
   158 
   159     while (SDL_loglevels) {
   160         entry = SDL_loglevels;
   161         SDL_loglevels = entry->next;
   162         SDL_free(entry);
   163     }
   164 
   165     SDL_default_priority = DEFAULT_PRIORITY;
   166     SDL_assert_priority = DEFAULT_ASSERT_PRIORITY;
   167     SDL_application_priority = DEFAULT_APPLICATION_PRIORITY;
   168     SDL_test_priority = DEFAULT_TEST_PRIORITY;
   169 }
   170 
   171 void
   172 SDL_Log(const char *fmt, ...)
   173 {
   174     va_list ap;
   175 
   176     va_start(ap, fmt);
   177     SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap);
   178     va_end(ap);
   179 }
   180 
   181 void
   182 SDL_LogVerbose(int category, const char *fmt, ...)
   183 {
   184     va_list ap;
   185 
   186     va_start(ap, fmt);
   187     SDL_LogMessageV(category, SDL_LOG_PRIORITY_VERBOSE, fmt, ap);
   188     va_end(ap);
   189 }
   190 
   191 void
   192 SDL_LogDebug(int category, const char *fmt, ...)
   193 {
   194     va_list ap;
   195 
   196     va_start(ap, fmt);
   197     SDL_LogMessageV(category, SDL_LOG_PRIORITY_DEBUG, fmt, ap);
   198     va_end(ap);
   199 }
   200 
   201 void
   202 SDL_LogInfo(int category, const char *fmt, ...)
   203 {
   204     va_list ap;
   205 
   206     va_start(ap, fmt);
   207     SDL_LogMessageV(category, SDL_LOG_PRIORITY_INFO, fmt, ap);
   208     va_end(ap);
   209 }
   210 
   211 void
   212 SDL_LogWarn(int category, const char *fmt, ...)
   213 {
   214     va_list ap;
   215 
   216     va_start(ap, fmt);
   217     SDL_LogMessageV(category, SDL_LOG_PRIORITY_WARN, fmt, ap);
   218     va_end(ap);
   219 }
   220 
   221 void
   222 SDL_LogError(int category, const char *fmt, ...)
   223 {
   224     va_list ap;
   225 
   226     va_start(ap, fmt);
   227     SDL_LogMessageV(category, SDL_LOG_PRIORITY_ERROR, fmt, ap);
   228     va_end(ap);
   229 }
   230 
   231 void
   232 SDL_LogCritical(int category, const char *fmt, ...)
   233 {
   234     va_list ap;
   235 
   236     va_start(ap, fmt);
   237     SDL_LogMessageV(category, SDL_LOG_PRIORITY_CRITICAL, fmt, ap);
   238     va_end(ap);
   239 }
   240 
   241 void
   242 SDL_LogMessage(int category, SDL_LogPriority priority, const char *fmt, ...)
   243 {
   244     va_list ap;
   245 
   246     va_start(ap, fmt);
   247     SDL_LogMessageV(category, priority, fmt, ap);
   248     va_end(ap);
   249 }
   250 
   251 #ifdef __ANDROID__
   252 static const char *
   253 GetCategoryPrefix(int category)
   254 {
   255     if (category < SDL_LOG_CATEGORY_RESERVED1) {
   256         return SDL_category_prefixes[category];
   257     }
   258     if (category < SDL_LOG_CATEGORY_CUSTOM) {
   259         return "RESERVED";
   260     }
   261     return "CUSTOM";
   262 }
   263 #endif /* __ANDROID__ */
   264 
   265 void
   266 SDL_LogMessageV(int category, SDL_LogPriority priority, const char *fmt, va_list ap)
   267 {
   268     char *message;
   269     size_t len;
   270 
   271     /* Nothing to do if we don't have an output function */
   272     if (!SDL_log_function) {
   273         return;
   274     }
   275 
   276     /* Make sure we don't exceed array bounds */
   277     if ((int)priority < 0 || priority >= SDL_NUM_LOG_PRIORITIES) {
   278         return;
   279     }
   280 
   281     /* See if we want to do anything with this message */
   282     if (priority < SDL_LogGetPriority(category)) {
   283         return;
   284     }
   285 
   286     message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
   287     if (!message) {
   288         return;
   289     }
   290 
   291     SDL_vsnprintf(message, SDL_MAX_LOG_MESSAGE, fmt, ap);
   292 
   293     /* Chop off final endline. */
   294     len = SDL_strlen(message);
   295     if ((len > 0) && (message[len-1] == '\n')) {
   296         message[--len] = '\0';
   297         if ((len > 0) && (message[len-1] == '\r')) {  /* catch "\r\n", too. */
   298             message[--len] = '\0';
   299         }
   300     }
   301 
   302     SDL_log_function(SDL_log_userdata, category, priority, message);
   303     SDL_stack_free(message);
   304 }
   305 
   306 #if defined(__WIN32__)
   307 /* Flag tracking the attachment of the console: 0=unattached, 1=attached, -1=error */
   308 static int consoleAttached = 0;
   309 
   310 /* Handle to stderr output of console. */
   311 static HANDLE stderrHandle = NULL;
   312 #endif
   313 
   314 static void
   315 SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority,
   316               const char *message)
   317 {
   318 #if defined(__WIN32__) || defined(__WINRT__)
   319     /* Way too many allocations here, urgh */
   320     /* Note: One can't call SDL_SetError here, since that function itself logs. */
   321     {
   322         char *output;
   323         size_t length;
   324         LPTSTR tstr;
   325 
   326 #ifndef __WINRT__
   327         BOOL attachResult;
   328         DWORD attachError;
   329         unsigned long charsWritten; 
   330 
   331         /* Maybe attach console and get stderr handle */
   332         if (consoleAttached == 0) {
   333             attachResult = AttachConsole(ATTACH_PARENT_PROCESS);
   334             if (!attachResult) {
   335                     attachError = GetLastError();
   336                     if (attachError == ERROR_INVALID_HANDLE) {
   337                         OutputDebugString(TEXT("Parent process has no console"));
   338                         consoleAttached = -1;
   339                     } else if (attachError == ERROR_GEN_FAILURE) {
   340                          OutputDebugString(TEXT("Could not attach to console of parent process"));
   341                          consoleAttached = -1;
   342                     } else if (attachError == ERROR_ACCESS_DENIED) {  
   343                          /* Already attached */
   344                         consoleAttached = 1;
   345                     } else {
   346                         OutputDebugString(TEXT("Error attaching console"));
   347                         consoleAttached = -1;
   348                     }
   349                 } else {
   350                     /* Newly attached */
   351                     consoleAttached = 1;
   352                 }
   353 			
   354                 if (consoleAttached == 1) {
   355                         stderrHandle = GetStdHandle(STD_ERROR_HANDLE);
   356                 }
   357         }
   358 #endif /* ifndef __WINRT__ */
   359 
   360         length = SDL_strlen(SDL_priority_prefixes[priority]) + 2 + SDL_strlen(message) + 1 + 1;
   361         output = SDL_stack_alloc(char, length);
   362         SDL_snprintf(output, length, "%s: %s\n", SDL_priority_prefixes[priority], message);
   363         tstr = WIN_UTF8ToString(output);
   364         
   365         /* Output to debugger */
   366         OutputDebugString(tstr);
   367        
   368 #ifndef __WINRT__
   369         /* Screen output to stderr, if console was attached. */
   370         if (consoleAttached == 1) {
   371                 if (!WriteConsole(stderrHandle, tstr, lstrlen(tstr), &charsWritten, NULL)) {
   372                     OutputDebugString(TEXT("Error calling WriteConsole"));
   373                 }
   374                 if (charsWritten == ERROR_NOT_ENOUGH_MEMORY) {
   375                     OutputDebugString(TEXT("Insufficient heap memory to write message"));
   376                 }
   377         }
   378 #endif /* ifndef __WINRT__ */
   379 
   380         SDL_free(tstr);
   381         SDL_stack_free(output);
   382     }
   383 #elif defined(__ANDROID__)
   384     {
   385         char tag[32];
   386 
   387         SDL_snprintf(tag, SDL_arraysize(tag), "SDL/%s", GetCategoryPrefix(category));
   388         __android_log_write(SDL_android_priority[priority], tag, message);
   389     }
   390 #elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA)
   391     /* Technically we don't need SDL_VIDEO_DRIVER_COCOA, but that's where this function is defined for now.
   392     */
   393     extern void SDL_NSLog(const char *text);
   394     {
   395         char *text;
   396 
   397         text = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
   398         if (text) {
   399             SDL_snprintf(text, SDL_MAX_LOG_MESSAGE, "%s: %s", SDL_priority_prefixes[priority], message);
   400             SDL_NSLog(text);
   401             SDL_stack_free(text);
   402             return;
   403         }
   404     }
   405 #elif defined(__PSP__)
   406     {
   407         FILE*        pFile;
   408         pFile = fopen ("SDL_Log.txt", "a");
   409         fprintf(pFile, "%s: %s\n", SDL_priority_prefixes[priority], message);
   410         fclose (pFile);
   411     }
   412 #endif
   413 #if HAVE_STDIO_H
   414     fprintf(stderr, "%s: %s\n", SDL_priority_prefixes[priority], message);
   415 #endif
   416 }
   417 
   418 void
   419 SDL_LogGetOutputFunction(SDL_LogOutputFunction *callback, void **userdata)
   420 {
   421     if (callback) {
   422         *callback = SDL_log_function;
   423     }
   424     if (userdata) {
   425         *userdata = SDL_log_userdata;
   426     }
   427 }
   428 
   429 void
   430 SDL_LogSetOutputFunction(SDL_LogOutputFunction callback, void *userdata)
   431 {
   432     SDL_log_function = callback;
   433     SDL_log_userdata = userdata;
   434 }
   435 
   436 /* vi: set ts=4 sw=4 expandtab: */