src/SDL_log.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 05 Jul 2013 00:41:01 -0400
changeset 7343 faf0d8c7dbec
parent 7307 5d6b7b9432d3
child 7344 3d9397262c10
permissions -rw-r--r--
Added a FIXME.
     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__)
    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 
   270     /* Nothing to do if we don't have an output function */
   271     if (!SDL_log_function) {
   272         return;
   273     }
   274 
   275     /* Make sure we don't exceed array bounds */
   276     if ((int)priority < 0 || priority >= SDL_NUM_LOG_PRIORITIES) {
   277         return;
   278     }
   279 
   280     /* See if we want to do anything with this message */
   281     if (priority < SDL_LogGetPriority(category)) {
   282         return;
   283     }
   284 
   285     message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
   286     if (!message) {
   287         return;
   288     }
   289     SDL_vsnprintf(message, SDL_MAX_LOG_MESSAGE, fmt, ap);
   290     SDL_log_function(SDL_log_userdata, category, priority, message);
   291     SDL_stack_free(message);
   292 }
   293 
   294 #if defined(__WIN32__)
   295 /* Flag tracking the attachment of the console: 0=unattached, 1=attached, -1=error */
   296 static int consoleAttached = 0;
   297 
   298 /* Handle to stderr output of console. */
   299 static HANDLE stderrHandle = NULL;
   300 #endif
   301 
   302 static void
   303 SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority,
   304               const char *message)
   305 {
   306 #if defined(__WIN32__)
   307     /* Way too many allocations here, urgh */
   308     {
   309         char *output;
   310         size_t length;
   311         LPTSTR tstr;
   312         BOOL attachResult;
   313         DWORD attachError;
   314         unsigned long charsWritten; 
   315 
   316         /* Maybe attach console and get stderr handle */
   317         if (consoleAttached == 0) {
   318             attachResult = AttachConsole(ATTACH_PARENT_PROCESS);
   319             if (!attachResult) {
   320                     attachError = GetLastError();
   321                     if (attachError == ERROR_INVALID_HANDLE) {
   322                         SDL_SetError("Parent process has no console");
   323                         consoleAttached = -1;
   324                     } else if (attachError == ERROR_GEN_FAILURE) {
   325                          SDL_SetError("Could not attach to console of parent process");
   326                          consoleAttached = -1;
   327                     } else if (attachError == ERROR_ACCESS_DENIED) {  
   328                          /* Already attached */
   329                         consoleAttached = 1;
   330                     } else {
   331                         SDL_SetError("Error %d attaching console", attachError);
   332                         consoleAttached = -1;
   333                     }
   334                 } else {
   335                     /* Newly attached */
   336                     consoleAttached = 1;
   337                 }
   338 			
   339                 if (consoleAttached == 1) {
   340                         stderrHandle = GetStdHandle(STD_ERROR_HANDLE);
   341                 }
   342         }
   343 
   344         length = SDL_strlen(SDL_priority_prefixes[priority]) + 2 + SDL_strlen(message) + 1 + 1;
   345         output = SDL_stack_alloc(char, length);
   346         SDL_snprintf(output, length, "%s: %s\n", SDL_priority_prefixes[priority], message);
   347         tstr = WIN_UTF8ToString(output);
   348         
   349         /* Output to debugger */
   350         OutputDebugString(tstr);
   351        
   352         /* Screen output to stderr, if console was attached. */
   353         if (consoleAttached == 1) {
   354                 if (!WriteConsole(stderrHandle, tstr, lstrlen(tstr), &charsWritten, NULL)) {
   355                     SDL_SetError("Error %d calling WriteConsole", GetLastError());
   356                 }
   357                 if (charsWritten == ERROR_NOT_ENOUGH_MEMORY) {
   358                     SDL_SetError("Insufficient heap memory to write message of size %d", length);
   359                 }
   360         }
   361 
   362         SDL_free(tstr);
   363         SDL_stack_free(output);
   364     }
   365 #elif defined(__ANDROID__)
   366     {
   367         char tag[32];
   368 
   369         SDL_snprintf(tag, SDL_arraysize(tag), "SDL/%s", GetCategoryPrefix(category));
   370         __android_log_write(SDL_android_priority[priority], tag, message);
   371     }
   372 #elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA)
   373     /* Technically we don't need SDL_VIDEO_DRIVER_COCOA, but that's where this function is defined for now.
   374     */
   375     extern void SDL_NSLog(const char *text);
   376     {
   377         char *text;
   378 
   379         text = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
   380         if (text) {
   381             SDL_snprintf(text, SDL_MAX_LOG_MESSAGE, "%s: %s", SDL_priority_prefixes[priority], message);
   382             SDL_NSLog(text);
   383             SDL_stack_free(text);
   384             return;
   385         }
   386     }
   387 #elif defined(__PSP__)
   388     {
   389         unsigned int length;
   390         char*        output;
   391         FILE*        pFile;
   392         /* !!! FIXME: is there any reason we didn't just use fprintf() here? */
   393         length = SDL_strlen(SDL_priority_prefixes[priority]) + 2 + SDL_strlen(message) + 1;
   394         output = SDL_stack_alloc(char, length);
   395         SDL_snprintf(output, length, "%s: %s", SDL_priority_prefixes[priority], message);
   396         pFile = fopen ("SDL_Log.txt", "a");
   397         fwrite (output, strlen (output), 1, pFile);
   398         SDL_stack_free(output);
   399         fclose (pFile);
   400     }
   401 #endif
   402 #if HAVE_STDIO_H
   403     fprintf(stderr, "%s: %s\n", SDL_priority_prefixes[priority], message);
   404 #endif
   405 }
   406 
   407 void
   408 SDL_LogGetOutputFunction(SDL_LogOutputFunction *callback, void **userdata)
   409 {
   410     if (callback) {
   411         *callback = SDL_log_function;
   412     }
   413     if (userdata) {
   414         *userdata = SDL_log_userdata;
   415     }
   416 }
   417 
   418 void
   419 SDL_LogSetOutputFunction(SDL_LogOutputFunction callback, void *userdata)
   420 {
   421     SDL_log_function = callback;
   422     SDL_log_userdata = userdata;
   423 }
   424 
   425 /* vi: set ts=4 sw=4 expandtab: */