Simple Directmedia Layer
at main 11 kB view raw
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#include "SDL_hints_c.h" 24 25typedef struct SDL_HintWatch 26{ 27 SDL_HintCallback callback; 28 void *userdata; 29 struct SDL_HintWatch *next; 30} SDL_HintWatch; 31 32typedef struct SDL_Hint 33{ 34 char *value; 35 SDL_HintPriority priority; 36 SDL_HintWatch *callbacks; 37} SDL_Hint; 38 39static SDL_AtomicU32 SDL_hint_props; 40 41 42void SDL_InitHints(void) 43{ 44} 45 46void SDL_QuitHints(void) 47{ 48 SDL_PropertiesID props; 49 do { 50 props = SDL_GetAtomicU32(&SDL_hint_props); 51 } while (!SDL_CompareAndSwapAtomicU32(&SDL_hint_props, props, 0)); 52 53 if (props) { 54 SDL_DestroyProperties(props); 55 } 56} 57 58static SDL_PropertiesID GetHintProperties(bool create) 59{ 60 SDL_PropertiesID props = SDL_GetAtomicU32(&SDL_hint_props); 61 if (!props && create) { 62 props = SDL_CreateProperties(); 63 if (!SDL_CompareAndSwapAtomicU32(&SDL_hint_props, 0, props)) { 64 // Somebody else created hint properties before us, just use those 65 SDL_DestroyProperties(props); 66 props = SDL_GetAtomicU32(&SDL_hint_props); 67 } 68 } 69 return props; 70} 71 72static void SDLCALL CleanupHintProperty(void *userdata, void *value) 73{ 74 SDL_Hint *hint = (SDL_Hint *) value; 75 SDL_free(hint->value); 76 77 SDL_HintWatch *entry = hint->callbacks; 78 while (entry) { 79 SDL_HintWatch *freeable = entry; 80 entry = entry->next; 81 SDL_free(freeable); 82 } 83 SDL_free(hint); 84} 85 86static const char* GetHintEnvironmentVariable(const char *name) 87{ 88 const char *result = SDL_getenv(name); 89 if (!result && name && *name) { 90 // fall back to old (SDL2) names of environment variables that 91 // are important to users (e.g. many use SDL_VIDEODRIVER=wayland) 92 if (SDL_strcmp(name, SDL_HINT_VIDEO_DRIVER) == 0) { 93 result = SDL_getenv("SDL_VIDEODRIVER"); 94 } else if (SDL_strcmp(name, SDL_HINT_AUDIO_DRIVER) == 0) { 95 result = SDL_getenv("SDL_AUDIODRIVER"); 96 } 97 } 98 return result; 99} 100 101bool SDL_SetHintWithPriority(const char *name, const char *value, SDL_HintPriority priority) 102{ 103 if (!name || !*name) { 104 return SDL_InvalidParamError("name"); 105 } 106 107 const char *env = GetHintEnvironmentVariable(name); 108 if (env && (priority < SDL_HINT_OVERRIDE)) { 109 return SDL_SetError("An environment variable is taking priority"); 110 } 111 112 const SDL_PropertiesID hints = GetHintProperties(true); 113 if (!hints) { 114 return false; 115 } 116 117 bool result = false; 118 119 SDL_LockProperties(hints); 120 121 SDL_Hint *hint = (SDL_Hint *)SDL_GetPointerProperty(hints, name, NULL); 122 if (hint) { 123 if (priority >= hint->priority) { 124 if (hint->value != value && (!value || !hint->value || SDL_strcmp(hint->value, value) != 0)) { 125 char *old_value = hint->value; 126 127 hint->value = value ? SDL_strdup(value) : NULL; 128 SDL_HintWatch *entry = hint->callbacks; 129 while (entry) { 130 // Save the next entry in case this one is deleted 131 SDL_HintWatch *next = entry->next; 132 entry->callback(entry->userdata, name, old_value, value); 133 entry = next; 134 } 135 SDL_free(old_value); 136 } 137 hint->priority = priority; 138 result = true; 139 } 140 } else { // Couldn't find the hint? Add a new one. 141 hint = (SDL_Hint *)SDL_malloc(sizeof(*hint)); 142 if (hint) { 143 hint->value = value ? SDL_strdup(value) : NULL; 144 hint->priority = priority; 145 hint->callbacks = NULL; 146 result = SDL_SetPointerPropertyWithCleanup(hints, name, hint, CleanupHintProperty, NULL); 147 } 148 } 149 150 SDL_UnlockProperties(hints); 151 152 return result; 153} 154 155bool SDL_ResetHint(const char *name) 156{ 157 if (!name || !*name) { 158 return SDL_InvalidParamError("name"); 159 } 160 161 const char *env = GetHintEnvironmentVariable(name); 162 163 const SDL_PropertiesID hints = GetHintProperties(false); 164 if (!hints) { 165 return false; 166 } 167 168 bool result = false; 169 170 SDL_LockProperties(hints); 171 172 SDL_Hint *hint = (SDL_Hint *)SDL_GetPointerProperty(hints, name, NULL); 173 if (hint) { 174 if ((!env && hint->value) || (env && !hint->value) || (env && SDL_strcmp(env, hint->value) != 0)) { 175 for (SDL_HintWatch *entry = hint->callbacks; entry;) { 176 // Save the next entry in case this one is deleted 177 SDL_HintWatch *next = entry->next; 178 entry->callback(entry->userdata, name, hint->value, env); 179 entry = next; 180 } 181 } 182 SDL_free(hint->value); 183 hint->value = NULL; 184 hint->priority = SDL_HINT_DEFAULT; 185 result = true; 186 } 187 188 SDL_UnlockProperties(hints); 189 190 return result; 191} 192 193static void SDLCALL ResetHintsCallback(void *userdata, SDL_PropertiesID hints, const char *name) 194{ 195 SDL_Hint *hint = (SDL_Hint *)SDL_GetPointerProperty(hints, name, NULL); 196 if (!hint) { 197 return; // uh...okay. 198 } 199 200 const char *env = GetHintEnvironmentVariable(name); 201 if ((!env && hint->value) || (env && !hint->value) || (env && SDL_strcmp(env, hint->value) != 0)) { 202 SDL_HintWatch *entry = hint->callbacks; 203 while (entry) { 204 // Save the next entry in case this one is deleted 205 SDL_HintWatch *next = entry->next; 206 entry->callback(entry->userdata, name, hint->value, env); 207 entry = next; 208 } 209 } 210 SDL_free(hint->value); 211 hint->value = NULL; 212 hint->priority = SDL_HINT_DEFAULT; 213} 214 215void SDL_ResetHints(void) 216{ 217 SDL_EnumerateProperties(GetHintProperties(false), ResetHintsCallback, NULL); 218} 219 220bool SDL_SetHint(const char *name, const char *value) 221{ 222 return SDL_SetHintWithPriority(name, value, SDL_HINT_NORMAL); 223} 224 225const char *SDL_GetHint(const char *name) 226{ 227 if (!name) { 228 return NULL; 229 } 230 231 const char *result = GetHintEnvironmentVariable(name); 232 233 const SDL_PropertiesID hints = GetHintProperties(false); 234 if (hints) { 235 SDL_LockProperties(hints); 236 237 SDL_Hint *hint = (SDL_Hint *)SDL_GetPointerProperty(hints, name, NULL); 238 if (hint) { 239 if (!result || hint->priority == SDL_HINT_OVERRIDE) { 240 result = SDL_GetPersistentString(hint->value); 241 } 242 } 243 244 SDL_UnlockProperties(hints); 245 } 246 247 return result; 248} 249 250int SDL_GetStringInteger(const char *value, int default_value) 251{ 252 if (!value || !*value) { 253 return default_value; 254 } 255 if (SDL_strcasecmp(value, "false") == 0) { 256 return 0; 257 } 258 if (SDL_strcasecmp(value, "true") == 0) { 259 return 1; 260 } 261 if (*value == '-' || SDL_isdigit(*value)) { 262 return SDL_atoi(value); 263 } 264 return default_value; 265} 266 267bool SDL_GetStringBoolean(const char *value, bool default_value) 268{ 269 if (!value || !*value) { 270 return default_value; 271 } 272 if (*value == '0' || SDL_strcasecmp(value, "false") == 0) { 273 return false; 274 } 275 return true; 276} 277 278bool SDL_GetHintBoolean(const char *name, bool default_value) 279{ 280 const char *hint = SDL_GetHint(name); 281 return SDL_GetStringBoolean(hint, default_value); 282} 283 284bool SDL_AddHintCallback(const char *name, SDL_HintCallback callback, void *userdata) 285{ 286 if (!name || !*name) { 287 return SDL_InvalidParamError("name"); 288 } else if (!callback) { 289 return SDL_InvalidParamError("callback"); 290 } 291 292 const SDL_PropertiesID hints = GetHintProperties(true); 293 if (!hints) { 294 return false; 295 } 296 297 SDL_HintWatch *entry = (SDL_HintWatch *)SDL_malloc(sizeof(*entry)); 298 if (!entry) { 299 return false; 300 } 301 entry->callback = callback; 302 entry->userdata = userdata; 303 304 bool result = false; 305 306 SDL_LockProperties(hints); 307 308 SDL_RemoveHintCallback(name, callback, userdata); 309 310 SDL_Hint *hint = (SDL_Hint *)SDL_GetPointerProperty(hints, name, NULL); 311 if (hint) { 312 result = true; 313 } else { // Need to add a hint entry for this watcher 314 hint = (SDL_Hint *)SDL_malloc(sizeof(*hint)); 315 if (!hint) { 316 SDL_free(entry); 317 SDL_UnlockProperties(hints); 318 return false; 319 } else { 320 hint->value = NULL; 321 hint->priority = SDL_HINT_DEFAULT; 322 hint->callbacks = NULL; 323 result = SDL_SetPointerPropertyWithCleanup(hints, name, hint, CleanupHintProperty, NULL); 324 } 325 } 326 327 // Add it to the callbacks for this hint 328 entry->next = hint->callbacks; 329 hint->callbacks = entry; 330 331 // Now call it with the current value 332 const char *value = SDL_GetHint(name); 333 callback(userdata, name, value, value); 334 335 SDL_UnlockProperties(hints); 336 337 return result; 338} 339 340void SDL_RemoveHintCallback(const char *name, SDL_HintCallback callback, void *userdata) 341{ 342 if (!name || !*name) { 343 return; 344 } 345 346 const SDL_PropertiesID hints = GetHintProperties(false); 347 if (!hints) { 348 return; 349 } 350 351 SDL_LockProperties(hints); 352 SDL_Hint *hint = (SDL_Hint *)SDL_GetPointerProperty(hints, name, NULL); 353 if (hint) { 354 SDL_HintWatch *prev = NULL; 355 for (SDL_HintWatch *entry = hint->callbacks; entry; entry = entry->next) { 356 if ((callback == entry->callback) && (userdata == entry->userdata)) { 357 if (prev) { 358 prev->next = entry->next; 359 } else { 360 hint->callbacks = entry->next; 361 } 362 SDL_free(entry); 363 break; 364 } 365 prev = entry; 366 } 367 } 368 SDL_UnlockProperties(hints); 369} 370