init.c (14504B)
1 //======================================================================== 2 // GLFW 3.4 - www.glfw.org 3 //------------------------------------------------------------------------ 4 // Copyright (c) 2002-2006 Marcus Geelnard 5 // Copyright (c) 2006-2018 Camilla Löwy <elmindreda@glfw.org> 6 // 7 // This software is provided 'as-is', without any express or implied 8 // warranty. In no event will the authors be held liable for any damages 9 // arising from the use of this software. 10 // 11 // Permission is granted to anyone to use this software for any purpose, 12 // including commercial applications, and to alter it and redistribute it 13 // freely, subject to the following restrictions: 14 // 15 // 1. The origin of this software must not be misrepresented; you must not 16 // claim that you wrote the original software. If you use this software 17 // in a product, an acknowledgment in the product documentation would 18 // be appreciated but is not required. 19 // 20 // 2. Altered source versions must be plainly marked as such, and must not 21 // be misrepresented as being the original software. 22 // 23 // 3. This notice may not be removed or altered from any source 24 // distribution. 25 // 26 //======================================================================== 27 28 #include "internal.h" 29 30 #include <string.h> 31 #include <stdlib.h> 32 #include <stdio.h> 33 #include <stdarg.h> 34 #include <assert.h> 35 36 37 // NOTE: The global variables below comprise all mutable global data in GLFW 38 // Any other mutable global variable is a bug 39 40 // This contains all mutable state shared between compilation units of GLFW 41 // 42 _GLFWlibrary _glfw = { GLFW_FALSE }; 43 44 // These are outside of _glfw so they can be used before initialization and 45 // after termination without special handling when _glfw is cleared to zero 46 // 47 static _GLFWerror _glfwMainThreadError; 48 static GLFWerrorfun _glfwErrorCallback; 49 static GLFWallocator _glfwInitAllocator; 50 static _GLFWinitconfig _glfwInitHints = 51 { 52 .hatButtons = GLFW_TRUE, 53 .angleType = GLFW_ANGLE_PLATFORM_TYPE_NONE, 54 .platformID = GLFW_ANY_PLATFORM, 55 .vulkanLoader = NULL, 56 .ns = 57 { 58 .menubar = GLFW_TRUE, 59 .chdir = GLFW_TRUE 60 }, 61 .x11 = 62 { 63 .xcbVulkanSurface = GLFW_TRUE, 64 }, 65 .wl = 66 { 67 .libdecorMode = GLFW_WAYLAND_PREFER_LIBDECOR 68 }, 69 }; 70 71 // The allocation function used when no custom allocator is set 72 // 73 static void* defaultAllocate(size_t size, void* user) 74 { 75 return malloc(size); 76 } 77 78 // The deallocation function used when no custom allocator is set 79 // 80 static void defaultDeallocate(void* block, void* user) 81 { 82 free(block); 83 } 84 85 // The reallocation function used when no custom allocator is set 86 // 87 static void* defaultReallocate(void* block, size_t size, void* user) 88 { 89 return realloc(block, size); 90 } 91 92 // Terminate the library 93 // 94 static void terminate(void) 95 { 96 int i; 97 98 memset(&_glfw.callbacks, 0, sizeof(_glfw.callbacks)); 99 100 while (_glfw.windowListHead) 101 glfwDestroyWindow((GLFWwindow*) _glfw.windowListHead); 102 103 while (_glfw.cursorListHead) 104 glfwDestroyCursor((GLFWcursor*) _glfw.cursorListHead); 105 106 for (i = 0; i < _glfw.monitorCount; i++) 107 { 108 _GLFWmonitor* monitor = _glfw.monitors[i]; 109 if (monitor->originalRamp.size) 110 _glfw.platform.setGammaRamp(monitor, &monitor->originalRamp); 111 _glfwFreeMonitor(monitor); 112 } 113 114 _glfw_free(_glfw.monitors); 115 _glfw.monitors = NULL; 116 _glfw.monitorCount = 0; 117 118 _glfw_free(_glfw.mappings); 119 _glfw.mappings = NULL; 120 _glfw.mappingCount = 0; 121 122 _glfwTerminateVulkan(); 123 _glfw.platform.terminateJoysticks(); 124 _glfw.platform.terminate(); 125 126 _glfw.initialized = GLFW_FALSE; 127 128 while (_glfw.errorListHead) 129 { 130 _GLFWerror* error = _glfw.errorListHead; 131 _glfw.errorListHead = error->next; 132 _glfw_free(error); 133 } 134 135 _glfwPlatformDestroyTls(&_glfw.contextSlot); 136 _glfwPlatformDestroyTls(&_glfw.errorSlot); 137 _glfwPlatformDestroyMutex(&_glfw.errorLock); 138 139 memset(&_glfw, 0, sizeof(_glfw)); 140 } 141 142 143 ////////////////////////////////////////////////////////////////////////// 144 ////// GLFW internal API ////// 145 ////////////////////////////////////////////////////////////////////////// 146 147 // Encode a Unicode code point to a UTF-8 stream 148 // Based on cutef8 by Jeff Bezanson (Public Domain) 149 // 150 size_t _glfwEncodeUTF8(char* s, uint32_t codepoint) 151 { 152 size_t count = 0; 153 154 if (codepoint < 0x80) 155 s[count++] = (char) codepoint; 156 else if (codepoint < 0x800) 157 { 158 s[count++] = (codepoint >> 6) | 0xc0; 159 s[count++] = (codepoint & 0x3f) | 0x80; 160 } 161 else if (codepoint < 0x10000) 162 { 163 s[count++] = (codepoint >> 12) | 0xe0; 164 s[count++] = ((codepoint >> 6) & 0x3f) | 0x80; 165 s[count++] = (codepoint & 0x3f) | 0x80; 166 } 167 else if (codepoint < 0x110000) 168 { 169 s[count++] = (codepoint >> 18) | 0xf0; 170 s[count++] = ((codepoint >> 12) & 0x3f) | 0x80; 171 s[count++] = ((codepoint >> 6) & 0x3f) | 0x80; 172 s[count++] = (codepoint & 0x3f) | 0x80; 173 } 174 175 return count; 176 } 177 178 // Splits and translates a text/uri-list into separate file paths 179 // NOTE: This function destroys the provided string 180 // 181 char** _glfwParseUriList(char* text, int* count) 182 { 183 const char* prefix = "file://"; 184 char** paths = NULL; 185 char* line; 186 187 *count = 0; 188 189 while ((line = strtok(text, "\r\n"))) 190 { 191 char* path; 192 193 text = NULL; 194 195 if (line[0] == '#') 196 continue; 197 198 if (strncmp(line, prefix, strlen(prefix)) == 0) 199 { 200 line += strlen(prefix); 201 // TODO: Validate hostname 202 while (*line != '/') 203 line++; 204 } 205 206 (*count)++; 207 208 path = _glfw_calloc(strlen(line) + 1, 1); 209 paths = _glfw_realloc(paths, *count * sizeof(char*)); 210 paths[*count - 1] = path; 211 212 while (*line) 213 { 214 if (line[0] == '%' && line[1] && line[2]) 215 { 216 const char digits[3] = { line[1], line[2], '\0' }; 217 *path = (char) strtol(digits, NULL, 16); 218 line += 2; 219 } 220 else 221 *path = *line; 222 223 path++; 224 line++; 225 } 226 } 227 228 return paths; 229 } 230 231 char* _glfw_strdup(const char* source) 232 { 233 const size_t length = strlen(source); 234 char* result = _glfw_calloc(length + 1, 1); 235 strcpy(result, source); 236 return result; 237 } 238 239 int _glfw_min(int a, int b) 240 { 241 return a < b ? a : b; 242 } 243 244 int _glfw_max(int a, int b) 245 { 246 return a > b ? a : b; 247 } 248 249 void* _glfw_calloc(size_t count, size_t size) 250 { 251 if (count && size) 252 { 253 void* block; 254 255 if (count > SIZE_MAX / size) 256 { 257 _glfwInputError(GLFW_INVALID_VALUE, "Allocation size overflow"); 258 return NULL; 259 } 260 261 block = _glfw.allocator.allocate(count * size, _glfw.allocator.user); 262 if (block) 263 return memset(block, 0, count * size); 264 else 265 { 266 _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); 267 return NULL; 268 } 269 } 270 else 271 return NULL; 272 } 273 274 void* _glfw_realloc(void* block, size_t size) 275 { 276 if (block && size) 277 { 278 void* resized = _glfw.allocator.reallocate(block, size, _glfw.allocator.user); 279 if (resized) 280 return resized; 281 else 282 { 283 _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); 284 return NULL; 285 } 286 } 287 else if (block) 288 { 289 _glfw_free(block); 290 return NULL; 291 } 292 else 293 return _glfw_calloc(1, size); 294 } 295 296 void _glfw_free(void* block) 297 { 298 if (block) 299 _glfw.allocator.deallocate(block, _glfw.allocator.user); 300 } 301 302 303 ////////////////////////////////////////////////////////////////////////// 304 ////// GLFW event API ////// 305 ////////////////////////////////////////////////////////////////////////// 306 307 // Notifies shared code of an error 308 // 309 void _glfwInputError(int code, const char* format, ...) 310 { 311 _GLFWerror* error; 312 char description[_GLFW_MESSAGE_SIZE]; 313 314 if (format) 315 { 316 va_list vl; 317 318 va_start(vl, format); 319 vsnprintf(description, sizeof(description), format, vl); 320 va_end(vl); 321 322 description[sizeof(description) - 1] = '\0'; 323 } 324 else 325 { 326 if (code == GLFW_NOT_INITIALIZED) 327 strcpy(description, "The GLFW library is not initialized"); 328 else if (code == GLFW_NO_CURRENT_CONTEXT) 329 strcpy(description, "There is no current context"); 330 else if (code == GLFW_INVALID_ENUM) 331 strcpy(description, "Invalid argument for enum parameter"); 332 else if (code == GLFW_INVALID_VALUE) 333 strcpy(description, "Invalid value for parameter"); 334 else if (code == GLFW_OUT_OF_MEMORY) 335 strcpy(description, "Out of memory"); 336 else if (code == GLFW_API_UNAVAILABLE) 337 strcpy(description, "The requested API is unavailable"); 338 else if (code == GLFW_VERSION_UNAVAILABLE) 339 strcpy(description, "The requested API version is unavailable"); 340 else if (code == GLFW_PLATFORM_ERROR) 341 strcpy(description, "A platform-specific error occurred"); 342 else if (code == GLFW_FORMAT_UNAVAILABLE) 343 strcpy(description, "The requested format is unavailable"); 344 else if (code == GLFW_NO_WINDOW_CONTEXT) 345 strcpy(description, "The specified window has no context"); 346 else if (code == GLFW_CURSOR_UNAVAILABLE) 347 strcpy(description, "The specified cursor shape is unavailable"); 348 else if (code == GLFW_FEATURE_UNAVAILABLE) 349 strcpy(description, "The requested feature cannot be implemented for this platform"); 350 else if (code == GLFW_FEATURE_UNIMPLEMENTED) 351 strcpy(description, "The requested feature has not yet been implemented for this platform"); 352 else if (code == GLFW_PLATFORM_UNAVAILABLE) 353 strcpy(description, "The requested platform is unavailable"); 354 else 355 strcpy(description, "ERROR: UNKNOWN GLFW ERROR"); 356 } 357 358 if (_glfw.initialized) 359 { 360 error = _glfwPlatformGetTls(&_glfw.errorSlot); 361 if (!error) 362 { 363 error = _glfw_calloc(1, sizeof(_GLFWerror)); 364 _glfwPlatformSetTls(&_glfw.errorSlot, error); 365 _glfwPlatformLockMutex(&_glfw.errorLock); 366 error->next = _glfw.errorListHead; 367 _glfw.errorListHead = error; 368 _glfwPlatformUnlockMutex(&_glfw.errorLock); 369 } 370 } 371 else 372 error = &_glfwMainThreadError; 373 374 error->code = code; 375 strcpy(error->description, description); 376 377 if (_glfwErrorCallback) 378 _glfwErrorCallback(code, description); 379 } 380 381 382 ////////////////////////////////////////////////////////////////////////// 383 ////// GLFW public API ////// 384 ////////////////////////////////////////////////////////////////////////// 385 386 GLFWAPI int glfwInit(void) 387 { 388 if (_glfw.initialized) 389 return GLFW_TRUE; 390 391 memset(&_glfw, 0, sizeof(_glfw)); 392 _glfw.hints.init = _glfwInitHints; 393 394 _glfw.allocator = _glfwInitAllocator; 395 if (!_glfw.allocator.allocate) 396 { 397 _glfw.allocator.allocate = defaultAllocate; 398 _glfw.allocator.reallocate = defaultReallocate; 399 _glfw.allocator.deallocate = defaultDeallocate; 400 } 401 402 if (!_glfwSelectPlatform(_glfw.hints.init.platformID, &_glfw.platform)) 403 return GLFW_FALSE; 404 405 if (!_glfw.platform.init()) 406 { 407 terminate(); 408 return GLFW_FALSE; 409 } 410 411 if (!_glfwPlatformCreateMutex(&_glfw.errorLock) || 412 !_glfwPlatformCreateTls(&_glfw.errorSlot) || 413 !_glfwPlatformCreateTls(&_glfw.contextSlot)) 414 { 415 terminate(); 416 return GLFW_FALSE; 417 } 418 419 _glfwPlatformSetTls(&_glfw.errorSlot, &_glfwMainThreadError); 420 421 _glfwInitGamepadMappings(); 422 423 _glfwPlatformInitTimer(); 424 _glfw.timer.offset = _glfwPlatformGetTimerValue(); 425 426 _glfw.initialized = GLFW_TRUE; 427 428 glfwDefaultWindowHints(); 429 return GLFW_TRUE; 430 } 431 432 GLFWAPI void glfwTerminate(void) 433 { 434 if (!_glfw.initialized) 435 return; 436 437 terminate(); 438 } 439 440 GLFWAPI void glfwInitHint(int hint, int value) 441 { 442 switch (hint) 443 { 444 case GLFW_JOYSTICK_HAT_BUTTONS: 445 _glfwInitHints.hatButtons = value; 446 return; 447 case GLFW_ANGLE_PLATFORM_TYPE: 448 _glfwInitHints.angleType = value; 449 return; 450 case GLFW_PLATFORM: 451 _glfwInitHints.platformID = value; 452 return; 453 case GLFW_COCOA_CHDIR_RESOURCES: 454 _glfwInitHints.ns.chdir = value; 455 return; 456 case GLFW_COCOA_MENUBAR: 457 _glfwInitHints.ns.menubar = value; 458 return; 459 case GLFW_X11_XCB_VULKAN_SURFACE: 460 _glfwInitHints.x11.xcbVulkanSurface = value; 461 return; 462 case GLFW_WAYLAND_LIBDECOR: 463 _glfwInitHints.wl.libdecorMode = value; 464 return; 465 } 466 467 _glfwInputError(GLFW_INVALID_ENUM, 468 "Invalid init hint 0x%08X", hint); 469 } 470 471 GLFWAPI void glfwInitAllocator(const GLFWallocator* allocator) 472 { 473 if (allocator) 474 { 475 if (allocator->allocate && allocator->reallocate && allocator->deallocate) 476 _glfwInitAllocator = *allocator; 477 else 478 _glfwInputError(GLFW_INVALID_VALUE, "Missing function in allocator"); 479 } 480 else 481 memset(&_glfwInitAllocator, 0, sizeof(GLFWallocator)); 482 } 483 484 GLFWAPI void glfwInitVulkanLoader(PFN_vkGetInstanceProcAddr loader) 485 { 486 _glfwInitHints.vulkanLoader = loader; 487 } 488 489 GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev) 490 { 491 if (major != NULL) 492 *major = GLFW_VERSION_MAJOR; 493 if (minor != NULL) 494 *minor = GLFW_VERSION_MINOR; 495 if (rev != NULL) 496 *rev = GLFW_VERSION_REVISION; 497 } 498 499 GLFWAPI int glfwGetError(const char** description) 500 { 501 _GLFWerror* error; 502 int code = GLFW_NO_ERROR; 503 504 if (description) 505 *description = NULL; 506 507 if (_glfw.initialized) 508 error = _glfwPlatformGetTls(&_glfw.errorSlot); 509 else 510 error = &_glfwMainThreadError; 511 512 if (error) 513 { 514 code = error->code; 515 error->code = GLFW_NO_ERROR; 516 if (description && code) 517 *description = error->description; 518 } 519 520 return code; 521 } 522 523 GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun) 524 { 525 _GLFW_SWAP(GLFWerrorfun, _glfwErrorCallback, cbfun); 526 return cbfun; 527 } 528