src/SDL_log.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 05 Jun 2013 22:33:10 -0700
changeset 7286 04087a99e3f4
parent 7274 d9ef44d91559
child 7292 fdf42e4cdcec
permissions -rw-r--r--
Added testautomation to the Visual Studio 2008 project.
     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 pbRemoteDebuggerPresent;        
   313         BOOL attachResult;
   314         DWORD attachError;
   315         unsigned long charsWritten; 
   316 
   317         /* Maybe attach console and get stderr handle */
   318         if (consoleAttached == 0) {
   319             attachResult = AttachConsole(ATTACH_PARENT_PROCESS);
   320             if (!attachResult) {
   321                     attachError = GetLastError();
   322                     if (attachError == ERROR_INVALID_HANDLE) {
   323                         SDL_SetError("Parent process has no console");
   324                         consoleAttached = -1;
   325                     } else if (attachError == ERROR_GEN_FAILURE) {
   326                          SDL_SetError("Could not attach to console of parent process");
   327                          consoleAttached = -1;
   328                     } else if (attachError == ERROR_ACCESS_DENIED) {  
   329                          /* Already attached */
   330                         consoleAttached = 1;
   331                     } else {
   332                         SDL_SetError("Error %d attaching console", attachError);
   333                         consoleAttached = -1;
   334                     }
   335                 } else {
   336                     /* Newly attached */
   337                     consoleAttached = 1;
   338                 }
   339 			
   340                 if (consoleAttached == 1) {
   341                         stderrHandle = GetStdHandle(STD_ERROR_HANDLE);
   342                 }
   343         }
   344 
   345         length = SDL_strlen(SDL_priority_prefixes[priority]) + 2 + SDL_strlen(message) + 1 + 1;
   346         output = SDL_stack_alloc(char, length);
   347         SDL_snprintf(output, length, "%s: %s\n", SDL_priority_prefixes[priority], message);
   348         tstr = WIN_UTF8ToString(output);
   349         
   350         /* Debugger output, if attached. Check each time since debugger can be attached at runtime. */
   351         CheckRemoteDebuggerPresent(GetCurrentProcess(), &pbRemoteDebuggerPresent);
   352         if (pbRemoteDebuggerPresent || IsDebuggerPresent()) {
   353             OutputDebugString(tstr);
   354         }
   355        
   356         /* Screen output to stderr, if console was attached. */
   357         if (consoleAttached == 1) {
   358                 if (!WriteConsole(stderrHandle, tstr, lstrlen(tstr), &charsWritten, NULL)) {
   359                     SDL_SetError("Error %d calling WriteConsole", GetLastError());
   360                 }
   361                 if (charsWritten == ERROR_NOT_ENOUGH_MEMORY) {
   362                     SDL_SetError("Insufficient heap memory to write message of size %d", length);
   363                 }
   364         }
   365 
   366         SDL_free(tstr);
   367         SDL_stack_free(output);
   368     }
   369 #elif defined(__ANDROID__)
   370     {
   371         char tag[32];
   372 
   373         SDL_snprintf(tag, SDL_arraysize(tag), "SDL/%s", GetCategoryPrefix(category));
   374         __android_log_write(SDL_android_priority[priority], tag, message);
   375     }
   376 #elif defined(__APPLE__)
   377     extern void SDL_NSLog(const char *text);
   378     {
   379         char *text;
   380 
   381         text = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
   382         if (text) {
   383             SDL_snprintf(text, SDL_MAX_LOG_MESSAGE, "%s: %s", SDL_priority_prefixes[priority], message);
   384             SDL_NSLog(text);
   385             SDL_stack_free(text);
   386             return;
   387         }
   388     }
   389 #elif defined(__PSP__)
   390     {
   391         unsigned int length;
   392         char*        output;
   393         FILE*        pFile;
   394         length = SDL_strlen(SDL_priority_prefixes[priority]) + 2 + SDL_strlen(message) + 1;
   395         output = SDL_stack_alloc(char, length);
   396         SDL_snprintf(output, length, "%s: %s", SDL_priority_prefixes[priority], message);
   397         pFile = fopen ("SDL_Log.txt", "a");
   398         fwrite (output, strlen (output), 1, pFile);
   399         SDL_stack_free(output);
   400         fclose (pFile);
   401     }
   402 #endif
   403 #if HAVE_STDIO_H
   404     fprintf(stderr, "%s: %s\n", SDL_priority_prefixes[priority], message);
   405 #endif
   406 }
   407 
   408 void
   409 SDL_LogGetOutputFunction(SDL_LogOutputFunction *callback, void **userdata)
   410 {
   411     if (callback) {
   412         *callback = SDL_log_function;
   413     }
   414     if (userdata) {
   415         *userdata = SDL_log_userdata;
   416     }
   417 }
   418 
   419 void
   420 SDL_LogSetOutputFunction(SDL_LogOutputFunction callback, void *userdata)
   421 {
   422     SDL_log_function = callback;
   423     SDL_log_userdata = userdata;
   424 }
   425 
   426 /* vi: set ts=4 sw=4 expandtab: */