Atlas - SDL_utils.c

Home / ext / SDL / src Lines: 1 | Size: 17677 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 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#if defined(HAVE_GETHOSTNAME) && !defined(SDL_PLATFORM_WINDOWS) 24#include <unistd.h> 25#endif 26 27#include "joystick/SDL_joystick_c.h" // For SDL_GetGamepadTypeFromVIDPID() 28 29#ifdef SDL_PLATFORM_EMSCRIPTEN 30#include <emscripten.h> 31 32EMSCRIPTEN_KEEPALIVE void Emscripten_force_free(void *ptr) 33{ 34 free(ptr); // This should NOT be SDL_free() 35} 36#endif 37 38// Common utility functions that aren't in the public API 39 40int SDL_powerof2(int x) 41{ 42 int value; 43 44 if (x <= 0) { 45 // Return some sane value - we shouldn't hit this in our use cases 46 return 1; 47 } 48 49 // This trick works for 32-bit values 50 { 51 SDL_COMPILE_TIME_ASSERT(SDL_powerof2, sizeof(x) == sizeof(Uint32)); 52 } 53 value = x; 54 value -= 1; 55 value |= value >> 1; 56 value |= value >> 2; 57 value |= value >> 4; 58 value |= value >> 8; 59 value |= value >> 16; 60 value += 1; 61 62 return value; 63} 64 65Uint32 SDL_CalculateGCD(Uint32 a, Uint32 b) 66{ 67 if (b == 0) { 68 return a; 69 } 70 return SDL_CalculateGCD(b, (a % b)); 71} 72 73// Algorithm adapted with thanks from John Cook's blog post: 74// http://www.johndcook.com/blog/2010/10/20/best-rational-approximation 75void SDL_CalculateFraction(float x, int *numerator, int *denominator) 76{ 77 const int N = 1000; 78 int a = 0, b = 1; 79 int c = 1, d = 0; 80 81 while (b <= N && d <= N) { 82 float mediant = (float)(a + c) / (b + d); 83 if (x == mediant) { 84 if (b + d <= N) { 85 *numerator = a + c; 86 *denominator = b + d; 87 } else if (d > b) { 88 *numerator = c; 89 *denominator = d; 90 } else { 91 *numerator = a; 92 *denominator = b; 93 } 94 return; 95 } else if (x > mediant) { 96 a = a + c; 97 b = b + d; 98 } else { 99 c = a + c; 100 d = b + d; 101 } 102 } 103 if (b > N) { 104 *numerator = c; 105 *denominator = d; 106 } else { 107 *numerator = a; 108 *denominator = b; 109 } 110} 111 112bool SDL_startswith(const char *string, const char *prefix) 113{ 114 if (SDL_strncmp(string, prefix, SDL_strlen(prefix)) == 0) { 115 return true; 116 } 117 return false; 118} 119 120bool SDL_endswith(const char *string, const char *suffix) 121{ 122 size_t string_length = string ? SDL_strlen(string) : 0; 123 size_t suffix_length = suffix ? SDL_strlen(suffix) : 0; 124 125 if (suffix_length > 0 && suffix_length <= string_length) { 126 if (SDL_memcmp(string + string_length - suffix_length, suffix, suffix_length) == 0) { 127 return true; 128 } 129 } 130 return false; 131} 132 133SDL_COMPILE_TIME_ASSERT(sizeof_object_id, sizeof(int) == sizeof(Uint32)); 134 135Uint32 SDL_GetNextObjectID(void) 136{ 137 static SDL_AtomicInt last_id; 138 139 Uint32 id = (Uint32)SDL_AtomicIncRef(&last_id) + 1; 140 if (id == 0) { 141 id = (Uint32)SDL_AtomicIncRef(&last_id) + 1; 142 } 143 return id; 144} 145 146static SDL_InitState SDL_objects_init; 147static SDL_HashTable *SDL_objects; 148bool SDL_object_validation = true; 149 150static void SDLCALL SDL_InvalidParamChecksChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 151{ 152 bool validation_enabled = true; 153 154#ifndef OBJECT_VALIDATION_REQUIRED 155 if (hint) { 156 switch (*hint) { 157 case '0': 158 case '1': 159 validation_enabled = false; 160 break; 161 case '2': 162 validation_enabled = true; 163 break; 164 default: 165 break; 166 } 167 } 168#endif // !OBJECT_VALIDATION_REQUIRED 169 170 SDL_object_validation = validation_enabled; 171} 172 173static Uint32 SDLCALL SDL_HashObject(void *unused, const void *key) 174{ 175 return (Uint32)(uintptr_t)key; 176} 177 178static bool SDL_KeyMatchObject(void *unused, const void *a, const void *b) 179{ 180 return (a == b); 181} 182 183void SDL_SetObjectValid(void *object, SDL_ObjectType type, bool valid) 184{ 185 SDL_assert(object != NULL); 186 187 if (SDL_ShouldInit(&SDL_objects_init)) { 188 SDL_objects = SDL_CreateHashTable(0, true, SDL_HashObject, SDL_KeyMatchObject, NULL, NULL); 189 const bool initialized = (SDL_objects != NULL); 190 SDL_SetInitialized(&SDL_objects_init, initialized); 191 if (!initialized) { 192 return; 193 } 194 SDL_AddHintCallback(SDL_HINT_INVALID_PARAM_CHECKS, SDL_InvalidParamChecksChanged, NULL); 195 } 196 197 if (valid) { 198 SDL_InsertIntoHashTable(SDL_objects, object, (void *)(uintptr_t)type, true); 199 } else { 200 SDL_RemoveFromHashTable(SDL_objects, object); 201 } 202} 203 204bool SDL_FindObject(void *object, SDL_ObjectType type) 205{ 206 const void *object_type; 207 if (!SDL_FindInHashTable(SDL_objects, object, &object_type)) { 208 return false; 209 } 210 211 return (((SDL_ObjectType)(uintptr_t)object_type) == type); 212} 213 214typedef struct GetOneObjectData 215{ 216 const SDL_ObjectType type; 217 void **objects; 218 const int count; 219 int num_objects; 220} GetOneObjectData; 221 222static bool SDLCALL GetOneObject(void *userdata, const SDL_HashTable *table, const void *object, const void *object_type) 223{ 224 GetOneObjectData *data = (GetOneObjectData *) userdata; 225 if ((SDL_ObjectType)(uintptr_t)object_type == data->type) { 226 if (data->num_objects < data->count) { 227 data->objects[data->num_objects] = (void *)object; 228 } 229 ++data->num_objects; 230 } 231 return true; // keep iterating. 232} 233 234 235int SDL_GetObjects(SDL_ObjectType type, void **objects, int count) 236{ 237 GetOneObjectData data = { type, objects, count, 0 }; 238 SDL_IterateHashTable(SDL_objects, GetOneObject, &data); 239 return data.num_objects; 240} 241 242static bool SDLCALL LogOneLeakedObject(void *userdata, const SDL_HashTable *table, const void *object, const void *object_type) 243{ 244 const char *type = "unknown object"; 245 switch ((SDL_ObjectType)(uintptr_t)object_type) { 246 #define SDLOBJTYPECASE(typ, name) case SDL_OBJECT_TYPE_##typ: type = name; break 247 SDLOBJTYPECASE(WINDOW, "SDL_Window"); 248 SDLOBJTYPECASE(RENDERER, "SDL_Renderer"); 249 SDLOBJTYPECASE(TEXTURE, "SDL_Texture"); 250 SDLOBJTYPECASE(JOYSTICK, "SDL_Joystick"); 251 SDLOBJTYPECASE(GAMEPAD, "SDL_Gamepad"); 252 SDLOBJTYPECASE(HAPTIC, "SDL_Haptic"); 253 SDLOBJTYPECASE(SENSOR, "SDL_Sensor"); 254 SDLOBJTYPECASE(HIDAPI_DEVICE, "hidapi device"); 255 SDLOBJTYPECASE(HIDAPI_JOYSTICK, "hidapi joystick"); 256 SDLOBJTYPECASE(THREAD, "thread"); 257 SDLOBJTYPECASE(TRAY, "SDL_Tray"); 258 #undef SDLOBJTYPECASE 259 default: break; 260 } 261 SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Leaked %s (%p)", type, object); 262 return true; // keep iterating. 263} 264 265void SDL_SetObjectsInvalid(void) 266{ 267 if (SDL_ShouldQuit(&SDL_objects_init)) { 268 // Log any leaked objects 269 SDL_IterateHashTable(SDL_objects, LogOneLeakedObject, NULL); 270 SDL_DestroyHashTable(SDL_objects); 271 SDL_objects = NULL; 272 SDL_SetInitialized(&SDL_objects_init, false); 273 SDL_RemoveHintCallback(SDL_HINT_INVALID_PARAM_CHECKS, SDL_InvalidParamChecksChanged, NULL); 274 } 275} 276 277static int SDL_URIDecode(const char *src, char *dst, int len) 278{ 279 int ri, wi, di; 280 char decode = '\0'; 281 if (!src || !dst || len < 0) { 282 return -1; 283 } 284 if (len == 0) { 285 len = (int)SDL_strlen(src); 286 } 287 for (ri = 0, wi = 0, di = 0; ri < len && wi < len; ri += 1) { 288 if (di == 0) { 289 // start decoding 290 if (src[ri] == '%') { 291 decode = '\0'; 292 di += 1; 293 continue; 294 } 295 // normal write 296 dst[wi] = src[ri]; 297 wi += 1; 298 } else if (di == 1 || di == 2) { 299 char off = '\0'; 300 char isa = src[ri] >= 'a' && src[ri] <= 'f'; 301 char isA = src[ri] >= 'A' && src[ri] <= 'F'; 302 char isn = src[ri] >= '0' && src[ri] <= '9'; 303 if (!(isa || isA || isn)) { 304 // not a hexadecimal 305 int sri; 306 for (sri = ri - di; sri <= ri; sri += 1) { 307 dst[wi] = src[sri]; 308 wi += 1; 309 } 310 di = 0; 311 continue; 312 } 313 // itsy bitsy magicsy 314 if (isn) { 315 off = 0 - '0'; 316 } else if (isa) { 317 off = 10 - 'a'; 318 } else if (isA) { 319 off = 10 - 'A'; 320 } 321 decode |= (src[ri] + off) << (2 - di) * 4; 322 if (di == 2) { 323 dst[wi] = decode; 324 wi += 1; 325 di = 0; 326 } else { 327 di += 1; 328 } 329 } 330 } 331 dst[wi] = '\0'; 332 return wi; 333} 334 335int SDL_URIToLocal(const char *src, char *dst) 336{ 337 if (SDL_memcmp(src, "file:/", 6) == 0) { 338 src += 6; // local file? 339 } else if (SDL_strstr(src, ":/") != NULL) { 340 return -1; // wrong scheme 341 } 342 343 bool local = src[0] != '/' || (src[0] != '\0' && src[1] == '/'); 344 345 // Check the hostname, if present. RFC 3986 states that the hostname component of a URI is not case-sensitive. 346 if (!local && src[0] == '/' && src[2] != '/') { 347 char *hostname_end = SDL_strchr(src + 1, '/'); 348 if (hostname_end) { 349 const size_t src_len = hostname_end - (src + 1); 350 size_t hostname_len; 351 352#if defined(HAVE_GETHOSTNAME) && !defined(SDL_PLATFORM_WINDOWS) 353 char hostname[257]; 354 if (gethostname(hostname, 255) == 0) { 355 hostname[256] = '\0'; 356 hostname_len = SDL_strlen(hostname); 357 if (hostname_len == src_len && SDL_strncasecmp(src + 1, hostname, src_len) == 0) { 358 src = hostname_end + 1; 359 local = true; 360 } 361 } 362#endif 363 364 if (!local) { 365 static const char *localhost = "localhost"; 366 hostname_len = SDL_strlen(localhost); 367 if (hostname_len == src_len && SDL_strncasecmp(src + 1, localhost, src_len) == 0) { 368 src = hostname_end + 1; 369 local = true; 370 } 371 } 372 } 373 } 374 375 if (local) { 376 // Convert URI escape sequences to real characters 377 if (src[0] == '/') { 378 src++; 379 } else { 380 src--; 381 } 382 return SDL_URIDecode(src, dst, 0); 383 } 384 return -1; 385} 386 387// This is a set of per-thread persistent strings that we can return from the SDL API. 388// This is used for short strings that might persist past the lifetime of the object 389// they are related to. 390 391static SDL_TLSID SDL_string_storage; 392 393static void SDL_FreePersistentStrings( void *value ) 394{ 395 SDL_HashTable *strings = (SDL_HashTable *)value; 396 SDL_DestroyHashTable(strings); 397} 398 399const char *SDL_GetPersistentString(const char *string) 400{ 401 if (!string) { 402 return NULL; 403 } 404 if (!*string) { 405 return ""; 406 } 407 408 SDL_HashTable *strings = (SDL_HashTable *)SDL_GetTLS(&SDL_string_storage); 409 if (!strings) { 410 strings = SDL_CreateHashTable(0, false, SDL_HashString, SDL_KeyMatchString, SDL_DestroyHashValue, NULL); 411 if (!strings) { 412 return NULL; 413 } 414 415 SDL_SetTLS(&SDL_string_storage, strings, SDL_FreePersistentStrings); 416 } 417 418 const char *result; 419 if (!SDL_FindInHashTable(strings, string, (const void **)&result)) { 420 char *new_string = SDL_strdup(string); 421 if (!new_string) { 422 return NULL; 423 } 424 425 // If the hash table insert fails, at least we can return the string we allocated 426 SDL_InsertIntoHashTable(strings, new_string, new_string, false); 427 result = new_string; 428 } 429 return result; 430} 431 432static int PrefixMatch(const char *a, const char *b) 433{ 434 int matchlen = 0; 435 // Fixes the "HORI HORl Taiko No Tatsujin Drum Controller" 436 if (SDL_strncmp(a, "HORI ", 5) == 0 && SDL_strncmp(b, "HORl ", 5) == 0) { 437 return 5; 438 } 439 while (*a && *b) { 440 if (SDL_tolower((unsigned char)*a++) == SDL_tolower((unsigned char)*b++)) { 441 ++matchlen; 442 } else { 443 break; 444 } 445 } 446 return matchlen; 447} 448 449char *SDL_CreateDeviceName(Uint16 vendor, Uint16 product, const char *vendor_name, const char *product_name, const char *default_name) 450{ 451 static struct 452 { 453 const char *prefix; 454 const char *replacement; 455 } replacements[] = { 456 { "(Standard system devices) ", "" }, 457 { "8BitDo Tech Ltd", "8BitDo" }, 458 { "ASTRO Gaming", "ASTRO" }, 459 { "Bensussen Deutsch & Associates,Inc.(BDA)", "BDA" }, 460 { "Guangzhou Chicken Run Network Technology Co., Ltd.", "GameSir" }, 461 { "HORI CO.,LTD.", "HORI" }, 462 { "HORI CO.,LTD", "HORI" }, 463 { "Mad Catz Inc.", "Mad Catz" }, 464 { "Nintendo Co., Ltd.", "Nintendo" }, 465 { "NVIDIA Corporation ", "" }, 466 { "Performance Designed Products", "PDP" }, 467 { "QANBA USA, LLC", "Qanba" }, 468 { "QANBA USA,LLC", "Qanba" }, 469 { "Unknown ", "" }, 470 }; 471 char *name = NULL; 472 size_t i, len; 473 474 if (!vendor_name) { 475 vendor_name = ""; 476 } 477 if (!product_name) { 478 product_name = ""; 479 } 480 481 while (*vendor_name == ' ') { 482 ++vendor_name; 483 } 484 while (*product_name == ' ') { 485 ++product_name; 486 } 487 488 if (*vendor_name && *product_name) { 489 len = (SDL_strlen(vendor_name) + 1 + SDL_strlen(product_name) + 1); 490 name = (char *)SDL_malloc(len); 491 if (name) { 492 (void)SDL_snprintf(name, len, "%s %s", vendor_name, product_name); 493 } 494 } else if (*product_name) { 495 name = SDL_strdup(product_name); 496 } else if (vendor || product) { 497 // Couldn't find a controller name, try to give it one based on device type 498 switch (SDL_GetGamepadTypeFromVIDPID(vendor, product, NULL, true)) { 499 case SDL_GAMEPAD_TYPE_XBOX360: 500 name = SDL_strdup("Xbox 360 Controller"); 501 break; 502 case SDL_GAMEPAD_TYPE_XBOXONE: 503 name = SDL_strdup("Xbox One Controller"); 504 break; 505 case SDL_GAMEPAD_TYPE_PS3: 506 name = SDL_strdup("PS3 Controller"); 507 break; 508 case SDL_GAMEPAD_TYPE_PS4: 509 name = SDL_strdup("PS4 Controller"); 510 break; 511 case SDL_GAMEPAD_TYPE_PS5: 512 name = SDL_strdup("DualSense Wireless Controller"); 513 break; 514 case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO: 515 name = SDL_strdup("Nintendo Switch Pro Controller"); 516 break; 517 default: 518 len = (6 + 1 + 6 + 1); 519 name = (char *)SDL_malloc(len); 520 if (name) { 521 (void)SDL_snprintf(name, len, "0x%.4x/0x%.4x", vendor, product); 522 } 523 break; 524 } 525 } else if (default_name) { 526 name = SDL_strdup(default_name); 527 } 528 529 if (!name) { 530 return NULL; 531 } 532 533 // Trim trailing whitespace 534 for (len = SDL_strlen(name); (len > 0 && name[len - 1] == ' '); --len) { 535 // continue 536 } 537 name[len] = '\0'; 538 539 // Compress duplicate spaces 540 for (i = 0; i < (len - 1);) { 541 if (name[i] == ' ' && name[i + 1] == ' ') { 542 SDL_memmove(&name[i], &name[i + 1], (len - i)); 543 --len; 544 } else { 545 ++i; 546 } 547 } 548 549 // Perform any manufacturer replacements 550 for (i = 0; i < SDL_arraysize(replacements); ++i) { 551 size_t prefixlen = SDL_strlen(replacements[i].prefix); 552 if (SDL_strncasecmp(name, replacements[i].prefix, prefixlen) == 0) { 553 size_t replacementlen = SDL_strlen(replacements[i].replacement); 554 if (replacementlen <= prefixlen) { 555 SDL_memcpy(name, replacements[i].replacement, replacementlen); 556 SDL_memmove(name + replacementlen, name + prefixlen, (len - prefixlen) + 1); 557 len -= (prefixlen - replacementlen); 558 } else { 559 // FIXME: Need to handle the expand case by reallocating the string 560 } 561 break; 562 } 563 } 564 565 /* Remove duplicate manufacturer or product in the name 566 * e.g. Razer Razer Raiju Tournament Edition Wired 567 */ 568 for (i = 1; i < (len - 1); ++i) { 569 int matchlen = PrefixMatch(name, &name[i]); 570 while (matchlen > 0) { 571 if (name[matchlen] == ' ' || name[matchlen] == '-') { 572 SDL_memmove(name, name + matchlen + 1, len - matchlen); 573 break; 574 } 575 --matchlen; 576 } 577 if (matchlen > 0) { 578 // We matched the manufacturer's name and removed it 579 break; 580 } 581 } 582 583 return name; 584} 585 586 587void SDL_DebugLogBackend(const char *subsystem, const char *backend) 588{ 589 SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "SDL chose %s backend '%s'", subsystem, backend); 590} 591 592
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.