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