src/SDL_log.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 13 Nov 2019 21:53:01 -0800
changeset 13244 50987b0b4533
parent 12950 05dddfb66b85
permissions -rw-r--r--
Added a utility function to simplify the hint handling logic
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 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_error.h"
    30 #include "SDL_log.h"
    31 
    32 #if HAVE_STDIO_H
    33 #include <stdio.h>
    34 #endif
    35 
    36 #if defined(__ANDROID__)
    37 #include <android/log.h>
    38 #endif
    39 
    40 #define DEFAULT_PRIORITY                SDL_LOG_PRIORITY_CRITICAL
    41 #define DEFAULT_ASSERT_PRIORITY         SDL_LOG_PRIORITY_WARN
    42 #define DEFAULT_APPLICATION_PRIORITY    SDL_LOG_PRIORITY_INFO
    43 #define DEFAULT_TEST_PRIORITY           SDL_LOG_PRIORITY_VERBOSE
    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 SDLCALL SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, const char *message);
    54 
    55 static SDL_LogLevel *SDL_loglevels;
    56 static SDL_LogPriority SDL_default_priority = DEFAULT_PRIORITY;
    57 static SDL_LogPriority SDL_assert_priority = DEFAULT_ASSERT_PRIORITY;
    58 static SDL_LogPriority SDL_application_priority = DEFAULT_APPLICATION_PRIORITY;
    59 static SDL_LogPriority SDL_test_priority = DEFAULT_TEST_PRIORITY;
    60 static SDL_LogOutputFunction SDL_log_function = SDL_LogOutput;
    61 static void *SDL_log_userdata = NULL;
    62 
    63 static const char *SDL_priority_prefixes[SDL_NUM_LOG_PRIORITIES] = {
    64     NULL,
    65     "VERBOSE",
    66     "DEBUG",
    67     "INFO",
    68     "WARN",
    69     "ERROR",
    70     "CRITICAL"
    71 };
    72 
    73 #ifdef __ANDROID__
    74 static const char *SDL_category_prefixes[SDL_LOG_CATEGORY_RESERVED1] = {
    75     "APP",
    76     "ERROR",
    77     "SYSTEM",
    78     "AUDIO",
    79     "VIDEO",
    80     "RENDER",
    81     "INPUT"
    82 };
    83 
    84 static int SDL_android_priority[SDL_NUM_LOG_PRIORITIES] = {
    85     ANDROID_LOG_UNKNOWN,
    86     ANDROID_LOG_VERBOSE,
    87     ANDROID_LOG_DEBUG,
    88     ANDROID_LOG_INFO,
    89     ANDROID_LOG_WARN,
    90     ANDROID_LOG_ERROR,
    91     ANDROID_LOG_FATAL
    92 };
    93 #endif /* __ANDROID__ */
    94 
    95 
    96 void
    97 SDL_LogSetAllPriority(SDL_LogPriority priority)
    98 {
    99     SDL_LogLevel *entry;
   100 
   101     for (entry = SDL_loglevels; entry; entry = entry->next) {
   102         entry->priority = priority;
   103     }
   104     SDL_default_priority = priority;
   105     SDL_assert_priority = priority;
   106     SDL_application_priority = priority;
   107 }
   108 
   109 void
   110 SDL_LogSetPriority(int category, SDL_LogPriority priority)
   111 {
   112     SDL_LogLevel *entry;
   113 
   114     for (entry = SDL_loglevels; entry; entry = entry->next) {
   115         if (entry->category == category) {
   116             entry->priority = priority;
   117             return;
   118         }
   119     }
   120 
   121     /* Create a new entry */
   122     entry = (SDL_LogLevel *)SDL_malloc(sizeof(*entry));
   123     if (entry) {
   124         entry->category = category;
   125         entry->priority = priority;
   126         entry->next = SDL_loglevels;
   127         SDL_loglevels = entry;
   128     }
   129 }
   130 
   131 SDL_LogPriority
   132 SDL_LogGetPriority(int category)
   133 {
   134     SDL_LogLevel *entry;
   135 
   136     for (entry = SDL_loglevels; entry; entry = entry->next) {
   137         if (entry->category == category) {
   138             return entry->priority;
   139         }
   140     }
   141 
   142     if (category == SDL_LOG_CATEGORY_TEST) {
   143         return SDL_test_priority;
   144     } else if (category == SDL_LOG_CATEGORY_APPLICATION) {
   145         return SDL_application_priority;
   146     } else if (category == SDL_LOG_CATEGORY_ASSERT) {
   147         return SDL_assert_priority;
   148     } else {
   149         return SDL_default_priority;
   150     }
   151 }
   152 
   153 void
   154 SDL_LogResetPriorities(void)
   155 {
   156     SDL_LogLevel *entry;
   157 
   158     while (SDL_loglevels) {
   159         entry = SDL_loglevels;
   160         SDL_loglevels = entry->next;
   161         SDL_free(entry);
   162     }
   163 
   164     SDL_default_priority = DEFAULT_PRIORITY;
   165     SDL_assert_priority = DEFAULT_ASSERT_PRIORITY;
   166     SDL_application_priority = DEFAULT_APPLICATION_PRIORITY;
   167     SDL_test_priority = DEFAULT_TEST_PRIORITY;
   168 }
   169 
   170 void
   171 SDL_Log(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
   172 {
   173     va_list ap;
   174 
   175     va_start(ap, fmt);
   176     SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap);
   177     va_end(ap);
   178 }
   179 
   180 void
   181 SDL_LogVerbose(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
   182 {
   183     va_list ap;
   184 
   185     va_start(ap, fmt);
   186     SDL_LogMessageV(category, SDL_LOG_PRIORITY_VERBOSE, fmt, ap);
   187     va_end(ap);
   188 }
   189 
   190 void
   191 SDL_LogDebug(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
   192 {
   193     va_list ap;
   194 
   195     va_start(ap, fmt);
   196     SDL_LogMessageV(category, SDL_LOG_PRIORITY_DEBUG, fmt, ap);
   197     va_end(ap);
   198 }
   199 
   200 void
   201 SDL_LogInfo(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
   202 {
   203     va_list ap;
   204 
   205     va_start(ap, fmt);
   206     SDL_LogMessageV(category, SDL_LOG_PRIORITY_INFO, fmt, ap);
   207     va_end(ap);
   208 }
   209 
   210 void
   211 SDL_LogWarn(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
   212 {
   213     va_list ap;
   214 
   215     va_start(ap, fmt);
   216     SDL_LogMessageV(category, SDL_LOG_PRIORITY_WARN, fmt, ap);
   217     va_end(ap);
   218 }
   219 
   220 void
   221 SDL_LogError(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
   222 {
   223     va_list ap;
   224 
   225     va_start(ap, fmt);
   226     SDL_LogMessageV(category, SDL_LOG_PRIORITY_ERROR, fmt, ap);
   227     va_end(ap);
   228 }
   229 
   230 void
   231 SDL_LogCritical(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
   232 {
   233     va_list ap;
   234 
   235     va_start(ap, fmt);
   236     SDL_LogMessageV(category, SDL_LOG_PRIORITY_CRITICAL, fmt, ap);
   237     va_end(ap);
   238 }
   239 
   240 void
   241 SDL_LogMessage(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
   242 {
   243     va_list ap;
   244 
   245     va_start(ap, fmt);
   246     SDL_LogMessageV(category, priority, fmt, ap);
   247     va_end(ap);
   248 }
   249 
   250 #ifdef __ANDROID__
   251 static const char *
   252 GetCategoryPrefix(int category)
   253 {
   254     if (category < SDL_LOG_CATEGORY_RESERVED1) {
   255         return SDL_category_prefixes[category];
   256     }
   257     if (category < SDL_LOG_CATEGORY_CUSTOM) {
   258         return "RESERVED";
   259     }
   260     return "CUSTOM";
   261 }
   262 #endif /* __ANDROID__ */
   263 
   264 void
   265 SDL_LogMessageV(int category, SDL_LogPriority priority, const char *fmt, va_list ap)
   266 {
   267     char *message;
   268     size_t len;
   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     /* !!! FIXME: why not just "char message[SDL_MAX_LOG_MESSAGE];" ? */
   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__) && !defined(HAVE_STDIO_H) && !defined(__WINRT__)
   307 /* Flag tracking the attachment of the console: 0=unattached, 1=attached to a console, 2=attached to a file, -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 SDLCALL
   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         SDL_bool isstack;
   326 
   327 #if !defined(HAVE_STDIO_H) && !defined(__WINRT__)
   328         BOOL attachResult;
   329         DWORD attachError;
   330         unsigned long charsWritten; 
   331         DWORD consoleMode;
   332 
   333         /* Maybe attach console and get stderr handle */
   334         if (consoleAttached == 0) {
   335             attachResult = AttachConsole(ATTACH_PARENT_PROCESS);
   336             if (!attachResult) {
   337                     attachError = GetLastError();
   338                     if (attachError == ERROR_INVALID_HANDLE) {
   339                         /* This is expected when running from Visual Studio */
   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                         if (GetConsoleMode(stderrHandle, &consoleMode) == 0) {
   361                             /* WriteConsole fails if the output is redirected to a file. Must use WriteFile instead. */
   362                             consoleAttached = 2;
   363                         }
   364                 }
   365         }
   366 #endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) */
   367 
   368         length = SDL_strlen(SDL_priority_prefixes[priority]) + 2 + SDL_strlen(message) + 1 + 1 + 1;
   369         output = SDL_small_alloc(char, length, &isstack);
   370         SDL_snprintf(output, length, "%s: %s\r\n", SDL_priority_prefixes[priority], message);
   371         tstr = WIN_UTF8ToString(output);
   372         
   373         /* Output to debugger */
   374         OutputDebugString(tstr);
   375        
   376 #if !defined(HAVE_STDIO_H) && !defined(__WINRT__)
   377         /* Screen output to stderr, if console was attached. */
   378         if (consoleAttached == 1) {
   379                 if (!WriteConsole(stderrHandle, tstr, lstrlen(tstr), &charsWritten, NULL)) {
   380                     OutputDebugString(TEXT("Error calling WriteConsole\r\n"));
   381                     if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {
   382                         OutputDebugString(TEXT("Insufficient heap memory to write message\r\n"));
   383                     }
   384                 }
   385 
   386         } else if (consoleAttached == 2) {
   387             if (!WriteFile(stderrHandle, output, lstrlenA(output), &charsWritten, NULL)) {
   388                 OutputDebugString(TEXT("Error calling WriteFile\r\n"));
   389             }
   390         }
   391 #endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) */
   392 
   393         SDL_free(tstr);
   394         SDL_small_free(output, isstack);
   395     }
   396 #elif defined(__ANDROID__)
   397     {
   398         char tag[32];
   399 
   400         SDL_snprintf(tag, SDL_arraysize(tag), "SDL/%s", GetCategoryPrefix(category));
   401         __android_log_write(SDL_android_priority[priority], tag, message);
   402     }
   403 #elif defined(__APPLE__) && (defined(SDL_VIDEO_DRIVER_COCOA) || defined(SDL_VIDEO_DRIVER_UIKIT))
   404     /* Technically we don't need Cocoa/UIKit, but that's where this function is defined for now.
   405     */
   406     extern void SDL_NSLog(const char *text);
   407     {
   408         char *text;
   409         /* !!! FIXME: why not just "char text[SDL_MAX_LOG_MESSAGE];" ? */
   410         text = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
   411         if (text) {
   412             SDL_snprintf(text, SDL_MAX_LOG_MESSAGE, "%s: %s", SDL_priority_prefixes[priority], message);
   413             SDL_NSLog(text);
   414             SDL_stack_free(text);
   415             return;
   416         }
   417     }
   418 #elif defined(__PSP__)
   419     {
   420         FILE*        pFile;
   421         pFile = fopen ("SDL_Log.txt", "a");
   422         fprintf(pFile, "%s: %s\n", SDL_priority_prefixes[priority], message);
   423         fclose (pFile);
   424     }
   425 #endif
   426 #if HAVE_STDIO_H
   427     fprintf(stderr, "%s: %s\n", SDL_priority_prefixes[priority], message);
   428 #if __NACL__
   429     fflush(stderr);
   430 #endif
   431 #endif
   432 }
   433 
   434 void
   435 SDL_LogGetOutputFunction(SDL_LogOutputFunction *callback, void **userdata)
   436 {
   437     if (callback) {
   438         *callback = SDL_log_function;
   439     }
   440     if (userdata) {
   441         *userdata = SDL_log_userdata;
   442     }
   443 }
   444 
   445 void
   446 SDL_LogSetOutputFunction(SDL_LogOutputFunction callback, void *userdata)
   447 {
   448     SDL_log_function = callback;
   449     SDL_log_userdata = userdata;
   450 }
   451 
   452 /* vi: set ts=4 sw=4 expandtab: */