From 64ea6cefafb006799dedca0f11e937158ce60045 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 2 Oct 2022 17:17:31 -0700
Subject: [PATCH] SDL_ResetHint() no longer clears the callbacks associated
with a hint
Also added SDL_ResetHints() to reset all callbacks, and clarified that SDL_ClearHints() is just used for cleanup at shutdown.
Fixes https://github.com/libsdl-org/SDL/issues/6313
---
include/SDL_hints.h | 19 +++++++++++-
src/SDL_hints.c | 35 ++++++++++++++++-----
src/dynapi/SDL2.exports | 1 +
src/dynapi/SDL_dynapi_overrides.h | 1 +
src/dynapi/SDL_dynapi_procs.h | 1 +
test/testautomation_hints.c | 51 +++++++++++++++++++++++++++++++
6 files changed, 99 insertions(+), 9 deletions(-)
diff --git a/include/SDL_hints.h b/include/SDL_hints.h
index 9e8295b4849..b379dbad60f 100644
--- a/include/SDL_hints.h
+++ b/include/SDL_hints.h
@@ -2423,6 +2423,19 @@ extern DECLSPEC SDL_bool SDLCALL SDL_SetHint(const char *name,
*/
extern DECLSPEC SDL_bool SDLCALL SDL_ResetHint(const char *name);
+/**
+ * Reset all hints to the default values.
+ *
+ * This will reset all hints to the value of the associated environment variable, or NULL if the environment isn't set. Callbacks will be called normally with this change.
+ *
+ * \since This function is available since SDL 2.26.0.
+ *
+ * \sa SDL_GetHint
+ * \sa SDL_SetHint
+ * \sa SDL_ResetHint
+ */
+extern DECLSPEC void SDLCALL SDL_ResetHints(void);
+
/**
* Get the value of a hint.
*
@@ -2496,9 +2509,13 @@ extern DECLSPEC void SDLCALL SDL_DelHintCallback(const char *name,
/**
* Clear all hints.
*
- * This function is automatically called during SDL_Quit().
+ * This function is automatically called during SDL_Quit(), and deletes all callbacks without calling them and frees all memory associated with hints. If you're calling this from application code you probably want to call SDL_ResetHints() instead.
+ *
+ * This function will be removed from the API the next time we rev the ABI.
*
* \since This function is available since SDL 2.0.0.
+ *
+ * \sa SDL_ResetHints
*/
extern DECLSPEC void SDLCALL SDL_ClearHints(void);
diff --git a/src/SDL_hints.c b/src/SDL_hints.c
index 93bcc47cab3..901c58b6346 100644
--- a/src/SDL_hints.c
+++ b/src/SDL_hints.c
@@ -100,7 +100,6 @@ SDL_bool
SDL_ResetHint(const char *name)
{
const char *env;
- SDL_Hint *hint, *prev;
SDL_HintWatch *entry;
if (!name) {
@@ -108,7 +107,7 @@ SDL_ResetHint(const char *name)
}
env = SDL_getenv(name);
- for (prev = NULL, hint = SDL_hints; hint; prev = hint, hint = hint->next) {
+ for (hint = SDL_hints; hint; hint = hint->next) {
if (SDL_strcmp(name, hint->name) == 0) {
if ((env == NULL && hint->value != NULL) ||
(env != NULL && hint->value == NULL) ||
@@ -120,19 +119,39 @@ SDL_ResetHint(const char *name)
entry = next;
}
}
- if (prev) {
- prev->next = hint->next;
- } else {
- SDL_hints = hint->next;
- }
SDL_free(hint->value);
- SDL_free(hint);
+ hint->value = NULL;
+ hint->priority = SDL_HINT_DEFAULT;
return SDL_TRUE;
}
}
return SDL_FALSE;
}
+void
+SDL_ResetHints(void)
+{
+ const char *env;
+ SDL_HintWatch *entry;
+
+ for (hint = SDL_hints; hint; hint = hint->next) {
+ env = SDL_getenv(hint->name);
+ if ((env == NULL && hint->value != NULL) ||
+ (env != NULL && hint->value == NULL) ||
+ (env && SDL_strcmp(env, hint->value) != 0)) {
+ for (entry = hint->callbacks; entry; ) {
+ /* Save the next entry in case this one is deleted */
+ SDL_HintWatch *next = entry->next;
+ entry->callback(entry->userdata, name, hint->value, env);
+ entry = next;
+ }
+ }
+ SDL_free(hint->value);
+ hint->value = NULL;
+ hint->priority = SDL_HINT_DEFAULT;
+ }
+}
+
SDL_bool
SDL_SetHint(const char *name, const char *value)
{
diff --git a/src/dynapi/SDL2.exports b/src/dynapi/SDL2.exports
index c2e903ecbeb..8cc0c617bb6 100644
--- a/src/dynapi/SDL2.exports
+++ b/src/dynapi/SDL2.exports
@@ -865,3 +865,4 @@
++'_SDL_HasPrimarySelectionText'.'SDL2.dll'.'SDL_HasPrimarySelectionText'
++'_SDL_GameControllerGetSensorDataWithTimestamp'.'SDL2.dll'.'SDL_GameControllerGetSensorDataWithTimestamp'
++'_SDL_SensorGetDataWithTimestamp'.'SDL2.dll'.'SDL_SensorGetDataWithTimestamp'
+++'_SDL_ResetHints'.'SDL2.dll'.'SDL_ResetHints'
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index fc9255cdd76..d5d782f3419 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -891,3 +891,4 @@
#define SDL_HasPrimarySelectionText SDL_HasPrimarySelectionText_REAL
#define SDL_GameControllerGetSensorDataWithTimestamp SDL_GameControllerGetSensorDataWithTimestamp_REAL
#define SDL_SensorGetDataWithTimestamp SDL_SensorGetDataWithTimestamp_REAL
+#define SDL_ResetHints SDL_ResetHints_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index f8d858ddc8f..435ccb46a01 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -974,3 +974,4 @@ SDL_DYNAPI_PROC(char*,SDL_GetPrimarySelectionText,(void),(),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_HasPrimarySelectionText,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GameControllerGetSensorDataWithTimestamp,(SDL_GameController *a, SDL_SensorType b, Uint64 *c, float *d, int e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(int,SDL_SensorGetDataWithTimestamp,(SDL_Sensor *a, Uint64 *b, float *c, int d),(a,b,c,d),return)
+SDL_DYNAPI_PROC(void,SDL_ResetHints,(void),(),)
diff --git a/test/testautomation_hints.c b/test/testautomation_hints.c
index bbcc9fd219f..c3f5ddfaa99 100644
--- a/test/testautomation_hints.c
+++ b/test/testautomation_hints.c
@@ -92,6 +92,11 @@ hints_getHint(void *arg)
return TEST_COMPLETED;
}
+static void SDLCALL hints_testHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
+{
+ *(char **)userdata = hint ? SDL_strdup(hint) : NULL;
+}
+
/**
* @brief Call to SDL_SetHint
*/
@@ -102,6 +107,7 @@ hints_setHint(void *arg)
const char *originalValue;
char *value;
const char *testValue;
+ char *callbackValue;
SDL_bool result;
int i, j;
@@ -192,6 +198,51 @@ hints_setHint(void *arg)
"testValue = %s, expected \"original\"",
testValue);
+ /* Make sure callback functionality works past a reset */
+ SDLTest_AssertPass("Call to SDL_AddHintCallback()");
+ callbackValue = NULL;
+ SDL_AddHintCallback(testHint, hints_testHintChanged, &callbackValue);
+ SDLTest_AssertCheck(
+ callbackValue && SDL_strcmp(callbackValue, "original") == 0,
+ "callbackValue = %s, expected \"original\"",
+ callbackValue);
+ SDL_free(callbackValue);
+
+ SDLTest_AssertPass("Call to SDL_SetHintWithPriority(\"temp\", SDL_HINT_OVERRIDE), using callback");
+ callbackValue = NULL;
+ SDL_SetHintWithPriority(testHint, "temp", SDL_HINT_OVERRIDE);
+ SDLTest_AssertCheck(
+ callbackValue && SDL_strcmp(callbackValue, "temp") == 0,
+ "callbackValue = %s, expected \"temp\"",
+ callbackValue);
+ SDL_free(callbackValue);
+
+ SDLTest_AssertPass("Call to SDL_ResetHint(), using callback");
+ callbackValue = NULL;
+ SDL_ResetHint(testHint);
+ SDLTest_AssertCheck(
+ callbackValue && SDL_strcmp(callbackValue, "original") == 0,
+ "callbackValue = %s, expected \"original\"",
+ callbackValue);
+
+ SDLTest_AssertPass("Call to SDL_SetHintWithPriority(\"temp\", SDL_HINT_OVERRIDE), using callback after reset");
+ callbackValue = NULL;
+ SDL_SetHintWithPriority(testHint, "temp", SDL_HINT_OVERRIDE);
+ SDLTest_AssertCheck(
+ callbackValue && SDL_strcmp(callbackValue, "temp") == 0,
+ "callbackValue = %s, expected \"temp\"",
+ callbackValue);
+ SDL_free(callbackValue);
+
+ SDLTest_AssertPass("Call to SDL_ResetHint(), after clearing callback");
+ callbackValue = NULL;
+ SDL_DelHintCallback(testHint, hints_testHintChanged, &callbackValue);
+ SDL_ResetHint(testHint);
+ SDLTest_AssertCheck(
+ callbackValue == NULL,
+ "callbackValue = %s, expected \"(null)\"",
+ callbackValue);
+
return TEST_COMPLETED;
}