Simple Directmedia Layer
at main 805 lines 23 kB view raw
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2024 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(SDL_PLATFORM_WINDOWS) 24#include "core/windows/SDL_windows.h" 25#endif 26 27// Simple log messages in SDL 28 29#include "SDL_log_c.h" 30 31#ifdef HAVE_STDIO_H 32#include <stdio.h> 33#endif 34 35#ifdef SDL_PLATFORM_ANDROID 36#include <android/log.h> 37#endif 38 39#include "stdlib/SDL_vacopy.h" 40 41// The size of the stack buffer to use for rendering log messages. 42#define SDL_MAX_LOG_MESSAGE_STACK 256 43 44#define DEFAULT_CATEGORY -1 45 46typedef struct SDL_LogLevel 47{ 48 int category; 49 SDL_LogPriority priority; 50 struct SDL_LogLevel *next; 51} SDL_LogLevel; 52 53 54// The default log output function 55static void SDLCALL SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, const char *message); 56 57static void CleanupLogPriorities(void); 58static void CleanupLogPrefixes(void); 59 60static SDL_InitState SDL_log_init; 61static SDL_Mutex *SDL_log_lock; 62static SDL_Mutex *SDL_log_function_lock; 63static SDL_LogLevel *SDL_loglevels SDL_GUARDED_BY(SDL_log_lock); 64static SDL_LogPriority SDL_log_priorities[SDL_LOG_CATEGORY_CUSTOM] SDL_GUARDED_BY(SDL_log_lock); 65static SDL_LogPriority SDL_log_default_priority SDL_GUARDED_BY(SDL_log_lock); 66static SDL_LogOutputFunction SDL_log_function SDL_GUARDED_BY(SDL_log_function_lock) = SDL_LogOutput; 67static void *SDL_log_userdata SDL_GUARDED_BY(SDL_log_function_lock) = NULL; 68 69#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA 70#pragma GCC diagnostic push 71#pragma GCC diagnostic ignored "-Wunused-variable" 72#endif 73 74// If this list changes, update the documentation for SDL_HINT_LOGGING 75static const char * const SDL_priority_names[] = { 76 NULL, 77 "TRACE", 78 "VERBOSE", 79 "DEBUG", 80 "INFO", 81 "WARN", 82 "ERROR", 83 "CRITICAL" 84}; 85SDL_COMPILE_TIME_ASSERT(priority_names, SDL_arraysize(SDL_priority_names) == SDL_LOG_PRIORITY_COUNT); 86 87// This is guarded by SDL_log_function_lock because it's the logging function that calls GetLogPriorityPrefix() 88static char *SDL_priority_prefixes[SDL_LOG_PRIORITY_COUNT] SDL_GUARDED_BY(SDL_log_function_lock); 89 90// If this list changes, update the documentation for SDL_HINT_LOGGING 91static const char * const SDL_category_names[] = { 92 "APP", 93 "ERROR", 94 "ASSERT", 95 "SYSTEM", 96 "AUDIO", 97 "VIDEO", 98 "RENDER", 99 "INPUT", 100 "TEST", 101 "GPU" 102}; 103SDL_COMPILE_TIME_ASSERT(category_names, SDL_arraysize(SDL_category_names) == SDL_LOG_CATEGORY_RESERVED2); 104 105#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA 106#pragma GCC diagnostic pop 107#endif 108 109#ifdef SDL_PLATFORM_ANDROID 110static int SDL_android_priority[] = { 111 ANDROID_LOG_UNKNOWN, 112 ANDROID_LOG_VERBOSE, 113 ANDROID_LOG_VERBOSE, 114 ANDROID_LOG_DEBUG, 115 ANDROID_LOG_INFO, 116 ANDROID_LOG_WARN, 117 ANDROID_LOG_ERROR, 118 ANDROID_LOG_FATAL 119}; 120SDL_COMPILE_TIME_ASSERT(android_priority, SDL_arraysize(SDL_android_priority) == SDL_LOG_PRIORITY_COUNT); 121#endif // SDL_PLATFORM_ANDROID 122 123static void SDLCALL SDL_LoggingChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 124{ 125 SDL_ResetLogPriorities(); 126} 127 128void SDL_InitLog(void) 129{ 130 if (!SDL_ShouldInit(&SDL_log_init)) { 131 return; 132 } 133 134 // If these fail we'll continue without them. 135 SDL_log_lock = SDL_CreateMutex(); 136 SDL_log_function_lock = SDL_CreateMutex(); 137 138 SDL_AddHintCallback(SDL_HINT_LOGGING, SDL_LoggingChanged, NULL); 139 140 SDL_SetInitialized(&SDL_log_init, true); 141} 142 143void SDL_QuitLog(void) 144{ 145 if (!SDL_ShouldQuit(&SDL_log_init)) { 146 return; 147 } 148 149 SDL_RemoveHintCallback(SDL_HINT_LOGGING, SDL_LoggingChanged, NULL); 150 151 CleanupLogPriorities(); 152 CleanupLogPrefixes(); 153 154 if (SDL_log_lock) { 155 SDL_DestroyMutex(SDL_log_lock); 156 SDL_log_lock = NULL; 157 } 158 if (SDL_log_function_lock) { 159 SDL_DestroyMutex(SDL_log_function_lock); 160 SDL_log_function_lock = NULL; 161 } 162 163 SDL_SetInitialized(&SDL_log_init, false); 164} 165 166static void SDL_CheckInitLog(void) 167{ 168 int status = SDL_GetAtomicInt(&SDL_log_init.status); 169 if (status == SDL_INIT_STATUS_INITIALIZED || 170 (status == SDL_INIT_STATUS_INITIALIZING && SDL_log_init.thread == SDL_GetCurrentThreadID())) { 171 return; 172 } 173 174 SDL_InitLog(); 175} 176 177static void CleanupLogPriorities(void) 178{ 179 while (SDL_loglevels) { 180 SDL_LogLevel *entry = SDL_loglevels; 181 SDL_loglevels = entry->next; 182 SDL_free(entry); 183 } 184} 185 186void SDL_SetLogPriorities(SDL_LogPriority priority) 187{ 188 SDL_CheckInitLog(); 189 190 SDL_LockMutex(SDL_log_lock); 191 { 192 CleanupLogPriorities(); 193 194 SDL_log_default_priority = priority; 195 for (int i = 0; i < SDL_arraysize(SDL_log_priorities); ++i) { 196 SDL_log_priorities[i] = priority; 197 } 198 } 199 SDL_UnlockMutex(SDL_log_lock); 200} 201 202void SDL_SetLogPriority(int category, SDL_LogPriority priority) 203{ 204 SDL_LogLevel *entry; 205 206 SDL_CheckInitLog(); 207 208 SDL_LockMutex(SDL_log_lock); 209 { 210 if (category >= 0 && category < SDL_arraysize(SDL_log_priorities)) { 211 SDL_log_priorities[category] = priority; 212 } else { 213 for (entry = SDL_loglevels; entry; entry = entry->next) { 214 if (entry->category == category) { 215 entry->priority = priority; 216 break; 217 } 218 } 219 220 if (!entry) { 221 entry = (SDL_LogLevel *)SDL_malloc(sizeof(*entry)); 222 if (entry) { 223 entry->category = category; 224 entry->priority = priority; 225 entry->next = SDL_loglevels; 226 SDL_loglevels = entry; 227 } 228 } 229 } 230 } 231 SDL_UnlockMutex(SDL_log_lock); 232} 233 234SDL_LogPriority SDL_GetLogPriority(int category) 235{ 236 SDL_LogLevel *entry; 237 SDL_LogPriority priority = SDL_LOG_PRIORITY_INVALID; 238 239 SDL_CheckInitLog(); 240 241 // Bypass the lock for known categories 242 // Technically if the priority was set on a different CPU the value might not 243 // be visible on this CPU for a while, but in practice it's fast enough that 244 // this performance improvement is worthwhile. 245 if (category >= 0 && category < SDL_arraysize(SDL_log_priorities)) { 246 return SDL_log_priorities[category]; 247 } 248 249 SDL_LockMutex(SDL_log_lock); 250 { 251 if (category >= 0 && category < SDL_arraysize(SDL_log_priorities)) { 252 priority = SDL_log_priorities[category]; 253 } else { 254 for (entry = SDL_loglevels; entry; entry = entry->next) { 255 if (entry->category == category) { 256 priority = entry->priority; 257 break; 258 } 259 } 260 if (priority == SDL_LOG_PRIORITY_INVALID) { 261 priority = SDL_log_default_priority; 262 } 263 } 264 } 265 SDL_UnlockMutex(SDL_log_lock); 266 267 return priority; 268} 269 270static bool ParseLogCategory(const char *string, size_t length, int *category) 271{ 272 int i; 273 274 if (SDL_isdigit(*string)) { 275 *category = SDL_atoi(string); 276 return true; 277 } 278 279 if (*string == '*') { 280 *category = DEFAULT_CATEGORY; 281 return true; 282 } 283 284 for (i = 0; i < SDL_arraysize(SDL_category_names); ++i) { 285 if (SDL_strncasecmp(string, SDL_category_names[i], length) == 0) { 286 *category = i; 287 return true; 288 } 289 } 290 return false; 291} 292 293static bool ParseLogPriority(const char *string, size_t length, SDL_LogPriority *priority) 294{ 295 int i; 296 297 if (SDL_isdigit(*string)) { 298 i = SDL_atoi(string); 299 if (i == 0) { 300 // 0 has a special meaning of "disable this category" 301 *priority = SDL_LOG_PRIORITY_COUNT; 302 return true; 303 } 304 if (i > SDL_LOG_PRIORITY_INVALID && i < SDL_LOG_PRIORITY_COUNT) { 305 *priority = (SDL_LogPriority)i; 306 return true; 307 } 308 return false; 309 } 310 311 if (SDL_strncasecmp(string, "quiet", length) == 0) { 312 *priority = SDL_LOG_PRIORITY_COUNT; 313 return true; 314 } 315 316 for (i = SDL_LOG_PRIORITY_INVALID + 1; i < SDL_LOG_PRIORITY_COUNT; ++i) { 317 if (SDL_strncasecmp(string, SDL_priority_names[i], length) == 0) { 318 *priority = (SDL_LogPriority)i; 319 return true; 320 } 321 } 322 return false; 323} 324 325static void ParseLogPriorities(const char *hint) 326{ 327 const char *name, *next; 328 int category = DEFAULT_CATEGORY; 329 SDL_LogPriority priority = SDL_LOG_PRIORITY_INVALID; 330 331 if (SDL_strchr(hint, '=') == NULL) { 332 if (ParseLogPriority(hint, SDL_strlen(hint), &priority)) { 333 SDL_SetLogPriorities(priority); 334 } 335 return; 336 } 337 338 for (name = hint; name; name = next) { 339 const char *sep = SDL_strchr(name, '='); 340 if (!sep) { 341 break; 342 } 343 next = SDL_strchr(sep, ','); 344 if (next) { 345 ++next; 346 } 347 348 if (ParseLogCategory(name, (sep - name), &category)) { 349 const char *value = sep + 1; 350 size_t len; 351 if (next) { 352 len = (next - value - 1); 353 } else { 354 len = SDL_strlen(value); 355 } 356 if (ParseLogPriority(value, len, &priority)) { 357 if (category == DEFAULT_CATEGORY) { 358 for (int i = 0; i < SDL_arraysize(SDL_log_priorities); ++i) { 359 if (SDL_log_priorities[i] == SDL_LOG_PRIORITY_INVALID) { 360 SDL_log_priorities[i] = priority; 361 } 362 } 363 SDL_log_default_priority = priority; 364 } else { 365 SDL_SetLogPriority(category, priority); 366 } 367 } 368 } 369 } 370} 371 372void SDL_ResetLogPriorities(void) 373{ 374 SDL_CheckInitLog(); 375 376 SDL_LockMutex(SDL_log_lock); 377 { 378 CleanupLogPriorities(); 379 380 SDL_log_default_priority = SDL_LOG_PRIORITY_INVALID; 381 for (int i = 0; i < SDL_arraysize(SDL_log_priorities); ++i) { 382 SDL_log_priorities[i] = SDL_LOG_PRIORITY_INVALID; 383 } 384 385 const char *hint = SDL_GetHint(SDL_HINT_LOGGING); 386 if (hint) { 387 ParseLogPriorities(hint); 388 } 389 390 if (SDL_log_default_priority == SDL_LOG_PRIORITY_INVALID) { 391 SDL_log_default_priority = SDL_LOG_PRIORITY_ERROR; 392 } 393 for (int i = 0; i < SDL_arraysize(SDL_log_priorities); ++i) { 394 if (SDL_log_priorities[i] != SDL_LOG_PRIORITY_INVALID) { 395 continue; 396 } 397 398 switch (i) { 399 case SDL_LOG_CATEGORY_APPLICATION: 400 SDL_log_priorities[i] = SDL_LOG_PRIORITY_INFO; 401 break; 402 case SDL_LOG_CATEGORY_ASSERT: 403 SDL_log_priorities[i] = SDL_LOG_PRIORITY_WARN; 404 break; 405 case SDL_LOG_CATEGORY_TEST: 406 SDL_log_priorities[i] = SDL_LOG_PRIORITY_VERBOSE; 407 break; 408 default: 409 SDL_log_priorities[i] = SDL_LOG_PRIORITY_ERROR; 410 break; 411 } 412 } 413 } 414 SDL_UnlockMutex(SDL_log_lock); 415} 416 417static void CleanupLogPrefixes(void) 418{ 419 for (int i = 0; i < SDL_arraysize(SDL_priority_prefixes); ++i) { 420 if (SDL_priority_prefixes[i]) { 421 SDL_free(SDL_priority_prefixes[i]); 422 SDL_priority_prefixes[i] = NULL; 423 } 424 } 425} 426 427static const char *GetLogPriorityPrefix(SDL_LogPriority priority) 428{ 429 if (priority <= SDL_LOG_PRIORITY_INVALID || priority >= SDL_LOG_PRIORITY_COUNT) { 430 return ""; 431 } 432 433 if (SDL_priority_prefixes[priority]) { 434 return SDL_priority_prefixes[priority]; 435 } 436 437 switch (priority) { 438 case SDL_LOG_PRIORITY_WARN: 439 return "WARNING: "; 440 case SDL_LOG_PRIORITY_ERROR: 441 return "ERROR: "; 442 case SDL_LOG_PRIORITY_CRITICAL: 443 return "ERROR: "; 444 default: 445 return ""; 446 } 447} 448 449bool SDL_SetLogPriorityPrefix(SDL_LogPriority priority, const char *prefix) 450{ 451 char *prefix_copy; 452 453 if (priority <= SDL_LOG_PRIORITY_INVALID || priority >= SDL_LOG_PRIORITY_COUNT) { 454 return SDL_InvalidParamError("priority"); 455 } 456 457 if (!prefix || !*prefix) { 458 prefix_copy = SDL_strdup(""); 459 } else { 460 prefix_copy = SDL_strdup(prefix); 461 } 462 if (!prefix_copy) { 463 return false; 464 } 465 466 SDL_LockMutex(SDL_log_function_lock); 467 { 468 if (SDL_priority_prefixes[priority]) { 469 SDL_free(SDL_priority_prefixes[priority]); 470 } 471 SDL_priority_prefixes[priority] = prefix_copy; 472 } 473 SDL_UnlockMutex(SDL_log_function_lock); 474 475 return true; 476} 477 478void SDL_Log(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) 479{ 480 va_list ap; 481 482 va_start(ap, fmt); 483 SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap); 484 va_end(ap); 485} 486 487void SDL_LogTrace(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) 488{ 489 va_list ap; 490 491 va_start(ap, fmt); 492 SDL_LogMessageV(category, SDL_LOG_PRIORITY_TRACE, fmt, ap); 493 va_end(ap); 494} 495 496void SDL_LogVerbose(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) 497{ 498 va_list ap; 499 500 va_start(ap, fmt); 501 SDL_LogMessageV(category, SDL_LOG_PRIORITY_VERBOSE, fmt, ap); 502 va_end(ap); 503} 504 505void SDL_LogDebug(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) 506{ 507 va_list ap; 508 509 va_start(ap, fmt); 510 SDL_LogMessageV(category, SDL_LOG_PRIORITY_DEBUG, fmt, ap); 511 va_end(ap); 512} 513 514void SDL_LogInfo(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) 515{ 516 va_list ap; 517 518 va_start(ap, fmt); 519 SDL_LogMessageV(category, SDL_LOG_PRIORITY_INFO, fmt, ap); 520 va_end(ap); 521} 522 523void SDL_LogWarn(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) 524{ 525 va_list ap; 526 527 va_start(ap, fmt); 528 SDL_LogMessageV(category, SDL_LOG_PRIORITY_WARN, fmt, ap); 529 va_end(ap); 530} 531 532void SDL_LogError(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) 533{ 534 va_list ap; 535 536 va_start(ap, fmt); 537 SDL_LogMessageV(category, SDL_LOG_PRIORITY_ERROR, fmt, ap); 538 va_end(ap); 539} 540 541void SDL_LogCritical(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) 542{ 543 va_list ap; 544 545 va_start(ap, fmt); 546 SDL_LogMessageV(category, SDL_LOG_PRIORITY_CRITICAL, fmt, ap); 547 va_end(ap); 548} 549 550void SDL_LogMessage(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) 551{ 552 va_list ap; 553 554 va_start(ap, fmt); 555 SDL_LogMessageV(category, priority, fmt, ap); 556 va_end(ap); 557} 558 559#ifdef SDL_PLATFORM_ANDROID 560static const char *GetCategoryPrefix(int category) 561{ 562 if (category < SDL_LOG_CATEGORY_RESERVED2) { 563 return SDL_category_names[category]; 564 } 565 if (category < SDL_LOG_CATEGORY_CUSTOM) { 566 return "RESERVED"; 567 } 568 return "CUSTOM"; 569} 570#endif // SDL_PLATFORM_ANDROID 571 572void SDL_LogMessageV(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap) 573{ 574 char *message = NULL; 575 char stack_buf[SDL_MAX_LOG_MESSAGE_STACK]; 576 size_t len_plus_term; 577 int len; 578 va_list aq; 579 580 // Nothing to do if we don't have an output function 581 if (!SDL_log_function) { 582 return; 583 } 584 585 // See if we want to do anything with this message 586 if (priority < SDL_GetLogPriority(category)) { 587 return; 588 } 589 590 // Render into stack buffer 591 va_copy(aq, ap); 592 len = SDL_vsnprintf(stack_buf, sizeof(stack_buf), fmt, aq); 593 va_end(aq); 594 595 if (len < 0) { 596 return; 597 } 598 599 // If message truncated, allocate and re-render 600 if (len >= sizeof(stack_buf) && SDL_size_add_check_overflow(len, 1, &len_plus_term)) { 601 // Allocate exactly what we need, including the zero-terminator 602 message = (char *)SDL_malloc(len_plus_term); 603 if (!message) { 604 return; 605 } 606 va_copy(aq, ap); 607 len = SDL_vsnprintf(message, len_plus_term, fmt, aq); 608 va_end(aq); 609 } else { 610 message = stack_buf; 611 } 612 613 // Chop off final endline. 614 if ((len > 0) && (message[len - 1] == '\n')) { 615 message[--len] = '\0'; 616 if ((len > 0) && (message[len - 1] == '\r')) { // catch "\r\n", too. 617 message[--len] = '\0'; 618 } 619 } 620 621 SDL_LockMutex(SDL_log_function_lock); 622 { 623 SDL_log_function(SDL_log_userdata, category, priority, message); 624 } 625 SDL_UnlockMutex(SDL_log_function_lock); 626 627 // Free only if dynamically allocated 628 if (message != stack_buf) { 629 SDL_free(message); 630 } 631} 632 633#if defined(SDL_PLATFORM_WIN32) && !defined(SDL_PLATFORM_GDK) 634enum { 635 CONSOLE_UNATTACHED = 0, 636 CONSOLE_ATTACHED_CONSOLE = 1, 637 CONSOLE_ATTACHED_FILE = 2, 638 CONSOLE_ATTACHED_ERROR = -1, 639} consoleAttached = CONSOLE_UNATTACHED; 640 641// Handle to stderr output of console. 642static HANDLE stderrHandle = NULL; 643#endif 644 645static void SDLCALL SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, 646 const char *message) 647{ 648#if defined(SDL_PLATFORM_WINDOWS) 649 // Way too many allocations here, urgh 650 // Note: One can't call SDL_SetError here, since that function itself logs. 651 { 652 char *output; 653 size_t length; 654 LPTSTR tstr; 655 bool isstack; 656 657#if !defined(SDL_PLATFORM_GDK) 658 BOOL attachResult; 659 DWORD attachError; 660 DWORD consoleMode; 661 DWORD charsWritten; 662 663 // Maybe attach console and get stderr handle 664 if (consoleAttached == CONSOLE_UNATTACHED) { 665 attachResult = AttachConsole(ATTACH_PARENT_PROCESS); 666 if (!attachResult) { 667 attachError = GetLastError(); 668 if (attachError == ERROR_INVALID_HANDLE) { 669 // This is expected when running from Visual Studio 670 // OutputDebugString(TEXT("Parent process has no console\r\n")); 671 consoleAttached = CONSOLE_ATTACHED_ERROR; 672 } else if (attachError == ERROR_GEN_FAILURE) { 673 OutputDebugString(TEXT("Could not attach to console of parent process\r\n")); 674 consoleAttached = CONSOLE_ATTACHED_ERROR; 675 } else if (attachError == ERROR_ACCESS_DENIED) { 676 // Already attached 677 consoleAttached = CONSOLE_ATTACHED_CONSOLE; 678 } else { 679 OutputDebugString(TEXT("Error attaching console\r\n")); 680 consoleAttached = CONSOLE_ATTACHED_ERROR; 681 } 682 } else { 683 // Newly attached 684 consoleAttached = CONSOLE_ATTACHED_CONSOLE; 685 } 686 687 if (consoleAttached == CONSOLE_ATTACHED_CONSOLE) { 688 stderrHandle = GetStdHandle(STD_ERROR_HANDLE); 689 690 if (GetConsoleMode(stderrHandle, &consoleMode) == 0) { 691 // WriteConsole fails if the output is redirected to a file. Must use WriteFile instead. 692 consoleAttached = CONSOLE_ATTACHED_FILE; 693 } 694 } 695 } 696#endif // !defined(SDL_PLATFORM_GDK) 697 length = SDL_strlen(GetLogPriorityPrefix(priority)) + SDL_strlen(message) + 1 + 1 + 1; 698 output = SDL_small_alloc(char, length, &isstack); 699 if (!output) { 700 return; 701 } 702 (void)SDL_snprintf(output, length, "%s%s\r\n", GetLogPriorityPrefix(priority), message); 703 tstr = WIN_UTF8ToString(output); 704 705 // Output to debugger 706 OutputDebugString(tstr); 707 708#if !defined(SDL_PLATFORM_GDK) 709 // Screen output to stderr, if console was attached. 710 if (consoleAttached == CONSOLE_ATTACHED_CONSOLE) { 711 if (!WriteConsole(stderrHandle, tstr, (DWORD)SDL_tcslen(tstr), &charsWritten, NULL)) { 712 OutputDebugString(TEXT("Error calling WriteConsole\r\n")); 713 if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) { 714 OutputDebugString(TEXT("Insufficient heap memory to write message\r\n")); 715 } 716 } 717 718 } else if (consoleAttached == CONSOLE_ATTACHED_FILE) { 719 if (!WriteFile(stderrHandle, output, (DWORD)SDL_strlen(output), &charsWritten, NULL)) { 720 OutputDebugString(TEXT("Error calling WriteFile\r\n")); 721 } 722 } 723#endif // !defined(SDL_PLATFORM_GDK) 724 725 SDL_free(tstr); 726 SDL_small_free(output, isstack); 727 } 728#elif defined(SDL_PLATFORM_ANDROID) 729 { 730 char tag[32]; 731 732 SDL_snprintf(tag, SDL_arraysize(tag), "SDL/%s", GetCategoryPrefix(category)); 733 __android_log_write(SDL_android_priority[priority], tag, message); 734 } 735#elif defined(SDL_PLATFORM_APPLE) && (defined(SDL_VIDEO_DRIVER_COCOA) || defined(SDL_VIDEO_DRIVER_UIKIT)) 736 /* Technically we don't need Cocoa/UIKit, but that's where this function is defined for now. 737 */ 738 extern void SDL_NSLog(const char *prefix, const char *text); 739 { 740 SDL_NSLog(GetLogPriorityPrefix(priority), message); 741 return; 742 } 743#elif defined(SDL_PLATFORM_PSP) || defined(SDL_PLATFORM_PS2) 744 { 745 FILE *pFile; 746 pFile = fopen("SDL_Log.txt", "a"); 747 if (pFile) { 748 (void)fprintf(pFile, "%s%s\n", GetLogPriorityPrefix(priority), message); 749 (void)fclose(pFile); 750 } 751 } 752#elif defined(SDL_PLATFORM_VITA) 753 { 754 FILE *pFile; 755 pFile = fopen("ux0:/data/SDL_Log.txt", "a"); 756 if (pFile) { 757 (void)fprintf(pFile, "%s%s\n", GetLogPriorityPrefix(priority), message); 758 (void)fclose(pFile); 759 } 760 } 761#elif defined(SDL_PLATFORM_3DS) 762 { 763 FILE *pFile; 764 pFile = fopen("sdmc:/3ds/SDL_Log.txt", "a"); 765 if (pFile) { 766 (void)fprintf(pFile, "%s%s\n", GetLogPriorityPrefix(priority), message); 767 (void)fclose(pFile); 768 } 769 } 770#endif 771#if defined(HAVE_STDIO_H) && \ 772 !(defined(SDL_PLATFORM_APPLE) && (defined(SDL_VIDEO_DRIVER_COCOA) || defined(SDL_VIDEO_DRIVER_UIKIT))) && \ 773 !(defined(SDL_PLATFORM_WIN32)) 774 (void)fprintf(stderr, "%s%s\n", GetLogPriorityPrefix(priority), message); 775#endif 776} 777 778SDL_LogOutputFunction SDL_GetDefaultLogOutputFunction(void) 779{ 780 return SDL_LogOutput; 781} 782 783void SDL_GetLogOutputFunction(SDL_LogOutputFunction *callback, void **userdata) 784{ 785 SDL_LockMutex(SDL_log_function_lock); 786 { 787 if (callback) { 788 *callback = SDL_log_function; 789 } 790 if (userdata) { 791 *userdata = SDL_log_userdata; 792 } 793 } 794 SDL_UnlockMutex(SDL_log_function_lock); 795} 796 797void SDL_SetLogOutputFunction(SDL_LogOutputFunction callback, void *userdata) 798{ 799 SDL_LockMutex(SDL_log_function_lock); 800 { 801 SDL_log_function = callback; 802 SDL_log_userdata = userdata; 803 } 804 SDL_UnlockMutex(SDL_log_function_lock); 805}