2 Simple DirectMedia Layer
3 Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "./SDL_internal.h"
23 #if defined(__WIN32__) || defined(__WINRT__)
24 #include "core/windows/SDL_windows.h"
27 /* Simple log messages in SDL */
29 #include "SDL_error.h"
36 #if defined(__ANDROID__)
37 #include <android/log.h>
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
45 typedef struct SDL_LogLevel
48 SDL_LogPriority priority;
49 struct SDL_LogLevel *next;
52 /* The default log output function */
53 static void SDLCALL SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, const char *message);
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;
63 static const char *SDL_priority_prefixes[SDL_NUM_LOG_PRIORITIES] = {
74 static const char *SDL_category_prefixes[SDL_LOG_CATEGORY_RESERVED1] = {
84 static int SDL_android_priority[SDL_NUM_LOG_PRIORITIES] = {
93 #endif /* __ANDROID__ */
97 SDL_LogSetAllPriority(SDL_LogPriority priority)
101 for (entry = SDL_loglevels; entry; entry = entry->next) {
102 entry->priority = priority;
104 SDL_default_priority = priority;
105 SDL_assert_priority = priority;
106 SDL_application_priority = priority;
110 SDL_LogSetPriority(int category, SDL_LogPriority priority)
114 for (entry = SDL_loglevels; entry; entry = entry->next) {
115 if (entry->category == category) {
116 entry->priority = priority;
121 /* Create a new entry */
122 entry = (SDL_LogLevel *)SDL_malloc(sizeof(*entry));
124 entry->category = category;
125 entry->priority = priority;
126 entry->next = SDL_loglevels;
127 SDL_loglevels = entry;
132 SDL_LogGetPriority(int category)
136 for (entry = SDL_loglevels; entry; entry = entry->next) {
137 if (entry->category == category) {
138 return entry->priority;
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;
149 return SDL_default_priority;
154 SDL_LogResetPriorities(void)
158 while (SDL_loglevels) {
159 entry = SDL_loglevels;
160 SDL_loglevels = entry->next;
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;
171 SDL_Log(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
176 SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap);
181 SDL_LogVerbose(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
186 SDL_LogMessageV(category, SDL_LOG_PRIORITY_VERBOSE, fmt, ap);
191 SDL_LogDebug(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
196 SDL_LogMessageV(category, SDL_LOG_PRIORITY_DEBUG, fmt, ap);
201 SDL_LogInfo(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
206 SDL_LogMessageV(category, SDL_LOG_PRIORITY_INFO, fmt, ap);
211 SDL_LogWarn(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
216 SDL_LogMessageV(category, SDL_LOG_PRIORITY_WARN, fmt, ap);
221 SDL_LogError(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
226 SDL_LogMessageV(category, SDL_LOG_PRIORITY_ERROR, fmt, ap);
231 SDL_LogCritical(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
236 SDL_LogMessageV(category, SDL_LOG_PRIORITY_CRITICAL, fmt, ap);
241 SDL_LogMessage(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
246 SDL_LogMessageV(category, priority, fmt, ap);
252 GetCategoryPrefix(int category)
254 if (category < SDL_LOG_CATEGORY_RESERVED1) {
255 return SDL_category_prefixes[category];
257 if (category < SDL_LOG_CATEGORY_CUSTOM) {
262 #endif /* __ANDROID__ */
265 SDL_LogMessageV(int category, SDL_LogPriority priority, const char *fmt, va_list ap)
270 /* Nothing to do if we don't have an output function */
271 if (!SDL_log_function) {
275 /* Make sure we don't exceed array bounds */
276 if ((int)priority < 0 || priority >= SDL_NUM_LOG_PRIORITIES) {
280 /* See if we want to do anything with this message */
281 if (priority < SDL_LogGetPriority(category)) {
285 message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
290 SDL_vsnprintf(message, SDL_MAX_LOG_MESSAGE, fmt, ap);
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';
301 SDL_log_function(SDL_log_userdata, category, priority, message);
302 SDL_stack_free(message);
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;
309 /* Handle to stderr output of console. */
310 static HANDLE stderrHandle = NULL;
314 SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority,
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. */
325 #if !defined(HAVE_STDIO_H) && !defined(__WINRT__)
328 unsigned long charsWritten;
331 /* Maybe attach console and get stderr handle */
332 if (consoleAttached == 0) {
333 attachResult = AttachConsole(ATTACH_PARENT_PROCESS);
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 */
347 OutputDebugString(TEXT("Error attaching console\r\n"));
348 consoleAttached = -1;
355 if (consoleAttached == 1) {
356 stderrHandle = GetStdHandle(STD_ERROR_HANDLE);
358 if (GetConsoleMode(stderrHandle, &consoleMode) == 0) {
359 /* WriteConsole fails if the output is redirected to a file. Must use WriteFile instead. */
364 #endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) */
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);
371 /* Output to debugger */
372 OutputDebugString(tstr);
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"));
384 } else if (consoleAttached == 2) {
385 if (!WriteFile(stderrHandle, output, lstrlenA(output), &charsWritten, NULL)) {
386 OutputDebugString(TEXT("Error calling WriteFile\r\n"));
389 #endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) */
392 SDL_stack_free(output);
394 #elif defined(__ANDROID__)
398 SDL_snprintf(tag, SDL_arraysize(tag), "SDL/%s", GetCategoryPrefix(category));
399 __android_log_write(SDL_android_priority[priority], tag, message);
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.
404 extern void SDL_NSLog(const char *text);
408 text = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
410 SDL_snprintf(text, SDL_MAX_LOG_MESSAGE, "%s: %s", SDL_priority_prefixes[priority], message);
412 SDL_stack_free(text);
416 #elif defined(__PSP__)
419 pFile = fopen ("SDL_Log.txt", "a");
420 fprintf(pFile, "%s: %s\n", SDL_priority_prefixes[priority], message);
425 fprintf(stderr, "%s: %s\n", SDL_priority_prefixes[priority], message);
433 SDL_LogGetOutputFunction(SDL_LogOutputFunction *callback, void **userdata)
436 *callback = SDL_log_function;
439 *userdata = SDL_log_userdata;
444 SDL_LogSetOutputFunction(SDL_LogOutputFunction callback, void *userdata)
446 SDL_log_function = callback;
447 SDL_log_userdata = userdata;
450 /* vi: set ts=4 sw=4 expandtab: */