mirror of
				https://github.com/libsdl-org/SDL.git
				synced 2025-10-26 12:27:44 +00:00 
			
		
		
		
	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:
		
							
								
								
									
										336
									
								
								src/SDL_hints.c
									
									
									
									
									
								
							
							
						
						
									
										336
									
								
								src/SDL_hints.c
									
									
									
									
									
								
							| @@ -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; | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Ryan C. Gordon
					Ryan C. Gordon