diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h index 56a2194b32..25c0432503 100644 --- a/include/SDL3/SDL_events.h +++ b/include/SDL3/SDL_events.h @@ -1565,6 +1565,38 @@ extern SDL_DECLSPEC Uint32 SDLCALL SDL_RegisterEvents(int numevents); */ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GetWindowFromEvent(const SDL_Event *event); +/** + * Generate a human-readable description of an event. + * + * This will fill `buf` with a null-terminated string that might look + * something like this: + * + * ``` + * SDL_EVENT_MOUSE_MOTION (timestamp=1140256324 windowid=2 which=0 state=0 x=492.99 y=139.09 xrel=52 yrel=6) + * ``` + * + * The exact format of the string is not guaranteed; it is intended for + * logging purposes, to be read by a human, and not parsed by a computer. + * + * The returned value follows the same rules as SDL_snprintf(): `buf` + * will always be NULL-terminated (unless `buflen` is zero), and will be + * truncated if `buflen` is too small. The return code is the number of bytes + * needed for the complete string, not counting the NULL-terminator, whether + * the string was truncated or not. Unlike SDL_snprintf(), though, this + * function never returns -1. + * + * \param event an event to describe. May be NULL. + * \param buf the buffer to fill with the description string. May be NULL. + * \param buflen the maximum bytes that can be written to `buf`. + * \returns number of bytes needed for the full string, not counting the + * null-terminator byte. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.4.0. + */ +extern SDL_DECLSPEC int SDLCALL SDL_GetEventDescription(const SDL_Event *event, char *buf, int buflen); + /* Ends C function definitions when using C++ */ #ifdef __cplusplus } diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index b602a1a9ca..a3e47763ea 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -1252,6 +1252,7 @@ SDL3_0.0.0 { SDL_CreateGPURenderer; SDL_PutAudioStreamPlanarData; SDL_SetAudioIterationCallbacks; + SDL_GetEventDescription; # 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 bfbce4fa75..4242dab7e9 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -1277,3 +1277,4 @@ #define SDL_CreateGPURenderer SDL_CreateGPURenderer_REAL #define SDL_PutAudioStreamPlanarData SDL_PutAudioStreamPlanarData_REAL #define SDL_SetAudioIterationCallbacks SDL_SetAudioIterationCallbacks_REAL +#define SDL_GetEventDescription SDL_GetEventDescription_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index b29428f58b..b4de5f9493 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1285,3 +1285,4 @@ SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_GetGPUDeviceProperties,(SDL_GPUDevice *a),( SDL_DYNAPI_PROC(SDL_Renderer*,SDL_CreateGPURenderer,(SDL_Window *a,SDL_GPUShaderFormat b,SDL_GPUDevice **c),(a,b,c),return) SDL_DYNAPI_PROC(bool,SDL_PutAudioStreamPlanarData,(SDL_AudioStream *a,const void * const*b,int c),(a,b,c),return) SDL_DYNAPI_PROC(bool,SDL_SetAudioIterationCallbacks,(SDL_AudioDeviceID a,SDL_AudioIterationCallback b,SDL_AudioIterationCallback c,void *d),(a,b,c,d),return) +SDL_DYNAPI_PROC(int,SDL_GetEventDescription,(const SDL_Event *a,char *b,int c),(a,b,c),return) diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index 349d575015..1bac2c2bb8 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -430,26 +430,18 @@ static void SDLCALL SDL_EventLoggingChanged(void *userdata, const char *name, co SDL_EventLoggingVerbosity = (hint && *hint) ? SDL_clamp(SDL_atoi(hint), 0, 3) : 0; } -static void SDL_LogEvent(const SDL_Event *event) +int SDL_GetEventDescription(const SDL_Event *event, char *buf, int buflen) { + if (!event) { + return SDL_snprintf(buf, buflen, "(null)"); + } + static const char *pen_axisnames[] = { "PRESSURE", "XTILT", "YTILT", "DISTANCE", "ROTATION", "SLIDER", "TANGENTIAL_PRESSURE" }; SDL_COMPILE_TIME_ASSERT(pen_axisnames_array_matches, SDL_arraysize(pen_axisnames) == SDL_PEN_AXIS_COUNT); char name[64]; char details[128]; - // sensor/mouse/pen/finger motion are spammy, ignore these if they aren't demanded. - if ((SDL_EventLoggingVerbosity < 2) && - ((event->type == SDL_EVENT_MOUSE_MOTION) || - (event->type == SDL_EVENT_FINGER_MOTION) || - (event->type == SDL_EVENT_PEN_AXIS) || - (event->type == SDL_EVENT_PEN_MOTION) || - (event->type == SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION) || - (event->type == SDL_EVENT_GAMEPAD_SENSOR_UPDATE) || - (event->type == SDL_EVENT_SENSOR_UPDATE))) { - return; - } - // this is to make (void)SDL_snprintf() calls cleaner. #define uint unsigned int @@ -879,12 +871,41 @@ static void SDL_LogEvent(const SDL_Event *event) } break; } +#undef uint + int retval = 0; if (name[0]) { - SDL_Log("SDL EVENT: %s%s", name, details); + retval = SDL_snprintf(buf, buflen, "%s%s", name, details); + } else if (buf && (buflen > 0)) { + *buf = '\0'; + } + return retval; +} + +static void SDL_LogEvent(const SDL_Event *event) +{ + if (!event) { + return; } -#undef uint + // sensor/mouse/pen/finger motion are spammy, ignore these if they aren't demanded. + if ((SDL_EventLoggingVerbosity < 2) && + ((event->type == SDL_EVENT_MOUSE_MOTION) || + (event->type == SDL_EVENT_FINGER_MOTION) || + (event->type == SDL_EVENT_PEN_AXIS) || + (event->type == SDL_EVENT_PEN_MOTION) || + (event->type == SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION) || + (event->type == SDL_EVENT_GAMEPAD_SENSOR_UPDATE) || + (event->type == SDL_EVENT_SENSOR_UPDATE))) { + return; + } + + char buf[256]; + const int rc = SDL_GetEventDescription(event, buf, sizeof (buf)); + SDL_assert(rc < sizeof (buf)); // if this overflows, we should make `buf` larger, but this is currently larger than the max SDL_GetEventDescription returns. + if (buf[0]) { + SDL_Log("SDL EVENT: %s", buf); + } } void SDL_StopEventLoop(void)