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