Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/SDL_hints.c
9903 views
1
/*
2
Simple DirectMedia Layer
3
Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
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
25
#ifdef SDL_PLATFORM_ANDROID
26
#include "core/android/SDL_android.h"
27
#endif
28
29
typedef struct SDL_HintWatch
30
{
31
SDL_HintCallback callback;
32
void *userdata;
33
struct SDL_HintWatch *next;
34
} SDL_HintWatch;
35
36
typedef struct SDL_Hint
37
{
38
char *value;
39
SDL_HintPriority priority;
40
SDL_HintWatch *callbacks;
41
} SDL_Hint;
42
43
static SDL_AtomicU32 SDL_hint_props;
44
45
46
void SDL_InitHints(void)
47
{
48
}
49
50
void SDL_QuitHints(void)
51
{
52
SDL_PropertiesID props;
53
do {
54
props = SDL_GetAtomicU32(&SDL_hint_props);
55
} while (!SDL_CompareAndSwapAtomicU32(&SDL_hint_props, props, 0));
56
57
if (props) {
58
SDL_DestroyProperties(props);
59
}
60
}
61
62
static SDL_PropertiesID GetHintProperties(bool create)
63
{
64
SDL_PropertiesID props = SDL_GetAtomicU32(&SDL_hint_props);
65
if (!props && create) {
66
props = SDL_CreateProperties();
67
if (!SDL_CompareAndSwapAtomicU32(&SDL_hint_props, 0, props)) {
68
// Somebody else created hint properties before us, just use those
69
SDL_DestroyProperties(props);
70
props = SDL_GetAtomicU32(&SDL_hint_props);
71
}
72
}
73
return props;
74
}
75
76
static void SDLCALL CleanupHintProperty(void *userdata, void *value)
77
{
78
SDL_Hint *hint = (SDL_Hint *) value;
79
SDL_free(hint->value);
80
81
SDL_HintWatch *entry = hint->callbacks;
82
while (entry) {
83
SDL_HintWatch *freeable = entry;
84
entry = entry->next;
85
SDL_free(freeable);
86
}
87
SDL_free(hint);
88
}
89
90
static const char* GetHintEnvironmentVariable(const char *name)
91
{
92
const char *result = SDL_getenv(name);
93
if (!result && name && *name) {
94
// fall back to old (SDL2) names of environment variables that
95
// are important to users (e.g. many use SDL_VIDEODRIVER=wayland)
96
if (SDL_strcmp(name, SDL_HINT_VIDEO_DRIVER) == 0) {
97
result = SDL_getenv("SDL_VIDEODRIVER");
98
} else if (SDL_strcmp(name, SDL_HINT_AUDIO_DRIVER) == 0) {
99
result = SDL_getenv("SDL_AUDIODRIVER");
100
}
101
}
102
return result;
103
}
104
105
bool SDL_SetHintWithPriority(const char *name, const char *value, SDL_HintPriority priority)
106
{
107
if (!name || !*name) {
108
return SDL_InvalidParamError("name");
109
}
110
111
const char *env = GetHintEnvironmentVariable(name);
112
if (env && (priority < SDL_HINT_OVERRIDE)) {
113
return SDL_SetError("An environment variable is taking priority");
114
}
115
116
const SDL_PropertiesID hints = GetHintProperties(true);
117
if (!hints) {
118
return false;
119
}
120
121
bool result = false;
122
123
SDL_LockProperties(hints);
124
125
SDL_Hint *hint = (SDL_Hint *)SDL_GetPointerProperty(hints, name, NULL);
126
if (hint) {
127
if (priority >= hint->priority) {
128
if (hint->value != value && (!value || !hint->value || SDL_strcmp(hint->value, value) != 0)) {
129
char *old_value = hint->value;
130
131
hint->value = value ? SDL_strdup(value) : NULL;
132
SDL_HintWatch *entry = hint->callbacks;
133
while (entry) {
134
// Save the next entry in case this one is deleted
135
SDL_HintWatch *next = entry->next;
136
entry->callback(entry->userdata, name, old_value, value);
137
entry = next;
138
}
139
SDL_free(old_value);
140
}
141
hint->priority = priority;
142
result = true;
143
}
144
} else { // Couldn't find the hint? Add a new one.
145
hint = (SDL_Hint *)SDL_malloc(sizeof(*hint));
146
if (hint) {
147
hint->value = value ? SDL_strdup(value) : NULL;
148
hint->priority = priority;
149
hint->callbacks = NULL;
150
result = SDL_SetPointerPropertyWithCleanup(hints, name, hint, CleanupHintProperty, NULL);
151
}
152
}
153
154
#ifdef SDL_PLATFORM_ANDROID
155
if (SDL_strcmp(name, SDL_HINT_ANDROID_ALLOW_RECREATE_ACTIVITY) == 0) {
156
// Special handling for this hint, which needs to persist outside the normal application flow
157
Android_SetAllowRecreateActivity(SDL_GetStringBoolean(value, false));
158
}
159
#endif // SDL_PLATFORM_ANDROID
160
161
SDL_UnlockProperties(hints);
162
163
return result;
164
}
165
166
bool SDL_ResetHint(const char *name)
167
{
168
if (!name || !*name) {
169
return SDL_InvalidParamError("name");
170
}
171
172
const char *env = GetHintEnvironmentVariable(name);
173
174
const SDL_PropertiesID hints = GetHintProperties(false);
175
if (!hints) {
176
return false;
177
}
178
179
bool result = false;
180
181
SDL_LockProperties(hints);
182
183
SDL_Hint *hint = (SDL_Hint *)SDL_GetPointerProperty(hints, name, NULL);
184
if (hint) {
185
if ((!env && hint->value) || (env && !hint->value) || (env && SDL_strcmp(env, hint->value) != 0)) {
186
for (SDL_HintWatch *entry = hint->callbacks; entry;) {
187
// Save the next entry in case this one is deleted
188
SDL_HintWatch *next = entry->next;
189
entry->callback(entry->userdata, name, hint->value, env);
190
entry = next;
191
}
192
}
193
SDL_free(hint->value);
194
hint->value = NULL;
195
hint->priority = SDL_HINT_DEFAULT;
196
result = true;
197
}
198
199
#ifdef SDL_PLATFORM_ANDROID
200
if (SDL_strcmp(name, SDL_HINT_ANDROID_ALLOW_RECREATE_ACTIVITY) == 0) {
201
// Special handling for this hint, which needs to persist outside the normal application flow
202
if (env) {
203
Android_SetAllowRecreateActivity(SDL_GetStringBoolean(env, false));
204
} else {
205
Android_SetAllowRecreateActivity(false);
206
}
207
}
208
#endif // SDL_PLATFORM_ANDROID
209
210
SDL_UnlockProperties(hints);
211
212
return result;
213
}
214
215
static void SDLCALL ResetHintsCallback(void *userdata, SDL_PropertiesID hints, const char *name)
216
{
217
SDL_Hint *hint = (SDL_Hint *)SDL_GetPointerProperty(hints, name, NULL);
218
if (!hint) {
219
return; // uh...okay.
220
}
221
222
const char *env = GetHintEnvironmentVariable(name);
223
if ((!env && hint->value) || (env && !hint->value) || (env && SDL_strcmp(env, hint->value) != 0)) {
224
SDL_HintWatch *entry = hint->callbacks;
225
while (entry) {
226
// Save the next entry in case this one is deleted
227
SDL_HintWatch *next = entry->next;
228
entry->callback(entry->userdata, name, hint->value, env);
229
entry = next;
230
}
231
}
232
SDL_free(hint->value);
233
hint->value = NULL;
234
hint->priority = SDL_HINT_DEFAULT;
235
236
#ifdef SDL_PLATFORM_ANDROID
237
if (SDL_strcmp(name, SDL_HINT_ANDROID_ALLOW_RECREATE_ACTIVITY) == 0) {
238
// Special handling for this hint, which needs to persist outside the normal application flow
239
if (env) {
240
Android_SetAllowRecreateActivity(SDL_GetStringBoolean(env, false));
241
} else {
242
Android_SetAllowRecreateActivity(false);
243
}
244
}
245
#endif // SDL_PLATFORM_ANDROID
246
}
247
248
void SDL_ResetHints(void)
249
{
250
SDL_EnumerateProperties(GetHintProperties(false), ResetHintsCallback, NULL);
251
}
252
253
bool SDL_SetHint(const char *name, const char *value)
254
{
255
return SDL_SetHintWithPriority(name, value, SDL_HINT_NORMAL);
256
}
257
258
const char *SDL_GetHint(const char *name)
259
{
260
if (!name) {
261
return NULL;
262
}
263
264
const char *result = GetHintEnvironmentVariable(name);
265
266
const SDL_PropertiesID hints = GetHintProperties(false);
267
if (hints) {
268
SDL_LockProperties(hints);
269
270
SDL_Hint *hint = (SDL_Hint *)SDL_GetPointerProperty(hints, name, NULL);
271
if (hint) {
272
if (!result || hint->priority == SDL_HINT_OVERRIDE) {
273
result = SDL_GetPersistentString(hint->value);
274
}
275
}
276
277
SDL_UnlockProperties(hints);
278
}
279
280
return result;
281
}
282
283
int SDL_GetStringInteger(const char *value, int default_value)
284
{
285
if (!value || !*value) {
286
return default_value;
287
}
288
if (SDL_strcasecmp(value, "false") == 0) {
289
return 0;
290
}
291
if (SDL_strcasecmp(value, "true") == 0) {
292
return 1;
293
}
294
if (*value == '-' || SDL_isdigit(*value)) {
295
return SDL_atoi(value);
296
}
297
return default_value;
298
}
299
300
bool SDL_GetStringBoolean(const char *value, bool default_value)
301
{
302
if (!value || !*value) {
303
return default_value;
304
}
305
if (*value == '0' || SDL_strcasecmp(value, "false") == 0) {
306
return false;
307
}
308
return true;
309
}
310
311
bool SDL_GetHintBoolean(const char *name, bool default_value)
312
{
313
const char *hint = SDL_GetHint(name);
314
return SDL_GetStringBoolean(hint, default_value);
315
}
316
317
bool SDL_AddHintCallback(const char *name, SDL_HintCallback callback, void *userdata)
318
{
319
if (!name || !*name) {
320
return SDL_InvalidParamError("name");
321
} else if (!callback) {
322
return SDL_InvalidParamError("callback");
323
}
324
325
const SDL_PropertiesID hints = GetHintProperties(true);
326
if (!hints) {
327
return false;
328
}
329
330
SDL_HintWatch *entry = (SDL_HintWatch *)SDL_malloc(sizeof(*entry));
331
if (!entry) {
332
return false;
333
}
334
entry->callback = callback;
335
entry->userdata = userdata;
336
337
bool result = false;
338
339
SDL_LockProperties(hints);
340
341
SDL_RemoveHintCallback(name, callback, userdata);
342
343
SDL_Hint *hint = (SDL_Hint *)SDL_GetPointerProperty(hints, name, NULL);
344
if (hint) {
345
result = true;
346
} else { // Need to add a hint entry for this watcher
347
hint = (SDL_Hint *)SDL_malloc(sizeof(*hint));
348
if (!hint) {
349
SDL_free(entry);
350
SDL_UnlockProperties(hints);
351
return false;
352
} else {
353
hint->value = NULL;
354
hint->priority = SDL_HINT_DEFAULT;
355
hint->callbacks = NULL;
356
result = SDL_SetPointerPropertyWithCleanup(hints, name, hint, CleanupHintProperty, NULL);
357
}
358
}
359
360
// Add it to the callbacks for this hint
361
entry->next = hint->callbacks;
362
hint->callbacks = entry;
363
364
// Now call it with the current value
365
const char *value = SDL_GetHint(name);
366
callback(userdata, name, value, value);
367
368
SDL_UnlockProperties(hints);
369
370
return result;
371
}
372
373
void SDL_RemoveHintCallback(const char *name, SDL_HintCallback callback, void *userdata)
374
{
375
if (!name || !*name) {
376
return;
377
}
378
379
const SDL_PropertiesID hints = GetHintProperties(false);
380
if (!hints) {
381
return;
382
}
383
384
SDL_LockProperties(hints);
385
SDL_Hint *hint = (SDL_Hint *)SDL_GetPointerProperty(hints, name, NULL);
386
if (hint) {
387
SDL_HintWatch *prev = NULL;
388
for (SDL_HintWatch *entry = hint->callbacks; entry; entry = entry->next) {
389
if ((callback == entry->callback) && (userdata == entry->userdata)) {
390
if (prev) {
391
prev->next = entry->next;
392
} else {
393
hint->callbacks = entry->next;
394
}
395
SDL_free(entry);
396
break;
397
}
398
prev = entry;
399
}
400
}
401
SDL_UnlockProperties(hints);
402
}
403
404
405