slouken@8582: /* slouken@8582: Simple DirectMedia Layer slouken@8149: Copyright (C) 1997-2014 Sam Lantinga slouken@8582: slouken@8582: This software is provided 'as-is', without any express or implied slouken@8582: warranty. In no event will the authors be held liable for any damages slouken@8582: arising from the use of this software. slouken@8582: slouken@8582: Permission is granted to anyone to use this software for any purpose, slouken@8582: including commercial applications, and to alter it and redistribute it slouken@8582: freely, subject to the following restrictions: slouken@8582: slouken@8582: 1. The origin of this software must not be misrepresented; you must not slouken@8582: claim that you wrote the original software. If you use this software slouken@8582: in a product, an acknowledgment in the product documentation would be slouken@8582: appreciated but is not required. slouken@8582: 2. Altered source versions must be plainly marked as such, and must not be slouken@8582: misrepresented as being the original software. slouken@8582: 3. This notice may not be removed or altered from any source distribution. slouken@8582: */ icculus@8093: #include "./SDL_internal.h" slouken@8582: slouken@8582: #if defined(__WIN32__) || defined(__WINRT__) slouken@8582: #include "core/windows/SDL_windows.h" slouken@8582: #endif slouken@8582: slouken@8582: /* Simple log messages in SDL */ slouken@8582: slouken@8820: #include "SDL_error.h" slouken@8582: #include "SDL_log.h" slouken@8582: slouken@8582: #if HAVE_STDIO_H slouken@8582: #include slouken@8582: #endif slouken@8582: slouken@8582: #if defined(__ANDROID__) slouken@8582: #include slouken@8582: #endif slouken@8582: slouken@8582: #define DEFAULT_PRIORITY SDL_LOG_PRIORITY_CRITICAL slouken@8582: #define DEFAULT_ASSERT_PRIORITY SDL_LOG_PRIORITY_WARN slouken@8582: #define DEFAULT_APPLICATION_PRIORITY SDL_LOG_PRIORITY_INFO slouken@8582: #define DEFAULT_TEST_PRIORITY SDL_LOG_PRIORITY_VERBOSE slouken@8582: slouken@8582: typedef struct SDL_LogLevel slouken@8582: { slouken@8582: int category; slouken@8582: SDL_LogPriority priority; slouken@8582: struct SDL_LogLevel *next; slouken@8582: } SDL_LogLevel; slouken@8582: slouken@8582: /* The default log output function */ slouken@8582: static void SDL_LogOutput(void *userdata, slouken@8582: int category, SDL_LogPriority priority, slouken@8582: const char *message); slouken@8582: slouken@8582: static SDL_LogLevel *SDL_loglevels; slouken@8582: static SDL_LogPriority SDL_default_priority = DEFAULT_PRIORITY; slouken@8582: static SDL_LogPriority SDL_assert_priority = DEFAULT_ASSERT_PRIORITY; slouken@8582: static SDL_LogPriority SDL_application_priority = DEFAULT_APPLICATION_PRIORITY; slouken@8582: static SDL_LogPriority SDL_test_priority = DEFAULT_TEST_PRIORITY; slouken@8582: static SDL_LogOutputFunction SDL_log_function = SDL_LogOutput; slouken@8582: static void *SDL_log_userdata = NULL; slouken@8582: slouken@8582: static const char *SDL_priority_prefixes[SDL_NUM_LOG_PRIORITIES] = { slouken@8582: NULL, slouken@8582: "VERBOSE", slouken@8582: "DEBUG", slouken@8582: "INFO", slouken@8582: "WARN", slouken@8582: "ERROR", slouken@8582: "CRITICAL" slouken@8582: }; slouken@8582: slouken@8582: #ifdef __ANDROID__ slouken@8582: static const char *SDL_category_prefixes[SDL_LOG_CATEGORY_RESERVED1] = { slouken@8582: "APP", slouken@8582: "ERROR", slouken@8582: "SYSTEM", slouken@8582: "AUDIO", slouken@8582: "VIDEO", slouken@8582: "RENDER", slouken@8582: "INPUT" slouken@8582: }; slouken@8582: slouken@8582: static int SDL_android_priority[SDL_NUM_LOG_PRIORITIES] = { slouken@8582: ANDROID_LOG_UNKNOWN, slouken@8582: ANDROID_LOG_VERBOSE, slouken@8582: ANDROID_LOG_DEBUG, slouken@8582: ANDROID_LOG_INFO, slouken@8582: ANDROID_LOG_WARN, slouken@8582: ANDROID_LOG_ERROR, slouken@8582: ANDROID_LOG_FATAL slouken@8582: }; slouken@8582: #endif /* __ANDROID__ */ slouken@8582: slouken@8582: slouken@8582: void slouken@8582: SDL_LogSetAllPriority(SDL_LogPriority priority) slouken@8582: { slouken@8582: SDL_LogLevel *entry; slouken@8582: slouken@8582: for (entry = SDL_loglevels; entry; entry = entry->next) { slouken@8582: entry->priority = priority; slouken@8582: } slouken@8582: SDL_default_priority = priority; slouken@8582: SDL_assert_priority = priority; slouken@8582: SDL_application_priority = priority; slouken@8582: } slouken@8582: slouken@8582: void slouken@8582: SDL_LogSetPriority(int category, SDL_LogPriority priority) slouken@8582: { slouken@8582: SDL_LogLevel *entry; slouken@8582: slouken@8582: for (entry = SDL_loglevels; entry; entry = entry->next) { slouken@8582: if (entry->category == category) { slouken@8582: entry->priority = priority; slouken@8582: return; slouken@8582: } slouken@8582: } slouken@8582: slouken@8582: /* Create a new entry */ slouken@8582: entry = (SDL_LogLevel *)SDL_malloc(sizeof(*entry)); slouken@8582: if (entry) { slouken@8582: entry->category = category; slouken@8582: entry->priority = priority; slouken@8582: entry->next = SDL_loglevels; slouken@8582: SDL_loglevels = entry; slouken@8582: } slouken@8582: } slouken@8582: slouken@8582: SDL_LogPriority slouken@8582: SDL_LogGetPriority(int category) slouken@8582: { slouken@8582: SDL_LogLevel *entry; slouken@8582: slouken@8582: for (entry = SDL_loglevels; entry; entry = entry->next) { slouken@8582: if (entry->category == category) { slouken@8582: return entry->priority; slouken@8582: } slouken@8582: } slouken@8582: slouken@8582: if (category == SDL_LOG_CATEGORY_TEST) { slouken@8582: return SDL_test_priority; slouken@8582: } else if (category == SDL_LOG_CATEGORY_APPLICATION) { slouken@8582: return SDL_application_priority; slouken@8582: } else if (category == SDL_LOG_CATEGORY_ASSERT) { slouken@8582: return SDL_assert_priority; slouken@8582: } else { slouken@8582: return SDL_default_priority; slouken@8582: } slouken@8582: } slouken@8582: slouken@8582: void slouken@8582: SDL_LogResetPriorities(void) slouken@8582: { slouken@8582: SDL_LogLevel *entry; slouken@8582: slouken@8582: while (SDL_loglevels) { slouken@8582: entry = SDL_loglevels; slouken@8582: SDL_loglevels = entry->next; slouken@8582: SDL_free(entry); slouken@8582: } slouken@8582: slouken@8582: SDL_default_priority = DEFAULT_PRIORITY; slouken@8582: SDL_assert_priority = DEFAULT_ASSERT_PRIORITY; slouken@8582: SDL_application_priority = DEFAULT_APPLICATION_PRIORITY; slouken@8582: SDL_test_priority = DEFAULT_TEST_PRIORITY; slouken@8582: } slouken@8582: slouken@8582: void slouken@8820: SDL_Log(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) slouken@8582: { slouken@8582: va_list ap; slouken@8582: slouken@8582: va_start(ap, fmt); slouken@8582: SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap); slouken@8582: va_end(ap); slouken@8582: } slouken@8582: slouken@8582: void slouken@8820: SDL_LogVerbose(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) slouken@8582: { slouken@8582: va_list ap; slouken@8582: slouken@8582: va_start(ap, fmt); slouken@8582: SDL_LogMessageV(category, SDL_LOG_PRIORITY_VERBOSE, fmt, ap); slouken@8582: va_end(ap); slouken@8582: } slouken@8582: slouken@8582: void slouken@8820: SDL_LogDebug(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) slouken@8582: { slouken@8582: va_list ap; slouken@8582: slouken@8582: va_start(ap, fmt); slouken@8582: SDL_LogMessageV(category, SDL_LOG_PRIORITY_DEBUG, fmt, ap); slouken@8582: va_end(ap); slouken@8582: } slouken@8582: slouken@8582: void slouken@8820: SDL_LogInfo(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) slouken@8582: { slouken@8582: va_list ap; slouken@8582: slouken@8582: va_start(ap, fmt); slouken@8582: SDL_LogMessageV(category, SDL_LOG_PRIORITY_INFO, fmt, ap); slouken@8582: va_end(ap); slouken@8582: } slouken@8582: slouken@8582: void slouken@8820: SDL_LogWarn(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) slouken@8582: { slouken@8582: va_list ap; slouken@8582: slouken@8582: va_start(ap, fmt); slouken@8582: SDL_LogMessageV(category, SDL_LOG_PRIORITY_WARN, fmt, ap); slouken@8582: va_end(ap); slouken@8582: } slouken@8582: slouken@8582: void slouken@8820: SDL_LogError(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) slouken@8582: { slouken@8582: va_list ap; slouken@8582: slouken@8582: va_start(ap, fmt); slouken@8582: SDL_LogMessageV(category, SDL_LOG_PRIORITY_ERROR, fmt, ap); slouken@8582: va_end(ap); slouken@8582: } slouken@8582: slouken@8582: void slouken@8820: SDL_LogCritical(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) slouken@8582: { slouken@8582: va_list ap; slouken@8582: slouken@8582: va_start(ap, fmt); slouken@8582: SDL_LogMessageV(category, SDL_LOG_PRIORITY_CRITICAL, fmt, ap); slouken@8582: va_end(ap); slouken@8582: } slouken@8582: slouken@8582: void slouken@8820: SDL_LogMessage(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) slouken@8582: { slouken@8582: va_list ap; slouken@8582: slouken@8582: va_start(ap, fmt); slouken@8582: SDL_LogMessageV(category, priority, fmt, ap); slouken@8582: va_end(ap); slouken@8582: } slouken@8582: slouken@8582: #ifdef __ANDROID__ slouken@8582: static const char * slouken@8582: GetCategoryPrefix(int category) slouken@8582: { slouken@8582: if (category < SDL_LOG_CATEGORY_RESERVED1) { slouken@8582: return SDL_category_prefixes[category]; slouken@8582: } slouken@8582: if (category < SDL_LOG_CATEGORY_CUSTOM) { slouken@8582: return "RESERVED"; slouken@8582: } slouken@8582: return "CUSTOM"; slouken@8582: } slouken@8582: #endif /* __ANDROID__ */ slouken@8582: slouken@8582: void slouken@8582: SDL_LogMessageV(int category, SDL_LogPriority priority, const char *fmt, va_list ap) slouken@8582: { slouken@8582: char *message; slouken@8582: size_t len; slouken@8582: slouken@8582: /* Nothing to do if we don't have an output function */ slouken@8582: if (!SDL_log_function) { slouken@8582: return; slouken@8582: } slouken@8582: slouken@8582: /* Make sure we don't exceed array bounds */ slouken@8582: if ((int)priority < 0 || priority >= SDL_NUM_LOG_PRIORITIES) { slouken@8582: return; slouken@8582: } slouken@8582: slouken@8582: /* See if we want to do anything with this message */ slouken@8582: if (priority < SDL_LogGetPriority(category)) { slouken@8582: return; slouken@8582: } slouken@8582: slouken@8582: message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE); slouken@8582: if (!message) { slouken@8582: return; slouken@8582: } slouken@8582: slouken@8582: SDL_vsnprintf(message, SDL_MAX_LOG_MESSAGE, fmt, ap); slouken@8582: slouken@8582: /* Chop off final endline. */ slouken@8582: len = SDL_strlen(message); slouken@8582: if ((len > 0) && (message[len-1] == '\n')) { slouken@8582: message[--len] = '\0'; slouken@8582: if ((len > 0) && (message[len-1] == '\r')) { /* catch "\r\n", too. */ slouken@8582: message[--len] = '\0'; slouken@8582: } slouken@8582: } slouken@8582: slouken@8582: SDL_log_function(SDL_log_userdata, category, priority, message); slouken@8582: SDL_stack_free(message); slouken@8582: } slouken@8582: slouken@8582: #if defined(__WIN32__) slouken@8582: /* Flag tracking the attachment of the console: 0=unattached, 1=attached, -1=error */ slouken@8582: static int consoleAttached = 0; slouken@8582: slouken@8582: /* Handle to stderr output of console. */ slouken@8582: static HANDLE stderrHandle = NULL; slouken@8582: #endif slouken@8582: slouken@8582: static void slouken@8582: SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, slouken@8582: const char *message) slouken@8582: { slouken@8582: #if defined(__WIN32__) || defined(__WINRT__) slouken@8582: /* Way too many allocations here, urgh */ slouken@8582: /* Note: One can't call SDL_SetError here, since that function itself logs. */ slouken@8582: { slouken@8582: char *output; slouken@8582: size_t length; slouken@8582: LPTSTR tstr; slouken@8582: slouken@8582: #ifndef __WINRT__ slouken@8582: BOOL attachResult; slouken@8582: DWORD attachError; slouken@8582: unsigned long charsWritten; slouken@8582: slouken@8582: /* Maybe attach console and get stderr handle */ slouken@8582: if (consoleAttached == 0) { slouken@8582: attachResult = AttachConsole(ATTACH_PARENT_PROCESS); slouken@8582: if (!attachResult) { slouken@8582: attachError = GetLastError(); slouken@8582: if (attachError == ERROR_INVALID_HANDLE) { slouken@8582: OutputDebugString(TEXT("Parent process has no console\r\n")); slouken@8582: consoleAttached = -1; slouken@8582: } else if (attachError == ERROR_GEN_FAILURE) { slouken@8582: OutputDebugString(TEXT("Could not attach to console of parent process\r\n")); slouken@8582: consoleAttached = -1; slouken@8582: } else if (attachError == ERROR_ACCESS_DENIED) { slouken@8582: /* Already attached */ slouken@8582: consoleAttached = 1; slouken@8582: } else { slouken@8582: OutputDebugString(TEXT("Error attaching console\r\n")); slouken@8582: consoleAttached = -1; slouken@8582: } slouken@8582: } else { slouken@8582: /* Newly attached */ slouken@8582: consoleAttached = 1; slouken@8582: } slouken@8582: slouken@8582: if (consoleAttached == 1) { slouken@8582: stderrHandle = GetStdHandle(STD_ERROR_HANDLE); slouken@8582: } slouken@8582: } slouken@8582: #endif /* ifndef __WINRT__ */ slouken@8582: slouken@8582: length = SDL_strlen(SDL_priority_prefixes[priority]) + 2 + SDL_strlen(message) + 1 + 1 + 1; slouken@8582: output = SDL_stack_alloc(char, length); slouken@8582: SDL_snprintf(output, length, "%s: %s\r\n", SDL_priority_prefixes[priority], message); slouken@8582: tstr = WIN_UTF8ToString(output); slouken@8582: slouken@8582: /* Output to debugger */ slouken@8582: OutputDebugString(tstr); slouken@8582: slouken@8582: #ifndef __WINRT__ slouken@8582: /* Screen output to stderr, if console was attached. */ slouken@8582: if (consoleAttached == 1) { slouken@8582: if (!WriteConsole(stderrHandle, tstr, lstrlen(tstr), &charsWritten, NULL)) { slouken@8582: OutputDebugString(TEXT("Error calling WriteConsole\r\n")); slouken@8582: } slouken@8582: if (charsWritten == ERROR_NOT_ENOUGH_MEMORY) { slouken@8582: OutputDebugString(TEXT("Insufficient heap memory to write message\r\n")); slouken@8582: } slouken@8582: } slouken@8582: #endif /* ifndef __WINRT__ */ slouken@8582: slouken@8582: SDL_free(tstr); slouken@8582: SDL_stack_free(output); slouken@8582: } slouken@8582: #elif defined(__ANDROID__) slouken@8582: { slouken@8582: char tag[32]; slouken@8582: slouken@8582: SDL_snprintf(tag, SDL_arraysize(tag), "SDL/%s", GetCategoryPrefix(category)); slouken@8582: __android_log_write(SDL_android_priority[priority], tag, message); slouken@8582: } slouken@8582: #elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA) slouken@8582: /* Technically we don't need SDL_VIDEO_DRIVER_COCOA, but that's where this function is defined for now. slouken@8582: */ slouken@8582: extern void SDL_NSLog(const char *text); slouken@8582: { slouken@8582: char *text; slouken@8582: slouken@8582: text = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE); slouken@8582: if (text) { slouken@8582: SDL_snprintf(text, SDL_MAX_LOG_MESSAGE, "%s: %s", SDL_priority_prefixes[priority], message); slouken@8582: SDL_NSLog(text); slouken@8582: SDL_stack_free(text); slouken@8582: return; slouken@8582: } slouken@8582: } slouken@8582: #elif defined(__PSP__) slouken@8582: { slouken@8582: FILE* pFile; slouken@8582: pFile = fopen ("SDL_Log.txt", "a"); slouken@8582: fprintf(pFile, "%s: %s\n", SDL_priority_prefixes[priority], message); slouken@8582: fclose (pFile); slouken@8582: } slouken@8582: #endif slouken@8582: #if HAVE_STDIO_H slouken@8582: fprintf(stderr, "%s: %s\n", SDL_priority_prefixes[priority], message); slouken@8582: #endif slouken@8582: } slouken@8582: slouken@8582: void slouken@8582: SDL_LogGetOutputFunction(SDL_LogOutputFunction *callback, void **userdata) slouken@8582: { slouken@8582: if (callback) { slouken@8582: *callback = SDL_log_function; slouken@8582: } slouken@8582: if (userdata) { slouken@8582: *userdata = SDL_log_userdata; slouken@8582: } slouken@8582: } slouken@8582: slouken@8582: void slouken@8582: SDL_LogSetOutputFunction(SDL_LogOutputFunction callback, void *userdata) slouken@8582: { slouken@8582: SDL_log_function = callback; slouken@8582: SDL_log_userdata = userdata; slouken@8582: } slouken@8582: slouken@8582: /* vi: set ts=4 sw=4 expandtab: */