src/SDL_log.c
author Ryan C. Gordon <icculus@icculus.org>
Sun, 21 Oct 2018 22:40:17 -0400
changeset 12345 50e1cca28b39
parent 11811 5d94cb6b24d3
child 12349 a67dedb293c8
permissions -rw-r--r--
wasapi/win32: Sort initial device lists by device GUID.

This makes an unchanged set of hardware always report devices in the same
order on each run.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 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     message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
   286     if (!message) {
   287         return;
   288     }
   289 
   290     SDL_vsnprintf(message, SDL_MAX_LOG_MESSAGE, fmt, ap);
   291 
   292     /* Chop off final endline. */
   293     len = SDL_strlen(message);
   294     if ((len > 0) && (message[len-1] == '\n')) {
   295         message[--len] = '\0';
   296         if ((len > 0) && (message[len-1] == '\r')) {  /* catch "\r\n", too. */
   297             message[--len] = '\0';
   298         }
   299     }
   300 
   301     SDL_log_function(SDL_log_userdata, category, priority, message);
   302     SDL_stack_free(message);
   303 }
   304 
   305 #if defined(__WIN32__) && !defined(HAVE_STDIO_H) && !defined(__WINRT__)
   306 /* Flag tracking the attachment of the console: 0=unattached, 1=attached to a console, 2=attached to a file, -1=error */
   307 static int consoleAttached = 0;
   308 
   309 /* Handle to stderr output of console. */
   310 static HANDLE stderrHandle = NULL;
   311 #endif
   312 
   313 static void SDLCALL
   314 SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority,
   315               const char *message)
   316 {
   317 #if defined(__WIN32__) || defined(__WINRT__)
   318     /* Way too many allocations here, urgh */
   319     /* Note: One can't call SDL_SetError here, since that function itself logs. */
   320     {
   321         char *output;
   322         size_t length;
   323         LPTSTR tstr;
   324 
   325 #if !defined(HAVE_STDIO_H) && !defined(__WINRT__)
   326         BOOL attachResult;
   327         DWORD attachError;
   328         unsigned long charsWritten; 
   329         DWORD consoleMode;
   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                         /* This is expected when running from Visual Studio */
   338                         /*OutputDebugString(TEXT("Parent process has no console\r\n"));*/
   339                         consoleAttached = -1;
   340                     } else if (attachError == ERROR_GEN_FAILURE) {
   341                          OutputDebugString(TEXT("Could not attach to console of parent process\r\n"));
   342                          consoleAttached = -1;
   343                     } else if (attachError == ERROR_ACCESS_DENIED) {  
   344                          /* Already attached */
   345                         consoleAttached = 1;
   346                     } else {
   347                         OutputDebugString(TEXT("Error attaching console\r\n"));
   348                         consoleAttached = -1;
   349                     }
   350                 } else {
   351                     /* Newly attached */
   352                     consoleAttached = 1;
   353                 }
   354 
   355                 if (consoleAttached == 1) {
   356                         stderrHandle = GetStdHandle(STD_ERROR_HANDLE);
   357 
   358                         if (GetConsoleMode(stderrHandle, &consoleMode) == 0) {
   359                             /* WriteConsole fails if the output is redirected to a file. Must use WriteFile instead. */
   360                             consoleAttached = 2;
   361                         }
   362                 }
   363         }
   364 #endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) */
   365 
   366         length = SDL_strlen(SDL_priority_prefixes[priority]) + 2 + SDL_strlen(message) + 1 + 1 + 1;
   367         output = SDL_stack_alloc(char, length);
   368         SDL_snprintf(output, length, "%s: %s\r\n", SDL_priority_prefixes[priority], message);
   369         tstr = WIN_UTF8ToString(output);
   370         
   371         /* Output to debugger */
   372         OutputDebugString(tstr);
   373        
   374 #if !defined(HAVE_STDIO_H) && !defined(__WINRT__)
   375         /* Screen output to stderr, if console was attached. */
   376         if (consoleAttached == 1) {
   377                 if (!WriteConsole(stderrHandle, tstr, lstrlen(tstr), &charsWritten, NULL)) {
   378                     OutputDebugString(TEXT("Error calling WriteConsole\r\n"));
   379                     if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {
   380                         OutputDebugString(TEXT("Insufficient heap memory to write message\r\n"));
   381                     }
   382                 }
   383 
   384         } else if (consoleAttached == 2) {
   385             if (!WriteFile(stderrHandle, output, lstrlenA(output), &charsWritten, NULL)) {
   386                 OutputDebugString(TEXT("Error calling WriteFile\r\n"));
   387             }
   388         }
   389 #endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) */
   390 
   391         SDL_free(tstr);
   392         SDL_stack_free(output);
   393     }
   394 #elif defined(__ANDROID__)
   395     {
   396         char tag[32];
   397 
   398         SDL_snprintf(tag, SDL_arraysize(tag), "SDL/%s", GetCategoryPrefix(category));
   399         __android_log_write(SDL_android_priority[priority], tag, message);
   400     }
   401 #elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA)
   402     /* Technically we don't need SDL_VIDEO_DRIVER_COCOA, but that's where this function is defined for now.
   403     */
   404     extern void SDL_NSLog(const char *text);
   405     {
   406         char *text;
   407 
   408         text = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
   409         if (text) {
   410             SDL_snprintf(text, SDL_MAX_LOG_MESSAGE, "%s: %s", SDL_priority_prefixes[priority], message);
   411             SDL_NSLog(text);
   412             SDL_stack_free(text);
   413             return;
   414         }
   415     }
   416 #elif defined(__PSP__)
   417     {
   418         FILE*        pFile;
   419         pFile = fopen ("SDL_Log.txt", "a");
   420         fprintf(pFile, "%s: %s\n", SDL_priority_prefixes[priority], message);
   421         fclose (pFile);
   422     }
   423 #endif
   424 #if HAVE_STDIO_H
   425     fprintf(stderr, "%s: %s\n", SDL_priority_prefixes[priority], message);
   426 #if __NACL__
   427     fflush(stderr);
   428 #endif
   429 #endif
   430 }
   431 
   432 void
   433 SDL_LogGetOutputFunction(SDL_LogOutputFunction *callback, void **userdata)
   434 {
   435     if (callback) {
   436         *callback = SDL_log_function;
   437     }
   438     if (userdata) {
   439         *userdata = SDL_log_userdata;
   440     }
   441 }
   442 
   443 void
   444 SDL_LogSetOutputFunction(SDL_LogOutputFunction callback, void *userdata)
   445 {
   446     SDL_log_function = callback;
   447     SDL_log_userdata = userdata;
   448 }
   449 
   450 /* vi: set ts=4 sw=4 expandtab: */