Introduce formal policy for APIs that return strings.

This declares that any `const char *` returned from SDL is owned by SDL, and
promises to be valid _at least_ until the next time the event queue runs, or
SDL_Quit() is called, even if the thing that owns the string gets destroyed
or changed before then.

This is noted in the headers as "the SDL_GetStringRule", so this will both be
greppable to find a detailed explaination in docs/README-strings.md and
wikiheaders will automatically turn it into a link we can point at the
appropriate documentation.

Fixes #9902.

(and several FIXMEs, both known and yet-undocumented.)
This commit is contained in:
Ryan C. Gordon 2024-06-01 22:05:21 -04:00
parent b1f3682216
commit e23257307e
51 changed files with 262 additions and 123 deletions

58
docs/README-strings.md Normal file
View file

@ -0,0 +1,58 @@
# String policies in SDL3.
## Encoding.
Unless otherwise specified, all strings in SDL, across all platforms, are
UTF-8 encoded.
## The SDL Get String Rule.
Did you see 'SDL_GetStringRule' in the wiki or headers? Here are the details
that aren't worth copying across dozens of functions' documentation.
tl;dr: If an SDL function returns a `const char *` string, do not modify or
free it, and if you need to save it, make a copy right away.
In several cases, SDL wants to return a string to the app, and the question
in any library that does this is: _who owns this thing?_
The answer in almost all cases, is that SDL does, but not for long.
The pointer is only guaranteed to be valid until the next time the event
queue is updated, or SDL_Quit is called.
The reason for this is memory safety.
There are several strings that SDL provides that could be freed at
any moment. For example, an app calls SDL_GetAudioDeviceName(), which returns
a string that is part of the internal audio device structure. But, while this
function is returning, the user yanks the USB audio device out of the
computer, and SDL decides to deallocate the structure...and the string!
Now the app is holding a pointer that didn't live long enough to be useful,
and could crash if accessed.
To avoid this, instead of calling SDL_free on a string as soon as it's done
with it, SDL adds the pointer to a list. This list is freed at specific
points: when the event queue is run (for ongoing cleanup) and when SDL_Quit
is called (to catch things that are just hanging around). This allows the
app to use a string without worrying about it becoming bogus in the middle
of a printf() call. If the app needs it for longer, it should copy it.
When does "the event queue run"? There are several points:
- If the app calls SDL_PumpEvents() _from any thread_.
- SDL_PumpEvents is also called by several other APIs internally:
SDL_PollEvent(), SDL_PeepEvents(), SDL_WaitEvent(),
SDL_WaitEventTimeout(), and maybe others.
- If you are using [the main callbacks](README-main-functions), the
event queue can run immediately after any of the callback functions return.
Note that these are just guaranteed minimum lifespans; any given string
might live much longer--some might even be static memory that is _never_
deallocated--but this rule promises that the app has a safe window.
Note that none of this applies if the return value is `char *` instead of
`const char *`. Please see the specific function's documentation for how
to handle those pointers.

View file

@ -374,9 +374,11 @@ extern SDL_DECLSPEC int SDLCALL SDL_GetNumAudioDrivers(void);
* first (as far as the SDL developers believe) are earlier in the list. * first (as far as the SDL developers believe) are earlier in the list.
* *
* The names of drivers are all simple, low-ASCII identifiers, like "alsa", * The names of drivers are all simple, low-ASCII identifiers, like "alsa",
* "coreaudio" or "xaudio2". These never have Unicode characters, and are not * "coreaudio" or "wasapi". These never have Unicode characters, and are not
* meant to be proper names. * meant to be proper names.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param index the index of the audio driver; the value ranges from 0 to * \param index the index of the audio driver; the value ranges from 0 to
* SDL_GetNumAudioDrivers() - 1 * SDL_GetNumAudioDrivers() - 1
* \returns the name of the audio driver at the requested index, or NULL if an * \returns the name of the audio driver at the requested index, or NULL if an
@ -394,11 +396,11 @@ extern SDL_DECLSPEC const char *SDLCALL SDL_GetAudioDriver(int index);
/** /**
* Get the name of the current audio driver. * Get the name of the current audio driver.
* *
* The returned string points to internal static memory and thus never becomes * The names of drivers are all simple, low-ASCII identifiers, like "alsa",
* invalid, even if you quit the audio subsystem and initialize a new driver * "coreaudio" or "wasapi". These never have Unicode characters, and are not
* (although such a case would return a different static string from another * meant to be proper names.
* call to this function, of course). As such, you should not modify or free *
* the returned string. * The returned string follows the SDL_GetStringRule.
* *
* \returns the name of the current audio driver or NULL if no driver has been * \returns the name of the current audio driver or NULL if no driver has been
* initialized. * initialized.
@ -470,8 +472,7 @@ extern SDL_DECLSPEC SDL_AudioDeviceID *SDLCALL SDL_GetAudioCaptureDevices(int *c
/** /**
* Get the human-readable name of a specific audio device. * Get the human-readable name of a specific audio device.
* *
* The string returned by this function is UTF-8 encoded. The caller should * The returned string follows the SDL_GetStringRule.
* call SDL_free on the return value when done with it.
* *
* \param devid the instance ID of the device to query. * \param devid the instance ID of the device to query.
* \returns the name of the audio device, or NULL on error. * \returns the name of the audio device, or NULL on error.
@ -484,7 +485,7 @@ extern SDL_DECLSPEC SDL_AudioDeviceID *SDLCALL SDL_GetAudioCaptureDevices(int *c
* \sa SDL_GetAudioCaptureDevices * \sa SDL_GetAudioCaptureDevices
* \sa SDL_GetDefaultAudioInfo * \sa SDL_GetDefaultAudioInfo
*/ */
extern SDL_DECLSPEC char *SDLCALL SDL_GetAudioDeviceName(SDL_AudioDeviceID devid); extern SDL_DECLSPEC const char *SDLCALL SDL_GetAudioDeviceName(SDL_AudioDeviceID devid);
/** /**
* Get the current audio format of a specific audio device. * Get the current audio format of a specific audio device.

View file

@ -134,6 +134,8 @@ extern SDL_DECLSPEC int SDLCALL SDL_GetNumCameraDrivers(void);
* "coremedia" or "android". These never have Unicode characters, and are not * "coremedia" or "android". These never have Unicode characters, and are not
* meant to be proper names. * meant to be proper names.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param index the index of the camera driver; the value ranges from 0 to * \param index the index of the camera driver; the value ranges from 0 to
* SDL_GetNumCameraDrivers() - 1 * SDL_GetNumCameraDrivers() - 1
* \returns the name of the camera driver at the requested index, or NULL if * \returns the name of the camera driver at the requested index, or NULL if
@ -150,11 +152,11 @@ extern SDL_DECLSPEC const char *SDLCALL SDL_GetCameraDriver(int index);
/** /**
* Get the name of the current camera driver. * Get the name of the current camera driver.
* *
* The returned string points to internal static memory and thus never becomes * The names of drivers are all simple, low-ASCII identifiers, like "v4l2",
* invalid, even if you quit the camera subsystem and initialize a new driver * "coremedia" or "android". These never have Unicode characters, and are not
* (although such a case would return a different static string from another * meant to be proper names.
* call to this function, of course). As such, you should not modify or free *
* the returned string. * The returned string follows the SDL_GetStringRule.
* *
* \returns the name of the current camera driver or NULL if no driver has * \returns the name of the current camera driver or NULL if no driver has
* been initialized. * been initialized.

View file

@ -96,15 +96,14 @@ extern SDL_DECLSPEC int SDLCALL SDL_OutOfMemory(void);
* Error strings are set per-thread, so an error set in a different thread * Error strings are set per-thread, so an error set in a different thread
* will not interfere with the current thread's operation. * will not interfere with the current thread's operation.
* *
* The returned string is internally allocated and must not be freed by the * The returned string does **NOT** follow the SDL_GetStringRule! The
* application. * pointer is valid until the current thread's error string is changed, so
* the caller should make a copy if the string is to be used after calling
* into SDL again.
* *
* \returns a message with information about the specific error that occurred, * \returns a message with information about the specific error that occurred,
* or an empty string if there hasn't been an error message set since * or an empty string if there hasn't been an error message set since
* the last call to SDL_ClearError(). The message is only applicable * the last call to SDL_ClearError().
* when an SDL function has signaled an error. You must check the
* return values of SDL function calls to determine when to
* appropriately call SDL_GetError().
* *
* \since This function is available since SDL 3.0.0. * \since This function is available since SDL 3.0.0.
* *

View file

@ -500,6 +500,8 @@ extern SDL_DECLSPEC SDL_bool SDLCALL SDL_IsGamepad(SDL_JoystickID instance_id);
* *
* This can be called before any gamepads are opened. * This can be called before any gamepads are opened.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param instance_id the joystick instance ID * \param instance_id the joystick instance ID
* \returns the name of the selected gamepad. If no name can be found, this * \returns the name of the selected gamepad. If no name can be found, this
* function returns NULL; call SDL_GetError() for more information. * function returns NULL; call SDL_GetError() for more information.
@ -516,6 +518,8 @@ extern SDL_DECLSPEC const char *SDLCALL SDL_GetGamepadInstanceName(SDL_JoystickI
* *
* This can be called before any gamepads are opened. * This can be called before any gamepads are opened.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param instance_id the joystick instance ID * \param instance_id the joystick instance ID
* \returns the path of the selected gamepad. If no path can be found, this * \returns the path of the selected gamepad. If no path can be found, this
* function returns NULL; call SDL_GetError() for more information. * function returns NULL; call SDL_GetError() for more information.
@ -748,6 +752,8 @@ extern SDL_DECLSPEC SDL_JoystickID SDLCALL SDL_GetGamepadInstanceID(SDL_Gamepad
/** /**
* Get the implementation-dependent name for an opened gamepad. * Get the implementation-dependent name for an opened gamepad.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param gamepad a gamepad identifier previously returned by * \param gamepad a gamepad identifier previously returned by
* SDL_OpenGamepad() * SDL_OpenGamepad()
* \returns the implementation dependent name for the gamepad, or NULL if * \returns the implementation dependent name for the gamepad, or NULL if
@ -762,6 +768,8 @@ extern SDL_DECLSPEC const char *SDLCALL SDL_GetGamepadName(SDL_Gamepad *gamepad)
/** /**
* Get the implementation-dependent path for an opened gamepad. * Get the implementation-dependent path for an opened gamepad.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param gamepad a gamepad identifier previously returned by * \param gamepad a gamepad identifier previously returned by
* SDL_OpenGamepad() * SDL_OpenGamepad()
* \returns the implementation dependent path for the gamepad, or NULL if * \returns the implementation dependent path for the gamepad, or NULL if
@ -887,6 +895,8 @@ extern SDL_DECLSPEC Uint16 SDLCALL SDL_GetGamepadFirmwareVersion(SDL_Gamepad *ga
* *
* Returns the serial number of the gamepad, or NULL if it is not available. * Returns the serial number of the gamepad, or NULL if it is not available.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param gamepad the gamepad object to query. * \param gamepad the gamepad object to query.
* \returns the serial number, or NULL if unavailable. * \returns the serial number, or NULL if unavailable.
* *
@ -1045,7 +1055,7 @@ extern SDL_DECLSPEC SDL_GamepadType SDLCALL SDL_GetGamepadTypeFromString(const c
/** /**
* Convert from an SDL_GamepadType enum to a string. * Convert from an SDL_GamepadType enum to a string.
* *
* The caller should not SDL_free() the returned string. * The returned string follows the SDL_GetStringRule.
* *
* \param type an enum value for a given SDL_GamepadType * \param type an enum value for a given SDL_GamepadType
* \returns a string for the given type, or NULL if an invalid type is * \returns a string for the given type, or NULL if an invalid type is
@ -1083,7 +1093,7 @@ extern SDL_DECLSPEC SDL_GamepadAxis SDLCALL SDL_GetGamepadAxisFromString(const c
/** /**
* Convert from an SDL_GamepadAxis enum to a string. * Convert from an SDL_GamepadAxis enum to a string.
* *
* The caller should not SDL_free() the returned string. * The returned string follows the SDL_GetStringRule.
* *
* \param axis an enum value for a given SDL_GamepadAxis * \param axis an enum value for a given SDL_GamepadAxis
* \returns a string for the given axis, or NULL if an invalid axis is * \returns a string for the given axis, or NULL if an invalid axis is
@ -1158,7 +1168,7 @@ extern SDL_DECLSPEC SDL_GamepadButton SDLCALL SDL_GetGamepadButtonFromString(con
/** /**
* Convert from an SDL_GamepadButton enum to a string. * Convert from an SDL_GamepadButton enum to a string.
* *
* The caller should not SDL_free() the returned string. * The returned string follows the SDL_GetStringRule.
* *
* \param button an enum value for a given SDL_GamepadButton * \param button an enum value for a given SDL_GamepadButton
* \returns a string for the given button, or NULL if an invalid button is * \returns a string for the given button, or NULL if an invalid button is
@ -1446,6 +1456,8 @@ extern SDL_DECLSPEC void SDLCALL SDL_CloseGamepad(SDL_Gamepad *gamepad);
* Return the sfSymbolsName for a given button on a gamepad on Apple * Return the sfSymbolsName for a given button on a gamepad on Apple
* platforms. * platforms.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param gamepad the gamepad to query * \param gamepad the gamepad to query
* \param button a button on the gamepad * \param button a button on the gamepad
* \returns the sfSymbolsName or NULL if the name can't be found * \returns the sfSymbolsName or NULL if the name can't be found
@ -1459,6 +1471,8 @@ extern SDL_DECLSPEC const char* SDLCALL SDL_GetGamepadAppleSFSymbolsNameForButto
/** /**
* Return the sfSymbolsName for a given axis on a gamepad on Apple platforms. * Return the sfSymbolsName for a given axis on a gamepad on Apple platforms.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param gamepad the gamepad to query * \param gamepad the gamepad to query
* \param axis an axis on the gamepad * \param axis an axis on the gamepad
* \returns the sfSymbolsName or NULL if the name can't be found * \returns the sfSymbolsName or NULL if the name can't be found

View file

@ -948,6 +948,8 @@ extern SDL_DECLSPEC SDL_HapticID *SDLCALL SDL_GetHaptics(int *count);
* *
* This can be called before any haptic devices are opened. * This can be called before any haptic devices are opened.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param instance_id the haptic device instance ID * \param instance_id the haptic device instance ID
* \returns the name of the selected haptic device. If no name can be found, * \returns the name of the selected haptic device. If no name can be found,
* this function returns NULL; call SDL_GetError() for more * this function returns NULL; call SDL_GetError() for more
@ -1011,6 +1013,8 @@ extern SDL_DECLSPEC SDL_HapticID SDLCALL SDL_GetHapticInstanceID(SDL_Haptic *hap
/** /**
* Get the implementation dependent name of a haptic device. * Get the implementation dependent name of a haptic device.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param haptic the SDL_Haptic obtained from SDL_OpenJoystick() * \param haptic the SDL_Haptic obtained from SDL_OpenJoystick()
* \returns the name of the selected haptic device. If no name can be found, * \returns the name of the selected haptic device. If no name can be found,
* this function returns NULL; call SDL_GetError() for more * this function returns NULL; call SDL_GetError() for more

View file

@ -3806,6 +3806,8 @@ extern SDL_DECLSPEC void SDLCALL SDL_ResetHints(void);
/** /**
* Get the value of a hint. * Get the value of a hint.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param name the hint to query * \param name the hint to query
* \returns the string value of a hint or NULL if the hint isn't set. * \returns the string value of a hint or NULL if the hint isn't set.
* *

View file

@ -231,6 +231,8 @@ extern SDL_DECLSPEC SDL_JoystickID *SDLCALL SDL_GetJoysticks(int *count);
* *
* This can be called before any joysticks are opened. * This can be called before any joysticks are opened.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param instance_id the joystick instance ID * \param instance_id the joystick instance ID
* \returns the name of the selected joystick. If no name can be found, this * \returns the name of the selected joystick. If no name can be found, this
* function returns NULL; call SDL_GetError() for more information. * function returns NULL; call SDL_GetError() for more information.
@ -247,6 +249,8 @@ extern SDL_DECLSPEC const char *SDLCALL SDL_GetJoystickInstanceName(SDL_Joystick
* *
* This can be called before any joysticks are opened. * This can be called before any joysticks are opened.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param instance_id the joystick instance ID * \param instance_id the joystick instance ID
* \returns the path of the selected joystick. If no path can be found, this * \returns the path of the selected joystick. If no path can be found, this
* function returns NULL; call SDL_GetError() for more information. * function returns NULL; call SDL_GetError() for more information.
@ -669,6 +673,8 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetJoystickProperties(SDL_Joyst
/** /**
* Get the implementation dependent name of a joystick. * Get the implementation dependent name of a joystick.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param joystick the SDL_Joystick obtained from SDL_OpenJoystick() * \param joystick the SDL_Joystick obtained from SDL_OpenJoystick()
* \returns the name of the selected joystick. If no name can be found, this * \returns the name of the selected joystick. If no name can be found, this
* function returns NULL; call SDL_GetError() for more information. * function returns NULL; call SDL_GetError() for more information.
@ -682,6 +688,8 @@ extern SDL_DECLSPEC const char *SDLCALL SDL_GetJoystickName(SDL_Joystick *joysti
/** /**
* Get the implementation dependent path of a joystick. * Get the implementation dependent path of a joystick.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param joystick the SDL_Joystick obtained from SDL_OpenJoystick() * \param joystick the SDL_Joystick obtained from SDL_OpenJoystick()
* \returns the path of the selected joystick. If no path can be found, this * \returns the path of the selected joystick. If no path can be found, this
* function returns NULL; call SDL_GetError() for more information. * function returns NULL; call SDL_GetError() for more information.
@ -799,6 +807,8 @@ extern SDL_DECLSPEC Uint16 SDLCALL SDL_GetJoystickFirmwareVersion(SDL_Joystick *
* *
* Returns the serial number of the joystick, or NULL if it is not available. * Returns the serial number of the joystick, or NULL if it is not available.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param joystick the SDL_Joystick obtained from SDL_OpenJoystick() * \param joystick the SDL_Joystick obtained from SDL_OpenJoystick()
* \returns the serial number of the selected joystick, or NULL if * \returns the serial number of the selected joystick, or NULL if
* unavailable. * unavailable.

View file

@ -109,6 +109,8 @@ extern SDL_DECLSPEC SDL_KeyboardID *SDLCALL SDL_GetKeyboards(int *count);
* *
* This function returns "" if the keyboard doesn't have a name. * This function returns "" if the keyboard doesn't have a name.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param instance_id the keyboard instance ID * \param instance_id the keyboard instance ID
* \returns the name of the selected keyboard, or NULL on failure; call * \returns the name of the selected keyboard, or NULL on failure; call
* SDL_GetError() for more information. * SDL_GetError() for more information.
@ -255,6 +257,8 @@ extern SDL_DECLSPEC SDL_Scancode SDLCALL SDL_GetScancodeFromKey(SDL_Keycode key)
* *
* See SDL_Scancode for details. * See SDL_Scancode for details.
* *
* The returned string follows the SDL_GetStringRule.
*
* **Warning**: The returned name is by design not stable across platforms, * **Warning**: The returned name is by design not stable across platforms,
* e.g. the name for `SDL_SCANCODE_LGUI` is "Left GUI" under Linux but "Left * e.g. the name for `SDL_SCANCODE_LGUI` is "Left GUI" under Linux but "Left
* Windows" under Microsoft Windows, and some scancodes like * Windows" under Microsoft Windows, and some scancodes like
@ -295,6 +299,8 @@ extern SDL_DECLSPEC SDL_Scancode SDLCALL SDL_GetScancodeFromName(const char *nam
* *
* See SDL_Scancode and SDL_Keycode for details. * See SDL_Scancode and SDL_Keycode for details.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param key the desired SDL_Keycode to query * \param key the desired SDL_Keycode to query
* \returns a pointer to a UTF-8 string that stays valid at least until the * \returns a pointer to a UTF-8 string that stays valid at least until the
* next call to this function. If you need it around any longer, you * next call to this function. If you need it around any longer, you

View file

@ -152,6 +152,8 @@ extern SDL_DECLSPEC SDL_MouseID *SDLCALL SDL_GetMice(int *count);
* *
* This function returns "" if the mouse doesn't have a name. * This function returns "" if the mouse doesn't have a name.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param instance_id the mouse instance ID * \param instance_id the mouse instance ID
* \returns the name of the selected mouse, or NULL on failure; call * \returns the name of the selected mouse, or NULL on failure; call
* SDL_GetError() for more information. * SDL_GetError() for more information.

View file

@ -224,6 +224,8 @@ extern SDL_DECLSPEC SDL_bool SDLCALL SDL_PenConnected(SDL_PenID instance_id);
/** /**
* Retrieves a human-readable description for a SDL_PenID. * Retrieves a human-readable description for a SDL_PenID.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param instance_id The pen to query. * \param instance_id The pen to query.
* \returns A string that contains the name of the pen, intended for human * \returns A string that contains the name of the pen, intended for human
* consumption. The string might or might not be localised, depending * consumption. The string might or might not be localised, depending

View file

@ -758,6 +758,8 @@ typedef struct SDL_PixelFormat
/** /**
* Get the human readable name of a pixel format. * Get the human readable name of a pixel format.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param format the pixel format to query * \param format the pixel format to query
* \returns the human readable name of the specified pixel format or * \returns the human readable name of the specified pixel format or
* `SDL_PIXELFORMAT_UNKNOWN` if the format isn't recognized. * `SDL_PIXELFORMAT_UNKNOWN` if the format isn't recognized.

View file

@ -48,12 +48,14 @@ extern "C" {
* - "iOS" * - "iOS"
* - "Android" * - "Android"
* *
* The returned string follows the SDL_GetStringRule.
*
* \returns the name of the platform. If the correct platform name is not * \returns the name of the platform. If the correct platform name is not
* available, returns a string beginning with the text "Unknown". * available, returns a string beginning with the text "Unknown".
* *
* \since This function is available since SDL 3.0.0. * \since This function is available since SDL 3.0.0.
*/ */
extern SDL_DECLSPEC const char * SDLCALL SDL_GetPlatform (void); extern SDL_DECLSPEC const char * SDLCALL SDL_GetPlatform(void);
/* Ends C function definitions when using C++ */ /* Ends C function definitions when using C++ */
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -360,6 +360,8 @@ extern SDL_DECLSPEC void *SDLCALL SDL_GetProperty(SDL_PropertiesID props, const
/** /**
* Get a string property on a set of properties. * Get a string property on a set of properties.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param props the properties to query * \param props the properties to query
* \param name the name of the property to query * \param name the name of the property to query
* \param default_value the default value of the property * \param default_value the default value of the property

View file

@ -166,8 +166,7 @@ extern SDL_DECLSPEC int SDLCALL SDL_GetNumRenderDrivers(void);
* "direct3d12" or "metal". These never have Unicode characters, and are not * "direct3d12" or "metal". These never have Unicode characters, and are not
* meant to be proper names. * meant to be proper names.
* *
* The returned value points to a static, read-only string; do not modify or * The returned string follows the SDL_GetStringRule.
* free it!
* *
* \param index the index of the rendering driver; the value ranges from 0 to * \param index the index of the rendering driver; the value ranges from 0 to
* SDL_GetNumRenderDrivers() - 1 * SDL_GetNumRenderDrivers() - 1

View file

@ -158,6 +158,8 @@ extern SDL_DECLSPEC SDL_SensorID *SDLCALL SDL_GetSensors(int *count);
/** /**
* Get the implementation dependent name of a sensor. * Get the implementation dependent name of a sensor.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param instance_id the sensor instance ID * \param instance_id the sensor instance ID
* \returns the sensor name, or NULL if `instance_id` is not valid * \returns the sensor name, or NULL if `instance_id` is not valid
* *
@ -224,6 +226,8 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetSensorProperties(SDL_Sensor
/** /**
* Get the implementation dependent name of a sensor. * Get the implementation dependent name of a sensor.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param sensor The SDL_Sensor object * \param sensor The SDL_Sensor object
* \returns the sensor name, or NULL if `sensor` is NULL. * \returns the sensor name, or NULL if `sensor` is NULL.
* *

View file

@ -385,6 +385,8 @@ extern SDL_DECLSPEC void SDLCALL SDL_AndroidBackButton(void);
* Your internal storage path is typically: * Your internal storage path is typically:
* `/data/data/your.app.package/files`. * `/data/data/your.app.package/files`.
* *
* The returned string follows the SDL_GetStringRule.
*
* \returns the path used for internal storage or NULL on failure; call * \returns the path used for internal storage or NULL on failure; call
* SDL_GetError() for more information. * SDL_GetError() for more information.
* *
@ -392,7 +394,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_AndroidBackButton(void);
* *
* \sa SDL_AndroidGetExternalStorageState * \sa SDL_AndroidGetExternalStorageState
*/ */
extern SDL_DECLSPEC const char * SDLCALL SDL_AndroidGetInternalStoragePath(void); extern SDL_DECLSPEC const char *SDLCALL SDL_AndroidGetInternalStoragePath(void);
/** /**
* Get the current state of external storage. * Get the current state of external storage.
@ -422,6 +424,8 @@ extern SDL_DECLSPEC int SDLCALL SDL_AndroidGetExternalStorageState(Uint32 *state
* Your external storage path is typically: * Your external storage path is typically:
* `/storage/sdcard0/Android/data/your.app.package/files`. * `/storage/sdcard0/Android/data/your.app.package/files`.
* *
* The returned string follows the SDL_GetStringRule.
*
* \returns the path used for external storage for this application on success * \returns the path used for external storage for this application on success
* or NULL on failure; call SDL_GetError() for more information. * or NULL on failure; call SDL_GetError() for more information.
* *
@ -576,6 +580,8 @@ typedef enum SDL_WinRT_DeviceFamily
* *
* https://msdn.microsoft.com/en-us/library/windows/apps/hh464917.aspx * https://msdn.microsoft.com/en-us/library/windows/apps/hh464917.aspx
* *
* The returned string follows the SDL_GetStringRule.
*
* \param pathType the type of path to retrieve, one of SDL_WinRT_Path * \param pathType the type of path to retrieve, one of SDL_WinRT_Path
* \returns a UTF-8 string (8-bit, multi-byte) containing the path, or NULL if * \returns a UTF-8 string (8-bit, multi-byte) containing the path, or NULL if
* the path is not available for any reason; call SDL_GetError() for * the path is not available for any reason; call SDL_GetError() for

View file

@ -331,8 +331,7 @@ extern SDL_DECLSPEC SDL_Thread *SDLCALL SDL_CreateThreadWithPropertiesRuntime(SD
/** /**
* Get the thread name as it was specified in SDL_CreateThread(). * Get the thread name as it was specified in SDL_CreateThread().
* *
* This is internal memory, not to be freed by the caller, and remains valid * The returned string follows the SDL_GetStringRule.
* until the specified thread is cleaned up by SDL_WaitThread().
* *
* \param thread the thread to query * \param thread the thread to query
* \returns a pointer to a UTF-8 string that names the specified thread, or * \returns a pointer to a UTF-8 string that names the specified thread, or

View file

@ -96,7 +96,7 @@ extern SDL_DECLSPEC SDL_TouchID *SDLCALL SDL_GetTouchDevices(int *count);
/** /**
* Get the touch device name as reported from the driver. * Get the touch device name as reported from the driver.
* *
* You do not own the returned string, do not modify or free it. * The returned string follows the SDL_GetStringRule.
* *
* \param touchID the touch device instance ID. * \param touchID the touch device instance ID.
* \returns touch device name, or NULL on error; call SDL_GetError() for more * \returns touch device name, or NULL on error; call SDL_GetError() for more
@ -104,7 +104,7 @@ extern SDL_DECLSPEC SDL_TouchID *SDLCALL SDL_GetTouchDevices(int *count);
* *
* \since This function is available since SDL 3.0.0. * \since This function is available since SDL 3.0.0.
*/ */
extern SDL_DECLSPEC const char* SDLCALL SDL_GetTouchDeviceName(SDL_TouchID touchID); extern SDL_DECLSPEC const char *SDLCALL SDL_GetTouchDeviceName(SDL_TouchID touchID);
/** /**
* Get the type of the given touch device. * Get the type of the given touch device.

View file

@ -143,6 +143,8 @@ extern SDL_DECLSPEC int SDLCALL SDL_GetVersion(void);
* You shouldn't use this function for anything but logging it for debugging * You shouldn't use this function for anything but logging it for debugging
* purposes. The string is not intended to be reliable in any way. * purposes. The string is not intended to be reliable in any way.
* *
* The returned string follows the SDL_GetStringRule.
*
* \returns an arbitrary string, uniquely identifying the exact revision of * \returns an arbitrary string, uniquely identifying the exact revision of
* the SDL library in use. * the SDL library in use.
* *

View file

@ -338,6 +338,12 @@ extern SDL_DECLSPEC int SDLCALL SDL_GetNumVideoDrivers(void);
* The video drivers are presented in the order in which they are normally * The video drivers are presented in the order in which they are normally
* checked during initialization. * checked during initialization.
* *
* The names of drivers are all simple, low-ASCII identifiers, like "cocoa",
* "x11" or "windows". These never have Unicode characters, and are not
* meant to be proper names.
*
* The returned string follows the SDL_GetStringRule.
*
* \param index the index of a video driver * \param index the index of a video driver
* \returns the name of the video driver with the given **index**. * \returns the name of the video driver with the given **index**.
* *
@ -350,6 +356,12 @@ extern SDL_DECLSPEC const char *SDLCALL SDL_GetVideoDriver(int index);
/** /**
* Get the name of the currently initialized video driver. * Get the name of the currently initialized video driver.
* *
* The names of drivers are all simple, low-ASCII identifiers, like "cocoa",
* "x11" or "windows". These never have Unicode characters, and are not
* meant to be proper names.
*
* The returned string follows the SDL_GetStringRule.
*
* \returns the name of the current video driver or NULL if no driver has been * \returns the name of the current video driver or NULL if no driver has been
* initialized. * initialized.
* *
@ -438,6 +450,8 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetDisplayProperties(SDL_Displa
/** /**
* Get the name of a display in UTF-8 encoding. * Get the name of a display in UTF-8 encoding.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param displayID the instance ID of the display to query * \param displayID the instance ID of the display to query
* \returns the name of a display or NULL on failure; call SDL_GetError() for * \returns the name of a display or NULL on failure; call SDL_GetError() for
* more information. * more information.
@ -1260,6 +1274,8 @@ extern SDL_DECLSPEC int SDLCALL SDL_SetWindowTitle(SDL_Window *window, const cha
/** /**
* Get the title of a window. * Get the title of a window.
* *
* The returned string follows the SDL_GetStringRule.
*
* \param window the window to query * \param window the window to query
* \returns the title of the window in UTF-8 format or "" if there is no * \returns the title of the window in UTF-8 format or "" if there is no
* title. * title.

View file

@ -573,10 +573,11 @@ int SDL_GetVersion(void)
/* Get the library source revision */ /* Get the library source revision */
const char *SDL_GetRevision(void) const char *SDL_GetRevision(void)
{ {
return SDL_REVISION; return SDL_REVISION; // a string literal, no need to SDL_FreeLater it.
} }
/* Get the name of the platform */ // Get the name of the platform
// (a string literal, no need to SDL_FreeLater it.)
const char *SDL_GetPlatform(void) const char *SDL_GetPlatform(void)
{ {
#if defined(SDL_PLATFORM_AIX) #if defined(SDL_PLATFORM_AIX)

View file

@ -74,9 +74,7 @@ SDL_bool SDL_SetHintWithPriority(const char *name, const char *value, SDL_HintPr
entry->callback(entry->userdata, name, old_value, value); entry->callback(entry->userdata, name, old_value, value);
entry = next; entry = next;
} }
if (old_value) { SDL_FreeLater(old_value);
SDL_free(old_value);
}
} }
hint->priority = priority; hint->priority = priority;
return SDL_TRUE; return SDL_TRUE;
@ -120,7 +118,7 @@ SDL_bool SDL_ResetHint(const char *name)
entry = next; entry = next;
} }
} }
SDL_free(hint->value); SDL_FreeLater(hint->value);
hint->value = NULL; hint->value = NULL;
hint->priority = SDL_HINT_DEFAULT; hint->priority = SDL_HINT_DEFAULT;
return SDL_TRUE; return SDL_TRUE;
@ -147,7 +145,7 @@ void SDL_ResetHints(void)
entry = next; entry = next;
} }
} }
SDL_free(hint->value); SDL_FreeLater(hint->value);
hint->value = NULL; hint->value = NULL;
hint->priority = SDL_HINT_DEFAULT; hint->priority = SDL_HINT_DEFAULT;
} }
@ -305,7 +303,7 @@ void SDL_ClearHints(void)
SDL_hints = hint->next; SDL_hints = hint->next;
SDL_free(hint->name); SDL_free(hint->name);
SDL_free(hint->value); SDL_FreeLater(hint->value);
for (entry = hint->callbacks; entry;) { for (entry = hint->callbacks; entry;) {
SDL_HintWatch *freeable = entry; SDL_HintWatch *freeable = entry;
entry = entry->next; entry = entry->next;

View file

@ -293,6 +293,13 @@ extern int SDLCALL SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeout
extern int SDLCALL SDL_WaitConditionTimeoutNS(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS); extern int SDLCALL SDL_WaitConditionTimeoutNS(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS);
extern SDL_bool SDLCALL SDL_WaitEventTimeoutNS(SDL_Event *event, Sint64 timeoutNS); extern SDL_bool SDLCALL SDL_WaitEventTimeoutNS(SDL_Event *event, Sint64 timeoutNS);
/* Queue `memory` to be passed to SDL_free once the event queue is emptied.
this manages the list of pointers to SDL_AllocateEventMemory, but you
can use it to queue pointers from other subsystems that can die at any
moment but definitely need to live long enough for the app to copy them
if they happened to query them in their last moments. */
extern void *SDL_FreeLater(void *memory);
/* Ends C function definitions when using C++ */ /* Ends C function definitions when using C++ */
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -65,14 +65,12 @@ static void SDL_FreePropertyWithCleanup(const void *key, const void *value, void
} }
break; break;
case SDL_PROPERTY_TYPE_STRING: case SDL_PROPERTY_TYPE_STRING:
SDL_free(property->value.string_value); SDL_FreeLater(property->value.string_value); // this pointer might be given to the app by SDL_GetStringProperty.
break; break;
default: default:
break; break;
} }
if (property->string_storage) { SDL_FreeLater(property->string_storage); // this pointer might be given to the app by SDL_GetStringProperty.
SDL_free(property->string_storage);
}
} }
SDL_free((void *)key); SDL_free((void *)key);
SDL_free((void *)value); SDL_free((void *)value);
@ -552,12 +550,6 @@ const char *SDL_GetStringProperty(SDL_PropertiesID props, const char *name, cons
return value; return value;
} }
/* Note that taking the lock here only guarantees that we won't read the
* hashtable while it's being modified. The value itself can easily be
* freed from another thread after it is returned here.
*
* FIXME: Should we SDL_strdup() the return value to avoid this?
*/
SDL_LockMutex(properties->lock); SDL_LockMutex(properties->lock);
{ {
SDL_Property *property = NULL; SDL_Property *property = NULL;

View file

@ -131,6 +131,7 @@ int SDL_GetNumAudioDrivers(void)
return num_drivers; return num_drivers;
} }
// this returns string literals, so there's no need to use SDL_FreeLater.
const char *SDL_GetAudioDriver(int index) const char *SDL_GetAudioDriver(int index)
{ {
if (index >= 0 && index < SDL_GetNumAudioDrivers()) { if (index >= 0 && index < SDL_GetNumAudioDrivers()) {
@ -139,6 +140,7 @@ const char *SDL_GetAudioDriver(int index)
return NULL; return NULL;
} }
// this returns string literals, so there's no need to use SDL_FreeLater.
const char *SDL_GetCurrentAudioDriver(void) const char *SDL_GetCurrentAudioDriver(void)
{ {
return current_audio.name; return current_audio.name;
@ -521,7 +523,7 @@ static void DestroyPhysicalAudioDevice(SDL_AudioDevice *device)
SDL_DestroyMutex(device->lock); SDL_DestroyMutex(device->lock);
SDL_DestroyCondition(device->close_cond); SDL_DestroyCondition(device->close_cond);
SDL_free(device->work_buffer); SDL_free(device->work_buffer);
SDL_free(device->name); SDL_FreeLater(device->name); // this pointer is handed to the app during SDL_GetAudioDeviceName
SDL_free(device); SDL_free(device);
} }
@ -1402,12 +1404,12 @@ SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByHandle(void *handle)
return SDL_FindPhysicalAudioDeviceByCallback(TestDeviceHandleCallback, handle); return SDL_FindPhysicalAudioDeviceByCallback(TestDeviceHandleCallback, handle);
} }
char *SDL_GetAudioDeviceName(SDL_AudioDeviceID devid) const char *SDL_GetAudioDeviceName(SDL_AudioDeviceID devid)
{ {
char *retval = NULL; const char *retval = NULL;
SDL_AudioDevice *device = ObtainPhysicalAudioDevice(devid); SDL_AudioDevice *device = ObtainPhysicalAudioDevice(devid);
if (device) { if (device) {
retval = SDL_strdup(device->name); retval = device->name;
} }
ReleaseAudioDevice(device); ReleaseAudioDevice(device);

View file

@ -63,6 +63,7 @@ int SDL_GetNumCameraDrivers(void)
return SDL_arraysize(bootstrap) - 1; return SDL_arraysize(bootstrap) - 1;
} }
// this returns string literals, so there's no need to use SDL_FreeLater.
const char *SDL_GetCameraDriver(int index) const char *SDL_GetCameraDriver(int index)
{ {
if (index >= 0 && index < SDL_GetNumCameraDrivers()) { if (index >= 0 && index < SDL_GetNumCameraDrivers()) {
@ -71,6 +72,7 @@ const char *SDL_GetCameraDriver(int index)
return NULL; return NULL;
} }
// this returns string literals, so there's no need to use SDL_FreeLater.
const char *SDL_GetCurrentCameraDriver(void) const char *SDL_GetCurrentCameraDriver(void)
{ {
return camera_driver.name; return camera_driver.name;

View file

@ -141,7 +141,7 @@ int SDL_AndroidGetExternalStorageState(Uint32 *state)
return SDL_Unsupported(); return SDL_Unsupported();
} }
SDL_DECLSPEC const char *SDLCALL SDL_AndroidGetInternalStoragePath(void); SDL_DECLSPEC const char *SDLCALL SDL_AndroidGetInternalStoragePath(void);
const char *SDL_AndroidGetInternalStoragePath() const char *SDL_AndroidGetInternalStoragePath(void)
{ {
SDL_Unsupported(); SDL_Unsupported();
return NULL; return NULL;

View file

@ -2417,6 +2417,7 @@ void SDL_AndroidBackButton(void)
(*env)->CallStaticVoidMethod(env, mActivityClass, midManualBackButton); (*env)->CallStaticVoidMethod(env, mActivityClass, midManualBackButton);
} }
// this caches a string until the process ends, so there's no need to use SDL_FreeLater.
const char *SDL_AndroidGetInternalStoragePath(void) const char *SDL_AndroidGetInternalStoragePath(void)
{ {
static char *s_AndroidInternalFilesPath = NULL; static char *s_AndroidInternalFilesPath = NULL;
@ -2516,6 +2517,7 @@ int SDL_AndroidGetExternalStorageState(Uint32 *state)
return 0; return 0;
} }
// this caches a string until the process ends, so there's no need to use SDL_FreeLater.
const char *SDL_AndroidGetExternalStoragePath(void) const char *SDL_AndroidGetExternalStoragePath(void)
{ {
static char *s_AndroidExternalFilesPath = NULL; static char *s_AndroidExternalFilesPath = NULL;

View file

@ -213,7 +213,7 @@ SDL_DYNAPI_PROC(SDL_AssertionHandler,SDL_GetAssertionHandler,(void **a),(a),retu
SDL_DYNAPI_PROC(const SDL_AssertData*,SDL_GetAssertionReport,(void),(),return) SDL_DYNAPI_PROC(const SDL_AssertData*,SDL_GetAssertionReport,(void),(),return)
SDL_DYNAPI_PROC(SDL_AudioDeviceID*,SDL_GetAudioCaptureDevices,(int *a),(a),return) SDL_DYNAPI_PROC(SDL_AudioDeviceID*,SDL_GetAudioCaptureDevices,(int *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioDeviceFormat,(SDL_AudioDeviceID a, SDL_AudioSpec *b, int *c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_GetAudioDeviceFormat,(SDL_AudioDeviceID a, SDL_AudioSpec *b, int *c),(a,b,c),return)
SDL_DYNAPI_PROC(char*,SDL_GetAudioDeviceName,(SDL_AudioDeviceID a),(a),return) SDL_DYNAPI_PROC(const char*,SDL_GetAudioDeviceName,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(const char*,SDL_GetAudioDriver,(int a),(a),return) SDL_DYNAPI_PROC(const char*,SDL_GetAudioDriver,(int a),(a),return)
SDL_DYNAPI_PROC(SDL_AudioDeviceID*,SDL_GetAudioOutputDevices,(int *a),(a),return) SDL_DYNAPI_PROC(SDL_AudioDeviceID*,SDL_GetAudioOutputDevices,(int *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamAvailable,(SDL_AudioStream *a),(a),return) SDL_DYNAPI_PROC(int,SDL_GetAudioStreamAvailable,(SDL_AudioStream *a),(a),return)

View file

@ -108,37 +108,40 @@ static SDL_Mutex *SDL_event_memory_lock;
static SDL_EventMemory *SDL_event_memory_head; static SDL_EventMemory *SDL_event_memory_head;
static SDL_EventMemory *SDL_event_memory_tail; static SDL_EventMemory *SDL_event_memory_tail;
void *SDL_AllocateEventMemory(size_t size) void *SDL_FreeLater(void *memory)
{ {
void *memory = SDL_malloc(size); if (memory == NULL) {
if (!memory) {
return NULL; return NULL;
} }
SDL_EventMemory *entry = (SDL_EventMemory *)SDL_malloc(sizeof(*entry));
if (!entry) {
return memory; // this is now a leak, but you probably have bigger problems if malloc failed. We could probably pool up and reuse entries, though.
}
SDL_LockMutex(SDL_event_memory_lock); SDL_LockMutex(SDL_event_memory_lock);
{ {
SDL_EventMemory *entry = (SDL_EventMemory *)SDL_malloc(sizeof(*entry)); entry->eventID = SDL_last_event_id;
if (entry) { entry->memory = memory;
entry->eventID = SDL_last_event_id; entry->next = NULL;
entry->memory = memory;
entry->next = NULL;
if (SDL_event_memory_tail) { if (SDL_event_memory_tail) {
SDL_event_memory_tail->next = entry; SDL_event_memory_tail->next = entry;
} else {
SDL_event_memory_head = entry;
}
SDL_event_memory_tail = entry;
} else { } else {
SDL_free(memory); SDL_event_memory_head = entry;
memory = NULL;
} }
SDL_event_memory_tail = entry;
} }
SDL_UnlockMutex(SDL_event_memory_lock); SDL_UnlockMutex(SDL_event_memory_lock);
return memory; return memory;
} }
void *SDL_AllocateEventMemory(size_t size)
{
return SDL_FreeLater(SDL_malloc(size));
}
static void SDL_FlushEventMemory(Uint32 eventID) static void SDL_FlushEventMemory(Uint32 eventID)
{ {
SDL_LockMutex(SDL_event_memory_lock); SDL_LockMutex(SDL_event_memory_lock);

View file

@ -742,7 +742,7 @@ void SDL_RemoveKeyboard(SDL_KeyboardID keyboardID, SDL_bool send_event)
return; return;
} }
SDL_free(SDL_keyboards[keyboard_index].name); SDL_FreeLater(SDL_keyboards[keyboard_index].name);
if (keyboard_index != SDL_keyboard_count - 1) { if (keyboard_index != SDL_keyboard_count - 1) {
SDL_memcpy(&SDL_keyboards[keyboard_index], &SDL_keyboards[keyboard_index + 1], (SDL_keyboard_count - keyboard_index - 1) * sizeof(SDL_keyboards[keyboard_index])); SDL_memcpy(&SDL_keyboards[keyboard_index], &SDL_keyboards[keyboard_index + 1], (SDL_keyboard_count - keyboard_index - 1) * sizeof(SDL_keyboards[keyboard_index]));
@ -1334,6 +1334,7 @@ SDL_Scancode SDL_GetScancodeFromKey(SDL_Keycode key)
return SDL_SCANCODE_UNKNOWN; return SDL_SCANCODE_UNKNOWN;
} }
// these are static memory, so we don't use SDL_FreeLater on them.
const char *SDL_GetScancodeName(SDL_Scancode scancode) const char *SDL_GetScancodeName(SDL_Scancode scancode)
{ {
const char *name; const char *name;
@ -1374,7 +1375,7 @@ SDL_Scancode SDL_GetScancodeFromName(const char *name)
const char *SDL_GetKeyName(SDL_Keycode key) const char *SDL_GetKeyName(SDL_Keycode key)
{ {
static char name[8]; char name[8];
char *end; char *end;
if (key & SDLK_SCANCODE_MASK) { if (key & SDLK_SCANCODE_MASK) {
@ -1405,7 +1406,7 @@ const char *SDL_GetKeyName(SDL_Keycode key)
end = SDL_UCS4ToUTF8((Uint32)key, name); end = SDL_UCS4ToUTF8((Uint32)key, name);
*end = '\0'; *end = '\0';
return name; return SDL_FreeLater(SDL_strdup(name));
} }
} }

View file

@ -288,7 +288,7 @@ void SDL_RemoveMouse(SDL_MouseID mouseID, SDL_bool send_event)
return; return;
} }
SDL_free(SDL_mice[mouse_index].name); SDL_FreeLater(SDL_mice[mouse_index].name); // SDL_GetMouseInstanceName returns this pointer.
if (mouse_index != SDL_mouse_count - 1) { if (mouse_index != SDL_mouse_count - 1) {
SDL_memcpy(&SDL_mice[mouse_index], &SDL_mice[mouse_index + 1], (SDL_mouse_count - mouse_index - 1) * sizeof(SDL_mice[mouse_index])); SDL_memcpy(&SDL_mice[mouse_index], &SDL_mice[mouse_index + 1], (SDL_mouse_count - mouse_index - 1) * sizeof(SDL_mice[mouse_index]));

View file

@ -493,7 +493,7 @@ void SDL_DelTouch(SDL_TouchID id)
SDL_free(touch->fingers[i]); SDL_free(touch->fingers[i]);
} }
SDL_free(touch->fingers); SDL_free(touch->fingers);
SDL_free(touch->name); SDL_FreeLater(touch->name); // this pointer might be given to the app by SDL_GetTouchDeviceName.
SDL_free(touch); SDL_free(touch);
SDL_num_touch--; SDL_num_touch--;

View file

@ -93,6 +93,7 @@ static const wchar_t *SDL_WinRTGetFSPathUNICODE(SDL_WinRT_Path pathType)
return NULL; return NULL;
} }
// this caches a string until the process ends, so there's no need to use SDL_FreeLater.
extern "C" const char *SDL_WinRTGetFSPath(SDL_WinRT_Path pathType) extern "C" const char *SDL_WinRTGetFSPath(SDL_WinRT_Path pathType)
{ {
typedef unordered_map<SDL_WinRT_Path, string> UTF8PathMap; typedef unordered_map<SDL_WinRT_Path, string> UTF8PathMap;

View file

@ -100,7 +100,7 @@ const char *SDL_GetHapticInstanceName(SDL_HapticID instance_id)
if (SDL_GetHapticIndex(instance_id, &device_index)) { if (SDL_GetHapticIndex(instance_id, &device_index)) {
name = SDL_SYS_HapticName(device_index); name = SDL_SYS_HapticName(device_index);
} }
return name; return name ? SDL_FreeLater(SDL_strdup(name)) : NULL;
} }
SDL_Haptic *SDL_OpenHaptic(SDL_HapticID instance_id) SDL_Haptic *SDL_OpenHaptic(SDL_HapticID instance_id)
@ -338,7 +338,7 @@ void SDL_CloseHaptic(SDL_Haptic *haptic)
} }
/* Free the data associated with this device */ /* Free the data associated with this device */
SDL_free(haptic->name); SDL_FreeLater(haptic->name); // this pointer is handed to the app in SDL_GetHapticName()
SDL_free(haptic); SDL_free(haptic);
} }

View file

@ -1594,7 +1594,7 @@ static GamepadMapping_t *SDL_PrivateAddMappingForGUID(SDL_JoystickGUID jGUID, co
/* Only overwrite the mapping if the priority is the same or higher. */ /* Only overwrite the mapping if the priority is the same or higher. */
if (pGamepadMapping->priority <= priority) { if (pGamepadMapping->priority <= priority) {
/* Update existing mapping */ /* Update existing mapping */
SDL_free(pGamepadMapping->name); SDL_FreeLater(pGamepadMapping->name); // this is returned in SDL_GetGamepadName.
pGamepadMapping->name = pchName; pGamepadMapping->name = pchName;
SDL_free(pGamepadMapping->mapping); SDL_free(pGamepadMapping->mapping);
pGamepadMapping->mapping = pchMapping; pGamepadMapping->mapping = pchMapping;
@ -3413,7 +3413,8 @@ const char * SDL_GetGamepadSerial(SDL_Gamepad *gamepad)
if (!joystick) { if (!joystick) {
return NULL; return NULL;
} }
return SDL_GetJoystickSerial(joystick); return SDL_GetJoystickSerial(joystick); // this already returns a SDL_FreeLater pointer.
} }
Uint64 SDL_GetGamepadSteamHandle(SDL_Gamepad *gamepad) Uint64 SDL_GetGamepadSteamHandle(SDL_Gamepad *gamepad)
@ -3680,7 +3681,7 @@ void SDL_QuitGamepadMappings(void)
while (s_pSupportedGamepads) { while (s_pSupportedGamepads) {
pGamepadMap = s_pSupportedGamepads; pGamepadMap = s_pSupportedGamepads;
s_pSupportedGamepads = s_pSupportedGamepads->next; s_pSupportedGamepads = s_pSupportedGamepads->next;
SDL_free(pGamepadMap->name); SDL_FreeLater(pGamepadMap->name); // this is returned in SDL_GetGamepadName.
SDL_free(pGamepadMap->mapping); SDL_free(pGamepadMap->mapping);
SDL_free(pGamepadMap); SDL_free(pGamepadMap);
} }
@ -3826,8 +3827,8 @@ void SDL_GamepadHandleDelayedGuideButton(SDL_Joystick *joystick)
const char *SDL_GetGamepadAppleSFSymbolsNameForButton(SDL_Gamepad *gamepad, SDL_GamepadButton button) const char *SDL_GetGamepadAppleSFSymbolsNameForButton(SDL_Gamepad *gamepad, SDL_GamepadButton button)
{ {
#ifdef SDL_JOYSTICK_MFI #ifdef SDL_JOYSTICK_MFI
const char *IOS_GetAppleSFSymbolsNameForButton(SDL_Gamepad *gamepad, SDL_GamepadButton button); char *IOS_GetAppleSFSymbolsNameForButton(SDL_Gamepad *gamepad, SDL_GamepadButton button);
const char *retval; char *retval;
SDL_LockJoysticks(); SDL_LockJoysticks();
{ {
@ -3837,9 +3838,11 @@ const char *SDL_GetGamepadAppleSFSymbolsNameForButton(SDL_Gamepad *gamepad, SDL_
} }
SDL_UnlockJoysticks(); SDL_UnlockJoysticks();
// retval was malloc'd by IOS_GetAppleSFSymbolsNameForButton
if (retval && *retval) { if (retval && *retval) {
return retval; return SDL_FreeLater(retval);
} }
SDL_free(retval);
#endif #endif
return NULL; return NULL;
} }
@ -3847,8 +3850,8 @@ const char *SDL_GetGamepadAppleSFSymbolsNameForButton(SDL_Gamepad *gamepad, SDL_
const char *SDL_GetGamepadAppleSFSymbolsNameForAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis) const char *SDL_GetGamepadAppleSFSymbolsNameForAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis)
{ {
#ifdef SDL_JOYSTICK_MFI #ifdef SDL_JOYSTICK_MFI
const char *IOS_GetAppleSFSymbolsNameForAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis); char *IOS_GetAppleSFSymbolsNameForAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis);
const char *retval; char *retval;
SDL_LockJoysticks(); SDL_LockJoysticks();
{ {
@ -3858,9 +3861,11 @@ const char *SDL_GetGamepadAppleSFSymbolsNameForAxis(SDL_Gamepad *gamepad, SDL_Ga
} }
SDL_UnlockJoysticks(); SDL_UnlockJoysticks();
// retval was malloc'd by IOS_GetAppleSFSymbolsNameForAxis
if (retval && *retval) { if (retval && *retval) {
return retval; return SDL_FreeLater(retval);
} }
SDL_free(retval);
#endif #endif
return NULL; return NULL;
} }

View file

@ -785,8 +785,7 @@ const char *SDL_GetJoystickInstanceName(SDL_JoystickID instance_id)
} }
SDL_UnlockJoysticks(); SDL_UnlockJoysticks();
/* FIXME: Really we should reference count this name so it doesn't go away after unlock */ return name ? SDL_FreeLater(SDL_strdup(name)) : NULL;
return name;
} }
/* /*
@ -804,11 +803,10 @@ const char *SDL_GetJoystickInstancePath(SDL_JoystickID instance_id)
} }
SDL_UnlockJoysticks(); SDL_UnlockJoysticks();
/* FIXME: Really we should reference count this path so it doesn't go away after unlock */
if (!path) { if (!path) {
SDL_Unsupported(); SDL_Unsupported();
} }
return path; return path ? SDL_FreeLater(SDL_strdup(path)) : NULL;
} }
/* /*
@ -1663,7 +1661,6 @@ const char *SDL_GetJoystickName(SDL_Joystick *joystick)
} }
SDL_UnlockJoysticks(); SDL_UnlockJoysticks();
/* FIXME: Really we should reference count this name so it doesn't go away after unlock */
return retval; return retval;
} }
@ -1888,9 +1885,9 @@ void SDL_CloseJoystick(SDL_Joystick *joystick)
} }
/* Free the data associated with this joystick */ /* Free the data associated with this joystick */
SDL_free(joystick->name); SDL_FreeLater(joystick->name); // SDL_GetJoystickName returns this pointer.
SDL_free(joystick->path); SDL_FreeLater(joystick->path); // SDL_GetJoystickPath returns this pointer.
SDL_free(joystick->serial); SDL_FreeLater(joystick->serial); // SDL_GetJoystickSerial returns this pointer.
SDL_free(joystick->axes); SDL_free(joystick->axes);
SDL_free(joystick->balls); SDL_free(joystick->balls);
SDL_free(joystick->hats); SDL_free(joystick->hats);

View file

@ -1916,11 +1916,11 @@ static GCControllerDirectionPad *GetDirectionalPadForController(GCController *co
} }
#endif /* SDL_JOYSTICK_MFI && ENABLE_PHYSICAL_INPUT_PROFILE */ #endif /* SDL_JOYSTICK_MFI && ENABLE_PHYSICAL_INPUT_PROFILE */
static char elementName[256]; char *IOS_GetAppleSFSymbolsNameForButton(SDL_Gamepad *gamepad, SDL_GamepadButton button)
const char *IOS_GetAppleSFSymbolsNameForButton(SDL_Gamepad *gamepad, SDL_GamepadButton button)
{ {
char elementName[256];
elementName[0] = '\0'; elementName[0] = '\0';
#if defined(SDL_JOYSTICK_MFI) && defined(ENABLE_PHYSICAL_INPUT_PROFILE) #if defined(SDL_JOYSTICK_MFI) && defined(ENABLE_PHYSICAL_INPUT_PROFILE)
if (gamepad && SDL_GetGamepadJoystick(gamepad)->driver == &SDL_IOS_JoystickDriver) { if (gamepad && SDL_GetGamepadJoystick(gamepad)->driver == &SDL_IOS_JoystickDriver) {
if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) { if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
@ -2030,12 +2030,15 @@ const char *IOS_GetAppleSFSymbolsNameForButton(SDL_Gamepad *gamepad, SDL_Gamepad
} }
} }
#endif #endif
return elementName;
return SDL_strdup(elementName);
} }
const char *IOS_GetAppleSFSymbolsNameForAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis) char *IOS_GetAppleSFSymbolsNameForAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis)
{ {
char elementName[256];
elementName[0] = '\0'; elementName[0] = '\0';
#if defined(SDL_JOYSTICK_MFI) && defined(ENABLE_PHYSICAL_INPUT_PROFILE) #if defined(SDL_JOYSTICK_MFI) && defined(ENABLE_PHYSICAL_INPUT_PROFILE)
if (gamepad && SDL_GetGamepadJoystick(gamepad)->driver == &SDL_IOS_JoystickDriver) { if (gamepad && SDL_GetGamepadJoystick(gamepad)->driver == &SDL_IOS_JoystickDriver) {
if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) { if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
@ -2068,7 +2071,7 @@ const char *IOS_GetAppleSFSymbolsNameForAxis(SDL_Gamepad *gamepad, SDL_GamepadAx
} }
} }
#endif #endif
return *elementName ? elementName : NULL; return *elementName ? SDL_strdup(elementName) : NULL;
} }
SDL_JoystickDriver SDL_IOS_JoystickDriver = { SDL_JoystickDriver SDL_IOS_JoystickDriver = {

View file

@ -780,6 +780,7 @@ int SDL_GetNumRenderDrivers(void)
#endif #endif
} }
// this returns string literals, so there's no need to use SDL_FreeLater.
const char *SDL_GetRenderDriver(int index) const char *SDL_GetRenderDriver(int index)
{ {
#ifndef SDL_RENDER_DISABLED #ifndef SDL_RENDER_DISABLED

View file

@ -250,8 +250,7 @@ const char *SDL_GetSensorInstanceName(SDL_SensorID instance_id)
} }
SDL_UnlockSensors(); SDL_UnlockSensors();
/* FIXME: Really we should reference count this name so it doesn't go away after unlock */ return name ? SDL_FreeLater(SDL_strdup(name)) : NULL;
return name;
} }
SDL_SensorType SDL_GetSensorInstanceType(SDL_SensorID instance_id) SDL_SensorType SDL_GetSensorInstanceType(SDL_SensorID instance_id)
@ -527,7 +526,7 @@ void SDL_CloseSensor(SDL_Sensor *sensor)
} }
/* Free the data associated with this sensor */ /* Free the data associated with this sensor */
SDL_free(sensor->name); SDL_FreeLater(sensor->name); // this pointer gets handed to the app by SDL_GetSensorName().
SDL_free(sensor); SDL_free(sensor);
} }
SDL_UnlockSensors(); SDL_UnlockSensors();

View file

@ -299,9 +299,7 @@ void SDL_RunThread(SDL_Thread *thread)
if (!SDL_AtomicCompareAndSwap(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_ZOMBIE)) { if (!SDL_AtomicCompareAndSwap(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_ZOMBIE)) {
/* Clean up if something already detached us. */ /* Clean up if something already detached us. */
if (SDL_AtomicCompareAndSwap(&thread->state, SDL_THREAD_STATE_DETACHED, SDL_THREAD_STATE_CLEANED)) { if (SDL_AtomicCompareAndSwap(&thread->state, SDL_THREAD_STATE_DETACHED, SDL_THREAD_STATE_CLEANED)) {
if (thread->name) { SDL_FreeLater(thread->name);
SDL_free(thread->name);
}
SDL_free(thread); SDL_free(thread);
} }
} }
@ -421,9 +419,7 @@ void SDL_WaitThread(SDL_Thread *thread, int *status)
if (status) { if (status) {
*status = thread->status; *status = thread->status;
} }
if (thread->name) { SDL_FreeLater(thread->name);
SDL_free(thread->name);
}
SDL_free(thread); SDL_free(thread);
} }
} }

View file

@ -85,6 +85,7 @@ Uint16 SDL_expand_byte10[] = {
/* Helper functions */ /* Helper functions */
// This doesn't need SDL_FreeLater since it returns string literals.
#define CASE(X) \ #define CASE(X) \
case X: \ case X: \
return #X; return #X;

View file

@ -498,6 +498,7 @@ int SDL_GetNumVideoDrivers(void)
return SDL_arraysize(bootstrap) - 1; return SDL_arraysize(bootstrap) - 1;
} }
// this returns string literals, so there's no need to use SDL_FreeLater.
const char *SDL_GetVideoDriver(int index) const char *SDL_GetVideoDriver(int index)
{ {
if (index >= 0 && index < SDL_GetNumVideoDrivers()) { if (index >= 0 && index < SDL_GetNumVideoDrivers()) {
@ -642,6 +643,7 @@ pre_driver_error:
return -1; return -1;
} }
// this returns string literals, so there's no need to use SDL_FreeLater.
const char *SDL_GetCurrentVideoDriver(void) const char *SDL_GetCurrentVideoDriver(void)
{ {
if (!_this) { if (!_this) {

View file

@ -973,7 +973,7 @@ static Thing *CreatePhysicalDeviceThing(const SDL_AudioDeviceID which, const SDL
if (thing) { if (thing) {
thing->data.physdev.devid = which; thing->data.physdev.devid = which;
thing->data.physdev.iscapture = iscapture; thing->data.physdev.iscapture = iscapture;
thing->data.physdev.name = SDL_GetAudioDeviceName(which); thing->data.physdev.name = SDL_strdup(SDL_GetAudioDeviceName(which));
thing->ondrag = DeviceThing_ondrag; thing->ondrag = DeviceThing_ondrag;
thing->ondrop = PhysicalDeviceThing_ondrop; thing->ondrop = PhysicalDeviceThing_ondrop;
thing->ontick = PhysicalDeviceThing_ontick; thing->ontick = PhysicalDeviceThing_ontick;

View file

@ -81,12 +81,11 @@ int SDL_AppInit(void **appstate, int argc, char **argv)
devices = SDL_GetAudioCaptureDevices(NULL); devices = SDL_GetAudioCaptureDevices(NULL);
for (i = 0; devices[i] != 0; i++) { for (i = 0; devices[i] != 0; i++) {
char *name = SDL_GetAudioDeviceName(devices[i]); const char *name = SDL_GetAudioDeviceName(devices[i]);
SDL_Log(" Capture device #%d: '%s'\n", i, name); SDL_Log(" Capture device #%d: '%s'\n", i, name);
if (devname && (SDL_strcmp(devname, name) == 0)) { if (devname && (SDL_strcmp(devname, name) == 0)) {
want_device = devices[i]; want_device = devices[i];
} }
SDL_free(name);
} }
if (devname && (want_device == SDL_AUDIO_DEVICE_DEFAULT_CAPTURE)) { if (devname && (want_device == SDL_AUDIO_DEVICE_DEFAULT_CAPTURE)) {

View file

@ -71,7 +71,7 @@ static void iteration(void)
} else if (e.type == SDL_EVENT_AUDIO_DEVICE_ADDED) { } else if (e.type == SDL_EVENT_AUDIO_DEVICE_ADDED) {
const SDL_AudioDeviceID which = (SDL_AudioDeviceID) e.adevice.which; const SDL_AudioDeviceID which = (SDL_AudioDeviceID) e.adevice.which;
const SDL_bool iscapture = e.adevice.iscapture ? SDL_TRUE : SDL_FALSE; const SDL_bool iscapture = e.adevice.iscapture ? SDL_TRUE : SDL_FALSE;
char *name = SDL_GetAudioDeviceName(which); const char *name = SDL_GetAudioDeviceName(which);
if (name) { if (name) {
SDL_Log("New %s audio device at id %u: %s", devtypestr(iscapture), (unsigned int)which, name); SDL_Log("New %s audio device at id %u: %s", devtypestr(iscapture), (unsigned int)which, name);
} else { } else {
@ -92,7 +92,6 @@ static void iteration(void)
/* !!! FIXME: this is leaking the stream for now. We'll wire it up to a dictionary or whatever later. */ /* !!! FIXME: this is leaking the stream for now. We'll wire it up to a dictionary or whatever later. */
} }
} }
SDL_free(name);
} else if (e.type == SDL_EVENT_AUDIO_DEVICE_REMOVED) { } else if (e.type == SDL_EVENT_AUDIO_DEVICE_REMOVED) {
dev = (SDL_AudioDeviceID)e.adevice.which; dev = (SDL_AudioDeviceID)e.adevice.which;
SDL_Log("%s device %u removed.\n", devtypestr(e.adevice.iscapture), (unsigned int)dev); SDL_Log("%s device %u removed.\n", devtypestr(e.adevice.iscapture), (unsigned int)dev);

View file

@ -30,10 +30,9 @@ print_devices(SDL_bool iscapture)
int i; int i;
SDL_Log("Found %d %s device%s:\n", n, typestr, n != 1 ? "s" : ""); SDL_Log("Found %d %s device%s:\n", n, typestr, n != 1 ? "s" : "");
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
char *name = SDL_GetAudioDeviceName(devices[i]); const char *name = SDL_GetAudioDeviceName(devices[i]);
if (name) { if (name) {
SDL_Log(" %d: %s\n", i, name); SDL_Log(" %d: %s\n", i, name);
SDL_free(name);
} else { } else {
SDL_Log(" %d Error: %s\n", i, SDL_GetError()); SDL_Log(" %d Error: %s\n", i, SDL_GetError());
} }

View file

@ -365,7 +365,7 @@ static int audio_enumerateAndNameAudioDevices(void *arg)
{ {
int t; int t;
int i, n; int i, n;
char *name; const char *name;
SDL_AudioDeviceID *devices = NULL; SDL_AudioDeviceID *devices = NULL;
/* Iterate over types: t=0 output device, t=1 input/capture device */ /* Iterate over types: t=0 output device, t=1 input/capture device */
@ -385,7 +385,6 @@ static int audio_enumerateAndNameAudioDevices(void *arg)
SDLTest_AssertCheck(name != NULL, "Verify result from SDL_GetAudioDeviceName(%i) is not NULL", i); SDLTest_AssertCheck(name != NULL, "Verify result from SDL_GetAudioDeviceName(%i) is not NULL", i);
if (name != NULL) { if (name != NULL) {
SDLTest_AssertCheck(name[0] != '\0', "verify result from SDL_GetAudioDeviceName(%i) is not empty, got: '%s'", i, name); SDLTest_AssertCheck(name[0] != '\0', "verify result from SDL_GetAudioDeviceName(%i) is not empty, got: '%s'", i, name);
SDL_free(name);
} }
} }
} }

View file

@ -57,7 +57,7 @@ test_multi_audio(SDL_AudioDeviceID *devices, int devcount)
#endif #endif
for (i = 0; i < devcount; i++) { for (i = 0; i < devcount; i++) {
char *devname = SDL_GetAudioDeviceName(devices[i]); const char *devname = SDL_GetAudioDeviceName(devices[i]);
SDL_Log("Playing on device #%d of %d: id=%u, name='%s'...", i, devcount, (unsigned int) devices[i], devname); SDL_Log("Playing on device #%d of %d: id=%u, name='%s'...", i, devcount, (unsigned int) devices[i], devname);
@ -82,7 +82,6 @@ test_multi_audio(SDL_AudioDeviceID *devices, int devcount)
SDL_Log("done."); SDL_Log("done.");
SDL_DestroyAudioStream(stream); SDL_DestroyAudioStream(stream);
} }
SDL_free(devname);
stream = NULL; stream = NULL;
} }

View file

@ -191,12 +191,11 @@ int main(int argc, char *argv[])
for (i = 0; i < devcount; i++) { for (i = 0; i < devcount; i++) {
SDL_AudioStream *stream = NULL; SDL_AudioStream *stream = NULL;
char *devname = SDL_GetAudioDeviceName(devices[i]); const char *devname = SDL_GetAudioDeviceName(devices[i]);
int j; int j;
SDL_AudioSpec spec; SDL_AudioSpec spec;
SDL_Log("Testing audio device: %s\n", devname); SDL_Log("Testing audio device: %s\n", devname);
SDL_free(devname);
if (SDL_GetAudioDeviceFormat(devices[i], &spec, NULL) != 0) { if (SDL_GetAudioDeviceFormat(devices[i], &spec, NULL) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_GetAudioDeviceFormat() failed: %s\n", SDL_GetError()); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_GetAudioDeviceFormat() failed: %s\n", SDL_GetError());