1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2021 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.h" |
24 | #include "SDL_error.h" |
25 | #include "SDL_hints_c.h" |
26 | |
27 | |
28 | /* Assuming there aren't many hints set and they aren't being queried in |
29 | critical performance paths, we'll just use linked lists here. |
30 | */ |
31 | typedef struct SDL_HintWatch { |
32 | SDL_HintCallback callback; |
33 | void *userdata; |
34 | struct SDL_HintWatch *next; |
35 | } SDL_HintWatch; |
36 | |
37 | typedef struct SDL_Hint { |
38 | char *name; |
39 | char *value; |
40 | SDL_HintPriority priority; |
41 | SDL_HintWatch *callbacks; |
42 | struct SDL_Hint *next; |
43 | } SDL_Hint; |
44 | |
45 | static SDL_Hint *SDL_hints; |
46 | |
47 | SDL_bool |
48 | SDL_SetHintWithPriority(const char *name, const char *value, |
49 | SDL_HintPriority priority) |
50 | { |
51 | const char *env; |
52 | SDL_Hint *hint; |
53 | SDL_HintWatch *entry; |
54 | |
55 | if (!name || !value) { |
56 | return SDL_FALSE; |
57 | } |
58 | |
59 | env = SDL_getenv(name); |
60 | if (env && priority < SDL_HINT_OVERRIDE) { |
61 | return SDL_FALSE; |
62 | } |
63 | |
64 | for (hint = SDL_hints; hint; hint = hint->next) { |
65 | if (SDL_strcmp(name, hint->name) == 0) { |
66 | if (priority < hint->priority) { |
67 | return SDL_FALSE; |
68 | } |
69 | if (!hint->value || !value || SDL_strcmp(hint->value, value) != 0) { |
70 | for (entry = hint->callbacks; entry; ) { |
71 | /* Save the next entry in case this one is deleted */ |
72 | SDL_HintWatch *next = entry->next; |
73 | entry->callback(entry->userdata, name, hint->value, value); |
74 | entry = next; |
75 | } |
76 | SDL_free(hint->value); |
77 | hint->value = value ? SDL_strdup(value) : NULL; |
78 | } |
79 | hint->priority = priority; |
80 | return SDL_TRUE; |
81 | } |
82 | } |
83 | |
84 | /* Couldn't find the hint, add a new one */ |
85 | hint = (SDL_Hint *)SDL_malloc(sizeof(*hint)); |
86 | if (!hint) { |
87 | return SDL_FALSE; |
88 | } |
89 | hint->name = SDL_strdup(name); |
90 | hint->value = value ? SDL_strdup(value) : NULL; |
91 | hint->priority = priority; |
92 | hint->callbacks = NULL; |
93 | hint->next = SDL_hints; |
94 | SDL_hints = hint; |
95 | return SDL_TRUE; |
96 | } |
97 | |
98 | SDL_bool |
99 | SDL_SetHint(const char *name, const char *value) |
100 | { |
101 | return SDL_SetHintWithPriority(name, value, SDL_HINT_NORMAL); |
102 | } |
103 | |
104 | const char * |
105 | SDL_GetHint(const char *name) |
106 | { |
107 | const char *env; |
108 | SDL_Hint *hint; |
109 | |
110 | env = SDL_getenv(name); |
111 | for (hint = SDL_hints; hint; hint = hint->next) { |
112 | if (SDL_strcmp(name, hint->name) == 0) { |
113 | if (!env || hint->priority == SDL_HINT_OVERRIDE) { |
114 | return hint->value; |
115 | } |
116 | break; |
117 | } |
118 | } |
119 | return env; |
120 | } |
121 | |
122 | SDL_bool |
123 | SDL_GetStringBoolean(const char *value, SDL_bool default_value) |
124 | { |
125 | if (!value || !*value) { |
126 | return default_value; |
127 | } |
128 | if (*value == '0' || SDL_strcasecmp(value, "false" ) == 0) { |
129 | return SDL_FALSE; |
130 | } |
131 | return SDL_TRUE; |
132 | } |
133 | |
134 | SDL_bool |
135 | SDL_GetHintBoolean(const char *name, SDL_bool default_value) |
136 | { |
137 | const char *hint = SDL_GetHint(name); |
138 | return SDL_GetStringBoolean(hint, default_value); |
139 | } |
140 | |
141 | void |
142 | SDL_AddHintCallback(const char *name, SDL_HintCallback callback, void *userdata) |
143 | { |
144 | SDL_Hint *hint; |
145 | SDL_HintWatch *entry; |
146 | const char *value; |
147 | |
148 | if (!name || !*name) { |
149 | SDL_InvalidParamError("name" ); |
150 | return; |
151 | } |
152 | if (!callback) { |
153 | SDL_InvalidParamError("callback" ); |
154 | return; |
155 | } |
156 | |
157 | SDL_DelHintCallback(name, callback, userdata); |
158 | |
159 | entry = (SDL_HintWatch *)SDL_malloc(sizeof(*entry)); |
160 | if (!entry) { |
161 | SDL_OutOfMemory(); |
162 | return; |
163 | } |
164 | entry->callback = callback; |
165 | entry->userdata = userdata; |
166 | |
167 | for (hint = SDL_hints; hint; hint = hint->next) { |
168 | if (SDL_strcmp(name, hint->name) == 0) { |
169 | break; |
170 | } |
171 | } |
172 | if (!hint) { |
173 | /* Need to add a hint entry for this watcher */ |
174 | hint = (SDL_Hint *)SDL_malloc(sizeof(*hint)); |
175 | if (!hint) { |
176 | SDL_OutOfMemory(); |
177 | SDL_free(entry); |
178 | return; |
179 | } |
180 | hint->name = SDL_strdup(name); |
181 | hint->value = NULL; |
182 | hint->priority = SDL_HINT_DEFAULT; |
183 | hint->callbacks = NULL; |
184 | hint->next = SDL_hints; |
185 | SDL_hints = hint; |
186 | } |
187 | |
188 | /* Add it to the callbacks for this hint */ |
189 | entry->next = hint->callbacks; |
190 | hint->callbacks = entry; |
191 | |
192 | /* Now call it with the current value */ |
193 | value = SDL_GetHint(name); |
194 | callback(userdata, name, value, value); |
195 | } |
196 | |
197 | void |
198 | SDL_DelHintCallback(const char *name, SDL_HintCallback callback, void *userdata) |
199 | { |
200 | SDL_Hint *hint; |
201 | SDL_HintWatch *entry, *prev; |
202 | |
203 | for (hint = SDL_hints; hint; hint = hint->next) { |
204 | if (SDL_strcmp(name, hint->name) == 0) { |
205 | prev = NULL; |
206 | for (entry = hint->callbacks; entry; entry = entry->next) { |
207 | if (callback == entry->callback && userdata == entry->userdata) { |
208 | if (prev) { |
209 | prev->next = entry->next; |
210 | } else { |
211 | hint->callbacks = entry->next; |
212 | } |
213 | SDL_free(entry); |
214 | break; |
215 | } |
216 | prev = entry; |
217 | } |
218 | return; |
219 | } |
220 | } |
221 | } |
222 | |
223 | void SDL_ClearHints(void) |
224 | { |
225 | SDL_Hint *hint; |
226 | SDL_HintWatch *entry; |
227 | |
228 | while (SDL_hints) { |
229 | hint = SDL_hints; |
230 | SDL_hints = hint->next; |
231 | |
232 | SDL_free(hint->name); |
233 | SDL_free(hint->value); |
234 | for (entry = hint->callbacks; entry; ) { |
235 | SDL_HintWatch *freeable = entry; |
236 | entry = entry->next; |
237 | SDL_free(freeable); |
238 | } |
239 | SDL_free(hint); |
240 | } |
241 | } |
242 | |
243 | /* vi: set ts=4 sw=4 expandtab: */ |
244 | |