1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 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 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_UNKNOWN,
88 ANDROID_LOG_VERBOSE,
89 ANDROID_LOG_DEBUG,
90 ANDROID_LOG_INFO,
91 ANDROID_LOG_WARN,
92 ANDROID_LOG_ERROR,
93 ANDROID_LOG_FATAL
94 };
95 #endif /* __ANDROID__ */
96
97
98 void
SDL_LogSetAllPriority(SDL_LogPriority priority)99 SDL_LogSetAllPriority(SDL_LogPriority priority)
100 {
101 SDL_LogLevel *entry;
102
103 for (entry = SDL_loglevels; entry; entry = entry->next) {
104 entry->priority = priority;
105 }
106 SDL_default_priority = priority;
107 SDL_assert_priority = priority;
108 SDL_application_priority = priority;
109 }
110
111 void
SDL_LogSetPriority(int category,SDL_LogPriority priority)112 SDL_LogSetPriority(int category, SDL_LogPriority priority)
113 {
114 SDL_LogLevel *entry;
115
116 for (entry = SDL_loglevels; entry; entry = entry->next) {
117 if (entry->category == category) {
118 entry->priority = priority;
119 return;
120 }
121 }
122
123 /* Create a new entry */
124 entry = (SDL_LogLevel *)SDL_malloc(sizeof(*entry));
125 if (entry) {
126 entry->category = category;
127 entry->priority = priority;
128 entry->next = SDL_loglevels;
129 SDL_loglevels = entry;
130 }
131 }
132
133 SDL_LogPriority
SDL_LogGetPriority(int category)134 SDL_LogGetPriority(int category)
135 {
136 SDL_LogLevel *entry;
137
138 for (entry = SDL_loglevels; entry; entry = entry->next) {
139 if (entry->category == category) {
140 return entry->priority;
141 }
142 }
143
144 if (category == SDL_LOG_CATEGORY_TEST) {
145 return SDL_test_priority;
146 } else if (category == SDL_LOG_CATEGORY_APPLICATION) {
147 return SDL_application_priority;
148 } else if (category == SDL_LOG_CATEGORY_ASSERT) {
149 return SDL_assert_priority;
150 } else {
151 return SDL_default_priority;
152 }
153 }
154
155 void
SDL_LogResetPriorities(void)156 SDL_LogResetPriorities(void)
157 {
158 SDL_LogLevel *entry;
159
160 while (SDL_loglevels) {
161 entry = SDL_loglevels;
162 SDL_loglevels = entry->next;
163 SDL_free(entry);
164 }
165
166 SDL_default_priority = DEFAULT_PRIORITY;
167 SDL_assert_priority = DEFAULT_ASSERT_PRIORITY;
168 SDL_application_priority = DEFAULT_APPLICATION_PRIORITY;
169 SDL_test_priority = DEFAULT_TEST_PRIORITY;
170 }
171
172 void
SDL_Log(SDL_PRINTF_FORMAT_STRING const char * fmt,...)173 SDL_Log(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
174 {
175 va_list ap;
176
177 va_start(ap, fmt);
178 SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap);
179 va_end(ap);
180 }
181
182 void
SDL_LogVerbose(int category,SDL_PRINTF_FORMAT_STRING const char * fmt,...)183 SDL_LogVerbose(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
184 {
185 va_list ap;
186
187 va_start(ap, fmt);
188 SDL_LogMessageV(category, SDL_LOG_PRIORITY_VERBOSE, fmt, ap);
189 va_end(ap);
190 }
191
192 void
SDL_LogDebug(int category,SDL_PRINTF_FORMAT_STRING const char * fmt,...)193 SDL_LogDebug(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
194 {
195 va_list ap;
196
197 va_start(ap, fmt);
198 SDL_LogMessageV(category, SDL_LOG_PRIORITY_DEBUG, fmt, ap);
199 va_end(ap);
200 }
201
202 void
SDL_LogInfo(int category,SDL_PRINTF_FORMAT_STRING const char * fmt,...)203 SDL_LogInfo(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
204 {
205 va_list ap;
206
207 va_start(ap, fmt);
208 SDL_LogMessageV(category, SDL_LOG_PRIORITY_INFO, fmt, ap);
209 va_end(ap);
210 }
211
212 void
SDL_LogWarn(int category,SDL_PRINTF_FORMAT_STRING const char * fmt,...)213 SDL_LogWarn(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
214 {
215 va_list ap;
216
217 va_start(ap, fmt);
218 SDL_LogMessageV(category, SDL_LOG_PRIORITY_WARN, fmt, ap);
219 va_end(ap);
220 }
221
222 void
SDL_LogError(int category,SDL_PRINTF_FORMAT_STRING const char * fmt,...)223 SDL_LogError(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
224 {
225 va_list ap;
226
227 va_start(ap, fmt);
228 SDL_LogMessageV(category, SDL_LOG_PRIORITY_ERROR, fmt, ap);
229 va_end(ap);
230 }
231
232 void
SDL_LogCritical(int category,SDL_PRINTF_FORMAT_STRING const char * fmt,...)233 SDL_LogCritical(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
234 {
235 va_list ap;
236
237 va_start(ap, fmt);
238 SDL_LogMessageV(category, SDL_LOG_PRIORITY_CRITICAL, fmt, ap);
239 va_end(ap);
240 }
241
242 void
SDL_LogMessage(int category,SDL_LogPriority priority,SDL_PRINTF_FORMAT_STRING const char * fmt,...)243 SDL_LogMessage(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
244 {
245 va_list ap;
246
247 va_start(ap, fmt);
248 SDL_LogMessageV(category, priority, fmt, ap);
249 va_end(ap);
250 }
251
252 #ifdef __ANDROID__
253 static const char *
GetCategoryPrefix(int category)254 GetCategoryPrefix(int category)
255 {
256 if (category < SDL_LOG_CATEGORY_RESERVED1) {
257 return SDL_category_prefixes[category];
258 }
259 if (category < SDL_LOG_CATEGORY_CUSTOM) {
260 return "RESERVED";
261 }
262 return "CUSTOM";
263 }
264 #endif /* __ANDROID__ */
265
266 void
SDL_LogMessageV(int category,SDL_LogPriority priority,const char * fmt,va_list ap)267 SDL_LogMessageV(int category, SDL_LogPriority priority, const char *fmt, va_list ap)
268 {
269 char *message;
270 size_t len;
271
272 /* Nothing to do if we don't have an output function */
273 if (!SDL_log_function) {
274 return;
275 }
276
277 /* Make sure we don't exceed array bounds */
278 if ((int)priority < 0 || priority >= SDL_NUM_LOG_PRIORITIES) {
279 return;
280 }
281
282 /* See if we want to do anything with this message */
283 if (priority < SDL_LogGetPriority(category)) {
284 return;
285 }
286
287 message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
288 if (!message) {
289 return;
290 }
291
292 SDL_vsnprintf(message, SDL_MAX_LOG_MESSAGE, fmt, ap);
293
294 /* Chop off final endline. */
295 len = SDL_strlen(message);
296 if ((len > 0) && (message[len-1] == '\n')) {
297 message[--len] = '\0';
298 if ((len > 0) && (message[len-1] == '\r')) { /* catch "\r\n", too. */
299 message[--len] = '\0';
300 }
301 }
302
303 SDL_log_function(SDL_log_userdata, category, priority, message);
304 SDL_stack_free(message);
305 }
306
307 #if defined(__WIN32__)
308 /* Flag tracking the attachment of the console: 0=unattached, 1=attached, -1=error */
309 static int consoleAttached = 0;
310
311 /* Handle to stderr output of console. */
312 static HANDLE stderrHandle = NULL;
313 #endif
314
315 static void
SDL_LogOutput(void * userdata,int category,SDL_LogPriority priority,const char * message)316 SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority,
317 const char *message)
318 {
319 #if defined(__WIN32__) || defined(__WINRT__)
320 /* Way too many allocations here, urgh */
321 /* Note: One can't call SDL_SetError here, since that function itself logs. */
322 {
323 char *output;
324 size_t length;
325 LPTSTR tstr;
326
327 #if !defined(HAVE_STDIO_H) && !defined(__WINRT__)
328 BOOL attachResult;
329 DWORD attachError;
330 unsigned long charsWritten;
331
332 /* Maybe attach console and get stderr handle */
333 if (consoleAttached == 0) {
334 attachResult = AttachConsole(ATTACH_PARENT_PROCESS);
335 if (!attachResult) {
336 attachError = GetLastError();
337 if (attachError == ERROR_INVALID_HANDLE) {
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 }
359 #endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) */
360
361 length = SDL_strlen(SDL_priority_prefixes[priority]) + 2 + SDL_strlen(message) + 1 + 1 + 1;
362 output = SDL_stack_alloc(char, length);
363 SDL_snprintf(output, length, "%s: %s\r\n", SDL_priority_prefixes[priority], message);
364 tstr = WIN_UTF8ToString(output);
365
366 /* Output to debugger */
367 OutputDebugString(tstr);
368
369 #if !defined(HAVE_STDIO_H) && !defined(__WINRT__)
370 /* Screen output to stderr, if console was attached. */
371 if (consoleAttached == 1) {
372 if (!WriteConsole(stderrHandle, tstr, lstrlen(tstr), &charsWritten, NULL)) {
373 OutputDebugString(TEXT("Error calling WriteConsole\r\n"));
374 if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {
375 OutputDebugString(TEXT("Insufficient heap memory to write message\r\n"));
376 }
377 }
378 }
379 #endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) */
380
381 SDL_free(tstr);
382 SDL_stack_free(output);
383 }
384 #elif defined(__ANDROID__)
385 {
386 char tag[32];
387
388 SDL_snprintf(tag, SDL_arraysize(tag), "SDL/%s", GetCategoryPrefix(category));
389 __android_log_write(SDL_android_priority[priority], tag, message);
390 }
391 #elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA)
392 /* Technically we don't need SDL_VIDEO_DRIVER_COCOA, but that's where this function is defined for now.
393 */
394 extern void SDL_NSLog(const char *text);
395 {
396 char *text;
397
398 text = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
399 if (text) {
400 SDL_snprintf(text, SDL_MAX_LOG_MESSAGE, "%s: %s", SDL_priority_prefixes[priority], message);
401 SDL_NSLog(text);
402 SDL_stack_free(text);
403 return;
404 }
405 }
406 #elif defined(__PSP__)
407 {
408 FILE* pFile;
409 pFile = fopen ("SDL_Log.txt", "a");
410 fprintf(pFile, "%s: %s\n", SDL_priority_prefixes[priority], message);
411 fclose (pFile);
412 }
413 #endif
414 #if HAVE_STDIO_H
415 fprintf(stderr, "%s: %s\n", SDL_priority_prefixes[priority], message);
416 #if __NACL__
417 fflush(stderr);
418 #endif
419 #endif
420 }
421
422 void
SDL_LogGetOutputFunction(SDL_LogOutputFunction * callback,void ** userdata)423 SDL_LogGetOutputFunction(SDL_LogOutputFunction *callback, void **userdata)
424 {
425 if (callback) {
426 *callback = SDL_log_function;
427 }
428 if (userdata) {
429 *userdata = SDL_log_userdata;
430 }
431 }
432
433 void
SDL_LogSetOutputFunction(SDL_LogOutputFunction callback,void * userdata)434 SDL_LogSetOutputFunction(SDL_LogOutputFunction callback, void *userdata)
435 {
436 SDL_log_function = callback;
437 SDL_log_userdata = userdata;
438 }
439
440 /* vi: set ts=4 sw=4 expandtab: */
441