1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 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#include "SDL_properties_c.h"
25
26
27typedef struct
28{
29 SDL_PropertyType type;
30
31 union {
32 void *pointer_value;
33 char *string_value;
34 Sint64 number_value;
35 float float_value;
36 bool boolean_value;
37 } value;
38
39 char *string_storage;
40
41 SDL_CleanupPropertyCallback cleanup;
42 void *userdata;
43} SDL_Property;
44
45typedef struct
46{
47 SDL_HashTable *props;
48 SDL_Mutex *lock;
49} SDL_Properties;
50
51static SDL_InitState SDL_properties_init;
52static SDL_HashTable *SDL_properties;
53static SDL_AtomicU32 SDL_last_properties_id;
54static SDL_AtomicU32 SDL_global_properties;
55
56
57static void SDL_FreePropertyWithCleanup(const void *key, const void *value, void *data, bool cleanup)
58{
59 SDL_Property *property = (SDL_Property *)value;
60 if (property) {
61 switch (property->type) {
62 case SDL_PROPERTY_TYPE_POINTER:
63 if (property->cleanup && cleanup) {
64 property->cleanup(property->userdata, property->value.pointer_value);
65 }
66 break;
67 case SDL_PROPERTY_TYPE_STRING:
68 SDL_free(property->value.string_value);
69 break;
70 default:
71 break;
72 }
73 SDL_free(property->string_storage);
74 }
75 SDL_free((void *)key);
76 SDL_free((void *)value);
77}
78
79static void SDLCALL SDL_FreeProperty(void *data, const void *key, const void *value)
80{
81 SDL_FreePropertyWithCleanup(key, value, data, true);
82}
83
84static void SDL_FreeProperties(SDL_Properties *properties)
85{
86 if (properties) {
87 SDL_DestroyHashTable(properties->props);
88 SDL_DestroyMutex(properties->lock);
89 SDL_free(properties);
90 }
91}
92
93bool SDL_InitProperties(void)
94{
95 if (!SDL_ShouldInit(&SDL_properties_init)) {
96 return true;
97 }
98
99 SDL_properties = SDL_CreateHashTable(0, true, SDL_HashID, SDL_KeyMatchID, NULL, NULL);
100 const bool initialized = (SDL_properties != NULL);
101 SDL_SetInitialized(&SDL_properties_init, initialized);
102 return initialized;
103}
104
105static bool SDLCALL FreeOneProperties(void *userdata, const SDL_HashTable *table, const void *key, const void *value)
106{
107 SDL_FreeProperties((SDL_Properties *)value);
108 return true; // keep iterating.
109}
110
111void SDL_QuitProperties(void)
112{
113 if (!SDL_ShouldQuit(&SDL_properties_init)) {
114 return;
115 }
116
117 SDL_PropertiesID props;
118 do {
119 props = SDL_GetAtomicU32(&SDL_global_properties);
120 } while (!SDL_CompareAndSwapAtomicU32(&SDL_global_properties, props, 0));
121
122 if (props) {
123 SDL_DestroyProperties(props);
124 }
125
126 // this can't just DestroyHashTable with SDL_FreeProperties as the destructor, because
127 // other destructors under this might cause use to attempt a recursive lock on SDL_properties,
128 // which isn't allowed with rwlocks. So manually iterate and free everything.
129 SDL_HashTable *properties = SDL_properties;
130 SDL_properties = NULL;
131 SDL_IterateHashTable(properties, FreeOneProperties, NULL);
132 SDL_DestroyHashTable(properties);
133
134 SDL_SetInitialized(&SDL_properties_init, false);
135}
136
137static bool SDL_CheckInitProperties(void)
138{
139 return SDL_InitProperties();
140}
141
142SDL_PropertiesID SDL_GetGlobalProperties(void)
143{
144 SDL_PropertiesID props = SDL_GetAtomicU32(&SDL_global_properties);
145 if (!props) {
146 props = SDL_CreateProperties();
147 if (!SDL_CompareAndSwapAtomicU32(&SDL_global_properties, 0, props)) {
148 // Somebody else created global properties before us, just use those
149 SDL_DestroyProperties(props);
150 props = SDL_GetAtomicU32(&SDL_global_properties);
151 }
152 }
153 return props;
154}
155
156SDL_PropertiesID SDL_CreateProperties(void)
157{
158 if (!SDL_CheckInitProperties()) {
159 return 0;
160 }
161
162 SDL_Properties *properties = (SDL_Properties *)SDL_calloc(1, sizeof(*properties));
163 if (!properties) {
164 return 0;
165 }
166
167 properties->lock = SDL_CreateMutex();
168 if (!properties->lock) {
169 SDL_free(properties);
170 return 0;
171 }
172
173 properties->props = SDL_CreateHashTable(0, false, SDL_HashString, SDL_KeyMatchString, SDL_FreeProperty, NULL);
174 if (!properties->props) {
175 SDL_DestroyMutex(properties->lock);
176 SDL_free(properties);
177 return 0;
178 }
179
180 SDL_PropertiesID props = 0;
181 while (true) {
182 props = (SDL_GetAtomicU32(&SDL_last_properties_id) + 1);
183 if (props == 0) {
184 continue;
185 } else if (SDL_CompareAndSwapAtomicU32(&SDL_last_properties_id, props - 1, props)) {
186 break;
187 }
188 }
189
190 SDL_assert(!SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, NULL)); // should NOT be in the hash table already.
191
192 if (!SDL_InsertIntoHashTable(SDL_properties, (const void *)(uintptr_t)props, properties, false)) {
193 SDL_FreeProperties(properties);
194 return 0;
195 }
196
197 return props; // All done!
198}
199
200typedef struct CopyOnePropertyData
201{
202 SDL_Properties *dst_properties;
203 bool result;
204} CopyOnePropertyData;
205
206static bool SDLCALL CopyOneProperty(void *userdata, const SDL_HashTable *table, const void *key, const void *value)
207{
208 const SDL_Property *src_property = (const SDL_Property *)value;
209 if (src_property->cleanup) {
210 // Can't copy properties with cleanup functions, we don't know how to duplicate the data
211 return true; // keep iterating.
212 }
213
214 CopyOnePropertyData *data = (CopyOnePropertyData *) userdata;
215 SDL_Properties *dst_properties = data->dst_properties;
216 const char *src_name = (const char *)key;
217 SDL_Property *dst_property;
218
219 char *dst_name = SDL_strdup(src_name);
220 if (!dst_name) {
221 data->result = false;
222 return true; // keep iterating (I guess...?)
223 }
224
225 dst_property = (SDL_Property *)SDL_malloc(sizeof(*dst_property));
226 if (!dst_property) {
227 SDL_free(dst_name);
228 data->result = false;
229 return true; // keep iterating (I guess...?)
230 }
231
232 SDL_copyp(dst_property, src_property);
233 if (src_property->type == SDL_PROPERTY_TYPE_STRING) {
234 dst_property->value.string_value = SDL_strdup(src_property->value.string_value);
235 if (!dst_property->value.string_value) {
236 SDL_free(dst_name);
237 SDL_free(dst_property);
238 data->result = false;
239 return true; // keep iterating (I guess...?)
240 }
241 }
242
243 if (!SDL_InsertIntoHashTable(dst_properties->props, dst_name, dst_property, true)) {
244 SDL_FreePropertyWithCleanup(dst_name, dst_property, NULL, false);
245 data->result = false;
246 }
247
248 return true; // keep iterating.
249}
250
251bool SDL_CopyProperties(SDL_PropertiesID src, SDL_PropertiesID dst)
252{
253 if (!src) {
254 return SDL_InvalidParamError("src");
255 }
256 if (!dst) {
257 return SDL_InvalidParamError("dst");
258 }
259
260 SDL_Properties *src_properties = NULL;
261 SDL_Properties *dst_properties = NULL;
262
263 SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)src, (const void **)&src_properties);
264 if (!src_properties) {
265 return SDL_InvalidParamError("src");
266 }
267 SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)dst, (const void **)&dst_properties);
268 if (!dst_properties) {
269 return SDL_InvalidParamError("dst");
270 }
271
272 bool result = true;
273 SDL_LockMutex(src_properties->lock);
274 SDL_LockMutex(dst_properties->lock);
275 {
276 CopyOnePropertyData data = { dst_properties, true };
277 SDL_IterateHashTable(src_properties->props, CopyOneProperty, &data);
278 result = data.result;
279 }
280 SDL_UnlockMutex(dst_properties->lock);
281 SDL_UnlockMutex(src_properties->lock);
282
283 return result;
284}
285
286bool SDL_LockProperties(SDL_PropertiesID props)
287{
288 SDL_Properties *properties = NULL;
289
290 if (!props) {
291 return SDL_InvalidParamError("props");
292 }
293
294 SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
295 if (!properties) {
296 return SDL_InvalidParamError("props");
297 }
298
299 SDL_LockMutex(properties->lock);
300 return true;
301}
302
303void SDL_UnlockProperties(SDL_PropertiesID props)
304{
305 SDL_Properties *properties = NULL;
306
307 if (!props) {
308 return;
309 }
310
311 SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
312 if (!properties) {
313 return;
314 }
315
316 SDL_UnlockMutex(properties->lock);
317}
318
319static bool SDL_PrivateSetProperty(SDL_PropertiesID props, const char *name, SDL_Property *property)
320{
321 SDL_Properties *properties = NULL;
322 bool result = true;
323
324 if (!props) {
325 SDL_FreePropertyWithCleanup(NULL, property, NULL, true);
326 return SDL_InvalidParamError("props");
327 }
328 if (!name || !*name) {
329 SDL_FreePropertyWithCleanup(NULL, property, NULL, true);
330 return SDL_InvalidParamError("name");
331 }
332
333 SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
334 if (!properties) {
335 SDL_FreePropertyWithCleanup(NULL, property, NULL, true);
336 return SDL_InvalidParamError("props");
337 }
338
339 SDL_LockMutex(properties->lock);
340 {
341 SDL_RemoveFromHashTable(properties->props, name);
342 if (property) {
343 char *key = SDL_strdup(name);
344 if (!key || !SDL_InsertIntoHashTable(properties->props, key, property, false)) {
345 SDL_FreePropertyWithCleanup(key, property, NULL, true);
346 result = false;
347 }
348 }
349 }
350 SDL_UnlockMutex(properties->lock);
351
352 return result;
353}
354
355bool SDL_SetPointerPropertyWithCleanup(SDL_PropertiesID props, const char *name, void *value, SDL_CleanupPropertyCallback cleanup, void *userdata)
356{
357 SDL_Property *property;
358
359 if (!value) {
360 if (cleanup) {
361 cleanup(userdata, value);
362 }
363 return SDL_ClearProperty(props, name);
364 }
365
366 property = (SDL_Property *)SDL_calloc(1, sizeof(*property));
367 if (!property) {
368 if (cleanup) {
369 cleanup(userdata, value);
370 }
371 SDL_FreePropertyWithCleanup(NULL, property, NULL, false);
372 return false;
373 }
374 property->type = SDL_PROPERTY_TYPE_POINTER;
375 property->value.pointer_value = value;
376 property->cleanup = cleanup;
377 property->userdata = userdata;
378 return SDL_PrivateSetProperty(props, name, property);
379}
380
381bool SDL_SetPointerProperty(SDL_PropertiesID props, const char *name, void *value)
382{
383 SDL_Property *property;
384
385 if (!value) {
386 return SDL_ClearProperty(props, name);
387 }
388
389 property = (SDL_Property *)SDL_calloc(1, sizeof(*property));
390 if (!property) {
391 return false;
392 }
393 property->type = SDL_PROPERTY_TYPE_POINTER;
394 property->value.pointer_value = value;
395 return SDL_PrivateSetProperty(props, name, property);
396}
397
398static void SDLCALL CleanupFreeableProperty(void *userdata, void *value)
399{
400 SDL_free(value);
401}
402
403bool SDL_SetFreeableProperty(SDL_PropertiesID props, const char *name, void *value)
404{
405 return SDL_SetPointerPropertyWithCleanup(props, name, value, CleanupFreeableProperty, NULL);
406}
407
408static void SDLCALL CleanupSurface(void *userdata, void *value)
409{
410 SDL_Surface *surface = (SDL_Surface *)value;
411
412 SDL_DestroySurface(surface);
413}
414
415bool SDL_SetSurfaceProperty(SDL_PropertiesID props, const char *name, SDL_Surface *surface)
416{
417 return SDL_SetPointerPropertyWithCleanup(props, name, surface, CleanupSurface, NULL);
418}
419
420bool SDL_SetStringProperty(SDL_PropertiesID props, const char *name, const char *value)
421{
422 SDL_Property *property;
423
424 if (!value) {
425 return SDL_ClearProperty(props, name);
426 }
427
428 property = (SDL_Property *)SDL_calloc(1, sizeof(*property));
429 if (!property) {
430 return false;
431 }
432 property->type = SDL_PROPERTY_TYPE_STRING;
433 property->value.string_value = SDL_strdup(value);
434 if (!property->value.string_value) {
435 SDL_free(property);
436 return false;
437 }
438 return SDL_PrivateSetProperty(props, name, property);
439}
440
441bool SDL_SetNumberProperty(SDL_PropertiesID props, const char *name, Sint64 value)
442{
443 SDL_Property *property = (SDL_Property *)SDL_calloc(1, sizeof(*property));
444 if (!property) {
445 return false;
446 }
447 property->type = SDL_PROPERTY_TYPE_NUMBER;
448 property->value.number_value = value;
449 return SDL_PrivateSetProperty(props, name, property);
450}
451
452bool SDL_SetFloatProperty(SDL_PropertiesID props, const char *name, float value)
453{
454 SDL_Property *property = (SDL_Property *)SDL_calloc(1, sizeof(*property));
455 if (!property) {
456 return false;
457 }
458 property->type = SDL_PROPERTY_TYPE_FLOAT;
459 property->value.float_value = value;
460 return SDL_PrivateSetProperty(props, name, property);
461}
462
463bool SDL_SetBooleanProperty(SDL_PropertiesID props, const char *name, bool value)
464{
465 SDL_Property *property = (SDL_Property *)SDL_calloc(1, sizeof(*property));
466 if (!property) {
467 return false;
468 }
469 property->type = SDL_PROPERTY_TYPE_BOOLEAN;
470 property->value.boolean_value = value ? true : false;
471 return SDL_PrivateSetProperty(props, name, property);
472}
473
474bool SDL_HasProperty(SDL_PropertiesID props, const char *name)
475{
476 return (SDL_GetPropertyType(props, name) != SDL_PROPERTY_TYPE_INVALID);
477}
478
479SDL_PropertyType SDL_GetPropertyType(SDL_PropertiesID props, const char *name)
480{
481 SDL_Properties *properties = NULL;
482 SDL_PropertyType type = SDL_PROPERTY_TYPE_INVALID;
483
484 if (!props) {
485 return SDL_PROPERTY_TYPE_INVALID;
486 }
487 if (!name || !*name) {
488 return SDL_PROPERTY_TYPE_INVALID;
489 }
490
491 SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
492 if (!properties) {
493 return SDL_PROPERTY_TYPE_INVALID;
494 }
495
496 SDL_LockMutex(properties->lock);
497 {
498 SDL_Property *property = NULL;
499 if (SDL_FindInHashTable(properties->props, name, (const void **)&property)) {
500 type = property->type;
501 }
502 }
503 SDL_UnlockMutex(properties->lock);
504
505 return type;
506}
507
508void *SDL_GetPointerProperty(SDL_PropertiesID props, const char *name, void *default_value)
509{
510 SDL_Properties *properties = NULL;
511 void *value = default_value;
512
513 if (!props) {
514 return value;
515 }
516 if (!name || !*name) {
517 return value;
518 }
519
520 SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
521 if (!properties) {
522 return value;
523 }
524
525 // Note that taking the lock here only guarantees that we won't read the
526 // hashtable while it's being modified. The value itself can easily be
527 // freed from another thread after it is returned here.
528 SDL_LockMutex(properties->lock);
529 {
530 SDL_Property *property = NULL;
531 if (SDL_FindInHashTable(properties->props, name, (const void **)&property)) {
532 if (property->type == SDL_PROPERTY_TYPE_POINTER) {
533 value = property->value.pointer_value;
534 }
535 }
536 }
537 SDL_UnlockMutex(properties->lock);
538
539 return value;
540}
541
542const char *SDL_GetStringProperty(SDL_PropertiesID props, const char *name, const char *default_value)
543{
544 SDL_Properties *properties = NULL;
545 const char *value = default_value;
546
547 if (!props) {
548 return value;
549 }
550 if (!name || !*name) {
551 return value;
552 }
553
554 SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
555 if (!properties) {
556 return value;
557 }
558
559 SDL_LockMutex(properties->lock);
560 {
561 SDL_Property *property = NULL;
562 if (SDL_FindInHashTable(properties->props, name, (const void **)&property)) {
563 switch (property->type) {
564 case SDL_PROPERTY_TYPE_STRING:
565 value = property->value.string_value;
566 break;
567 case SDL_PROPERTY_TYPE_NUMBER:
568 if (property->string_storage) {
569 value = property->string_storage;
570 } else {
571 SDL_asprintf(&property->string_storage, "%" SDL_PRIs64, property->value.number_value);
572 if (property->string_storage) {
573 value = property->string_storage;
574 }
575 }
576 break;
577 case SDL_PROPERTY_TYPE_FLOAT:
578 if (property->string_storage) {
579 value = property->string_storage;
580 } else {
581 SDL_asprintf(&property->string_storage, "%f", property->value.float_value);
582 if (property->string_storage) {
583 value = property->string_storage;
584 }
585 }
586 break;
587 case SDL_PROPERTY_TYPE_BOOLEAN:
588 value = property->value.boolean_value ? "true" : "false";
589 break;
590 default:
591 break;
592 }
593 }
594 }
595 SDL_UnlockMutex(properties->lock);
596
597 return value;
598}
599
600Sint64 SDL_GetNumberProperty(SDL_PropertiesID props, const char *name, Sint64 default_value)
601{
602 SDL_Properties *properties = NULL;
603 Sint64 value = default_value;
604
605 if (!props) {
606 return value;
607 }
608 if (!name || !*name) {
609 return value;
610 }
611
612 SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
613 if (!properties) {
614 return value;
615 }
616
617 SDL_LockMutex(properties->lock);
618 {
619 SDL_Property *property = NULL;
620 if (SDL_FindInHashTable(properties->props, name, (const void **)&property)) {
621 switch (property->type) {
622 case SDL_PROPERTY_TYPE_STRING:
623 value = (Sint64)SDL_strtoll(property->value.string_value, NULL, 0);
624 break;
625 case SDL_PROPERTY_TYPE_NUMBER:
626 value = property->value.number_value;
627 break;
628 case SDL_PROPERTY_TYPE_FLOAT:
629 value = (Sint64)SDL_round((double)property->value.float_value);
630 break;
631 case SDL_PROPERTY_TYPE_BOOLEAN:
632 value = property->value.boolean_value;
633 break;
634 default:
635 break;
636 }
637 }
638 }
639 SDL_UnlockMutex(properties->lock);
640
641 return value;
642}
643
644float SDL_GetFloatProperty(SDL_PropertiesID props, const char *name, float default_value)
645{
646 SDL_Properties *properties = NULL;
647 float value = default_value;
648
649 if (!props) {
650 return value;
651 }
652 if (!name || !*name) {
653 return value;
654 }
655
656 SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
657 if (!properties) {
658 return value;
659 }
660
661 SDL_LockMutex(properties->lock);
662 {
663 SDL_Property *property = NULL;
664 if (SDL_FindInHashTable(properties->props, name, (const void **)&property)) {
665 switch (property->type) {
666 case SDL_PROPERTY_TYPE_STRING:
667 value = (float)SDL_atof(property->value.string_value);
668 break;
669 case SDL_PROPERTY_TYPE_NUMBER:
670 value = (float)property->value.number_value;
671 break;
672 case SDL_PROPERTY_TYPE_FLOAT:
673 value = property->value.float_value;
674 break;
675 case SDL_PROPERTY_TYPE_BOOLEAN:
676 value = (float)property->value.boolean_value;
677 break;
678 default:
679 break;
680 }
681 }
682 }
683 SDL_UnlockMutex(properties->lock);
684
685 return value;
686}
687
688bool SDL_GetBooleanProperty(SDL_PropertiesID props, const char *name, bool default_value)
689{
690 SDL_Properties *properties = NULL;
691 bool value = default_value ? true : false;
692
693 if (!props) {
694 return value;
695 }
696 if (!name || !*name) {
697 return value;
698 }
699
700 SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
701 if (!properties) {
702 return value;
703 }
704
705 SDL_LockMutex(properties->lock);
706 {
707 SDL_Property *property = NULL;
708 if (SDL_FindInHashTable(properties->props, name, (const void **)&property)) {
709 switch (property->type) {
710 case SDL_PROPERTY_TYPE_STRING:
711 value = SDL_GetStringBoolean(property->value.string_value, default_value);
712 break;
713 case SDL_PROPERTY_TYPE_NUMBER:
714 value = (property->value.number_value != 0);
715 break;
716 case SDL_PROPERTY_TYPE_FLOAT:
717 value = (property->value.float_value != 0.0f);
718 break;
719 case SDL_PROPERTY_TYPE_BOOLEAN:
720 value = property->value.boolean_value;
721 break;
722 default:
723 break;
724 }
725 }
726 }
727 SDL_UnlockMutex(properties->lock);
728
729 return value;
730}
731
732bool SDL_ClearProperty(SDL_PropertiesID props, const char *name)
733{
734 return SDL_PrivateSetProperty(props, name, NULL);
735}
736
737typedef struct EnumerateOnePropertyData
738{
739 SDL_EnumeratePropertiesCallback callback;
740 void *userdata;
741 SDL_PropertiesID props;
742} EnumerateOnePropertyData;
743
744
745static bool SDLCALL EnumerateOneProperty(void *userdata, const SDL_HashTable *table, const void *key, const void *value)
746{
747 (void) table;
748 (void) value;
749 const EnumerateOnePropertyData *data = (const EnumerateOnePropertyData *) userdata;
750 data->callback(data->userdata, data->props, (const char *)key);
751 return true; // keep iterating.
752}
753
754bool SDL_EnumerateProperties(SDL_PropertiesID props, SDL_EnumeratePropertiesCallback callback, void *userdata)
755{
756 SDL_Properties *properties = NULL;
757
758 if (!props) {
759 return SDL_InvalidParamError("props");
760 }
761 if (!callback) {
762 return SDL_InvalidParamError("callback");
763 }
764
765 SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
766 if (!properties) {
767 return SDL_InvalidParamError("props");
768 }
769
770 SDL_LockMutex(properties->lock);
771 {
772 EnumerateOnePropertyData data = { callback, userdata, props };
773 SDL_IterateHashTable(properties->props, EnumerateOneProperty, &data);
774 }
775 SDL_UnlockMutex(properties->lock);
776
777 return true;
778}
779
780static void SDLCALL SDL_DumpPropertiesCallback(void *userdata, SDL_PropertiesID props, const char *name)
781{
782 switch (SDL_GetPropertyType(props, name)) {
783 case SDL_PROPERTY_TYPE_POINTER:
784 SDL_Log("%s: %p", name, SDL_GetPointerProperty(props, name, NULL));
785 break;
786 case SDL_PROPERTY_TYPE_STRING:
787 SDL_Log("%s: \"%s\"", name, SDL_GetStringProperty(props, name, ""));
788 break;
789 case SDL_PROPERTY_TYPE_NUMBER:
790 {
791 Sint64 value = SDL_GetNumberProperty(props, name, 0);
792 SDL_Log("%s: %" SDL_PRIs64 " (%" SDL_PRIx64 ")", name, value, value);
793 }
794 break;
795 case SDL_PROPERTY_TYPE_FLOAT:
796 SDL_Log("%s: %g", name, SDL_GetFloatProperty(props, name, 0.0f));
797 break;
798 case SDL_PROPERTY_TYPE_BOOLEAN:
799 SDL_Log("%s: %s", name, SDL_GetBooleanProperty(props, name, false) ? "true" : "false");
800 break;
801 default:
802 SDL_Log("%s UNKNOWN TYPE", name);
803 break;
804 }
805}
806
807bool SDL_DumpProperties(SDL_PropertiesID props)
808{
809 return SDL_EnumerateProperties(props, SDL_DumpPropertiesCallback, NULL);
810}
811
812void SDL_DestroyProperties(SDL_PropertiesID props)
813{
814 if (props) {
815 // this can't just use RemoveFromHashTable with SDL_FreeProperties as the destructor, because
816 // other destructors under this might cause use to attempt a recursive lock on SDL_properties,
817 // which isn't allowed with rwlocks. So manually look it up and remove/free it.
818 SDL_Properties *properties = NULL;
819 if (SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties)) {
820 SDL_FreeProperties(properties);
821 SDL_RemoveFromHashTable(SDL_properties, (const void *)(uintptr_t)props);
822 }
823 }
824}
825