diff --git a/include/SDL_hints.h b/include/SDL_hints.h index 9e8295b48..b379dbad6 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 93bcc47ca..901c58b63 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 c2e903ecb..8cc0c617b 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 fc9255cdd..d5d782f34 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 f8d858ddc..435ccb46a 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 bbcc9fd21..c3f5ddfaa 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; }