hints: Change hints to be backed by Properties, add documentation. (#9892)

This makes the subsystem thread-safe, more performant, and cleans up the code a little.

Also removed SDL_HINT_WINDOWS_FORCE_MUTEX_CRITICAL_SECTIONS, since setting this hint programmatically initializes properties, which creates a lock, so we can't check hints while creating locks. The slim reader-writer locks have been the default for ages and are solid, so we'll just use those when available.
This commit is contained in:
Ryan C. Gordon
2024-08-05 12:02:28 -04:00
committed by GitHub
parent e3682995f5
commit 074dd8c35f
7 changed files with 257 additions and 215 deletions

View File

@@ -22,9 +22,6 @@
#include "SDL_hints_c.h"
/* Assuming there aren't many hints set and they aren't being queried in
critical performance paths, we'll just use linked lists here.
*/
typedef struct SDL_HintWatch
{
SDL_HintCallback callback;
@@ -34,42 +31,71 @@ typedef struct SDL_HintWatch
typedef struct SDL_Hint
{
char *name;
char *value;
SDL_HintPriority priority;
SDL_HintWatch *callbacks;
struct SDL_Hint *next;
} SDL_Hint;
static SDL_Hint *SDL_hints;
static SDL_PropertiesID SDL_hint_props = 0;
SDL_bool SDL_SetHintWithPriority(const char *name, const char *value, SDL_HintPriority priority)
static SDL_PropertiesID GetHintProperties(SDL_bool create)
{
const char *env;
SDL_Hint *hint;
SDL_HintWatch *entry;
if (!SDL_hint_props && create) {
SDL_hint_props = SDL_CreateProperties();
}
return SDL_hint_props;
}
if (!name) {
return SDL_FALSE;
void SDL_InitHints(void)
{
// Just make sure the hint properties are created on the main thread
(void)GetHintProperties(SDL_TRUE);
}
static void SDLCALL CleanupHintProperty(void *userdata, void *value)
{
SDL_Hint *hint = (SDL_Hint *) value;
SDL_free(hint->value);
SDL_HintWatch *entry = hint->callbacks;
while (entry) {
SDL_HintWatch *freeable = entry;
entry = entry->next;
SDL_free(freeable);
}
SDL_free(hint);
}
int SDL_SetHintWithPriority(const char *name, const char *value, SDL_HintPriority priority)
{
if (!name || !*name) {
return SDL_InvalidParamError("name");
}
env = SDL_getenv(name);
if (env && priority < SDL_HINT_OVERRIDE) {
return SDL_FALSE;
const char *env = SDL_getenv(name);
if (env && (priority < SDL_HINT_OVERRIDE)) {
return SDL_SetError("An environment variable is taking priority");
}
for (hint = SDL_hints; hint; hint = hint->next) {
if (SDL_strcmp(name, hint->name) == 0) {
if (priority < hint->priority) {
return SDL_FALSE;
}
if (hint->value != value &&
(!value || !hint->value || SDL_strcmp(hint->value, value) != 0)) {
const SDL_PropertiesID hints = GetHintProperties(SDL_TRUE);
if (!hints) {
return -1;
}
int retval = -1;
SDL_LockProperties(hints);
SDL_Hint *hint = SDL_GetPointerProperty(hints, name, NULL);
if (hint) {
if (priority >= hint->priority) {
if (hint->value != value && (!value || !hint->value || SDL_strcmp(hint->value, value) != 0)) {
char *old_value = hint->value;
hint->value = value ? SDL_strdup(value) : NULL;
for (entry = hint->callbacks; entry;) {
/* Save the next entry in case this one is deleted */
SDL_HintWatch *entry = hint->callbacks;
while (entry) {
// Save the next entry in case this one is deleted
SDL_HintWatch *next = entry->next;
entry->callback(entry->userdata, name, old_value, value);
entry = next;
@@ -77,104 +103,118 @@ SDL_bool SDL_SetHintWithPriority(const char *name, const char *value, SDL_HintPr
SDL_free(old_value);
}
hint->priority = priority;
return SDL_TRUE;
retval = 0;
}
} else { // Couldn't find the hint? Add a new one.
hint = (SDL_Hint *)SDL_malloc(sizeof(*hint));
if (hint) {
hint->value = value ? SDL_strdup(value) : NULL;
hint->priority = priority;
hint->callbacks = NULL;
retval = (SDL_SetPointerPropertyWithCleanup(hints, name, hint, CleanupHintProperty, NULL) != -1);
}
}
/* Couldn't find the hint, add a new one */
hint = (SDL_Hint *)SDL_malloc(sizeof(*hint));
if (!hint) {
return SDL_FALSE;
}
hint->name = SDL_strdup(name);
hint->value = value ? SDL_strdup(value) : NULL;
hint->priority = priority;
hint->callbacks = NULL;
hint->next = SDL_hints;
SDL_hints = hint;
return SDL_TRUE;
SDL_UnlockProperties(hints);
return retval;
}
SDL_bool SDL_ResetHint(const char *name)
int SDL_ResetHint(const char *name)
{
const char *env;
SDL_Hint *hint;
SDL_HintWatch *entry;
if (!name) {
return SDL_FALSE;
if (!name || !*name) {
return SDL_InvalidParamError("name");
}
env = SDL_getenv(name);
for (hint = SDL_hints; hint; hint = hint->next) {
if (SDL_strcmp(name, hint->name) == 0) {
if ((!env && hint->value) ||
(env && !hint->value) ||
(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;
return SDL_TRUE;
}
const char *env = SDL_getenv(name);
const SDL_PropertiesID hints = GetHintProperties(SDL_FALSE);
if (!hints) {
return -1;
}
return SDL_FALSE;
}
void SDL_ResetHints(void)
{
const char *env;
SDL_Hint *hint;
SDL_HintWatch *entry;
int retval = -1;
for (hint = SDL_hints; hint; hint = hint->next) {
env = SDL_getenv(hint->name);
if ((!env && hint->value) ||
(env && !hint->value) ||
(env && SDL_strcmp(env, hint->value) != 0)) {
for (entry = hint->callbacks; entry;) {
/* Save the next entry in case this one is deleted */
SDL_LockProperties(hints);
SDL_Hint *hint = SDL_GetPointerProperty(hints, name, NULL);
if (hint) {
if ((!env && hint->value) || (env && !hint->value) || (env && SDL_strcmp(env, hint->value) != 0)) {
for (SDL_HintWatch *entry = hint->callbacks; entry;) {
// Save the next entry in case this one is deleted
SDL_HintWatch *next = entry->next;
entry->callback(entry->userdata, hint->name, hint->value, env);
entry->callback(entry->userdata, name, hint->value, env);
entry = next;
}
}
SDL_free(hint->value);
hint->value = NULL;
hint->priority = SDL_HINT_DEFAULT;
retval = 0;
}
SDL_UnlockProperties(hints);
return retval;
}
SDL_bool SDL_SetHint(const char *name, const char *value)
static void SDLCALL ResetHintsCallback(void *userdata, SDL_PropertiesID hints, const char *name)
{
SDL_Hint *hint = SDL_GetPointerProperty(hints, name, NULL);
if (!hint) {
return; // uh...okay.
}
const char *env = SDL_getenv(name);
if ((!env && hint->value) || (env && !hint->value) || (env && SDL_strcmp(env, hint->value) != 0)) {
SDL_HintWatch *entry = hint->callbacks;
while (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;
}
void SDL_ResetHints(void)
{
SDL_EnumerateProperties(GetHintProperties(SDL_FALSE), ResetHintsCallback, NULL);
}
int SDL_SetHint(const char *name, const char *value)
{
return SDL_SetHintWithPriority(name, value, SDL_HINT_NORMAL);
}
const char *SDL_GetHint(const char *name)
{
const char *env;
SDL_Hint *hint;
if (!name) {
return NULL;
}
env = SDL_getenv(name);
for (hint = SDL_hints; hint; hint = hint->next) {
if (SDL_strcmp(name, hint->name) == 0) {
if (!env || hint->priority == SDL_HINT_OVERRIDE) {
return SDL_GetPersistentString(hint->value);
}
break;
const SDL_PropertiesID hints = GetHintProperties(SDL_FALSE);
if (!hints) {
return NULL;
}
const char *retval = SDL_getenv(name);
SDL_LockProperties(hints);
SDL_Hint *hint = SDL_GetPointerProperty(hints, name, NULL);
if (hint) {
if (!retval || hint->priority == SDL_HINT_OVERRIDE) {
retval = SDL_GetPersistentString(hint->value);
}
}
return env;
SDL_UnlockProperties(hints);
return retval;
}
int SDL_GetStringInteger(const char *value, int default_value)
@@ -213,102 +253,92 @@ SDL_bool SDL_GetHintBoolean(const char *name, SDL_bool default_value)
int SDL_AddHintCallback(const char *name, SDL_HintCallback callback, void *userdata)
{
SDL_Hint *hint;
SDL_HintWatch *entry;
const char *value;
if (!name || !*name) {
return SDL_InvalidParamError("name");
}
if (!callback) {
} else if (!callback) {
return SDL_InvalidParamError("callback");
}
SDL_DelHintCallback(name, callback, userdata);
const SDL_PropertiesID hints = GetHintProperties(SDL_TRUE);
if (!hints) {
return -1;
}
entry = (SDL_HintWatch *)SDL_malloc(sizeof(*entry));
SDL_HintWatch *entry = (SDL_HintWatch *)SDL_malloc(sizeof(*entry));
if (!entry) {
return -1;
}
entry->callback = callback;
entry->userdata = userdata;
for (hint = SDL_hints; hint; hint = hint->next) {
if (SDL_strcmp(name, hint->name) == 0) {
break;
}
}
if (!hint) {
/* Need to add a hint entry for this watcher */
int retval = -1;
SDL_LockProperties(hints);
SDL_DelHintCallback(name, callback, userdata);
SDL_Hint *hint = SDL_GetPointerProperty(hints, name, NULL);
if (hint) {
retval = 0;
} else { // Need to add a hint entry for this watcher
hint = (SDL_Hint *)SDL_malloc(sizeof(*hint));
if (!hint) {
SDL_free(entry);
return -1;
} else {
hint->value = NULL;
hint->priority = SDL_HINT_DEFAULT;
hint->callbacks = NULL;
retval = SDL_SetPointerPropertyWithCleanup(hints, name, hint, CleanupHintProperty, NULL);
}
hint->name = SDL_strdup(name);
if (!hint->name) {
SDL_free(entry);
SDL_free(hint);
return -1;
}
hint->value = NULL;
hint->priority = SDL_HINT_DEFAULT;
hint->callbacks = NULL;
hint->next = SDL_hints;
SDL_hints = hint;
}
/* Add it to the callbacks for this hint */
// Add it to the callbacks for this hint
entry->next = hint->callbacks;
hint->callbacks = entry;
/* Now call it with the current value */
value = SDL_GetHint(name);
// Now call it with the current value
const char *value = SDL_GetHint(name);
callback(userdata, name, value, value);
return 0;
SDL_UnlockProperties(hints);
return retval;
}
void SDL_DelHintCallback(const char *name, SDL_HintCallback callback, void *userdata)
{
SDL_Hint *hint;
SDL_HintWatch *entry, *prev;
if (!name || !*name) {
return;
}
for (hint = SDL_hints; hint; hint = hint->next) {
if (SDL_strcmp(name, hint->name) == 0) {
prev = NULL;
for (entry = hint->callbacks; entry; entry = entry->next) {
if (callback == entry->callback && userdata == entry->userdata) {
if (prev) {
prev->next = entry->next;
} else {
hint->callbacks = entry->next;
}
SDL_free(entry);
break;
const SDL_PropertiesID hints = GetHintProperties(SDL_FALSE);
if (!hints) {
return;
}
SDL_LockProperties(hints);
SDL_Hint *hint = SDL_GetPointerProperty(hints, name, NULL);
if (hint) {
SDL_HintWatch *prev = NULL;
for (SDL_HintWatch *entry = hint->callbacks; entry; entry = entry->next) {
if ((callback == entry->callback) && (userdata == entry->userdata)) {
if (prev) {
prev->next = entry->next;
} else {
hint->callbacks = entry->next;
}
prev = entry;
SDL_free(entry);
break;
}
return;
prev = entry;
}
}
SDL_UnlockProperties(hints);
}
void SDL_ClearHints(void)
void SDL_QuitHints(void)
{
SDL_Hint *hint;
SDL_HintWatch *entry;
while (SDL_hints) {
hint = SDL_hints;
SDL_hints = hint->next;
SDL_free(hint->name);
SDL_free(hint->value);
for (entry = hint->callbacks; entry;) {
SDL_HintWatch *freeable = entry;
entry = entry->next;
SDL_free(freeable);
}
SDL_free(hint);
}
SDL_DestroyProperties(SDL_hint_props);
SDL_hint_props = 0;
}