From 814f61149b8325d854ed27e7183be51a073dca5e Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 13 Oct 2024 11:12:13 -0700 Subject: [PATCH] Added SDL_OutputDebug(), SDL_OutputDebugV(), and SDL_OutputDebugString() Fixes https://github.com/libsdl-org/SDL/issues/10603 Closes https://github.com/libsdl-org/SDL/pull/11106 --- include/SDL3/SDL_log.h | 66 +++++++++++++++++++--- src/SDL_log.c | 91 +++++++++++++++++++++++++------ src/dynapi/SDL_dynapi.c | 18 +++++- src/dynapi/SDL_dynapi.sym | 3 + src/dynapi/SDL_dynapi_overrides.h | 3 + src/dynapi/SDL_dynapi_procs.h | 5 ++ 6 files changed, 161 insertions(+), 25 deletions(-) diff --git a/include/SDL3/SDL_log.h b/include/SDL3/SDL_log.h index 8feb9e0af4cb35..93a6e2072b6997 100644 --- a/include/SDL3/SDL_log.h +++ b/include/SDL3/SDL_log.h @@ -240,7 +240,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_Log(SDL_PRINTF_FORMAT_STRING const char *fm * * \param category the category of the message. * \param fmt a printf() style message format string. - * \param ... additional parameters matching % tokens in the **fmt** string, + * \param ... additional parameters matching % tokens in the `fmt` string, * if any. * * \threadsafety It is safe to call this function from any thread. @@ -265,7 +265,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_LogTrace(int category, SDL_PRINTF_FORMAT_ST * * \param category the category of the message. * \param fmt a printf() style message format string. - * \param ... additional parameters matching % tokens in the **fmt** string, + * \param ... additional parameters matching % tokens in the `fmt` string, * if any. * * \threadsafety It is safe to call this function from any thread. @@ -288,7 +288,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_LogVerbose(int category, SDL_PRINTF_FORMAT_ * * \param category the category of the message. * \param fmt a printf() style message format string. - * \param ... additional parameters matching % tokens in the **fmt** string, + * \param ... additional parameters matching % tokens in the `fmt` string, * if any. * * \threadsafety It is safe to call this function from any thread. @@ -312,7 +312,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_LogDebug(int category, SDL_PRINTF_FORMAT_ST * * \param category the category of the message. * \param fmt a printf() style message format string. - * \param ... additional parameters matching % tokens in the **fmt** string, + * \param ... additional parameters matching % tokens in the `fmt` string, * if any. * * \threadsafety It is safe to call this function from any thread. @@ -336,7 +336,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_LogInfo(int category, SDL_PRINTF_FORMAT_STR * * \param category the category of the message. * \param fmt a printf() style message format string. - * \param ... additional parameters matching % tokens in the **fmt** string, + * \param ... additional parameters matching % tokens in the `fmt` string, * if any. * * \threadsafety It is safe to call this function from any thread. @@ -360,7 +360,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_LogWarn(int category, SDL_PRINTF_FORMAT_STR * * \param category the category of the message. * \param fmt a printf() style message format string. - * \param ... additional parameters matching % tokens in the **fmt** string, + * \param ... additional parameters matching % tokens in the `fmt` string, * if any. * * \threadsafety It is safe to call this function from any thread. @@ -384,7 +384,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_LogError(int category, SDL_PRINTF_FORMAT_ST * * \param category the category of the message. * \param fmt a printf() style message format string. - * \param ... additional parameters matching % tokens in the **fmt** string, + * \param ... additional parameters matching % tokens in the `fmt` string, * if any. * * \threadsafety It is safe to call this function from any thread. @@ -409,7 +409,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_LogCritical(int category, SDL_PRINTF_FORMAT * \param category the category of the message. * \param priority the priority of the message. * \param fmt a printf() style message format string. - * \param ... additional parameters matching % tokens in the **fmt** string, + * \param ... additional parameters matching % tokens in the `fmt` string, * if any. * * \threadsafety It is safe to call this function from any thread. @@ -503,6 +503,56 @@ extern SDL_DECLSPEC void SDLCALL SDL_GetLogOutputFunction(SDL_LogOutputFunction */ extern SDL_DECLSPEC void SDLCALL SDL_SetLogOutputFunction(SDL_LogOutputFunction callback, void *userdata); +/** + * Output a line of text to the default debug output for the platform. + * + * The text should not include any newline characters, those will be automatically added as needed for the current platform. + * + * \param fmt a printf() style message format string. + * \param ... additional parameters matching % tokens in the `fmt` string, + * if any. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.6. + * + * \sa SDL_OutputDebugString + * \sa SDL_OutputDebugV + */ +extern SDL_DECLSPEC void SDLCALL SDL_OutputDebug(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) SDL_PRINTF_VARARG_FUNC(3); + +/** + * Output a line of text to the default debug output for the platform. + * + * The text should not include any newline characters, those will be automatically added as needed for the current platform. + * + * \param fmt a printf() style message format string. + * \param ap a variable argument list. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.6. + * + * \sa SDL_OutputDebug + * \sa SDL_OutputDebugString + */ +extern SDL_DECLSPEC void SDLCALL SDL_OutputDebugV(SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap) SDL_PRINTF_VARARG_FUNCV(3); + +/** + * Output a line of text to the default debug output for the platform. + * + * The text should not include any newline characters, those will be automatically added as needed for the current platform. + * + * \param message a line of text in UTF-8 format. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.1.6. + * + * \sa SDL_OutputDebug + * \sa SDL_OutputDebugV + */ +extern SDL_DECLSPEC void SDLCALL SDL_OutputDebugString(const char *message); /* Ends C function definitions when using C++ */ #ifdef __cplusplus diff --git a/src/SDL_log.c b/src/SDL_log.c index 1b9be017e21b0c..90d3240861dd6d 100644 --- a/src/SDL_log.c +++ b/src/SDL_log.c @@ -630,7 +630,55 @@ void SDL_LogMessageV(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_S } } -#if defined(SDL_PLATFORM_WIN32) && !defined(SDL_PLATFORM_GDK) +void SDL_OutputDebug(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + SDL_OutputDebugV(fmt, ap); + va_end(ap); +} + +void SDL_OutputDebugV(SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap) +{ + char *message = NULL; + char stack_buf[SDL_MAX_LOG_MESSAGE_STACK]; + size_t len_plus_term; + int len; + va_list aq; + + // Render into stack buffer + va_copy(aq, ap); + len = SDL_vsnprintf(stack_buf, sizeof(stack_buf), fmt, aq); + va_end(aq); + + if (len < 0) { + return; + } + + // If message truncated, allocate and re-render + if (len >= sizeof(stack_buf) && SDL_size_add_check_overflow(len, 1, &len_plus_term)) { + // Allocate exactly what we need, including the zero-terminator + message = (char *)SDL_malloc(len_plus_term); + if (!message) { + return; + } + va_copy(aq, ap); + len = SDL_vsnprintf(message, len_plus_term, fmt, aq); + va_end(aq); + } else { + message = stack_buf; + } + + SDL_OutputDebugString(message); + + // Free only if dynamically allocated + if (message != stack_buf) { + SDL_free(message); + } +} + +#if defined(SDL_PLATFORM_WINDOWS) && !defined(SDL_PLATFORM_GDK) enum { CONSOLE_UNATTACHED = 0, CONSOLE_ATTACHED_CONSOLE = 1, @@ -642,8 +690,7 @@ enum { static HANDLE stderrHandle = NULL; #endif -static void SDLCALL SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, - const char *message) +static void SDL_OutputDebugStringWithPrefix(const char *prefix, const char *message) { #if defined(SDL_PLATFORM_WINDOWS) // Way too many allocations here, urgh @@ -694,9 +741,9 @@ static void SDLCALL SDL_LogOutput(void *userdata, int category, SDL_LogPriority } } #endif // !defined(SDL_PLATFORM_GDK) - length = SDL_strlen(GetLogPriorityPrefix(priority)) + SDL_strlen(message) + 1 + 1 + 1; + length = SDL_strlen(prefix) + SDL_strlen(message) + 1 + 1 + 1; output = SDL_small_alloc(char, length, &isstack); - (void)SDL_snprintf(output, length, "%s%s\r\n", GetLogPriorityPrefix(priority), message); + (void)SDL_snprintf(output, length, "%s%s\r\n", prefix, message); tstr = WIN_UTF8ToString(output); // Output to debugger @@ -723,18 +770,13 @@ static void SDLCALL SDL_LogOutput(void *userdata, int category, SDL_LogPriority SDL_small_free(output, isstack); } #elif defined(SDL_PLATFORM_ANDROID) - { - char tag[32]; - - SDL_snprintf(tag, SDL_arraysize(tag), "SDL/%s", GetCategoryPrefix(category)); - __android_log_write(SDL_android_priority[priority], tag, message); - } + __android_log_write(ANDROID_LOG_DEBUG, "SDL", message); #elif defined(SDL_PLATFORM_APPLE) && (defined(SDL_VIDEO_DRIVER_COCOA) || defined(SDL_VIDEO_DRIVER_UIKIT)) /* Technically we don't need Cocoa/UIKit, but that's where this function is defined for now. */ extern void SDL_NSLog(const char *prefix, const char *text); { - SDL_NSLog(GetLogPriorityPrefix(priority), message); + SDL_NSLog(prefix, message); return; } #elif defined(SDL_PLATFORM_PSP) || defined(SDL_PLATFORM_PS2) @@ -742,7 +784,7 @@ static void SDLCALL SDL_LogOutput(void *userdata, int category, SDL_LogPriority FILE *pFile; pFile = fopen("SDL_Log.txt", "a"); if (pFile) { - (void)fprintf(pFile, "%s%s\n", GetLogPriorityPrefix(priority), message); + (void)fprintf(pFile, "%s%s\n", prefix, message); (void)fclose(pFile); } } @@ -751,7 +793,7 @@ static void SDLCALL SDL_LogOutput(void *userdata, int category, SDL_LogPriority FILE *pFile; pFile = fopen("ux0:/data/SDL_Log.txt", "a"); if (pFile) { - (void)fprintf(pFile, "%s%s\n", GetLogPriorityPrefix(priority), message); + (void)fprintf(pFile, "%s%s\n", prefix, message); (void)fclose(pFile); } } @@ -760,7 +802,7 @@ static void SDLCALL SDL_LogOutput(void *userdata, int category, SDL_LogPriority FILE *pFile; pFile = fopen("sdmc:/3ds/SDL_Log.txt", "a"); if (pFile) { - (void)fprintf(pFile, "%s%s\n", GetLogPriorityPrefix(priority), message); + (void)fprintf(pFile, "%s%s\n", prefix, message); (void)fclose(pFile); } } @@ -768,7 +810,24 @@ static void SDLCALL SDL_LogOutput(void *userdata, int category, SDL_LogPriority #if defined(HAVE_STDIO_H) && \ !(defined(SDL_PLATFORM_APPLE) && (defined(SDL_VIDEO_DRIVER_COCOA) || defined(SDL_VIDEO_DRIVER_UIKIT))) && \ !(defined(SDL_PLATFORM_WIN32)) - (void)fprintf(stderr, "%s%s\n", GetLogPriorityPrefix(priority), message); + (void)fprintf(stderr, "%s%s\n", prefix, message); +#endif +} + +void SDL_OutputDebugString(const char *message) +{ + SDL_OutputDebugStringWithPrefix("", message); +} + +static void SDLCALL SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, const char *message) +{ +#ifdef SDL_PLATFORM_ANDROID + char tag[32]; + + SDL_snprintf(tag, SDL_arraysize(tag), "SDL/%s", GetCategoryPrefix(category)); + __android_log_write(SDL_android_priority[priority], tag, message); +#else + SDL_OutputDebugStringWithPrefix(GetLogPriorityPrefix(priority), message); #endif } diff --git a/src/dynapi/SDL_dynapi.c b/src/dynapi/SDL_dynapi.c index 21b9bb80f33aff..893cffe59c9b2f 100644 --- a/src/dynapi/SDL_dynapi.c +++ b/src/dynapi/SDL_dynapi.c @@ -100,7 +100,15 @@ static void SDL_InitDynamicAPI(void); if (str != buf) { \ jump_table.SDL_free(str); \ } \ - return false; \ + return false; \ + } \ + _static void SDLCALL SDL_OutputDebug##name(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \ + { \ + va_list ap; \ + initcall; \ + va_start(ap, fmt); \ + jump_table.SDL_OutputDebugV(fmt, ap); \ + va_end(ap); \ } \ _static int SDLCALL SDL_sscanf##name(const char *buf, SDL_SCANF_FORMAT_STRING const char *fmt, ...) \ { \ @@ -238,6 +246,14 @@ static bool SDLCALL SDL_SetError_LOGSDLCALLS(SDL_PRINTF_FORMAT_STRING const char va_end(ap); return SDL_SetError_REAL("%s", buf); } +static void SDLCALL SDL_OutputDebug_LOGSDLCALLS(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) +{ + va_list ap; + SDL_Log_REAL("SDL3CALL SDL_OutputDebug"); + va_start(ap, fmt); + SDL_OutputDebugV_REAL(fmt, ap); + va_end(ap); +} static int SDLCALL SDL_sscanf_LOGSDLCALLS(const char *buf, SDL_SCANF_FORMAT_STRING const char *fmt, ...) { int result; diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index c99ddd0240ff70..02f793341695ee 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -1180,6 +1180,9 @@ SDL3_0.0.0 { SDL_DelayPrecise; SDL_CalculateGPUTextureFormatSize; SDL_SetErrorV; + SDL_OutputDebug; + SDL_OutputDebugV; + SDL_OutputDebugString; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 444d20378bbdbc..04b9321f5c2fef 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -1205,3 +1205,6 @@ #define SDL_DelayPrecise SDL_DelayPrecise_REAL #define SDL_CalculateGPUTextureFormatSize SDL_CalculateGPUTextureFormatSize_REAL #define SDL_SetErrorV SDL_SetErrorV_REAL +#define SDL_OutputDebug SDL_OutputDebug_REAL +#define SDL_OutputDebugV SDL_OutputDebugV_REAL +#define SDL_OutputDebugString SDL_OutputDebugString_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index c931a4328a46d6..6082360ac90aaf 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1211,3 +1211,8 @@ SDL_DYNAPI_PROC(Uint32,SDL_StepBackUTF8,(const char *a, const char **b),(a,b),re SDL_DYNAPI_PROC(void,SDL_DelayPrecise,(Uint64 a),(a),) SDL_DYNAPI_PROC(Uint32,SDL_CalculateGPUTextureFormatSize,(SDL_GPUTextureFormat a, Uint32 b, Uint32 c, Uint32 d),(a,b,c,d),return) SDL_DYNAPI_PROC(bool,SDL_SetErrorV,(SDL_PRINTF_FORMAT_STRING const char *a,va_list b),(a,b),return) +#ifndef SDL_DYNAPI_PROC_NO_VARARGS +SDL_DYNAPI_PROC(void,SDL_OutputDebug,(SDL_PRINTF_FORMAT_STRING const char *a,...),(a),) +#endif +SDL_DYNAPI_PROC(void,SDL_OutputDebugV,(SDL_PRINTF_FORMAT_STRING const char *a,va_list b),(a,b),) +SDL_DYNAPI_PROC(void,SDL_OutputDebugString,(const char *a),(a),)