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